Home | History | Annotate | Line # | Download | only in isc
      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