]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Implement incremental hash table resizing in isc_ht
authorOndřej Surý <ondrej@isc.org>
Tue, 15 Mar 2022 20:45:33 +0000 (21:45 +0100)
committerOndřej Surý <ondrej@isc.org>
Wed, 11 Jan 2023 16:15:33 +0000 (17:15 +0100)
Previously, an incremental hash table resizing was implemented for the
dns_rbt_t hash table implementation.  Using that as a base, also
implement the incremental hash table resizing also for isc_ht API
hashtables:

 1. During the resize, allocate the new hash table, but keep the old
    table unchanged.
 2. In each lookup, delete, or iterator operation, check both tables.
 3. Perform insertion operations only in the new table.
 4. At each insertion also move <r> elements from the old table to
    the new table.
 5. When all elements are removed from the old table, deallocate it.

To ensure that the old table is completely copied over before the new
table itself needs to be enlarged, it is necessary to increase the
size of the table by a factor of at least (<r> + 1)/<r> during resizing.

In our implementation <r> is equal to 1.

The downside of this approach is that the old table and the new table
could stay in memory for longer when there are no new insertions into
the hash table for prolonged periods of time as the incremental
rehashing happens only during the insertions.

(cherry picked from commit e42cb1f198ae945fc322ffed388cd647c8c75fbf)

bin/plugins/filter-a.c
bin/plugins/filter-aaaa.c
bin/tests/system/hooks/driver/test-async.c
lib/dns/catz.c
lib/dns/rpz.c
lib/isc/ht.c
lib/isc/include/isc/ht.h
lib/isc/tls.c
tests/isc/ht_test.c

index 295ceb5474f3d115d72cfabab1f0bb2e172429ed..06cd3a7b54fc5a2e5f9b71b1d5b0535ca18ddc31 100644 (file)
@@ -347,7 +347,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
                                       cfg_line, mctx, lctx, actx));
        }
 
-       isc_ht_init(&inst->ht, mctx, 16);
+       isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
        isc_mutex_init(&inst->hlock);
 
        /*
index f9ed9fe9cf40f1de132cd2ce8e39f75a0307d61c..6a17f18038351ba7e67bc0748a5a3627945c3978 100644 (file)
@@ -350,7 +350,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
                                       cfg_line, mctx, lctx, actx));
        }
 
-       isc_ht_init(&inst->ht, mctx, 16);
+       isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
        isc_mutex_init(&inst->hlock);
 
        /*
index 577d5de6f84971b214a47512938a5abed3572c05..652b6715a8875517661d717947bf735712e41c59 100644 (file)
@@ -143,7 +143,7 @@ plugin_register(const char *parameters, const void *cfg, const char *cfg_file,
        *inst = (async_instance_t){ .mctx = NULL };
        isc_mem_attach(mctx, &inst->mctx);
 
-       isc_ht_init(&inst->ht, mctx, 16);
+       isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
        isc_mutex_init(&inst->hlock);
 
        /*
index 5d2870ea89054f7f4c046f5de286dca94b06bcd1..ed50dea053c6192072e53e24a9b184ecc8172439 100644 (file)
@@ -477,9 +477,9 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
 
        dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
 
-       isc_ht_init(&toadd, target->catzs->mctx, 16);
+       isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
 
-       isc_ht_init(&tomod, target->catzs->mctx, 16);
+       isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
 
        isc_ht_iter_create(newzone->entries, &iter1);
 
@@ -759,7 +759,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
 
        isc_refcount_init(&new_zones->refs, 1);
 
-       isc_ht_init(&new_zones->zones, mctx, 4);
+       isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE);
 
        isc_mem_attach(mctx, &new_zones->mctx);
        new_zones->zmm = zmm;
@@ -811,8 +811,8 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
        dns_name_init(&new_zone->name, NULL);
        dns_name_dup(name, catzs->mctx, &new_zone->name);
 
-       isc_ht_init(&new_zone->entries, catzs->mctx, 4);
-       isc_ht_init(&new_zone->coos, catzs->mctx, 4);
+       isc_ht_init(&new_zone->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE);
+       isc_ht_init(&new_zone->coos, catzs->mctx, 4, ISC_HT_CASE_INSENSITIVE);
 
        new_zone->updatetimer = NULL;
        result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, NULL,
index c05125624454281d168eb524c275640edee75bfe..71340785d68e79ab3123f46aa7eeed719b47a795 100644 (file)
@@ -1544,7 +1544,7 @@ dns_rpz_new_zone(dns_rpz_zones_t *rpzs, dns_rpz_zone_t **rpzp) {
         * simplifies update_from_db
         */
 
