/** * @file cf_list.c * @author myusgun */ #include "cf_list.h" #include "cf_local.h" #include "cf_error.h" #include #define ASSERT_CTX(__ctx) \ if (__ctx == NULL) \ return CF_ERROR_DS_INVALID_CTX #define ASSERT_TRAVERSER(__trav) \ if (__trav == NULL) \ return CF_ERROR_DS_INVALID_TRAVERSER #define ASSERT_ARGS(x) \ if ((x)) \ return CF_ERROR_DS_INVALID_ARGS /** 리스트 노드 (CF_Traverser의 구현) */ typedef struct __cf_node__ { void * element; struct __cf_node__ * prev; struct __cf_node__ * next; } CF_NODE; /** 리스트 컨텍스트 (CF_List_Ctx의 구현) */ typedef struct __cf_list__ { int size; CF_NODE * front; CF_NODE * rear; } CF_LIST_CTX; /** * 리스트 컨텍스트 생성 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 포인터 */ int CF_List_CreateCtx (CF_List_Ctx * ctx) { CF_LIST_CTX * context = NULL; ASSERT_ARGS (ctx == NULL); context = (CF_LIST_CTX *) calloc (sizeof (CF_LIST_CTX), 1); if (context == NULL) return CF_ERROR_DS_CREATE_CTX; *ctx = (CF_List_Ctx) context; return CF_OK; } /** * 리스트 컨텍스트 해제 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 */ int CF_List_DestroyCtx (CF_List_Ctx ctx) { CF_LIST_CTX * context = (CF_LIST_CTX *) ctx; ASSERT_CTX (ctx); CF_List_RemoveAll (ctx); free (context); return CF_OK; } /** * 리스트 탐색자(Traverser)를 Front 위치로 설정 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 * @param traverser 리스트 탐색자 주소 */ int CF_List_Front (CF_List_Ctx ctx, CF_Traverser * traverser) { CF_LIST_CTX * context = (CF_LIST_CTX *) ctx; ASSERT_CTX (ctx); ASSERT_TRAVERSER (traverser); *traverser = (CF_Traverser *) context->front; return CF_OK; } /** * 리스트 탐색자(Traverser)를 Rear 위치로 설정 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 * @param traverser 리스트 탐색자 주소 */ int CF_List_Rear (CF_List_Ctx ctx, CF_Traverser * traverser) { CF_LIST_CTX * context = (CF_LIST_CTX *) ctx; ASSERT_CTX (ctx); ASSERT_TRAVERSER (traverser); *traverser = (CF_Traverser *) context->rear; return CF_OK; } /** * 리스트에 새 데이터 삽입 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 * @param traverser 리스트 탐색자 * @param direction 탐색자의 전/후를 지정 * @param element 삽입할 데이터 주소 */ int CF_List_Insert (CF_List_Ctx ctx, const CF_Traverser traverser, const CF_DIRECTION direction, const void * element) { CF_LIST_CTX * context = (CF_LIST_CTX *) ctx; CF_NODE * node = NULL; CF_NODE * newnode = NULL; ASSERT_CTX (ctx); newnode = (CF_NODE *) calloc (sizeof (CF_NODE), 1); if (newnode == NULL) return CF_ERROR_DS_CREATE_NODE; newnode->element = (void *) element; TRY { if (CF_List_GetSize (ctx) == 0) { context->front = context->rear = newnode; TRY_BREAK; } if (direction == CF_DIRECTION_BEFORE) { node = traverser ? (CF_NODE *) traverser : context->front; newnode->next = node; if (node) { newnode->prev = node->prev; node->prev = newnode; if (newnode->prev) newnode->prev->next = newnode; } if (node == context->front) context->front = newnode; } else /* if (direction == CF_DIRECTION_AFTER) */ { node = traverser ? (CF_NODE *) traverser : context->rear; newnode->prev = node; if (node) { newnode->next = node->next; node->next = newnode; if (newnode->next) newnode->next->prev = newnode; } if (node == context->rear) context->rear = newnode; } } NO_CATCH; context->size++; return CF_OK; } /** * 리스트에서 탐색자 위치의 항목을 삭제 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 * @param traverser 리스트 탐색자 주소 */ int CF_List_Remove (CF_List_Ctx ctx, CF_Traverser * traverser) { CF_LIST_CTX * context = (CF_LIST_CTX *) ctx; CF_NODE * node = NULL; ASSERT_CTX (ctx); ASSERT_TRAVERSER (traverser); ASSERT_ARGS (*traverser == NULL); node = (CF_NODE *) *traverser; if (node->prev) { node->prev->next = node->next; if (node == context->rear) context->rear = node->prev; } if (node->next) { node->next->prev = node->prev; if (node == context->front) context->front = node->next; } free (node); *traverser = NULL; context->size--; if (CF_List_GetSize (ctx) == 0) context->front = context->rear = NULL; return CF_OK; } /** * 리스트에서 모든 항목을 삭제 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 */ int CF_List_RemoveAll (CF_List_Ctx ctx) { CF_Traverser traverser = NULL; ASSERT_CTX (ctx); while (CF_List_GetSize (ctx) > 0) { CF_List_Front (ctx, &traverser); CF_List_Remove (ctx, &traverser); } return CF_OK; } /** * 탐색자 위치의 데이터를 가져옴 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param traverser 리스트 탐색자 * @param element 데이터 주소 */ int CF_List_GetElement (const CF_Traverser traverser, void ** element) { CF_NODE * node = (CF_NODE *) traverser; ASSERT_TRAVERSER (traverser); ASSERT_ARGS (element == NULL); *element = node->element; return CF_OK; } /** * 탐색자 위치를 이전으로 이동 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param traverser 리스트 탐색자 주소 */ int CF_List_Prev (CF_Traverser * traverser) { CF_NODE * node = (CF_NODE *) *traverser; ASSERT_TRAVERSER (traverser); ASSERT_ARGS (*traverser == NULL); node = node->prev; *traverser = (CF_Traverser *) node; if (node == NULL) return CF_ERROR_DS_NO_MORE; return CF_OK; } /** * 탐색자 위치를 다음으로 이동 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param traverser 리스트 탐색자 주소 */ int CF_List_Next (CF_Traverser * traverser) { CF_NODE * node = (CF_NODE *) *traverser; ASSERT_TRAVERSER (traverser); ASSERT_ARGS (*traverser == NULL); node = node->next; *traverser = (CF_Traverser *) node; if (node == NULL) return CF_ERROR_DS_NO_MORE; return CF_OK; } /** * 리스트에 등록된 항목의 수를 가져옴 * * @return 성공 시, 항목 수; 실패 시, 오류 코드 * * @param ctx 리스트 컨텍스트 */ int CF_List_GetSize (CF_List_Ctx ctx) { CF_LIST_CTX * context = (CF_LIST_CTX *) ctx; ASSERT_CTX (ctx); return context->size; }