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

nickg / nvc / 14627476359

23 Apr 2025 08:23PM UTC coverage: 92.358% (+0.02%) from 92.342%
14627476359

push

github

nickg
Fix crash with expression coverage and non-static actual in port map

Issue #1194

9 of 9 new or added lines in 2 files covered. (100.0%)

30 existing lines in 3 files now uncovered.

69248 of 74978 relevant lines covered (92.36%)

418867.66 hits per line

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

89.63
/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)
3,983,063✔
36
{
37
   assert(key != NULL);
3,983,063✔
38
   return mix_bits_64((uintptr_t)key) & (size - 1);
3,983,063✔
39
}
40

41
hash_t *hash_new(int size)
106,293✔
42
{
43
   hash_t *h = xmalloc(sizeof(hash_t));
106,293✔
44
   h->size    = next_power_of_2(size);
106,293✔
45
   h->members = 0;
106,293✔
46

47
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
106,293✔
48
   h->values = (void **)mem;
106,293✔
49
   h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
106,293✔
50

51
   return h;
106,293✔
52
}
53

54
void hash_free(hash_t *h)
292,884✔
55
{
56
   if (h != NULL) {
292,884✔
57
      free(h->values);
100,948✔
58
      free(h);
100,948✔
59
   }
60
}
292,884✔
61

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

68
      const int old_size = h->size;
755✔
69
      h->size *= 2;
755✔
70

71
      const void **old_keys = h->keys;
755✔
72
      void **old_values = h->values;
755✔
73

74
      char *mem = xcalloc(h->size * 2 * sizeof(void *));
755✔
75
      h->values = (void **)mem;
755✔
76
      h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
755✔
77

78
      h->members = 0;
755✔
79

80
      for (int i = 0; i < old_size; i++) {
763,891✔
81
         if (old_keys[i] != NULL && old_values[i] != NULL) {
763,136✔
82
            int slot = hash_slot(h->size, old_keys[i]);
382,323✔
83

84
            for (; ; slot = (slot + 1) & (h->size - 1)) {
65,752✔
85
               if (h->keys[slot] == NULL) {
448,075✔
86
                  h->values[slot] = old_values[i];
382,323✔
87
                  h->keys[slot] = old_keys[i];
382,323✔
88
                  h->members++;
382,323✔
89
                  break;
382,323✔
90
               }
91
               else
92
                  assert(h->keys[slot] != old_keys[i]);
65,752✔
93
            }
94
         }
95
      }
96

97
      free(old_values);
755✔
98
   }
99

100
   int slot = hash_slot(h->size, key);
650,365✔
101

102
   for (; ; slot = (slot + 1) & (h->size - 1)) {
223,557✔
103
      if (h->keys[slot] == key) {
873,922✔
104
         h->values[slot] = value;
54,617✔
105
         return true;
54,617✔
106
      }
107
      else if (h->keys[slot] == NULL) {
819,305✔
108
         h->values[slot] = value;
595,748✔
109
         h->keys[slot] = key;
595,748✔
110
         h->members++;
595,748✔
111
         break;
595,748✔
112
      }
113
   }
114

115
   return false;
595,748✔
116
}
117

