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
result = ISC_R_SUCCESS;
-cleanup:
if (iter1 != NULL)
isc_ht_iter_destroy(&iter1);
if (iter2 != NULL)
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;
cleanup_ht:
isc_ht_destroy(&new_zones->zones);
- cleanup_refcount:
isc_refcount_destroy(&new_zones->refs);
cleanup_mutex:
isc_mutex_destroy(&new_zones->lock);
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,
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));
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))
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;)
{
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))
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;)
{
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);
}
/*
* 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;
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
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
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);
}
}
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);
*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);
}
/*
* 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
*
* Requires:
*\li 'ht' is a valid hashtable
+ *\li write-lock
*
* Returns:
*\li #ISC_R_NOMEMORY -- not enough memory to create pool
*\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
*/
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.
*\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);
/*%
*\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
*\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
*\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
*
* 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);