123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- /*
- * 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:
- */
|