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