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 | | _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 | -- | -- | -- | -- | | _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- |
+------------------------------------+------+------+------+---------+------------+ +------------------------------------+------+------+------+---------+------------+
@ -113,9 +114,10 @@ Functions provided:
+------------------------------------+------+------+------+---------+------------+ +------------------------------------+------+------+------+---------+------------+
| _del, _pop | yes | yes | yes | yes | yes | | _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 | | 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`` 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.
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 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. 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 Returns the number of items in a structure. All structures store a
counter in their `Z_head` so that calling this function completes 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 outdated by the time this function returns and can therefore only be
used as an estimate. used as an estimate.
.. c:function:: const itemtype *Z_const_first(const struct Z_head *)
.. c:function:: itemtype *Z_first(struct Z_head *) .. c:function:: itemtype *Z_first(struct Z_head *)
Returns the first item in the structure, or ``NULL`` if the structure is 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 affected by the "modification while iterating" problem. To remove
all items from a hash table, use the loop demonstrated above. 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) .. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev)
Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is 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`` For ``_NONUNIQ`` lists, this function always returns NULL since ``item``
can always be successfully added to the list. 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) .. 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 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 containing non-unique items, more than one item may compare as equal to
the item that is searched for. 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) .. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref)
Search the list for an item that compares greater or equal to Search the list for an item that compares greater or equal to
``ref``. See :c:func:`Z_find()` above. ``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) .. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref)
Search the list for an item that compares less than Search the list for an item that compares less than
@ -616,21 +627,9 @@ Head removal (pop) and deallocation:
FAQ FAQ
--- ---
Why is the list head not ``const`` in the list APIs? What are the semantics of ``const`` in the list APIs?
The semantics that a ``const`` list head would imply are not obvious. It ``const`` pointers to list heads and/or items are interpreted to mean that
could mean any of the following: both the list itself as well as the data items are read-only.
* 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.
Why is there no "is this item on a/the list" test? 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 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 */ /* 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)( int (*cmpfn)(
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 rb_entry *tmp = RBH_ROOT(rbt); const struct rb_entry *tmp = RBH_ROOT(rbt);
int comp; int comp;
while (tmp != NULL) { while (tmp != NULL) {
@ -398,13 +399,13 @@ struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key,
return NULL; 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, const struct rb_entry *key,
int (*cmpfn)( int (*cmpfn)(
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 rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
int comp; int comp;
while (tmp != NULL) { while (tmp != NULL) {
@ -421,13 +422,13 @@ struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt,
return best; 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, const struct rb_entry *key,
int (*cmpfn)( int (*cmpfn)(
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 rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL;
int comp; int comp;
while (tmp != NULL) { while (tmp != NULL) {
@ -443,8 +444,10 @@ struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt,
return best; 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) { if (RBE_RIGHT(rbe) != NULL) {
rbe = RBE_RIGHT(rbe); rbe = RBE_RIGHT(rbe);
while (RBE_LEFT(rbe) != NULL) while (RBE_LEFT(rbe) != NULL)
@ -463,7 +466,7 @@ struct rb_entry *typed_rb_next(struct rb_entry *rbe)
return 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 *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL; 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)); const struct typed_rb_entry *b));
struct typed_rb_entry *typed_rb_remove(struct typed_rb_root *rbt, struct typed_rb_entry *typed_rb_remove(struct typed_rb_root *rbt,
struct typed_rb_entry *rbe); 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, const struct typed_rb_entry *rbe,
int (*cmpfn)( int (*cmpfn)(
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_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, const struct typed_rb_entry *rbe,
int (*cmpfn)( int (*cmpfn)(
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_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, const struct typed_rb_entry *rbe,
int (*cmpfn)( int (*cmpfn)(
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(struct typed_rb_root *rbt); struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt);
struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *rbe); struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe);
#define _PREDECL_RBTREE(prefix) \ #define _PREDECL_RBTREE(prefix) \
struct prefix ## _head { struct typed_rb_root rr; }; \ 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); \ re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \
return container_of_null(re, type, field.re); \ return container_of_null(re, type, field.re); \
} \ } \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ macro_inline const type *prefix ## _const_find_gteq( \
const type *item) \ 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); \ re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \
return container_of_null(re, type, field.re); \ return container_of_null(re, type, field.re); \
} \ } \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ macro_inline const type *prefix ## _const_find_lt( \
const type *item) \ 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); \ re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \
return container_of_null(re, type, field.re); \ return container_of_null(re, type, field.re); \
} \ } \
TYPESAFE_FIND_CMP(prefix, type) \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \ { \
struct typed_rb_entry *re; \ struct typed_rb_entry *re; \
@ -115,18 +116,20 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
typed_rb_remove(&h->rr, re); \ typed_rb_remove(&h->rr, re); \
return container_of(re, type, field.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); \ re = typed_rb_min(&h->rr); \
return container_of_null(re, type, field.re); \ 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); \ re = typed_rb_next(&item->field.re); \
return container_of_null(re, type, 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) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \ { \
struct typed_rb_entry *re; \ 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), \ return cmpfn(container_of(a, type, field.re), \
container_of(b, 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); \ re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \
return container_of_null(re, type, field.re); \ return container_of_null(re, type, field.re); \
} \ } \
TYPESAFE_FIND(prefix, type) \
\ \
_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \ _DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \
/* ... */ /* ... */

