• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 23966678904

03 Apr 2026 11:59PM UTC coverage: 92.402% (+0.02%) from 92.381%
23966678904

Pull #1384

github

web-flow
Merge f2d03bd19 into 8749025fa
Pull Request #1384: Random signal initialization plugin.

21 of 21 new or added lines in 1 file covered. (100.0%)

3 existing lines in 2 files now uncovered.

76802 of 83117 relevant lines covered (92.4%)

608291.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.71
/src/hash.c
1
//
2
//  Copyright (C) 2013-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "hash.h"
19
#include "thread.h"
20

21
#include <stdlib.h>
22
#include <string.h>
23
#include <assert.h>
24

25
////////////////////////////////////////////////////////////////////////////////
26
// Hash table of pointers to pointers
27

28
struct _hash {
29
   unsigned     size;
30
   unsigned     members;
31
   void       **values;
32
   const void **keys;
33
};
34

35
static inline int hash_slot(unsigned size, const void *key)
9,721,708✔
36
{
37
   assert(key != NULL);
9,721,708✔
38
   return mix_bits_64((uintptr_t)key) & (size - 1);
9,721,708✔
39
}
40

41
hash_t *hash_new(int size)
172,008✔
42
{
43
   assert(size > 0);
172,008✔
44

45
   hash_t *h = xmalloc(sizeof(hash_t));
172,008✔
46
   h->size    = next_power_of_2(size);
172,008✔
47
   h->members = 0;
172,008✔
48

49
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
172,008✔
50
   h->values = (void **)mem;
172,008✔
51
   h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
172,008✔
52

53
   return h;
172,008✔
54
}
55

56
void hash_free(hash_t *h)
485,266✔
57
{
58
   if (h != NULL) {
485,266✔
59
      free(h->values);
163,341✔
60
      free(h);
163,341✔
61
   }
62
}
485,266✔
63

64
bool hash_put(hash_t *h, const void *key, void *value)
1,786,232✔
65
{
66
   if (unlikely(h->members > h->size / 2)) {
1,786,232✔
67
      // Rebuild the hash table with a larger size
68
      // This is expensive so a conservative initial size should be chosen
69

70
      const int old_size = h->size;
8,800✔
71
      h->size *= 2;
8,800✔
72

73
      const void **old_keys = h->keys;
8,800✔
74
      void **old_values = h->values;
8,800✔
75

76
      char *mem = xcalloc(h->size * 2 * sizeof(void *));
8,800✔
77
      h->values = (void **)mem;
8,800✔
78
      h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
8,800✔
79

80
      h->members = 0;
8,800✔
81

82
      for (int i = 0; i < old_size; i++) {
2,388,140✔
83
         if (old_keys[i] != NULL && old_values[i] != NULL) {
2,379,340✔
84
            int slot = hash_slot(h->size, old_keys[i]);
1,198,470✔
85

86
            for (; ; slot = (slot + 1) & (h->size - 1)) {
205,828✔
87
               if (h->keys[slot] == NULL) {
1,404,298✔
88
                  h->values[slot] = old_values[i];
1,198,470✔
89
                  h->keys[slot] = old_keys[i];
1,198,470✔
90
                  h->members++;
1,198,470✔
91
                  break;
1,198,470✔
92
               }
93
               else
94
                  assert(h->keys[slot] != old_keys[i]);
205,828✔
95
            }
96
         }
97
      }
98

99
      free(old_values);
8,800✔
100
   }
101

102
   int slot = hash_slot(h->size, key);
1,786,232✔
103

104
   for (; ; slot = (slot + 1) & (h->size - 1)) {
784,875✔
105
      if (h->keys[slot] == key) {
2,571,107✔
106
         h->values[slot] = value;
114,978✔
107
         return true;
114,978✔
108
      }
109
      else if (h->keys[slot] == NULL) {
2,456,129✔
110
         h->values[slot] = value;
1,671,254✔
111
         h->keys[slot] = key;
1,671,254✔
112
         h->members++;
1,671,254✔
113
         break;
1,671,254✔
114
      }
115
   }
116

117
   return false;
1,671,254✔
118
}
119

120
void hash_delete(hash_t *h, const void *key)
2✔
121
{
122
   int slot = hash_slot(h->size, key);
2✔
123

UNCOV
124
   for (; ; slot = (slot + 1) & (h->size - 1)) {
×
125
      if (h->keys[slot] == key) {
2✔
126
         h->values[slot] = NULL;
2✔
127
         return;
2✔
128
      }
UNCOV
129
      else if (h->keys[slot] == NULL)
×
130
         return;
131
   }
132
}
133