118
void hash_delete(hash_t *h, const void *key)
1✔
119
{
120
   int slot = hash_slot(h->size, key);
1✔
121

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

132
void *hash_get(hash_t *h, const void *key)
1,995,563✔
133
{
134
   int slot = hash_slot(h->size, key);
1,995,563✔
135

136
   for (; ; slot = (slot + 1) & (h->size - 1)) {
355,669✔
137
      if (h->keys[slot] == key)
2,351,232✔
138
         return h->values[slot];
1,014,711✔
139
      else if (h->keys[slot] == NULL)
1,336,521✔
140
         return NULL;
141
   }
142
}
143

144
bool hash_iter(hash_t *h, hash_iter_t *now, const void **key, void **value)
802,002✔
145
{
146
   assert(*now != HASH_END);
802,002✔
147

148
   while (*now < h->size) {
18,659,392✔
149
      const unsigned old = (*now)++;
18,632,128✔
150
      if (h->keys[old] != NULL && h->values[old] != NULL) {
18,632,128✔
151
         *key   = h->keys[old];
774,738✔
152
         *value = h->values[old];
774,738✔
153
         return true;
774,738✔
154
      }
155
   }
156

157
   *now = HASH_END;
27,264✔
158
   return false;
27,264✔
159
}
160

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

166
////////////////////////////////////////////////////////////////////////////////
167
// Hash table of strings to pointers
168

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

176
static inline int shash_slot(shash_t *h, const char *key)
435,184✔
177
{
178
   assert(key != NULL);
435,184✔
179

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

183
   uint32_t hash = 5381;
184
   int c;
185

186
   while ((c = *key++))
10,996,001✔
187
      hash = ((hash << 5) + hash) + c;
10,560,817✔
188

189
   return mix_bits_32(hash) & (h->size - 1);
435,184✔
190
}
191

192
shash_t *shash_new(int size)
9,853✔
193
{
194
   shash_t *h = xmalloc(sizeof(shash_t));
9,853✔
195
   h->size    = next_power_of_2(size);
9,853✔
196
   h->members = 0;
9,853✔
197

198
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
9,853✔
199
   h->values = (void **)mem;
9,853✔
200
   h->keys   = (char **)(mem + (h->size * sizeof(void *)));
9,853✔
201

202
   return h;
9,853✔
203
}
204

205
void shash_free(shash_t *h)
5,309✔
206
{
207
   if (h == NULL)
5,309✔
208
      return;
209

210
   for (unsigned i = 0; i < h->size; i++) {
287,573✔
211
      if (h->keys[i] != NULL)
282,264✔
212
         free(h->keys[i]);
73,815✔
213
   }
214

215
   free(h->values);
5,309✔
216
   free(h);
5,309✔
217
}
218

219
static void shash_put_copy(shash_t *h, char *key, void *value)
130,664✔
220
{
221
   int slot = shash_slot(h, key);
130,664✔
222

223
   for (; ; slot = (slot + 1) & (h->size - 1)) {
30,600✔
224
      if (h->keys[slot] == NULL) {
161,264✔
225
         h->values[slot] = value;
130,664✔
226
         h->keys[slot] = key;
130,664✔
227
         h->members++;
130,664✔
228
         break;
130,664✔
229
      }
230
      else if (strcmp(h->keys[slot], key) == 0) {
30,600✔
231
         h->values[slot] = value;
×
232
         return;
×
233
      }
234
   }
235
}
236

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

242
      const int old_size = h->size;
1,517✔
243
      h->size *= 2;
1,517✔
244

245
      char **old_keys = h->keys;
1,517✔
246
      void **old_values = h->values;
1,517✔
247

248
      char *mem = xcalloc(h->size * 2 * sizeof(void *));
1,517✔
249
      h->values = (void **)mem;
1,517✔
250
      h->keys   = (char **)(mem + (h->size * sizeof(void *)));
1,517✔
251

252
      h->members = 0;
1,517✔
253

254
      for (int i = 0; i < old_size; i++) {
58,821✔
255
         if (old_keys[i] != NULL)
57,304✔
256
            shash_put_copy(h, old_keys[i], old_values[i]);
30,169✔
257
      }
258

259
      free(old_values);
1,517✔
260
   }
261

262
   shash_put_copy(h, xstrdup(key), value);
100,495✔
263
}
100,495✔
264

265
void *shash_get(shash_t *h, const char *key)
304,519✔
266
{
267
   int slot = shash_slot(h, key);
304,519✔
268

269
   for (; ; slot = (slot + 1) & (h->size - 1)) {
124,988✔
270
      if (h->keys[slot] == NULL)
429,507✔
271
         return NULL;
272
      else if (strcmp(h->keys[slot], key) == 0)
370,070✔
273
         return h->values[slot];
245,082✔
274
   }
275
}
276

