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

nickg / nvc / 15180719318

22 May 2025 07:28AM UTC coverage: 92.297% (-0.01%) from 92.31%
15180719318

push

github

nickg
Generate 'image helpers in MIR directly

351 of 379 new or added lines in 6 files covered. (92.61%)

15 existing lines in 4 files now uncovered.

69412 of 75205 relevant lines covered (92.3%)

520015.75 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)
4,906,743✔
36
{
37
   assert(key != NULL);
4,906,743✔
38
   return mix_bits_64((uintptr_t)key) & (size - 1);
4,906,743✔
39
}
40

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

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

51
   return h;
136,942✔
52
}
53

54
void hash_free(hash_t *h)
368,009✔
55
{
56
   if (h != NULL) {
368,009✔
57
      free(h->values);
130,133✔
58
      free(h);
130,133✔
59
   }
60
}
368,009✔
61

62
bool hash_put(hash_t *h, const void *key, void *value)
799,019✔
63
{
64
   if (unlikely(h->members > h->size / 2)) {
799,019✔
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;
867✔
69
      h->size *= 2;
867✔
70

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

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

78
      h->members = 0;
867✔
79

80
      for (int i = 0; i < old_size; i++) {
873,859✔
81
         if (old_keys[i] != NULL && old_values[i] != NULL) {
872,992✔
82
            int slot = hash_slot(h->size, old_keys[i]);
437,363✔
83

84
            for (; ; slot = (slot + 1) & (h->size - 1)) {
76,041✔
85
               if (h->keys[slot] == NULL) {
513,404✔
86
                  h->values[slot] = old_values[i];
437,363✔
87
                  h->keys[slot] = old_keys[i];
437,363✔
88
                  h->members++;
437,363✔
89
                  break;
437,363✔
90
               }
91
               else
92
                  assert(h->keys[slot] != old_keys[i]);
76,041✔
93
            }
94
         }
95
      }
96

97
      free(old_values);
867✔
98
   }
99

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

102
   for (; ; slot = (slot + 1) & (h->size - 1)) {
253,442✔
103
      if (h->keys[slot] == key) {
1,052,461✔
104
         h->values[slot] = value;
68,037✔
105
         return true;
68,037✔
106
      }
107
      else if (h->keys[slot] == NULL) {
984,424✔
108
         h->values[slot] = value;
730,982✔
109
         h->keys[slot] = key;
730,982✔
110
         h->members++;
730,982✔
111
         break;
730,982✔
112
      }
113
   }
114

115
   return false;
730,982✔
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)
2,456,430✔
133
{
134
   int slot = hash_slot(h->size, key);
2,456,430✔
135

136
   for (; ; slot = (slot + 1) & (h->size - 1)) {
413,578✔
137
      if (h->keys[slot] == key)
2,870,008✔
138
         return h->values[slot];
1,216,516✔
139
      else if (h->keys[slot] == NULL)
1,653,492✔
140
         return NULL;
141
   }
142
}
143

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

148
   while (*now < h->size) {
24,615,770✔
149
      const unsigned old = (*now)++;
24,579,632✔
150
      if (h->keys[old] != NULL && h->values[old] != NULL) {
24,579,632✔
151
         *key   = h->keys[old];
948,348✔
152
         *value = h->values[old];
948,348✔
153
         return true;
948,348✔
154
      }
155
   }
156

157
   *now = HASH_END;
36,138✔
158
   return false;
36,138✔
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)
486,948✔
177
{
178
   assert(key != NULL);
486,948✔
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++))
13,905,793✔
187
      hash = ((hash << 5) + hash) + c;
13,418,845✔
188

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

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

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

202
   return h;
12,513✔
203
}
204

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

210
   for (unsigned i = 0; i < h->size; i++) {
365,322✔
211
      if (h->keys[i] != NULL)
358,568✔
212
         free(h->keys[i]);
92,243✔
213
   }
214

215
   free(h->values);
6,754✔
216
   free(h);
6,754✔
217
}
218

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

223
   for (; ; slot = (slot + 1) & (h->size - 1)) {
35,947✔
224
      if (h->keys[slot] == NULL) {
195,149✔
225
         h->values[slot] = value;
159,202✔
226
         h->keys[slot] = key;
159,202✔
227
         h->members++;
159,202✔
228
         break;
159,202✔
229
      }
230
      else if (strcmp(h->keys[slot], key) == 0) {
35,947✔
231
         h->values[slot] = value;
×
232
         return;
×
233
      }
234
   }
