Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/ck/src/ck_hs.c
48262 views
1
/*
2
* Copyright 2012-2015 Samy Al Bahra.
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <ck_cc.h>
28
#include <ck_hs.h>
29
#include <ck_limits.h>
30
#include <ck_md.h>
31
#include <ck_pr.h>
32
#include <ck_stdint.h>
33
#include <ck_stdbool.h>
34
#include <ck_string.h>
35
36
#include "ck_internal.h"
37
38
#ifndef CK_HS_PROBE_L1_SHIFT
39
#define CK_HS_PROBE_L1_SHIFT 3ULL
40
#endif /* CK_HS_PROBE_L1_SHIFT */
41
42
#define CK_HS_PROBE_L1 (1 << CK_HS_PROBE_L1_SHIFT)
43
#define CK_HS_PROBE_L1_MASK (CK_HS_PROBE_L1 - 1)
44
45
#ifndef CK_HS_PROBE_L1_DEFAULT
46
#define CK_HS_PROBE_L1_DEFAULT CK_MD_CACHELINE
47
#endif
48
49
#define CK_HS_VMA_MASK ((uintptr_t)((1ULL << CK_MD_VMA_BITS) - 1))
50
#define CK_HS_VMA(x) \
51
((void *)((uintptr_t)(x) & CK_HS_VMA_MASK))
52
53
#define CK_HS_EMPTY NULL
54
#define CK_HS_TOMBSTONE ((void *)~(uintptr_t)0)
55
#define CK_HS_G (2)
56
#define CK_HS_G_MASK (CK_HS_G - 1)
57
58
#if defined(CK_F_PR_LOAD_8) && defined(CK_F_PR_STORE_8)
59
#define CK_HS_WORD uint8_t
60
#define CK_HS_WORD_MAX UINT8_MAX
61
#define CK_HS_STORE(x, y) ck_pr_store_8(x, y)
62
#define CK_HS_LOAD(x) ck_pr_load_8(x)
63
#elif defined(CK_F_PR_LOAD_16) && defined(CK_F_PR_STORE_16)
64
#define CK_HS_WORD uint16_t
65
#define CK_HS_WORD_MAX UINT16_MAX
66
#define CK_HS_STORE(x, y) ck_pr_store_16(x, y)
67
#define CK_HS_LOAD(x) ck_pr_load_16(x)
68
#elif defined(CK_F_PR_LOAD_32) && defined(CK_F_PR_STORE_32)
69
#define CK_HS_WORD uint32_t
70
#define CK_HS_WORD_MAX UINT32_MAX
71
#define CK_HS_STORE(x, y) ck_pr_store_32(x, y)
72
#define CK_HS_LOAD(x) ck_pr_load_32(x)
73
#else
74
#error "ck_hs is not supported on your platform."
75
#endif
76
77
enum ck_hs_probe_behavior {
78
CK_HS_PROBE = 0, /* Default behavior. */
79
CK_HS_PROBE_TOMBSTONE, /* Short-circuit on tombstone. */
80
CK_HS_PROBE_INSERT /* Short-circuit on probe bound if tombstone found. */
81
};
82
83
struct ck_hs_map {
84
unsigned int generation[CK_HS_G];
85
unsigned int probe_maximum;
86
unsigned long mask;
87
unsigned long step;
88
unsigned int probe_limit;
89
unsigned int tombstones;
90
unsigned long n_entries;
91
unsigned long capacity;
92
unsigned long size;
93
CK_HS_WORD *probe_bound;
94
const void **entries;
95
};
96
97
static inline void
98
ck_hs_map_signal(struct ck_hs_map *map, unsigned long h)
99
{
100
101
h &= CK_HS_G_MASK;
102
ck_pr_store_uint(&map->generation[h],
103
map->generation[h] + 1);
104
ck_pr_fence_store();
105
return;
106
}
107
108
static bool
109
_ck_hs_next(struct ck_hs *hs, struct ck_hs_map *map,
110
struct ck_hs_iterator *i, void **key)
111
{
112
void *value;
113
114
if (i->offset >= map->capacity)
115
return false;
116
117
do {
118
value = CK_CC_DECONST_PTR(map->entries[i->offset]);
119
if (value != CK_HS_EMPTY && value != CK_HS_TOMBSTONE) {
120
#ifdef CK_HS_PP
121
if (hs->mode & CK_HS_MODE_OBJECT)
122
value = CK_HS_VMA(value);
123
#else
124
(void)hs; /* Avoid unused parameter warning. */
125
#endif
126
i->offset++;
127
*key = value;
128
return true;
129
}
130
} while (++i->offset < map->capacity);
131
132
return false;
133
}
134
135
void
136
ck_hs_iterator_init(struct ck_hs_iterator *iterator)
137
{
138
139
iterator->cursor = NULL;
140
iterator->offset = 0;
141
iterator->map = NULL;
142
return;
143
}
144
145
bool
146
ck_hs_next(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
147
{
148
149
return _ck_hs_next(hs, hs->map, i, key);
150
}
151
152
bool
153
ck_hs_next_spmc(struct ck_hs *hs, struct ck_hs_iterator *i, void **key)
154
{
155
struct ck_hs_map *m = i->map;
156
157
if (m == NULL) {
158
m = i->map = ck_pr_load_ptr(&hs->map);
159
}
160
161
return _ck_hs_next(hs, m, i, key);
162
}
163
164
void
165
ck_hs_stat(struct ck_hs *hs, struct ck_hs_stat *st)
166
{
167
struct ck_hs_map *map = hs->map;
168
169
st->n_entries = map->n_entries;
170
st->tombstones = map->tombstones;
171
st->probe_maximum = map->probe_maximum;
172
return;
173
}
174
175
unsigned long
176
ck_hs_count(struct ck_hs *hs)
177
{
178
179
return hs->map->n_entries;
180
}
181
182
static void
183
ck_hs_map_destroy(struct ck_malloc *m, struct ck_hs_map *map, bool defer)
184
{
185
186
m->free(map, map->size, defer);
187
return;
188
}
189
190
void
191
ck_hs_destroy(struct ck_hs *hs)
192
{
193
194
ck_hs_map_destroy(hs->m, hs->map, false);
195
return;
196
}
197
198
static struct ck_hs_map *
199
ck_hs_map_create(struct ck_hs *hs, unsigned long entries)
200
{
201
struct ck_hs_map *map;
202
unsigned long size, n_entries, prefix, limit;
203
204
n_entries = ck_internal_power_2(entries);
205
if (n_entries < CK_HS_PROBE_L1)
206
n_entries = CK_HS_PROBE_L1;
207
208
size = sizeof(struct ck_hs_map) + (sizeof(void *) * n_entries + CK_MD_CACHELINE - 1);
209
210
if (hs->mode & CK_HS_MODE_DELETE) {
211
prefix = sizeof(CK_HS_WORD) * n_entries;
212
size += prefix;
213
} else {
214
prefix = 0;
215
}
216
217
map = hs->m->malloc(size);
218
if (map == NULL)
219
return NULL;
220
221
map->size = size;
222
223
/* We should probably use a more intelligent heuristic for default probe length. */
224
limit = ck_internal_max(n_entries >> (CK_HS_PROBE_L1_SHIFT + 2), CK_HS_PROBE_L1_DEFAULT);
225
if (limit > UINT_MAX)
226
limit = UINT_MAX;
227
228
map->probe_limit = (unsigned int)limit;
229
map->probe_maximum = 0;
230
map->capacity = n_entries;
231
map->step = ck_cc_ffsl(n_entries);
232
map->mask = n_entries - 1;
233
map->n_entries = 0;
234
235
/* Align map allocation to cache line. */
236
map->entries = (void *)(((uintptr_t)&map[1] + prefix +
237
CK_MD_CACHELINE - 1) & ~(CK_MD_CACHELINE - 1));
238
239
memset(map->entries, 0, sizeof(void *) * n_entries);
240
memset(map->generation, 0, sizeof map->generation);
241
242
if (hs->mode & CK_HS_MODE_DELETE) {
243
map->probe_bound = (CK_HS_WORD *)&map[1];
244
memset(map->probe_bound, 0, prefix);
245
} else {
246
map->probe_bound = NULL;
247
}
248
249
/* Commit entries purge with respect to map publication. */
250
ck_pr_fence_store();
251
return map;
252
}
253
254
bool
255
ck_hs_reset_size(struct ck_hs *hs, unsigned long capacity)
256
{
257
struct ck_hs_map *map, *previous;
258
259
previous = hs->map;
260
map = ck_hs_map_create(hs, capacity);
261
if (map == NULL)
262
return false;
263
264
ck_pr_store_ptr(&hs->map, map);
265
ck_hs_map_destroy(hs->m, previous, true);
266
return true;
267
}
268
269
bool
270
ck_hs_reset(struct ck_hs *hs)
271
{
272
struct ck_hs_map *previous;
273
274
previous = hs->map;
275
return ck_hs_reset_size(hs, previous->capacity);
276
}
277
278
static inline unsigned long
279
ck_hs_map_probe_next(struct ck_hs_map *map,
280
unsigned long offset,
281
unsigned long h,
282
unsigned long level,
283
unsigned long probes)
284
{
285
unsigned long r, stride;
286
287
r = (h >> map->step) >> level;
288
stride = (r & ~CK_HS_PROBE_L1_MASK) << 1 | (r & CK_HS_PROBE_L1_MASK);
289
290
return (offset + (probes >> CK_HS_PROBE_L1_SHIFT) +
291
(stride | CK_HS_PROBE_L1)) & map->mask;
292
}
293
294
static inline void
295
ck_hs_map_bound_set(struct ck_hs_map *m,
296
unsigned long h,
297
unsigned long n_probes)
298
{
299
unsigned long offset = h & m->mask;
300
301
if (n_probes > m->probe_maximum)
302
ck_pr_store_uint(&m->probe_maximum, n_probes);
303
304
if (m->probe_bound != NULL && m->probe_bound[offset] < n_probes) {
305
if (n_probes > CK_HS_WORD_MAX)
306
n_probes = CK_HS_WORD_MAX;
307
308
CK_HS_STORE(&m->probe_bound[offset], n_probes);
309
ck_pr_fence_store();
310
}
311
312
return;
313
}
314
315
static inline unsigned int
316
ck_hs_map_bound_get(struct ck_hs_map *m, unsigned long h)
317
{
318
unsigned long offset = h & m->mask;
319
unsigned int r = CK_HS_WORD_MAX;
320
321
if (m->probe_bound != NULL) {
322
r = CK_HS_LOAD(&m->probe_bound[offset]);
323
if (r == CK_HS_WORD_MAX)
324
r = ck_pr_load_uint(&m->probe_maximum);
325
} else {
326
r = ck_pr_load_uint(&m->probe_maximum);
327
}
328
329
return r;
330
}
331
332
bool
333
ck_hs_grow(struct ck_hs *hs,
334
unsigned long capacity)
335
{
336
struct ck_hs_map *map, *update;
337
unsigned long k, i, j, offset, probes;
338
const void *previous, **bucket;
339
340
restart:
341
map = hs->map;
342
if (map->capacity > capacity)
343
return false;
344
345
update = ck_hs_map_create(hs, capacity);
346
if (update == NULL)
347
return false;
348
349
for (k = 0; k < map->capacity; k++) {
350
unsigned long h;
351
352
previous = map->entries[k];
353
if (previous == CK_HS_EMPTY || previous == CK_HS_TOMBSTONE)
354
continue;
355
356
#ifdef CK_HS_PP
357
if (hs->mode & CK_HS_MODE_OBJECT)
358
previous = CK_HS_VMA(previous);
359
#endif
360
361
h = hs->hf(previous, hs->seed);
362
offset = h & update->mask;
363
i = probes = 0;
364
365
for (;;) {
366
bucket = (const void **)((uintptr_t)&update->entries[offset] & ~(CK_MD_CACHELINE - 1));
367
368
for (j = 0; j < CK_HS_PROBE_L1; j++) {
369
const void **cursor = bucket + ((j + offset) & (CK_HS_PROBE_L1 - 1));
370
371
if (probes++ == update->probe_limit)
372
break;
373
374
if (CK_CC_LIKELY(*cursor == CK_HS_EMPTY)) {
375
*cursor = map->entries[k];
376
update->n_entries++;
377
378
ck_hs_map_bound_set(update, h, probes);
379
break;
380
}
381
}
382
383
if (j < CK_HS_PROBE_L1)
384
break;
385
386
offset = ck_hs_map_probe_next(update, offset, h, i++, probes);
387
}
388
389
if (probes > update->probe_limit) {
390
/*
391
* We have hit the probe limit, map needs to be even larger.
392
*/
393
ck_hs_map_destroy(hs->m, update, false);
394
capacity <<= 1;
395
goto restart;
396
}
397
}
398
399
ck_pr_fence_store();
400
ck_pr_store_ptr(&hs->map, update);
401
ck_hs_map_destroy(hs->m, map, true);
402
return true;
403
}
404
405
static void
406
ck_hs_map_postinsert(struct ck_hs *hs, struct ck_hs_map *map)
407
{
408
409
map->n_entries++;
410
if ((map->n_entries << 1) > map->capacity)
411
ck_hs_grow(hs, map->capacity << 1);
412
413
return;
414
}
415
416
bool
417
ck_hs_rebuild(struct ck_hs *hs)
418
{
419
420
return ck_hs_grow(hs, hs->map->capacity);
421
}
422
423
static const void **
424
ck_hs_map_probe(struct ck_hs *hs,
425
struct ck_hs_map *map,
426
unsigned long *n_probes,
427
const void ***priority,
428
unsigned long h,
429
const void *key,
430
const void **object,
431
unsigned long probe_limit,
432
enum ck_hs_probe_behavior behavior)
433
{
434
const void **bucket, **cursor, *k, *compare;
435
const void **pr = NULL;
436
unsigned long offset, j, i, probes, opl;
437
438
#ifdef CK_HS_PP
439
/* If we are storing object pointers, then we may leverage pointer packing. */
440
unsigned long hv = 0;
441
442
if (hs->mode & CK_HS_MODE_OBJECT) {
443
hv = (h >> 25) & CK_HS_KEY_MASK;
444
compare = CK_HS_VMA(key);
445
} else {
446
compare = key;
447
}
448
#else
449
compare = key;
450
#endif
451
452
offset = h & map->mask;
453
*object = NULL;
454
i = probes = 0;
455
456
opl = probe_limit;
457
if (behavior == CK_HS_PROBE_INSERT)
458
probe_limit = ck_hs_map_bound_get(map, h);
459
460
for (;;) {
461
bucket = (const void **)((uintptr_t)&map->entries[offset] & ~(CK_MD_CACHELINE - 1));
462
463
for (j = 0; j < CK_HS_PROBE_L1; j++) {
464
cursor = bucket + ((j + offset) & (CK_HS_PROBE_L1 - 1));
465
466
if (probes++ == probe_limit) {
467
if (probe_limit == opl || pr != NULL) {
468
k = CK_HS_EMPTY;
469
goto leave;
470
}
471
472
/*
473
* If no eligible slot has been found yet, continue probe
474
* sequence with original probe limit.
475
*/
476
probe_limit = opl;
477
}
478
479
k = ck_pr_load_ptr(cursor);
480
if (k == CK_HS_EMPTY)
481
goto leave;
482
483
if (k == CK_HS_TOMBSTONE) {
484
if (pr == NULL) {
485
pr = cursor;
486
*n_probes = probes;
487
488
if (behavior == CK_HS_PROBE_TOMBSTONE) {
489
k = CK_HS_EMPTY;
490
goto leave;
491
}
492
}
493
494
continue;
495
}
496
497
#ifdef CK_HS_PP
498
if (hs->mode & CK_HS_MODE_OBJECT) {
499
if (((uintptr_t)k >> CK_MD_VMA_BITS) != hv)
500
continue;
501
502
k = CK_HS_VMA(k);
503
}
504
#endif
505
506
if (k == compare)
507
goto leave;
508
509
if (hs->compare == NULL)
510
continue;
511
512
if (hs->compare(k, key) == true)
513
goto leave;
514
}
515
516
offset = ck_hs_map_probe_next(map, offset, h, i++, probes);
517
}
518
519
leave:
520
if (probes > probe_limit) {
521
cursor = NULL;
522
} else {
523
*object = k;
524
}
525
526
if (pr == NULL)
527
*n_probes = probes;
528
529
*priority = pr;
530
return cursor;
531
}
532
533
static inline const void *
534
ck_hs_marshal(unsigned int mode, const void *key, unsigned long h)
535
{
536
#ifdef CK_HS_PP
537
const void *insert;
538
539
if (mode & CK_HS_MODE_OBJECT) {
540
insert = (void *)((uintptr_t)CK_HS_VMA(key) |
541
((h >> 25) << CK_MD_VMA_BITS));
542
} else {
543
insert = key;
544
}
545
546
return insert;
547
#else
548
(void)mode;
549
(void)h;
550
551
return key;
552
#endif
553
}
554
555
bool
556
ck_hs_gc(struct ck_hs *hs, unsigned long cycles, unsigned long seed)
557
{
558
unsigned long size = 0;
559
unsigned long i;
560
struct ck_hs_map *map = hs->map;
561
unsigned int maximum;
562
CK_HS_WORD *bounds = NULL;
563
564
if (map->n_entries == 0) {
565
ck_pr_store_uint(&map->probe_maximum, 0);
566
if (map->probe_bound != NULL)
567
memset(map->probe_bound, 0, sizeof(CK_HS_WORD) * map->capacity);
568
569
return true;
570
}
571
572
if (cycles == 0) {
573
maximum = 0;
574
575
if (map->probe_bound != NULL) {
576
size = sizeof(CK_HS_WORD) * map->capacity;
577
bounds = hs->m->malloc(size);
578
if (bounds == NULL)
579
return false;
580
581
memset(bounds, 0, size);
582
}
583
} else {
584
maximum = map->probe_maximum;
585
}
586
587
for (i = 0; i < map->capacity; i++) {
588
const void **first, *object, **slot, *entry;
589
unsigned long n_probes, offset, h;
590
591
entry = map->entries[(i + seed) & map->mask];
592
if (entry == CK_HS_EMPTY || entry == CK_HS_TOMBSTONE)
593
continue;
594
595
#ifdef CK_HS_PP
596
if (hs->mode & CK_HS_MODE_OBJECT)
597
entry = CK_HS_VMA(entry);
598
#endif
599
600
h = hs->hf(entry, hs->seed);
601
offset = h & map->mask;
602
603
slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, entry, &object,
604
ck_hs_map_bound_get(map, h), CK_HS_PROBE);
605
606
if (first != NULL) {
607
const void *insert = ck_hs_marshal(hs->mode, entry, h);
608
609
ck_pr_store_ptr(first, insert);
610
ck_hs_map_signal(map, h);
611
ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
612
}
613
614
if (cycles == 0) {
615
if (n_probes > maximum)
616
maximum = n_probes;
617
618
if (n_probes > CK_HS_WORD_MAX)
619
n_probes = CK_HS_WORD_MAX;
620
621
if (bounds != NULL && n_probes > bounds[offset])
622
bounds[offset] = n_probes;
623
} else if (--cycles == 0)
624
break;
625
}
626
627
/*
628
* The following only apply to garbage collection involving
629
* a full scan of all entries.
630
*/
631
if (maximum != map->probe_maximum)
632
ck_pr_store_uint(&map->probe_maximum, maximum);
633
634
if (bounds != NULL) {
635
for (i = 0; i < map->capacity; i++)
636
CK_HS_STORE(&map->probe_bound[i], bounds[i]);
637
638
hs->m->free(bounds, size, false);
639
}
640
641
return true;
642
}
643
644
bool
645
ck_hs_fas(struct ck_hs *hs,
646
unsigned long h,
647
const void *key,
648
void **previous)
649
{
650
const void **slot, **first, *object, *insert;
651
struct ck_hs_map *map = hs->map;
652
unsigned long n_probes;
653
654
*previous = NULL;
655
slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
656
ck_hs_map_bound_get(map, h), CK_HS_PROBE);
657
658
/* Replacement semantics presume existence. */
659
if (object == NULL)
660
return false;
661
662
insert = ck_hs_marshal(hs->mode, key, h);
663
664
if (first != NULL) {
665
ck_pr_store_ptr(first, insert);
666
ck_hs_map_signal(map, h);
667
ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
668
} else {
669
ck_pr_store_ptr(slot, insert);
670
}
671
672
*previous = CK_CC_DECONST_PTR(object);
673
return true;
674
}
675
676
/*
677
* An apply function takes two arguments. The first argument is a pointer to a
678
* pre-existing object. The second argument is a pointer to the fifth argument
679
* passed to ck_hs_apply. If a non-NULL pointer is passed to the first argument
680
* and the return value of the apply function is NULL, then the pre-existing
681
* value is deleted. If the return pointer is the same as the one passed to the
682
* apply function then no changes are made to the hash table. If the first
683
* argument is non-NULL and the return pointer is different than that passed to
684
* the apply function, then the pre-existing value is replaced. For
685
* replacement, it is required that the value itself is identical to the
686
* previous value.
687
*/
688
bool
689
ck_hs_apply(struct ck_hs *hs,
690
unsigned long h,
691
const void *key,
692
ck_hs_apply_fn_t *fn,
693
void *cl)
694
{
695
const void **slot, **first, *object, *delta, *insert;
696
unsigned long n_probes;
697
struct ck_hs_map *map;
698
699
restart:
700
map = hs->map;
701
702
slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, map->probe_limit, CK_HS_PROBE_INSERT);
703
if (slot == NULL && first == NULL) {
704
if (ck_hs_grow(hs, map->capacity << 1) == false)
705
return false;
706
707
goto restart;
708
}
709
710
delta = fn(CK_CC_DECONST_PTR(object), cl);
711
if (delta == NULL) {
712
/*
713
* The apply function has requested deletion. If the object doesn't exist,
714
* then exit early.
715
*/
716
if (CK_CC_UNLIKELY(object == NULL))
717
return true;
718
719
/* Otherwise, mark slot as deleted. */
720
ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
721
map->n_entries--;
722
map->tombstones++;
723
return true;
724
}
725
726
/* The apply function has not requested hash set modification so exit early. */
727
if (delta == object)
728
return true;
729
730
/* A modification or insertion has been requested. */
731
ck_hs_map_bound_set(map, h, n_probes);
732
733
insert = ck_hs_marshal(hs->mode, delta, h);
734
if (first != NULL) {
735
/*
736
* This follows the same semantics as ck_hs_set, please refer to that
737
* function for documentation.
738
*/
739
ck_pr_store_ptr(first, insert);
740
741
if (object != NULL) {
742
ck_hs_map_signal(map, h);
743
ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
744
}
745
} else {
746
/*
747
* If we are storing into same slot, then atomic store is sufficient
748
* for replacement.
749
*/
750
ck_pr_store_ptr(slot, insert);
751
}
752
753
if (object == NULL)
754
ck_hs_map_postinsert(hs, map);
755
756
return true;
757
}
758
759
bool
760
ck_hs_set(struct ck_hs *hs,
761
unsigned long h,
762
const void *key,
763
void **previous)
764
{
765
const void **slot, **first, *object, *insert;
766
unsigned long n_probes;
767
struct ck_hs_map *map;
768
769
*previous = NULL;
770
771
restart:
772
map = hs->map;
773
774
slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, map->probe_limit, CK_HS_PROBE_INSERT);
775
if (slot == NULL && first == NULL) {
776
if (ck_hs_grow(hs, map->capacity << 1) == false)
777
return false;
778
779
goto restart;
780
}
781
782
ck_hs_map_bound_set(map, h, n_probes);
783
insert = ck_hs_marshal(hs->mode, key, h);
784
785
if (first != NULL) {
786
/* If an earlier bucket was found, then store entry there. */
787
ck_pr_store_ptr(first, insert);
788
789
/*
790
* If a duplicate key was found, then delete it after
791
* signaling concurrent probes to restart. Optionally,
792
* it is possible to install tombstone after grace
793
* period if we can guarantee earlier position of
794
* duplicate key.
795
*/
796
if (object != NULL) {
797
ck_hs_map_signal(map, h);
798
ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
799
}
800
} else {
801
/*
802
* If we are storing into same slot, then atomic store is sufficient
803
* for replacement.
804
*/
805
ck_pr_store_ptr(slot, insert);
806
}
807
808
if (object == NULL)
809
ck_hs_map_postinsert(hs, map);
810
811
*previous = CK_CC_DECONST_PTR(object);
812
return true;
813
}
814
815
CK_CC_INLINE static bool
816
ck_hs_put_internal(struct ck_hs *hs,
817
unsigned long h,
818
const void *key,
819
enum ck_hs_probe_behavior behavior)
820
{
821
const void **slot, **first, *object, *insert;
822
unsigned long n_probes;
823
struct ck_hs_map *map;
824
825
restart:
826
map = hs->map;
827
828
slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
829
map->probe_limit, behavior);
830
831
if (slot == NULL && first == NULL) {
832
if (ck_hs_grow(hs, map->capacity << 1) == false)
833
return false;
834
835
goto restart;
836
}
837
838
/* Fail operation if a match was found. */
839
if (object != NULL)
840
return false;
841
842
ck_hs_map_bound_set(map, h, n_probes);
843
insert = ck_hs_marshal(hs->mode, key, h);
844
845
if (first != NULL) {
846
/* Insert key into first bucket in probe sequence. */
847
ck_pr_store_ptr(first, insert);
848
} else {
849
/* An empty slot was found. */
850
ck_pr_store_ptr(slot, insert);
851
}
852
853
ck_hs_map_postinsert(hs, map);
854
return true;
855
}
856
857
bool
858
ck_hs_put(struct ck_hs *hs,
859
unsigned long h,
860
const void *key)
861
{
862
863
return ck_hs_put_internal(hs, h, key, CK_HS_PROBE_INSERT);
864
}
865
866
bool
867
ck_hs_put_unique(struct ck_hs *hs,
868
unsigned long h,
869
const void *key)
870
{
871
872
return ck_hs_put_internal(hs, h, key, CK_HS_PROBE_TOMBSTONE);
873
}
874
875
void *
876
ck_hs_get(struct ck_hs *hs,
877
unsigned long h,
878
const void *key)
879
{
880
const void **first, *object;
881
struct ck_hs_map *map;
882
unsigned long n_probes;
883
unsigned int g, g_p, probe;
884
unsigned int *generation;
885
886
do {
887
map = ck_pr_load_ptr(&hs->map);
888
generation = &map->generation[h & CK_HS_G_MASK];
889
g = ck_pr_load_uint(generation);
890
probe = ck_hs_map_bound_get(map, h);
891
ck_pr_fence_load();
892
893
ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object, probe, CK_HS_PROBE);
894
895
ck_pr_fence_load();
896
g_p = ck_pr_load_uint(generation);
897
} while (g != g_p);
898
899
return CK_CC_DECONST_PTR(object);
900
}
901
902
void *
903
ck_hs_remove(struct ck_hs *hs,
904
unsigned long h,
905
const void *key)
906
{
907
const void **slot, **first, *object;
908
struct ck_hs_map *map = hs->map;
909
unsigned long n_probes;
910
911
slot = ck_hs_map_probe(hs, map, &n_probes, &first, h, key, &object,
912
ck_hs_map_bound_get(map, h), CK_HS_PROBE);
913
if (object == NULL)
914
return NULL;
915
916
ck_pr_store_ptr(slot, CK_HS_TOMBSTONE);
917
map->n_entries--;
918
map->tombstones++;
919
return CK_CC_DECONST_PTR(object);
920
}
921
922
bool
923
ck_hs_move(struct ck_hs *hs,
924
struct ck_hs *source,
925
ck_hs_hash_cb_t *hf,
926
ck_hs_compare_cb_t *compare,
927
struct ck_malloc *m)
928
{
929
930
if (m == NULL || m->malloc == NULL || m->free == NULL || hf == NULL)
931
return false;
932
933
hs->mode = source->mode;
934
hs->seed = source->seed;
935
hs->map = source->map;
936
hs->m = m;
937
hs->hf = hf;
938
hs->compare = compare;
939
return true;
940
}
941
942
bool
943
ck_hs_init(struct ck_hs *hs,
944
unsigned int mode,
945
ck_hs_hash_cb_t *hf,
946
ck_hs_compare_cb_t *compare,
947
struct ck_malloc *m,
948
unsigned long n_entries,
949
unsigned long seed)
950
{
951
952
if (m == NULL || m->malloc == NULL || m->free == NULL || hf == NULL)
953
return false;
954
955
hs->m = m;
956
hs->mode = mode;
957
hs->seed = seed;
958
hs->hf = hf;
959
hs->compare = compare;
960
961
hs->map = ck_hs_map_create(hs, n_entries);
962
return hs->map != NULL;
963
}
964
965