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