134
void *hash_get(hash_t *h, const void *key)
3,212,232✔
135
{
136
   int slot = hash_slot(h->size, key);
3,212,232✔
137

138
   for (; ; slot = (slot + 1) & (h->size - 1)) {
913,737✔
139
      if (h->keys[slot] == key)
4,125,969✔
140
         return h->values[slot];
1,336,839✔
141
      else if (h->keys[slot] == NULL)
2,789,130✔
142
         return NULL;
143
   }
144
}
145

146
bool hash_iter(hash_t *h, hash_iter_t *now, const void **key, void **value)
1,224,385✔
147
{
148
   assert(*now != HASH_END);
1,224,385✔
149

150
   while (*now < h->size) {
21,356,582✔
151
      const unsigned old = (*now)++;
21,320,912✔
152
      if (h->keys[old] != NULL && h->values[old] != NULL) {
21,320,912✔
153
         *key   = h->keys[old];
1,188,715✔
154
         *value = h->values[old];
1,188,715✔
155
         return true;
1,188,715✔
156
      }
157
   }
158

159
   *now = HASH_END;
35,670✔
160
   return false;
35,670✔
161
}
162

163
unsigned hash_members(hash_t *h)
×
164
{
165
   return h->members;
×
166
}
167

168
////////////////////////////////////////////////////////////////////////////////
169
// Hash table of strings to pointers
170

171
struct _shash {
172
   unsigned   size;
173
   unsigned   members;
174
   void     **values;
175
   char     **keys;
176
};
177

178
static inline int shash_slot(shash_t *h, const char *key)
252,618✔
179
{
180
   assert(key != NULL);
252,618✔
181

182
   // DJB2 hash function from here:
183
   //   http://www.cse.yorku.ca/~oz/hash.html
184

185
   uint32_t hash = 5381;
186
   int c;
187

188
   while ((c = *key++))
3,476,861✔
189
      hash = ((hash << 5) + hash) + c;
3,224,243✔
190

191
   return mix_bits_32(hash) & (h->size - 1);
252,618✔
192
}
193

194
shash_t *shash_new(int size)
12,096✔
195
{
196
   shash_t *h = xmalloc(sizeof(shash_t));
12,096✔
197
   h->size    = next_power_of_2(size);
12,096✔
198
   h->members = 0;
12,096✔
199

200
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
12,096✔
201
   h->values = (void **)mem;
12,096✔
202
   h->keys   = (char **)(mem + (h->size * sizeof(void *)));
12,096✔
203

204
   return h;
12,096✔
205
}
206

207
void shash_free(shash_t *h)
5,745✔
208
{
209
   if (h == NULL)
5,745✔
210
      return;
211

212
   for (unsigned i = 0; i < h->size; i++) {
366,681✔
213
      if (h->keys[i] != NULL)
360,936✔
214
         free(h->keys[i]);
99,767✔
215
   }
216

217
   free(h->values);
5,745✔
218
   free(h);
5,745✔
219
}
220

221
static void shash_put_copy(shash_t *h, char *key, void *value)
147,227✔
222
{
223
   int slot = shash_slot(h, key);
147,227✔
224

225
   for (; ; slot = (slot + 1) & (h->size - 1)) {
29,663✔
226
      if (h->keys[slot] == NULL) {
176,890✔
227
         h->values[slot] = value;
147,227✔
228
         h->keys[slot] = key;
147,227✔
229
         h->members++;
147,227✔
230
         break;
147,227✔
231
      }
232
      else if (strcmp(h->keys[slot], key) == 0) {
29,663✔
233
         h->values[slot] = value;
×
234
         return;
×
235
      }
236
   }
237
}
238

239
void shash_put(shash_t *h, const char *key, void *value)
137,995✔
240
{
241
   if (unlikely(h->members > h->size / 2)) {
137,995✔
242
      // Rebuild the hash table with a larger size
243

244
      const int old_size = h->size;
20✔
245
      h->size *= 2;
20✔
246

247
      char **old_keys = h->keys;
20✔
248
      void **old_values = h->values;
20✔
249

250
      char *mem = xcalloc(h->size * 2 * sizeof(void *));
20✔
251
      h->values = (void **)mem;
20✔
252
      h->keys   = (char **)(mem + (h->size * sizeof(void *)));
20✔
253

254
      h->members = 0;
20✔
255

256
      for (int i = 0; i < old_size; i++) {
18,444✔
257
         if (old_keys[i] != NULL)
18,424✔
258
            shash_put_copy(h, old_keys[i], old_values[i]);
9,232✔
259
      }
260

261
      free(old_values);
20✔
262
   }
263

264
   shash_put_copy(h, xstrdup(key), value);
137,995✔
265
}
137,995✔
266

