]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: typesafe *_member()
authorDavid Lamparter <equinox@diac24.net>
Sat, 27 Mar 2021 21:05:07 +0000 (22:05 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Tue, 19 Oct 2021 12:55:39 +0000 (14:55 +0200)
This provides a "is this item on this list" check, which may or may not
be faster than using *_find() for the same purpose.  (If the container
has no faster way of doing it, it falls back to using *_find().)

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
lib/typerb.c
lib/typerb.h
lib/typesafe.c
lib/typesafe.h
tests/lib/test_typelist.h

index 151d91ce20b755488d6926985590a3a1c8c6c76c..e1346df191cc1514bc29648e32bdb2f1778f770e 100644 (file)
@@ -480,3 +480,11 @@ struct rb_entry *typed_rb_min(const struct rbt_tree *rbt)
 
        return parent;
 }
+
+bool typed_rb_member(const struct typed_rb_root *rbt,
+                    const struct typed_rb_entry *rbe)
+{
+       while (rbe->rbt_parent)
+               rbe = rbe->rbt_parent;
+       return rbe == rbt->rbt_root;
+}
index cbed8d489375f989de6ed079bbfff10ce6a4d10b..d22d864aae86e53504cb644a4965bd81edd6d8a5 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 *b));
 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);
+bool typed_rb_member(const struct typed_rb_root *rbt,
+                    const struct typed_rb_entry *rbe);
 
 #define _PREDECL_RBTREE(prefix)                                                \
 struct prefix ## _head { struct typed_rb_root rr; };                           \
@@ -142,6 +144,11 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h)              \
 {                                                                              \
        return h->rr.count;                                                    \
 }                                                                              \
+macro_pure bool prefix ## _member(const struct prefix##_head *h,               \
+                                 const type *item)                            \
+{                                                                              \
+       return typed_rb_member(&h->rr, &item->field.re);                       \
+}                                                                              \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 #define PREDECL_RBTREE_UNIQ(prefix)                                            \
index 76705fad0d3607937921c860b2f9cf228b95486b..f886c5bdf6fefd3925135d324ee7d858579934bc 100644 (file)
@@ -29,6 +29,44 @@ DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket");
 DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow");
 DEFINE_MTYPE_STATIC(LIB, HEAP_ARRAY, "Typed-heap array");
 
+bool typesafe_list_member(const struct slist_head *head,
+                         const struct slist_item *item)
+{
+       struct slist_item *fromhead = head->first;
+       struct slist_item **fromnext = (struct slist_item **)&item->next;
+
+       while (fromhead) {
+               if (fromhead == item || fromnext == head->last_next)
+                       return true;
+
+               fromhead = fromhead->next;
+               if (!*fromnext)
+                       break;
+               fromnext = &(*fromnext)->next;
+       }
+
+       return false;
+}
+
+bool typesafe_dlist_member(const struct dlist_head *head,
+                          const struct dlist_item *item)
+{
+       const struct dlist_item *fromhead = head->hitem.next;
+       const struct dlist_item *fromitem = item->next;
+
+       if (!item->prev || !item->next)
+               return false;
+
+       while (fromhead != &head->hitem && fromitem != item) {
+               if (fromitem == &head->hitem || fromhead == item)
+                       return true;
+               fromhead = fromhead->next;
+               fromitem = fromitem->next;
+       }
+
+       return false;
+}
+
 #if 0
 static void hash_consistency_check(struct thash_head *head)
 {
index 6d6fcf282eac0223361ed86279703d5d5d03553e..ecf7d10eb1a9a5b48445d89eaa454ee65bc48f05 100644 (file)
@@ -78,8 +78,34 @@ macro_inline type *prefix ## _find_gteq(struct prefix##_head *h,               \
 }                                                                              \
 /* ... */
 
+/* *_member via find - when there is no better membership check than find() */
+#define TYPESAFE_MEMBER_VIA_FIND(prefix, type)                                 \
+macro_inline bool prefix ## _member(struct prefix##_head *h,                   \
+                                   const type *item)                          \
+{                                                                              \
+       return item == prefix ## _const_find(h, item);                         \
+}                                                                              \
+/* ... */
+
+/* *_member via find_gteq - same for non-unique containers */
+#define TYPESAFE_MEMBER_VIA_FIND_GTEQ(prefix, type, cmpfn)                     \
+macro_inline bool prefix ## _member(struct prefix##_head *h,                   \
+                                   const type *item)                          \
+{                                                                              \
+       const type *iter;                                                      \
+       for (iter = prefix ## _const_find_gteq(h, item); iter;                 \
+            iter = prefix ## _const_next(h, iter)) {                          \
+               if (iter == item)                                              \
+                       return true;                                           \
+               if (cmpfn(iter, item) > 0)                                     \
+                       break;                                                 \
+       }                                                                      \
+       return false;                                                          \
+}                                                                              \
+/* ... */
+
 /* SWAP_ALL_SIMPLE = for containers where the items don't point back to the
- * head *AND* the head doesn'T points to itself (= everything except LIST,
+ * head *AND* the head doesn't point to itself (= everything except LIST,
  * DLIST and SKIPLIST), just switch out the entire head
  */
 #define TYPESAFE_SWAP_ALL_SIMPLE(prefix)                                       \
