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

nickg / nvc / 25290162267

03 May 2026 08:36PM UTC coverage: 92.198% (-0.002%) from 92.2%
25290162267

Pull #1527

github

web-flow
Merge 42ea04698 into e6aaf3c12
Pull Request #1527: handle non-const bounds on array types in lower_generic_ref.

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

2 existing lines in 1 file now uncovered.

77294 of 83835 relevant lines covered (92.2%)

607228.91 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
#define HASH_MIN_SIZE 4
26

27
////////////////////////////////////////////////////////////////////////////////
28
// Hash table of pointers to pointers
29

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

37
static inline int hash_slot(unsigned size, const void *key)
10,332,121✔
38
{
39
   assert(key != NULL);
10,332,121✔
40
   return mix_bits_64((uintptr_t)key) & (size - 1);
10,332,121✔
41
}
42

43
hash_t *hash_new(int size)
177,054✔
44
{
45
   assert(size > 0);
177,054✔
46

47
   hash_t *h = xmalloc(sizeof(hash_t));
177,054✔
48
   h->size    = MAX(HASH_MIN_SIZE, next_power_of_2(size));
177,054✔
49
   h->members = 0;
177,054✔
50

51
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
177,054✔
52
   h->values = (void **)mem;
177,054✔
53
   h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
177,054✔
54

55
   return h;
177,054✔
56
}
57

58
void hash_free(hash_t *h)
492,748✔
59
{
60
   if (h != NULL) {
492,748✔
61
      free(h->values);
166,613✔
62
      free(h);
166,613✔
63
   }
64
}
492,748✔
65

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

72
      const int old_size = h->size;
9,873✔
73
      h->size *= 2;
9,873✔
74

75
      const void **old_keys = h->keys;
9,873✔
76
      void **old_values = h->values;
9,873✔
77

78
      char *mem = xcalloc(h->size * 2 * sizeof(void *));
9,873✔
79
      h->values = (void **)mem;
9,873✔
80
      h->keys   = (const void **)(mem + (h->size * sizeof(void *)));
9,873✔
81

82
      h->members = 0;
9,873✔
83

84
      for (int i = 0; i < old_size; i++) {
2,638,537✔
85
         if (old_keys[i] != NULL && old_values[i] != NULL) {
2,628,664✔
86
            int slot = hash_slot(h->size, old_keys[i]);
1,324,205✔
87

88
            for (; ; slot = (slot + 1) & (h->size - 1)) {
226,426✔
89
               if (h->keys[slot] == NULL) {
1,550,631✔
90
                  h->values[slot] = old_values[i];
1,324,205✔
91
                  h->keys[slot] = old_keys[i];
1,324,205✔
92
                  h->members++;
1,324,205✔
93
                  break;
1,324,205✔
94
               }
95
               else
96
                  assert(h->keys[slot] != old_keys[i]);
226,426✔
97
            }
98
         }
99
      }
100

101
      free(old_values);
9,873✔
102
   }
103

104
   int slot = hash_slot(h->size, key);
1,905,177✔
105

106
   for (; ; slot = (slot + 1) & (h->size - 1)) {
866,379✔
107
      if (h->keys[slot] == key) {
2,771,556✔
108
         h->values[slot] = value;
117,258✔
109
         return true;
117,258✔
110
      }
111
      else if (h->keys[slot] == NULL) {
2,654,298✔
112
         h->values[slot] = value;
1,787,919✔
113
         h->keys[slot] = key;
1,787,919✔
114
         h->members++;
1,787,919✔
115
         break;
1,787,919✔
116
      }
117
   }
118

119
   return false;
1,787,919✔
120
}
121

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

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

136
void *hash_get(hash_t *h, const void *key)
3,399,609✔
137
{
138
   int slot = hash_slot(h->size, key);
3,399,609✔
139

140
   for (; ; slot = (slot + 1) & (h->size - 1)) {
997,444✔
141
      if (h->keys[slot] == key)
4,397,053✔
142
         return h->values[slot];
1,360,778✔
143
      else if (h->keys[slot] == NULL)
3,036,275✔
144
         return NULL;
145
   }
146
}
147

