lib: add const iteration & find to typesafe lists

Based on work originally by Mark Stapp <mjs@voltanet.io>.

Make it possible to iterate the typesafe lists in a const
context, as well as find items from them.

Signed-off-by: Mark Stapp <mjs@voltanet.io>
[above signoff was for the original version before modification]
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
David Lamparter 2020-05-04 21:36:03 +02:00
parent 15e9c561b2
commit daf3441d2b
6 changed files with 181 additions and 100 deletions

View file

@ -105,7 +105,8 @@ Functions provided:
+====================================+======+======+======+=========+============+
| _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 | | | | | |
+------------------------------------+------+------+------+---------+------------+
| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- |
+------------------------------------+------+------+------+---------+------------+
@ -113,9 +114,10 @@ Functions provided:
+------------------------------------+------+------+------+---------+------------+
| _del, _pop | yes | yes | yes | yes | yes |
+------------------------------------+------+------+------+---------+------------+
| _find | -- | -- | yes | yes | -- |
| _find, _const_find | -- | -- | yes | yes | -- |
+------------------------------------+------+------+------+---------+------------+
| _find_lt, _find_gteq | -- | -- | -- | yes | yes |
| _find_lt, _find_gteq, | -- | -- | -- | yes | yes |
| _const_find_lt, _const_find_gteq | | | | | |
+------------------------------------+------+------+------+---------+------------+
| use with frr_each() macros | yes | yes | yes | yes | yes |
+------------------------------------+------+------+------+---------+------------+
@ -226,6 +228,10 @@ The following iteration macros work across all data structures:
resume iteration after breaking out of the loop by keeping the ``from``
value persistent and reusing it for the next loop.
To iterate over ``const`` pointers, add ``_const`` to the name of the
datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes
``frr_each (mylist_const, head, item)``.
Common API
----------
@ -248,7 +254,7 @@ The following documentation assumes that a list has been defined using
This function may ``assert()`` if the list is not empty.
.. c:function:: size_t Z_count(struct Z_head *)
.. c:function:: size_t Z_count(const struct Z_head *)
Returns the number of items in a structure. All structures store a
counter in their `Z_head` so that calling this function completes
@ -260,6 +266,7 @@ The following documentation assumes that a list has been defined using
outdated by the time this function returns and can therefore only be
used as an estimate.
.. c:function:: const itemtype *Z_const_first(const struct Z_head *)
.. c:function:: itemtype *Z_first(struct Z_head *)
Returns the first item in the structure, or ``NULL`` if the structure is
@ -288,6 +295,7 @@ The following documentation assumes that a list has been defined using
affected by the "modification while iterating" problem. To remove
all items from a hash table, use the loop demonstrated above.
.. c:function:: const itemtype *Z_next(const struct Z_head *, const itemtype *prev)
.. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev)
Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is
@ -421,6 +429,7 @@ sorted lists can be searched for a value.
For ``_NONUNIQ`` lists, this function always returns NULL since ``item``
can always be successfully added to the list.
.. c:function:: const itemtype *Z_find(const struct Z_head *, const itemtype *ref)
.. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref)
Search the list for an item that compares equal to ``ref``. If no equal
@ -442,11 +451,13 @@ sorted lists can be searched for a value.
containing non-unique items, more than one item may compare as equal to
the item that is searched for.
.. c:function:: const itemtype *Z_find_gteq(const struct Z_head *, const itemtype *ref)
.. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref)
Search the list for an item that compares greater or equal to
``ref``. See :c:func:`Z_find()` above.
.. c:function:: const itemtype *Z_find_lt(const struct Z_head *, const itemtype *ref)
.. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref)
Search the list for an item that compares less than
@ -616,21 +627,9 @@ Head removal (pop) and deallocation:
FAQ
---
Why is the list head not ``const`` in the list APIs?
The semantics that a ``const`` list head would imply are not obvious. It
could mean any of the following:
* the list just shouldn't be allocated/deallocated, but may be modified.
This doesn't actually work since the list head needs to be modified for
inserting or deleting items.
* the list shouldn't be modified, but items can. This may make sense for
iterating, but it's not exactly consistent - an item might be on more
than one list, does it apply to all of them? If not, which one?
* neither the list nor the items should be modified. This is consistent,
but hard to do without creating a ``const`` copy of every single list
function. Ease of use trumps this.
What are the semantics of ``const`` in the list APIs?
``const`` pointers to list heads and/or items are interpreted to mean that
both the list itself as well as the data items are read-only.
Why is there no "is this item on a/the list" test?
It's slow for several of the data structures, and the work of adding it

