Home | History | Annotate | Line # | Download | only in isc
hashmap.c revision 1.1.1.2
      1 /*	$NetBSD: hashmap.c,v 1.1.1.2 2026/01/29 18:19:49 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*
     17  * This is an implementation of the Robin Hood hash table algorithm as
     18  * described in [a] with simple linear searching, and backwards shift
     19  * deletion algorithm as described in [b] and [c].
     20  *
     21  * Further work:
     22  * 1. Implement 4.1 Speeding up Searches - 4.4 Smart Search [a]
     23  * 2. Implement A Fast Concurrent and Resizable Robin Hood Hash Table [b]
     24  *
     25  * a. https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf paper.
     26  * b. https://dspace.mit.edu/bitstream/handle/1721.1/130693/1251799942-MIT.pdf
     27  * c.
     28  * https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
     29  */
     30 
     31 #include <ctype.h>
     32 #include <inttypes.h>
     33 #include <string.h>
     34 
     35 #include <isc/ascii.h>
     36 #include <isc/atomic.h>
     37 #include <isc/hash.h>
     38 #include <isc/hashmap.h>
     39 #include <isc/magic.h>
     40 #include <isc/mem.h>
     41 #include <isc/result.h>
     42 #include <isc/types.h>
     43 #include <isc/util.h>
     44 
     45 #define APPROX_99_PERCENT(x) (((x) * 1013) >> 10)
     46 #define APPROX_95_PERCENT(x) (((x) * 972) >> 10)
     47 #define APPROX_90_PERCENT(x) (((x) * 921) >> 10)
     48 #define APPROX_85_PERCENT(x) (((x) * 870) >> 10)
     49 #define APPROX_40_PERCENT(x) (((x) * 409) >> 10)
     50 #define APPROX_35_PERCENT(x) (((x) * 359) >> 10)
     51 #define APPROX_30_PERCENT(x) (((x) * 308) >> 10)
     52 #define APPROX_25_PERCENT(x) (((x) * 256) >> 10)
     53 #define APPROX_20_PERCENT(x) (((x) * 205) >> 10)
     54 #define APPROX_15_PERCENT(x) (((x) * 154) >> 10)
     55 #define APPROX_10_PERCENT(x) (((x) * 103) >> 10)
     56 #define APPROX_05_PERCENT(x) (((x) * 52) >> 10)
     57 #define APPROX_01_PERCENT(x) (((x) * 11) >> 10)
     58 
     59 #define ISC_HASHMAP_MAGIC	   ISC_MAGIC('H', 'M', 'a', 'p')
     60 #define ISC_HASHMAP_VALID(hashmap) ISC_MAGIC_VALID(hashmap, ISC_HASHMAP_MAGIC)
     61 
     62 /* We have two tables for incremental rehashing */
     63 #define HASHMAP_NUM_TABLES 2
     64 
     65 #define HASHSIZE(bits) (UINT64_C(1) << (bits))
     66 
     67 #define HASHMAP_NO_BITS	 0U
     68 #define HASHMAP_MIN_BITS 1U
     69 #define HASHMAP_MAX_BITS 32U
     70 
     71 typedef struct hashmap_node {
     72 	const void *key;
     73 	void *value;
     74 	uint32_t hashval;
     75 	uint32_t psl;
     76 } hashmap_node_t;
     77 
     78 typedef struct hashmap_table {
     79 	size_t size;
     80 	uint8_t hashbits;
     81 	uint32_t hashmask;
     82 	hashmap_node_t *table;
     83 } hashmap_table_t;
     84 
     85 struct isc_hashmap {
     86 	unsigned int magic;
     87 	uint8_t hindex;
     88 	uint32_t hiter; /* rehashing iterator */
     89 	isc_mem_t *mctx;
     90 	size_t count;
     91 	hashmap_table_t tables[HASHMAP_NUM_TABLES];
     92 	atomic_uint_fast32_t iterators;
     93 };
     94 
     95 struct isc_hashmap_iter {
     96 	isc_hashmap_t *hashmap;
     97 	size_t i;
     98 	size_t size;
     99 	uint8_t hindex;
    100 	hashmap_node_t *cur;
    101 };
    102 
    103 static isc_result_t
    104 hashmap_add(isc_hashmap_t *hashmap, const uint32_t hashval,
    105 	    isc_hashmap_match_fn match, const uint8_t *key, void *value,
    106 	    void **foundp, uint8_t idx);
    107 
    108 static void
    109 hashmap_rehash_one(isc_hashmap_t *hashmap);
    110 static void
    111 hashmap_rehash_start_grow(isc_hashmap_t *hashmap);
    112 static void
    113 hashmap_rehash_start_shrink(isc_hashmap_t *hashmap);
    114 static bool
    115 over_threshold(isc_hashmap_t *hashmap);
    116 static bool
    117 under_threshold(isc_hashmap_t *hashmap);
    118 
    119 static uint8_t
    120 hashmap_nexttable(uint8_t idx) {
    121 	return (idx == 0) ? 1 : 0;
    122 }
    123 
    124 static bool
    125 rehashing_in_progress(const isc_hashmap_t *hashmap) {
    126 	return hashmap->tables[hashmap_nexttable(hashmap->hindex)].table !=
    127 	       NULL;
    128 }
    129 
    130 static bool
    131 try_nexttable(const isc_hashmap_t *hashmap, uint8_t idx) {
    132 	return idx == hashmap->hindex && rehashing_in_progress(hashmap);
    133 }
    134 
    135 static void
    136 hashmap_node_init(hashmap_node_t *node, const uint32_t hashval,
    137 		  const uint8_t *key, void *value) {
    138 	*node = (hashmap_node_t){
    139 		.value = value,
    140 		.hashval = hashval,
    141 		.key = key,
    142 		.psl = 0,
    143 	};
    144 }
    145 
    146 ISC_ATTR_UNUSED static void
    147 hashmap_dump_table(const isc_hashmap_t *hashmap, const uint8_t idx) {
    148 	fprintf(stderr,
    149 		"====== %" PRIu8 " (bits = %" PRIu8 ", size = %zu =====\n", idx,
    150 		hashmap->tables[idx].hashbits, hashmap->tables[idx].size);
    151 	for (size_t i = 0; i < hashmap->tables[idx].size; i++) {
    152 		hashmap_node_t *node = &hashmap->tables[idx].table[i];
    153 		if (node->key != NULL) {
    154 			uint32_t hash = isc_hash_bits32(
    155 				node->hashval, hashmap->tables[idx].hashbits);
    156 			fprintf(stderr,
    157 				"%p: %zu -> %p"
    158 				", value = %p"
    159 				", hash = %" PRIu32 ", hashval = %" PRIu32
    160 				", psl = %" PRIu32 ", key = %s\n",
    161 				hashmap, i, node, node->value, hash,
    162 				node->hashval, node->psl, (char *)node->key);
    163 		}
    164 	}
    165 	fprintf(stderr, "================\n\n");
    166 }
    167 
    168 static void
    169 hashmap_create_table(isc_hashmap_t *hashmap, const uint8_t idx,
    170 		     const uint8_t bits) {
    171 	REQUIRE(hashmap->tables[idx].hashbits == HASHMAP_NO_BITS);
    172 	REQUIRE(hashmap->tables[idx].table == NULL);
    173 	REQUIRE(bits >= HASHMAP_MIN_BITS);
    174 	REQUIRE(bits <= HASHMAP_MAX_BITS);
    175 
    176 	hashmap->tables[idx] = (hashmap_table_t){
    177 		.hashbits = bits,
    178 		.hashmask = HASHSIZE(bits) - 1,
    179 		.size = HASHSIZE(bits),
    180 	};
    181 
    182 	hashmap->tables[idx].table =
    183 		isc_mem_cget(hashmap->mctx, hashmap->tables[idx].size,
    184 			     sizeof(hashmap->tables[idx].table[0]));
    185 }
    186 
    187 static void
    188 hashmap_free_table(isc_hashmap_t *hashmap, const uint8_t idx, bool cleanup) {
    189 	size_t size;
    190 
    191 	if (cleanup) {
    192 		for (size_t i = 0; i < hashmap->tables[idx].size; i++) {
    193 			hashmap_node_t *node = &hashmap->tables[idx].table[i];
    194 			if (node->key != NULL) {
    195 				*node = (hashmap_node_t){ 0 };
    196 				hashmap->count--;
    197 			}
    198 		}
    199 	}
    200 
    201 	size = hashmap->tables[idx].size *
    202 	       sizeof(hashmap->tables[idx].table[0]);
    203 	isc_mem_put(hashmap->mctx, hashmap->tables[idx].table, size);
    204 
    205 	hashmap->tables[idx] = (hashmap_table_t){
    206 		.hashbits = HASHMAP_NO_BITS,
    207 	};
    208 }
    209 
    210 void
    211 isc_hashmap_create(isc_mem_t *mctx, uint8_t bits, isc_hashmap_t **hashmapp) {
    212 	isc_hashmap_t *hashmap = isc_mem_get(mctx, sizeof(*hashmap));
    213 
    214 	REQUIRE(hashmapp != NULL && *hashmapp == NULL);
    215 	REQUIRE(mctx != NULL);
    216 	REQUIRE(bits >= HASHMAP_MIN_BITS && bits <= HASHMAP_MAX_BITS);
    217 
    218 	*hashmap = (isc_hashmap_t){
    219 		.magic = ISC_HASHMAP_MAGIC,
    220 	};
    221 	isc_mem_attach(mctx, &hashmap->mctx);
    222 
    223 	hashmap_create_table(hashmap, 0, bits);
    224 
    225 	hashmap->magic = ISC_HASHMAP_MAGIC;
    226 
    227 	*hashmapp = hashmap;
    228 }
    229 
    230 void
    231 isc_hashmap_destroy(isc_hashmap_t **hashmapp) {
    232 	isc_hashmap_t *hashmap;
    233 
    234 	REQUIRE(hashmapp != NULL && *hashmapp != NULL);
    235 	REQUIRE(ISC_HASHMAP_VALID(*hashmapp));
    236 
    237 	hashmap = *hashmapp;
    238 	*hashmapp = NULL;
    239 
    240 	hashmap->magic = 0;
    241 
    242 	for (size_t i = 0; i < HASHMAP_NUM_TABLES; i++) {
    243 		if (hashmap->tables[i].table != NULL) {
    244 			hashmap_free_table(hashmap, i, true);
    245 		}
    246 	}
    247 	INSIST(hashmap->count == 0);
    248 
    249 	isc_mem_putanddetach(&hashmap->mctx, hashmap, sizeof(*hashmap));
    250 }
    251 
    252 static hashmap_node_t *
    253 hashmap_find(const isc_hashmap_t *hashmap, const uint32_t hashval,
    254 	     isc_hashmap_match_fn match, const uint8_t *key, uint32_t *pslp,
    255 	     uint8_t *idxp) {
    256 	uint32_t hash;
    257 	uint32_t psl;
    258 	uint8_t idx = *idxp;
    259 	uint32_t pos;
    260 
    261 nexttable:
    262 	psl = 0;
    263 	hash = isc_hash_bits32(hashval, hashmap->tables[idx].hashbits);
    264 
    265 	while (true) {
    266 		hashmap_node_t *node = NULL;
    267 
    268 		pos = (hash + psl) & hashmap->tables[idx].hashmask;
    269 
    270 		node = &hashmap->tables[idx].table[pos];
    271 
    272 		if (node->key == NULL || psl > node->psl) {
    273 			break;
    274 		}
    275 
    276 		if (node->hashval == hashval) {
    277 			if (match(node->value, key)) {
    278 				*pslp = psl;
    279 				*idxp = idx;
    280 				return node;
    281 			}
    282 		}
    283 
    284 		psl++;
    285 	}
    286 	if (try_nexttable(hashmap, idx)) {
    287 		idx = hashmap_nexttable(idx);
    288 		goto nexttable;
    289 	}
    290 
    291 	return NULL;
    292 }
    293 
    294 isc_result_t
    295 isc_hashmap_find(const isc_hashmap_t *hashmap, const uint32_t hashval,
    296 		 isc_hashmap_match_fn match, const void *key, void **valuep) {
    297 	REQUIRE(ISC_HASHMAP_VALID(hashmap));
    298 	REQUIRE(valuep == NULL || *valuep == NULL);
    299 
    300 	uint8_t idx = hashmap->hindex;
    301 	hashmap_node_t *node = hashmap_find(hashmap, hashval, match, key,
    302 					    &(uint32_t){ 0 }, &idx);
    303 	if (node == NULL) {
    304 		return ISC_R_NOTFOUND;
    305 	}
    306 
    307 	INSIST(node->key != NULL);
    308 	SET_IF_NOT_NULL(valuep, node->value);
    309 	return ISC_R_SUCCESS;
    310 }
    311 
    312 static bool
    313 hashmap_delete_node(isc_hashmap_t *hashmap, hashmap_node_t *entry,
    314 		    uint32_t hashval, uint32_t psl, const uint8_t idx,
    315 		    size_t size) {
    316 	uint32_t pos;
    317 	uint32_t hash;
    318 	bool last = false;
    319 
    320 	hashmap->count--;
    321 
    322 	hash = isc_hash_bits32(hashval, hashmap->tables[idx].hashbits);
    323 	pos = (hash + psl) & hashmap->tables[idx].hashmask;
    324 
    325 	while (true) {
    326 		hashmap_node_t *node = NULL;
    327 
    328 		pos = (pos + 1) & hashmap->tables[idx].hashmask;
    329 		INSIST(pos < hashmap->tables[idx].size);
    330 
    331 		node = &hashmap->tables[idx].table[pos];
    332 
    333 		if (node->key == NULL || node->psl == 0) {
    334 			break;
    335 		}
    336 
    337 		if ((pos % size) == 0) {
    338 			last = true;
    339 		}
    340 
    341 		node->psl--;
    342 		*entry = *node;
    343 		entry = &hashmap->tables[idx].table[pos];
    344 	}
    345 
    346 	*entry = (hashmap_node_t){ 0 };
    347 	return last;
    348 }
    349 
    350 static void
    351 hashmap_rehash_one(isc_hashmap_t *hashmap) {
    352 	uint8_t oldidx = hashmap_nexttable(hashmap->hindex);
    353 	uint32_t oldsize = hashmap->tables[oldidx].size;
    354 	hashmap_node_t *oldtable = hashmap->tables[oldidx].table;
    355 	hashmap_node_t node;
    356 
    357 	/* Don't rehash when iterating */
    358 	INSIST(atomic_load_acquire(&hashmap->iterators) == 0);
    359 
    360 	/* Find first non-empty node */
    361 	while (hashmap->hiter < oldsize && oldtable[hashmap->hiter].key == NULL)
    362 	{
    363 		hashmap->hiter++;
    364 	}
    365 
    366 	/* Rehashing complete */
    367 	if (hashmap->hiter == oldsize) {
    368 		hashmap_free_table(hashmap, hashmap_nexttable(hashmap->hindex),
    369 				   false);
    370 		hashmap->hiter = 0;
    371 		return;
    372 	}
    373 
    374 	/* Move the first non-empty node from old table to new table */
    375 	node = oldtable[hashmap->hiter];
    376 
    377 	(void)hashmap_delete_node(hashmap, &oldtable[hashmap->hiter],
    378 				  node.hashval, node.psl, oldidx, UINT32_MAX);
    379 
    380 	isc_result_t result = hashmap_add(hashmap, node.hashval, NULL, node.key,
    381 					  node.value, NULL, hashmap->hindex);
    382 	INSIST(result == ISC_R_SUCCESS);
    383 
    384 	/*
    385 	 * we don't increase the hiter here because the table has been reordered
    386 	 * when we deleted the old node
    387 	 */
    388 }
    389 
    390 static uint32_t
    391 grow_bits(isc_hashmap_t *hashmap) {
    392 	uint32_t newbits = hashmap->tables[hashmap->hindex].hashbits + 1;
    393 	size_t newsize = HASHSIZE(newbits);
    394 
    395 	while (hashmap->count > APPROX_40_PERCENT(newsize)) {
    396 		newbits += 1;
    397 		newsize = HASHSIZE(newbits);
    398 	}
    399 	if (newbits > HASHMAP_MAX_BITS) {
    400 		newbits = HASHMAP_MAX_BITS;
    401 	}
    402 
    403 	return newbits;
    404 }
    405 
    406 static uint32_t
    407 shrink_bits(isc_hashmap_t *hashmap) {
    408 	uint32_t newbits = hashmap->tables[hashmap->hindex].hashbits - 1;
    409 
    410 	if (newbits <= HASHMAP_MIN_BITS) {
    411 		newbits = HASHMAP_MIN_BITS;
    412 	}
    413 
    414 	return newbits;
    415 }
    416 
    417 static void
    418 hashmap_rehash_start_grow(isc_hashmap_t *hashmap) {
    419 	uint32_t newbits;
    420 	uint8_t oldindex = hashmap->hindex;
    421 	uint32_t oldbits = hashmap->tables[oldindex].hashbits;
    422 	uint8_t newindex = hashmap_nexttable(oldindex);
    423 
    424 	REQUIRE(!rehashing_in_progress(hashmap));
    425 
    426 	newbits = grow_bits(hashmap);
    427 
    428 	if (newbits > oldbits) {
    429 		hashmap_create_table(hashmap, newindex, newbits);
    430 		hashmap->hindex = newindex;
    431 	}
    432 }
    433 
    434 static void
    435 hashmap_rehash_start_shrink(isc_hashmap_t *hashmap) {
    436 	uint32_t newbits;
    437 	uint8_t oldindex = hashmap->hindex;
    438 	uint32_t oldbits = hashmap->tables[oldindex].hashbits;
    439 	uint8_t newindex = hashmap_nexttable(oldindex);
    440 
    441 	REQUIRE(!rehashing_in_progress(hashmap));
    442 
    443 	newbits = shrink_bits(hashmap);
    444 
    445 	if (newbits < oldbits) {
    446 		hashmap_create_table(hashmap, newindex, newbits);
    447 		hashmap->hindex = newindex;
    448 	}
    449 }
    450 
    451 isc_result_t
    452 isc_hashmap_delete(isc_hashmap_t *hashmap, const uint32_t hashval,
    453 		   isc_hashmap_match_fn match, const void *key) {
    454 	REQUIRE(ISC_HASHMAP_VALID(hashmap));
    455 	REQUIRE(key != NULL);
    456 
    457 	hashmap_node_t *node;
    458 	isc_result_t result = ISC_R_NOTFOUND;
    459 	uint32_t psl = 0;
    460 	uint8_t idx;
    461 
    462 	if (rehashing_in_progress(hashmap)) {
    463 		hashmap_rehash_one(hashmap);
    464 	} else if (under_threshold(hashmap)) {
    465 		hashmap_rehash_start_shrink(hashmap);
    466 		hashmap_rehash_one(hashmap);
    467 	}
    468 
    469 	/* Initialize idx after possible shrink start */
    470 	idx = hashmap->hindex;
    471 
    472 	node = hashmap_find(hashmap, hashval, match, key, &psl, &idx);
    473 	if (node != NULL) {
    474 		INSIST(node->key != NULL);
    475 		(void)hashmap_delete_node(hashmap, node, hashval, psl, idx,
    476 					  UINT32_MAX);
    477 		result = ISC_R_SUCCESS;
    478 	}
    479 
    480 	return result;
    481 }
    482 
    483 static bool
    484 over_threshold(isc_hashmap_t *hashmap) {
    485 	uint32_t bits = hashmap->tables[hashmap->hindex].hashbits;
    486 	if (bits == HASHMAP_MAX_BITS) {
    487 		return false;
    488 	}
    489 	size_t threshold = APPROX_90_PERCENT(HASHSIZE(bits));
    490 	return hashmap->count > threshold;
    491 }
    492 
    493 static bool
    494 under_threshold(isc_hashmap_t *hashmap) {
    495 	uint32_t bits = hashmap->tables[hashmap->hindex].hashbits;
    496 	if (bits == HASHMAP_MIN_BITS) {
    497 		return false;
    498 	}
    499 	size_t threshold = APPROX_20_PERCENT(HASHSIZE(bits));
    500 	return hashmap->count < threshold;
    501 }
    502 
    503 static isc_result_t
    504 hashmap_add(isc_hashmap_t *hashmap, const uint32_t hashval,
    505 	    isc_hashmap_match_fn match, const uint8_t *key, void *value,
    506 	    void **foundp, uint8_t idx) {
    507 	uint32_t hash;
    508 	uint32_t psl = 0;
    509 	hashmap_node_t node;
    510 	hashmap_node_t *current = NULL;
    511 	uint32_t pos;
    512 
    513 	INSIST(atomic_load_acquire(&hashmap->iterators) == 0);
    514 
    515 	hash = isc_hash_bits32(hashval, hashmap->tables[idx].hashbits);
    516 
    517 	/* Initialize the node to be store to 'node' */
    518 	hashmap_node_init(&node, hashval, key, value);
    519 
    520 	psl = 0;
    521 	while (true) {
    522 		pos = (hash + psl) & hashmap->tables[idx].hashmask;
    523 
    524 		current = &hashmap->tables[idx].table[pos];
    525 
    526 		/* Found an empty node */
    527 		if (current->key == NULL) {
    528 			break;
    529 		}
    530 
    531 		if (current->hashval == hashval) {
    532 			if (match != NULL && match(current->value, key)) {
    533 				SET_IF_NOT_NULL(foundp, current->value);
    534 				return ISC_R_EXISTS;
    535 			}
    536 		}
    537 
    538 		/* Found rich node */
    539 		if (node.psl > current->psl) {
    540 			/* Swap the poor with the rich node */
    541 			ISC_SWAP(*current, node);
    542 		}
    543 
    544 		node.psl++;
    545 		psl++;
    546 	}
    547 
    548 	/*
    549 	 * Possible optimalization - start growing when the poor node is too far
    550 	 */
    551 #if ISC_HASHMAP_GROW_FAST
    552 	if (psl > hashmap->hashbits[idx]) {
    553 		if (!rehashing_in_progress(hashmap)) {
    554 			hashmap_rehash_start_grow(hashmap);
    555 		}
    556 	}
    557 #endif
    558 
    559 	hashmap->count++;
    560 
    561 	/* We found an empty place, store entry into current node */
    562 	*current = node;
    563 
    564 	return ISC_R_SUCCESS;
    565 }
    566 
    567 isc_result_t
    568 isc_hashmap_add(isc_hashmap_t *hashmap, const uint32_t hashval,
    569 		isc_hashmap_match_fn match, const void *key, void *value,
    570 		void **foundp) {
    571 	REQUIRE(ISC_HASHMAP_VALID(hashmap));
    572 	REQUIRE(key != NULL);
    573 
    574 	if (rehashing_in_progress(hashmap)) {
    575 		hashmap_rehash_one(hashmap);
    576 	} else if (over_threshold(hashmap)) {
    577 		hashmap_rehash_start_grow(hashmap);
    578 		hashmap_rehash_one(hashmap);
    579 	}
    580 
    581 	if (rehashing_in_progress(hashmap)) {
    582 		uint8_t fidx = hashmap_nexttable(hashmap->hindex);
    583 		uint32_t psl;
    584 
    585 		/* Look for the value in the old table */
    586 		hashmap_node_t *found = hashmap_find(hashmap, hashval, match,
    587 						     key, &psl, &fidx);
    588 		if (found != NULL) {
    589 			INSIST(found->key != NULL);
    590 			SET_IF_NOT_NULL(foundp, found->value);
    591 			return ISC_R_EXISTS;
    592 		}
    593 	}
    594 
    595 	return hashmap_add(hashmap, hashval, match, key, value, foundp,
    596 			   hashmap->hindex);
    597 }
    598 
    599 void
    600 isc_hashmap_iter_create(isc_hashmap_t *hashmap, isc_hashmap_iter_t **iterp) {
    601 	isc_hashmap_iter_t *iter;
    602 
    603 	REQUIRE(ISC_HASHMAP_VALID(hashmap));
    604 	REQUIRE(iterp != NULL && *iterp == NULL);
    605 
    606 	iter = isc_mem_get(hashmap->mctx, sizeof(*iter));
    607 	*iter = (isc_hashmap_iter_t){
    608 		.hashmap = hashmap,
    609 		.hindex = hashmap->hindex,
    610 	};
    611 
    612 	(void)atomic_fetch_add_release(&hashmap->iterators, 1);
    613 
    614 	*iterp = iter;
    615 }
    616 
    617 void
    618 isc_hashmap_iter_destroy(isc_hashmap_iter_t **iterp) {
    619 	isc_hashmap_iter_t *iter;
    620 	isc_hashmap_t *hashmap;
    621 
    622 	REQUIRE(iterp != NULL && *iterp != NULL);
    623 
    624 	iter = *iterp;
    625 	*iterp = NULL;
    626 	hashmap = iter->hashmap;
    627 	isc_mem_put(hashmap->mctx, iter, sizeof(*iter));
    628 
    629 	INSIST(atomic_fetch_sub_release(&hashmap->iterators, 1) > 0);
    630 }
    631 
    632 static isc_result_t
    633 isc__hashmap_iter_next(isc_hashmap_iter_t *iter) {
    634 	isc_hashmap_t *hashmap = iter->hashmap;
    635 
    636 	while (iter->i < iter->size &&
    637 	       hashmap->tables[iter->hindex].table[iter->i].key == NULL)
    638 	{
    639 		iter->i++;
    640 	}
    641 
    642 	if (iter->i < iter->size) {
    643 		iter->cur = &hashmap->tables[iter->hindex].table[iter->i];
    644 
    645 		return ISC_R_SUCCESS;
    646 	}
    647 
    648 	if (try_nexttable(hashmap, iter->hindex)) {
    649 		iter->hindex = hashmap_nexttable(iter->hindex);
    650 		iter->i = hashmap->hiter;
    651 		iter->size = hashmap->tables[iter->hindex].size;
    652 		return isc__hashmap_iter_next(iter);
    653 	}
    654 
    655 	return ISC_R_NOMORE;
    656 }
    657 
    658 isc_result_t
    659 isc_hashmap_iter_first(isc_hashmap_iter_t *iter) {
    660 	REQUIRE(iter != NULL);
    661 
    662 	iter->hindex = iter->hashmap->hindex;
    663 	iter->i = 0;
    664 	iter->size = iter->hashmap->tables[iter->hashmap->hindex].size;
    665 
    666 	return isc__hashmap_iter_next(iter);
    667 }
    668 
    669 isc_result_t
    670 isc_hashmap_iter_next(isc_hashmap_iter_t *iter) {
    671 	REQUIRE(iter != NULL);
    672 	REQUIRE(iter->cur != NULL);
    673 
    674 	iter->i++;
    675 
    676 	return isc__hashmap_iter_next(iter);
    677 }
    678 
    679 isc_result_t
    680 isc_hashmap_iter_delcurrent_next(isc_hashmap_iter_t *iter) {
    681 	REQUIRE(iter != NULL);
    682 	REQUIRE(iter->cur != NULL);
    683 
    684 	hashmap_node_t *node =
    685 		&iter->hashmap->tables[iter->hindex].table[iter->i];
    686 
    687 	if (hashmap_delete_node(iter->hashmap, node, node->hashval, node->psl,
    688 				iter->hindex, iter->size))
    689 	{
    690 		/*
    691 		 * We have seen the new last element so reduce the size
    692 		 * so we don't iterate over it twice.
    693 		 */
    694 		INSIST(iter->size != 0);
    695 		iter->size--;
    696 	}
    697 
    698 	return isc__hashmap_iter_next(iter);
    699 }
    700 
    701 void
    702 isc_hashmap_iter_current(isc_hashmap_iter_t *it, void **valuep) {
    703 	REQUIRE(it != NULL);
    704 	REQUIRE(it->cur != NULL);
    705 	REQUIRE(valuep != NULL && *valuep == NULL);
    706 
    707 	*valuep = it->cur->value;
    708 }
    709 
    710 void
    711 isc_hashmap_iter_currentkey(isc_hashmap_iter_t *it, const unsigned char **key) {
    712 	REQUIRE(it != NULL);
    713 	REQUIRE(it->cur != NULL);
    714 	REQUIRE(key != NULL && *key == NULL);
    715 
    716 	*key = it->cur->key;
    717 }
    718 
    719 unsigned int
    720 isc_hashmap_count(isc_hashmap_t *hashmap) {
    721 	REQUIRE(ISC_HASHMAP_VALID(hashmap));
    722 
    723 	return hashmap->count;
    724 }
    725