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 /* start with 4 buckets */
19 #define HASHMAP_MIN_CAP_BITS 2
21 static void hashmap_add_entry(struct hashmap_entry
**pprev
,
22 struct hashmap_entry
*entry
)
28 static void hashmap_del_entry(struct hashmap_entry
**pprev
,
29 struct hashmap_entry
*entry
)
35 void hashmap__init(struct hashmap
*map
, hashmap_hash_fn hash_fn
,
36 hashmap_equal_fn equal_fn
, void *ctx
)
38 map
->hash_fn
= hash_fn
;
39 map
->equal_fn
= equal_fn
;
48 struct hashmap
*hashmap__new(hashmap_hash_fn hash_fn
,
49 hashmap_equal_fn equal_fn
,
52 struct hashmap
*map
= malloc(sizeof(struct hashmap
));
55 return ERR_PTR(-ENOMEM
);
56 hashmap__init(map
, hash_fn
, equal_fn
, ctx
);
60 void hashmap__clear(struct hashmap
*map
)
63 map
->cap
= map
->cap_bits
= map
->sz
= 0;
66 void hashmap__free(struct hashmap
*map
)
75 size_t hashmap__size(const struct hashmap
*map
)
80 size_t hashmap__capacity(const struct hashmap
*map
)
85 static bool hashmap_needs_to_grow(struct hashmap
*map
)
87 /* grow if empty or more than 75% filled */
88 return (map
->cap
== 0) || ((map
->sz
+ 1) * 4 / 3 > map
->cap
);
91 static int hashmap_grow(struct hashmap
*map
)
93 struct hashmap_entry
**new_buckets
;
94 struct hashmap_entry
*cur
, *tmp
;
95 size_t new_cap_bits
, new_cap
;
99 new_cap_bits
= map
->cap_bits
+ 1;
100 if (new_cap_bits
< HASHMAP_MIN_CAP_BITS
)
101 new_cap_bits
= HASHMAP_MIN_CAP_BITS
;
103 new_cap
= 1UL << new_cap_bits
;
104 new_buckets
= calloc(new_cap
, sizeof(new_buckets
[0]));
108 hashmap__for_each_entry_safe(map
, cur
, tmp
, bkt
) {
109 h
= hash_bits(map
->hash_fn(cur
->key
, map
->ctx
), new_cap_bits
);
110 hashmap_add_entry(&new_buckets
[h
], cur
);
114 map
->cap_bits
= new_cap_bits
;
116 map
->buckets
= new_buckets
;
121 static bool hashmap_find_entry(const struct hashmap
*map
,
122 const void *key
, size_t hash
,
123 struct hashmap_entry
***pprev
,
124 struct hashmap_entry
**entry
)
126 struct hashmap_entry
*cur
, **prev_ptr
;
131 for (prev_ptr
= &map
->buckets
[hash
], cur
= *prev_ptr
;
133 prev_ptr
= &cur
->next
, cur
= cur
->next
) {
134 if (map
->equal_fn(cur
->key
, key
, map
->ctx
)) {
145 int hashmap__insert(struct hashmap
*map
, const void *key
, void *value
,
146 enum hashmap_insert_strategy strategy
,
147 const void **old_key
, void **old_value
)
149 struct hashmap_entry
*entry
;
158 h
= hash_bits(map
->hash_fn(key
, map
->ctx
), map
->cap_bits
);
159 if (strategy
!= HASHMAP_APPEND
&&
160 hashmap_find_entry(map
, key
, h
, NULL
, &entry
)) {
162 *old_key
= entry
->key
;
164 *old_value
= entry
->value
;
166 if (strategy
== HASHMAP_SET
|| strategy
== HASHMAP_UPDATE
) {
168 entry
->value
= value
;
170 } else if (strategy
== HASHMAP_ADD
) {
175 if (strategy
== HASHMAP_UPDATE
)
178 if (hashmap_needs_to_grow(map
)) {
179 err
= hashmap_grow(map
);
182 h
= hash_bits(map
->hash_fn(key
, map
->ctx
), map
->cap_bits
);
185 entry
= malloc(sizeof(struct hashmap_entry
));
190 entry
->value
= value
;
191 hashmap_add_entry(&map
->buckets
[h
], entry
);
197 bool hashmap__find(const struct hashmap
*map
, const void *key
, void **value
)
199 struct hashmap_entry
*entry
;
202 h
= hash_bits(map
->hash_fn(key
, map
->ctx
), map
->cap_bits
);
203 if (!hashmap_find_entry(map
, key
, h
, NULL
, &entry
))
207 *value
= entry
->value
;
211 bool hashmap__delete(struct hashmap
*map
, const void *key
,
212 const void **old_key
, void **old_value
)
214 struct hashmap_entry
**pprev
, *entry
;
217 h
= hash_bits(map
->hash_fn(key
, map
->ctx
), map
->cap_bits
);
218 if (!hashmap_find_entry(map
, key
, h
, &pprev
, &entry
))
222 *old_key
= entry
->key
;
224 *old_value
= entry
->value
;
226 hashmap_del_entry(pprev
, entry
);