View file

@ -377,12 +377,13 @@ struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt,
}
/* Finds the node with the same key as elm */
struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key,
const struct rb_entry *typed_rb_find(const struct rbt_tree *rbt,
const struct rb_entry *key,
int (*cmpfn)(
const struct typed_rb_entry *a,
const struct typed_rb_entry *b))
{
struct rb_entry *tmp = RBH_ROOT(rbt);
const struct rb_entry *tmp = RBH_ROOT(rbt);
int comp;
while (tmp != NULL) {
@ -398,13 +399,13 @@ struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key,
return NULL;
}
struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt,
const struct rb_entry *typed_rb_find_gteq(const struct rbt_tree *rbt,
const struct rb_entry *key,
int (*cmpfn)(
const struct typed_rb_entry *a,
const struct typed_rb_entry *b))
{
struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
int comp;
while (tmp != NULL) {
@ -421,13 +422,13 @@ struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt,
return best;
}
struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt,
const struct rb_entry *typed_rb_find_lt(const struct rbt_tree *rbt,
const struct rb_entry *key,
int (*cmpfn)(
const struct typed_rb_entry *a,
const struct typed_rb_entry *b))
{
struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
int comp;
while (tmp != NULL) {
@ -443,8 +444,10 @@ struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt,
return best;
}
struct rb_entry *typed_rb_next(struct rb_entry *rbe)
struct rb_entry *typed_rb_next(const struct rb_entry *rbe_const)
{
struct rb_entry *rbe = (struct rb_entry *)rbe_const;
if (RBE_RIGHT(rbe) != NULL) {
rbe = RBE_RIGHT(rbe);
while (RBE_LEFT(rbe) != NULL)
@ -463,7 +466,7 @@ struct rb_entry *typed_rb_next(struct rb_entry *rbe)
return rbe;
}
struct rb_entry *typed_rb_min(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 *parent = NULL;

View file

@ -45,23 +45,23 @@ struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *rbt,
const struct typed_rb_entry *b));
struct typed_rb_entry *typed_rb_remove(struct typed_rb_root *rbt,
struct typed_rb_entry *rbe);
struct typed_rb_entry *typed_rb_find(struct typed_rb_root *rbt,
const struct typed_rb_entry *typed_rb_find(const struct typed_rb_root *rbt,
const struct typed_rb_entry *rbe,
int (*cmpfn)(
const struct typed_rb_entry *a,
const struct typed_rb_entry *b));
struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *rbt,
const struct typed_rb_entry *typed_rb_find_gteq(const struct typed_rb_root *rbt,
const struct typed_rb_entry *rbe,
int (*cmpfn)(
const struct typed_rb_entry *a,
const struct typed_rb_entry *b));
struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *rbt,
const struct typed_rb_entry *typed_rb_find_lt(const struct typed_rb_root *rbt,
const struct typed_rb_entry *rbe,
int (*cmpfn)(
const struct typed_rb_entry *a,
const struct typed_rb_entry *b));
struct typed_rb_entry *typed_rb_min(struct typed_rb_root *rbt);
struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *rbe);
struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt);
struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe);
#define _PREDECL_RBTREE(prefix) \
struct prefix ## _head { struct typed_rb_root rr; }; \
@ -86,20 +86,21 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \
return container_of_null(re, type, field.re); \
} \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
const type *item) \
macro_inline const type *prefix ## _const_find_gteq( \
const struct prefix##_head *h, const type *item) \
{ \
struct typed_rb_entry *re; \
const struct typed_rb_entry *re; \
re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \
return container_of_null(re, type, field.re); \
} \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
const type *item) \
macro_inline const type *prefix ## _const_find_lt( \
const struct prefix##_head *h, const type *item) \
{ \
struct typed_rb_entry *re; \
const struct typed_rb_entry *re; \
re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \
return container_of_null(re, type, field.re); \
} \
TYPESAFE_FIND_CMP(prefix, type) \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \
struct typed_rb_entry *re; \
@ -115,18 +116,20 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
typed_rb_remove(&h->rr, re); \
return container_of(re, type, field.re); \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
struct typed_rb_entry *re; \
const struct typed_rb_entry *re; \
re = typed_rb_min(&h->rr); \
return container_of_null(re, type, field.re); \
} \
macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
struct typed_rb_entry *re; \
const struct typed_rb_entry *re; \
re = typed_rb_next(&item->field.re); \
return container_of_null(re, type, field.re); \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
struct typed_rb_entry *re; \
@ -149,12 +152,14 @@ macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \
return cmpfn(container_of(a, type, field.re), \
container_of(b, type, field.re)); \
} \
macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \
const type *item) \
{ \
struct typed_rb_entry *re; \
const struct typed_rb_entry *re; \
re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \
return container_of_null(re, type, field.re); \
} \
TYPESAFE_FIND(prefix, type) \
\
_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \
/* ... */