148
bool hash_iter(hash_t *h, hash_iter_t *now, const void **key, void **value)
1,305,197✔
149
{
150
   assert(*now != HASH_END);
1,305,197✔
151

152
   while (*now < h->size) {
21,880,513✔
153
      const unsigned old = (*now)++;
21,844,160✔
154
      if (h->keys[old] != NULL && h->values[old] != NULL) {
21,844,160✔
155
         *key   = h->keys[old];
1,268,844✔
156
         *value = h->values[old];
1,268,844✔
157
         return true;
1,268,844✔
158
      }
159
   }
160

161
   *now = HASH_END;
36,353✔
162
   return false;
36,353✔
163
}
164

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

170
////////////////////////////////////////////////////////////////////////////////
171
// Hash table of strings to pointers
172

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

180
static inline int shash_slot(shash_t *h, const char *key)
256,564✔
181
{
182
   assert(key != NULL);
256,564✔
183

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

187
   uint32_t hash = 5381;
188
   int c;
189

190
   while ((c = *key++))
3,542,437✔
191
      hash = ((hash << 5) + hash) + c;
3,285,873✔
192

193
   return mix_bits_32(hash) & (h->size - 1);
256,564✔
194
}
195

196
shash_t *shash_new(int size)
12,323✔
197
{
198
   shash_t *h = xmalloc(sizeof(shash_t));
12,323✔
199
   h->size    = MAX(HASH_MIN_SIZE, next_power_of_2(size));
12,323✔
200
   h->members = 0;
12,323✔
201

202
   char *mem = xcalloc(h->size * 2 * sizeof(void *));
12,323✔
203
   h->values = (void **)mem;
12,323✔
204
   h->keys   = (char **)(mem + (h->size * sizeof(void *)));
12,323✔
205

206
   return h;
12,323✔
207
}
208

209
void shash_free(shash_t *h)
5,864✔
210
{
211
   if (h == NULL)
5,864✔
212
      return;
213

214
   for (unsigned i = 0; i < h->size; i++) {
382,512✔
215
      if (h->keys[i] != NULL)
376,648✔
216
         free(h->keys[i]);
101,128✔
217
   }
218

219
   free(h->values);
5,864✔
220
   free(h);
5,864✔
221
}
222

223
static void shash_put_copy(shash_t *h, char *key, void *value)
149,236✔
224
{
225
   int slot = shash_slot(h, key);
149,236✔
226

227
   for (; ; slot = (slot + 1) & (h->size - 1)) {
30,039✔
228
      if (h->keys[slot] == NULL) {
179,275✔
229
         h->values[slot] = value;
149,236✔
230
         h->keys[slot] = key;
149,236✔
231
         h->members++;
149,236✔
232
         break;
149,236✔
233
      }
234
      else if (strcmp(h->keys[slot], key) == 0) {
30,039✔
235
         h->values[slot] = value;
×
236
         return;
×
237
      }
238
   }
239
}
240

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

246
      const int old_size = h->size;
20✔
247
      h->size *= 2;
20✔
248

249
      char **old_keys = h->keys;
20✔
250
      void **old_values = h->values;
20✔
251

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

256
      h->members = 0;
20✔
257

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

263
      free(old_values);
20✔
264
   }
265

266
   shash_put_copy(h, xstrdup(key), value);
140,004✔
267
}
140,004✔
268

269
void *shash_get(shash_t *h, const char *key)
107,328✔
270
{
271
   int slot = shash_slot(h, key);
107,328✔
272

273
   for (; ; slot = (slot + 1) & (h->size - 1)) {
20,512✔
274
      if (h->keys[slot] == NULL)
127,840✔
275
         return NULL;
276
      else if (strcmp(h->keys[slot], key) == 0)
76,910✔
277
         return h->values[slot];
56,398✔
278
   }
279
}
280

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

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

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

299
   while (*now < h->size) {
22,119✔
300
      const unsigned old = (*now)++;
21,184✔
301
      if (h->keys[old] != NULL && h->values[old] != NULL) {
21,184✔
302
         *key   = h->keys[old];
3,689✔
303
         *value = h->values[old];
3,689✔
304
         return true;
3,689✔
305
      }
306
   }
307

308
   *now = HASH_END;
