1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
4 * Generic non-thread safe hash map implementation.
6 * Copyright (c) 2019 Facebook
12 #include <linux/err.h>
15 /* make sure libbpf doesn't use kernel-only integer typedefs */
16 #pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
18 /* prevent accidental re-addition of reallocarray() */
19 #pragma GCC poison reallocarray
21 /* start with 4 buckets */
22 #define HASHMAP_MIN_CAP_BITS 2
24 static void hashmap_add_entry(struct hashmap_entry **pprev,
25 struct hashmap_entry *entry)
31 static void hashmap_del_entry(struct hashmap_entry **pprev,
32 struct hashmap_entry *entry)
38 void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
39 hashmap_equal_fn equal_fn, void *ctx)
41 map->hash_fn = hash_fn;
42 map->equal_fn = equal_fn;
51 struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
52 hashmap_equal_fn equal_fn,
55 struct hashmap *map = malloc(sizeof(struct hashmap));
58 return ERR_PTR(-ENOMEM);
59 hashmap__init(map, hash_fn, equal_fn, ctx);
63 void hashmap__clear(struct hashmap *map)
65 struct hashmap_entry *cur, *tmp;
68 hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
73 map->cap = map->cap_bits = map->sz = 0;
76 void hashmap__free(struct hashmap *map)
85 size_t hashmap__size(const struct hashmap *map)
90 size_t hashmap__capacity(const struct hashmap *map)
95 static bool hashmap_needs_to_grow(struct hashmap *map)
97 /* grow if empty or more than 75% filled */
98 return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
101 static int hashmap_grow(struct hashmap *map)
103 struct hashmap_entry **new_buckets;
104 struct hashmap_entry *cur, *tmp;
105 size_t new_cap_bits, new_cap;
108 new_cap_bits = map->cap_bits + 1;
109 if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
110 new_cap_bits = HASHMAP_MIN_CAP_BITS;
112 new_cap = 1UL << new_cap_bits;
113 new_buckets = calloc(new_cap, sizeof(new_buckets[0]));
117 hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
118 h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
119 hashmap_add_entry(&new_buckets[h], cur);
123 map->cap_bits = new_cap_bits;
125 map->buckets = new_buckets;
130 static bool hashmap_find_entry(const struct hashmap *map,
131 const void *key, size_t hash,
132 struct hashmap_entry ***pprev,
133 struct hashmap_entry **entry)
135 struct hashmap_entry *cur, **prev_ptr;
140 for (prev_ptr = &map->buckets[hash], cur = *prev_ptr;
142 prev_ptr = &cur->next, cur = cur->next) {
143 if (map->equal_fn(cur->key, key, map->ctx)) {
154 int hashmap__insert(struct hashmap *map, const void *key, void *value,
155 enum hashmap_insert_strategy strategy,
156 const void **old_key, void **old_value)
158 struct hashmap_entry *entry;
167 h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
168 if (strategy != HASHMAP_APPEND &&
169 hashmap_find_entry(map, key, h, NULL, &entry)) {
171 *old_key = entry->key;
173 *old_value = entry->value;
175 if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
177 entry->value = value;
179 } else if (strategy == HASHMAP_ADD) {
184 if (strategy == HASHMAP_UPDATE)
187 if (hashmap_needs_to_grow(map)) {
188 err = hashmap_grow(map);
191 h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
194 entry = malloc(sizeof(struct hashmap_entry));
199 entry->value = value;
200 hashmap_add_entry(&map->buckets[h], entry);
206 bool hashmap__find(const struct hashmap *map, const void *key, void **value)
208 struct hashmap_entry *entry;
211 h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
212 if (!hashmap_find_entry(map, key, h, NULL, &entry))
216 *value = entry->value;
220 bool hashmap__delete(struct hashmap *map, const void *key,
221 const void **old_key, void **old_value)
223 struct hashmap_entry **pprev, *entry;
226 h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
227 if (!hashmap_find_entry(map, key, h, &pprev, &entry))
231 *old_key = entry->key;
233 *old_value = entry->value;
235 hashmap_del_entry(pprev, entry);