1*4882a593Smuzhiyun // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun /*
4*4882a593Smuzhiyun * Generic non-thread safe hash map implementation.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (c) 2019 Facebook
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #include <stdint.h>
9*4882a593Smuzhiyun #include <stdlib.h>
10*4882a593Smuzhiyun #include <stdio.h>
11*4882a593Smuzhiyun #include <errno.h>
12*4882a593Smuzhiyun #include <linux/err.h>
13*4882a593Smuzhiyun #include "hashmap.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun /* make sure libbpf doesn't use kernel-only integer typedefs */
16*4882a593Smuzhiyun #pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun /* prevent accidental re-addition of reallocarray() */
19*4882a593Smuzhiyun #pragma GCC poison reallocarray
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /* start with 4 buckets */
22*4882a593Smuzhiyun #define HASHMAP_MIN_CAP_BITS 2
23*4882a593Smuzhiyun
hashmap_add_entry(struct hashmap_entry ** pprev,struct hashmap_entry * entry)24*4882a593Smuzhiyun static void hashmap_add_entry(struct hashmap_entry **pprev,
25*4882a593Smuzhiyun struct hashmap_entry *entry)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun entry->next = *pprev;
28*4882a593Smuzhiyun *pprev = entry;
29*4882a593Smuzhiyun }
30*4882a593Smuzhiyun
hashmap_del_entry(struct hashmap_entry ** pprev,struct hashmap_entry * entry)31*4882a593Smuzhiyun static void hashmap_del_entry(struct hashmap_entry **pprev,
32*4882a593Smuzhiyun struct hashmap_entry *entry)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun *pprev = entry->next;
35*4882a593Smuzhiyun entry->next = NULL;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
hashmap__init(struct hashmap * map,hashmap_hash_fn hash_fn,hashmap_equal_fn equal_fn,void * ctx)38*4882a593Smuzhiyun void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
39*4882a593Smuzhiyun hashmap_equal_fn equal_fn, void *ctx)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun map->hash_fn = hash_fn;
42*4882a593Smuzhiyun map->equal_fn = equal_fn;
43*4882a593Smuzhiyun map->ctx = ctx;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun map->buckets = NULL;
46*4882a593Smuzhiyun map->cap = 0;
47*4882a593Smuzhiyun map->cap_bits = 0;
48*4882a593Smuzhiyun map->sz = 0;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
hashmap__new(hashmap_hash_fn hash_fn,hashmap_equal_fn equal_fn,void * ctx)51*4882a593Smuzhiyun struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
52*4882a593Smuzhiyun hashmap_equal_fn equal_fn,
53*4882a593Smuzhiyun void *ctx)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun struct hashmap *map = malloc(sizeof(struct hashmap));
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun if (!map)
58*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
59*4882a593Smuzhiyun hashmap__init(map, hash_fn, equal_fn, ctx);
60*4882a593Smuzhiyun return map;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
hashmap__clear(struct hashmap * map)63*4882a593Smuzhiyun void hashmap__clear(struct hashmap *map)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun struct hashmap_entry *cur, *tmp;
66*4882a593Smuzhiyun size_t bkt;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
69*4882a593Smuzhiyun free(cur);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun free(map->buckets);
72*4882a593Smuzhiyun map->buckets = NULL;
73*4882a593Smuzhiyun map->cap = map->cap_bits = map->sz = 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
hashmap__free(struct hashmap * map)76*4882a593Smuzhiyun void hashmap__free(struct hashmap *map)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun if (!map)
79*4882a593Smuzhiyun return;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun hashmap__clear(map);
82*4882a593Smuzhiyun free(map);
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
hashmap__size(const struct hashmap * map)85*4882a593Smuzhiyun size_t hashmap__size(const struct hashmap *map)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun return map->sz;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
hashmap__capacity(const struct hashmap * map)90*4882a593Smuzhiyun size_t hashmap__capacity(const struct hashmap *map)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun return map->cap;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
hashmap_needs_to_grow(struct hashmap * map)95*4882a593Smuzhiyun static bool hashmap_needs_to_grow(struct hashmap *map)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun /* grow if empty or more than 75% filled */
98*4882a593Smuzhiyun return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
hashmap_grow(struct hashmap * map)101*4882a593Smuzhiyun static int hashmap_grow(struct hashmap *map)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun struct hashmap_entry **new_buckets;
104*4882a593Smuzhiyun struct hashmap_entry *cur, *tmp;
105*4882a593Smuzhiyun size_t new_cap_bits, new_cap;
106*4882a593Smuzhiyun size_t h, bkt;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun new_cap_bits = map->cap_bits + 1;
109*4882a593Smuzhiyun if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
110*4882a593Smuzhiyun new_cap_bits = HASHMAP_MIN_CAP_BITS;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun new_cap = 1UL << new_cap_bits;
113*4882a593Smuzhiyun new_buckets = calloc(new_cap, sizeof(new_buckets[0]));
114*4882a593Smuzhiyun if (!new_buckets)
115*4882a593Smuzhiyun return -ENOMEM;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
118*4882a593Smuzhiyun h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
119*4882a593Smuzhiyun hashmap_add_entry(&new_buckets[h], cur);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun map->cap = new_cap;
123*4882a593Smuzhiyun map->cap_bits = new_cap_bits;
124*4882a593Smuzhiyun free(map->buckets);
125*4882a593Smuzhiyun map->buckets = new_buckets;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
hashmap_find_entry(const struct hashmap * map,const void * key,size_t hash,struct hashmap_entry *** pprev,struct hashmap_entry ** entry)130*4882a593Smuzhiyun static bool hashmap_find_entry(const struct hashmap *map,
131*4882a593Smuzhiyun const void *key, size_t hash,
132*4882a593Smuzhiyun struct hashmap_entry ***pprev,
133*4882a593Smuzhiyun struct hashmap_entry **entry)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun struct hashmap_entry *cur, **prev_ptr;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun if (!map->buckets)
138*4882a593Smuzhiyun return false;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun for (prev_ptr = &map->buckets[hash], cur = *prev_ptr;
141*4882a593Smuzhiyun cur;
142*4882a593Smuzhiyun prev_ptr = &cur->next, cur = cur->next) {
143*4882a593Smuzhiyun if (map->equal_fn(cur->key, key, map->ctx)) {
144*4882a593Smuzhiyun if (pprev)
145*4882a593Smuzhiyun *pprev = prev_ptr;
146*4882a593Smuzhiyun *entry = cur;
147*4882a593Smuzhiyun return true;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun return false;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
hashmap__insert(struct hashmap * map,const void * key,void * value,enum hashmap_insert_strategy strategy,const void ** old_key,void ** old_value)154*4882a593Smuzhiyun int hashmap__insert(struct hashmap *map, const void *key, void *value,
155*4882a593Smuzhiyun enum hashmap_insert_strategy strategy,
156*4882a593Smuzhiyun const void **old_key, void **old_value)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct hashmap_entry *entry;
159*4882a593Smuzhiyun size_t h;
160*4882a593Smuzhiyun int err;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (old_key)
163*4882a593Smuzhiyun *old_key = NULL;
164*4882a593Smuzhiyun if (old_value)
165*4882a593Smuzhiyun *old_value = NULL;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
168*4882a593Smuzhiyun if (strategy != HASHMAP_APPEND &&
169*4882a593Smuzhiyun hashmap_find_entry(map, key, h, NULL, &entry)) {
170*4882a593Smuzhiyun if (old_key)
171*4882a593Smuzhiyun *old_key = entry->key;
172*4882a593Smuzhiyun if (old_value)
173*4882a593Smuzhiyun *old_value = entry->value;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
176*4882a593Smuzhiyun entry->key = key;
177*4882a593Smuzhiyun entry->value = value;
178*4882a593Smuzhiyun return 0;
179*4882a593Smuzhiyun } else if (strategy == HASHMAP_ADD) {
180*4882a593Smuzhiyun return -EEXIST;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun if (strategy == HASHMAP_UPDATE)
185*4882a593Smuzhiyun return -ENOENT;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun if (hashmap_needs_to_grow(map)) {
188*4882a593Smuzhiyun err = hashmap_grow(map);
189*4882a593Smuzhiyun if (err)
190*4882a593Smuzhiyun return err;
191*4882a593Smuzhiyun h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun entry = malloc(sizeof(struct hashmap_entry));
195*4882a593Smuzhiyun if (!entry)
196*4882a593Smuzhiyun return -ENOMEM;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun entry->key = key;
199*4882a593Smuzhiyun entry->value = value;
200*4882a593Smuzhiyun hashmap_add_entry(&map->buckets[h], entry);
201*4882a593Smuzhiyun map->sz++;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun return 0;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
hashmap__find(const struct hashmap * map,const void * key,void ** value)206*4882a593Smuzhiyun bool hashmap__find(const struct hashmap *map, const void *key, void **value)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun struct hashmap_entry *entry;
209*4882a593Smuzhiyun size_t h;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
212*4882a593Smuzhiyun if (!hashmap_find_entry(map, key, h, NULL, &entry))
213*4882a593Smuzhiyun return false;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (value)
216*4882a593Smuzhiyun *value = entry->value;
217*4882a593Smuzhiyun return true;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
hashmap__delete(struct hashmap * map,const void * key,const void ** old_key,void ** old_value)220*4882a593Smuzhiyun bool hashmap__delete(struct hashmap *map, const void *key,
221*4882a593Smuzhiyun const void **old_key, void **old_value)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun struct hashmap_entry **pprev, *entry;
224*4882a593Smuzhiyun size_t h;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
227*4882a593Smuzhiyun if (!hashmap_find_entry(map, key, h, &pprev, &entry))
228*4882a593Smuzhiyun return false;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun if (old_key)
231*4882a593Smuzhiyun *old_key = entry->key;
232*4882a593Smuzhiyun if (old_value)
233*4882a593Smuzhiyun *old_value = entry->value;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun hashmap_del_entry(pprev, entry);
236*4882a593Smuzhiyun free(entry);
237*4882a593Smuzhiyun map->sz--;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return true;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242