@@ -116,6 +142,9 @@ static inline void typesafe_list_add(struct slist_head *head,
        head->count++;
 }
 
+extern bool typesafe_list_member(const struct slist_head *head,
+                                const struct slist_item *item);
+
 /* use as:
  *
  * PREDECL_LIST(namelist);
@@ -218,6 +247,11 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h)              \
 {                                                                              \
        return h->sh.count;                                                    \
 }                                                                              \
+macro_pure bool prefix ## _member(const struct prefix##_head *h,               \
+                                 const type *item)                            \
+{                                                                              \
+       return typesafe_list_member(&h->sh, &item->field.si);                  \
+}                                                                              \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 /* don't use these structs directly */
@@ -269,6 +303,9 @@ static inline void typesafe_dlist_swap_all(struct dlist_head *a,
        }
 }
 
+extern bool typesafe_dlist_member(const struct dlist_head *head,
+                                 const struct dlist_item *item);
+
 /* double-linked list, for fast item deletion
  */
 #define PREDECL_DLIST(prefix)                                                  \
@@ -357,6 +394,11 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h)              \
 {                                                                              \
        return h->dh.count;                                                    \
 }                                                                              \
+macro_pure bool prefix ## _member(const struct prefix##_head *h,               \
+                                 const type *item)                            \
+{                                                                              \
+       return typesafe_dlist_member(&h->dh, &item->field.di);                 \
+}                                                                              \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 /* note: heap currently caps out at 4G items */
@@ -466,6 +508,14 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h)              \
 {                                                                              \
        return h->hh.count;                                                    \
 }                                                                              \
+macro_pure bool prefix ## _member(const struct prefix##_head *h,               \
+                                 const type *item)                            \
+{                                                                              \
+       uint32_t idx = item->field.hi.index;                                   \
+       if (idx >= h->hh.count)                                                \
+               return false;                                                  \
+       return h->hh.array[idx] == &item->field.hi;                            \
+}                                                                              \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 extern void typesafe_heap_resize(struct heap_head *head, bool grow);
@@ -622,6 +672,7 @@ macro_inline const type *prefix ## _const_find(const struct prefix##_head *h,  \
        return container_of(sitem, type, field.si);                            \
 }                                                                              \
 TYPESAFE_FIND(prefix, type)                                                    \
+TYPESAFE_MEMBER_VIA_FIND(prefix, type)                                         \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 #define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn)                   \
@@ -637,6 +688,7 @@ macro_inline int _ ## prefix ## _cmp(const type *a, const type *b)             \
        return 0;                                                              \
 }                                                                              \
        _DECLARE_SORTLIST(prefix, type, field, cmpfn, _ ## prefix ## _cmp);    \
+TYPESAFE_MEMBER_VIA_FIND_GTEQ(prefix, type, cmpfn)                             \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 
@@ -803,6 +855,19 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h)              \
 {                                                                              \
        return h->hh.count;                                                    \
 }                                                                              \
+macro_pure bool prefix ## _member(const struct prefix##_head *h,               \
+                                 const type *item)                            \
+{                                                                              \
+       uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \
+       const struct thash_item *hitem = h->hh.entries[hbits];                 \
+       while (hitem && hitem->hashval < hval)                                 \
+               hitem = hitem->next;                                           \
+       for (hitem = h->hh.entries[hbits]; hitem && hitem->hashval <= hval;    \
+            hitem = hitem->next)                                              \
+               if (hitem == &item->field.hi)                                  \
+                       return true;                                           \
+       return false;                                                          \
+}                                                                              \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 /* skiplist, sorted.
@@ -941,6 +1006,7 @@ macro_inline const type *prefix ## _const_find(const struct prefix##_head *h,  \
        return container_of_null(sitem, type, field.si);                       \
 }                                                                              \
 TYPESAFE_FIND(prefix, type)                                                    \
+TYPESAFE_MEMBER_VIA_FIND(prefix, type)                                         \
                                                                                \
 _DECLARE_SKIPLIST(prefix, type, field,                                         \
                prefix ## __cmp, prefix ## __cmp);                             \
@@ -972,6 +1038,7 @@ macro_inline int prefix ## __cmp_uq(const struct sskip_item *a,                \
                                                                                \
 _DECLARE_SKIPLIST(prefix, type, field,                                         \
                prefix ## __cmp, prefix ## __cmp_uq);                          \
+TYPESAFE_MEMBER_VIA_FIND_GTEQ(prefix, type, cmpfn)                             \
 MACRO_REQUIRE_SEMICOLON() /* end */
 
 
