]> 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>
Thu, 22 Feb 2024 11:00:47 +0000 (12:00 +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.

(cherry picked from commit a4baf324159ec3764195c949cb56c861d9f173ff)

lib/dns/catz.c
lib/dns/message.c
lib/isc/ht.c
lib/isc/include/isc/ht.h
lib/isc/tests/ht_test.c

index 304b038637221545331bd6d663e4e27d62fe5b74..df101e32255af7c7ed6121abc9c3d859891a15ec 100644 (file)
@@ -398,33 +398,21 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
 
        dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
 
-       result = isc_ht_init(&toadd, target->catzs->mctx, 16);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
 
-       result = isc_ht_init(&tomod, target->catzs->mctx, 16);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
 
-       result = isc_ht_iter_create(newzone->entries, &iter1);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       isc_ht_iter_create(newzone->entries, &iter1);
 
-       result = isc_ht_iter_create(target->entries, &iter2);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       isc_ht_iter_create(target->entries, &iter2);
 
        /*
         * We can create those iterators now, even though toadd and tomod are
         * empty
         */
-       result = isc_ht_iter_create(toadd, &iteradd);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       isc_ht_iter_create(toadd, &iteradd);
 
-       result = isc_ht_iter_create(tomod, &itermod);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
+       isc_ht_iter_create(tomod, &itermod);
 
        /*
         * First - walk the new zone and find all nodes that are not in the
@@ -570,7 +558,6 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
 
        result = ISC_R_SUCCESS;
 
-cleanup:
        if (iter1 != NULL)
                isc_ht_iter_destroy(&iter1);
        if (iter2 != NULL)
@@ -610,9 +597,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
        if (result != ISC_R_SUCCESS)
                goto cleanup_mutex;
 
-       result = isc_ht_init(&new_zones->zones, mctx, 4);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_refcount;
+       isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE);
 
        isc_mem_attach(mctx, &new_zones->mctx);
        new_zones->zmm = zmm;
@@ -629,7 +614,6 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
 
   cleanup_ht:
        isc_ht_destroy(&new_zones->zones);
-  cleanup_refcount:
        isc_refcount_destroy(&new_zones->refs);
   cleanup_mutex:
        isc_mutex_destroy(&new_zones->lock);
@@ -672,9 +656,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
        if (result != ISC_R_SUCCESS)
                goto cleanup_newzone;
 
-       result = isc_ht_init(&new_zone->entries, catzs->mctx, 4);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_name;
+       isc_ht_init(&new_zone->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE);
 
        new_zone->updatetimer = NULL;
        result = isc_timer_create(catzs->timermgr, isc_timertype_inactive,
@@ -703,7 +685,6 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
 
   cleanup_ht:
        isc_ht_destroy(&new_zone->entries);
-  cleanup_name:
        dns_name_free(&new_zone->name, catzs->mctx);
   cleanup_newzone:
        isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
@@ -805,8 +786,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) {
        if (refs == 0) {
                isc_mem_t *mctx = zone->catzs->mctx;
                if (zone->entries != NULL) {
-                       result = isc_ht_iter_create(zone->entries, &iter);
-                       INSIST(result == ISC_R_SUCCESS);
+                       isc_ht_iter_create(zone->entries, &iter);
                        for (result = isc_ht_iter_first(iter);
                             result == ISC_R_SUCCESS;
                             result = isc_ht_iter_delcurrent_next(iter))
@@ -865,8 +845,7 @@ dns_catz_catzs_detach(dns_catz_zones_t **catzsp) {
                catzs->magic = 0;
                DESTROYLOCK(&catzs->lock);
                if (catzs->zones != NULL) {
-                       result = isc_ht_iter_create(catzs->zones, &iter);
-                       INSIST(result == ISC_R_SUCCESS);
+                       isc_ht_iter_create(catzs->zones, &iter);
                        for (result = isc_ht_iter_first(iter);
                             result == ISC_R_SUCCESS;)
                        {
@@ -1996,8 +1975,7 @@ dns_catz_prereconfig(dns_catz_zones_t *catzs) {
 
        REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
 
-       result = isc_ht_iter_create(catzs->zones, &iter);
-       INSIST(result == ISC_R_SUCCESS);
+       isc_ht_iter_create(catzs->zones, &iter);
        for (result = isc_ht_iter_first(iter);
             result == ISC_R_SUCCESS;
             result = isc_ht_iter_next(iter))
@@ -2019,8 +1997,7 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
        REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
 
        LOCK(&catzs->lock);
-       result = isc_ht_iter_create(catzs->zones, &iter);
-       INSIST(result == ISC_R_SUCCESS);
+       isc_ht_iter_create(catzs->zones, &iter);
        for (result = isc_ht_iter_first(iter);
             result == ISC_R_SUCCESS;)
        {
@@ -2062,5 +2039,6 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
 isc_result_t
 dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
        REQUIRE(DNS_CATZ_ZONE_VALID(catz));
-       return (isc_ht_iter_create(catz->entries, itp));
+       isc_ht_iter_create(catz->entries, itp);
+       return (ISC_R_SUCCESS);
 }
index 1d2b08de9a54b27ad1033ed023ddad4689803915..258e83a29a48b7aadbaaccd2d9a28f6e8ff2ce43 100644 (file)
@@ -868,16 +868,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);
 }
@@ -1226,7 +1222,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;
 
                        dns_rdataset_t *old_rdataset = NULL;
index 3234ea80292a4e3262b41396ea76c98b2cadd143..c33ad6eb8d2a22952471a58814ad26d2c2c63511 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
  *
+ * SPDX-License-Identifier: MPL-2.0
+ *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
  */
 
 #include <config.h>
-
 #include <inttypes.h>
 #include <string.h>
 
 #include <isc/hash.h>
 #include <isc/ht.h>
-#include <isc/types.h>
 #include <isc/magic.h>
 #include <isc/mem.h>
 #include <isc/result.h>
+#include <isc/types.h>
 #include <isc/util.h>
 
-
 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 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;
 };
 
