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

nickg / nvc / 15093978462

18 May 2025 08:16AM UTC coverage: 92.342% (-0.006%) from 92.348%
15093978462

push

github

nickg
Avoid potential use-after-free with cover tags. Issue xxx

15 of 19 new or added lines in 2 files covered. (78.95%)

9 existing lines in 2 files now uncovered.

69306 of 75054 relevant lines covered (92.34%)

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

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

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

51
   return h;
106,334✔
52
}
53

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

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

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

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

78
      h->members = 0;
761✔
79

80
      for (int i = 0; i < old_size; i++) {
765,049✔
81
         if (old_keys[i] != NULL && old_values[i] != NULL) {
764,288✔
82
            int slot = hash_slot(h->size, old_keys[i]);
382,905✔
83

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

97
      free(old_values);
761✔
98
   }
99

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

102
   for (; ; slot = (slot + 1) & (h->size - 1)) {
221,393✔
103
      if (h->keys[slot] == key) {
872,552✔
104
         h->values[slot] = value;
54,752✔
105
         return true;
54,752✔
106
      }
107
      else if (h->keys[slot] == NULL) {
817,800✔
108
         h->values[slot] = value;
596,407✔
109
         h->keys[slot] = key;
596,407✔
110
         h->members++;
596,407✔
111
         break;
596,407✔
112
      }
113
   }
114

115
   return false;
596,407✔
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,976,086✔
133
{
134
   int slot = hash_slot(h->size, key);
1,976,086✔
135

136
   for (; ; slot = (slot + 1) & (h->size - 1)) {
325,760✔
137
      if (h->keys[slot] == key)
2,301,846✔
138
         return h->values[slot];
994,052✔
139
      else if (h->keys[slot] == NULL)
1,307,794✔
140
         return NULL;
141
   }
142
}
143

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

148
   while (*now < h->size) {
18,669,182✔
149
      const unsigned old = (*now)++;
18,641,904✔
150
      if (h->keys[old] != NULL && h->values[old] != NULL) {
18,641,904✔
151
         *key   = h->keys[old];
775,403✔
152
         *value = h->values[old];
775,403✔
153
         return true;
775,403✔
154
      }
155
   }
156

157
   *now = HASH_END;
27,278✔
158
   return false;
27,278✔
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,229✔
177
{
178
   assert(key != NULL);
435,229✔
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,997,898✔
187
      hash = ((hash << 5) + hash) + c;
10,562,669✔
188

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

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

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

202
   return h;
9,857✔
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,541✔
211
      if (h->keys[i] != NULL)
282,232✔
212
         free(h->keys[i]);
73,813✔
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,669✔
220
{
221
   int slot = shash_slot(h, key);
130,669✔
222

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

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

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

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

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

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

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

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

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

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

269
   for (; ; slot = (slot + 1) & (h->size - 1)) {
125,083✔
270
      if (h->keys[slot] == NULL)
429,642✔
271
         return NULL;
272
      else if (strcmp(h->keys[slot], key) == 0)
370,183✔
273
         return h->values[slot];
245,100✔
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)
579,590✔
322
{
323
   key = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
579,590✔
324
   key = (key ^ (key >> 27)) * UINT64_C(0x94d049bb133111eb);
579,590✔
325
   key = key ^ (key >> 31);
579,590✔
326

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

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

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

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

347
   return h;
49,769✔
348
}
349

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

358
void ihash_put(ihash_t *h, uint64_t key, void *value)
175,240✔
359
{
360
   if (unlikely(h->members > h->size / 2)) {
175,240✔
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;
175,240✔
392
   h->cacheval = value;
175,240✔
393

394
   int slot = ihash_slot(h, key);
175,240✔
395

396
   for (; ; slot = (slot + 1) & (h->size - 1)) {
7,555✔
397
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)))) {
182,795✔
398
         h->values[slot] = value;
175,240✔
399
         h->keys[slot] = key;
175,240✔
400
         h->mask[slot / 64] |= (UINT64_C(1) << (slot % 64));
175,240✔
401
         h->members++;
175,240✔
402
         break;
175,240✔
403
      }
404
      else if (h->keys[slot] == key) {
7,555✔
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)
717,411✔
413
{
414
   if (h->members > 0 && key == h->cachekey)
717,411✔
415
      return h->cacheval;
313,061✔
416

417
   h->cachekey = key;
404,350✔
418

419
   int slot = ihash_slot(h, key);
404,350✔
420

421
   for (; ; slot = (slot + 1) & (h->size - 1)) {
15,919✔
422
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64))))
420,269✔
423
         return (h->cacheval = NULL);