267
void *shash_get(shash_t *h, const char *key)
105,391✔
268
{
269
   int slot = shash_slot(h, key);
105,391✔
270

271
   for (; ; slot = (slot + 1) & (h->size - 1)) {
20,196✔
272
      if (h->keys[slot] == NULL)
125,587✔
273
         return NULL;
274
      else if (strcmp(h->keys[slot], key) == 0)
75,098✔
275
         return h->values[slot];
54,902✔
276
   }
277
}
278

279
void shash_delete(shash_t *h, const void *key)
×
280
{
281
   int slot = shash_slot(h, key);
×
282

283
   for (; ; slot = (slot + 1) & (h->size - 1)) {
×
284
      if (h->keys[slot] == NULL)
×
285
         return;
286
      else if (strcmp(h->keys[slot], key) == 0) {
×
287
         h->values[slot] = NULL;
×
288
         return;
×
289
      }
290
   }
291
}
292

293
bool shash_iter(shash_t *h, hash_iter_t *now, const char **key, void **value)
4,142✔
294
{
295
   assert(*now != HASH_END);
4,142✔
296

297
   while (*now < h->size) {
20,470✔
298
      const unsigned old = (*now)++;
19,616✔
299
      if (h->keys[old] != NULL && h->values[old] != NULL) {
19,616✔
300
         *key   = h->keys[old];
3,288✔
301
         *value = h->values[old];
3,288✔
302
         return true;
3,288✔
303
      }
304
   }
305

306
   *now = HASH_END;
854✔
307
   return false;
854✔
308
}
309

310
////////////////////////////////////////////////////////////////////////////////
311
// Hash of unsigned integers to pointers
312

313
struct _ihash {
314
   unsigned   size;
315
   unsigned   members;
316
   void     **values;
317
   uint64_t  *keys;
318
   uint64_t  *mask;
319
   uint64_t   cachekey;
320
   void      *cacheval;
321
};
322

323
static inline int ihash_slot(ihash_t *h, uint64_t key)
971,040✔
324
{
325
   key = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
971,040✔
326
   key = (key ^ (key >> 27)) * UINT64_C(0x94d049bb133111eb);
971,040✔
327
   key = key ^ (key >> 31);
971,040✔
328

329
   return key & (h->size - 1);
971,040✔
330
}
331

332
ihash_t *ihash_new(int size)
82,472✔
333
{
334
   ihash_t *h = xmalloc(sizeof(ihash_t));
82,472✔
335
   h->size    = next_power_of_2(size);
82,472✔
336
   h->members = 0;
82,472✔
337

338
   const size_t bytes =
82,472✔
339
      h->size * sizeof(void *) +
82,472✔
340
      h->size * sizeof(uint64_t) +
341
      (h->size + 63 / 64) * sizeof(uint64_t);
342

343
   char *mem = xcalloc(bytes);
82,472✔
344
   h->values = (void **)mem;
82,472✔
345
   h->keys   = (uint64_t *)(mem + (h->size * sizeof(void *)));
82,472✔
346
   h->mask   = (uint64_t *)(mem + (h->size * sizeof(void *))
82,472✔
347
                            + (h->size * sizeof(uint64_t)));
82,472✔
348

349
   return h;
82,472✔
350
}
351

352
void ihash_free(ihash_t *h)
97,114✔
353
{
354
   if (h != NULL) {
97,114✔
355
      free(h->values);
82,459✔
356
      free(h);
82,459✔
357
   }
358
}
97,114✔
359