277
void shash_delete(shash_t *h, const void *key)
1✔
278
{
279
   int slot = shash_slot(h, key);
1✔
280

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

291
bool shash_iter(shash_t *h, hash_iter_t *now, const char **key, void **value)
586✔
292
{
293
   assert(*now != HASH_END);
586✔
294

295
   while (*now < h->size) {
1,394✔
296
      const unsigned old = (*now)++;
1,312✔
297
      if (h->keys[old] != NULL && h->values[old] != NULL) {
1,312✔
298
         *key   = h->keys[old];
504✔
299
         *value = h->values[old];
504✔
300
         return true;
504✔
301
      }
302
   }
303

304
   *now = HASH_END;
82✔
305
   return false;
82✔
306
}
307

308
////////////////////////////////////////////////////////////////////////////////
309
// Hash of unsigned integers to pointers
310

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

321
static inline int ihash_slot(ihash_t *h, uint64_t key)
573,607✔
322
{
323
   key = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
573,607✔
324
   key = (key ^ (key >> 27)) * UINT64_C(0x94d049bb133111eb);
573,607✔
325
   key = key ^ (key >> 31);
573,607✔
326

327
   return key & (h->size - 1);
573,607✔
328
}
329

330
ihash_t *ihash_new(int size)
49,025✔
331
{
332
   ihash_t *h = xmalloc(sizeof(ihash_t));
49,025✔
333
   h->size    = next_power_of_2(size);
49,025✔
334
   h->members = 0;
49,025✔
335

336
   const size_t bytes =
49,025✔
337
      h->size * sizeof(void *) +
49,025✔
338
      h->size * sizeof(uint64_t) +
339
      (h->size + 63 / 64) * sizeof(uint64_t);
340

341
   char *mem = xcalloc(bytes);
49,025✔
342
   h->values = (void **)mem;
49,025✔
343
   h->keys   = (uint64_t *)(mem + (h->size * sizeof(void *)));
49,025✔
344
   h->mask   = (uint64_t *)(mem + (h->size * sizeof(void *))
49,025✔
345
                            + (h->size * sizeof(uint64_t)));
49,025✔
346

347
   return h;
49,025✔
348
}
349

350
void ihash_free(ihash_t *h)
57,153✔
351
{
352
   if (h != NULL) {
57,153✔
353
      free(h->values);
49,021✔
354
      free(h);
49,021✔
355
   }
356
}
57,153✔
357

358
void ihash_put(ihash_t *h, uint64_t key, void *value)
172,908✔
359
{
360
   if (unlikely(h->members > h->size / 2)) {
172,908✔
361
      // Rebuild the hash table with a larger size
362

363
      const int old_size = h->size;
45✔
364
      h->size *= 2;
45✔
365

366
      uint64_t *old_keys = h->keys;
45✔
367
      uint64_t *old_mask = h->mask;
45✔
368
      void **old_values = h->values;
45✔
369

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

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

381
      h->members = 0;
45✔
382

383
      for (int i = 0; i < old_size; i++) {
4,749✔
384
         if (old_mask[i / 64] & (UINT64_C(1) << (i % 64)))
4,704✔
385
            ihash_put(h, old_keys[i], old_values[i]);
2,397✔
386
      }
387

388
      free(old_values);
45✔
389
   }
390

391
   h->cachekey = key;
172,908✔
392
   h->cacheval = value;
172,908✔
393

394
   int slot = ihash_slot(h, key);
172,908✔
395

396
   for (; ; slot = (slot + 1) & (h->size - 1)) {
7,669✔
397
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)))) {
180,577✔
398
         h->values[slot] = value;
172,908✔
399
         h->keys[slot] = key;
172,908✔
400
         h->mask[slot / 64] |= (UINT64_C(1) << (slot % 64));
172,908✔
401
         h->members++;
172,908✔
402
         break;
172,908✔
403
      }
404
      else if (h->keys[slot] == key) {
7,669✔
405
         h->values[slot] = value;
×
406
         assert(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)));
×
407
         return;
408
      }
409
   }
410
}
411

412
void *ihash_get(ihash_t *h, uint64_t key)
712,174✔
413
{
414
   if (h->members > 0 && key == h->cachekey)
712,174✔
415
      return h->cacheval;
311,475✔
416

417
   h->cachekey = key;
400,699✔
418

419
   int slot = ihash_slot(h, key);
400,699✔
420

421
   for (; ; slot = (slot + 1) & (h->size - 1)) {
16,048✔
422
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64))))
416,747✔
423
         return (h->cacheval = NULL);
169,418✔
424
      else if (h->keys[slot] == key)
247,329✔
425
         return (h->cacheval = h->values[slot]);
231,281✔
426
   }
427
}
428

429
////////////////////////////////////////////////////////////////////////////////
430
// Set of pointers implemented as a hash table
431

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

438
hset_t *hset_new(int size)
15,181✔
439
{
440
   hset_t *h = xmalloc(sizeof(hset_t));
15,181✔
441
   h->size    = next_power_of_2(size);
15,181✔
442
   h->members = 0;
15,181✔
443
   h->keys    = xcalloc_array(h->size, sizeof(void *));
15,181✔
444

445
   return h;
15,181✔
446
}
447