index be37a9e6c9c815930a84d61596938fed6283df98..a12a995bf6f33559280c538c7d09244c3d28c7ed 100644 (file)
@@ -39,6 +39,7 @@
 #define list_find      concat(TYPE, _find)
 #define list_find_lt   concat(TYPE, _find_lt)
 #define list_find_gteq concat(TYPE, _find_gteq)
+#define list_member    concat(TYPE, _member)
 #define list_del       concat(TYPE, _del)
 #define list_pop       concat(TYPE, _pop)
 #define list_swap_all  concat(TYPE, _swap_all)
@@ -301,7 +302,7 @@ static void concat(test_, TYPE)(void)
 #elif IS_HEAP(REALTYPE)
        /* heap - partially sorted. */
        prev = NULL;
-       l = k / 2;
+       l = k / 4;
        for (i = 0; i < l; i++) {
                item = list_pop(&head);
                if (prev)
@@ -310,7 +311,24 @@ static void concat(test_, TYPE)(void)
                k--;
                prev = item;
        }
-       ts_hash("pop", NULL);
+       ts_hash("pop#1", NULL);
+
+       for (i = 0; i < NITEM; i++)
+               assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+                       "%zu should:%d is:%d", i, itm[i].scratchpad,
+                       list_member(&head, &itm[i]));
+       ts_hash("member", NULL);
+
+       l = k / 2;
+       for (; i < l; i++) {
+               item = list_pop(&head);
+               if (prev)
+                       assert(prev->val < item->val);
+               item->scratchpad = 0;
+               k--;
+               prev = item;
+       }
+       ts_hash("pop#2", NULL);
 
 #else /* !IS_UNIQ(REALTYPE) && !IS_HEAP(REALTYPE) */
        for (i = 0; i < NITEM; i++) {
@@ -387,6 +405,14 @@ static void concat(test_, TYPE)(void)
        assert(l + list_count(&head) == k);
        ts_hashx("del", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca");
 
+#if !IS_ATOMIC(REALTYPE)
+       for (i = 0; i < NITEM; i++)
+               assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+                       "%zu should:%d is:%d", i, itm[i].scratchpad,
+                       list_member(&head, &itm[i]));
+       ts_hashx("member", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca");
+#endif
+
        frr_each_safe(list, &head, item) {
                assert(item->scratchpad != 0);
 
@@ -475,7 +501,31 @@ static void concat(test_, TYPE)(void)
        }
        ts_hash("del-prng", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564");
 
+#if !IS_ATOMIC(REALTYPE)
+       for (i = 0; i < NITEM; i++)
+               assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+                       "%zu should:%d is:%d", i, itm[i].scratchpad,
+                       list_member(&head, &itm[i]));
+       ts_hash("member", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564");
+#endif
+
        l = 0;
+       while (l < (k / 4) && (prev = list_pop(&head))) {
+               assert(prev->scratchpad != 0);
+
+               prev->scratchpad = 0;
+               l++;
+       }
+       ts_hash("pop#1", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
+
+#if !IS_ATOMIC(REALTYPE)
+       for (i = 0; i < NITEM; i++)
+               assertf(list_member(&head, &itm[i]) == itm[i].scratchpad,
+                       "%zu should:%d is:%d", i, itm[i].scratchpad,
+                       list_member(&head, &itm[i]));
+       ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
+#endif
+
        while ((item = list_pop(&head))) {
                assert(item->scratchpad != 0);
 
@@ -485,7 +535,7 @@ static void concat(test_, TYPE)(void)
        assert(l == k);
        assert(list_count(&head) == 0);
        assert(list_first(&head) == NULL);
-       ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+       ts_hash("pop#2", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
 
        prng_free(prng);
        prng = prng_new(0x1e5a2d69);
@@ -695,6 +745,7 @@ static void concat(test_, TYPE)(void)
 #undef list_find
 #undef list_find_lt
 #undef list_find_gteq
+#undef list_member
 #undef list_del
 #undef list_pop
 #undef list_swap_all