360
void ihash_put(ihash_t *h, uint64_t key, void *value)
291,418✔
361
{
362
   if (unlikely(h->members > h->size / 2)) {
291,418✔
363
      // Rebuild the hash table with a larger size
364

365
      const int old_size = h->size;
70✔
366
      h->size *= 2;
70✔
367

368
      uint64_t *old_keys = h->keys;
70✔
369
      uint64_t *old_mask = h->mask;
70✔
370
      void **old_values = h->values;
70✔
371

372
      const size_t bytes =
70✔
373
         h->size * sizeof(void *) +
70✔
374
         h->size * sizeof(uint64_t) +
70✔
375
         (ALIGN_UP(h->size, 64) / 64) * sizeof(uint64_t);
70✔
376

377
      char *mem = xcalloc(bytes);
70✔
378
      h->values = (void **)mem;
70✔
379
      h->keys   = (uint64_t *)(mem + (h->size * sizeof(void *)));
70✔
380
      h->mask   = (uint64_t *)(mem + (h->size * sizeof(void *))
70✔
381
                               + (h->size * sizeof(uint64_t)));
70✔
382

383
      h->members = 0;
70✔
384

385
      for (int i = 0; i < old_size; i++) {
6,502✔
386
         if (old_mask[i / 64] & (UINT64_C(1) << (i % 64)))
6,432✔
387
            ihash_put(h, old_keys[i], old_values[i]);
3,286✔
388
      }
389

390
      free(old_values);
70✔
391
   }
392

393
   h->cachekey = key;
291,418✔
394
   h->cacheval = value;
291,418✔
395

396
   int slot = ihash_slot(h, key);
291,418✔
397

398
   for (; ; slot = (slot + 1) & (h->size - 1)) {
14,729✔
399
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)))) {
306,147✔
400
         h->values[slot] = value;
291,418✔
401
         h->keys[slot] = key;
291,418✔
402
         h->mask[slot / 64] |= (UINT64_C(1) << (slot % 64));
291,418✔
403
         h->members++;
291,418✔
404
         break;
291,418✔
405
      }
406
      else if (h->keys[slot] == key) {
14,729✔
407
         h->values[slot] = value;
×
408
         assert(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)));
×
409
         return;
410
      }
411
   }
412
}
413

414
void *ihash_get(ihash_t *h, uint64_t key)
1,222,526✔
415
{
416
   if (h->members > 0 && key == h->cachekey)
1,222,526✔
417
      return h->cacheval;
542,904✔
418

419
   h->cachekey = key;
679,622✔
420

421
   int slot = ihash_slot(h, key);
679,622✔
422

423
   for (; ; slot = (slot + 1) & (h->size - 1)) {
28,407✔
424
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64))))
708,029✔
425
         return (h->cacheval = NULL);
286,255✔
426
      else if (h->keys[slot] == key)
421,774✔
427
         return (h->cacheval = h->values[slot]);
393,367✔
428
   }
429
}
430

431
////////////////////////////////////////////////////////////////////////////////
432
// Set of pointers implemented as a hash table
433

434
struct _hset {
435
   unsigned     size;
436
   unsigned     members;
437
   const void **keys;
438
};
439

440
hset_t *hset_new(int size)
23,990✔
441
{
442
   hset_t *h = xmalloc(sizeof(hset_t));
23,990✔
443
   h->size    = next_power_of_2(size);
23,990✔
444
   h->members = 0;
23,990✔
445
   h->keys    = xcalloc_array(h->size, sizeof(void *));
23,990✔
446

447
   return h;
23,990✔
448
}
449

450
void hset_free(hset_t *h)
23,990✔
451
{
452
   if (h != NULL) {
23,990✔
453
      free(h->keys);
23,990✔
454
      free(h);
23,990✔
455
   }
456
}
23,990✔
457

458
void hset_insert(hset_t *h, const void *key)
46,857✔
459
{
460
   if (unlikely(h->members > h->size / 2)) {
46,857✔
461
      const int old_size = h->size;
486✔
462
      h->size *= 2;
486✔
463

464
      const void **old_keys = h->keys;
486✔
465
      h->keys = xcalloc_array(h->size, sizeof(void *));
486✔
466

467
      h->members = 0;
486✔
468

469
      for (int i = 0; i < old_size; i++) {
12,346✔
470
         if (old_keys[i] != NULL)
11,860✔
471
            hset_insert(h, old_keys[i]);
6,416✔
472
      }
473

474
      free(old_keys);
486✔
475
   }
476

477
   int slot = hash_slot(h->size, key);
46,857✔
478

479
   for (; ; slot = (slot + 1) & (h->size - 1)) {
8,700✔
480
      if (h->keys[slot] == key)
55,557✔
481
         return;
482
      else if (h->keys[slot] == NULL) {
55,532✔
483
         h->keys[slot] = key;
46,832✔
484
         h->members++;
46,832✔
485
         break;
46,832✔
486
      }
487
   }
488
}
489

