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