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

nickg / nvc / 13887457702

16 Mar 2025 09:12PM UTC coverage: 92.285% (-0.04%) from 92.324%
13887457702

push

github

nickg
Tag Docker images on releases. Fixes #1165

68273 of 73981 relevant lines covered (92.28%)

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

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

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

51
   return h;
105,517✔
52
}
53

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

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

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

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

78
      h->members = 0;
752✔
79

80
      for (int i = 0; i < old_size; i++) {
755,984✔
81
         if (old_keys[i] != NULL && old_values[i] != NULL) {
755,232✔
82
            int slot = hash_slot(h->size, old_keys[i]);
378,368✔
83

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

97
      free(old_values);
752✔
98
   }
99

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

102
   for (; ; slot = (slot + 1) & (h->size - 1)) {
226,799✔
103
      if (h->keys[slot] == key) {
850,636✔
104
         h->values[slot] = value;
42,382✔
105
         return true;
42,382✔
106
      }
107
      else if (h->keys[slot] == NULL) {
808,254✔
108
         h->values[slot] = value;
581,455✔
109
         h->keys[slot] = key;
581,455✔
110
         h->members++;
581,455✔
111
         break;
581,455✔
112
      }
113
   }
114

115
   return false;
581,455✔
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,977,804✔
133
{
134
   int slot = hash_slot(h->size, key);
1,977,804✔
135

136
   for (; ; slot = (slot + 1) & (h->size - 1)) {
353,370✔
137
      if (h->keys[slot] == key)
2,331,174✔
138
         return h->values[slot];
1,029,342✔
139
      else if (h->keys[slot] == NULL)
1,301,832✔
140
         return NULL;
141
   }
142
}
143

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

148
   while (*now < h->size) {
18,564,076✔
149
      const unsigned old = (*now)++;
18,536,992✔
150
      if (h->keys[old] != NULL && h->values[old] != NULL) {
18,536,992✔
151
         *key   = h->keys[old];
787,448✔
152
         *value = h->values[old];
787,448✔
153
         return true;
787,448✔
154
      }
155
   }
156

157
   *now = HASH_END;
27,084✔
158
   return false;
27,084✔
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)
352,593✔
177
{
178
   assert(key != NULL);
352,593✔
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++))
9,775,963✔
187
      hash = ((hash << 5) + hash) + c;
9,423,370✔
188

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

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

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

202
   return h;
6,201✔
203
}
204

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

210
   for (unsigned i = 0; i < h->size; i++) {
165,728✔
211
      if (h->keys[i] != NULL)
164,024✔
212
         free(h->keys[i]);
33,971✔
213
   }
214

215
   free(h->values);
1,704✔
216
   free(h);
1,704✔
217
}
218

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

223
   for (; ; slot = (slot + 1) & (h->size - 1)) {
23,049✔
224
      if (h->keys[slot] == NULL) {
113,507✔
225
         h->values[slot] = value;
90,458✔
226
         h->keys[slot] = key;
90,458✔
227
         h->members++;
90,458✔
228
         break;
90,458✔
229
      }
230
      else if (strcmp(h->keys[slot], key) == 0) {
23,049✔
231
         h->values[slot] = value;
×
232
         return;
×
233
      }
234
   }
235
}
236

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

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

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

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

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

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

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

262
   shash_put_copy(h, xstrdup(key), value);
60,359✔
263
}
60,359✔
264

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

269
   for (; ; slot = (slot + 1) & (h->size - 1)) {
117,701✔
270
      if (h->keys[slot] == NULL)
379,835✔
271
         return NULL;
272
      else if (strcmp(h->keys[slot], key) == 0)
321,033✔
273
         return h->values[slot];
203,332✔
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)
562,314✔
322
{
323
   key = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
562,314✔
324
   key = (key ^ (key >> 27)) * UINT64_C(0x94d049bb133111eb);
562,314✔
325
   key = key ^ (key >> 31);
562,314✔
326

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

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

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

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

347
   return h;
47,972✔
348
}
349

350
void ihash_free(ihash_t *h)
55,995✔
351
{
352
   if (h != NULL) {
55,995✔
353
      free(h->values);
47,968✔
354
      free(h);
47,968✔
355
   }
356
}
55,995✔
357

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

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

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

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

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