171,750✔
424
      else if (h->keys[slot] == key)
248,519✔
425
         return (h->cacheval = h->values[slot]);
232,600✔
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,189✔
439
{
440
   hset_t *h = xmalloc(sizeof(hset_t));
15,189✔
441
   h->size    = next_power_of_2(size);
15,189✔
442
   h->members = 0;
15,189✔
443
   h->keys    = xcalloc_array(h->size, sizeof(void *));
15,189✔
444

445
   return h;
15,189✔
446
}
447

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

456
void hset_insert(hset_t *h, const void *key)
105,259✔
457
{
458
   if (unlikely(h->members > h->size / 2)) {
105,259✔
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,259✔
476

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

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

492
   for (; ; slot = (slot + 1) & (h->size - 1)) {
41,633✔
493
      if (h->keys[slot] == key)
163,830✔
494
         return true;
495
      else if (h->keys[slot] == NULL)
109,493✔
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)
27,857✔
535
{
536
   const int roundup = next_power_of_2(size);
27,857✔
537

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

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

546
   return h;
27,857✔
547
}
548

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

555
      if (from == to)
22,969✔
556
         break;
557

UNCOV
558
      thread_sleep(10);
×
559
   }
560
}
22,969✔
561

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

567
   chash_wait_for_resize(h);
22,969✔
568

569
   free(h->tab);
22,969✔
570
   free(h);
22,969✔
571
}
572

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

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

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

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

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

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

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

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

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

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

650
      switch (marker) {
207,280✔
651
      case BUSY_MARKER:
×
652
         spin_wait();
×
653
         return false;
×
654
      case FREE_MARKER:
655
         {
656
            void *busy = tag_pointer(NULL, BUSY_MARKER);
178,060✔
657
            if (atomic_cas(&tab->slots[i].tagged_key, NULL, busy)) {
178,060✔
658
               tab->slots[i].value = value;
178,060✔
659
               void *inuse = tag_pointer(key, INUSE_MARKER);
178,060✔
660
               store_release(&tab->slots[i].tagged_key, inuse);
178,060✔
661
               relaxed_add(&h->members, 1);
178,060✔
662
               return true;
178,060✔
663
            }
664
            else {
665
               spin_wait();
×
666
               return false;
×
667
            }
668
         }
669
      case INUSE_MARKER:
29,220✔
670
         if (untag_pointer(tagged_key, void) == key) {
29,220✔
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)
178,245✔
694
{
695
   while (!chash_try_put(h, key, value));
178,827✔
696
}
178,245✔
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)
521,387✔
750
{
751
   chash_tab_t *tab = load_acquire(&h->tab);
521,387✔
752
   assert(is_power_of_2(tab->size));
521,387✔
753

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

758
      switch (marker) {
599,831✔
759
      case BUSY_MARKER:
×
760
         spin_wait();
×
761
         return false;
×
762
      case FREE_MARKER:
237,151✔
763
         *value = NULL;
237,151✔
764
         return true;
237,151✔
765
      case INUSE_MARKER:
362,680✔
766
         if (untag_pointer(tagged_key, void) == key) {
362,680✔
767
            *value = tab->slots[i].value;
284,236✔
768
            return true;
284,236✔
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)
521,387✔
780
{
781
   void *value;
521,387✔
782
   while (!chash_try_get(h, key, &value));
521,387✔
783
   return value;
521,387✔
784
}
785

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

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

805
      pos++;
1,920,160✔
806
   }
807
}
14,040✔
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,875✔
822
{
823
   assert(key != NULL);
192,875✔
824

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

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

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

841
   return h;
11,475✔
842
}
843

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

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

879
   for (; ; slot = (slot + 1) & (h->size - 1)) {
26,999✔
880
      if (h->keys[slot] == NULL) {
26,734✔
881
         h->values[slot] = value;
26,469✔
882
         h->keys[slot] = key;
26,469✔
883
         h->members++;
26,469✔
884
         break;
26,469✔
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,469✔
890

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

895
   for (; ; slot = (slot + 1) & (h->size - 1)) {
1,007✔
896
      if (h->keys[slot] == NULL)
167,412✔
897
         return NULL;
898
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
130,026✔
899
         return h->values[slot];
129,019✔
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