935✔
309
   return false;
935✔
310
}
311

312
////////////////////////////////////////////////////////////////////////////////
313
// Hash of unsigned integers to pointers
314

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

325
static inline int ihash_slot(ihash_t *h, uint64_t key)
984,481✔
326
{
327
   key = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
984,481✔
328
   key = (key ^ (key >> 27)) * UINT64_C(0x94d049bb133111eb);
984,481✔
329
   key = key ^ (key >> 31);
984,481✔
330

331
   return key & (h->size - 1);
984,481✔
332
}
333

334
ihash_t *ihash_new(int size)
83,874✔
335
{
336
   ihash_t *h = xmalloc(sizeof(ihash_t));
83,874✔
337
   h->size    = MAX(HASH_MIN_SIZE, next_power_of_2(size));
83,874✔
338
   h->members = 0;
83,874✔
339

340
   const size_t bytes =
83,874✔
341
      h->size * sizeof(void *) +
83,874✔
342
      h->size * sizeof(uint64_t) +
343
      (h->size + 63 / 64) * sizeof(uint64_t);
344

345
   char *mem = xcalloc(bytes);
83,874✔
346
   h->values = (void **)mem;
83,874✔
347
   h->keys   = (uint64_t *)(mem + (h->size * sizeof(void *)));
83,874✔
348
   h->mask   = (uint64_t *)(mem + (h->size * sizeof(void *))
83,874✔
349
                            + (h->size * sizeof(uint64_t)));
83,874✔
350

351
   return h;
83,874✔
352
}
353

354
void ihash_free(ihash_t *h)
98,820✔
355
{
356
   if (h != NULL) {
98,820✔
357
      free(h->values);
83,861✔
358
      free(h);
83,861✔
359
   }
360
}
98,820✔
361

362
void ihash_put(ihash_t *h, uint64_t key, void *value)
295,754✔
363
{
364
   if (unlikely(h->members > h->size / 2)) {
295,754✔
365
      // Rebuild the hash table with a larger size
366

367
      const int old_size = h->size;
70✔
368
      h->size *= 2;
70✔
369

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

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

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

385
      h->members = 0;
70✔
386

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

392
      free(old_values);
70✔
393
   }
394

395
   h->cachekey = key;
295,754✔
396
   h->cacheval = value;
295,754✔
397

398
   int slot = ihash_slot(h, key);
295,754✔
399

400
   for (; ; slot = (slot + 1) & (h->size - 1)) {
14,974✔
401
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)))) {
310,728✔
402
         h->values[slot] = value;
295,754✔
403
         h->keys[slot] = key;
295,754✔
404
         h->mask[slot / 64] |= (UINT64_C(1) << (slot % 64));
295,754✔
405
         h->members++;
295,754✔
406
         break;
295,754✔
407
      }
408
      else if (h->keys[slot] == key) {
14,974✔
409
         h->values[slot] = value;
×
410
         assert(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)));
×
411
         return;
412
      }
413
   }
414
}
415

416
void *ihash_get(ihash_t *h, uint64_t key)
1,238,225✔
417
{
418
   if (h->members > 0 && key == h->cachekey)
1,238,225✔
419
      return h->cacheval;
549,498✔
420

421
   h->cachekey = key;
688,727✔
422

423
   int slot = ihash_slot(h, key);
688,727✔
424

425
   for (; ; slot = (slot + 1) & (h->size - 1)) {
28,685✔
426
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64))))
717,412✔
427
         return (h->cacheval = NULL);
290,482✔
428
      else if (h->keys[slot] == key)
426,930✔
429
         return (h->cacheval = h->values[slot]);
398,245✔
430
   }
431
}
432

433
////////////////////////////////////////////////////////////////////////////////
434
// Set of pointers implemented as a hash table
435

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

442
hset_t *hset_new(int size)
24,313✔
443
{
444
   hset_t *h = xmalloc(sizeof(hset_t));
24,313✔
445
   h->size    = MAX(HASH_MIN_SIZE, next_power_of_2(size));
24,313✔
446
   h->members = 0;
24,313✔
447
   h->keys    = xcalloc_array(h->size, sizeof(void *));
24,313✔
448

449
   return h;
24,313✔
450
}
451