View file

@ -158,7 +158,7 @@ void typesafe_hash_shrink(struct thash_head *head)
/* skiplist */
static inline struct sskip_item *sl_level_get(struct sskip_item *item,
static inline struct sskip_item *sl_level_get(const struct sskip_item *item,
size_t level)
{
if (level < SKIPLIST_OVERFLOW)
@ -263,13 +263,14 @@ struct sskip_item *typesafe_skiplist_add(struct sskip_head *head,
/* NOTE: level counting below is 1-based since that makes the code simpler! */
struct sskip_item *typesafe_skiplist_find(struct sskip_head *head,
const struct sskip_item *typesafe_skiplist_find(
const struct sskip_head *head,
const struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b))
{
size_t level = SKIPLIST_MAXDEPTH;
struct sskip_item *prev = &head->hitem, *next;
const struct sskip_item *prev = &head->hitem, *next;
int cmpval;
while (level) {
@ -290,13 +291,14 @@ struct sskip_item *typesafe_skiplist_find(struct sskip_head *head,
return NULL;
}
struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head,
const struct sskip_item *typesafe_skiplist_find_gteq(
const struct sskip_head *head,
const struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b))
{
size_t level = SKIPLIST_MAXDEPTH;
struct sskip_item *prev = &head->hitem, *next;
const struct sskip_item *prev = &head->hitem, *next;
int cmpval;
while (level) {
@ -317,13 +319,14 @@ struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head,
return next;
}
struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head,
const struct sskip_item *typesafe_skiplist_find_lt(
const struct sskip_head *head,
const struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b))
{
size_t level = SKIPLIST_MAXDEPTH;
struct sskip_item *prev = &head->hitem, *next, *best = NULL;
const struct sskip_item *prev = &head->hitem, *next, *best = NULL;
int cmpval;
while (level) {

View file

@ -44,6 +44,41 @@ extern "C" {
item; \
item = from, from = prefix##_next_safe(head, from))
/* non-const variants. these wrappers are the same for all the types, so
* bundle them together here.
*/
#define TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
{ \
return (type *)prefix ## _const_first(h); \
} \
macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
{ \
return (type *)prefix ## _const_next(h, item); \
} \
/* ... */
#define TYPESAFE_FIND(prefix, type) \
macro_inline type *prefix ## _find(struct prefix##_head *h, \
const type *item) \
{ \
return (type *)prefix ## _const_find(h, item); \
} \
/* ... */
#define TYPESAFE_FIND_CMP(prefix, type) \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
const type *item) \
{ \
return (type *)prefix ## _const_find_lt(h, item); \
} \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
const type *item) \
{ \
return (type *)prefix ## _const_find_gteq(h, item); \
} \
/* ... */
/* single-linked list, unsorted/arbitrary.
* can be used as queue with add_tail / pop
*/
@ -133,15 +168,17 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
h->sh.last_next = &h->sh.first; \
return container_of(sitem, type, field.si); \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
return container_of_null(h->sh.first, type, field.si); \
} \
macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
struct slist_item *sitem = &item->field.si; \
const struct slist_item *sitem = &item->field.si; \
return container_of_null(sitem->next, type, field.si); \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
struct slist_item *sitem; \
@ -232,20 +269,22 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
h->dh.count--; \
return container_of(ditem, type, field.di); \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
struct dlist_item *ditem = h->dh.hitem.next; \
const struct dlist_item *ditem = h->dh.hitem.next; \
if (ditem == &h->dh.hitem) \
return NULL; \
return container_of(ditem, type, field.di); \
} \
macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
struct dlist_item *ditem = &item->field.di; \
const struct dlist_item *ditem = &item->field.di; \
if (ditem->next == &h->dh.hitem) \
return NULL; \
return container_of(ditem->next, type, field.di); \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
if (!item) \
@ -338,19 +377,21 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
typesafe_heap_resize(&h->hh, false); \
return container_of(hitem, type, field.hi); \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
if (h->hh.count == 0) \
return NULL; \
return container_of(h->hh.array[0], type, field.hi); \
} \
macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
uint32_t idx = item->field.hi.index + 1; \
if (idx >= h->hh.count) \
return NULL; \
return container_of(h->hh.array[idx], type, field.hi); \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
if (!item) \
@ -431,26 +472,27 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
h->sh.count++; \
return NULL; \
} \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
const type *item) \
macro_inline const type *prefix ## _const_find_gteq( \
const struct prefix##_head *h, const type *item) \
{ \
struct ssort_item *sitem = h->sh.first; \
const struct ssort_item *sitem = h->sh.first; \
int cmpval = 0; \
while (sitem && (cmpval = cmpfn_nuq( \
container_of(sitem, type, field.si), item)) < 0) \
sitem = sitem->next; \
return container_of_null(sitem, type, field.si); \
} \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
const type *item) \
macro_inline const type *prefix ## _const_find_lt( \
const struct prefix##_head *h, const type *item) \
{ \
struct ssort_item *prev = NULL, *sitem = h->sh.first; \
const struct ssort_item *prev = NULL, *sitem = h->sh.first; \
int cmpval = 0; \
while (sitem && (cmpval = cmpfn_nuq( \
container_of(sitem, type, field.si), item)) < 0) \
sitem = (prev = sitem)->next; \
return container_of_null(prev, type, field.si); \
} \
TYPESAFE_FIND_CMP(prefix, type) \
/* TODO: del_hint */ \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \
@ -472,15 +514,17 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
h->sh.first = sitem->next; \
return container_of(sitem, type, field.si); \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
return container_of_null(h->sh.first, type, field.si); \
} \
macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
struct ssort_item *sitem = &item->field.si; \
const struct ssort_item *sitem = &item->field.si; \
return container_of_null(sitem->next, type, field.si); \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
struct ssort_item *sitem; \
@ -497,10 +541,11 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
#define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \
_DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \
\
macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
\
macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \
const type *item) \
{ \
struct ssort_item *sitem = h->sh.first; \
const struct ssort_item *sitem = h->sh.first; \
int cmpval = 0; \
while (sitem && (cmpval = cmpfn( \
container_of(sitem, type, field.si), item)) < 0) \
@ -509,6 +554,7 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
return NULL; \
return container_of(sitem, type, field.si); \
} \
TYPESAFE_FIND(prefix, type) \
/* ... */
#define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \
@ -606,12 +652,13 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
*np = &item->field.hi; \
return NULL; \
} \
macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \
const type *item) \
{ \
if (!h->hh.tabshift) \
return NULL; \
uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \
struct thash_item *hitem = h->hh.entries[hbits]; \
const struct thash_item *hitem = h->hh.entries[hbits]; \
while (hitem && hitem->hashval < hval) \
hitem = hitem->next; \
while (hitem && hitem->hashval == hval) { \
@ -621,6 +668,7 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
} \
return NULL; \
} \
TYPESAFE_FIND(prefix, type) \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \
if (!h->hh.tabshift) \
@ -655,7 +703,7 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
} \
return NULL; \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
uint32_t i; \
for (i = 0; i < HASH_SIZE(h->hh); i++) \
@ -663,17 +711,19 @@ macro_pure type *prefix ## _first(struct prefix##_head *h) \
return container_of(h->hh.entries[i], type, field.hi); \
return NULL; \
} \
macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
struct thash_item *hitem = &item->field.hi; \
const struct thash_item *hitem = &item->field.hi; \
if (hitem->next) \
return container_of(hitem->next, type, field.hi); \
uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \
for (; i < HASH_SIZE(h->hh); i++) \
for (; i < HASH_SIZE(h->hh); i++) \
if (h->hh.entries[i]) \
return container_of(h->hh.entries[i], type, field.hi); \
return NULL; \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
if (!item) \
@ -742,20 +792,21 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \
return container_of_null(si, type, field.si); \
} \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \
const type *item) \
macro_inline const type *prefix ## _const_find_gteq( \
const struct prefix##_head *h, const type *item) \
{ \
struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \
const struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \
&item->field.si, cmpfn_nuq); \
return container_of_null(sitem, type, field.si); \
} \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \
const type *item) \
macro_inline const type *prefix ## _const_find_lt( \
const struct prefix##_head *h, const type *item) \
{ \
struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \
const struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \
&item->field.si, cmpfn_nuq); \
return container_of_null(sitem, type, field.si); \
} \
TYPESAFE_FIND_CMP(prefix, type) \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \
struct sskip_item *sitem = typesafe_skiplist_del(&h->sh, \
@ -767,16 +818,18 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
struct sskip_item *sitem = typesafe_skiplist_pop(&h->sh); \
return container_of_null(sitem, type, field.si); \
} \
macro_pure type *prefix ## _first(struct prefix##_head *h) \
macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \
{ \
struct sskip_item *first = h->sh.hitem.next[0]; \
const struct sskip_item *first = h->sh.hitem.next[0]; \
return container_of_null(first, type, field.si); \
} \
macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \
const type *item) \
{ \
struct sskip_item *next = item->field.si.next[0]; \
const struct sskip_item *next = item->field.si.next[0]; \
return container_of_null(next, type, field.si); \
} \
TYPESAFE_FIRST_NEXT(prefix, type) \
macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \
struct sskip_item *next; \
@ -792,19 +845,21 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
#define PREDECL_SKIPLIST_UNIQ(prefix) \
_PREDECL_SKIPLIST(prefix)
#define DECLARE_SKIPLIST_UNIQ(prefix, type, field, cmpfn) \
\
\
macro_inline int prefix ## __cmp(const struct sskip_item *a, \
const struct sskip_item *b) \
{ \
return cmpfn(container_of(a, type, field.si), \
container_of(b, type, field.si)); \
} \
macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \
const type *item) \
{ \
struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \
const struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \
&item->field.si, &prefix ## __cmp); \
return container_of_null(sitem, type, field.si); \
} \
TYPESAFE_FIND(prefix, type) \
\
_DECLARE_SKIPLIST(prefix, type, field, \
prefix ## __cmp, prefix ## __cmp) \
@ -843,15 +898,18 @@ extern struct sskip_item *typesafe_skiplist_add(struct sskip_head *head,
struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b));
extern struct sskip_item *typesafe_skiplist_find(struct sskip_head *head,
extern const struct sskip_item *typesafe_skiplist_find(
const struct sskip_head *head,
const struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b));
extern struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head,
extern const struct sskip_item *typesafe_skiplist_find_gteq(
const struct sskip_head *head,
const struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b));
extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head,
extern const struct sskip_item *typesafe_skiplist_find_lt(
const struct sskip_head *head,
const struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a,
const struct sskip_item *b));

