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