/** * \file cf_list.c * * \author myusgun * * \brief 연결 리스트 구현 */ #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_ctx의 구현) */ typedef struct __cf_list__ { int size; CF_NODE * front; CF_NODE * rear; } CF_LIST_CONTEXT; /** * 리스트 컨텍스트 생성 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 리스트 컨텍스트 포인터 */ int CF_List_Create (cf_ctx * ctx) { CF_LIST_CONTEXT * context = NULL; ASSERT_CTX (ctx); context = NEWCTX (CF_LIST_CONTEXT); if (context == NULL) return CF_ERROR_DS_CREATE_CTX; *ctx = (cf_ctx) context; return CF_OK; } /** * 리스트 컨텍스트 해제 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 리스트 컨텍스트 */ int CF_List_Destroy (cf_ctx ctx) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) 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_ctx ctx, cf_traverser * traverser) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) 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_ctx ctx, cf_traverser * traverser) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) ctx; ASSERT_CTX (ctx); ASSERT_TRAVERSER (traverser); *traverser = (cf_traverser *) context->rear; return CF_OK; } /** * 리스트 처음에 새 데이터 삽입 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 리스트 컨텍스트 * \param element 삽입할 데이터 주소 */ int CF_List_AddFront (cf_ctx ctx, const void * element) { int result = 0; cf_traverser trav = NULL; ASSERT_CTX (ctx); result = CF_List_Front (ctx, &trav); if (result < 0) return result; return CF_List_InsertBefore (ctx, trav, element); } /** * 리스트 끝에 새 데이터 삽입 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 리스트 컨텍스트 * \param element 삽입할 데이터 주소 */ int CF_List_AddRear (cf_ctx ctx, const void * element) { int result = 0; cf_traverser trav = NULL; ASSERT_CTX (ctx); result = CF_List_Rear (ctx, &trav); if (result < 0) return result; return CF_List_InsertAfter (ctx, trav, element); } /** * 리스트 탐색자(traverser)의 앞에 새 데이터 삽입 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 리스트 컨텍스트 * \param traverser 리스트 탐색자 * \param element 삽입할 데이터 주소 */ int CF_List_InsertBefore (cf_ctx ctx, const cf_traverser traverser, const void * element) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) ctx; CF_NODE * node = NULL; CF_NODE * newnode = NULL; ASSERT_CTX (ctx); newnode = NEWCTX (CF_NODE); 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; } 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; } NO_CATCH; context->size++; return CF_OK; } /** * 리스트 탐색자(traverser)의 뒤에 새 데이터 삽입 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 리스트 컨텍스트 * \param traverser 리스트 탐색자 * \param element 삽입할 데이터 주소 */ int CF_List_InsertAfter (cf_ctx ctx, const cf_traverser traverser, const void * element) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) ctx; CF_NODE * node = NULL; CF_NODE * newnode = NULL; ASSERT_CTX (ctx); newnode = NEWCTX (CF_NODE); 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; } 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; } /** * 리스트 탐색자(traverser)의 자리에 새 데이터 삽입 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param traverser 리스트 탐색자 * \param element 할당할 데이터 주소 */ int CF_List_Set (const cf_traverser traverser, const void * element) { CF_NODE * node = (CF_NODE *) traverser; ASSERT_TRAVERSER (traverser); node->element = (void *) element; return CF_OK; } /** * 탐색자 위치의 데이터를 가져옴 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param traverser 리스트 탐색자 * \param element 데이터 주소 */ int CF_List_Get (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 ctx 리스트 컨텍스트 * \param traverser 리스트 탐색자 주소 */ int CF_List_Remove (cf_ctx ctx, cf_traverser * traverser) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) 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_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 리스트 탐색자 주소 */ 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_ctx ctx) { CF_LIST_CONTEXT * context = (CF_LIST_CONTEXT *) ctx; ASSERT_CTX (ctx); return context->size; }