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