/* * tkDList.h -- * * A list is headed by pointers to first and last element. The elements * are doubly linked so that an arbitrary element can be removed without * a need to traverse the list. New elements can be added to the list * before or after an existing element or at the head/tail of the list. * A list may be traversed in the forward or backward direction. * * Copyright (c) 2018 by Gregor Cramer. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ /* * Note that this file will not be included in header files, it is the purpose * of this file to be included in source files only. Thus we are not using the * prefix "Tk_" here for functions, because all the functions have private scope. */ /* * ------------------------------------------------------------------------------- * Use the double linked list in the following way: * ------------------------------------------------------------------------------- * typedef struct MyListEntry { TK_DLIST_LINKS(MyListEntry); int value; } MyListEntry; * TK_DLIST_DEFINE(MyList, MyListEntry); * MyList listHdr = TK_DLIST_LIST_INITIALIZER; // or MyList_Init(&listHdr) * MyListEntry *p; * int i = 0; * MyList_Append(&listHdr, ckalloc(sizeof(MyListEntry))); * MyList_Append(&listHdr, ckalloc(sizeof(MyListEntry))); * TK_DLIST_FOREACH(p, &listHdr) { p->value = i++; } * // ... * MyList_RemoveHead(&listHdr); * MyList_RemoveHead(&listHdr); * MyList_Clear(MyListEntry, &listHdr); // invokes ckfree() for each element * ------------------------------------------------------------------------------- * IMPORTANT NOTE: TK_DLIST_LINKS must be used at start of struct! */ #ifndef TK_DLIST_DEFINED #define TK_DLIST_DEFINED #include "tkInt.h" /* * List definitions. */ #define TK_DLIST_LINKS(ElemType) \ struct { \ struct ElemType *prev; /* previous element */ \ struct ElemType *next; /* next element */ \ } _dl_ #define TK_DLIST_LIST_INITIALIZER { NULL, NULL } #define TK_DLIST_ELEM_INITIALIZER { NULL, NULL } /*************************************************************************/ /* * DList_Init: Initialize list header. This can also be used to clear the * list. * * See also: DList_Clear() */ /*************************************************************************/ /* * DList_ElemInit: Initialize a list element. */ /*************************************************************************/ /* * DList_InsertAfter: Insert 'elem' after 'listElem'. 'elem' must not * be linked, but 'listElem' must be linked. */ /*************************************************************************/ /* * DList_InsertBefore: Insert 'elem' before 'listElem'. 'elem' must not * be linked, but 'listElem' must be linked. */ /*************************************************************************/ /* * DList_Prepend: Insert 'elem' at start of list. This element must not * be linked. * * See also: DList_Append() */ /*************************************************************************/ /* * DList_Append: Append 'elem' to end of list. This element must not * be linked. * * See also: DList_Prepend() */ /*************************************************************************/ /* * DList_Move: Append all list items of 'src' to end of 'dst'. */ /*************************************************************************/ /* * DList_Remove: Remove 'elem' from list. This element must be linked. * * See also: DList_Free() */ /*************************************************************************/ /* * DList_Free: Remove 'elem' from list and free it. This element must * be linked. * * See also: DList_Remove() */ /*************************************************************************/ /* * DList_RemoveHead: Remove first element from list. The list must * not be empty. * * See also: DList_FreeHead() */ /*************************************************************************/ /* * DList_RemoveTail: Remove last element from list. The list must * not be empty. * * See also: DList_FreeTail() */ /*************************************************************************/ /* * DList_FreeHead: Remove first element from list and free it. * The list must not be empty. * * See also: DList_RemoveHead() */ /*************************************************************************/ /* * DList_FreeTail: Remove last element from list and free it. * The list must not be empty. * * See also: DList_RemoveTail() */ /*************************************************************************/ /* * DList_SwapElems: Swap positions of given elements 'lhs' and 'rhs'. * Both elements must be linked, and must belong to same list. */ /*************************************************************************/ /* * DList_Clear: Clear whole list and free all elements. * * See also: DList_Init */ /*************************************************************************/ /* * DList_Traverse: Iterate over all elements in list from first to last. * Call given function func(head, elem) for each element. The function has * to return the next element in list to traverse, normally this is * DList_Next(elem). * * See also: TK_DLIST_FOREACH, TK_DLIST_FOREACH_REVERSE */ /*************************************************************************/ /* * DList_First: Return pointer of first element in list, maybe it's NULL. */ /*************************************************************************/ /* * DList_Last: Return pointer of last element in list, maybe it's NULL. */ /*************************************************************************/ /* * DList_Next: Return pointer of next element after 'elem', maybe it's NULL. * * See also: DList_Prev() */ /*************************************************************************/ /* * DList_Prev: Return pointer of previous element before 'elem', maybe it's * NULL. * * See also: DList_Next() */ /*************************************************************************/ /* * DList_IsEmpty: Test whether given list is empty. */ /*************************************************************************/ /* * DList_IsLinked: Test whether given element is linked. */ /*************************************************************************/ /* * DList_IsFirst: Test whether given element is first element in list. * Note that 'elem' must be linked. * * See also: DList_IsLast(), DList_IsLinked() */ /*************************************************************************/ /* * DList_IsLast: Test whether given element is last element in list. * Note that 'elem' must be linked. * * See also: DList_IsFirst(), DList_IsLinked() */ /*************************************************************************/ /* * DList_Size: Count number of elements in given list. */ /*************************************************************************/ /* * TK_DLIST_FOREACH: Iterate over all elements in list from first to last. * 'var' is the name of the variable which points to current element. * * See also: TK_DLIST_FOREACH_REVERSE, DList_Traverse() */ /*************************************************************************/ /* * TK_DLIST_FOREACH_REVERSE: Iterate over all elements in list from last * to first (backwards). 'var' is the name of the variable which points to * current element. */ /*************************************************************************/ #if defined(__GNUC__) || defined(__clang__) # define __TK_DLIST_UNUSED __attribute__((unused)) #else # define __TK_DLIST_UNUSED #endif #define TK_DLIST_DEFINE(LT, ElemType) /* LT = type of head/list */ \ /* ------------------------------------------------------------------------- */ \ typedef struct LT { \ struct ElemType *first; /* first element */ \ struct ElemType *last; /* last element */ \ } LT; \ \ typedef struct ElemType *(LT##_Func)(LT *head, struct ElemType *elem); \ \ __TK_DLIST_UNUSED \ static void \ LT##_Init(LT *head) \ { \ assert(head); \ head->first = NULL; \ head->last = NULL; \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_ElemInit(struct ElemType *elem) \ { \ assert(elem); \ elem->_dl_.next = NULL; \ elem->_dl_.prev = NULL; \ } \ \ __TK_DLIST_UNUSED \ static int \ LT##_IsEmpty(LT *head) \ { \ assert(head); \ return head->first == NULL; \ } \ \ __TK_DLIST_UNUSED \ static int \ LT##_IsLinked(struct ElemType *elem) \ { \ assert(elem); \ return elem->_dl_.next && elem->_dl_.prev; \ } \ \ __TK_DLIST_UNUSED \ static int \ LT##_IsFirst(struct ElemType *elem) \ { \ assert(LT##_IsLinked(elem)); \ return elem->_dl_.prev->_dl_.prev == elem; \ } \ \ __TK_DLIST_UNUSED \ static int \ LT##_IsLast(struct ElemType *elem) \ { \ assert(LT##_IsLinked(elem)); \ return elem->_dl_.next->_dl_.next == elem; \ } \ \ __TK_DLIST_UNUSED \ static struct ElemType * \ LT##_First(LT *head) \ { \ assert(head); \ return head->first; \ } \ \ __TK_DLIST_UNUSED \ static struct ElemType * \ LT##_Last(LT *head) \ { \ assert(head); \ return head->last; \ } \ \ __TK_DLIST_UNUSED \ static struct ElemType * \ LT##_Next(struct ElemType *elem) \ { \ assert(elem); \ return LT##_IsLast(elem) ? NULL : elem->_dl_.next; \ } \ \ __TK_DLIST_UNUSED \ static struct ElemType * \ LT##_Prev(struct ElemType *elem) \ { \ assert(elem); \ return LT##_IsFirst(elem) ? NULL : elem->_dl_.prev; \ } \ \ __TK_DLIST_UNUSED \ static unsigned \ LT##_Size(const LT *head) \ { \ const struct ElemType *elem; \ unsigned size = 0; \ assert(head); \ if ((elem = head->first)) { \ for ( ; elem != (void *) head; elem = elem->_dl_.next) { \ ++size; \ } \ } \ return size; \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_InsertAfter(struct ElemType *listElem, struct ElemType *elem) \ { \ assert(listElem); \ assert(elem); \ elem->_dl_.next = listElem->_dl_.next; \ elem->_dl_.prev = listElem; \ listElem->_dl_.next->_dl_.prev = elem; \ listElem->_dl_.next = elem; \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_InsertBefore(struct ElemType *listElem, struct ElemType *elem) \ { \ assert(listElem); \ assert(elem); \ elem->_dl_.next = listElem; \ elem->_dl_.prev = listElem->_dl_.prev;; \ listElem->_dl_.prev->_dl_.next = elem; \ listElem->_dl_.prev = elem; \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Prepend(LT *head, struct ElemType *elem) \ { \ assert(head); \ assert(elem); \ elem->_dl_.prev = (void *) head; \ if (!head->first) { \ elem->_dl_.next = (void *) head; \ head->last = elem; \ } else { \ elem->_dl_.next = head->first; \ head->first->_dl_.prev = elem; \ } \ head->first = elem; \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Append(LT *head, struct ElemType *elem) \ { \ assert(head); \ assert(elem); \ elem->_dl_.next = (void *) head; \ if (!head->first) { \ elem->_dl_.prev = (void *) head; \ head->first = elem; \ } else { \ elem->_dl_.prev = head->last; \ head->last->_dl_.next = elem; \ } \ head->last = elem; \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Move(LT *dst, LT *src) \ { \ assert(dst); \ assert(src); \ if (src->first) { \ if (dst->first) { \ dst->last->_dl_.next = src->first; \ src->first->_dl_.prev = dst->last; \ dst->last = src->last; \ } else { \ *dst = *src; \ dst->first->_dl_.prev = (void *) dst; \ } \ dst->last->_dl_.next = (void *) dst; \ LT##_Init(src); \ } \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Remove(struct ElemType *elem) \ { \ int isFirst, isLast; \ assert(LT##_IsLinked(elem)); \ isFirst = LT##_IsFirst(elem); \ isLast = LT##_IsLast(elem); \ if (isFirst && isLast) { \ ((LT *) elem->_dl_.prev)->first = NULL; \ ((LT *) elem->_dl_.next)->last = NULL; \ } else { \ if (isFirst) { \ ((LT *) elem->_dl_.prev)->first = elem->_dl_.next; \ } else { \ elem->_dl_.prev->_dl_.next = elem->_dl_.next; \ } \ if (isLast) { \ ((LT *) elem->_dl_.next)->last = elem->_dl_.prev; \ } else { \ elem->_dl_.next->_dl_.prev = elem->_dl_.prev; \ } \ } \ LT##_ElemInit(elem); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Free(struct ElemType *elem) \ { \ LT##_Remove(elem); \ ckfree((void *) elem); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_RemoveHead(LT *head) \ { \ assert(!LT##_IsEmpty(head)); \ LT##_Remove(head->first); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_RemoveTail(LT *head) \ { \ assert(!LT##_IsEmpty(head)); \ LT##_Remove(head->last); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_FreeHead(LT *head) \ { \ assert(!LT##_IsEmpty(head)); \ LT##_Free(head->first); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_FreeTail(LT *head) \ { \ assert(!LT##_IsEmpty(head)); \ LT##_Free(head->last); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_SwapElems(struct ElemType *lhs, struct ElemType *rhs) \ { \ assert(lhs); \ assert(rhs); \ if (lhs != rhs) { \ struct ElemType tmp; \ if (LT##_IsFirst(lhs)) { \ ((LT *) lhs->_dl_.prev)->first = rhs; \ } else if (LT##_IsFirst(rhs)) { \ ((LT *) rhs->_dl_.prev)->first = lhs; \ } \ if (LT##_IsLast(lhs)) { \ ((LT *) lhs->_dl_.next)->last = rhs; \ } else if (LT##_IsLast(rhs)) { \ ((LT *) rhs->_dl_.next)->last = lhs; \ } \ tmp._dl_ = lhs->_dl_; \ lhs->_dl_ = rhs->_dl_; \ rhs->_dl_ = tmp._dl_; \ } \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Clear(LT *head) \ { \ struct ElemType *p; \ struct ElemType *next; \ assert(head); \ for (p = head->first; p; p = next) { \ next = LT##_Next(p); \ ckfree((void *) p); \ } \ LT##_Init(head); \ } \ \ __TK_DLIST_UNUSED \ static void \ LT##_Traverse(LT *head, LT##_Func func) \ { \ struct ElemType *next; \ struct ElemType *p; \ assert(head); \ for (p = head->first; p; p = next) { \ next = func(head, p); \ } \ } \ /* ------------------------------------------------------------------------- */ #define TK_DLIST_FOREACH(var, head) \ assert(head); \ for (var = head->first ? head->first : (void *) head; var != (void *) head; var = var->_dl_.next) #define TK_DLIST_FOREACH_REVERSE(var, head) \ assert(head); \ for (var = head->last ? head->last : (void *) head; var != (void *) head; var = var->_dl_.prev) #endif /* TK_DLIST_DEFINED */ /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 105 * End: * vi:set ts=8 sw=4: */