-isc_result_t
-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) {
-       isc_ht_t *ht = NULL;
-       size_t i;
+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);
+}
 
-       REQUIRE(htp != NULL && *htp == NULL);
-       REQUIRE(mctx != NULL);
-       REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1));
+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));
+}
 
-       ht = isc_mem_get(mctx, sizeof(struct isc_ht));
-       if (ht == NULL) {
-               return (ISC_R_NOMEMORY);
+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;
        }
 
-       ht->mctx = NULL;
-       isc_mem_attach(mctx, &ht->mctx);
+       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->size = ((size_t)1<<bits);
-       ht->mask = ((size_t)1<<bits)-1;
-       ht->count = 0;
+       ht->hindex = newindex;
 
-       ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t*));
-       if (ht->table == NULL) {
-               isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
-               return (ISC_R_NOMEMORY);
+       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;
        }
 
-       for (i = 0; i < ht->size; i++) {
-               ht->table[i] = NULL;
+       /* 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,
+           unsigned int options) {
+       isc_ht_t *ht = NULL;
+       bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
+
+       REQUIRE(htp != NULL && *htp == NULL);
+       REQUIRE(mctx != NULL);
+       REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
+
+       ht = isc_mem_get(mctx, sizeof(*ht));
+       *ht = (isc_ht_t){
+               .case_sensitive = case_sensitive,
+       };
+
+       isc_mem_attach(mctx, &ht->mctx);
+
+       hashtable_new(ht, 0, bits);
+
        ht->magic = ISC_HT_MAGIC;
 
        *htp = ht;
-       return (ISC_R_SUCCESS);
 }
 
 void
 isc_ht_destroy(isc_ht_t **htp) {
        isc_ht_t *ht;
-       size_t i;
 
        REQUIRE(htp != NULL);
-       REQUIRE(ISC_HT_VALID((*htp)));
+       REQUIRE(ISC_HT_VALID(*htp));
 
        ht = *htp;
+       *htp = NULL;
        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));
-
-       *htp = NULL;
+       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, NULL);
-       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);
-       if (node == NULL)
-               return (ISC_R_NOMEMORY);
+       hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL);
 
-       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)
-{
+           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, NULL);
-       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_hash_function(key, keysize, ht->case_sensitive, NULL);
+
+       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, NULL);
-       node = ht->table[hash & ht->mask];
-       while (node != NULL) {
-               if (keysize == node->keysize &&
-                   memcmp(key, node->key, keysize) == 0) {
-                       if (prev == NULL)
-                               ht->table[hash & ht->mask] = node->next;
-                       else
+       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[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_hash_function(key, keysize, ht->case_sensitive, NULL);
+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;
 
@@ -219,16 +446,12 @@ 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));
-       if (it == NULL)
-               return (ISC_R_NOMEMORY);
-
-       it->ht = ht;
-       it->i = 0;
-       it->cur = NULL;
+       *it = (isc_ht_iter_t){
+               .ht = ht,
+               .hindex = ht->hindex,
+       };
 
        *itp = it;
-
-       return (ISC_R_SUCCESS);
 }
 
 void
@@ -239,26 +462,48 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp) {
        REQUIRE(itp != NULL && *itp != NULL);
 
        it = *itp;
-       ht = it->ht;
-       isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t));
-
        *itp = NULL;
+       ht = it->ht;
+       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 < ht->size[it->hindex]) {
+               it->cur = ht->table[it->hindex][it->i];
 
-       if (it->i == it->ht->size)
-               return (ISC_R_NOMORE);
+               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
@@ -267,58 +512,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,
-                                NULL);
-       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);
 }
@@ -333,8 +556,8 @@ isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) {
 }
 
 void
-isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize)
-{
+isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
+                      size_t *keysize) {
        REQUIRE(it != NULL);
        REQUIRE(it->cur != NULL);
        REQUIRE(key != NULL && *key == NULL);
@@ -343,9 +566,9 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize)
        *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);
+       return (ht->count);
 }
index a1315b0c5ad2d19907024d21bbdce91ecb2b5ff4..163fbefb793381c651de2d46f07f514e7227a0e8 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
  *
+ * SPDX-License-Identifier: MPL-2.0
+ *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
 
 /* ! \file */
 
-#ifndef ISC_HT_H
-#define ISC_HT_H 1
+#pragma once
 
 #include <inttypes.h>
 #include <string.h>
 
-#include <isc/types.h>
 #include <isc/result.h>
+#include <isc/types.h>
 
-typedef struct isc_ht isc_ht_t;
+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.
  *\li  'bits' >=1 and 'bits' <=32
  *
- * Returns:
- *\li  #ISC_R_NOMEMORY         -- not enough memory to create pool
- *\li  #ISC_R_SUCCESS          -- all is well.
  */
-isc_result_t
-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits);
+void
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
+           unsigned int options);
 
 /*%
  * Destroy hashtable, freeing everything
@@ -53,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
@@ -60,15 +66,18 @@ 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,
-                  void *value);
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+          void *value);
 
 /*%
  * Find a node matching 'key'/'keysize' in hashtable 'ht';
- * if found, set 'value' to its value
+ * if found, set '*valuep' to its value. (If 'valuep' is NULL,
+ * then simply return SUCCESS or NOTFOUND to indicate whether the
+ * key exists in the hashtable.)
  *
  * Requires:
  * \li 'ht' is a valid hashtable
+ * \li  read-lock
  *
  * Returns:
  * \li #ISC_R_SUCCESS          -- success
@@ -76,20 +85,21 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize,
  */
 isc_result_t
 isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