490
bool hset_contains(hset_t *h, const void *key)
65,278✔
491
{
492
   int slot = hash_slot(h->size, key);
65,278✔
493

494
   for (; ; slot = (slot + 1) & (h->size - 1)) {
13,984✔
495
      if (h->keys[slot] == key)
79,262✔
496
         return true;
497
      else if (h->keys[slot] == NULL)
54,881✔
498
         return false;
499
   }
500
}
501

502
////////////////////////////////////////////////////////////////////////////////
503
// Hash table that supports concurrent updates
504

505
typedef struct _chash_node chash_node_t;
506

507
typedef struct {
508
   void *tagged_key;
509
   void *value;
510
} chash_slot_t;
511

512
typedef struct {
513
   size_t       size;
514
   chash_slot_t slots[0];
515
} chash_tab_t;
516

517
struct _chash_node {
518
   chash_node_t *chain;
519
   const void   *key;
520
   void         *value;
521
};
522

523
struct _chash {
524
   chash_tab_t *tab;
525
   chash_tab_t *resizing;
526
   size_t       members;
527
};
528

529
typedef enum {
530
   FREE_MARKER,
531
   BUSY_MARKER,
532
   INUSE_MARKER,
533
   MOVED_MARKER,
534
} chash_marker_t;
535

536
chash_t *chash_new(int size)
33,508✔
537
{
538
   const int roundup = next_power_of_2(size);
33,508✔
539

540
   chash_tab_t *tab =
33,508✔
541
      xcalloc_flex(sizeof(chash_tab_t), roundup, sizeof(chash_slot_t));
33,508✔
542
   tab->size = roundup;
33,508✔
543

544
   chash_t *h = xcalloc(sizeof(chash_t));
33,508✔
545
   store_release(&h->resizing, tab);
33,508✔
546
   store_release(&h->tab, tab);
33,508✔
547

548
   return h;
33,508✔
549
}
550

551
static void chash_wait_for_resize(chash_t *h)
25,411✔
552
{
553
   for (;;) {
×
554
      chash_tab_t *from = atomic_load(&h->tab);
25,411✔
555
      chash_tab_t *to = atomic_load(&h->resizing);
25,411✔
556

557
      if (from == to)
25,411✔
558
         break;
559

560
      thread_sleep(10);
×
561
   }
562
}
25,411✔
563

564
void chash_free(chash_t *h)
25,411✔
565
{
566
   if (h == NULL)
25,411✔
567
      return;
568

569
   chash_wait_for_resize(h);
25,411✔
570

571
   free(h->tab);
25,411✔
572
   free(h);
25,411✔
573
}
574

575
static void chash_copy_table(chash_tab_t *from, chash_tab_t *to)
21,413✔
576
{
577
   for (int i = 0; i < from->size; i++) {
2,234,597✔
578
      for (;;) {
2,213,184✔
579
         void *const tagged_key = load_acquire(&from->slots[i].tagged_key);
2,213,184✔
580
         const chash_marker_t marker = pointer_tag(tagged_key);
2,213,184✔
581
         const void *key = untag_pointer(tagged_key, void);
2,213,184✔
582
         switch (marker) {
2,213,184✔
583
         case BUSY_MARKER:
×
584
            spin_wait();
×
585
            continue;
×
586
         case INUSE_MARKER:
1,128,005✔
587
            {
588
               for (int j = hash_slot(to->size, key);;
1,128,005✔
589
                    j = (j + 1) & (to->size - 1)) {
194,144✔
590
                  void *exist = load_acquire(&(to->slots[j].tagged_key));
1,322,149✔
591
                  if (exist == NULL) {
1,322,149✔
592
                     to->slots[j].value = from->slots[i].value;
1,128,005✔
593
                     store_release(&(to->slots[j].tagged_key), tagged_key);
1,128,005✔
594
                     break;
1,128,005✔
595
                  }
596
                  else
597
                     assert(exist != tagged_key);
194,144✔
598
               }
599
            }
600
            break;
1,128,005✔
601
         case FREE_MARKER:
602
            break;
603
         default:
×
604
            should_not_reach_here();
605
         }
606

607
         void *moved = tag_pointer(key, MOVED_MARKER);
2,213,184✔
608
         if (atomic_cas(&(from->slots[i].tagged_key), tagged_key, moved))
2,213,184✔
609
            break;
610
         else
611
            assert(marker == FREE_MARKER);   // Raced to move free slot
×
612
      }
613
   }
614
}
21,413✔
615

