+/*
+ * Helper function to scan a bucket in the hash table, it
+ * can only be called on a non-empty bucket for a valid table.
+ *
+ * In lookup and scan, consider ht->ht[i] as pointing to the tail
+ * of the queue (head is NEXTP(tail). The 'empty' value is irrelevant.
+ * While searching, start analysing p = head, end when p == tail.
+ * Note that 'tail' is a cache of the _original_ ht->ht[i]
+ * and is used to check for loop termination. If you remove
+ * it, you must also adjust 'p' when deleting the 'tail' element.
+ */
+#define NEXT(_h, _p) *((void **)((char *)(_p) + (_h)->ofs))
+static int
+dn_ht_scan_body(struct dn_ht *ht, int *bucket,
+ int (*fn)(void *, void *), void *arg)
+{
+ int ret, found = 0, i = *bucket;
+ void *tail, *pp, *p, *nextp;
+
+ pp = tail = ht->ht[i];
+ do {
+ p = NEXT(ht, pp);
+ nextp = NEXT(ht, p);
+ ret = fn(p, arg);
+ if ((ret & DNHT_SCAN_DEL) == 0) {
+ pp = p; /* prepare for next loop */
+ } else {
+ found++;
+ ht->entries--;
+ /* skip current element */
+ if (pp != p)
+ /* pp == p implies p == tail */
+ NEXT(ht, pp) = nextp;
+ if (p == tail)
+ ht->ht[i] = (pp != p) ? pp : NULL;
+ }
+ if (ret & DNHT_SCAN_END) {
+ /* Update ht->ht[i] before returning */
+ ht->ht[i] = (ht->ht[i] == NULL) ? NULL : pp;
+ return found;
+ }
+ } while (p != tail);
+
+ (*bucket)++;
+ return found;
+}
+
+/*
+ * lookup and optionally create or delete element.
+ * This is an optimized version of the scan so it is coded
+ * inline.
+ */