448
void hset_free(hset_t *h)
15,181✔
449
{
450
   if (h != NULL) {
15,181✔
451
      free(h->keys);
15,181✔
452
      free(h);
15,181✔
453
   }
454
}
15,181✔
455

456
void hset_insert(hset_t *h, const void *key)
105,150✔
457
{
458
   if (unlikely(h->members > h->size / 2)) {
105,150✔
459
      const int old_size = h->size;
673✔
460
      h->size *= 2;
673✔
461

462
      const void **old_keys = h->keys;
673✔
463
      h->keys = xcalloc_array(h->size, sizeof(void *));
673✔
464

465
      h->members = 0;
673✔
466

467
      for (int i = 0; i < old_size; i++) {
70,209✔
468
         if (old_keys[i] != NULL)
69,536✔
469
            hset_insert(h, old_keys[i]);
35,441✔
470
      }
471

472
      free(old_keys);
673✔
473
   }
474

475
   int slot = hash_slot(h->size, key);
105,150✔
476

477
   for (; ; slot = (slot + 1) & (h->size - 1)) {
30,254✔
478
      if (h->keys[slot] == key)
135,404✔
479
         return;
480
      else if (h->keys[slot] == NULL) {
134,540✔
481
         h->keys[slot] = key;
104,286✔
482
         h->members++;
104,286✔
483
         break;
104,286✔
484
      }
485
   }
486
}
487

488
bool hset_contains(hset_t *h, const void *key)
122,004✔
489
{
490
   int slot = hash_slot(h->size, key);
122,004✔
491

492
   for (; ; slot = (slot + 1) & (h->size - 1)) {
41,208✔
493
      if (h->keys[slot] == key)
163,212✔
494
         return true;
495
      else if (h->keys[slot] == NULL)
108,947✔
496
         return false;
497
   }
498
}
499

500
////////////////////////////////////////////////////////////////////////////////
501
// Hash table that supports concurrent updates
502

503
typedef struct _chash_node chash_node_t;
504

505
typedef struct {
506
   void *tagged_key;
507
   void *value;
508
} chash_slot_t;
509

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

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

521
struct _chash {
522
   chash_tab_t *tab;
523
   chash_tab_t *resizing;
524
   size_t       members;
525
};
526

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

534
chash_t *chash_new(int size)
28,964✔
535
{
536
   const int roundup = next_power_of_2(size);
28,964✔
537

538
   chash_tab_t *tab =
28,964✔
539
      xcalloc_flex(sizeof(chash_tab_t), roundup, sizeof(chash_slot_t));
28,964✔
540
   tab->size = roundup;
28,964✔
541

542
   chash_t *h = xcalloc(sizeof(chash_t));
28,964✔
543
   store_release(&h->resizing, tab);
28,964✔
544
   store_release(&h->tab, tab);
28,964✔
545

546
   return h;
28,964✔
547
}
548

549
static void chash_wait_for_resize(chash_t *h)
24,083✔
550
{
UNCOV
551
   for (;;) {
×
552
      chash_tab_t *from = atomic_load(&h->tab);
24,083✔
553
      chash_tab_t *to = atomic_load(&h->resizing);
24,083✔
554

555
      if (from == to)
24,083✔
556
         break;
557

UNCOV
558
      thread_sleep(10);
×
559
   }
560
}
24,083✔
561

562
void chash_free(chash_t *h)
24,083✔
563
{
564
   if (h == NULL)
24,083✔
565
      return;
566

567
   chash_wait_for_resize(h);
24,083✔
568

569
   free(h->tab);
24,083✔
570
   free(h);
24,083✔
571
}
572