View file

@ -25,7 +25,9 @@
#define list_hash concat(TYPE, _hash)
#define list_init concat(TYPE, _init)
#define list_fini concat(TYPE, _fini)
#define list_const_first concat(TYPE, _const_first)
#define list_first concat(TYPE, _first)
#define list_const_next concat(TYPE, _const_next)
#define list_next concat(TYPE, _next)
#define list_next_safe concat(TYPE, _next_safe)
#define list_count concat(TYPE, _count)
@ -177,18 +179,29 @@ static void concat(test_, TYPE)(void)
ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
k = 0;
prev = NULL;
frr_each(list, &head, item) {
#if IS_ATOMIC(REALTYPE)
struct list_head *chead = &head;
struct item *citem, *cprev = NULL;
frr_each(list, chead, citem) {
#else
const struct list_head *chead = &head;
const struct item *citem, *cprev = NULL;
frr_each(list_const, chead, citem) {
#endif
#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE)
/* hash table doesn't give sorting */
(void)prev;
(void)cprev;
#else
assert(!prev || prev->val < item->val);
assert(!cprev || cprev->val < citem->val);
#endif
prev = item;
cprev = citem;
k++;
}
assert(list_count(&head) == k);
assert(list_count(chead) == k);
ts_ref("walk");
#if IS_UNIQ(REALTYPE)