-       isc_ht_init(&zone->nodes, rpzs->mctx, 1);
+       isc_ht_init(&zone->nodes, rpzs->mctx, 1, ISC_HT_CASE_SENSITIVE);
 
        dns_name_init(&zone->origin, NULL);
        dns_name_init(&zone->client_ip, NULL);
@@ -1722,7 +1722,8 @@ setup_update(dns_rpz_zone_t *rpz) {
                      ISC_LOG_DEBUG(1), "rpz: %s: using hashtable size %d",
                      domain, hashsize);
 
-       isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize);
+       isc_ht_init(&rpz->newnodes, rpz->rpzs->mctx, hashsize,
+                   ISC_HT_CASE_SENSITIVE);
 
        result = dns_db_createiterator(rpz->updb, DNS_DB_NONSEC3, &rpz->updbit);
        if (result != ISC_R_SUCCESS) {
index 95ee13bc4891b75013fb29c29d18a337d91d08c2..eaf2b3c5085127fbf77a7b0b8e529bf565d918ca 100644 (file)
@@ -27,9 +27,22 @@ typedef struct isc_ht_node isc_ht_node_t;
 #define ISC_HT_MAGIC    ISC_MAGIC('H', 'T', 'a', 'b')
 #define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
 
+#define HT_NO_BITS    0
+#define HT_MIN_BITS   1
+#define HT_MAX_BITS   32
+#define HT_OVERCOMMIT 3
+
+#define HT_NEXTTABLE(idx)      ((idx == 0) ? 1 : 0)
+#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht))
+
+#define GOLDEN_RATIO_32 0x61C88647
+
+#define HASHSIZE(bits) (UINT64_C(1) << (bits))
+
 struct isc_ht_node {
        void *value;
        isc_ht_node_t *next;
+       uint32_t hashval;
        size_t keysize;
        unsigned char key[];
 };
@@ -37,41 +50,208 @@ struct isc_ht_node {
 struct isc_ht {
        unsigned int magic;
        isc_mem_t *mctx;
-       size_t size;
-       size_t mask;
-       unsigned int count;
-       isc_ht_node_t **table;
+       size_t count;
+       bool case_sensitive;
+       size_t size[2];
+       uint8_t hashbits[2];
+       isc_ht_node_t **table[2];
+       uint8_t hindex;
+       uint32_t hiter; /* rehashing iterator */
 };
 
 struct isc_ht_iter {
        isc_ht_t *ht;
        size_t i;
+       uint8_t hindex;
        isc_ht_node_t *cur;
 };
 
+static isc_ht_node_t *
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
+            const uint32_t keysize, const uint32_t hashval, const uint8_t idx);
+static void
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+           const uint32_t hashval, const uint8_t idx, void *value);
+static isc_result_t
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+              const uint32_t hashval, const uint8_t idx);
+
+static uint32_t
+rehash_bits(isc_ht_t *ht, size_t newcount);
+
+static void
+hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits);
+static void
+hashtable_free(isc_ht_t *ht, const uint8_t idx);
+static void
+hashtable_rehash(isc_ht_t *ht, uint32_t newbits);
+static void
+hashtable_rehash_one(isc_ht_t *ht);
+static void
+maybe_rehash(isc_ht_t *ht, size_t newcount);
+
+static isc_result_t
+isc__ht_iter_next(isc_ht_iter_t *it);
+
+static bool
+isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval,
+                  const uint8_t *key, uint32_t keysize) {
+       return (node->hashval == hashval && node->keysize == keysize &&
+               memcmp(node->key, key, keysize) == 0);
+}
+
+static uint32_t
+hash_32(uint32_t val, unsigned int bits) {
+       REQUIRE(bits <= HT_MAX_BITS);
+       /* High bits are more random. */
+       return (val * GOLDEN_RATIO_32 >> (32 - bits));
+}
+
+static bool
+rehashing_in_progress(const isc_ht_t *ht) {
+       return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL);
+}
+
+static bool
+hashtable_is_overcommited(isc_ht_t *ht) {
+       return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT));
+}
+
+static uint32_t
+rehash_bits(isc_ht_t *ht, size_t newcount) {
+       uint32_t newbits = ht->hashbits[ht->hindex];
+
+       while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) {
+               newbits += 1;
+       }
+
+       return (newbits);
+}
+
+/*
+ * Rebuild the hashtable to reduce the load factor
+ */
+static void
+hashtable_rehash(isc_ht_t *ht, uint32_t newbits) {
+       uint8_t oldindex = ht->hindex;
+       uint32_t oldbits = ht->hashbits[oldindex];
+       uint8_t newindex = HT_NEXTTABLE(oldindex);
+
+       REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS);
+       REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS);
+       REQUIRE(ht->table[oldindex] != NULL);
+
+       REQUIRE(newbits <= HT_MAX_BITS);
+       REQUIRE(ht->hashbits[newindex] == HT_NO_BITS);
+       REQUIRE(ht->table[newindex] == NULL);
+
+       REQUIRE(newbits > oldbits);
+
+       hashtable_new(ht, newindex, newbits);
+
+       ht->hindex = newindex;
+
+       hashtable_rehash_one(ht);
+}
+
+static void
+hashtable_rehash_one(isc_ht_t *ht) {
+       isc_ht_node_t **newtable = ht->table[ht->hindex];
+       uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)];
+       isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)];
+       isc_ht_node_t *node = NULL;
+       isc_ht_node_t *nextnode;
+
+       /* Find first non-empty node */
+       while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) {
+               ht->hiter++;
+       }
+
+       /* Rehashing complete */
+       if (ht->hiter == oldsize) {
+               hashtable_free(ht, HT_NEXTTABLE(ht->hindex));
+               ht->hiter = 0;
+               return;
+       }
+
+       /* Move the first non-empty node from old hashtable to new hashtable */
+       for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) {
+               uint32_t hash = hash_32(node->hashval,
+                                       ht->hashbits[ht->hindex]);
+               nextnode = node->next;
+               node->next = newtable[hash];
+               newtable[hash] = node;
+       }
+
+       oldtable[ht->hiter] = NULL;
+
+       ht->hiter++;
+}
+
+static void
+maybe_rehash(isc_ht_t *ht, size_t newcount) {
+       uint32_t newbits = rehash_bits(ht, newcount);
+
+       if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) {
+               hashtable_rehash(ht, newbits);
+       }
+}
+
+static void
+hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) {
+       size_t size;
+       REQUIRE(ht->hashbits[idx] == HT_NO_BITS);
+       REQUIRE(ht->table[idx] == NULL);
+       REQUIRE(bits >= HT_MIN_BITS);
+       REQUIRE(bits <= HT_MAX_BITS);
+
+       ht->hashbits[idx] = bits;
+       ht->size[idx] = HASHSIZE(ht->hashbits[idx]);
+
+       size = ht->size[idx] * sizeof(isc_ht_node_t *);
+
+       ht->table[idx] = isc_mem_get(ht->mctx, size);
+       memset(ht->table[idx], 0, size);
+}
+
+static void
+hashtable_free(isc_ht_t *ht, const uint8_t idx) {
+       size_t size = ht->size[idx] * sizeof(isc_ht_node_t *);
+
+       for (size_t i = 0; i < ht->size[idx]; i++) {
+               isc_ht_node_t *node = ht->table[idx][i];
+               while (node != NULL) {
+                       isc_ht_node_t *next = node->next;
+                       ht->count--;
+                       isc_mem_put(ht->mctx, node,
+                                   sizeof(*node) + node->keysize);
+                       node = next;
+               }
+       }
+
+       isc_mem_put(ht->mctx, ht->table[idx], size);
+       ht->hashbits[idx] = HT_NO_BITS;
+       ht->table[idx] = NULL;
+}
+
 void
