/src/libtsm/src/shared/shl-htable.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SHL - Dynamic hash-table |
3 | | * |
4 | | * Written-by: Rusty Russell <rusty@rustcorp.com.au> |
5 | | * Adjusted-by: David Herrmann <dh.herrmann@gmail.com> |
6 | | * Licensed under LGPLv2+ - see LICENSE_htable file for details |
7 | | */ |
8 | | |
9 | | /* |
10 | | * Please see ccan/htable/_info at: |
11 | | * https://github.com/rustyrussell/ccan/tree/master/ccan/htable |
12 | | * for information on the hashtable algorithm. This file copies the code inline |
13 | | * and is released under the same conditions. |
14 | | * |
15 | | * At the end of the file you can find some helpers to use this htable to store |
16 | | * objects with "unsigned long" or "char*" keys. |
17 | | */ |
18 | | |
19 | | #include <assert.h> |
20 | | #include <errno.h> |
21 | | #include <limits.h> |
22 | | #include <stdbool.h> |
23 | | #include <stdint.h> |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | #include "shl-htable.h" |
27 | | |
28 | | #define COLD __attribute__((cold)) |
29 | | |
30 | | struct htable { |
31 | | /* KEEP IN SYNC WITH "struct shl_htable_int" */ |
32 | | size_t (*rehash)(const void *elem, void *priv); |
33 | | void *priv; |
34 | | unsigned int bits; |
35 | | size_t elems, deleted, max, max_with_deleted; |
36 | | /* These are the bits which are the same in all pointers. */ |
37 | | uintptr_t common_mask, common_bits; |
38 | | uintptr_t perfect_bit; |
39 | | uintptr_t *table; |
40 | | }; |
41 | | |
42 | | #define HTABLE_INITIALIZER(name, rehash, priv) \ |
43 | 27.0k | { rehash, priv, 0, 0, 0, 0, 0, -1, 0, 0, &name.perfect_bit } |
44 | | |
45 | | struct htable_iter { |
46 | | size_t off; |
47 | | }; |
48 | | |
49 | | /* |
50 | | * INLINE COPY OF ccan/htable.c |
51 | | */ |
52 | | |
53 | | /* We use 0x1 as deleted marker. */ |
54 | 0 | #define HTABLE_DELETED (0x1) |
55 | | |
56 | | /* We clear out the bits which are always the same, and put metadata there. */ |
57 | | static inline uintptr_t get_extra_ptr_bits(const struct htable *ht, |
58 | | uintptr_t e) |
59 | 0 | { |
60 | 0 | return e & ht->common_mask; |
61 | 0 | } |
62 | | |
63 | | static inline void *get_raw_ptr(const struct htable *ht, uintptr_t e) |
64 | 0 | { |
65 | 0 | return (void *)((e & ~ht->common_mask) | ht->common_bits); |
66 | 0 | } |
67 | | |
68 | | static inline uintptr_t make_hval(const struct htable *ht, |
69 | | const void *p, uintptr_t bits) |
70 | 0 | { |
71 | 0 | return ((uintptr_t)p & ~ht->common_mask) | bits; |
72 | 0 | } |
73 | | |
74 | | static inline bool entry_is_valid(uintptr_t e) |
75 | 0 | { |
76 | 0 | return e > HTABLE_DELETED; |
77 | 0 | } |
78 | | |
79 | | static inline uintptr_t get_hash_ptr_bits(const struct htable *ht, |
80 | | size_t hash) |
81 | 0 | { |
82 | | /* Shuffling the extra bits (as specified in mask) down the |
83 | | * end is quite expensive. But the lower bits are redundant, so |
84 | | * we fold the value first. */ |
85 | 0 | return (hash ^ (hash >> ht->bits)) |
86 | 0 | & ht->common_mask & ~ht->perfect_bit; |
87 | 0 | } |
88 | | |
89 | | static void htable_init(struct htable *ht, |
90 | | size_t (*rehash)(const void *elem, void *priv), |
91 | | void *priv) |
92 | 27.0k | { |
93 | 27.0k | struct htable empty = HTABLE_INITIALIZER(empty, NULL, NULL); |
94 | 27.0k | *ht = empty; |
95 | 27.0k | ht->rehash = rehash; |
96 | 27.0k | ht->priv = priv; |
97 | 27.0k | ht->table = &ht->perfect_bit; |
98 | 27.0k | } |
99 | | |
100 | | static void htable_clear(struct htable *ht, |
101 | | void (*free_cb) (void *entry, void *ctx), |
102 | | void *ctx) |
103 | 13.5k | { |
104 | 13.5k | size_t i; |
105 | | |
106 | 13.5k | if (ht->table != &ht->perfect_bit) { |
107 | 0 | if (free_cb) { |
108 | 0 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { |
109 | 0 | if (entry_is_valid(ht->table[i])) |
110 | 0 | free_cb(get_raw_ptr(ht, ht->table[i]), |
111 | 0 | ctx); |
112 | 0 | } |
113 | 0 | } |
114 | |
|
115 | 0 | free((void *)ht->table); |
116 | 0 | } |
117 | | |
118 | 13.5k | htable_init(ht, ht->rehash, ht->priv); |
119 | 13.5k | } |
120 | | |
121 | | static void htable_visit(struct htable *ht, |
122 | | void (*visit_cb) (void *elem, void *ctx), |
123 | | void *ctx) |
124 | 0 | { |
125 | 0 | size_t i; |
126 | |
|
127 | 0 | if (visit_cb && ht->table != &ht->perfect_bit) { |
128 | 0 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { |
129 | 0 | if (entry_is_valid(ht->table[i])) |
130 | 0 | visit_cb(get_raw_ptr(ht, ht->table[i]), ctx); |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | static size_t hash_bucket(const struct htable *ht, size_t h) |
136 | 0 | { |
137 | 0 | return h & ((1 << ht->bits)-1); |
138 | 0 | } |
139 | | |
140 | | static void *htable_val(const struct htable *ht, |
141 | | struct htable_iter *i, size_t hash, uintptr_t perfect) |
142 | 0 | { |
143 | 0 | uintptr_t h2 = get_hash_ptr_bits(ht, hash) | perfect; |
144 | |
|
145 | 0 | while (ht->table[i->off]) { |
146 | 0 | if (ht->table[i->off] != HTABLE_DELETED) { |
147 | 0 | if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2) |
148 | 0 | return get_raw_ptr(ht, ht->table[i->off]); |
149 | 0 | } |
150 | 0 | i->off = (i->off + 1) & ((1 << ht->bits)-1); |
151 | 0 | h2 &= ~perfect; |
152 | 0 | } |
153 | 0 | return NULL; |
154 | 0 | } |
155 | | |
156 | | static void *htable_firstval(const struct htable *ht, |
157 | | struct htable_iter *i, size_t hash) |
158 | 0 | { |
159 | 0 | i->off = hash_bucket(ht, hash); |
160 | 0 | return htable_val(ht, i, hash, ht->perfect_bit); |
161 | 0 | } |
162 | | |
163 | | static void *htable_nextval(const struct htable *ht, |
164 | | struct htable_iter *i, size_t hash) |
165 | 0 | { |
166 | 0 | i->off = (i->off + 1) & ((1 << ht->bits)-1); |
167 | 0 | return htable_val(ht, i, hash, 0); |
168 | 0 | } |
169 | | |
170 | | /* This does not expand the hash table, that's up to caller. */ |
171 | | static void ht_add(struct htable *ht, const void *new, size_t h) |
172 | 0 | { |
173 | 0 | size_t i; |
174 | 0 | uintptr_t perfect = ht->perfect_bit; |
175 | |
|
176 | 0 | i = hash_bucket(ht, h); |
177 | |
|
178 | 0 | while (entry_is_valid(ht->table[i])) { |
179 | 0 | perfect = 0; |
180 | 0 | i = (i + 1) & ((1 << ht->bits)-1); |
181 | 0 | } |
182 | 0 | ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect); |
183 | 0 | } |
184 | | |
185 | | static COLD bool double_table(struct htable *ht) |
186 | 0 | { |
187 | 0 | unsigned int i; |
188 | 0 | size_t oldnum = (size_t)1 << ht->bits; |
189 | 0 | uintptr_t *oldtable, e; |
190 | |
|
191 | 0 | oldtable = ht->table; |
192 | 0 | ht->table = calloc(1 << (ht->bits+1), sizeof(size_t)); |
193 | 0 | if (!ht->table) { |
194 | 0 | ht->table = oldtable; |
195 | 0 | return false; |
196 | 0 | } |
197 | 0 | ht->bits++; |
198 | 0 | ht->max = ((size_t)3 << ht->bits) / 4; |
199 | 0 | ht->max_with_deleted = ((size_t)9 << ht->bits) / 10; |
200 | | |
201 | | /* If we lost our "perfect bit", get it back now. */ |
202 | 0 | if (!ht->perfect_bit && ht->common_mask) { |
203 | 0 | for (i = 0; i < sizeof(ht->common_mask) * CHAR_BIT; i++) { |
204 | 0 | if (ht->common_mask & ((size_t)1 << i)) { |
205 | 0 | ht->perfect_bit = (size_t)1 << i; |
206 | 0 | break; |
207 | 0 | } |
208 | 0 | } |
209 | 0 | } |
210 | |
|
211 | 0 | if (oldtable != &ht->perfect_bit) { |
212 | 0 | for (i = 0; i < oldnum; i++) { |
213 | 0 | if (entry_is_valid(e = oldtable[i])) { |
214 | 0 | void *p = get_raw_ptr(ht, e); |
215 | 0 | ht_add(ht, p, ht->rehash(p, ht->priv)); |
216 | 0 | } |
217 | 0 | } |
218 | 0 | free(oldtable); |
219 | 0 | } |
220 | 0 | ht->deleted = 0; |
221 | 0 | return true; |
222 | 0 | } |
223 | | |
224 | | static COLD void rehash_table(struct htable *ht) |
225 | 0 | { |
226 | 0 | size_t start, i; |
227 | 0 | uintptr_t e; |
228 | | |
229 | | /* Beware wrap cases: we need to start from first empty bucket. */ |
230 | 0 | for (start = 0; ht->table[start]; start++); |
231 | |
|
232 | 0 | for (i = 0; i < (size_t)1 << ht->bits; i++) { |
233 | 0 | size_t h = (i + start) & ((1 << ht->bits)-1); |
234 | 0 | e = ht->table[h]; |
235 | 0 | if (!e) |
236 | 0 | continue; |
237 | 0 | if (e == HTABLE_DELETED) |
238 | 0 | ht->table[h] = 0; |
239 | 0 | else if (!(e & ht->perfect_bit)) { |
240 | 0 | void *p = get_raw_ptr(ht, e); |
241 | 0 | ht->table[h] = 0; |
242 | 0 | ht_add(ht, p, ht->rehash(p, ht->priv)); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | ht->deleted = 0; |
246 | 0 | } |
247 | | |
248 | | /* We stole some bits, now we need to put them back... */ |
249 | | static COLD void update_common(struct htable *ht, const void *p) |
250 | 0 | { |
251 | 0 | unsigned int i; |
252 | 0 | uintptr_t maskdiff, bitsdiff; |
253 | |
|
254 | 0 | if (ht->elems == 0) { |
255 | | /* Always reveal one bit of the pointer in the bucket, |
256 | | * so it's not zero or HTABLE_DELETED (1), even if |
257 | | * hash happens to be 0. Assumes (void *)1 is not a |
258 | | * valid pointer. */ |
259 | 0 | for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) { |
260 | 0 | if ((uintptr_t)p & ((uintptr_t)1 << i)) |
261 | 0 | break; |
262 | 0 | } |
263 | |
|
264 | 0 | ht->common_mask = ~((uintptr_t)1 << i); |
265 | 0 | ht->common_bits = ((uintptr_t)p & ht->common_mask); |
266 | 0 | ht->perfect_bit = 1; |
267 | 0 | return; |
268 | 0 | } |
269 | | |
270 | | /* Find bits which are unequal to old common set. */ |
271 | 0 | maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask); |
272 | | |
273 | | /* These are the bits which go there in existing entries. */ |
274 | 0 | bitsdiff = ht->common_bits & maskdiff; |
275 | |
|
276 | 0 | for (i = 0; i < (size_t)1 << ht->bits; i++) { |
277 | 0 | if (!entry_is_valid(ht->table[i])) |
278 | 0 | continue; |
279 | | /* Clear the bits no longer in the mask, set them as |
280 | | * expected. */ |
281 | 0 | ht->table[i] &= ~maskdiff; |
282 | 0 | ht->table[i] |= bitsdiff; |
283 | 0 | } |
284 | | |
285 | | /* Take away those bits from our mask, bits and perfect bit. */ |
286 | 0 | ht->common_mask &= ~maskdiff; |
287 | 0 | ht->common_bits &= ~maskdiff; |
288 | 0 | ht->perfect_bit &= ~maskdiff; |
289 | 0 | } |
290 | | |
291 | | static bool htable_add(struct htable *ht, size_t hash, const void *p) |
292 | 0 | { |
293 | 0 | if (ht->elems+1 > ht->max && !double_table(ht)) |
294 | 0 | return false; |
295 | 0 | if (ht->elems+1 + ht->deleted > ht->max_with_deleted) |
296 | 0 | rehash_table(ht); |
297 | 0 | assert(p); |
298 | 0 | if (((uintptr_t)p & ht->common_mask) != ht->common_bits) |
299 | 0 | update_common(ht, p); |
300 | |
|
301 | 0 | ht_add(ht, p, hash); |
302 | 0 | ht->elems++; |
303 | 0 | return true; |
304 | 0 | } |
305 | | |
306 | | static void htable_delval(struct htable *ht, struct htable_iter *i) |
307 | 0 | { |
308 | 0 | assert(i->off < (size_t)1 << ht->bits); |
309 | 0 | assert(entry_is_valid(ht->table[i->off])); |
310 | | |
311 | 0 | ht->elems--; |
312 | 0 | ht->table[i->off] = HTABLE_DELETED; |
313 | 0 | ht->deleted++; |
314 | 0 | } |
315 | | |
316 | | /* |
317 | | * Wrapper code to make it easier to use this hash-table as map. |
318 | | */ |
319 | | |
320 | | void shl_htable_init(struct shl_htable *htable, |
321 | | bool (*compare) (const void *a, const void *b), |
322 | | size_t (*rehash)(const void *elem, void *priv), |
323 | | void *priv) |
324 | 13.5k | { |
325 | 13.5k | struct htable *ht = (void*)&htable->htable; |
326 | | |
327 | 13.5k | htable->compare = compare; |
328 | 13.5k | htable_init(ht, rehash, priv); |
329 | 13.5k | } |
330 | | |
331 | | void shl_htable_clear(struct shl_htable *htable, |
332 | | void (*free_cb) (void *elem, void *ctx), |
333 | | void *ctx) |
334 | 13.5k | { |
335 | 13.5k | struct htable *ht = (void*)&htable->htable; |
336 | | |
337 | 13.5k | htable_clear(ht, free_cb, ctx); |
338 | 13.5k | } |
339 | | |
340 | | void shl_htable_visit(struct shl_htable *htable, |
341 | | void (*visit_cb) (void *elem, void *ctx), |
342 | | void *ctx) |
343 | 0 | { |
344 | 0 | struct htable *ht = (void*)&htable->htable; |
345 | |
|
346 | 0 | htable_visit(ht, visit_cb, ctx); |
347 | 0 | } |
348 | | |
349 | | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, |
350 | | void **out) |
351 | 0 | { |
352 | 0 | struct htable *ht = (void*)&htable->htable; |
353 | 0 | struct htable_iter i; |
354 | 0 | void *c; |
355 | |
|
356 | 0 | for (c = htable_firstval(ht, &i, hash); |
357 | 0 | c; |
358 | 0 | c = htable_nextval(ht, &i, hash)) { |
359 | 0 | if (htable->compare(obj, c)) { |
360 | 0 | if (out) |
361 | 0 | *out = c; |
362 | 0 | return true; |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 0 | return false; |
367 | 0 | } |
368 | | |
369 | | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash) |
370 | 0 | { |
371 | 0 | struct htable *ht = (void*)&htable->htable; |
372 | 0 | bool b; |
373 | |
|
374 | 0 | b = htable_add(ht, hash, (void*)obj); |
375 | 0 | return b ? 0 : -ENOMEM; |
376 | 0 | } |
377 | | |
378 | | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, |
379 | | void **out) |
380 | 0 | { |
381 | 0 | struct htable *ht = (void*)&htable->htable; |
382 | 0 | struct htable_iter i; |
383 | 0 | void *c; |
384 | |
|
385 | 0 | for (c = htable_firstval(ht, &i, hash); |
386 | 0 | c; |
387 | 0 | c = htable_nextval(ht, &i, hash)) { |
388 | 0 | if (htable->compare(obj, c)) { |
389 | 0 | if (out) |
390 | 0 | *out = c; |
391 | 0 | htable_delval(ht, &i); |
392 | 0 | return true; |
393 | 0 | } |
394 | 0 | } |
395 | | |
396 | 0 | return false; |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | * Helpers |
401 | | */ |
402 | | |
403 | | bool shl_htable_compare_ulong(const void *a, const void *b) |
404 | 0 | { |
405 | 0 | return *(const unsigned long*)a == *(const unsigned long*)b; |
406 | 0 | } |
407 | | |
408 | | size_t shl_htable_rehash_ulong(const void *elem, void *priv) |
409 | 0 | { |
410 | 0 | return (size_t)*(const unsigned long*)elem; |
411 | 0 | } |
412 | | |
413 | | bool shl_htable_compare_str(const void *a, const void *b) |
414 | 0 | { |
415 | 0 | return !strcmp(*(char**)a, *(char**)b); |
416 | 0 | } |
417 | | |
418 | | /* DJB's hash function */ |
419 | | size_t shl_htable_rehash_str(const void *elem, void *priv) |
420 | 0 | { |
421 | 0 | const char *str = *(char**)elem; |
422 | 0 | size_t hash = 5381; |
423 | |
|
424 | 0 | for ( ; *str; ++str) |
425 | 0 | hash = (hash << 5) + hash + (size_t)*str; |
426 | |
|
427 | 0 | return hash; |
428 | 0 | } |