616
static void chash_grow_table(chash_t *h, chash_tab_t *cur)
21,413✔
617
{
618
   chash_tab_t *newtab =
21,413✔
619
      xcalloc_flex(sizeof(chash_tab_t), cur->size * 2, sizeof(chash_slot_t));
21,413✔
620
   newtab->size = cur->size * 2;
21,413✔
621

622
   if (atomic_cas(&h->resizing, cur, newtab)) {
21,413✔
623
      chash_copy_table(cur, newtab);
21,413✔
624

625
      assert(atomic_load(&h->resizing) == newtab);
21,413✔
626

627
      if (!atomic_cas(&h->tab, cur, newtab))
21,413✔
628
         should_not_reach_here();
629

630
      async_free(cur);
21,413✔
631
   }
632
   else {
633
      free(newtab);
×
634
      chash_wait_for_resize(h);
×
635
   }
636
}
21,413✔
637

638
static bool chash_try_put(chash_t *h, const void *key, void *value)
1,004,771✔
639
{
640
   chash_tab_t *tab = load_acquire(&h->tab);
1,004,771✔
641
   assert(is_power_of_2(tab->size));
1,004,771✔
642

643
   if (relaxed_load(&h->members) > tab->size / 2) {
1,004,771✔
644
      chash_grow_table(h, tab);
21,413✔
645
      return false;
21,413✔
646
   }
647

648
   for (int i = hash_slot(tab->size, key);; i = (i + 1) & (tab->size - 1)) {
1,590,129✔
649
      void *const tagged_key = load_acquire(&tab->slots[i].tagged_key);
1,590,129✔
650
      const chash_marker_t marker = pointer_tag(tagged_key);
1,590,129✔
651

652
      switch (marker) {
1,590,129✔
653
      case BUSY_MARKER:
×
654
         spin_wait();
×
655
         return false;
×
656
      case FREE_MARKER:
657
         {
658
            void *busy = tag_pointer(NULL, BUSY_MARKER);
978,817✔
659
            if (atomic_cas(&tab->slots[i].tagged_key, NULL, busy)) {
978,817✔
660
               tab->slots[i].value = value;
978,817✔
661
               void *inuse = tag_pointer(key, INUSE_MARKER);
978,817✔
662
               store_release(&tab->slots[i].tagged_key, inuse);
978,817✔
663
               relaxed_add(&h->members, 1);
978,817✔
664
               return true;
978,817✔
665
            }
666
            else {
667
               spin_wait();
×
668
               return false;
×
669
            }
670
         }
671
      case INUSE_MARKER:
611,312✔
672
         if (untag_pointer(tagged_key, void) == key) {
611,312✔
673
            // Must CAS to busy here to avoid lost update due to
674
            // concurrent move
675
            void *busy = tag_pointer(key, BUSY_MARKER);
4,541✔
676
            if (atomic_cas(&tab->slots[i].tagged_key, tagged_key, busy)) {
4,541✔
677
               tab->slots[i].value = value;
4,541✔
678
               store_release(&tab->slots[i].tagged_key, tagged_key);
4,541✔
679
               return true;
4,541✔
680
            }
681
            else {
682
               spin_wait();
×
683
               return false;
×
684
            }
685
         }
686
         else
687
            break;
688
      case MOVED_MARKER:
×
689
         chash_wait_for_resize(h);
×
690
         return false;
×
691
      }
692
   }
693
}
694

695
void chash_put(chash_t *h, const void *key, void *value)
983,358✔
696
{
697
   while (!chash_try_put(h, key, value));
1,004,771✔
698
}
983,358✔
699

700
static bool chash_try_cas(chash_t *h, const void *key, void *cmp, void **value)
3✔
701
{
702
   chash_tab_t *tab = load_acquire(&h->tab);
3✔
703
   assert(is_power_of_2(tab->size));
3✔
704

705
   if (relaxed_load(&h->members) > tab->size / 2) {
3✔
706
      chash_grow_table(h, tab);
×
707
      return false;
×
708
   }
709

710
   for (int i = hash_slot(tab->size, key);; i = (i + 1) & (tab->size - 1)) {
3✔
711
      void *const tagged_key = load_acquire(&tab->slots[i].tagged_key);
3✔
712
      const chash_marker_t marker = pointer_tag(tagged_key);
3✔
713

714
      switch (marker) {
3✔
715
      case BUSY_MARKER:
×
716
         spin_wait();
×
717
         return false;
×
718
      case INUSE_MARKER:
2✔
719
         if (untag_pointer(tagged_key, void) != key)
2✔
720
            break;
721
         // Fall-through
722
      case FREE_MARKER:
723
         {
724
            void *busy = tag_pointer(key, BUSY_MARKER);
3✔
725
            if (atomic_cas(&tab->slots[i].tagged_key, tagged_key, busy)) {
3✔
726
               void *new = *value;
3✔
727
               if ((*value = tab->slots[i].value) == cmp)
3✔
728
                  tab->slots[i].value = new;
2✔
729
               void *inuse = tag_pointer(key, INUSE_MARKER);
3✔
730
               store_release(&tab->slots[i].tagged_key, inuse);
3✔
731
               return true;
3✔
732
            }
733
            else {
734
               spin_wait();
×
735
               return false;
×
736
            }
737
         }
738
      case MOVED_MARKER:
×
739
         chash_wait_for_resize(h);
×
740
         return false;
×
741
      }
742
   }
743
}
744

