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
);