381
      h->members = 0;
39✔
382

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

388
      free(old_values);
39✔
389
   }
390

391
   h->cachekey = key;
167,914✔
392
   h->cacheval = value;
167,914✔
393

394
   int slot = ihash_slot(h, key);
167,914✔
395

396
   for (; ; slot = (slot + 1) & (h->size - 1)) {
7,563✔
397
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64)))) {
175,477✔
398
         h->values[slot] = value;
167,914✔
399
         h->keys[slot] = key;
167,914✔
400
         h->mask[slot / 64] |= (UINT64_C(1) << (slot % 64));
167,914✔
401
         h->members++;
167,914✔
402
         break;
167,914✔
403
      }
404
      else if (h->keys[slot] == key) {
7,563✔
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)
703,341✔
413
{
414
   if (h->members > 0 && key == h->cachekey)
703,341✔
415
      return h->cacheval;
308,941✔
416

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

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

421
   for (; ; slot = (slot + 1) & (h->size - 1)) {
15,571✔
422
      if (!(h->mask[slot / 64] & (UINT64_C(1) << (slot % 64))))
409,971✔
423
         return (h->cacheval = NULL);
164,622✔
424
      else if (h->keys[slot] == key)
245,349✔
425
         return (h->cacheval = h->values[slot]);
229,778✔
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)
14,855✔
439
{
440
   hset_t *h = xmalloc(sizeof(hset_t));
14,855✔
441
   h->size    = next_power_of_2(size);
14,855✔
442
   h->members = 0;
14,855✔
443
   h->keys    = xcalloc_array(h->size, sizeof(void *));
14,855✔
444

445
   return h;
14,855✔
446
}
447

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

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

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

465
      h->members = 0;
637✔
466

467
      for (int i = 0; i < old_size; i++) {
69,309✔
468
         if (old_keys[i] != NULL)
68,672✔
469
            hset_insert(h, old_keys[i]);
34,973✔
470
      }
471

472
      free(old_keys);
637✔
473
   }
474

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

477
   for (; ; slot = (slot + 1) & (h->size - 1)) {
29,190✔
478
      if (h->keys[slot] == key)
133,362✔
479
         return;
480
      else if (h->keys[slot] == NULL) {
132,539✔
481
         h->keys[slot] = key;
103,349✔
482
         h->members++;
103,349✔
483
         break;
103,349✔
484
      }
485
   }
486
}
487

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

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

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

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

546
   return h;
28,704✔
547
}
548

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

555
      if (from == to)
23,865✔
556
         break;
557

558
      thread_sleep(10);
×
559
   }
560
}
23,865✔
561

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

567
   chash_wait_for_resize(h);
23,865✔
568

569
   free(h->tab);
23,865✔
570
   free(h);
23,865✔
571
}
572

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

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

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

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

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

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

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

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

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

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

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

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

758
      switch (marker) {
537,818✔
759
      case BUSY_MARKER:
×
760
         spin_wait();
×
761
         return false;
×
762
      case FREE_MARKER:
236,017✔
763
         *value = NULL;
236,017✔
764
         return true;
236,017✔
765
      case INUSE_MARKER:
301,801✔
766
         if (untag_pointer(tagged_key, void) == key) {
301,801✔
767
            *value = tab->slots[i].value;
237,366✔
768
            return true;
237,366✔
769
         }
770
         else
771
            break;
772
      case MOVED_MARKER:
×
773
         chash_wait_for_resize(h);
×
774
         return false;
×
775
      }
776
   }
777
}
778

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

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

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

805
      pos++;
1,927,296✔
806
   }
807
}
15,022✔
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)
193,955✔
822
{
823
   assert(key != NULL);
193,955✔
824

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

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

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

841
   return h;
11,448✔
842
}
843

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

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

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

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

895
   for (; ; slot = (slot + 1) & (h->size - 1)) {
1,000✔
896
      if (h->keys[slot] == NULL)
168,527✔
897
         return NULL;
898
      else if (h->keys[slot] == key || (*h->cmp_fn)(h->keys[slot], key))
131,103✔
899
         return h->values[slot];
130,103✔
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

© 2025 Coveralls, Inc