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