235
}
236

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

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

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

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

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

254
      for (int i = 0; i < old_size; i++) {
64,727✔
255
         if (old_keys[i] != NULL)
63,080✔
256
            shash_put_copy(h, old_keys[i], old_values[i]);
33,187✔
257
      }
258

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

262
   shash_put_copy(h, xstrdup(key), value);
126,015✔
263
}
126,015✔
264

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

269
   for (; ; slot = (slot + 1) & (h->size - 1)) {
127,865✔
270
      if (h->keys[slot] == NULL)
455,610✔
271
         return NULL;
272
      else if (strcmp(h->keys[slot], key) == 0)
384,664✔
273
         return h->values[slot];
256,799✔
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)
765✔
292
{
293
   assert(*now != HASH_END);
765✔
294

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

304
   *now = HASH_END;
107✔
305
   return false;
107✔
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)
704,190✔
322
{
323
   key = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
704,190✔
324
   key = (key ^ (key >> 27)) * UINT64_C(0x94d049bb133111eb);
704,190✔
325
   key = key ^ (key >> 31);
704,190✔
326

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

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

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

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

347
   return h;
62,526✔
348
}
349

350
void ihash_free(ihash_t *h)
71,082✔
351
{
352
   if (h != NULL) {
71,082✔
353
      free(h->values);
62,517✔
354
      free(h);
62,517✔
355
   }
356
}
71,082✔
357

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

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

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

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

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

381
      h->members = 0;
58✔
382

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

388
      free(old_values);
58✔
389
   }
390

391
   h->cachekey = key;
216,017✔
392
   h->cacheval = value;
216,017✔
393

394
   int slot = ihash_slot(h, key);
216,017✔
395

396
   for (; ; slot = (slot + 1) & (h->size - 1)) {
9,330✔
397
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)))) {
225,347✔
398
         h->values[slot] = value;
216,017✔
399
         h->keys[slot] = key;
216,017✔
400
         h->mask[slot / 64] |= (UINT64_C(1) << (slot % 64));
216,017✔
401
         h->members++;
216,017✔
402
         break;
216,017✔
403
      }
404
      else if (h->keys[slot] == key) {
9,330✔
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)
853,415✔
413
{
414
   if (h->members > 0 && key == h->cachekey)
853,415✔
415
      return h->cacheval;
365,242✔
416

417
   h->cachekey = key;
488,173✔
418

419
   int slot = ihash_slot(h, key);
488,173✔
420

421
   for (; ; slot = (slot + 1) & (h->size - 1)) {
19,706✔
422
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64))))
507,879✔
423
         return (h->cacheval = NULL);
212,066✔
424
      else if (h->keys[slot] == key)
295,813✔
425
         return (h->cacheval = h->values[slot]);
276,107✔
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)
20,012✔
439
{
440
   hset_t *h = xmalloc(sizeof(hset_t));
20,012✔
441
   h->size    = next_power_of_2(size);
20,012✔
442
   h->members = 0;
20,012✔
443
   h->keys    = xcalloc_array(h->size, sizeof(void *));
20,012✔
444

445
   return h;
20,012✔
446
}
447

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

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

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

465
      h->members = 0;
806✔
466

467
      for (int i = 0; i < old_size; i++) {
78,812✔
468
         if (old_keys[i] != NULL)
78,006✔
469
            hset_insert(h, old_keys[i]);
39,809✔
470
      }
471

472
      free(old_keys);
806✔
473
   }
474

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

477
   for (; ; slot = (slot + 1) & (h->size - 1)) {
35,762✔
478
      if (h->keys[slot] == key)
161,783✔
479
         return;
480
      else if (h->keys[slot] == NULL) {
160,654✔
481
         h->keys[slot] = key;
124,892✔
482
         h->members++;
124,892✔
483
         break;
124,892✔
484
      }
485
   }
486
}
487

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

492
   for (; ; slot = (slot + 1) & (h->size - 1)) {
48,487✔
493
      if (h->keys[slot] == key)
198,609✔
494
         return true;
495
      else if (h->keys[slot] == NULL)
132,794✔
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)
35,398✔
535
{
536
   const int roundup = next_power_of_2(size);
35,398✔
537

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

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

546
   return h;
35,398✔
547
}
548

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

555
      if (from == to)
29,178✔
556
         break;
557

UNCOV
558
      thread_sleep(10);
×
559
   }
560
}
29,178✔
561

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