745
void *chash_cas(chash_t *h, const void *key, void *cmp, void *value)
3✔
746
{
747
   while (!chash_try_cas(h, key, cmp, &value));
3✔
748
   return value;
3✔
749
}
750

751
static bool chash_try_get(chash_t *h, const void *key, void **value)
1,301,271✔
752
{
753
   chash_tab_t *tab = load_acquire(&h->tab);
1,301,271✔
754
   assert(is_power_of_2(tab->size));
1,301,271✔
755

756
   for (int i = hash_slot(tab->size, key);; i = (i + 1) & (tab->size - 1)) {
2,012,089✔
757
      void *const tagged_key = load_acquire(&tab->slots[i].tagged_key);
2,012,089✔
758
      const chash_marker_t marker = pointer_tag(tagged_key);
2,012,089✔
759

760
      switch (marker) {
2,012,089✔
761
      case BUSY_MARKER:
×
762
         spin_wait();
×
763
         return false;
×
764
      case FREE_MARKER:
1,041,923✔
765
         *value = NULL;
1,041,923✔
766
         return true;
1,041,923✔
767
      case INUSE_MARKER:
970,166✔
768
         if (untag_pointer(tagged_key, void) == key) {
970,166✔
769
            *value = tab->slots[i].value;
259,348✔
770
            return true;
259,348✔
771
         }
772
         else
773
            break;
774
      case MOVED_MARKER:
×
775
         chash_wait_for_resize(h);
×
776
         return false;
×
777
      }
778
   }
779
}
780

781
void *chash_get(chash_t *h, const void *key)
1,301,271✔
782
{
783
   void *value;
1,301,271✔
784
   while (!chash_try_get(h, key, &value));
1,301,271✔
785
   return value;
1,301,271✔
786
}
787

788
void chash_iter(chash_t *h, hash_iter_fn_t fn)
12,668✔
789
{
790
   chash_tab_t *tab = load_acquire(&h->tab);
12,668✔
791
   for (int pos = 0; pos < tab->size;) {
2,618,940✔
792
      void *const tagged_key = load_acquire(&tab->slots[pos].tagged_key);
2,606,272✔
793
      const chash_marker_t marker = pointer_tag(tagged_key);
2,606,272✔
794

795
      switch (marker) {
2,606,272✔
796
      case BUSY_MARKER:
×
797
         spin_wait();
×
798
         continue;
×
799
      case FREE_MARKER:
800
         break;
801
      case MOVED_MARKER:
839,192✔
802
      case INUSE_MARKER:
803
         (*fn)(untag_pointer(tagged_key, void), tab->slots[pos].value);
839,192✔
804
         break;
839,192✔
805
      }
806

807
      pos++;
2,606,272✔
808
   }
809
}
12,668✔
810

811
////////////////////////////////////////////////////////////////////////////////
812
// Generic hash table
813

814
struct _ghash {
815
   unsigned          size;
816
   unsigned          members;
817
   ghash_hash_fn_t   hash_fn;
818
   ghash_cmp_fn_t    cmp_fn;
819
   void            **values;
820
   const void      **keys;
821
   uint32_t         *hashes;
822
};
823

824
static void ghash_alloc(ghash_t *h)
28,460✔
825
{
826
   size_t size = h->size * 2 * sizeof(void *) + h->size * sizeof(uint32_t);
28,460✔
827
   char *mem = xcalloc(size);
28,460✔
828
   h->values = (void **)mem;
28,460✔
829
   h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
28,460✔
830
   h->hashes = (uint32_t *)(mem + (2 * h->size * sizeof(void *)));
28,460✔
831
}
28,460✔
832

