]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: add `_last` and `_prev` on typesafe RB/DLIST
authorDavid Lamparter <equinox@opensourcerouting.org>
Wed, 9 Mar 2022 13:26:05 +0000 (14:26 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Sat, 12 Mar 2022 12:23:36 +0000 (13:23 +0100)
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>
.clang-format
doc/developer/lists.rst
lib/typerb.c
lib/typerb.h
lib/typesafe.h
tests/lib/test_typelist.c
tests/lib/test_typelist.h

index a620b5c2c03a47a02db7e27f6ef4cbc3a4dede60..b01157b05183992cf06a51aa05450947758c3087 100644 (file)
@@ -28,6 +28,9 @@ ForEachMacros:
   - frr_each
   - frr_each_safe
   - frr_each_from
+  - frr_rev_each
+  - frr_rev_each_safe
+  - frr_rev_each_from
   - frr_with_mutex
   - frr_with_privs
   - LIST_FOREACH
index dc8f2369277517e27d8f76c28e4ce6a595a7ee32..6b1e9cf41fa255d00ced32ba160ba863b77b2923 100644 (file)
@@ -100,35 +100,39 @@ Available types:
 
 Functions provided:
 
-+------------------------------------+------+------+------+---------+------------+
-| Function                           | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ |
-+====================================+======+======+======+=========+============+
-| _init, _fini                       | yes  | yes  | yes  | yes     | yes        |
-+------------------------------------+------+------+------+---------+------------+
-| _first, _next, _next_safe,         | yes  | yes  | yes  | yes     | yes        |
-|                                    |      |      |      |         |            |
-| _const_first, _const_next          |      |      |      |         |            |
-+------------------------------------+------+------+------+---------+------------+
-| _swap_all                          | yes  | yes  | yes  | yes     | yes        |
-+------------------------------------+------+------+------+---------+------------+
-| _anywhere                          | yes  | --   | --   | --      | --         |
-+------------------------------------+------+------+------+---------+------------+
-| _add_head, _add_tail, _add_after   | yes  | --   | --   | --      | --         |
-+------------------------------------+------+------+------+---------+------------+
-| _add                               | --   | yes  | yes  | yes     | yes        |
-+------------------------------------+------+------+------+---------+------------+
-| _member                            | yes  | yes  | yes  | yes     | yes        |
-+------------------------------------+------+------+------+---------+------------+
-| _del, _pop                         | yes  | yes  | yes  | yes     | yes        |
-+------------------------------------+------+------+------+---------+------------+
-| _find, _const_find                 | --   | --   | yes  | yes     | --         |
-+------------------------------------+------+------+------+---------+------------+
-| _find_lt, _find_gteq,              | --   | --   | --   | yes     | yes        |
-|                                    |      |      |      |         |            |
-| _const_find_lt, _const_find_gteq   |      |      |      |         |            |
-+------------------------------------+------+------+------+---------+------------+
-| use with frr_each() macros         | yes  | yes  | yes  | yes     | yes        |
-+------------------------------------+------+------+------+---------+------------+
++------------------------------------+-------+------+------+---------+------------+
+| Function                           | LIST  | HEAP | HASH | \*_UNIQ | \*_NONUNIQ |
++====================================+=======+======+======+=========+============+
+| _init, _fini                       | yes   | yes  | yes  | yes     | yes        |
++------------------------------------+-------+------+------+---------+------------+
+| _first, _next, _next_safe,         | yes   | yes  | yes  | yes     | yes        |
+|                                    |       |      |      |         |            |
+| _const_first, _const_next          |       |      |      |         |            |
++------------------------------------+-------+------+------+---------+------------+
+| _last, _prev, _prev_safe,          | DLIST | --   | --   | RB only | RB only    |
+|                                    | only  |      |      |         |            |
+| _const_last, _const_prev           |       |      |      |         |            |
++------------------------------------+-------+------+------+---------+------------+
+| _swap_all                          | yes   | yes  | yes  | yes     | yes        |
++------------------------------------+-------+------+------+---------+------------+
+| _anywhere                          | yes   | --   | --   | --      | --         |
++------------------------------------+-------+------+------+---------+------------+
+| _add_head, _add_tail, _add_after   | yes   | --   | --   | --      | --         |
++------------------------------------+-------+------+------+---------+------------+
+| _add                               | --    | yes  | yes  | yes     | yes        |
++------------------------------------+-------+------+------+---------+------------+
+| _member                            | yes   | yes  | yes  | yes     | yes        |
++------------------------------------+-------+------+------+---------+------------+
+| _del, _pop                         | yes   | yes  | yes  | yes     | yes        |
++------------------------------------+-------+------+------+---------+------------+
+| _find, _const_find                 | --    | --   | 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``
       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
 datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes
 ``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
    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 *)
 
    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
    ``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)
 
    Remove ``item`` from the list and return it.
index e1346df191cc1514bc29648e32bdb2f1778f770e..fe142ff354f0f846ddd342b9e5f1213fe35b9bff 100644 (file)
@@ -468,6 +468,28 @@ struct rb_entry *typed_rb_next(const struct rb_entry *rbe_const)
        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 *rbe = RBH_ROOT(rbt);