573
static void chash_copy_table(chash_tab_t *from, chash_tab_t *to)
565✔
574
{
575
   for (int i = 0; i < from->size; i++) {
81,333✔
576
      for (;;) {
80,768✔
577
         void *const tagged_key = load_acquire(&from->slots[i].tagged_key);
80,768✔
578
         const chash_marker_t marker = pointer_tag(tagged_key);
80,768✔
579
         const void *key = untag_pointer(tagged_key, void);
80,768✔
580
         switch (marker) {
80,768✔
581
         case BUSY_MARKER:
×
582
            spin_wait();
×
583
            continue;
×
584
         case INUSE_MARKER:
40,949✔
585
            {
586
               for (int j = hash_slot(to->size, key);;
40,949✔
587
                    j = (j + 1) & (to->size - 1)) {
6,666✔
588
                  void *exist = load_acquire(&(to->slots[j].tagged_key));
47,615✔
589
                  if (exist == NULL) {
47,615✔
590
                     to->slots[j].value = from->slots[i].value;
40,949✔
591
                     store_release(&(to->slots[j].tagged_key), tagged_key);
40,949✔
592
                     break;
40,949✔
593
                  }
594
                  else
595
                     assert(exist != tagged_key);
6,666✔
596
               }
597
            }
598
            break;
40,949✔
599
         case FREE_MARKER:
600
            break;
601
         default:
×
602
            should_not_reach_here();
603
         }
604

605
         void *moved = tag_pointer(key, MOVED_MARKER);
80,768✔
606
         if (atomic_cas(&(from->slots[i].tagged_key), tagged_key, moved))
80,768✔
607
            break;
608
         else
609
            assert(marker == FREE_MARKER);   // Raced to move free slot
×
610
      }
611
   }
612
}
565✔
613

614
static void chash_grow_table(chash_t *h, chash_tab_t *cur)
565✔
615
{
616
   chash_tab_t *newtab =
565✔
617
      xcalloc_flex(sizeof(chash_tab_t), cur->size * 2, sizeof(chash_slot_t));
565✔
618
   newtab->size = cur->size * 2;
565✔
619

620
   if (atomic_cas(&h->resizing, cur, newtab)) {
565✔
621
      chash_copy_table(cur, newtab);
565✔
622

623
      assert(atomic_load(&h->resizing) == newtab);
565✔
624

625
      if (!atomic_cas(&h->tab, cur, newtab))
565✔
626
         should_not_reach_here();
627

628
      async_free(cur);
565✔
629
   }
630
   else {
631
      free(newtab);
×
632
      chash_wait_for_resize(h);
×
633
   }
634
}
565✔
635

