]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Hash: iterable now per partes by an iterator
authorMaria Matejka <mq@ucw.cz>
Wed, 29 Jun 2022 11:22:40 +0000 (13:22 +0200)
committerMaria Matejka <mq@ucw.cz>
Mon, 11 Jul 2022 14:07:09 +0000 (16:07 +0200)
It's now possible to pause iteration through hash. This requires
struct hash_iterator to be allocated somewhere handy.

The iteration itself is surrounded by HASH_WALK_ITER and
HASH_WALK_ITER_END. Call HASH_WALK_ITER_PUT to ask for pausing; it may
still do some more iterations until it comes to a suitable pausing
point. The iterator must be initalized to an empty structure. No cleanup
is needed if iteration is abandoned inbetween.

lib/hash.h
lib/hash_test.c

index 8febb33fa99117a7c411ee9ca151f8a9d74f3580..ebb2857a8c2f0783fcdbcdfa72aea05b9568b7ea 100644 (file)
@@ -10,7 +10,7 @@
 #ifndef _BIRD_HASH_H_
 #define _BIRD_HASH_H_
 
-#define HASH(type)             struct { type **data; uint count, order; }
+#define HASH(type)             struct { type **data; uint count; u16 iterators; u8 order; u8 down_requested:1; }
 #define HASH_TYPE(v)           typeof(** (v).data)
 #define HASH_SIZE(v)           (1U << (v).order)
 
 
 #define HASH_MAY_STEP_DOWN_(v,pool,rehash_fn,args)                     \
   ({                                                                    \
-    if (((v).count < (HASH_SIZE(v) REHASH_LO_MARK(args))) &&           \
-       ((v).order > (REHASH_LO_BOUND(args))))                          \
+    if ((v).iterators)                                                 \
+      (v).down_requested = 1;                                          \
+    else if (((v).count < (HASH_SIZE(v) REHASH_LO_MARK(args))) &&      \
+            ((v).order > (REHASH_LO_BOUND(args))))                     \
       rehash_fn(&(v), pool, -(REHASH_LO_STEP(args)));                  \
   })
 
 #define HASH_MAY_RESIZE_DOWN_(v,pool,rehash_fn,args)                   \
   ({                                                                    \
-    uint _o = (v).order;                                                       \
-    while (((v).count < ((1U << _o) REHASH_LO_MARK(args))) &&          \
-          (_o > (REHASH_LO_BOUND(args))))                              \
-      _o -= (REHASH_LO_STEP(args));                                    \
-    if (_o < (v).order)                                                        \
-      rehash_fn(&(v), pool, _o - (v).order);                           \
-  })
+    if ((v).iterators)                                                 \
+      (v).down_requested = 1;                                          \
+    else {                                                             \
+      uint _o = (v).order;                                             \
+      while (((v).count < ((1U << _o) REHASH_LO_MARK(args))) &&                \
+            (_o > (REHASH_LO_BOUND(args))))                            \
+       _o -= (REHASH_LO_STEP(args));                                   \
+      if (_o < (v).order)                                              \
+       rehash_fn(&(v), pool, _o - (v).order);                          \
+    }                                                                  \
+   })
 
 
 #define HASH_INSERT2(v,id,pool,node)                                   \
 #define HASH_WALK_FILTER_END } while (0)
 
 
+#define HASH_WALK_ITER(v, id, n, iter)                                 \
+  do {                                                                 \
+    uint _hash_walk_iter_put = 0;                                      \
+    uint _shift = 32 - (v).order;                                      \
+    for ( ; !_hash_walk_iter_put; (iter) += (1U << _shift)) {          \
+      _hash_walk_iter_put = ((iter) + (1U << _shift) == 0);            \
+      for (HASH_TYPE(v) *n = (v).data[(iter) >> _shift]; n; n = id##_NEXT((n)))\
+       if (HASH_FN(v, id, id##_KEY(n)) >= ((iter) >> _shift))          \
+
+#define HASH_WALK_ITER_PUT     (_hash_walk_iter_put = 1)
+
+#define HASH_WALK_ITER_END } } while (0)
+
+
 static inline void
 mem_hash_init(u64 *h)
 {
index 4bce70179e0fcdf0416421018e1d8ec86de97100..ecfcdd665adf541e81713d682c8674656fa13f49 100644 (file)
@@ -285,6 +285,46 @@ t_walk_filter(void)
   return 1;
 }
 
+static int
+t_walk_iter(void)
+{
+  init_hash();
+  fill_hash();
+
+  u32 hit = 0;
+
+  u32 prev_hash = ~0;
+  for (uint cnt = 0; cnt < MAX_NUM; )
+  {
+    u32 last_hash = ~0;
+//    printf("PUT!\n");
+    HASH_WALK_ITER(hash, TEST, n, hit)
+    {
+      cnt++;
+      u32 cur_hash = HASH_FN(hash, TEST, n->key);
+      /*
+      printf("C%08x L%08x P%08x K%08x H%08x N%p S%d I%ld\n",
+         cur_hash, last_hash, prev_hash, n->key, hit, n, _shift, n - &nodes[0]);
+         */
+
+      if (last_hash == ~0U)
+      {
+       if (prev_hash != ~0U)
+         bt_assert(prev_hash < cur_hash);
+       last_hash = prev_hash = cur_hash;
+      }
+      else
+       bt_assert(last_hash == cur_hash);
+
+      if (cnt < MAX_NUM)
+       HASH_WALK_ITER_PUT;
+    }
+    HASH_WALK_ITER_END;
+  }
+
+  return 1;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -299,6 +339,7 @@ main(int argc, char *argv[])
   bt_test_suite(t_walk_delsafe_remove,         "HASH_WALK_DELSAFE and HASH_REMOVE");
   bt_test_suite(t_walk_delsafe_remove2,        "HASH_WALK_DELSAFE and HASH_REMOVE2. HASH_REMOVE2 is HASH_REMOVE and smart auto-resize function");
   bt_test_suite(t_walk_filter,         "HASH_WALK_FILTER");
+  bt_test_suite(t_walk_iter,           "HASH_WALK_ITER");
 
   return bt_exit_value();
 }