452
void hset_free(hset_t *h)
24,313✔
453
{
454
   if (h != NULL) {
24,313✔
455
      free(h->keys);
24,313✔
456
      free(h);
24,313✔
457
   }
458
}
24,313✔
459

460
void hset_insert(hset_t *h, const void *key)
48,196✔
461
{
462
   if (unlikely(h->members > h->size / 2)) {
48,196✔
463
      const int old_size = h->size;
527✔
464
      h->size *= 2;
527✔
465

466
      const void **old_keys = h->keys;
527✔
467
      h->keys = xcalloc_array(h->size, sizeof(void *));
527✔
468

469
      h->members = 0;
527✔
470

471
      for (int i = 0; i < old_size; i++) {
13,155✔
472
         if (old_keys[i] != NULL)
12,628✔
473
            hset_insert(h, old_keys[i]);
6,841✔
474
      }
475

476
      free(old_keys);
527✔
477
   }
478

479
   int slot = hash_slot(h->size, key);
48,196✔
480

481
   for (; ; slot = (slot + 1) & (h->size - 1)) {
8,231✔
482
      if (h->keys[slot] == key)
56,427✔
483
         return;
484
      else if (h->keys[slot] == NULL) {
56,402✔
485
         h->keys[slot] = key;
48,171✔
486
         h->members++;
48,171✔
487
         break;
48,171✔
488
      }
489
   }
490
}
491

492
bool hset_contains(hset_t *h, const void *key)
66,707✔
493
{
494
   int slot = hash_slot(h->size, key);
66,707✔
495

496
   for (; ; slot = (slot + 1) & (h->size - 1)) {
14,463✔
497
      if (h->keys[slot] == key)
81,170✔
498
         return true;
499
      else if (h->keys[slot] == NULL)
56,088✔
500
         return false;
501
   }
502
}
503

504
////////////////////////////////////////////////////////////////////////////////
505
// Hash table that supports concurrent updates
506

507
typedef struct _chash_node chash_node_t;
508

509
typedef struct {
510
   void *tagged_key;
511
   void *value;
512
} chash_slot_t;
513

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

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

525
struct _chash {
526
   chash_tab_t *tab;
527
   chash_tab_t *resizing;
528
   size_t       members;
529
};
530

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

538
chash_t *chash_new(int size)
34,081✔
539
{
540
   const int roundup = next_power_of_2(size);
34,081✔
541

542
   chash_tab_t *tab =
34,081✔
543
      xcalloc_flex(sizeof(chash_tab_t), roundup, sizeof(chash_slot_t));
34,081✔
544
   tab->size = roundup;
34,081✔
545

546
   chash_t *h = xcalloc(sizeof(chash_t));
34,081✔
547
   store_release(&h->resizing, tab);
34,081✔
548
   store_release(&h->tab, tab);
34,081✔
549

550
   return h;
34,081✔
551
}
552

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

559
      if (from == to)
25,850✔
560
         break;
561

562
      thread_sleep(10);
×
563
   }
564
}
25,850✔
565

566
void chash_free(chash_t *h)
25,850✔
567
{
568
   if (h == NULL)
25,850✔
569
      return;
570

571
   chash_wait_for_resize(h);
25,850✔
572

573
   free(h->tab);
25,850✔
574
   free(h);
25,850✔
575
}
576

