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