mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-05 17:15:31 +00:00
When the round robin hashing reorders the map entries on deletion, we were adjusting the iterator table size only when the reordering was happening at the internal table boundary. The iterator table size had to be reduced by one to prevent seeing the entry that resized on position [0] twice because it migrated to [iter->size - 1] position. However, the same thing could happen when the same entry migrates a second time from [iter->size - 1] to [iter->size - 2] position (and so on) because the check that we are manipulating the entry just in the [0] position was insufficient. Instead of checking the position [pos == 0], we now check that the [pos % iter->size == 0], thus ignoring all the entries that might have moved back to the end of the internal table.
724 lines
17 KiB
C
724 lines
17 KiB
C
/*
|
|
* 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/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*
|
|
* This is an implementation of the Robin Hood hash table algorithm as
|
|
* described in [a] with simple linear searching, and backwards shift
|
|
* deletion algorithm as described in [b] and [c].
|
|
*
|
|
* Further work:
|
|
* 1. Implement 4.1 Speeding up Searches - 4.4 Smart Search [a]
|
|
* 2. Implement A Fast Concurrent and Resizable Robin Hood Hash Table [b]
|
|
*
|
|
* a. https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf paper.
|
|
* b. https://dspace.mit.edu/bitstream/handle/1721.1/130693/1251799942-MIT.pdf
|
|
* c.
|
|
* https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
|
|
#include <isc/ascii.h>
|
|
#include <isc/atomic.h>
|
|
#include <isc/entropy.h>
|
|
#include <isc/hash.h>
|
|
#include <isc/hashmap.h>
|
|
#include <isc/magic.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/result.h>
|
|
#include <isc/types.h>
|
|
#include <isc/util.h>
|
|
|
|
#define APPROX_99_PERCENT(x) (((x) * 1013) >> 10)
|
|
#define APPROX_95_PERCENT(x) (((x) * 972) >> 10)
|
|
#define APPROX_90_PERCENT(x) (((x) * 921) >> 10)
|
|
#define APPROX_85_PERCENT(x) (((x) * 870) >> 10)
|
|
#define APPROX_40_PERCENT(x) (((x) * 409) >> 10)
|
|
#define APPROX_35_PERCENT(x) (((x) * 359) >> 10)
|
|
#define APPROX_30_PERCENT(x) (((x) * 308) >> 10)
|
|
#define APPROX_25_PERCENT(x) (((x) * 256) >> 10)
|
|
#define APPROX_20_PERCENT(x) (((x) * 205) >> 10)
|
|
#define APPROX_15_PERCENT(x) (((x) * 154) >> 10)
|
|
#define APPROX_10_PERCENT(x) (((x) * 103) >> 10)
|
|
#define APPROX_05_PERCENT(x) (((x) * 52) >> 10)
|
|
#define APPROX_01_PERCENT(x) (((x) * 11) >> 10)
|
|
|
|
#define ISC_HASHMAP_MAGIC ISC_MAGIC('H', 'M', 'a', 'p')
|
|
#define ISC_HASHMAP_VALID(hashmap) ISC_MAGIC_VALID(hashmap, ISC_HASHMAP_MAGIC)
|
|
|
|
/* We have two tables for incremental rehashing */
|
|
#define HASHMAP_NUM_TABLES 2
|
|
|
|
#define HASHSIZE(bits) (UINT64_C(1) << (bits))
|
|
|
|
#define HASHMAP_NO_BITS 0U
|
|
#define HASHMAP_MIN_BITS 1U
|
|
#define HASHMAP_MAX_BITS 32U
|
|
|
|
typedef struct hashmap_node {
|
|
const void *key;
|
|
void *value;
|
|
uint32_t hashval;
|
|
uint32_t psl;
|
|
} hashmap_node_t;
|
|
|
|
typedef struct hashmap_table {
|
|
size_t size;
|
|
uint8_t hashbits;
|
|
uint32_t hashmask;
|
|
hashmap_node_t *table;
|
|
} hashmap_table_t;
|
|
|
|
struct isc_hashmap {
|
|
unsigned int magic;
|
|
uint8_t hindex;
|
|
uint32_t hiter; /* rehashing iterator */
|
|
isc_mem_t *mctx;
|
|
size_t count;
|
|
hashmap_table_t tables[HASHMAP_NUM_TABLES];
|
|
atomic_uint_fast32_t iterators;
|
|
};
|
|
|
|
struct isc_hashmap_iter {
|
|
isc_hashmap_t *hashmap;
|
|
size_t i;
|
|
size_t size;
|
|
uint8_t hindex;
|
|
hashmap_node_t *cur;
|
|
};
|
|
|
|
static isc_result_t
|
|
hashmap_add(isc_hashmap_t *hashmap, const uint32_t hashval,
|
|
isc_hashmap_match_fn match, const uint8_t *key, void *value,
|
|
void **foundp, uint8_t idx);
|
|
|
|
static void
|
|
hashmap_rehash_one(isc_hashmap_t *hashmap);
|
|
static void
|
|
hashmap_rehash_start_grow(isc_hashmap_t *hashmap);
|
|
static void
|
|
hashmap_rehash_start_shrink(isc_hashmap_t *hashmap);
|
|
static bool
|
|
over_threshold(isc_hashmap_t *hashmap);
|
|
static bool
|
|
under_threshold(isc_hashmap_t *hashmap);
|
|
|
|
static uint8_t
|
|
hashmap_nexttable(uint8_t idx) {
|
|
return ((idx == 0) ? 1 : 0);
|
|
}
|
|
|
|
static bool
|
|
rehashing_in_progress(const isc_hashmap_t *hashmap) {
|
|
return (hashmap->tables[hashmap_nexttable(hashmap->hindex)].table !=
|
|
NULL);
|
|
}
|
|
|
|
static bool
|
|
try_nexttable(const isc_hashmap_t *hashmap, uint8_t idx) {
|
|
return (idx == hashmap->hindex && rehashing_in_progress(hashmap));
|
|
}
|
|
|
|
static void
|
|
hashmap_node_init(hashmap_node_t *node, const uint32_t hashval,
|
|
const uint8_t *key, void *value) {
|
|
*node = (hashmap_node_t){
|
|
.value = value,
|
|
.hashval = hashval,
|
|
.key = key,
|
|
.psl = 0,
|
|
};
|
|
}
|
|
|
|
ISC_ATTR_UNUSED static void
|
|
hashmap_dump_table(const isc_hashmap_t *hashmap, const uint8_t idx) {
|
|
fprintf(stderr,
|
|
"====== %" PRIu8 " (bits = %" PRIu8 ", size = %zu =====\n", idx,
|
|
hashmap->tables[idx].hashbits, hashmap->tables[idx].size);
|
|
for (size_t i = 0; i < hashmap->tables[idx].size; i++) {
|
|
hashmap_node_t *node = &hashmap->tables[idx].table[i];
|
|
if (node->key != NULL) {
|
|
uint32_t hash = isc_hash_bits32(
|
|
node->hashval, hashmap->tables[idx].hashbits);
|
|
fprintf(stderr,
|
|
"%p: %zu -> %p"
|
|
", value = %p"
|
|
", hash = %" PRIu32 ", hashval = %" PRIu32
|
|
", psl = %" PRIu32 ", key = %s\n",
|
|
hashmap, i, node, node->value, hash,
|
|
node->hashval, node->psl, (char *)node->key);
|
|
}
|
|
}
|
|
fprintf(stderr, "================\n\n");
|
|
}
|
|
|
|
static void
|
|
hashmap_create_table(isc_hashmap_t *hashmap, const uint8_t idx,
|
|
const uint8_t bits) {
|
|
REQUIRE(hashmap->tables[idx].hashbits == HASHMAP_NO_BITS);
|
|
REQUIRE(hashmap->tables[idx].table == NULL);
|
|
REQUIRE(bits >= HASHMAP_MIN_BITS);
|
|
REQUIRE(bits <= HASHMAP_MAX_BITS);
|
|
|
|
hashmap->tables[idx] = (hashmap_table_t){
|
|
.hashbits = bits,
|
|
.hashmask = HASHSIZE(bits) - 1,
|
|
.size = HASHSIZE(bits),
|
|
};
|
|
|
|
hashmap->tables[idx].table =
|
|
isc_mem_cget(hashmap->mctx, hashmap->tables[idx].size,
|
|
sizeof(hashmap->tables[idx].table[0]));
|
|
}
|
|
|
|
static void
|
|
hashmap_free_table(isc_hashmap_t *hashmap, const uint8_t idx, bool cleanup) {
|
|
size_t size;
|
|
|
|
if (cleanup) {
|
|
for (size_t i = 0; i < hashmap->tables[idx].size; i++) {
|
|
hashmap_node_t *node = &hashmap->tables[idx].table[i];
|
|
if (node->key != NULL) {
|
|
*node = (hashmap_node_t){ 0 };
|
|
hashmap->count--;
|
|
}
|
|
}
|
|
}
|
|
|
|
size = hashmap->tables[idx].size *
|
|
sizeof(hashmap->tables[idx].table[0]);
|
|
isc_mem_put(hashmap->mctx, hashmap->tables[idx].table, size);
|
|
|
|
hashmap->tables[idx] = (hashmap_table_t){
|
|
.hashbits = HASHMAP_NO_BITS,
|
|
};
|
|
}
|
|
|
|
void
|
|
isc_hashmap_create(isc_mem_t *mctx, uint8_t bits, isc_hashmap_t **hashmapp) {
|
|
isc_hashmap_t *hashmap = isc_mem_get(mctx, sizeof(*hashmap));
|
|
|
|
REQUIRE(hashmapp != NULL && *hashmapp == NULL);
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(bits >= HASHMAP_MIN_BITS && bits <= HASHMAP_MAX_BITS);
|
|
|
|
*hashmap = (isc_hashmap_t){
|
|
.magic = ISC_HASHMAP_MAGIC,
|
|
};
|
|
isc_mem_attach(mctx, &hashmap->mctx);
|
|
|
|
hashmap_create_table(hashmap, 0, bits);
|
|
|
|
hashmap->magic = ISC_HASHMAP_MAGIC;
|
|
|
|
*hashmapp = hashmap;
|
|
}
|
|
|
|
void
|
|
isc_hashmap_destroy(isc_hashmap_t **hashmapp) {
|
|
isc_hashmap_t *hashmap;
|
|
|
|
REQUIRE(hashmapp != NULL && *hashmapp != NULL);
|
|
REQUIRE(ISC_HASHMAP_VALID(*hashmapp));
|
|
|
|
hashmap = *hashmapp;
|
|
*hashmapp = NULL;
|
|
|
|
hashmap->magic = 0;
|
|
|
|
for (size_t i = 0; i < HASHMAP_NUM_TABLES; i++) {
|
|
if (hashmap->tables[i].table != NULL) {
|
|
hashmap_free_table(hashmap, i, true);
|
|
}
|
|
}
|
|
INSIST(hashmap->count == 0);
|
|
|
|
isc_mem_putanddetach(&hashmap->mctx, hashmap, sizeof(*hashmap));
|
|
}
|
|
|
|
static hashmap_node_t *
|
|
hashmap_find(const isc_hashmap_t *hashmap, const uint32_t hashval,
|
|
isc_hashmap_match_fn match, const uint8_t *key, uint32_t *pslp,
|
|
uint8_t *idxp) {
|
|
uint32_t hash;
|
|
uint32_t psl;
|
|
uint8_t idx = *idxp;
|
|
uint32_t pos;
|
|
|
|
nexttable:
|
|
psl = 0;
|
|
hash = isc_hash_bits32(hashval, hashmap->tables[idx].hashbits);
|
|
|
|
while (true) {
|
|
hashmap_node_t *node = NULL;
|
|
|
|
pos = (hash + psl) & hashmap->tables[idx].hashmask;
|
|
|
|
node = &hashmap->tables[idx].table[pos];
|
|
|
|
if (node->key == NULL || psl > node->psl) {
|
|
break;
|
|
}
|
|
|
|
if (node->hashval == hashval) {
|
|
if (match(node->value, key)) {
|
|
*pslp = psl;
|
|
*idxp = idx;
|
|
return (node);
|
|
}
|
|
}
|
|
|
|
psl++;
|
|
}
|
|
if (try_nexttable(hashmap, idx)) {
|
|
idx = hashmap_nexttable(idx);
|
|
goto nexttable;
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
isc_result_t
|
|
isc_hashmap_find(const isc_hashmap_t *hashmap, const uint32_t hashval,
|
|
isc_hashmap_match_fn match, const void *key, void **valuep) {
|
|
REQUIRE(ISC_HASHMAP_VALID(hashmap));
|
|
REQUIRE(valuep == NULL || *valuep == NULL);
|
|
|
|
uint8_t idx = hashmap->hindex;
|
|
hashmap_node_t *node = hashmap_find(hashmap, hashval, match, key,
|
|
&(uint32_t){ 0 }, &idx);
|
|
if (node == NULL) {
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
INSIST(node->key != NULL);
|
|
SET_IF_NOT_NULL(valuep, node->value);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static bool
|
|
hashmap_delete_node(isc_hashmap_t *hashmap, hashmap_node_t *entry,
|
|
uint32_t hashval, uint32_t psl, const uint8_t idx,
|
|
size_t size) {
|
|
uint32_t pos;
|
|
uint32_t hash;
|
|
bool last = false;
|
|
|
|
hashmap->count--;
|
|
|
|
hash = isc_hash_bits32(hashval, hashmap->tables[idx].hashbits);
|
|
pos = (hash + psl) & hashmap->tables[idx].hashmask;
|
|
|
|
while (true) {
|
|
hashmap_node_t *node = NULL;
|
|
|
|
pos = (pos + 1) & hashmap->tables[idx].hashmask;
|
|
INSIST(pos < hashmap->tables[idx].size);
|
|
|
|
node = &hashmap->tables[idx].table[pos];
|
|
|
|
if (node->key == NULL || node->psl == 0) {
|
|
break;
|
|
}
|
|
|
|
if ((pos % size) == 0) {
|
|
last = true;
|
|
}
|
|
|
|
node->psl--;
|
|
*entry = *node;
|
|
entry = &hashmap->tables[idx].table[pos];
|
|
}
|
|
|
|
*entry = (hashmap_node_t){ 0 };
|
|
return (last);
|
|
}
|
|
|
|
static void
|
|
hashmap_rehash_one(isc_hashmap_t *hashmap) {
|
|
uint8_t oldidx = hashmap_nexttable(hashmap->hindex);
|
|
uint32_t oldsize = hashmap->tables[oldidx].size;
|
|
hashmap_node_t *oldtable = hashmap->tables[oldidx].table;
|
|
hashmap_node_t node;
|
|
|
|
/* Don't rehash when iterating */
|
|
INSIST(atomic_load_acquire(&hashmap->iterators) == 0);
|
|
|
|
/* Find first non-empty node */
|
|
while (hashmap->hiter < oldsize && oldtable[hashmap->hiter].key == NULL)
|
|
{
|
|
hashmap->hiter++;
|
|
}
|
|
|
|
/* Rehashing complete */
|
|
if (hashmap->hiter == oldsize) {
|
|
hashmap_free_table(hashmap, hashmap_nexttable(hashmap->hindex),
|
|
false);
|
|
hashmap->hiter = 0;
|
|
return;
|
|
}
|
|
|
|
/* Move the first non-empty node from old table to new table */
|
|
node = oldtable[hashmap->hiter];
|
|
|
|
(void)hashmap_delete_node(hashmap, &oldtable[hashmap->hiter],
|
|
node.hashval, node.psl, oldidx, UINT32_MAX);
|
|
|
|
isc_result_t result = hashmap_add(hashmap, node.hashval, NULL, node.key,
|
|
node.value, NULL, hashmap->hindex);
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* we don't increase the hiter here because the table has been reordered
|
|
* when we deleted the old node
|
|
*/
|
|
}
|
|
|
|
static uint32_t
|
|
grow_bits(isc_hashmap_t *hashmap) {
|
|
uint32_t newbits = hashmap->tables[hashmap->hindex].hashbits + 1;
|
|
size_t newsize = HASHSIZE(newbits);
|
|
|
|
while (hashmap->count > APPROX_40_PERCENT(newsize)) {
|
|
newbits += 1;
|
|
newsize = HASHSIZE(newbits);
|
|
}
|
|
if (newbits > HASHMAP_MAX_BITS) {
|
|
newbits = HASHMAP_MAX_BITS;
|
|
}
|
|
|
|
return (newbits);
|
|
}
|
|
|
|
static uint32_t
|
|
shrink_bits(isc_hashmap_t *hashmap) {
|
|
uint32_t newbits = hashmap->tables[hashmap->hindex].hashbits - 1;
|
|
|
|
if (newbits <= HASHMAP_MIN_BITS) {
|
|
newbits = HASHMAP_MIN_BITS;
|
|
}
|
|
|
|
return (newbits);
|
|
}
|
|
|
|
static void
|
|
hashmap_rehash_start_grow(isc_hashmap_t *hashmap) {
|
|
uint32_t newbits;
|
|
uint8_t oldindex = hashmap->hindex;
|
|
uint32_t oldbits = hashmap->tables[oldindex].hashbits;
|
|
uint8_t newindex = hashmap_nexttable(oldindex);
|
|
|
|
REQUIRE(!rehashing_in_progress(hashmap));
|
|
|
|
newbits = grow_bits(hashmap);
|
|
|
|
if (newbits > oldbits) {
|
|
hashmap_create_table(hashmap, newindex, newbits);
|
|
hashmap->hindex = newindex;
|
|
}
|
|
}
|
|
|
|
static void
|
|
hashmap_rehash_start_shrink(isc_hashmap_t *hashmap) {
|
|
uint32_t newbits;
|
|
uint8_t oldindex = hashmap->hindex;
|
|
uint32_t oldbits = hashmap->tables[oldindex].hashbits;
|
|
uint8_t newindex = hashmap_nexttable(oldindex);
|
|
|
|
REQUIRE(!rehashing_in_progress(hashmap));
|
|
|
|
newbits = shrink_bits(hashmap);
|
|
|
|
if (newbits < oldbits) {
|
|
hashmap_create_table(hashmap, newindex, newbits);
|
|
hashmap->hindex = newindex;
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
isc_hashmap_delete(isc_hashmap_t *hashmap, const uint32_t hashval,
|
|
isc_hashmap_match_fn match, const void *key) {
|
|
REQUIRE(ISC_HASHMAP_VALID(hashmap));
|
|
REQUIRE(key != NULL);
|
|
|
|
hashmap_node_t *node;
|
|
isc_result_t result = ISC_R_NOTFOUND;
|
|
uint32_t psl = 0;
|
|
uint8_t idx;
|
|
|
|
if (rehashing_in_progress(hashmap)) {
|
|
hashmap_rehash_one(hashmap);
|
|
} else if (under_threshold(hashmap)) {
|
|
hashmap_rehash_start_shrink(hashmap);
|
|
hashmap_rehash_one(hashmap);
|
|
}
|
|
|
|
/* Initialize idx after possible shrink start */
|
|
idx = hashmap->hindex;
|
|
|
|
node = hashmap_find(hashmap, hashval, match, key, &psl, &idx);
|
|
if (node != NULL) {
|
|
INSIST(node->key != NULL);
|
|
(void)hashmap_delete_node(hashmap, node, hashval, psl, idx,
|
|
UINT32_MAX);
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
static bool
|
|
over_threshold(isc_hashmap_t *hashmap) {
|
|
uint32_t bits = hashmap->tables[hashmap->hindex].hashbits;
|
|
if (bits == HASHMAP_MAX_BITS) {
|
|
return (false);
|
|
}
|
|
size_t threshold = APPROX_90_PERCENT(HASHSIZE(bits));
|
|
return (hashmap->count > threshold);
|
|
}
|
|
|
|
static bool
|
|
under_threshold(isc_hashmap_t *hashmap) {
|
|
uint32_t bits = hashmap->tables[hashmap->hindex].hashbits;
|
|
if (bits == HASHMAP_MIN_BITS) {
|
|
return (false);
|
|
}
|
|
size_t threshold = APPROX_20_PERCENT(HASHSIZE(bits));
|
|
return (hashmap->count < threshold);
|
|
}
|
|
|
|
static isc_result_t
|
|
hashmap_add(isc_hashmap_t *hashmap, const uint32_t hashval,
|
|
isc_hashmap_match_fn match, const uint8_t *key, void *value,
|
|
void **foundp, uint8_t idx) {
|
|
uint32_t hash;
|
|
uint32_t psl = 0;
|
|
hashmap_node_t node;
|
|
hashmap_node_t *current = NULL;
|
|
uint32_t pos;
|
|
|
|
INSIST(atomic_load_acquire(&hashmap->iterators) == 0);
|
|
|
|
hash = isc_hash_bits32(hashval, hashmap->tables[idx].hashbits);
|
|
|
|
/* Initialize the node to be store to 'node' */
|
|
hashmap_node_init(&node, hashval, key, value);
|
|
|
|
psl = 0;
|
|
while (true) {
|
|
pos = (hash + psl) & hashmap->tables[idx].hashmask;
|
|
|
|
current = &hashmap->tables[idx].table[pos];
|
|
|
|
/* Found an empty node */
|
|
if (current->key == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (current->hashval == hashval) {
|
|
if (match != NULL && match(current->value, key)) {
|
|
SET_IF_NOT_NULL(foundp, current->value);
|
|
return (ISC_R_EXISTS);
|
|
}
|
|
}
|
|
|
|
/* Found rich node */
|
|
if (node.psl > current->psl) {
|
|
/* Swap the poor with the rich node */
|
|
ISC_SWAP(*current, node);
|
|
}
|
|
|
|
node.psl++;
|
|
psl++;
|
|
}
|
|
|
|
/*
|
|
* Possible optimalization - start growing when the poor node is too far
|
|
*/
|
|
#if ISC_HASHMAP_GROW_FAST
|
|
if (psl > hashmap->hashbits[idx]) {
|
|
if (!rehashing_in_progress(hashmap)) {
|
|
hashmap_rehash_start_grow(hashmap);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
hashmap->count++;
|
|
|
|
/* We found an empty place, store entry into current node */
|
|
*current = node;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
isc_hashmap_add(isc_hashmap_t *hashmap, const uint32_t hashval,
|
|
isc_hashmap_match_fn match, const void *key, void *value,
|
|
void **foundp) {
|
|
REQUIRE(ISC_HASHMAP_VALID(hashmap));
|
|
REQUIRE(key != NULL);
|
|
|
|
if (rehashing_in_progress(hashmap)) {
|
|
hashmap_rehash_one(hashmap);
|
|
} else if (over_threshold(hashmap)) {
|
|
hashmap_rehash_start_grow(hashmap);
|
|
hashmap_rehash_one(hashmap);
|
|
}
|
|
|
|
if (rehashing_in_progress(hashmap)) {
|
|
uint8_t fidx = hashmap_nexttable(hashmap->hindex);
|
|
uint32_t psl;
|
|
|
|
/* Look for the value in the old table */
|
|
hashmap_node_t *found = hashmap_find(hashmap, hashval, match,
|
|
key, &psl, &fidx);
|
|
if (found != NULL) {
|
|
INSIST(found->key != NULL);
|
|
SET_IF_NOT_NULL(foundp, found->value);
|
|
return (ISC_R_EXISTS);
|
|
}
|
|
}
|
|
|
|
return (hashmap_add(hashmap, hashval, match, key, value, foundp,
|
|
hashmap->hindex));
|
|
}
|
|
|
|
void
|
|
isc_hashmap_iter_create(isc_hashmap_t *hashmap, isc_hashmap_iter_t **iterp) {
|
|
isc_hashmap_iter_t *iter;
|
|
|
|
REQUIRE(ISC_HASHMAP_VALID(hashmap));
|
|
REQUIRE(iterp != NULL && *iterp == NULL);
|
|
|
|
iter = isc_mem_get(hashmap->mctx, sizeof(*iter));
|
|
*iter = (isc_hashmap_iter_t){
|
|
.hashmap = hashmap,
|
|
.hindex = hashmap->hindex,
|
|
};
|
|
|
|
(void)atomic_fetch_add_release(&hashmap->iterators, 1);
|
|
|
|
*iterp = iter;
|
|
}
|
|
|
|
void
|
|
isc_hashmap_iter_destroy(isc_hashmap_iter_t **iterp) {
|
|
isc_hashmap_iter_t *iter;
|
|
isc_hashmap_t *hashmap;
|
|
|
|
REQUIRE(iterp != NULL && *iterp != NULL);
|
|
|
|
iter = *iterp;
|
|
*iterp = NULL;
|
|
hashmap = iter->hashmap;
|
|
isc_mem_put(hashmap->mctx, iter, sizeof(*iter));
|
|
|
|
INSIST(atomic_fetch_sub_release(&hashmap->iterators, 1) > 0);
|
|
}
|
|
|
|
static isc_result_t
|
|
isc__hashmap_iter_next(isc_hashmap_iter_t *iter) {
|
|
isc_hashmap_t *hashmap = iter->hashmap;
|
|
|
|
while (iter->i < iter->size &&
|
|
hashmap->tables[iter->hindex].table[iter->i].key == NULL)
|
|
{
|
|
iter->i++;
|
|
}
|
|
|
|
if (iter->i < iter->size) {
|
|
iter->cur = &hashmap->tables[iter->hindex].table[iter->i];
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
if (try_nexttable(hashmap, iter->hindex)) {
|
|
iter->hindex = hashmap_nexttable(iter->hindex);
|
|
iter->i = 0;
|
|
iter->size = hashmap->tables[iter->hindex].size;
|
|
return (isc__hashmap_iter_next(iter));
|
|
}
|
|
|
|
return (ISC_R_NOMORE);
|
|
}
|
|
|
|
isc_result_t
|
|
isc_hashmap_iter_first(isc_hashmap_iter_t *iter) {
|
|
REQUIRE(iter != NULL);
|
|
|
|
iter->hindex = iter->hashmap->hindex;
|
|
iter->i = 0;
|
|
iter->size = iter->hashmap->tables[iter->hashmap->hindex].size;
|
|
|
|
return (isc__hashmap_iter_next(iter));
|
|
}
|
|
|
|
isc_result_t
|
|
isc_hashmap_iter_next(isc_hashmap_iter_t *iter) {
|
|
REQUIRE(iter != NULL);
|
|
REQUIRE(iter->cur != NULL);
|
|
|
|
iter->i++;
|
|
|
|
return (isc__hashmap_iter_next(iter));
|
|
}
|
|
|
|
isc_result_t
|
|
isc_hashmap_iter_delcurrent_next(isc_hashmap_iter_t *iter) {
|
|
REQUIRE(iter != NULL);
|
|
REQUIRE(iter->cur != NULL);
|
|
|
|
hashmap_node_t *node =
|
|
&iter->hashmap->tables[iter->hindex].table[iter->i];
|
|
|
|
if (hashmap_delete_node(iter->hashmap, node, node->hashval, node->psl,
|
|
iter->hindex, iter->size))
|
|
{
|
|
/*
|
|
* We have seen the new last element so reduce the size
|
|
* so we don't iterate over it twice.
|
|
*/
|
|
INSIST(iter->size != 0);
|
|
iter->size--;
|
|
}
|
|
|
|
return (isc__hashmap_iter_next(iter));
|
|
}
|
|
|
|
void
|
|
isc_hashmap_iter_current(isc_hashmap_iter_t *it, void **valuep) {
|
|
REQUIRE(it != NULL);
|
|
REQUIRE(it->cur != NULL);
|
|
REQUIRE(valuep != NULL && *valuep == NULL);
|
|
|
|
*valuep = it->cur->value;
|
|
}
|
|
|
|
void
|
|
isc_hashmap_iter_currentkey(isc_hashmap_iter_t *it, const unsigned char **key) {
|
|
REQUIRE(it != NULL);
|
|
REQUIRE(it->cur != NULL);
|
|
REQUIRE(key != NULL && *key == NULL);
|
|
|
|
*key = it->cur->key;
|
|
}
|
|
|
|
unsigned int
|
|
isc_hashmap_count(isc_hashmap_t *hashmap) {
|
|
REQUIRE(ISC_HASHMAP_VALID(hashmap));
|
|
|
|
return (hashmap->count);
|
|
}
|