577
static void chash_copy_table(chash_tab_t *from, chash_tab_t *to)
22,050✔
578
{
579
   for (int i = 0; i < from->size; i++) {
2,359,106✔
580
      for (;;) {
2,337,056✔
581
         void *const tagged_key = load_acquire(&from->slots[i].tagged_key);
2,337,056✔
582
         const chash_marker_t marker = pointer_tag(tagged_key);
2,337,056✔
583
         const void *key = untag_pointer(tagged_key, void);
2,337,056✔
584
         switch (marker) {
2,337,056✔
585
         case BUSY_MARKER:
×
586
            spin_wait();
×
587
            continue;
×
588
         case INUSE_MARKER:
1,190,578✔
589
            {
590
               for (int j = hash_slot(to->size, key);;
1,190,578✔
591
                    j = (j + 1) & (to->size - 1)) {
204,236✔
592
                  void *exist = load_acquire(&(to->slots[j].tagged_key));
1,394,814✔
593
                  if (exist == NULL) {
1,394,814✔
594
                     to->slots[j].value = from->slots[i].value;
1,190,578✔
595
                     store_release(&(to->slots[j].tagged_key), tagged_key);
1,190,578✔
596
                     break;
1,190,578✔
597
                  }
598
                  else
599
                     assert(exist != tagged_key);
204,236✔
600
               }
601
            }
602
            break;
1,190,578✔
603
         case FREE_MARKER:
604
            break;
605
         default:
×
606
            should_not_reach_here();
607
         }
608

609
         void *moved = tag_pointer(key, MOVED_MARKER);
2,337,056✔
610
         if (atomic_cas(&(from->slots[i].tagged_key), tagged_key, moved))
2,337,056✔
611
            break;
612
         else
613
            assert(marker == FREE_MARKER);   // Raced to move free slot
×
614
      }
615
   }
616
}
22,050✔
617

618
static void chash_grow_table(chash_t *h, chash_tab_t *cur)
22,050✔
619
{
620
   chash_tab_t *newtab =
22,050✔
621
      xcalloc_flex(sizeof(chash_tab_t), cur->size * 2, sizeof(chash_slot_t));
22,050✔
622
   newtab->size = cur->size * 2;
22,050✔
623

624
   if (atomic_cas(&h->resizing, cur, newtab)) {
22,050✔
625
      chash_copy_table(cur, newtab);
22,050✔
626

627
      assert(atomic_load(&h->resizing) == newtab);
22,050✔
628

629
      if (!atomic_cas(&h->tab, cur, newtab))
22,050✔
630
         should_not_reach_here();
631

632
      async_free(cur);
22,050✔
633
   }
634
   else {
635
      free(newtab);
×
636
      chash_wait_for_resize(h);
×
637
   }
638
}
22,050✔
639

640
static bool chash_try_put(chash_t *h, const void *key, void *value)
1,058,434✔
641
{
642
   chash_tab_t *tab = load_acquire(&h->tab);
1,058,434✔
643
   assert(is_power_of_2(tab->size));
1,058,434✔
644

645
   if (relaxed_load(&h->members) > tab->size / 2) {
1,058,434✔
646
      chash_grow_table(h, tab);
22,050✔
647
      return false;
22,050✔
648
   }
649

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

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

697
void chash_put(chash_t *h, const void *key, void *value)
1,036,384✔
698
{
699
   while (!chash_try_put(h, key, value));
1,058,434✔
700
}
1,036,384✔
701

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

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

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

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

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

753
static bool chash_try_get(chash_t *h, const void *key, void **value)
1,361,260✔
754
{
755
   chash_tab_t *tab = load_acquire(&h->tab);
1,361,260✔
756
   assert(is_power_of_2(tab->size));
1,361,260✔
757

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

762
      switch (marker) {
2,122,101✔
763
      case BUSY_MARKER:
×
764
         spin_wait();
×
765
         return false;
×
766
      case FREE_MARKER:
1,096,477✔
767
         *value = NULL;
1,096,477✔
768
         return true;
1,096,477✔
769
      case INUSE_MARKER:
1,025,624✔
770
         if (untag_pointer(tagged_key, void) == key) {
1,025,624✔
771
            *value = tab->slots[i].value;
264,783✔
772
            return true;
264,783✔
773
         }
774
         else
775
            break;
776
      case MOVED_MARKER:
×
777
         chash_wait_for_resize(h);
×
778
         return false;
×
779
      }
780
   }
781
}
782

783
void *chash_get(chash_t *h, const void *key)
1,361,260✔
784
{
785
   void *value;
1,361,260✔
786
   while (!chash_try_get(h, key, &value));
1,361,260✔
787
   return value;
1,361,260✔
788
}
789