567
   chash_wait_for_resize(h);
29,178✔
568

569
   free(h->tab);
29,178✔
570
   free(h);
29,178✔
571
}
572

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

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

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

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

623
      assert(atomic_load(&h->resizing) == newtab);
1,202✔
624

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

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

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

641
   if (relaxed_load(&h->members) > tab->size / 2) {
229,802✔
642
      chash_grow_table(h, tab);
1,202✔
643
      return false;
1,202✔
644
   }
645

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

650
      switch (marker) {
266,935✔
651
      case BUSY_MARKER:
×
652
         spin_wait();
×
653
         return false;
×
654
      case FREE_MARKER:
655
         {
656
            void *busy = tag_pointer(NULL, BUSY_MARKER);
228,164✔
657
            if (atomic_cas(&tab->slots[i].tagged_key, NULL, busy)) {
228,164✔
658
               tab->slots[i].value = value;
228,164✔
659
               void *inuse = tag_pointer(key, INUSE_MARKER);
228,164✔
660
               store_release(&tab->slots[i].tagged_key, inuse);
228,164✔
661
               relaxed_add(&h->members, 1);
228,164✔
662
               return true;
228,164✔
663
            }
664
            else {
665
               spin_wait();
×
666
               return false;
×
667
            }
668
         }
669
      case INUSE_MARKER:
38,771✔
670
         if (untag_pointer(tagged_key, void) == key) {
38,771✔
671
            // Must CAS to busy here to avoid lost update due to
672
            // concurrent move
673
            void *busy = tag_pointer(key, BUSY_MARKER);
436✔
674
            if (atomic_cas(&tab->slots[i].tagged_key, tagged_key, busy)) {
436✔
675
               tab->slots[i].value = value;
436✔
676
               store_release(&tab->slots[i].tagged_key, tagged_key);
436✔
677
               return true;
436✔
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)
228,600✔
694
{
695
   while (!chash_try_put(h, key, value));
229,802✔
696
}
228,600✔
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)
649,822✔
750
{
751
   chash_tab_t *tab = load_acquire(&h->tab);
649,822✔
752
   assert(is_power_of_2(tab->size));
649,822✔
753

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

758
      switch (marker) {
744,760✔
759
      case BUSY_MARKER:
×
760
         spin_wait();
×
761
         return false;
×
762
      case FREE_MARKER:
304,017✔
763
         *value = NULL;
304,017✔
764
         return true;
304,017✔
765
      case INUSE_MARKER:
440,743✔
766
         if (untag_pointer(tagged_key, void) == key) {
440,743✔
767
            *value = tab->slots[i].value;
345,805✔
768
            return true;
345,805✔
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)
649,822✔
780
{
781
   void *value;
649,822✔
782
   while (!chash_try_get(h, key, &value));
649,822✔
783
   return value;
649,822✔
784
}
785

786
void chash_iter(chash_t *h, hash_iter_fn_t fn)
17,847✔
787
{
788
   chash_tab_t *tab = load_acquire(&h->tab);
17,847✔
789
   for (int pos = 0; pos < tab->size;) {
2,404,311✔
790
      void *const tagged_key = load_acquire(&tab->slots[pos].tagged_key);
2,386,464✔
791
      const chash_marker_t marker = pointer_tag(tagged_key);
2,386,464✔
792

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

805
      pos++;
2,386,464✔
806
   }
807
}
17,847✔
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)
252,783✔
822
{
823
   assert(key != NULL);
252,783✔
824

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

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

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

841
   return h;
14,648✔
842
}
843

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

852
void ghash_put(ghash_t *h, const void *key, void *value)
34,023✔
853
{
854
   if (unlikely(h->members > h->size / 2)) {
34,023✔
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);
34,023✔
878

879
   for (; ; slot = (slot + 1) & (h->size - 1)) {
34,691✔
880
      if (h->keys[slot] == NULL) {
34,357✔
881
         h->values[slot] = value;
34,023✔
882
         h->keys[slot] = key;
34,023✔
883
         h->members++;
34,023✔
884
         break;
34,023✔
885
      }
886
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
334✔
887
         h->values[slot] = value;
31✔
888
   }
889
}
34,023✔
890

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

895
   for (; ; slot = (slot + 1) & (h->size - 1)) {
1,303✔
896
      if (h->keys[slot] == NULL)
220,062✔
897
         return NULL;
898
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
172,047✔
899
         return h->values[slot];
170,744✔
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