Coverage Report

Created: 2023-03-26 06:21

/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
}