* add entry at head
*
* LINK_TAIL_SLIST(listhead, pentry, nextlink, entrytype)
- * add entry at tail
+ * add entry at tail. This is O(n), if this is a common
+ * operation the FIFO template may be more appropriate.
*
* LINK_SORT_SLIST(listhead, pentry, beforecur, nextlink, entrytype)
* add entry in sorted order. beforecur is an expression comparing
* punlinked is pointed to the removed entry or NULL if none
* satisfy expr.
*
+ * FIFO: singly-linked lists plus tail pointer
+ * ===========================================
+ *
+ * This is the same as FreeBSD's sys/queue.h STAILQ -- a singly-linked
+ * list implementation with tail-pointer maintenance, so that adding
+ * at the tail for first-in, first-out access is O(1).
+ *
+ * DECL_FIFO_ANCHOR(entrytype)
+ * provides the type specification portion of the declaration for
+ * a variable to refer to a FIFO queue (similar to listhead). The
+ * anchor contains the head and indirect tail pointers. Example:
+ *
+ * #include "ntp_lists.h"
+ *
+ * typedef struct myentry_tag myentry;
+ * struct myentry_tag {
+ * myentry *next_link;
+ * ...
+ * };
+ *
+ * DECL_FIFO_ANCHOR(myentry) my_fifo;
+ *
+ * void somefunc(myentry *pentry)
+ * {
+ * LINK_FIFO(my_fifo, pentry, next_link);
+ * }
+ *
+ * If DECL_FIFO_ANCHOR is used with stack or heap storage, it
+ * should be initialized to NULL pointers using a = { NULL };
+ * initializer or memset.
+ *
+ * HEAD_FIFO(anchor)
+ * TAIL_FIFO(anchor)
+ * Pointer to first/last entry, NULL if FIFO is empty.
+ *
+ * LINK_FIFO(anchor, pentry, nextlink)
+ * add entry at tail.
+ *
+ * UNLINK_FIFO(punlinked, anchor, nextlink)
+ * unlink head entry and point punlinked to it, or set punlinked
+ * to NULL if the list is empty.
+ *
+ * CONCAT_FIFO(q1, q2, nextlink)
+ * empty fifoq q2 moving its nodes to q1 following q1's existing
+ * nodes.
+ *
* DLIST: doubly-linked lists
* ==========================
*
#ifndef NTP_LISTS_H
#define NTP_LISTS_H
-#ifndef TRUE
-# define TRUE 1
-# define NTP_LISTS_UNDEF_TRUE
+#include "ntp_types.h" /* TRUE and FALSE */
+#include "ntp_assert.h"
+
+#ifdef DEBUG
+# define NTP_DEBUG_LISTS_H
+#endif
+
+/*
+ * If list debugging is not enabled, save a little time by not clearing
+ * an entry's link pointer when it is unlinked, as the stale pointer
+ * is harmless as long as it is ignored when the entry is not in a
+ * list.
+ */
+#ifndef NTP_DEBUG_LISTS_H
+#define MAYBE_Z_LISTS(p) do { } while (FALSE)
+#else
+#define MAYBE_Z_LISTS(p) (p) = NULL
#endif
#define LINK_SLIST(listhead, pentry, nextlink) \
do { \
(pentry)->nextlink = (listhead); \
(listhead) = (pentry); \
-} while (0)
+} while (FALSE)
#define LINK_TAIL_SLIST(listhead, pentry, nextlink, entrytype) \
do { \
\
(pentry)->nextlink = NULL; \
*pptail = (pentry); \
-} while (0)
+} while (FALSE)
#define LINK_SORT_SLIST_CURRENT() (*ppentry)
#define L_S_S_CUR() LINK_SORT_SLIST_CURRENT()
entrytype **ppentry; \
\
ppentry = &(listhead); \
- while (*ppentry != NULL) { \
- if (beforecur) { \
+ while (TRUE) { \
+ if (NULL == *ppentry || (beforecur)) { \
(pentry)->nextlink = *ppentry; \
*ppentry = (pentry); \
break; \
break; \
} \
} \
-} while (0)
+} while (FALSE)
#define UNLINK_HEAD_SLIST(punlinked, listhead, nextlink) \
do { \
(punlinked) = (listhead); \
if (NULL != (punlinked)) { \
(listhead) = (punlinked)->nextlink; \
- (punlinked)->nextlink = NULL; \
+ MAYBE_Z_LISTS((punlinked)->nextlink); \
} \
-} while (0)
+} while (FALSE)
#define UNLINK_EXPR_SLIST_CURRENT() (*ppentry)
#define U_E_S_CUR() UNLINK_EXPR_SLIST_CURRENT()
ppentry = &(listhead); \
\
while (!(expr)) \
- if ((*ppentry) != NULL && \
+ if (*ppentry != NULL && \
(*ppentry)->nextlink != NULL) { \
ppentry = &((*ppentry)->nextlink); \
- } else { \
+ } else { \
ppentry = NULL; \
break; \
} \
if (ppentry != NULL) { \
(punlinked) = *ppentry; \
*ppentry = (punlinked)->nextlink; \
- (punlinked)->nextlink = NULL; \
- } else \
+ MAYBE_Z_LISTS((punlinked)->nextlink); \
+ } else { \
(punlinked) = NULL; \
-} while (0)
+ } \
+} while (FALSE)
#define UNLINK_SLIST(punlinked, listhead, ptounlink, nextlink, \
entrytype) \
NTP_INSIST(pentry != pentry->nextlink); \
NTP_INSIST((listhead) != pentry->nextlink); \
} \
-} while (0)
+} while (FALSE)
+
+/*
+ * FIFO
+ */
+
+#define DECL_FIFO_ANCHOR(entrytype) \
+struct { \
+ entrytype * phead; /* NULL if list empty */ \
+ entrytype ** pptail; /* NULL if list empty */ \
+}
+
+#define HEAD_FIFO(anchor) ((anchor).phead)
+#define TAIL_FIFO(anchor) ((NULL == (anchor).pptail) \
+ ? NULL \
+ : *((anchor).pptail))
+
+/*
+ * For DEBUG builds only, verify both or neither of the anchor pointers
+ * are NULL with each operation.
+ */
+#if !defined(NTP_DEBUG_LISTS_H)
+#define CHECK_FIFO_CONSISTENCY(anchor) do { } while (FALSE)
+#else
+#define CHECK_FIFO_CONSISTENCY(anchor) \
+ check_gen_fifo_consistency(&(anchor))
+void check_gen_fifo_consistency(void *fifo);
+#endif
+
+/*
+ * generic FIFO element used to access any FIFO where each element
+ * begins with the link pointer
+ */
+typedef struct gen_node_tag gen_node;
+struct gen_node_tag {
+ gen_node * link;
+};
+
+/* generic FIFO */
+typedef DECL_FIFO_ANCHOR(gen_node) gen_fifo;
+
+
+#define LINK_FIFO(anchor, pentry, nextlink) \
+do { \
+ CHECK_FIFO_CONSISTENCY(anchor); \
+ \
+ (pentry)->nextlink = NULL; \
+ if (NULL != (anchor).pptail) { \
+ (*((anchor).pptail))->nextlink = (pentry); \
+ (anchor).pptail = \
+ &(*((anchor).pptail))->nextlink; \
+ } else { \
+ (anchor).phead = (pentry); \
+ (anchor).pptail = &(anchor).phead; \
+ } \
+ \
+ CHECK_FIFO_CONSISTENCY(anchor); \
+} while (FALSE)
+
+#define UNLINK_FIFO(punlinked, anchor, nextlink) \
+do { \
+ CHECK_FIFO_CONSISTENCY(anchor); \
+ \
+ (punlinked) = (anchor).phead; \
+ if (NULL != (punlinked)) { \
+ (anchor).phead = (punlinked)->nextlink; \
+ if (NULL == (anchor).phead) \
+ (anchor).pptail = NULL; \
+ else if ((anchor).pptail == \
+ &(punlinked)->nextlink) \
+ (anchor).pptail = &(anchor).phead; \
+ MAYBE_Z_LISTS((punlinked)->nextlink); \
+ CHECK_FIFO_CONSISTENCY(anchor); \
+ } \
+} while (FALSE)
+
+#define UNLINK_MID_FIFO(punlinked, anchor, tounlink, nextlink, \
+ entrytype) \
+do { \
+ entrytype **ppentry; \
+ \
+ CHECK_FIFO_CONSISTENCY(anchor); \
+ \
+ ppentry = &(anchor).phead; \
+ \
+ while ((tounlink) != *ppentry) \
+ if ((*ppentry)->nextlink != NULL) { \
+ ppentry = &((*ppentry)->nextlink); \
+ } else { \
+ ppentry = NULL; \
+ break; \
+ } \
+ \
+ if (ppentry != NULL) { \
+ (punlinked) = *ppentry; \
+ *ppentry = (punlinked)->nextlink; \
+ if (NULL == *ppentry) \
+ (anchor).pptail = NULL; \
+ else if ((anchor).pptail == \
+ &(punlinked)->nextlink) \
+ (anchor).pptail = &(anchor).phead; \
+ MAYBE_Z_LISTS((punlinked)->nextlink); \
+ CHECK_FIFO_CONSISTENCY(anchor); \
+ } else { \
+ (punlinked) = NULL; \
+ } \
+} while (FALSE)
+
+#define CONCAT_FIFO(f1, f2, nextlink) \
+do { \
+ CHECK_FIFO_CONSISTENCY(f1); \
+ CHECK_FIFO_CONSISTENCY(f2); \
+ \
+ if ((f2).pptail != NULL) { \
+ if ((f1).pptail != NULL) { \
+ (*(f1).pptail)->nextlink = (f2).phead; \
+ if ((f2).pptail == &(f2).phead) \
+ (f1).pptail = \
+ &(*(f1).pptail)->nextlink; \
+ else \
+ (f1).pptail = (f2).pptail; \
+ CHECK_FIFO_CONSISTENCY(f1); \
+ } else { \
+ (f1) = (f2); \
+ } \
+ MAYBE_Z_LISTS((f2).phead); \
+ MAYBE_Z_LISTS((f2).pptail); \
+ } \
+} while (FALSE)
/*
* DLIST
do { \
(listhead).link.f = &(listhead); \
(listhead).link.b = &(listhead); \
-} while (0)
+} while (FALSE)
#define HEAD_DLIST(listhead, link) \
( \
(pentry)->link.b = &(listhead); \
(listhead).link.f->link.b = (pentry); \
(listhead).link.f = (pentry); \
-} while (0)
+} while (FALSE)
#define LINK_TAIL_DLIST(listhead, pentry, link) \
do { \
(pentry)->link.f = &(listhead); \
(listhead).link.b->link.f = (pentry); \
(listhead).link.b = (pentry); \
-} while (0)
+} while (FALSE)
#define UNLINK_DLIST(ptounlink, link) \
do { \
(ptounlink)->link.b->link.f = (ptounlink)->link.f; \
(ptounlink)->link.f->link.b = (ptounlink)->link.b; \
- (ptounlink)->link.b = NULL; \
- (ptounlink)->link.f = NULL; \
-} while (0)
+ MAYBE_Z_LISTS((ptounlink)->link.b); \
+ MAYBE_Z_LISTS((ptounlink)->link.f); \
+} while (FALSE)
#define ITER_DLIST_BEGIN(listhead, iter, link, entrytype) \
{ \
} \
}
-
-#ifdef NTP_LISTS_UNDEF_TRUE
-# undef TRUE
-#endif
-
#endif /* NTP_LISTS_H */