-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) {
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
+           unsigned int options) {
        isc_ht_t *ht = NULL;
-       size_t i;
+       bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
 
        REQUIRE(htp != NULL && *htp == NULL);
        REQUIRE(mctx != NULL);
-       REQUIRE(bits >= 1 && bits <= (sizeof(size_t) * 8 - 1));
+       REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
 
-       ht = isc_mem_get(mctx, sizeof(struct isc_ht));
+       ht = isc_mem_get(mctx, sizeof(*ht));
+       *ht = (isc_ht_t){
+               .case_sensitive = case_sensitive,
+       };
 
-       ht->mctx = NULL;
        isc_mem_attach(mctx, &ht->mctx);
 
-       ht->size = ((size_t)1 << bits);
-       ht->mask = ((size_t)1 << bits) - 1;
-       ht->count = 0;
-
-       ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t *));
-
-       for (i = 0; i < ht->size; i++) {
-               ht->table[i] = NULL;
-       }
+       hashtable_new(ht, 0, bits);
 
        ht->magic = ISC_HT_MAGIC;
 
@@ -81,128 +261,182 @@ isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) {
 void
 isc_ht_destroy(isc_ht_t **htp) {
        isc_ht_t *ht;
-       size_t i;
 
        REQUIRE(htp != NULL);
+       REQUIRE(ISC_HT_VALID(*htp));
 
        ht = *htp;
        *htp = NULL;
-
-       REQUIRE(ISC_HT_VALID(ht));
-
        ht->magic = 0;
 
-       for (i = 0; i < ht->size; i++) {
-               isc_ht_node_t *node = ht->table[i];
-               while (node != NULL) {
-                       isc_ht_node_t *next = node->next;
-                       ht->count--;
-                       isc_mem_put(ht->mctx, node,
-                                   offsetof(isc_ht_node_t, key) +
-                                           node->keysize);
-                       node = next;
+       for (size_t i = 0; i <= 1; i++) {
+               if (ht->table[i] != NULL) {
+                       hashtable_free(ht, i);
                }
        }
 
        INSIST(ht->count == 0);
 
-       isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t *));
-       isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
+       isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht));
 }
 
