547 lines
16 KiB
C
547 lines
16 KiB
C
/*
|
||
* 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:
|
||
*/
|