forked from Mirror/frr
lib: add _last
and _prev
on typesafe RB/DLIST
RB-tree and double-linked-list easily support backwards iteration, and an use case seems to have popped up. Let's make it accessible. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
parent
9d888674b2
commit
643ea83be2
|
@ -28,6 +28,9 @@ ForEachMacros:
|
||||||
- frr_each
|
- frr_each
|
||||||
- frr_each_safe
|
- frr_each_safe
|
||||||
- frr_each_from
|
- frr_each_from
|
||||||
|
- frr_rev_each
|
||||||
|
- frr_rev_each_safe
|
||||||
|
- frr_rev_each_from
|
||||||
- frr_with_mutex
|
- frr_with_mutex
|
||||||
- frr_with_privs
|
- frr_with_privs
|
||||||
- LIST_FOREACH
|
- LIST_FOREACH
|
||||||
|
|
|
@ -100,35 +100,39 @@ Available types:
|
||||||
|
|
||||||
Functions provided:
|
Functions provided:
|
||||||
|
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ |
|
| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ |
|
||||||
+====================================+======+======+======+=========+============+
|
+====================================+=======+======+======+=========+============+
|
||||||
| _init, _fini | yes | yes | yes | yes | yes |
|
| _init, _fini | yes | yes | yes | yes | yes |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _first, _next, _next_safe, | yes | yes | yes | yes | yes |
|
| _first, _next, _next_safe, | yes | yes | yes | yes | yes |
|
||||||
| | | | | | |
|
| | | | | | |
|
||||||
| _const_first, _const_next | | | | | |
|
| _const_first, _const_next | | | | | |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _swap_all | yes | yes | yes | yes | yes |
|
| _last, _prev, _prev_safe, | DLIST | -- | -- | RB only | RB only |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
| | only | | | | |
|
||||||
| _anywhere | yes | -- | -- | -- | -- |
|
| _const_last, _const_prev | | | | | |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- |
|
| _swap_all | yes | yes | yes | yes | yes |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _add | -- | yes | yes | yes | yes |
|
| _anywhere | yes | -- | -- | -- | -- |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _member | yes | yes | yes | yes | yes |
|
| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _del, _pop | yes | yes | yes | yes | yes |
|
| _add | -- | yes | yes | yes | yes |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _find, _const_find | -- | -- | yes | yes | -- |
|
| _member | yes | yes | yes | yes | yes |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _find_lt, _find_gteq, | -- | -- | -- | yes | yes |
|
| _del, _pop | yes | yes | yes | yes | yes |
|
||||||
| | | | | | |
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| _const_find_lt, _const_find_gteq | | | | | |
|
| _find, _const_find | -- | -- | yes | yes | -- |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
| use with frr_each() macros | yes | yes | yes | yes | yes |
|
| _find_lt, _find_gteq, | -- | -- | -- | yes | yes |
|
||||||
+------------------------------------+------+------+------+---------+------------+
|
| | | | | | |
|
||||||
|
| _const_find_lt, _const_find_gteq | | | | | |
|
||||||
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
|
| use with frr_each() macros | yes | yes | yes | yes | yes |
|
||||||
|
+------------------------------------+-------+------+------+---------+------------+
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,6 +240,13 @@ The following iteration macros work across all data structures:
|
||||||
resume iteration after breaking out of the loop by keeping the ``from``
|
resume iteration after breaking out of the loop by keeping the ``from``
|
||||||
value persistent and reusing it for the next loop.
|
value persistent and reusing it for the next loop.
|
||||||
|
|
||||||
|
.. c:macro:: frr_rev_each(Z, head, item)
|
||||||
|
.. c:macro:: frr_rev_each_safe(Z, head, item)
|
||||||
|
.. c:macro:: frr_rev_each_from(Z, head, item, from)
|
||||||
|
|
||||||
|
Reverse direction variants of the above. Only supported on containers that
|
||||||
|
implement ``_last`` and ``_prev`` (i.e. ``RBTREE`` and ``DLIST``).
|
||||||
|
|
||||||
To iterate over ``const`` pointers, add ``_const`` to the name of the
|
To iterate over ``const`` pointers, add ``_const`` to the name of the
|
||||||
datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes
|
datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes
|
||||||
``frr_each (mylist_const, head, item)``.
|
``frr_each (mylist_const, head, item)``.
|
||||||
|
@ -291,6 +302,12 @@ The following documentation assumes that a list has been defined using
|
||||||
empty. This is O(1) for all data structures except red-black trees
|
empty. This is O(1) for all data structures except red-black trees
|
||||||
where it is O(log n).
|
where it is O(log n).
|
||||||
|
|
||||||
|
.. c:function:: const itemtype *Z_const_last(const struct Z_head *)
|
||||||
|
.. c:function:: itemtype *Z_last(struct Z_head *)
|
||||||
|
|
||||||
|
Last item in the structure, or ``NULL``. Only available on containers
|
||||||
|
that support reverse iteration (i.e. ``RBTREE`` and ``DLIST``).
|
||||||
|
|
||||||
.. c:function:: itemtype *Z_pop(struct Z_head *)
|
.. c:function:: itemtype *Z_pop(struct Z_head *)
|
||||||
|
|
||||||
Remove and return the first item in the structure, or ``NULL`` if the
|
Remove and return the first item in the structure, or ``NULL`` if the
|
||||||
|
@ -329,6 +346,13 @@ The following documentation assumes that a list has been defined using
|
||||||
Same as :c:func:`Z_next()`, except that ``NULL`` is returned if
|
Same as :c:func:`Z_next()`, except that ``NULL`` is returned if
|
||||||
``prev`` is ``NULL``.
|
``prev`` is ``NULL``.
|
||||||
|
|
||||||
|
.. c:function:: const itemtype *Z_const_prev(const struct Z_head *, const itemtype *next)
|
||||||
|
.. c:function:: itemtype *Z_prev(struct Z_head *, itemtype *next)
|
||||||
|
.. c:function:: itemtype *Z_prev_safe(struct Z_head *, itemtype *next)
|
||||||
|
|
||||||
|
As above, but preceding item. Only available on structures that support
|
||||||
|
reverse iteration (i.e. ``RBTREE`` and ``DLIST``).
|
||||||
|
|
||||||
.. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item)
|
.. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item)
|
||||||
|
|
||||||
Remove ``item`` from the list and return it.
|
Remove ``item`` from the list and return it.
|
||||||
|
|
35
lib/typerb.c
35
lib/typerb.c
|
@ -468,6 +468,28 @@ struct rb_entry *typed_rb_next(const struct rb_entry *rbe_const)
|
||||||
return rbe;
|
return rbe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct rb_entry *typed_rb_prev(const struct rb_entry *rbe_const)
|
||||||
|
{
|
||||||
|
struct rb_entry *rbe = (struct rb_entry *)rbe_const;
|
||||||
|
|
||||||
|
if (RBE_LEFT(rbe)) {
|
||||||
|
rbe = RBE_LEFT(rbe);
|
||||||
|
while (RBE_RIGHT(rbe))
|
||||||
|
rbe = RBE_RIGHT(rbe);
|
||||||
|
} else {
|
||||||
|
if (RBE_PARENT(rbe) && (rbe == RBE_RIGHT(RBE_PARENT(rbe))))
|
||||||
|
rbe = RBE_PARENT(rbe);
|
||||||
|
else {
|
||||||
|
while (RBE_PARENT(rbe)
|
||||||
|
&& (rbe == RBE_LEFT(RBE_PARENT(rbe))))
|
||||||
|
rbe = RBE_PARENT(rbe);
|
||||||
|
rbe = RBE_PARENT(rbe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rbe;
|
||||||
|
}
|
||||||
|
|
||||||
struct rb_entry *typed_rb_min(const struct rbt_tree *rbt)
|
struct rb_entry *typed_rb_min(const struct rbt_tree *rbt)
|
||||||
{
|
{
|
||||||
struct rb_entry *rbe = RBH_ROOT(rbt);
|
struct rb_entry *rbe = RBH_ROOT(rbt);
|
||||||
|
@ -481,6 +503,19 @@ struct rb_entry *typed_rb_min(const struct rbt_tree *rbt)
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct rb_entry *typed_rb_max(const struct rbt_tree *rbt)
|
||||||
|
{
|
||||||
|
struct rb_entry *rbe = RBH_ROOT(rbt);
|
||||||
|
struct rb_entry *parent = NULL;
|
||||||
|
|
||||||
|
while (rbe != NULL) {
|
||||||
|
parent = rbe;
|
||||||
|
rbe = RBE_RIGHT(rbe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
bool typed_rb_member(const struct typed_rb_root *rbt,
|
bool typed_rb_member(const struct typed_rb_root *rbt,
|
||||||
const struct typed_rb_entry *rbe)
|
const struct typed_rb_entry *rbe)
|
||||||
{
|
{
|
||||||
|
|
22
lib/typerb.h
22
lib/typerb.h
|
@ -62,6 +62,8 @@ const struct typed_rb_entry *typed_rb_find_lt(const struct typed_rb_root *rbt,
|
||||||
const struct typed_rb_entry *a,
|
const struct typed_rb_entry *a,
|
||||||
const struct typed_rb_entry *b));
|
const struct typed_rb_entry *b));
|
||||||
struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt);
|
struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt);
|
||||||
|
struct typed_rb_entry *typed_rb_max(const struct typed_rb_root *rbt);
|
||||||
|
struct typed_rb_entry *typed_rb_prev(const struct typed_rb_entry *rbe);
|
||||||
struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe);
|
struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe);
|
||||||
bool typed_rb_member(const struct typed_rb_root *rbt,
|
bool typed_rb_member(const struct typed_rb_root *rbt,
|
||||||
const struct typed_rb_entry *rbe);
|
const struct typed_rb_entry *rbe);
|
||||||
|
@ -135,12 +137,32 @@ macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
|
||||||
return container_of_null(re, type, field.re); \
|
return container_of_null(re, type, field.re); \
|
||||||
} \
|
} \
|
||||||
TYPESAFE_FIRST_NEXT(prefix, type) \
|
TYPESAFE_FIRST_NEXT(prefix, type) \
|
||||||
|
macro_pure const type *prefix ## _const_last(const struct prefix##_head *h) \
|
||||||
|
{ \
|
||||||
|
const struct typed_rb_entry *re; \
|
||||||
|
re = typed_rb_max(&h->rr); \
|
||||||
|
return container_of_null(re, type, field.re); \
|
||||||
|
} \
|
||||||
|
macro_pure const type *prefix ## _const_prev(const struct prefix##_head *h, \
|
||||||
|
const type *item) \
|
||||||
|
{ \
|
||||||
|
const struct typed_rb_entry *re; \
|
||||||
|
re = typed_rb_prev(&item->field.re); \
|
||||||
|
return container_of_null(re, type, field.re); \
|
||||||
|
} \
|
||||||
|
TYPESAFE_LAST_PREV(prefix, type) \
|
||||||
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
|
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
|
||||||
{ \
|
{ \
|
||||||
struct typed_rb_entry *re; \
|
struct typed_rb_entry *re; \
|
||||||
re = item ? typed_rb_next(&item->field.re) : NULL; \
|
re = item ? typed_rb_next(&item->field.re) : NULL; \
|
||||||
return container_of_null(re, type, field.re); \
|
return container_of_null(re, type, field.re); \
|
||||||
} \
|
} \
|
||||||
|
macro_pure type *prefix ## _prev_safe(struct prefix##_head *h, type *item) \
|
||||||
|
{ \
|
||||||
|
struct typed_rb_entry *re; \
|
||||||
|
re = item ? typed_rb_prev(&item->field.re) : NULL; \
|
||||||
|
return container_of_null(re, type, field.re); \
|
||||||
|
} \
|
||||||
macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
|
macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
|
||||||
{ \
|
{ \
|
||||||
return h->rr.count; \
|
return h->rr.count; \
|
||||||
|
|
|
@ -43,6 +43,22 @@ extern "C" {
|
||||||
item; \
|
item; \
|
||||||
item = from, from = prefix##_next_safe(head, from))
|
item = from, from = prefix##_next_safe(head, from))
|
||||||
|
|
||||||
|
/* reverse direction, only supported by a few containers */
|
||||||
|
|
||||||
|
#define frr_rev_each(prefix, head, item) \
|
||||||
|
for (item = prefix##_last(head); item; \
|
||||||
|
item = prefix##_prev(head, item))
|
||||||
|
#define frr_rev_each_safe(prefix, head, item) \
|
||||||
|
for (typeof(prefix##_prev_safe(head, NULL)) prefix##_safe = \
|
||||||
|
prefix##_prev_safe(head, \
|
||||||
|
(item = prefix##_last(head))); \
|
||||||
|
item; \
|
||||||
|
item = prefix##_safe, \
|
||||||
|
prefix##_safe = prefix##_prev_safe(head, prefix##_safe))
|
||||||
|
#define frr_rev_each_from(prefix, head, item, from) \
|
||||||
|
for (item = from, from = prefix##_prev_safe(head, item); \
|
||||||
|
item; \
|
||||||
|
item = from, from = prefix##_prev_safe(head, from))
|
||||||
|
|
||||||
/* non-const variants. these wrappers are the same for all the types, so
|
/* non-const variants. these wrappers are the same for all the types, so
|
||||||
* bundle them together here.
|
* bundle them together here.
|
||||||
|
@ -57,6 +73,16 @@ macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
|
||||||
return (type *)prefix ## _const_next(h, item); \
|
return (type *)prefix ## _const_next(h, item); \
|
||||||
} \
|
} \
|
||||||
/* ... */
|
/* ... */
|
||||||
|
#define TYPESAFE_LAST_PREV(prefix, type) \
|
||||||
|
macro_pure type *prefix ## _last(struct prefix##_head *h) \
|
||||||
|
{ \
|
||||||
|
return (type *)prefix ## _const_last(h); \
|
||||||
|
} \
|
||||||
|
macro_pure type *prefix ## _prev(struct prefix##_head *h, type *item) \
|
||||||
|
{ \
|
||||||
|
return (type *)prefix ## _const_prev(h, item); \
|
||||||
|
} \
|
||||||
|
/* ... */
|
||||||
#define TYPESAFE_FIND(prefix, type) \
|
#define TYPESAFE_FIND(prefix, type) \
|
||||||
macro_inline type *prefix ## _find(struct prefix##_head *h, \
|
macro_inline type *prefix ## _find(struct prefix##_head *h, \
|
||||||
const type *item) \
|
const type *item) \
|
||||||
|
@ -398,12 +424,34 @@ macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
|
||||||
return container_of(ditem->next, type, field.di); \
|
return container_of(ditem->next, type, field.di); \
|
||||||
} \
|
} \
|
||||||
TYPESAFE_FIRST_NEXT(prefix, type) \
|
TYPESAFE_FIRST_NEXT(prefix, type) \
|
||||||
|
macro_pure const type *prefix ## _const_last(const struct prefix##_head *h) \
|
||||||
|
{ \
|
||||||
|
const struct dlist_item *ditem = h->dh.hitem.prev; \
|
||||||
|
if (ditem == &h->dh.hitem) \
|
||||||
|
return NULL; \
|
||||||
|
return container_of(ditem, type, field.di); \
|
||||||
|
} \
|
||||||
|
macro_pure const type *prefix ## _const_prev(const struct prefix##_head *h, \
|
||||||
|
const type *item) \
|
||||||
|
{ \
|
||||||
|
const struct dlist_item *ditem = &item->field.di; \
|
||||||
|
if (ditem->prev == &h->dh.hitem) \
|
||||||
|
return NULL; \
|
||||||
|
return container_of(ditem->prev, type, field.di); \
|
||||||
|
} \
|
||||||
|
TYPESAFE_LAST_PREV(prefix, type) \
|
||||||
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
|
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
|
||||||
{ \
|
{ \
|
||||||
if (!item) \
|
if (!item) \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
return prefix ## _next(h, item); \
|
return prefix ## _next(h, item); \
|
||||||
} \
|
} \
|
||||||
|
macro_pure type *prefix ## _prev_safe(struct prefix##_head *h, type *item) \
|
||||||
|
{ \
|
||||||
|
if (!item) \
|
||||||
|
return NULL; \
|
||||||
|
return prefix ## _prev(h, item); \
|
||||||
|
} \
|
||||||
macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
|
macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
|
||||||
{ \
|
{ \
|
||||||
return h->dh.count; \
|
return h->dh.count; \
|
||||||
|
|
|
@ -58,9 +58,10 @@
|
||||||
#define T_HASH (1 << 2)
|
#define T_HASH (1 << 2)
|
||||||
#define T_HEAP (1 << 3)
|
#define T_HEAP (1 << 3)
|
||||||
#define T_ATOMIC (1 << 4)
|
#define T_ATOMIC (1 << 4)
|
||||||
|
#define T_REVERSE (1 << 5)
|
||||||
|
|
||||||
#define _T_LIST (0)
|
#define _T_LIST (0)
|
||||||
#define _T_DLIST (0)
|
#define _T_DLIST (0 | T_REVERSE)
|
||||||
#define _T_ATOMLIST (0 | T_ATOMIC)
|
#define _T_ATOMLIST (0 | T_ATOMIC)
|
||||||
#define _T_HEAP (T_SORTED | T_HEAP)
|
#define _T_HEAP (T_SORTED | T_HEAP)
|
||||||
#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ)
|
#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ)
|
||||||
|
@ -68,8 +69,8 @@
|
||||||
#define _T_HASH (T_SORTED | T_UNIQ | T_HASH)
|
#define _T_HASH (T_SORTED | T_UNIQ | T_HASH)
|
||||||
#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ)
|
#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ)
|
||||||
#define _T_SKIPLIST_NONUNIQ (T_SORTED)
|
#define _T_SKIPLIST_NONUNIQ (T_SORTED)
|
||||||
#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ)
|
#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ | T_REVERSE)
|
||||||
#define _T_RBTREE_NONUNIQ (T_SORTED)
|
#define _T_RBTREE_NONUNIQ (T_SORTED | T_REVERSE)
|
||||||
#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC)
|
#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC)
|
||||||
#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC)
|
#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC)
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@
|
||||||
#define IS_HASH(type) (_T_TYPE(type) & T_HASH)
|
#define IS_HASH(type) (_T_TYPE(type) & T_HASH)
|
||||||
#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP)
|
#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP)
|
||||||
#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC)
|
#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC)
|
||||||
|
#define IS_REVERSE(type) (_T_TYPE(type) & T_REVERSE)
|
||||||
|
|
||||||
static struct timeval ref, ref0;
|
static struct timeval ref, ref0;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,11 @@
|
||||||
#define list_const_next concat(TYPE, _const_next)
|
#define list_const_next concat(TYPE, _const_next)
|
||||||
#define list_next concat(TYPE, _next)
|
#define list_next concat(TYPE, _next)
|
||||||
#define list_next_safe concat(TYPE, _next_safe)
|
#define list_next_safe concat(TYPE, _next_safe)
|
||||||
|
#define list_const_last concat(TYPE, _const_last)
|
||||||
|
#define list_last concat(TYPE, _last)
|
||||||
|
#define list_const_prev concat(TYPE, _const_prev)
|
||||||
|
#define list_prev concat(TYPE, _prev)
|
||||||
|
#define list_prev_safe concat(TYPE, _prev_safe)
|
||||||
#define list_count concat(TYPE, _count)
|
#define list_count concat(TYPE, _count)
|
||||||
#define list_add concat(TYPE, _add)
|
#define list_add concat(TYPE, _add)
|
||||||
#define list_add_head concat(TYPE, _add_head)
|
#define list_add_head concat(TYPE, _add_head)
|
||||||
|
@ -171,6 +176,9 @@ static void concat(test_, TYPE)(void)
|
||||||
|
|
||||||
list_init(&head);
|
list_init(&head);
|
||||||
assert(list_first(&head) == NULL);
|
assert(list_first(&head) == NULL);
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
assert(list_last(&head) == NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
|
ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
|
||||||
|
|
||||||
|
@ -203,6 +211,10 @@ static void concat(test_, TYPE)(void)
|
||||||
assert(!list_first(&head));
|
assert(!list_first(&head));
|
||||||
assert(list_count(&other) == k);
|
assert(list_count(&other) == k);
|
||||||
assert(list_first(&other) != NULL);
|
assert(list_first(&other) != NULL);
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
assert(!list_last(&head));
|
||||||
|
assert(list_last(&other) != NULL);
|
||||||
|
#endif
|
||||||
ts_hash_headx(
|
ts_hash_headx(
|
||||||
&other, "swap1",
|
&other, "swap1",
|
||||||
"a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
|
"a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
|
||||||
|
@ -269,13 +281,36 @@ static void concat(test_, TYPE)(void)
|
||||||
(void)cprev;
|
(void)cprev;
|
||||||
#else
|
#else
|
||||||
assert(!cprev || cprev->val < citem->val);
|
assert(!cprev || cprev->val < citem->val);
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
assert(list_const_prev(chead, citem) == cprev);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
cprev = citem;
|
cprev = citem;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
assert(list_count(chead) == k);
|
assert(list_count(chead) == k);
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
assert(cprev == list_const_last(chead));
|
||||||
|
#endif
|
||||||
ts_ref("walk");
|
ts_ref("walk");
|
||||||
|
|
||||||
|
#if IS_REVERSE(REALTYPE) && !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE)
|
||||||
|
cprev = NULL;
|
||||||
|
k = 0;
|
||||||
|
|
||||||
|
frr_rev_each(list_const, chead, citem) {
|
||||||
|
assert(!cprev || cprev->val > citem->val);
|
||||||
|
assert(list_const_next(chead, citem) == cprev);
|
||||||
|
|
||||||
|
cprev = citem;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
assert(list_count(chead) == k);
|
||||||
|
assert(cprev == list_const_first(chead));
|
||||||
|
|
||||||
|
ts_ref("reverse-walk");
|
||||||
|
#endif
|
||||||
|
|
||||||
#if IS_UNIQ(REALTYPE)
|
#if IS_UNIQ(REALTYPE)
|
||||||
prng_free(prng);
|
prng_free(prng);
|
||||||
prng = prng_new(0);
|
prng = prng_new(0);
|
||||||
|
@ -439,6 +474,9 @@ static void concat(test_, TYPE)(void)
|
||||||
}
|
}
|
||||||
assert(list_count(&head) == k);
|
assert(list_count(&head) == k);
|
||||||
assert(list_first(&head) != NULL);
|
assert(list_first(&head) != NULL);
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
assert(list_last(&head) != NULL);
|
||||||
|
#endif
|
||||||
ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
|
ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
|
||||||
|
|
||||||
#if !IS_ATOMIC(REALTYPE)
|
#if !IS_ATOMIC(REALTYPE)
|
||||||
|
@ -451,6 +489,10 @@ static void concat(test_, TYPE)(void)
|
||||||
assert(!list_first(&head));
|
assert(!list_first(&head));
|
||||||
assert(list_count(&other) == k);
|
assert(list_count(&other) == k);
|
||||||
assert(list_first(&other) != NULL);
|
assert(list_first(&other) != NULL);
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
assert(!list_last(&head));
|
||||||
|
assert(list_last(&other) != NULL);
|
||||||
|
#endif
|
||||||
ts_hash_head(
|
ts_hash_head(
|
||||||
&other, "swap1",
|
&other, "swap1",
|
||||||
"eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
|
"eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
|
||||||
|
@ -534,6 +576,21 @@ static void concat(test_, TYPE)(void)
|
||||||
}
|
}
|
||||||
ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
|
ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
|
||||||
#endif
|
#endif
|
||||||
|
#if IS_REVERSE(REALTYPE)
|
||||||
|
i = 0;
|
||||||
|
prev = NULL;
|
||||||
|
|
||||||
|
frr_rev_each (list, &head, item) {
|
||||||
|
assert(item->scratchpad != 0);
|
||||||
|
assert(list_next(&head, item) == prev);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
prev = item;
|
||||||
|
}
|
||||||
|
assert(list_first(&head) == prev);
|
||||||
|
assert(list_count(&head) == i);
|
||||||
|
ts_hash("reverse-walk", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
|
||||||
|
#endif
|
||||||
|
|
||||||
while ((item = list_pop(&head))) {
|
while ((item = list_pop(&head))) {
|
||||||
assert(item->scratchpad != 0);
|
assert(item->scratchpad != 0);
|
||||||
|
@ -746,6 +803,13 @@ static void concat(test_, TYPE)(void)
|
||||||
#undef list_first
|
#undef list_first
|
||||||
#undef list_next
|
#undef list_next
|
||||||
#undef list_next_safe
|
#undef list_next_safe
|
||||||
|
#undef list_const_first
|
||||||
|
#undef list_const_next
|
||||||
|
#undef list_last
|
||||||
|
#undef list_prev
|
||||||
|
#undef list_prev_safe
|
||||||
|
#undef list_const_last
|
||||||
|
#undef list_const_prev
|
||||||
#undef list_count
|
#undef list_count
|
||||||
#undef list_add
|
#undef list_add
|
||||||
#undef list_add_head
|
#undef list_add_head
|
||||||
|
|
Loading…
Reference in a new issue