-isc_result_t
-isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
-          void *value) {
+static void
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+           const uint32_t hashval, const uint8_t idx, void *value) {
        isc_ht_node_t *node;
        uint32_t hash;
 
+       hash = hash_32(hashval, ht->hashbits[idx]);
+
+       node = isc_mem_get(ht->mctx, sizeof(*node) + keysize);
+       *node = (isc_ht_node_t){
+               .keysize = keysize,
+               .hashval = hashval,
+               .next = ht->table[idx][hash],
+               .value = value,
+       };
+
+       memmove(node->key, key, keysize);
+
+       ht->count++;
+       ht->table[idx][hash] = node;
+}
+
+isc_result_t
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+          void *value) {
+       uint32_t hashval;
+
        REQUIRE(ISC_HT_VALID(ht));
        REQUIRE(key != NULL && keysize > 0);
 
-       hash = isc_hash_function(key, keysize, true);
-       node = ht->table[hash & ht->mask];
-       while (node != NULL) {
-               if (keysize == node->keysize &&
-                   memcmp(key, node->key, keysize) == 0)
-               {
-                       return (ISC_R_EXISTS);
-               }
-               node = node->next;
+       if (rehashing_in_progress(ht)) {
+               /* Rehash in progress */
+               hashtable_rehash_one(ht);
+       } else if (hashtable_is_overcommited(ht)) {
+               /* Rehash requested */
+               maybe_rehash(ht, ht->count);
        }
 
-       node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize);
+       hashval = isc_hash32(key, keysize, ht->case_sensitive);
 
-       memmove(node->key, key, keysize);
-       node->keysize = keysize;
-       node->next = ht->table[hash & ht->mask];
-       node->value = value;
+       if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) {
+               return (ISC_R_EXISTS);
+       }
+
+       isc__ht_add(ht, key, keysize, hashval, ht->hindex, value);
 
-       ht->count++;
-       ht->table[hash & ht->mask] = node;
        return (ISC_R_SUCCESS);
 }
 
+static isc_ht_node_t *
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
+            const uint32_t keysize, const uint32_t hashval,
+            const uint8_t idx) {
+       uint32_t hash;
+       uint8_t findex = idx;
+
+nexttable:
+       hash = hash_32(hashval, ht->hashbits[findex]);
+       for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL;
+            node = node->next)
+       {
+               if (isc__ht_node_match(node, hashval, key, keysize)) {
+                       return (node);
+               }
+       }
+       if (TRY_NEXTTABLE(findex, ht)) {
+               /*
+                * Rehashing in progress, check the other table
+                */
+               findex = HT_NEXTTABLE(findex);
+               goto nexttable;
+       }
+
+       return (NULL);
+}
+
 isc_result_t
-isc_ht_find(const isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
-           void **valuep) {
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+           const uint32_t keysize, void **valuep) {
+       uint32_t hashval;
        isc_ht_node_t *node;
-       uint32_t hash;
 
        REQUIRE(ISC_HT_VALID(ht));
        REQUIRE(key != NULL && keysize > 0);
        REQUIRE(valuep == NULL || *valuep == NULL);
 
-       hash = isc_hash_function(key, keysize, true);
-       node = ht->table[hash & ht->mask];
-       while (node != NULL) {
-               if (keysize == node->keysize &&
-                   memcmp(key, node->key, keysize) == 0)
-               {
-                       if (valuep != NULL) {
-                               *valuep = node->value;
-                       }
-                       return (ISC_R_SUCCESS);
-               }
-               node = node->next;
+       hashval = isc_hash32(key, keysize, ht->case_sensitive);
+
+       node = isc__ht_find(ht, key, keysize, hashval, ht->hindex);
+       if (node == NULL) {
+               return (ISC_R_NOTFOUND);
        }
 
-       return (ISC_R_NOTFOUND);
+       if (valuep != NULL) {
+               *valuep = node->value;
+       }
+       return (ISC_R_SUCCESS);
 }
 
-isc_result_t
-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) {
-       isc_ht_node_t *node, *prev;
+static isc_result_t
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+              const uint32_t hashval, const uint8_t idx) {
+       isc_ht_node_t *prev = NULL;
        uint32_t hash;
 
-       REQUIRE(ISC_HT_VALID(ht));
-       REQUIRE(key != NULL && keysize > 0);
+       hash = hash_32(hashval, ht->hashbits[idx]);
 
-       prev = NULL;
-       hash = isc_hash_function(key, keysize, true);
-       node = ht->table[hash & ht->mask];
-       while (node != NULL) {
-               if (keysize == node->keysize &&
-                   memcmp(key, node->key, keysize) == 0)
-               {
+       for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL;
+            prev = node, node = node->next)
+       {
+               if (isc__ht_node_match(node, hashval, key, keysize)) {
                        if (prev == NULL) {
-                               ht->table[hash & ht->mask] = node->next;
+                               ht->table[idx][hash] = node->next;
                        } else {
                                prev->next = node->next;
                        }
                        isc_mem_put(ht->mctx, node,
-                                   offsetof(isc_ht_node_t, key) +
-                                           node->keysize);
+                                   sizeof(*node) + node->keysize);
                        ht->count--;
 
                        return (ISC_R_SUCCESS);
                }
-
-               prev = node;
-               node = node->next;
        }
+
        return (ISC_R_NOTFOUND);
 }
 
+isc_result_t
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) {
+       uint32_t hashval;
+       uint8_t hindex;
+       isc_result_t result;
+
+       REQUIRE(ISC_HT_VALID(ht));
+       REQUIRE(key != NULL && keysize > 0);
+
+       if (rehashing_in_progress(ht)) {
+               /* Rehash in progress */
+               hashtable_rehash_one(ht);
+       }
+
+       hindex = ht->hindex;
+       hashval = isc_hash32(key, keysize, ht->case_sensitive);
+nexttable:
+       result = isc__ht_delete(ht, key, keysize, hashval, hindex);
+
+       if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) {
+               /*
+                * Rehashing in progress, check the other table
+                */
+               hindex = HT_NEXTTABLE(hindex);
+               goto nexttable;
+       }
+
+       return (result);
+}
+
 void
 isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
        isc_ht_iter_t *it;
@@ -211,10 +445,10 @@ isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
        REQUIRE(itp != NULL && *itp == NULL);
 
        it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
-
-       it->ht = ht;
-       it->i = 0;
-       it->cur = NULL;
+       *it = (isc_ht_iter_t){
+               .ht = ht,
+               .hindex = ht->hindex,
+       };
 
        *itp = it;
 }
@@ -229,25 +463,46 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp) {
        it = *itp;
        *itp = NULL;
        ht = it->ht;
-       isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t));
+       isc_mem_put(ht->mctx, it, sizeof(*it));
 }
 
 isc_result_t
 isc_ht_iter_first(isc_ht_iter_t *it) {
+       isc_ht_t *ht;
+
        REQUIRE(it != NULL);
 
+       ht = it->ht;
+
+       it->hindex = ht->hindex;
        it->i = 0;
-       while (it->i < it->ht->size && it->ht->table[it->i] == NULL) {
+
+       return (isc__ht_iter_next(it));
+}
+
+static isc_result_t
+isc__ht_iter_next(isc_ht_iter_t *it) {
+       isc_ht_t *ht = it->ht;
+
+       while (it->i < ht->size[it->hindex] &&
+              ht->table[it->hindex][it->i] == NULL)
+       {
                it->i++;
        }
 
-       if (it->i == it->ht->size) {
-               return (ISC_R_NOMORE);
+       if (it->i < ht->size[it->hindex]) {
+               it->cur = ht->table[it->hindex][it->i];
+
+               return (ISC_R_SUCCESS);
        }
 
-       it->cur = it->ht->table[it->i];
+       if (TRY_NEXTTABLE(it->hindex, ht)) {
+               it->hindex = HT_NEXTTABLE(it->hindex);
+               it->i = 0;
+               return (isc__ht_iter_next(it));
+       }
 
-       return (ISC_R_SUCCESS);
+       return (ISC_R_NOMORE);
 }
 
 isc_result_t
@@ -256,60 +511,36 @@ isc_ht_iter_next(isc_ht_iter_t *it) {
        REQUIRE(it->cur != NULL);
 
        it->cur = it->cur->next;
-       if (it->cur == NULL) {
-               do {
-                       it->i++;
-               } while (it->i < it->ht->size && it->ht->table[it->i] == NULL);
-               if (it->i >= it->ht->size) {
-                       return (ISC_R_NOMORE);
-               }
-               it->cur = it->ht->table[it->i];
+
+       if (it->cur != NULL) {
+               return (ISC_R_SUCCESS);
        }
 
-       return (ISC_R_SUCCESS);
+       it->i++;
+
+       return (isc__ht_iter_next(it));
 }
 
 isc_result_t
 isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
        isc_result_t result = ISC_R_SUCCESS;
-       isc_ht_node_t *to_delete = NULL;
-       isc_ht_node_t *prev = NULL;
-       isc_ht_node_t *node = NULL;
-       uint32_t hash;
+       isc_ht_node_t *dnode = NULL;
+       uint8_t dindex;
        isc_ht_t *ht;
+       isc_result_t dresult;
+
        REQUIRE(it != NULL);
        REQUIRE(it->cur != NULL);
-       to_delete = it->cur;
-       ht = it->ht;
 
-       it->cur = it->cur->next;
-       if (it->cur == NULL) {
-               do {
-                       it->i++;
-               } while (it->i < ht->size && ht->table[it->i] == NULL);
-               if (it->i >= ht->size) {
-                       result = ISC_R_NOMORE;
-               } else {
-                       it->cur = ht->table[it->i];
-               }
-       }
+       ht = it->ht;
+       dnode = it->cur;
+       dindex = it->hindex;
 
-       hash = isc_hash_function(to_delete->key, to_delete->keysize, true);
-       node = ht->table[hash & ht->mask];
-       while (node != to_delete) {
-               prev = node;
-               node = node->next;
-               INSIST(node != NULL);
-       }
+       result = isc_ht_iter_next(it);
 
-       if (prev == NULL) {
-               ht->table[hash & ht->mask] = node->next;
-       } else {
-               prev->next = node->next;
-       }
-       isc_mem_put(ht->mctx, node,
-                   offsetof(isc_ht_node_t, key) + node->keysize);
-       ht->count--;
+       dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval,
+                                dindex);
+       INSIST(dresult == ISC_R_SUCCESS);
 
        return (result);
 }
@@ -334,8 +565,8 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
        *keysize = it->cur->keysize;
 }
 