833
static inline uint32_t ghash_hash_fn(ghash_t *h, const void *key)
262,867✔
834
{
835
   assert(key != NULL);
262,867✔
836
   return mix_bits_32((*h->hash_fn)(key));
262,867✔
837
}
838

839
ghash_t *ghash_new(int size, ghash_hash_fn_t hash_fn, ghash_cmp_fn_t cmp_fn)
28,333✔
840
{
841
   ghash_t *h = xmalloc(sizeof(ghash_t));
28,333✔
842
   h->size    = next_power_of_2(size);
28,333✔
843
   h->members = 0;
28,333✔
844
   h->hash_fn = hash_fn;
28,333✔
845
   h->cmp_fn  = cmp_fn;
28,333✔
846

847
   ghash_alloc(h);
28,333✔
848

849
   return h;
28,333✔
850
}
851

852
void ghash_free(ghash_t *h)
8,255✔
853
{
854
   if (h != NULL) {
8,255✔
855
      free(h->values);
8,255✔
856
      free(h);
8,255✔
857
   }
858
}
8,255✔
859

860
void ghash_put(ghash_t *h, const void *key, void *value)
71,922✔
861
{
862
   if (unlikely(h->members > h->size / 2)) {
71,922✔
863
      // Rebuild the hash table with a larger size
864

865
      const int old_size = h->size;
127✔
866
      h->size *= 2;
127✔
867

868
      const void **old_keys = h->keys;
127✔
869
      void **old_values = h->values;
127✔
870
      uint32_t *old_hashes = h->hashes;
127✔
871

872
      ghash_alloc(h);
127✔
873

874
      h->members = 0;
127✔
875

876
      for (int i = 0; i < old_size; i++) {
3,315✔
877
         if (old_keys[i] != NULL && old_values[i] != NULL) {
3,188✔
878
            int slot = old_hashes[i] & (h->size - 1);
1,721✔
879

880
            for (; ; slot = (slot + 1) & (h->size - 1)) {
258✔
881
               if (h->keys[slot] == NULL) {
1,979✔
882
                  h->values[slot] = old_values[i];
1,721✔
883
                  h->keys[slot] = old_keys[i];
1,721✔
884
                  h->hashes[slot] = old_hashes[i];
1,721✔
885
                  h->members++;
1,721✔
886
                  break;
1,721✔
887
               }
888
               else
889
                  assert(h->keys[slot] != old_keys[i]);
258✔
890
            }
891
         }
892
      }
893

894
      free(old_values);
127✔
895
   }
896

897
   uint32_t hash = ghash_hash_fn(h, key);
71,922✔
898
   int slot = hash & (h->size - 1);
71,922✔
899

900
   for (; ; slot = (slot + 1) & (h->size - 1)) {
3,676✔
901
      if (h->keys[slot] == NULL) {
75,598✔
902
         h->values[slot] = value;
71,882✔
903
         h->keys[slot] = key;
71,882✔
904
         h->hashes[slot] = hash;
71,882✔
905
         h->members++;
71,882✔
906
         break;
71,882✔
907
      }
908
      else if (h->hashes[slot] != hash)
3,716✔
909
         continue;
3,620✔
910
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key)) {
96✔
911
         h->values[slot] = value;
40✔
912
         break;
40✔
913
      }
914
   }
915
}
71,922✔
916

917
void *ghash_get(ghash_t *h, const void *key)
190,944✔
918
{
919
   uint32_t hash = ghash_hash_fn(h, key);
190,944✔
920
   int slot = hash & (h->size - 1);
190,944✔
921

922
   for (; ; slot = (slot + 1) & (h->size - 1)) {
7,965✔
923
      if (h->keys[slot] == NULL)
198,909✔
924
         return NULL;
925
      else if (h->hashes[slot] != hash)
91,502✔
926
         continue;
7,612✔
927
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
83,890✔
928
         return h->values[slot];
83,537✔
929
   }
930
}
931

932
void ghash_delete(ghash_t *h, const void *key)
1✔
933
{
934
   uint32_t hash = ghash_hash_fn(h, key);
1✔
935
   int slot = hash & (h->size - 1);
1✔
936

937
   for (; ; slot = (slot + 1) & (h->size - 1)) {
×
938
      if (h->keys[slot] == NULL)
1✔
939
         return;
940
      else if (h->hashes[slot] != hash)
1✔
941
         continue;
×
942
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key)) {
1✔
943
         h->values[slot] = NULL;
1✔
944
         h->hashes[slot] = 0;
1✔
945
         return;
1✔
946
      }
947
   }
948
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc