Derive-C
Loading...
Searching...
No Matches
template.h
Go to the documentation of this file.
1
3
7#if !defined(SKIP_INCLUDES)
8 #include "includes.h"
9#endif
10
13
14#if !defined KEY
15 #if !defined PLACEHOLDERS
16TEMPLATE_ERROR("No KEY")
17 #endif
18 #define KEY map_key_t
19typedef int KEY;
20#endif
21
22#if !defined KEY_HASH
23 #if !defined PLACEHOLDERS
24TEMPLATE_ERROR("No KEY_HASH")
25 #endif
26
27 #define KEY_HASH key_hash
28static size_t KEY_HASH(KEY const* key);
29#endif
30
31#if !defined KEY_EQ
32 #define KEY_EQ DC_MEM_EQ
33#endif
34
35#if !defined KEY_DELETE
36 #define KEY_DELETE DC_NO_DELETE
37#endif
38
39#if !defined KEY_CLONE
40 #define KEY_CLONE DC_COPY_CLONE
41#endif
42
43#if !defined KEY_DEBUG
44 #define KEY_DEBUG DC_DEFAULT_DEBUG
45#endif
46
47#if !defined VALUE
48 #if !defined PLACEHOLDERS
49TEMPLATE_ERROR("No VALUE")
50 #endif
51typedef struct {
52 int x;
53} value_t;
54 #define VALUE value_t
55#endif
56
57#if !defined VALUE_DELETE
58 #define VALUE_DELETE DC_NO_DELETE
59#endif
60
61#if !defined VALUE_CLONE
62 #define VALUE_CLONE DC_COPY_CLONE
63#endif
64
65#if !defined VALUE_DEBUG
66 #define VALUE_DEBUG DC_DEFAULT_DEBUG
67#endif
68
69typedef KEY NS(SELF, key_t);
70typedef VALUE NS(SELF, value_t);
71typedef ALLOC NS(SELF, alloc_t);
72
73#define SLOT NS(SELF, slot_t)
74typedef struct {
75 // TODO: determine ordering
76 VALUE value;
77 KEY key;
78} SLOT;
79
80typedef struct {
81 size_t capacity;
82 size_t count;
83 size_t tombstones;
84
86 SLOT* slots;
87
88 ALLOC* alloc;
89 dc_gdb_marker derive_c_hashmap;
91} SELF;
92
93#define INVARIANT_CHECK(self) \
94 DC_ASSUME(self); \
95 DC_ASSUME(DC_MATH_IS_POWER_OF_2((self)->capacity)); \
96 DC_ASSUME((self)->slots); \
97 DC_ASSUME((self)->ctrl); \
98 DC_ASSUME((self)->alloc); \
99 DC_ASSUME((self)->count + (self)->tombstones <= (self)->capacity);
100
104 size_t ctrl_capacity = capacity + DC_SWISS_SIMD_PROBE_SIZE;
105
106 dc_swiss_ctrl* ctrl =
107 (dc_swiss_ctrl*)NS(ALLOC, calloc)(alloc, sizeof(dc_swiss_ctrl), ctrl_capacity);
108 SLOT* slots = (SLOT*)NS(ALLOC, malloc)(alloc, sizeof(SLOT) * capacity);
109 DC_ASSERT(ctrl && slots);
110
111 for (size_t i = 0; i < capacity; i++) {
112 ctrl[i] = DC_SWISS_VAL_EMPTY;
113 }
115 for (size_t i = 1; i < DC_SWISS_SIMD_PROBE_SIZE; i++) {
116 ctrl[capacity + i] = ctrl[i - 1]; // currently empty
117 }
118
119 return (SELF){
120 .capacity = capacity,
121 .count = 0,
122 .tombstones = 0,
123 .ctrl = ctrl,
124 .slots = slots,
125 .alloc = alloc,
126 .derive_c_hashmap = dc_gdb_marker_new(),
127 .iterator_invalidation_tracker = mutation_tracker_new(),
128 };
129}
130
131static SELF NS(SELF, new_with_capacity_for)(size_t for_items, ALLOC* alloc) {
132 DC_ASSERT(for_items > 0);
133
134 return PRIV(NS(SELF, new_with_exact_capacity))(dc_swiss_capacity(for_items), alloc);
135}
136
137static SELF NS(SELF, new)(ALLOC* alloc) {
139}
140
141static SELF NS(SELF, clone)(SELF const* self) {
142 INVARIANT_CHECK(self);
143
144 size_t ctrl_capacity = self->capacity + DC_SWISS_SIMD_PROBE_SIZE;
145
146 dc_swiss_ctrl* ctrl =
147 (dc_swiss_ctrl*)NS(ALLOC, malloc)(self->alloc, sizeof(dc_swiss_ctrl) * ctrl_capacity);
148 SLOT* slots = (SLOT*)NS(ALLOC, malloc)(self->alloc, sizeof(SLOT) * self->capacity);
149 DC_ASSERT(ctrl && slots);
150
151 memcpy(ctrl, self->ctrl, sizeof(dc_swiss_ctrl) * ctrl_capacity);
152
153 for (size_t i = 0; i < self->capacity; i++) {
154 if (dc_swiss_is_present(self->ctrl[i])) {
155 slots[i].key = KEY_CLONE(&self->slots[i].key);
156 slots[i].value = VALUE_CLONE(&self->slots[i].value);
157 }
158 }
159
160 return (SELF){
161 .capacity = self->capacity,
162 .count = self->count,
163 .tombstones = self->tombstones,
164 .ctrl = ctrl,
165 .slots = slots,
166 .alloc = self->alloc,
167 .derive_c_hashmap = dc_gdb_marker_new(),
168 .iterator_invalidation_tracker = mutation_tracker_new(),
169 };
170}
171
173 INVARIANT_CHECK(self);
174 DC_ASSUME(self->count + self->tombstones < self->capacity);
175 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
176
177 const size_t mask = self->capacity - 1;
178
179 const size_t hash = KEY_HASH(&key);
180 const dc_swiss_id id = dc_swiss_id_from_hash(hash);
181
183
184 const size_t start = hash & mask;
185
186 for (size_t step = 0;; step += DC_SWISS_SIMD_PROBE_SIZE) {
187 const size_t group = (start + step) & mask;
188
189 // Scan the group once:
190 // - detect duplicates
191 // - remember first DELETED
192 // - stop at first EMPTY and insert (EMPTY terminates the probe)
193 for (size_t j = 0; j < DC_SWISS_SIMD_PROBE_SIZE; j++) {
194 const size_t i = (group + j) & mask;
195 const dc_swiss_ctrl c = self->ctrl[i];
196
197 switch (c) {
199 if (first_deleted == DC_SWISS_NO_INDEX) {
200 first_deleted = i;
201 }
202 break;
203
204 case DC_SWISS_VAL_EMPTY: {
205 bool const has_deleted = first_deleted != DC_SWISS_NO_INDEX;
206 const size_t ins = has_deleted ? first_deleted : i;
207 if (has_deleted) {
208 self->tombstones--;
209 }
210
211 self->slots[ins].key = key;
212 self->slots[ins].value = value;
213
214 dc_swiss_ctrl_set_at(self->ctrl, self->capacity, ins, (dc_swiss_ctrl)id);
215
216 self->count++;
217 return &self->slots[ins].value;
218 }
219
221 // Treat as not usable; keep scanning.
222 break;
223
224 default:
225 // Present slot: only a possible duplicate if the 7-bit id matches.
226 if (dc_swiss_ctrl_get_id(c) == id && KEY_EQ(&self->slots[i].key, &key)) {
227 return NULL; // duplicate exists
228 }
229 break;
230 }
231 }
232 }
233}
234
235static void PRIV(NS(SELF, rehash))(SELF* self, size_t new_capacity) {
236 INVARIANT_CHECK(self);
237
238 // NOTE: This code also works for shrinking the hashmap
239 // - we never expect to do this, so are defensive.
240 // - We do expect to rehash with the same capacity (tombstone cleanup)
241 DC_ASSUME(new_capacity >= self->capacity);
242 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
243
244 SELF new_map = PRIV(NS(SELF, new_with_exact_capacity))(new_capacity, self->alloc);
245
246 for (size_t i = 0; i < self->capacity; i++) {
247 if (dc_swiss_is_present(self->ctrl[i])) {
248 (void)PRIV(NS(SELF, try_insert_no_extend_capacity))(&new_map, self->slots[i].key,
249 self->slots[i].value);
250 }
251 }
252
253 new_map.iterator_invalidation_tracker = self->iterator_invalidation_tracker;
254
255 NS(ALLOC, free)(self->alloc, self->ctrl);
256 NS(ALLOC, free)(self->alloc, self->slots);
257
258 *self = new_map;
259}
260
261static void NS(SELF, extend_capacity_for)(SELF* self, size_t expected_items) {
262 INVARIANT_CHECK(self);
263 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
264
265 size_t new_capacity = dc_swiss_capacity(expected_items);
266 if (new_capacity > self->capacity) {
267 PRIV(NS(SELF, rehash))(self, new_capacity);
268 }
269}
270
271static VALUE* NS(SELF, try_insert)(SELF* self, KEY key, VALUE value) {
272 INVARIANT_CHECK(self);
273
274 switch (dc_swiss_heuristic_should_extend(self->tombstones, self->count, self->capacity)) {
276 PRIV(NS(SELF, rehash))(self, self->capacity * 2);
277 break;
279 PRIV(NS(SELF, rehash))(self, self->capacity);
280 break;
282 break;
283 }
284
285 return PRIV(NS(SELF, try_insert_no_extend_capacity))(self, key, value);
286}
287
288static VALUE* NS(SELF, insert)(SELF* self, KEY key, VALUE value) {
289 VALUE* value_ptr = NS(SELF, try_insert)(self, key, value);
290 DC_ASSERT(value_ptr);
291 return value_ptr;
292}
293
294static VALUE const* NS(SELF, try_read)(SELF const* self, KEY key) {
295 const size_t mask = self->capacity - 1;
296
297 const size_t hash = KEY_HASH(&key);
298 const dc_swiss_id id = dc_swiss_id_from_hash(hash);
299
300 const size_t start = hash & mask;
301
302 for (size_t step = 0;; step += DC_SWISS_SIMD_PROBE_SIZE) {
303 const size_t group = (start + step) & mask;
304
305 for (size_t j = 0; j < DC_SWISS_SIMD_PROBE_SIZE; j++) {
306 const size_t i = (group + j) & mask;
307 const dc_swiss_ctrl c = self->ctrl[i];
308
309 switch (c) {
311 // EMPTY terminates the probe: key does not exist
312 return NULL;
313
316 // Keep probing
317 break;
318
319 default:
320 // Present slot: check fingerprint, then full key
321 if (dc_swiss_ctrl_get_id(c) == id && KEY_EQ(&self->slots[i].key, &key)) {
322 return &self->slots[i].value;
323 }
324 break;
325 }
326 }
327 }
328}
329
330static VALUE const* NS(SELF, read)(SELF const* self, KEY key) {
331 VALUE const* value = NS(SELF, try_read)(self, key);
332 DC_ASSERT(value);
333 return value;
334}
335
336static VALUE* NS(SELF, try_write)(SELF* self, KEY key) {
337 INVARIANT_CHECK(self);
338 return (VALUE*)NS(SELF, try_read)((SELF const*)self, key);
339}
340
341static VALUE* NS(SELF, write)(SELF* self, KEY key) {
342 VALUE* value = NS(SELF, try_write)(self, key);
343 DC_ASSERT(value);
344 return value;
345}
346
347static bool NS(SELF, try_remove)(SELF* self, KEY key, VALUE* destination) {
348 INVARIANT_CHECK(self);
349
350 const size_t mask = self->capacity - 1;
351
352 const size_t hash = KEY_HASH(&key);
353 const dc_swiss_id id = dc_swiss_id_from_hash(hash);
354
355 const size_t start = hash & mask;
356
357 for (size_t step = 0;; step += DC_SWISS_SIMD_PROBE_SIZE) {
358 const size_t group = (start + step) & mask;
359
360 for (size_t j = 0; j < DC_SWISS_SIMD_PROBE_SIZE; j++) {
361 const size_t i = (group + j) & mask;
362 const dc_swiss_ctrl c = self->ctrl[i];
363
364 switch (c) {
366 // EMPTY terminates probe: key not present
367 return false;
368
371 // keep probing
372 break;
373
374 default:
375 // Present slot: check fingerprint, then full key
376 if (dc_swiss_ctrl_get_id(c) != id) {
377 break;
378 }
379 if (!KEY_EQ(&self->slots[i].key, &key)) {
380 break;
381 }
382
383 // Found
384 if (destination != NULL) {
385 *destination = self->slots[i].value;
386 }
387
388 // Mark tombstone (must NOT become EMPTY)
389 dc_swiss_ctrl_set_at(self->ctrl, self->capacity, i, DC_SWISS_VAL_DELETED);
390
391 self->count--;
392 self->tombstones++;
393 return true;
394 }
395 }
396 }
397}
398
399static VALUE NS(SELF, remove)(SELF* self, KEY key) {
400 VALUE value;
401 DC_ASSERT(NS(SELF, try_remove)(self, key, &value));
402 return value;
403}
404
405static void NS(SELF, delete_entry)(SELF* self, KEY key) {
406 VALUE value = NS(SELF, remove)(self, key);
407 VALUE_DELETE(&value);
408}
409
410static size_t NS(SELF, size)(SELF const* self) {
411 INVARIANT_CHECK(self);
412 return self->count;
413}
414
415static void PRIV(NS(SELF, next_populated_index))(SELF const* self, dc_swiss_optional_index* index) {
416 size_t i = *index;
417 while (i < self->capacity && !dc_swiss_is_present(self->ctrl[i])) {
418 ++i;
419 }
420 *index = (i == self->capacity) ? DC_SWISS_NO_INDEX : i;
421}
422
423static void NS(SELF, delete)(SELF* self) {
424
425 for (size_t i = 0; i < self->capacity; i++) {
426 if (dc_swiss_is_present(self->ctrl[i])) {
427 KEY_DELETE(&self->slots[i].key);
428 VALUE_DELETE(&self->slots[i].value);
429 }
430 }
431 NS(ALLOC, free)(self->alloc, self->ctrl);
432 NS(ALLOC, free)(self->alloc, self->slots);
433}
434
435#define ITER_CONST NS(SELF, iter_const)
436#define KV_PAIR_CONST NS(ITER_CONST, item)
437
438typedef struct {
439 SELF const* map;
442} ITER_CONST;
443
444typedef struct {
445 KEY const* key;
446 VALUE const* value;
448
450 return item->key == NULL && item->value == NULL;
451}
452
454 mutation_version_check(&iter->version);
455
456 dc_swiss_optional_index index = iter->next_index;
457 if (index == DC_SWISS_NO_INDEX) {
458 return (KV_PAIR_CONST){
459 .key = NULL,
460 .value = NULL,
461 };
462 }
463
464 iter->next_index++;
465 PRIV(NS(SELF, next_populated_index))(iter->map, &iter->next_index);
466 return (KV_PAIR_CONST){
467 .key = &iter->map->slots[index].key,
468 .value = &iter->map->slots[index].value,
469 };
470}
471
472static ITER_CONST NS(SELF, get_iter_const)(SELF const* self) {
473 INVARIANT_CHECK(self);
474
475 dc_swiss_optional_index index = 0;
476 PRIV(NS(SELF, next_populated_index))(self, &index);
477
478 return (ITER_CONST){
479 .map = self,
480 .next_index = index,
481 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
482 };
483}
484
485static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
486 fprintf(stream, EXPAND_STRING(SELF) "@%p {\n", self);
487 fmt = dc_debug_fmt_scope_begin(fmt);
488
489 dc_debug_fmt_print(fmt, stream, "capacity: %lu,\n", self->capacity);
490 dc_debug_fmt_print(fmt, stream, "tombstones: %lu,\n", self->tombstones);
491 dc_debug_fmt_print(fmt, stream, "count: %lu,\n", self->count);
492
493 dc_debug_fmt_print(fmt, stream, "ctrl: @%p[%lu + simd probe size additional %lu],\n",
494 self->ctrl, self->capacity, DC_SWISS_SIMD_PROBE_SIZE);
495 dc_debug_fmt_print(fmt, stream, "slots: @%p[%lu],\n", self->slots, self->capacity);
496
497 dc_debug_fmt_print(fmt, stream, "alloc: ");
498 NS(ALLOC, debug)(self->alloc, fmt, stream);
499 fprintf(stream, ",\n");
500
501 ITER_CONST iter = NS(SELF, get_iter_const)(self);
503
505 item = NS(ITER_CONST, next)(&iter)) {
506 dc_debug_fmt_print(fmt, stream, "{\n");
507 fmt = dc_debug_fmt_scope_begin(fmt);
508
509 dc_debug_fmt_print(fmt, stream, "key: ");
510 KEY_DEBUG(item.key, fmt, stream);
511 fprintf(stream, ",\n");
512
513 dc_debug_fmt_print(fmt, stream, "value: ");
514 VALUE_DEBUG(item.value, fmt, stream);
515 fprintf(stream, ",\n");
516
517 fmt = dc_debug_fmt_scope_end(fmt);
518 dc_debug_fmt_print(fmt, stream, "},\n");
519 }
520
521 fmt = dc_debug_fmt_scope_end(fmt);
522 dc_debug_fmt_print(fmt, stream, "}");
523}
524
525#undef KV_PAIR_CONST
526#undef ITER_CONST
527
528#define ITER NS(SELF, iter)
529#define KV_PAIR NS(ITER, item)
530
531typedef struct {
532 SELF const* map;
535} ITER;
536
537typedef struct {
538 KEY const* key;
539 VALUE const* value;
540} KV_PAIR;
541
542static bool NS(ITER, empty_item)(KV_PAIR const* item) {
543 return item->key == NULL && item->value == NULL;
544}
545
546static KV_PAIR NS(ITER, next)(ITER* iter) {
547 mutation_version_check(&iter->version);
548
549 dc_swiss_optional_index index = iter->next_index;
550 if (index == DC_SWISS_NO_INDEX) {
551 return (KV_PAIR){
552 .key = NULL,
553 .value = NULL,
554 };
555 }
556
557 iter->next_index++;
558 PRIV(NS(SELF, next_populated_index))(iter->map, &iter->next_index);
559 return (KV_PAIR){
560 .key = &iter->map->slots[index].key,
561 .value = &iter->map->slots[index].value,
562 };
563}
564
565static ITER NS(SELF, get_iter)(SELF* self) {
566 INVARIANT_CHECK(self);
567
568 dc_swiss_optional_index index = 0;
569 PRIV(NS(SELF, next_populated_index))(self, &index);
570
571 return (ITER){
572 .map = self,
573 .next_index = index,
574 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
575 };
576}
577
578#undef KV_PAIR
579#undef ITER
580
581#undef INVARIANT_CHECK
582
583#undef SLOT
584
585#undef VALUE_DEBUG
586#undef VALUE_CLONE
587#undef VALUE_DELETE
588#undef VALUE
589
590#undef KEY_DEBUG
591#undef KEY_CLONE
592#undef KEY_DELETE
593#undef KEY_EQ
594#undef KEY_HASH
595#undef KEY
596
598
static void debug(SELF const *self, dc_debug_fmt fmt, FILE *stream)
Definition template.h:62
static void free(SELF *self, void *ptr)
Definition template.h:56
static void * malloc(SELF *self, size_t size)
Definition template.h:23
static void * calloc(SELF *self, size_t count, size_t size)
Definition template.h:34
#define ALLOC
Definition template.h:64
static INDEX insert(SELF *self, VALUE value)
Definition template.h:127
static ITER_CONST get_iter_const(SELF const *self)
Definition template.h:449
static ITER get_iter(SELF *self)
Definition template.h:370
#define VALUE_DEBUG
Definition template.h:39
static IV_PAIR next(ITER *iter)
Definition template.h:352
static INDEX_TYPE size(SELF const *self)
Definition template.h:252
#define SLOT
Definition template.h:63
#define INVARIANT_CHECK(self)
Definition template.h:88
static VALUE remove(SELF *self, INDEX index)
Definition template.h:292
static bool empty_item(IV_PAIR const *item)
Definition template.h:344
SLOT PRIV(block)[DC_ARENA_CHUNKED_BLOCK_SIZE(BLOCK_INDEX_BITS)]
Definition template.h:73
static VALUE * try_write(SELF *self, INDEX index)
Definition template.h:205
ALLOC alloc_t
Definition template.h:61
static VALUE const * read(SELF const *self, INDEX index)
Definition template.h:199
static VALUE * write(SELF *self, INDEX index)
Definition template.h:209
#define VALUE
Definition template.h:31
#define ITER
Definition template.h:320
#define VALUE_CLONE
Definition template.h:37
#define VALUE_DELETE
Definition template.h:35
static bool try_remove(SELF *self, INDEX index, VALUE *destination)
Definition template.h:264
#define ITER_CONST
Definition template.h:398
static SELF clone(SELF const *self)
Definition template.h:215
static VALUE const * try_read(SELF const *self, INDEX index)
Definition template.h:180
IV_PAIR item
Definition template.h:283
static SELF new_with_capacity_for(INDEX_TYPE items, ALLOC *alloc)
Definition template.h:96
#define KEY_HASH
Definition template.h:26
#define KV_PAIR
Definition template.h:584
#define KEY_DELETE
Definition template.h:35
static VALUE *PRIV try_insert_no_extend_capacity(SELF *self, KEY key, VALUE value)
Definition template.h:198
#define KEY_CLONE
Definition template.h:39
static VALUE * try_insert(SELF *self, KEY key, VALUE value)
Definition template.h:328
#define KEY
A simple swiss table implementation. See the abseil docs for swss table here.
Definition template.h:17
#define KEY_EQ
Definition template.h:31
#define KEY_DEBUG
Definition template.h:43
static void delete_entry(SELF *self, KEY key)
Definition template.h:508
static SELF PRIV new_with_exact_capacity(size_t capacity, ALLOC *alloc)
Definition template.h:157
static void extend_capacity_for(SELF *self, size_t expected_items)
Definition template.h:317
KEY key_t
Definition template.h:68
#define KV_PAIR_CONST
Definition template.h:526
static void PRIV rehash(SELF *self, size_t new_capacity)
Definition template.h:270
static size_t capacity()
Definition template.h:66
static void PRIV next_populated_index(SELF const *self, dc_swiss_optional_index *index)
Definition template.h:415
#define DC_TRAIT_MAP(SELF)
Definition trait.h:5
dc_debug_fmt dc_debug_fmt_scope_end(dc_debug_fmt fmt)
Definition fmt.h:39
dc_debug_fmt dc_debug_fmt_scope_begin(dc_debug_fmt fmt)
Definition fmt.h:33
static void dc_debug_fmt_print(dc_debug_fmt fmt, FILE *stream, const char *format,...)
Definition fmt.h:22
static dc_gdb_marker dc_gdb_marker_new()
Definition gdb_marker.h:7
static void dc_swiss_ctrl_set_at(dc_swiss_ctrl *self, size_t capacity, size_t index, dc_swiss_ctrl val)
Definition utils.h:51
#define DC_SWISS_INITIAL_CAPACITY
Definition utils.h:7
static size_t dc_swiss_capacity(size_t for_items)
Definition utils.h:44
@ DC_SWISS_DO_NOTHING
Definition utils.h:62
@ DC_SWISS_DOUBLE_CAPACITY
Definition utils.h:60
@ DC_SWISS_CLEANUP_TOMBSONES
Definition utils.h:61
static bool dc_swiss_is_present(dc_swiss_ctrl ctrl)
Definition utils.h:24
#define DC_SWISS_VAL_EMPTY
Definition utils.h:18
static dc_swiss_id dc_swiss_id_from_hash(size_t hash)
Definition utils.h:40
uint8_t dc_swiss_id
Definition utils.h:21
#define DC_SWISS_SIMD_PROBE_SIZE
Definition utils.h:8
static uint8_t dc_swiss_ctrl_get_id(dc_swiss_ctrl ctrl)
Definition utils.h:35
size_t dc_swiss_optional_index
Definition utils.h:82
#define DC_SWISS_NO_INDEX
Definition utils.h:81
#define DC_SWISS_VAL_DELETED
Definition utils.h:17
uint8_t dc_swiss_ctrl
Definition utils.h:22
static dc_swiss_rehash_action dc_swiss_heuristic_should_extend(size_t tombstones, size_t count, size_t capacity)
Definition utils.h:65
#define DC_SWISS_VAL_SENTINEL
Definition utils.h:16
#define DC_MATH_IS_POWER_OF_2(x)
Definition math.h:12
static mutation_tracker mutation_tracker_new()
static void mutation_version_check(mutation_version const *self)
static mutation_version mutation_tracker_get(mutation_tracker const *self)
static void mutation_tracker_mutate(mutation_tracker *self)
#define EXPAND_STRING(NAME)
Definition namespace.h:8
#define NS(pre, post)
Definition namespace.h:4
#define DC_ASSERT(expr,...)
Definition panic.h:36
#define DC_ASSUME(expr,...)
Definition panic.h:56
#define TEMPLATE_ERROR(...)
With the user provided name, even in nested templates.
Definition def.h:56
#define SELF
Definition def.h:52
INDEX_TYPE next_index
Definition template.h:403
mutation_version version
Definition template.h:404
INDEX_TYPE next_index
Definition template.h:325
mutation_version version
Definition template.h:326
SELF * map
Definition template.h:400
dc_swiss_ctrl * ctrl
Definition template.h:85
mutation_tracker iterator_invalidation_tracker
Definition template.h:85
SLOT * slots
Definition template.h:71
dc_gdb_marker derive_c_hashmap
Definition template.h:146
size_t tombstones
Definition template.h:83
ALLOC * alloc
Definition template.h:71
VALUE value
Definition template.h:78
KEY key
Definition template.h:77
Debug format helpers for debug printin data structures.
Definition fmt.h:10
tracks a specific version of a value, so that this can be compared later to check modification For ex...
static FILE * stream(SELF *self)
Opens a file for.
Definition template.h:107