-           uint32_t keysize, void **valuep);
+           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.
@@ -98,7 +108,7 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize);
  *\li  'ht' is a valid hashtable
  *\li  'itp' is non NULL and '*itp' is NULL.
  */
-isc_result_t
+void
 isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp);
 
 /*%
@@ -117,7 +127,7 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp);
  *\li  'it' is non NULL.
  *
  * Returns:
- * \li         #ISC_R_SUCCESS  -- success
+ * \li #ISC_R_SUCCESS  -- success
  * \li #ISC_R_NOMORE   -- no data in the hashtable
  */
 isc_result_t
@@ -130,7 +140,7 @@ isc_ht_iter_first(isc_ht_iter_t *it);
  *\li  'it' is non NULL.
  *
  * Returns:
- * \li         #ISC_R_SUCCESS  -- success
+ * \li #ISC_R_SUCCESS  -- success
  * \li #ISC_R_NOMORE   -- end of hashtable reached
  */
 isc_result_t
@@ -143,13 +153,12 @@ isc_ht_iter_next(isc_ht_iter_t *it);
  *\li  'it' is non NULL.
  *
  * Returns:
- * \li         #ISC_R_SUCCESS  -- success
+ * \li #ISC_R_SUCCESS  -- success
  * \li #ISC_R_NOMORE   -- end of hashtable reached
  */
 isc_result_t
 isc_ht_iter_delcurrent_next(isc_ht_iter_t *it);
 
-
 /*%
  * Set 'value' to the current value under the iterator
  *
@@ -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
+size_t
+isc_ht_count(const isc_ht_t *ht);
index 79e4260c2b5a3b149757deffc6e8b87498bf9cf1..2fe93602bd52d9553a55e97cc097006354a027c5 100644 (file)
@@ -60,8 +60,7 @@ test_ht_full(int bits, uintptr_t count) {
                                  NULL, &mctx, 0);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = isc_ht_init(&ht, mctx, bits);
-       assert_int_equal(result, ISC_R_SUCCESS);
+       isc_ht_init(&ht, mctx, bits, ISC_HT_CASE_SENSITIVE);
        assert_non_null(ht);
 
        for (i = 1; i < count; i++) {
@@ -214,8 +213,7 @@ test_ht_iterator() {
                                  NULL, &mctx, 0);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = isc_ht_init(&ht, mctx, 16);
-       assert_int_equal(result, ISC_R_SUCCESS);
+       isc_ht_init(&ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
        assert_non_null(ht);
        for (i = 1; i <= count; i++) {
                /*
@@ -229,8 +227,7 @@ test_ht_iterator() {
        }
 
        walked = 0;
-       result = isc_ht_iter_create(ht, &iter);
-       assert_int_equal(result, ISC_R_SUCCESS);
+       isc_ht_iter_create(ht, &iter);
 
        for (result = isc_ht_iter_first(iter);
             result == ISC_R_SUCCESS;