From: Ondřej Surý Date: Wed, 11 Oct 2023 11:54:50 +0000 (+0200) Subject: Backport isc_ht API changes from BIND 9.18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2fc28056b33018f7f78b625409eb44c32d5c9b11;p=thirdparty%2Fbind9.git Backport isc_ht API changes from BIND 9.18 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) --- diff --git a/lib/dns/catz.c b/lib/dns/catz.c index 304b0386372..df101e32255 100644 --- a/lib/dns/catz.c +++ b/lib/dns/catz.c @@ -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); } diff --git a/lib/dns/message.c b/lib/dns/message.c index 1d2b08de9a5..258e83a29a4 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -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; diff --git a/lib/isc/ht.c b/lib/isc/ht.c index 3234ea80292..c33ad6eb8d2 100644 --- a/lib/isc/ht.c +++ b/lib/isc/ht.c @@ -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/. @@ -10,208 +12,433 @@ */ #include - #include #include #include #include -#include #include #include #include +#include #include - 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<mask = ((size_t)1<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); } diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h index a1315b0c5ad..163fbefb793 100644 --- a/lib/isc/include/isc/ht.h +++ b/lib/isc/include/isc/ht.h @@ -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/. @@ -11,32 +13,35 @@ /* ! \file */ -#ifndef ISC_HT_H -#define ISC_HT_H 1 +#pragma once #include #include -#include #include +#include -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<=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); diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c index 79e4260c2b5..2fe93602bd5 100644 --- a/lib/isc/tests/ht_test.c +++ b/lib/isc/tests/ht_test.c @@ -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;