View file

@ -158,7 +158,7 @@ void typesafe_hash_shrink(struct thash_head *head)
/* skiplist */ /* 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) size_t level)
{ {
if (level < SKIPLIST_OVERFLOW) 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! */ /* 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 *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)) const struct sskip_item *b))
{ {
size_t level = SKIPLIST_MAXDEPTH; size_t level = SKIPLIST_MAXDEPTH;
struct sskip_item *prev = &head->hitem, *next; const struct sskip_item *prev = &head->hitem, *next;
int cmpval; int cmpval;
while (level) { while (level) {
@ -290,13 +291,14 @@ struct sskip_item *typesafe_skiplist_find(struct sskip_head *head,
return NULL; 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 *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)) const struct sskip_item *b))
{ {
size_t level = SKIPLIST_MAXDEPTH; size_t level = SKIPLIST_MAXDEPTH;
struct sskip_item *prev = &head->hitem, *next; const struct sskip_item *prev = &head->hitem, *next;
int cmpval; int cmpval;
while (level) { while (level) {
@ -317,13 +319,14 @@ struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head,
return next; 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 *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)) const struct sskip_item *b))
{ {
size_t level = SKIPLIST_MAXDEPTH; 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; int cmpval;
while (level) { while (level) {

View file

@ -44,6 +44,41 @@ extern "C" {
item; \ item; \
item = from, from = prefix##_next_safe(head, from)) 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. /* single-linked list, unsorted/arbitrary.
* can be used as queue with add_tail / pop * 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; \ h->sh.last_next = &h->sh.first; \
return container_of(sitem, type, field.si); \ 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); \ 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); \ 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) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \ { \
struct slist_item *sitem; \ struct slist_item *sitem; \
@ -232,20 +269,22 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
h->dh.count--; \ h->dh.count--; \
return container_of(ditem, type, field.di); \ 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) \ if (ditem == &h->dh.hitem) \
return NULL; \ return NULL; \
return container_of(ditem, type, field.di); \ 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) \ if (ditem->next == &h->dh.hitem) \
return NULL; \ return NULL; \
return container_of(ditem->next, type, field.di); \ 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) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \ { \
if (!item) \ if (!item) \
@ -338,19 +377,21 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
typesafe_heap_resize(&h->hh, false); \ typesafe_heap_resize(&h->hh, false); \
return container_of(hitem, type, field.hi); \ 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) \ if (h->hh.count == 0) \
return NULL; \ return NULL; \
return container_of(h->hh.array[0], type, field.hi); \ 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; \ uint32_t idx = item->field.hi.index + 1; \
if (idx >= h->hh.count) \ if (idx >= h->hh.count) \
return NULL; \ return NULL; \
return container_of(h->hh.array[idx], type, field.hi); \ 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) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \ { \
if (!item) \ if (!item) \
@ -431,26 +472,27 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \
h->sh.count++; \ h->sh.count++; \
return NULL; \ return NULL; \
} \ } \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ macro_inline const type *prefix ## _const_find_gteq( \
const type *item) \ 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; \ int cmpval = 0; \
while (sitem && (cmpval = cmpfn_nuq( \ while (sitem && (cmpval = cmpfn_nuq( \
container_of(sitem, type, field.si), item)) < 0) \ container_of(sitem, type, field.si), item)) < 0) \
sitem = sitem->next; \ sitem = sitem->next; \
return container_of_null(sitem, type, field.si); \ return container_of_null(sitem, type, field.si); \
} \ } \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ macro_inline const type *prefix ## _const_find_lt( \
const type *item) \ 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; \ int cmpval = 0; \
while (sitem && (cmpval = cmpfn_nuq( \ while (sitem && (cmpval = cmpfn_nuq( \
container_of(sitem, type, field.si), item)) < 0) \ container_of(sitem, type, field.si), item)) < 0) \
sitem = (prev = sitem)->next; \ sitem = (prev = sitem)->next; \
return container_of_null(prev, type, field.si); \ return container_of_null(prev, type, field.si); \
} \ } \
TYPESAFE_FIND_CMP(prefix, type) \
/* TODO: del_hint */ \ /* TODO: del_hint */ \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ 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; \ h->sh.first = sitem->next; \
return container_of(sitem, type, field.si); \ 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); \ 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); \ 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) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \ { \
struct ssort_item *sitem; \ struct ssort_item *sitem; \
@ -498,9 +542,10 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \
#define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ #define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \
_DECLARE_SORTLIST(prefix, type, field, cmpfn, 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; \ int cmpval = 0; \
while (sitem && (cmpval = cmpfn( \ while (sitem && (cmpval = cmpfn( \
container_of(sitem, type, field.si), item)) < 0) \ 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 NULL; \
return container_of(sitem, type, field.si); \ return container_of(sitem, type, field.si); \
} \ } \
TYPESAFE_FIND(prefix, type) \
/* ... */ /* ... */
#define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \ #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; \ *np = &item->field.hi; \
return NULL; \ 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) \ if (!h->hh.tabshift) \
return NULL; \ return NULL; \
uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ 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) \ while (hitem && hitem->hashval < hval) \
hitem = hitem->next; \ hitem = hitem->next; \
while (hitem && hitem->hashval == hval) { \ while (hitem && hitem->hashval == hval) { \
@ -621,6 +668,7 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \
} \ } \
return NULL; \ return NULL; \
} \ } \
TYPESAFE_FIND(prefix, type) \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \ { \
if (!h->hh.tabshift) \ if (!h->hh.tabshift) \
@ -655,7 +703,7 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \
} \ } \
return NULL; \ 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; \ uint32_t i; \
for (i = 0; i < HASH_SIZE(h->hh); i++) \ for (i = 0; i < HASH_SIZE(h->hh); i++) \
@ -663,9 +711,10 @@ macro_pure type *prefix ## _first(struct prefix##_head *h) \
return container_of(h->hh.entries[i], type, field.hi); \ return container_of(h->hh.entries[i], type, field.hi); \
return NULL; \ 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) \ if (hitem->next) \
return container_of(hitem->next, type, field.hi); \ return container_of(hitem->next, type, field.hi); \
uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \ uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \
@ -674,6 +723,7 @@ macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \
return container_of(h->hh.entries[i], type, field.hi); \ return container_of(h->hh.entries[i], type, field.hi); \
return NULL; \ return NULL; \
} \ } \
TYPESAFE_FIRST_NEXT(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) \
@ -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); \ si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \
return container_of_null(si, type, field.si); \ return container_of_null(si, type, field.si); \
} \ } \
macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ macro_inline const type *prefix ## _const_find_gteq( \
const type *item) \ 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); \ &item->field.si, cmpfn_nuq); \
return container_of_null(sitem, type, field.si); \ return container_of_null(sitem, type, field.si); \
} \ } \
macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ macro_inline const type *prefix ## _const_find_lt( \
const type *item) \ 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); \ &item->field.si, cmpfn_nuq); \
return container_of_null(sitem, type, field.si); \ return container_of_null(sitem, type, field.si); \
} \ } \
TYPESAFE_FIND_CMP(prefix, type) \
macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \
{ \ { \
struct sskip_item *sitem = typesafe_skiplist_del(&h->sh, \ 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); \ struct sskip_item *sitem = typesafe_skiplist_pop(&h->sh); \
return container_of_null(sitem, type, field.si); \ 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); \ 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); \ 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) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \
{ \ { \
struct sskip_item *next; \ struct sskip_item *next; \
@ -799,12 +852,14 @@ macro_inline int prefix ## __cmp(const struct sskip_item *a, \
return cmpfn(container_of(a, type, field.si), \ return cmpfn(container_of(a, type, field.si), \
container_of(b, 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); \ &item->field.si, &prefix ## __cmp); \
return container_of_null(sitem, type, field.si); \ return container_of_null(sitem, type, field.si); \
} \ } \
TYPESAFE_FIND(prefix, type) \
\ \
_DECLARE_SKIPLIST(prefix, type, field, \ _DECLARE_SKIPLIST(prefix, type, field, \
prefix ## __cmp, prefix ## __cmp) \ 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)( struct sskip_item *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)); 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 *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)); 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 *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)); 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 *item, int (*cmpfn)(
const struct sskip_item *a, const struct sskip_item *a,
const struct sskip_item *b)); const struct sskip_item *b));

View file

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