1 /* $NetBSD: hashmap.c,v 1.3 2026/01/29 18:37:54 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