636
static bool chash_try_put(chash_t *h, const void *key, void *value)
177,981✔
637
{
638
   chash_tab_t *tab = load_acquire(&h->tab);
177,981✔
639
   assert(is_power_of_2(tab->size));
177,981✔
640

641
   if (relaxed_load(&h->members) > tab->size / 2) {
177,981✔
642
      chash_grow_table(h, tab);
565✔
643
      return false;
565✔
644
   }
645

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

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

693
void chash_put(chash_t *h, const void *key, void *value)
177,416✔
694
{
695
   while (!chash_try_put(h, key, value));
177,981✔
696
}
177,416✔
697

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

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

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

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

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

749
static bool chash_try_get(chash_t *h, const void *key, void **value)
509,289✔
750
{
751
   chash_tab_t *tab = load_acquire(&h->tab);
509,289✔
752
   assert(is_power_of_2(tab->size));
509,289✔
753

754
   for (int i = hash_slot(tab->size, key);; i = (i + 1) & (tab->size - 1)) {
581,512✔
755
      void *const tagged_key = load_acquire(&tab->slots[i].tagged_key);
581,512✔
756
      const chash_marker_t marker = pointer_tag(tagged_key);
581,512✔
757

758
      switch (marker) {
581,512✔
759
      case BUSY_MARKER:
×
760
         spin_wait();
×
761
         return false;
×
762
      case FREE_MARKER:
257,339✔
763
         *value = NULL;
257,339✔
764
         return true;
257,339✔
765
      case INUSE_MARKER:
324,173✔
766
         if (untag_pointer(tagged_key, void) == key) {
324,173✔
767
            *value = tab->slots[i].value;
251,950✔
768
            return true;
251,950✔
769
         }
770
         else
771
            break;
UNCOV
772
      case MOVED_MARKER:
×
UNCOV
773
         chash_wait_for_resize(h);
×
UNCOV
774
         return false;
×
775
      }
776
   }
777
}
778

779
void *chash_get(chash_t *h, const void *key)
509,289✔
780
{
781
   void *value;
509,289✔
782
   while (!chash_try_get(h, key, &value));
509,289✔
783
   return value;
509,289✔
784
}
785

786
void chash_iter(chash_t *h, hash_iter_fn_t fn)
15,157✔
787
{
788
   chash_tab_t *tab = load_acquire(&h->tab);
15,157✔
789
   for (int pos = 0; pos < tab->size;) {
1,970,197✔
790
      void *const tagged_key = load_acquire(&tab->slots[pos].tagged_key);
1,955,040✔
791
      const chash_marker_t marker = pointer_tag(tagged_key);
1,955,040✔
792

793
      switch (marker) {
1,955,040✔
794
      case BUSY_MARKER:
×
795
         spin_wait();
×
796
         continue;
×
797
      case FREE_MARKER:
798
         break;
799
      case MOVED_MARKER:
78,020✔
800
      case INUSE_MARKER:
801
         (*fn)(untag_pointer(tagged_key, void), tab->slots[pos].value);
78,020✔
802
         break;
78,020✔
803
      }
804

805
      pos++;
1,955,040✔
806
   }
807
}
15,157✔
808

809
////////////////////////////////////////////////////////////////////////////////
810
// Generic hash table
811

812
struct _ghash {
813
   unsigned          size;
814
   unsigned          members;
815
   ghash_hash_fn_t   hash_fn;
816
   ghash_cmp_fn_t    cmp_fn;
817
   void            **values;
818
   const void      **keys;
819
};
820

821
static inline int ghash_slot(ghash_t *h, const char *key)
192,779✔
822
{
823
   assert(key != NULL);
192,779✔
824

825
   const uint64_t hash = (*h->hash_fn)(key);
192,779✔
826
   return mix_bits_64(hash) & (h->size - 1);
192,779✔
827
}
828

829
ghash_t *ghash_new(int size, ghash_hash_fn_t hash_fn, ghash_cmp_fn_t cmp_fn)
11,467✔
830
{
831
   ghash_t *h = xmalloc(sizeof(ghash_t));
11,467✔
832
   h->size    = next_power_of_2(size);
11,467✔
833
   h->members = 0;
11,467✔
834
   h->hash_fn = hash_fn;
11,467✔
835
   h->cmp_fn  = cmp_fn;
11,467✔
836

837
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
11,467✔
838
   h->values = (void **)mem;
11,467✔
839
   h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
11,467✔
840

841
   return h;
11,467✔
842
}
843

844
void ghash_free(ghash_t *h)
969✔
845
{
846
   if (h != NULL) {
969✔
847
      free(h->values);
969✔
848
      free(h);
969✔
849
   }
850
}
969✔
851

852
void ghash_put(ghash_t *h, const void *key, void *value)
26,452✔
853
{
854
   if (unlikely(h->members > h->size / 2)) {
26,452✔
855
      // Rebuild the hash table with a larger size
856

857
      const int old_size = h->size;
1✔
858
      h->size *= 2;
1✔
859

860
      const void **old_keys = h->keys;
1✔
861
      void **old_values = h->values;
1✔
862

863
      char *mem = xcalloc(h->size * 2 * sizeof(void *));
1✔
864
      h->values = (void **)mem;
1✔
865
      h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
1✔
866

867
      h->members = 0;
1✔
868

869
      for (int i = 0; i < old_size; i++) {
5✔
870
         if (old_keys[i] != NULL)
4✔
871
            ghash_put(h, old_keys[i], old_values[i]);
3✔
872
      }
873

874
      free(old_values);
1✔
875
   }
876

877
   int slot = ghash_slot(h, key);
26,452✔
878

879
   for (; ; slot = (slot + 1) & (h->size - 1)) {
26,982✔
880
      if (h->keys[slot] == NULL) {
26,717✔
881
         h->values[slot] = value;
26,452✔
882
         h->keys[slot] = key;
26,452✔
883
         h->members++;
26,452✔
884
         break;
26,452✔
885
      }
886
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
265✔
887
         h->values[slot] = value;
25✔
888
   }
889
}
26,452✔
890

891
void *ghash_get(ghash_t *h, const void *key)
166,326✔
892
{
893
   int slot = ghash_slot(h, key);
166,326✔
894

895
   for (; ; slot = (slot + 1) & (h->size - 1)) {
1,007✔
896
      if (h->keys[slot] == NULL)
167,333✔
897
         return NULL;
898
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
129,971✔
899
         return h->values[slot];
128,964✔
900
   }
901
}
902

903
void ghash_delete(ghash_t *h, const void *key)
1✔
904
{
905
   int slot = ghash_slot(h, key);
1✔
906

907
   for (; ; slot = (slot + 1) & (h->size - 1)) {
×
908
      if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key)) {
1✔
909
         h->values[slot] = NULL;
1✔
910
         return;
1✔
911
      }
912
      else if (h->keys[slot] == NULL)
×
913
         return;
914
   }
915
}
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