-unsigned int
-isc_ht_count(isc_ht_t *ht) {
+size_t
+isc_ht_count(const isc_ht_t *ht) {
        REQUIRE(ISC_HT_VALID(ht));
 
        return (ht->count);
index 1410a4a8100cbf4311a790b8787e873777c1ad07..163fbefb793381c651de2d46f07f514e7227a0e8 100644 (file)
 typedef struct isc_ht     isc_ht_t;
 typedef struct isc_ht_iter isc_ht_iter_t;
 
+enum { ISC_HT_CASE_SENSITIVE = 0x00, ISC_HT_CASE_INSENSITIVE = 0x01 };
+
 /*%
  * Initialize hashtable at *htp, using memory context and size of (1<<bits)
  *
+ * If 'options' contains ISC_HT_CASE_INSENSITIVE, then upper- and lower-case
+ * letters in key values will generate the same hash values; this can be used
+ * when the key for a hash table is a DNS name.
+ *
  * Requires:
  *\li  'htp' is not NULL and '*htp' is NULL.
  *\li  'mctx' is a valid memory context.
@@ -34,7 +40,8 @@ typedef struct isc_ht_iter isc_ht_iter_t;
  *
  */
 void
-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits);
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
+           unsigned int options);
 
 /*%
  * Destroy hashtable, freeing everything
@@ -51,6 +58,7 @@ isc_ht_destroy(isc_ht_t **htp);
  *
  * Requires:
  *\li  'ht' is a valid hashtable
+ *\li   write-lock
  *
  * Returns:
  *\li  #ISC_R_NOMEMORY         -- not enough memory to create pool
@@ -58,7 +66,7 @@ isc_ht_destroy(isc_ht_t **htp);
  *\li  #ISC_R_SUCCESS          -- all is well.
  */
 isc_result_t
-isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
           void *value);
 
 /*%
@@ -69,27 +77,29 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
  *
  * Requires:
  * \li 'ht' is a valid hashtable
+ * \li  read-lock
  *
  * Returns:
  * \li #ISC_R_SUCCESS          -- success
  * \li #ISC_R_NOTFOUND         -- key not found
  */
 isc_result_t
-isc_ht_find(const isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
-           void **valuep);
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+           const uint32_t keysize, void **valuep);
 
 /*%
  * Delete node from hashtable
  *
  * Requires:
  *\li  ht is a valid hashtable
+ *\li   write-lock
  *
  * Returns:
  *\li  #ISC_R_NOTFOUND         -- key not found
  *\li  #ISC_R_SUCCESS          -- all is well
  */
 isc_result_t
-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize);
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize);
 
 /*%
  * Create an iterator for the hashtable; point '*itp' to it.
@@ -177,5 +187,5 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize);
  * Requires:
  *\li  'ht' is a valid hashtable
  */
-unsigned int
-isc_ht_count(isc_ht_t *ht);
+size_t
+isc_ht_count(const isc_ht_t *ht);
index 9be787e9d76a14d07c6e53e2cf7bf4374b2c5e08..978f86dd992192080d0fce9031244781be99e99a 100644 (file)
@@ -1112,7 +1112,7 @@ isc_tlsctx_cache_create(isc_mem_t *mctx, isc_tlsctx_cache_t **cachep) {
        isc_refcount_init(&nc->references, 1);
        isc_mem_attach(mctx, &nc->mctx);
 
-       isc_ht_init(&nc->data, mctx, 5);
+       isc_ht_init(&nc->data, mctx, 5, ISC_HT_CASE_SENSITIVE);
        isc_rwlock_init(&nc->rwlock, 0, 0);
 
        *cachep = nc;
@@ -1408,7 +1408,7 @@ isc_tlsctx_client_session_cache_create(
        isc_mem_attach(mctx, &nc->mctx);
        isc_tlsctx_attach(ctx, &nc->ctx);
 
-       isc_ht_init(&nc->buckets, mctx, 5);
+       isc_ht_init(&nc->buckets, mctx, 5, ISC_HT_CASE_SENSITIVE);
        ISC_LIST_INIT(nc->lru_entries);
        isc_mutex_init(&nc->lock);
 
index b6026e190d7666f8faebb123c3cca5a34d88adf6..e656f03c84966cabc6ca84c56b818151dbb60f06 100644 (file)
@@ -44,7 +44,7 @@ test_ht_full(int bits, uintptr_t count) {
        isc_result_t result;
        uintptr_t i;
 
-       isc_ht_init(&ht, mctx, bits);
+       isc_ht_init(&ht, mctx, bits, ISC_HT_CASE_SENSITIVE);
        assert_non_null(ht);
 
        for (i = 1; i < count; i++) {
@@ -189,7 +189,7 @@ test_ht_iterator(void) {
        unsigned char key[16];
        size_t tksize;
 
-       isc_ht_init(&ht, mctx, 16);
+       isc_ht_init(&ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
        assert_non_null(ht);
        for (i = 1; i <= count; i++) {
                /*