]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Backport isc_ht API changes from BIND 9.18
authorOndřej Surý <ondrej@isc.org>
Wed, 11 Oct 2023 11:54:50 +0000 (13:54 +0200)
committerMichał Kępień <michal@isc.org>
Fri, 5 Jan 2024 10:52:05 +0000 (11:52 +0100)
To prevent allocating large hashtable in dns_message, we need to
backport the improvements to isc_ht API from BIND 9.18+ that includes
support for case insensitive keys and incremental rehashing of the
hashtables.

bin/plugins/filter-aaaa.c
lib/dns/catz.c
lib/dns/message.c
lib/dns/rpz.c
lib/isc/ht.c
lib/isc/include/isc/ht.h
lib/isc/tests/ht_test.c

index 265da26d6016fcbc756994c7983ebf4810d4e2ab..449124d0ae04df63fd4cfa9d7497a9836c489271 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 2c00d6eba9b42b69e580a3d4c26f144a18175c59..0275a4fdbfe75f6c4087a7037393452dd400843b 100644 (file)
@@ -423,9 +423,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);
 
@@ -610,7 +610,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;
@@ -662,7 +662,7 @@ 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, 16);
+       isc_ht_init(&new_zone->entries, catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
 
        new_zone->updatetimer = NULL;
        result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, NULL,
index 2f0075d5b37e51c6c0e8fa15160f6552f58c0e2c..d216cb17426f472df3c23d4f9ebe175bbf1afbca 100644 (file)
@@ -804,16 +804,12 @@ dns_message_detach(dns_message_t **messagep) {
 
 static isc_result_t
 name_hash_add(isc_ht_t *ht, dns_name_t *name, dns_name_t **foundp) {
-       dns_fixedname_t fixed;
-       dns_name_t *key = dns_fixedname_initname(&fixed);
-       dns_name_downcase(name, key, NULL);
-
-       isc_result_t result = isc_ht_find(ht, key->ndata, key->length,
+       isc_result_t result = isc_ht_find(ht, name->ndata, name->length,
                                          (void **)foundp);
        if (result == ISC_R_SUCCESS) {
                return (ISC_R_EXISTS);
        }
-       result = isc_ht_add(ht, key->ndata, key->length, (void *)name);
+       result = isc_ht_add(ht, name->ndata, name->length, (void *)name);
        INSIST(result == ISC_R_SUCCESS);
        return (ISC_R_SUCCESS);
 }
@@ -1150,7 +1146,8 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
                 * Can't ask the same question twice.
                 */
                if (name->ht == NULL) {
-                       isc_ht_init(&name->ht, msg->mctx, 1);
+                       isc_ht_init(&name->ht, msg->mctx, 1,
+                                   ISC_HT_CASE_SENSITIVE);
                        free_ht = true;
 
                        INSIST(ISC_LIST_HEAD(name->list) ==
@@ -1621,7 +1618,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
                        }
 
                        if (name->ht == NULL) {
-                               isc_ht_init(&name->ht, msg->mctx, 1);
+                               isc_ht_init(&name->ht, msg->mctx, 1,
+                                           ISC_HT_CASE_SENSITIVE);
                                free_ht = true;
 
                                INSIST(ISC_LIST_HEAD(name->list) ==
index 20db72fb15c69c3c2b3093e7722ed14d529d51be..fdc975976b0e38a2a08e8c69085fe42023c048bd 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 07a36b4cffba9189a1c55319afc32e5be3cbd540..eaf2b3c5085127fbf77a7b0b8e529bf565d918ca 100644 (file)
@@ -27,51 +27,231 @@ 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[FLEXIBLE_ARRAY_MEMBER];
+       unsigned char key[];
 };
 
 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 f1386bb51541bc3b971d52c97a1a5cd9aa7a644c..163fbefb793381c651de2d46f07f514e7227a0e8 100644 (file)
@@ -13,8 +13,7 @@
 
 /* ! \file */
 
-#ifndef ISC_HT_H
-#define ISC_HT_H 1
+#pragma once
 
 #include <inttypes.h>
 #include <string.h>
 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.
@@ -35,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
@@ -52,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
@@ -59,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);
 
 /*%
@@ -70,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.
@@ -178,6 +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);
-#endif /* ifndef ISC_HT_H */
+size_t
+isc_ht_count(const isc_ht_t *ht);
index 30cc615a9eb82fcf82147f6fde499f6d06b58e25..bd96aa861513d716ac2badd65f268ae94bcf9e11 100644 (file)
@@ -61,7 +61,7 @@ test_ht_full(int bits, uintptr_t count) {
        isc_result_t result;
        uintptr_t i;
 
-       isc_ht_init(&ht, test_mctx, bits);
+       isc_ht_init(&ht, test_mctx, bits, ISC_HT_CASE_SENSITIVE);
        assert_non_null(ht);
 
        for (i = 1; i < count; i++) {
@@ -206,7 +206,7 @@ test_ht_iterator() {
        unsigned char key[16];
        size_t tksize;
 
-       isc_ht_init(&ht, test_mctx, 16);
+       isc_ht_init(&ht, test_mctx, 16, ISC_HT_CASE_SENSITIVE);
        assert_non_null(ht);
        for (i = 1; i <= count; i++) {
                /*