790
void chash_iter(chash_t *h, hash_iter_fn_t fn)
12,888✔
791
{
792
   chash_tab_t *tab = load_acquire(&h->tab);
12,888✔
793
   for (int pos = 0; pos < tab->size;) {
2,750,072✔
794
      void *const tagged_key = load_acquire(&tab->slots[pos].tagged_key);
2,737,184✔
795
      const chash_marker_t marker = pointer_tag(tagged_key);
2,737,184✔
796

797
      switch (marker) {
2,737,184✔
798
      case BUSY_MARKER:
×
799
         spin_wait();
×
800
         continue;
×
801
      case FREE_MARKER:
802
         break;
803
      case MOVED_MARKER:
888,245✔
804
      case INUSE_MARKER:
805
         (*fn)(untag_pointer(tagged_key, void), tab->slots[pos].value);
888,245✔
806
         break;
888,245✔
807
      }
808

809
      pos++;
2,737,184✔
810
   }
811
}
12,888✔
812

813
////////////////////////////////////////////////////////////////////////////////
814
// Generic hash table
815

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

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

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

841
ghash_t *ghash_new(int size, ghash_hash_fn_t hash_fn, ghash_cmp_fn_t cmp_fn)
29,018✔
842
{
843
   ghash_t *h = xmalloc(sizeof(ghash_t));
29,018✔
844
   h->size    = MAX(HASH_MIN_SIZE, next_power_of_2(size));
29,018✔
845
   h->members = 0;
29,018✔
846
   h->hash_fn = hash_fn;
29,018✔
847
   h->cmp_fn  = cmp_fn;
29,018✔
848

849
   ghash_alloc(h);
29,018✔
850

851
   return h;
29,018✔
852
}
853

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

862
void ghash_put(ghash_t *h, const void *key, void *value)
74,526✔
863
{
864
   if (unlikely(h->members > h->size / 2)) {
74,526✔
865
      // Rebuild the hash table with a larger size
866

867
      const int old_size = h->size;
147✔
868
      h->size *= 2;
147✔
869

870
      const void **old_keys = h->keys;
147✔
871
      void **old_values = h->values;
147✔
872
      uint32_t *old_hashes = h->hashes;
147✔
873

874
      ghash_alloc(h);
147✔
875

876
      h->members = 0;
147✔
877

878
      for (int i = 0; i < old_size; i++) {
4,135✔
879
         if (old_keys[i] != NULL && old_values[i] != NULL) {
3,988✔
880
            int slot = old_hashes[i] & (h->size - 1);
2,141✔
881

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

896
      free(old_values);
147✔
897
   }
898

899
   uint32_t hash = ghash_hash_fn(h, key);
74,526✔
900
   int slot = hash & (h->size - 1);
74,526✔
901

902
   for (; ; slot = (slot + 1) & (h->size - 1)) {
4,006✔
903
      if (h->keys[slot] == NULL) {
78,532✔
904
         h->values[slot] = value;
74,486✔
905
         h->keys[slot] = key;
74,486✔
906
         h->hashes[slot] = hash;
74,486✔
907
         h->members++;
74,486✔
908
         break;
74,486✔
909
      }
910
      else if (h->hashes[slot] != hash)
4,046✔
911
         continue;
3,934✔
912
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key)) {
112✔
913
         h->values[slot] = value;
40✔
914
         break;
40✔
915
      }
916
   }
917
}
74,526✔
918

919
void *ghash_get(ghash_t *h, const void *key)
197,309✔
920
{
921
   uint32_t hash = ghash_hash_fn(h, key);
197,309✔
922
   int slot = hash & (h->size - 1);
197,309✔
923

924
   for (; ; slot = (slot + 1) & (h->size - 1)) {
8,510✔
925
      if (h->keys[slot] == NULL)
205,819✔
926
         return NULL;
927
      else if (h->hashes[slot] != hash)
94,752✔
928
         continue;
8,101✔
929
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
86,651✔
930
         return h->values[slot];
86,242✔
931
   }
932
}
933

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

939
   for (; ; slot = (slot + 1) & (h->size - 1)) {
×
940
      if (h->keys[slot] == NULL)
1✔
941
         return;
942
      else if (h->hashes[slot] != hash)
1✔
943
         continue;
×
944
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key)) {
1✔
945
         h->values[slot] = NULL;
1✔
946
         h->hashes[slot] = 0;
1✔
947
         return;
1✔
948
      }
949
   }
950
}
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