summaryrefslogtreecommitdiffstats
path: root/lib/list_util.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/list_util.h')
-rw-r--r--lib/list_util.h729
1 files changed, 729 insertions, 0 deletions
diff --git a/lib/list_util.h b/lib/list_util.h
new file mode 100644
index 00000000..b658c7ce
--- /dev/null
+++ b/lib/list_util.h
@@ -0,0 +1,729 @@
+/* List Utilities -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_LIST_UTIL_H
+#define _ZEBRA_LIST_UTIL_H
+
+#include <stddef.h>
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*------------------------------------------------------------------------------
+ * Note that the following fell foul of "strict-aliasing":
+ *
+ * #define ssl_del_head(base, next) \
+ * ssl_del_head_func_i((void**)&(base), _lu_off(base, next))
+ *
+ * Inline void*
+ * ssl_del_head_func_i(void** p_base, size_t link_offset)
+ * {
+ * void* item = *p_base ;
+ *
+ * if (item != NULL)
+ * *p_base = _sl_next(item, link_offset) ;
+ *
+ * return item ;
+ * } ;
+ *
+ * the assignment to *p_base is, apparently, unacceptable. This works
+ * perfectly well as am ordinary function. Using a GNUC extension it is
+ * possible to avoid the function call... hence the ugly skips.
+ */
+#ifdef __GNUC__
+#define __GNUC__LIST_UTIL
+#endif
+
+/*==============================================================================
+ * These utilities provide for linked lists of items, where the list pointers
+ * are fields in the items.
+ *
+ * This is a little less general that the linklist stuff, but carries less
+ * overhead.
+ *
+ * The items will be structures of some sort, and are described here as being
+ * of type "struct item". Pointers to those items will be of type
+ * "struct item*".
+ *
+ * Most of these utilities are implemented as macros.
+ *
+ *------------------------------------------------------------------------------
+ * Links and Bases.
+ *
+ * For a singly linked list, the item declaration is straightforward:
+ *
+ * struct item
+ * {
+ * ....
+ * struct item* foo_next ;
+ * ....
+ * }
+ *
+ * The item can live on more than one list, all that is required is that each
+ * list has its next pointer.
+ *
+ * For double linked lists, the item may be declared:
+ *
+ * struct item
+ * {
+ * ....
+ * struct list_pair(struct item*) foo_list ;
+ * ....
+ * } ;
+ *
+ * A single base is straighforward:
+ *
+ * struct item* foo_base ;
+ *
+ * and that may be a variable or a structure field.
+ *
+ * A double base may be declared:
+ *
+ * struct base_pair(struct item*) foo_base ;
+ *
+ * Various ways to construct structures or structure types:
+ *
+ * typedef struct list_pair(struct foo*) foo_list ;
+ *
+ * struct foo_list list_pair(struct foo*) ;
+ *
+ * struct foo_base base_pair(struct foo*) ;
+ */
+
+#define dl_list_pair(ptr_t) { ptr_t next ; ptr_t prev ; }
+
+#define dl_base_pair(ptr_t) { ptr_t head ; ptr_t tail ; }
+
+struct dl_void_list_pair list_pair(void*) ;
+struct dl_void_base_pair base_pair(void*) ;
+
+#define _lu_off(obj, field) ((char*)&((obj)->field) - (char*)(obj))
+
+/*==============================================================================
+ * Single Base, Single Link
+ *
+ * To delete entry must chase down list to find it.
+ *
+ * Supports:
+ *
+ * ssl_init(base) -- initialise base
+ *
+ * An empty list has a NULL base.
+ *
+ * ssl_push(base, item, next) -- add at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ssl_del(base, item, next) -- delete from list
+ *
+ * Treat as function returning int. Does nothing if the item is NULL.
+ *
+ * Returns: 0 => OK -- removed item from list (OR item == NULL)
+ * -1 => item not found on list
+ *
+ * ssl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ssl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ssl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * ssl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if item is NULL.
+ *
+ * Note that ssl_del() and ssl_pop() do NOT affect the item->next pointer.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type struct item*
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "next" to be the name of a field in struct item, with type struct item*
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct item* bar_next ;
+ * ...
+ * } ;
+ *
+ * static struct item* bar_base ; // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * ssl_push(bar_base, q, bar_next) ;
+ *
+ * // remove item from list
+ * ssl_del(bar_base, q, bar_next) ;
+ *
+ * // walk a list
+ * struct item* t = ssl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = ssl_next(t, bar_next) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (ssl_pop(&t, bar_base, bar_next) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = ssl_head(bar_base) != NULL)
+ * {
+ * ....
+ * ssl_del_head(bar_base, bar_next) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct item* bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * ssl_push(parent->bar_base, item, bar_next) ;
+ * ....
+ * }
+ */
+
+#define ssl_init(base) \
+ ((base) = NULL)
+
+#define ssl_push(base, item, next) \
+ do { confirm(_lu_off(base, next) == _lu_off(item, next)) ; \
+ (item)->next = (base) ; \
+ (base) = item ; \
+ } while (0)
+
+extern int ssl_del_func(void** p_this, void* obj, size_t link_offset) ;
+
+#define ssl_del(base, item, next) \
+ ssl_del_func((void**)&(base), item, _lu_off(base, next))
+
+#define ssl_del_head(base, next) \
+ do { if ((base) != NULL) \
+ (base) = (base)->next ; \
+ } while (0)
+
+#define ssl_pop(dst, base, next) \
+ ((*(dst) = (base)) != NULL ? ((base) = (base)->next, *(dst)) : NULL)
+
+#define ssl_head(base) (base)
+
+#define ssl_next(item, next) \
+ ((item) != NULL ? (item)->next : NULL)
+
+/* _sl_p_next(item, off) -- pointer to next pointer at given offset
+ * _sl_next(item, off) -- contents of next pointer at given offset
+ */
+
+#define _sl_p_next(item, off) \
+ ( (void**)( (char*)(item) + (off) ) )
+
+#define _sl_next(item, off) \
+ *_sl_p_next(item, off)
+
+/*==============================================================================
+ * Single Base, Double Link
+ *
+ * Can delete entry directly.
+ *
+ * Supports:
+ *
+ * sdl_init(base) -- initialise base
+ *
+ * An empty list has a NULL base.
+ *
+ * sdl_push(base, item, list) -- add at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * sdl_del(base, item, list) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * sdl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * sdl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * sdl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * sdl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * sdl_prev(item, next) -- step to prev item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that sdl_del() and sdl_pop() do NOT affect the item->list.next
+ * or item->list.prev pointers.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "list" to be the name of a field in struct item
+ * of type: struct list_pair(struct item*)
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct list_pair(struct item*) bar_list ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * sdl_push(bar_base, q, bar_list) ;
+ *
+ * // remove item from list
+ * sdl_del(bar_base, q, bar_list) ;
+ *
+ * // walk a list
+ * struct item* t = sdl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = sdl_next(t, bar_list) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (sdl_pop(&t, bar_base, bar_list) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = sdl_head(bar_base) != NULL)
+ * {
+ * ....
+ * sdl_del_head(bar_base, bar_list) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * sdl_push(parent->bar_base, item, bar_list) ;
+ * ....
+ * }
+ */
+
+#define sdl_init(base) \
+ ((base) = NULL)
+
+#define sdl_push(base, item, list) \
+ do { confirm(_lu_off(base, list.next) == _lu_off(item, list.next)) ; \
+ confirm(_lu_off(base, list.prev) == _lu_off(item, list.prev)) ; \
+ (item)->list.next = (base) ; \
+ (item)->list.prev = NULL ; \
+ if ((base) != NULL) \
+ (base)->list.prev = (item) ; \
+ (base) = (item) ; \
+ } while (0)
+
+#define sdl_del(base, item, list) \
+ do { confirm(_lu_off(base, list.next) == _lu_off(item, list.next)) ; \
+ confirm(_lu_off(base, list.prev) == _lu_off(item, list.prev)) ; \
+ if ((item) != NULL) \
+ { \
+ if ((item)->list.next != NULL) \
+ (item)->list.next->list.prev = (item)->list.prev ; \
+ if ((item)->list.prev != NULL) \
+ (item)->list.prev->list.next = (item)->list.next ; \
+ else \
+ (base) = (item)->list.next ; \
+ } ; \
+ } while (0)
+
+#define sdl_del_head(base, list) \
+ do { if ((base) != NULL) \
+ { \
+ (base) = (base)->list.next ; \
+ if ((base) != NULL) \
+ (base)->list.prev = NULL ; \
+ } \
+ } while (0)
+
+#define sdl_pop(dst, base, list) \
+ ((*(dst) = (base)) != NULL \
+ ? ( ((base) = (base)->list.next) != NULL \
+ ? ( (base)->list.prev = NULL, *(dst) ) : *(dst) ) : NULL)
+
+#define sdl_head(base) (base)
+
+#define sdl_next(item, list) \
+ ((item) != NULL ? (item)->list.next : NULL)
+
+#define sdl_prev(item, list) \
+ ((item) != NULL ? (item)->list.prev : NULL)
+
+/* _dl_p_next(obj, off) -- pointer to next pointer at given offset
+ * _dl_next(obj, off) -- contents of next pointer at given offset
+ * _dl_p_prev(obj, off) -- pointer to prev pointer at given offset
+ * _dl_prev(obj, off) -- contents of prev pointer at given offset
+ */
+#define _dl_p_next(obj, off) \
+ ( (void**)( (char*)(obj) + (off) + 0 ) )
+
+#define _dl_next(obj, off) \
+ *_dl_p_next(obj, off)
+
+#define _dl_p_prev(obj, off) \
+ ( (void**)( (char*)(obj) + (off) _ sizeof(void*) ) )
+
+#define _dl_prev(obj, off) \
+ *_dl_p_next(obj, off)
+
+/*==============================================================================
+ * Double Base, Double Link
+ *
+ * Can delete entry directly. Can insert and remove at tail.
+ *
+ * Supports:
+ *
+ * ddl_init(base) -- initialise base
+ *
+ * An empty list has *both* head and tail pointers NULL.
+ *
+ * NB: confusion will arise if only one of these pointers is NULL.
+ *
+ * ddl_push(base, item, list) -- insert at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ddl_append(base, item, list) -- insert at tail of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ddl_in_after(after, base, item, list) -- insert after
+ *
+ * Treat as void function. The after & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * after is not on the list.
+ *
+ * ddl_in_before(before, base, item, list) -- insert before
+ *
+ * Treat as void function. The before & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * before is not on the list.
+ *
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ddl_del(base, item, list) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * ddl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ddl_del_tail(base, next) -- delete tail of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ddl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * ddl_tail(base) -- return tail of list
+ *
+ * Treat as function returning void*.
+ *
+ * ddl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that ddl_del() and ddl_pop() do NOT affect the item->list.next
+ * or item->list.prev pointers.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "list" to be the name of a field in struct item
+ * of type: struct list_pair(struct item*)
+ *
+ *
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct list_pair(struct item*) bar_list ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * ddl_push(bar_base, q, bar_list) ;
+ *
+ * // remove item from list
+ * ddl_del(bar_base, q, bar_list) ;
+ *
+ * // walk a list
+ * struct item* t = ddl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = ddl_next(t, bar_list) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (ddl_pop(&t, bar_base, bar_list) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = ddl_head(bar_base) != NULL)
+ * {
+ * ....
+ * ddl_del_head(bar_base, bar_list) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * ddl_push(parent->bar_base, item, bar_list) ;
+ * ....
+ * }
+ */
+
+#define ddl_init(base) \
+ ((base).head = (base).tail = NULL)
+
+#define ddl_push(base, item, list) \
+ do { (item)->list.next = (base).head ; \
+ (item)->list.prev = NULL ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (base).head = (item) ; \
+ } while (0)
+
+#define ddl_append(base, item, list) \
+ do { (item)->list.next = NULL ; \
+ (item)->list.prev = (base).tail ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (base).tail = (item) ; \
+ } while (0)
+
+#define ddl_in_after(after, base, item, list) \
+ do { (item)->list.next = (after)->list.next ; \
+ (item)->list.prev = (after) ; \
+ if ((after)->list.next != NULL) \
+ (after)->list.next->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (after)->list.next = (item) ; \
+ } while (0)
+
+#define ddl_in_before(before, base, item, list) \
+ do { (item)->list.next = (before) ; \
+ (item)->list.prev = (before)->list.prev ; \
+ if ((before)->list.prev != NULL) \
+ (before)->list.prev->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (before)->list.prev = (item) ; \
+ } while (0)
+
+#define ddl_del(base, item, list) \
+ do { if ((item) != NULL) \
+ { \
+ if ((item)->list.next != NULL) \
+ (item)->list.next->list.prev = (item)->list.prev ; \
+ else \
+ (base).tail = (item)->list.prev ; \
+ if ((item)->list.prev != NULL) \
+ (item)->list.prev->list.next = (item)->list.next ; \
+ else \
+ (base).head = (item)->list.next ; \
+ } ; \
+ } while (0)
+
+#define ddl_del_head(base, list) \
+ do { if ((base).head != NULL) \
+ { \
+ (base).head = (base).head->list.next ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = NULL ; \
+ else \
+ (base).tail = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_del_tail(base, list) \
+ do { if ((base).tail != NULL) \
+ { \
+ (base).tail = (base).tail->list.prev ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = NULL ; \
+ else \
+ (base).head = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_pop(dst, base, list) \
+ ((*(dst) = (base).head) != NULL \
+ ? ( ((base).head = (base).head->list.next) != NULL \
+ ? ( (base).head->list.prev = NULL, *(dst) ) \
+ : ( (base).tail = NULL, *(dst) ) ) \
+ : NULL)
+
+#define ddl_crop(dst, base, list) \
+ ((*(dst) = (base).tail) != NULL \
+ ? ( ((base).tail = (base).tail->list.prev) != NULL \
+ ? ( (base).tail->list.next = NULL, *(dst) ) \
+ : ( (base).head = NULL, *(dst) ) ) \
+ : NULL)
+
+#define ddl_head(base) ((base).head)
+
+#define ddl_tail(base) ((base).tail)
+
+#define ddl_next(item, list) \
+ ((item) != NULL ? (item)->list.next : NULL)
+
+#define ddl_prev(item, list) \
+ ((item) != NULL ? (item)->list.prev : NULL)
+
+#endif /* _ZEBRA_LIST_UTIL_H */