@@ -481,6 +503,19 @@ struct rb_entry *typed_rb_min(const struct rbt_tree *rbt)
        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,
                     const struct typed_rb_entry *rbe)
 {
index 75a1de77b32f2956f74f6168c44ef55335409852..8ac18217425a722b4dfc40f2500b2c13431defd6 100644 (file)
@@ -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 *b));
 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);
 bool typed_rb_member(const struct typed_rb_root *rbt,
                     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);                          \
 }                                                                              \
 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)     \
 {                                                                              \
        struct typed_rb_entry *re;                                             \
        re = item ? typed_rb_next(&item->field.re) : NULL;                     \
        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)              \
 {                                                                              \
        return h->rr.count;                                                    \
index b284397d98c6e9a2cca9b806576301c51991830a..06fdc52e78ead3da1a2bedf44e050612dda9928d 100644 (file)
@@ -43,6 +43,22 @@ extern "C" {
                item;                                                          \
                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
  * 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);                         \
 }                                                                              \
 /* ... */
+#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)                                            \
 macro_inline type *prefix ## _find(struct prefix##_head *h,                    \
                                   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);                      \
 }                                                                              \
 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)     \
 {                                                                              \
        if (!item)                                                             \
                return NULL;                                                   \
        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)              \
 {                                                                              \
        return h->dh.count;                                                    \
index 607e29e56b1fe8016f440c8b0d3c303032e5bd5c..6e696584908c6063ff9a6407d84427408892b411 100644 (file)
 #define T_HASH                 (1 << 2)
 #define T_HEAP                 (1 << 3)
 #define T_ATOMIC               (1 << 4)
+#define T_REVERSE              (1 << 5)
 
 #define _T_LIST                        (0)
-#define _T_DLIST               (0)
+#define _T_DLIST               (0                 | T_REVERSE)
 #define _T_ATOMLIST            (0                 | T_ATOMIC)
 #define _T_HEAP                        (T_SORTED          | T_HEAP)
 #define _T_SORTLIST_UNIQ       (T_SORTED | T_UNIQ)
@@ -68,8 +69,8 @@
 #define _T_HASH                        (T_SORTED | T_UNIQ | T_HASH)
 #define _T_SKIPLIST_UNIQ       (T_SORTED | T_UNIQ)
 #define _T_SKIPLIST_NONUNIQ    (T_SORTED)
-#define _T_RBTREE_UNIQ         (T_SORTED | T_UNIQ)
-#define _T_RBTREE_NONUNIQ      (T_SORTED)
+#define _T_RBTREE_UNIQ         (T_SORTED | T_UNIQ | T_REVERSE)
+#define _T_RBTREE_NONUNIQ      (T_SORTED          | T_REVERSE)
 #define _T_ATOMSORT_UNIQ       (T_SORTED | T_UNIQ | 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_HEAP(type)          (_T_TYPE(type) & T_HEAP)
 #define IS_ATOMIC(type)                (_T_TYPE(type) & T_ATOMIC)
+#define IS_REVERSE(type)       (_T_TYPE(type) & T_REVERSE)
 
 static struct timeval ref, ref0;
 
index 8261616ed215af6d4292b71ed5a64a07aa45053b..e3579c67a24d92081c0d269459fcfa0279f06085 100644 (file)
 #define list_const_next        concat(TYPE, _const_next)
 #define list_next      concat(TYPE, _next)
 #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_add       concat(TYPE, _add)
 #define list_add_head  concat(TYPE, _add_head)
@@ -171,6 +176,9 @@ static void concat(test_, TYPE)(void)
 
        list_init(&head);
        assert(list_first(&head) == NULL);
+#if IS_REVERSE(REALTYPE)
+       assert(list_last(&head) == NULL);
+#endif
 
        ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
 
@@ -203,6 +211,10 @@ static void concat(test_, TYPE)(void)
        assert(!list_first(&head));
        assert(list_count(&other) == k);
        assert(list_first(&other) != NULL);
+#if IS_REVERSE(REALTYPE)
+       assert(!list_last(&head));
+       assert(list_last(&other) != NULL);
+#endif
        ts_hash_headx(
                &other, "swap1",
                "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
@@ -269,13 +281,36 @@ static void concat(test_, TYPE)(void)
                (void)cprev;
 #else
                assert(!cprev || cprev->val < citem->val);
+#if IS_REVERSE(REALTYPE)
+               assert(list_const_prev(chead, citem) == cprev);
+#endif
 #endif
                cprev = citem;
                k++;
        }
        assert(list_count(chead) == k);
+#if IS_REVERSE(REALTYPE)
+       assert(cprev == list_const_last(chead));
+#endif
        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)
        prng_free(prng);
        prng = prng_new(0);
@@ -439,6 +474,9 @@ static void concat(test_, TYPE)(void)
        }
        assert(list_count(&head) == k);
        assert(list_first(&head) != NULL);
+#if IS_REVERSE(REALTYPE)
+       assert(list_last(&head) != NULL);
+#endif
        ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
 
 #if !IS_ATOMIC(REALTYPE)
@@ -451,6 +489,10 @@ static void concat(test_, TYPE)(void)
        assert(!list_first(&head));
        assert(list_count(&other) == k);
        assert(list_first(&other) != NULL);
+#if IS_REVERSE(REALTYPE)
+       assert(!list_last(&head));
+       assert(list_last(&other) != NULL);
+#endif
        ts_hash_head(
                &other, "swap1",
                "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
@@ -534,6 +576,21 @@ static void concat(test_, TYPE)(void)
        }
        ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
 #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))) {
                assert(item->scratchpad != 0);
@@ -746,6 +803,13 @@ static void concat(test_, TYPE)(void)
 #undef list_first
 #undef list_next
 #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_add
 #undef list_add_head