Derive-C
Loading...
Searching...
No Matches
template.h
Go to the documentation of this file.
1
2
4#if !defined(SKIP_INCLUDES)
5 #include "includes.h"
6#endif
7
10
11#if !defined KEY
12 #if !defined DC_PLACEHOLDERS
13TEMPLATE_ERROR("No KEY")
14 #endif
15 #define KEY map_key_t
16typedef size_t KEY;
17#endif
18
19#if !defined KEY_HASH
20 #if !defined DC_PLACEHOLDERS
21TEMPLATE_ERROR("No KEY_HASH")
22 #endif
23
24 #define KEY_HASH key_hash
25static size_t KEY_HASH(KEY const* key) { return *key; }
26#endif
27
28#if !defined KEY_EQ
29 #define KEY_EQ DC_MEM_EQ
30#endif
31
32#if !defined KEY_DELETE
33 #define KEY_DELETE DC_NO_DELETE
34#endif
35
36#if !defined KEY_CLONE
37 #define KEY_CLONE DC_COPY_CLONE
38#endif
39
40#if !defined KEY_DEBUG
41 #define KEY_DEBUG DC_DEFAULT_DEBUG
42#endif
43
44#if !defined VALUE
45 #if !defined DC_PLACEHOLDERS
46TEMPLATE_ERROR("No VALUE")
47 #endif
48typedef struct {
49 int x;
50} value_t;
51 #define VALUE value_t
52#endif
53
54#if !defined VALUE_DELETE
55 #define VALUE_DELETE DC_NO_DELETE
56#endif
57
58#if !defined VALUE_CLONE
59 #define VALUE_CLONE DC_COPY_CLONE
60#endif
61
62#if !defined VALUE_DEBUG
63 #define VALUE_DEBUG DC_DEFAULT_DEBUG
64#endif
65
66typedef KEY NS(SELF, key_t);
67typedef VALUE NS(SELF, value_t);
68typedef ALLOC NS(SELF, alloc_t);
69
70#define KEY_ENTRY NS(SELF, key_entry)
71typedef struct {
72 bool present;
75} KEY_ENTRY;
76
77typedef struct {
78 size_t capacity; // INVARIANT: A power of 2
79 size_t items;
82 NS(ALLOC, ref) alloc_ref;
83 dc_gdb_marker derive_c_hashmap;
85} SELF;
86
87#define INVARIANT_CHECK(self) \
88 DC_ASSUME(self); \
89 DC_ASSUME(DC_MATH_IS_POWER_OF_2((self)->capacity)); \
90 DC_ASSUME((self)->keys); \
91 DC_ASSUME((self)->values);
92
94
95DC_PUBLIC static SELF NS(SELF, new_with_capacity_for)(size_t capacity, NS(ALLOC, ref) alloc_ref) {
96 DC_ASSERT(capacity > 0, "Cannot create map with capacity for 0 items {capacity=%lu}", capacity);
97 size_t const real_capacity = dc_apply_capacity_policy(capacity);
98 DC_ASSUME(real_capacity > 0);
99
100 // JUSTIFY: calloc of keys
101 // - A cheap way to get all precense flags as zeroed (os & allocater supported get zeroed page)
102 // - for the values, we do not need this (no precense checks are done on values)
103 KEY_ENTRY* keys =
104 (KEY_ENTRY*)NS(ALLOC, allocate_zeroed)(alloc_ref, sizeof(KEY_ENTRY) * real_capacity);
105 VALUE* values = (VALUE*)NS(ALLOC, allocate_uninit)(alloc_ref, sizeof(VALUE) * real_capacity);
106
107 // JUSTIFY: no access for values & but keys are fine
108 // - Keys are calloced/zeroed as we use this for item lookup, therefore it is valid to read
109 // them.
110 // - Values are only accessed when the corresponding key is present, so we can mark them as
111 // deleted.
113 sizeof(VALUE) * real_capacity);
114
115 return (SELF){
116 .capacity = real_capacity,
117 .items = 0,
118 .keys = keys,
119 .values = values,
120 .alloc_ref = alloc_ref,
121 .derive_c_hashmap = dc_gdb_marker_new(),
122 .iterator_invalidation_tracker = mutation_tracker_new(),
123 };
124}
125
126DC_PUBLIC static SELF NS(SELF, new)(NS(ALLOC, ref) alloc_ref) {
128}
129
130DC_PUBLIC static SELF NS(SELF, clone)(SELF const* self) {
131 INVARIANT_CHECK(self);
132
133 // JUSTIFY: Naive copy
134 // - We could resize (potentially a smaller map) and rehash
135 // - Not confident it would be any better than just a copy.
136 // JUSTIFY: Individually copy keys
137 // - Many entries are zeroed, no need to copy uninit data
138
139 KEY_ENTRY* keys =
140 (KEY_ENTRY*)NS(ALLOC, allocate_zeroed)(self->alloc_ref, sizeof(KEY_ENTRY) * self->capacity);
141 VALUE* values =
142 (VALUE*)NS(ALLOC, allocate_uninit)(self->alloc_ref, sizeof(VALUE) * self->capacity);
143
144 for (size_t i = 0; i < self->capacity; i++) {
145 if (self->keys[i].present) {
146 KEY_ENTRY const* old_entry = &self->keys[i];
147 keys[i] = (KEY_ENTRY){
148 .present = true,
149 .distance_from_desired = old_entry->distance_from_desired,
150 .key = KEY_CLONE(&old_entry->key),
151 };
152 values[i] = VALUE_CLONE(&self->values[i]);
153 } else {
155 &values[i], sizeof(VALUE));
156 }
157 }
158
159 return (SELF){
160 .capacity = self->capacity,
161 .items = self->items,
162 .keys = keys,
163 .values = values,
164 .alloc_ref = self->alloc_ref,
165 .derive_c_hashmap = dc_gdb_marker_new(),
166 .iterator_invalidation_tracker = mutation_tracker_new(),
167 };
168}
169
171 uint16_t distance_from_desired = 0;
172 size_t const hash = KEY_HASH(&key);
173 size_t index = dc_math_modulus_power_of_2_capacity(hash, self->capacity);
174
175 VALUE* inserted_to_entry = NULL;
176
177 for (;;) {
178 KEY_ENTRY* entry = &self->keys[index];
179 DC_ASSUME(distance_from_desired < self->capacity);
180
181 if (entry->present) {
182 if (KEY_EQ(&entry->key, &key)) {
183 return NULL;
184 }
185
186 if (entry->distance_from_desired < distance_from_desired) {
187 KEY const switch_key = entry->key;
188 uint16_t const switch_distance_from_desired = entry->distance_from_desired;
189 VALUE const switch_value = self->values[index];
190
191 entry->key = key;
192 entry->distance_from_desired = distance_from_desired;
193 self->values[index] = value;
194
195 key = switch_key;
196 distance_from_desired = switch_distance_from_desired;
197 value = switch_value;
198
199 if (!inserted_to_entry) {
200 inserted_to_entry = &self->values[index];
201 }
202 }
203
204 distance_from_desired++;
205 index = dc_math_modulus_power_of_2_capacity(index + 1, self->capacity);
206 } else {
207 entry->present = true;
208 entry->distance_from_desired = distance_from_desired;
209 entry->key = key;
210
212 &self->values[index], sizeof(VALUE));
213
214 self->values[index] = value;
215
216 if (!inserted_to_entry) {
217 inserted_to_entry = &self->values[index];
218 }
219
220 self->items++;
221 return inserted_to_entry;
222 }
223 }
224}
225
226DC_PUBLIC static void NS(SELF, extend_capacity_for)(SELF* self, size_t expected_items) {
227 INVARIANT_CHECK(self);
228
229 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
230 size_t const target_capacity = dc_apply_capacity_policy(expected_items);
231 if (target_capacity > self->capacity) {
232 SELF new_map = NS(SELF, new_with_capacity_for)(expected_items, self->alloc_ref);
233 for (size_t index = 0; index < self->capacity; index++) {
234 KEY_ENTRY* entry = &self->keys[index];
235 if (entry->present) {
236 PRIV(NS(SELF, try_insert_no_extend_capacity))(&new_map, entry->key,
237 self->values[index]);
238 }
239 }
240 NS(ALLOC, deallocate)(self->alloc_ref, (void*)self->keys,
241 self->capacity * sizeof(KEY_ENTRY));
242 NS(ALLOC, deallocate)(self->alloc_ref, (void*)self->values, self->capacity * sizeof(VALUE));
243
244 INVARIANT_CHECK(&new_map);
245 *self = new_map;
246 }
247}
248
249DC_PUBLIC static VALUE* NS(SELF, try_insert)(SELF* self, KEY key, VALUE value) {
250 INVARIANT_CHECK(self);
251 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
252
253 if (self->items >= NS(SELF, max_capacity)) {
254 return NULL;
255 }
256
257 if (dc_apply_capacity_policy(self->items) > self->capacity / 2) {
258 NS(SELF, extend_capacity_for)(self, self->items * 2);
259 }
260
261 return PRIV(NS(SELF, try_insert_no_extend_capacity))(self, key, value);
262}
263
264DC_PUBLIC static VALUE* NS(SELF, insert)(SELF* self, KEY key, VALUE value) {
265 VALUE* value_ptr = NS(SELF, try_insert)(self, key, value);
266 DC_ASSERT(value_ptr, "Failed to insert item {key=%s, value=%s}", DC_DEBUG(KEY_DEBUG, &key),
267 DC_DEBUG(VALUE_DEBUG, &value));
268 return value_ptr;
269}
270
271DC_PUBLIC static VALUE* NS(SELF, try_write)(SELF* self, KEY key) {
272 INVARIANT_CHECK(self);
273 size_t const hash = KEY_HASH(&key);
274 size_t index = dc_math_modulus_power_of_2_capacity(hash, self->capacity);
275
276 for (;;) {
277 KEY_ENTRY* entry = &self->keys[index];
278 if (entry->present) {
279 if (KEY_EQ(&entry->key, &key)) {
280 return &self->values[index];
281 }
282 index = dc_math_modulus_power_of_2_capacity(index + 1, self->capacity);
283 } else {
284 return NULL;
285 }
286 }
287}
288
289DC_PUBLIC static VALUE* NS(SELF, write)(SELF* self, KEY key) {
290 VALUE* value = NS(SELF, try_write)(self, key);
291 DC_ASSERT(value, "Cannot write item {key=%s}", DC_DEBUG(KEY_DEBUG, &key));
292 return value;
293}
294
295DC_PUBLIC static VALUE const* NS(SELF, try_read)(SELF const* self, KEY key) {
296 INVARIANT_CHECK(self);
297 size_t const hash = KEY_HASH(&key);
298 size_t index = dc_math_modulus_power_of_2_capacity(hash, self->capacity);
299
300 for (;;) {
301 KEY_ENTRY* entry = &self->keys[index];
302 if (entry->present) {
303 if (KEY_EQ(&entry->key, &key)) {
304 return &self->values[index];
305 }
306 index = dc_math_modulus_power_of_2_capacity(index + 1, self->capacity);
307 } else {
308 return NULL;
309 }
310 }
311}
312
313DC_PUBLIC static VALUE const* NS(SELF, read)(SELF const* self, KEY key) {
314 VALUE const* value = NS(SELF, try_read)(self, key);
315 DC_ASSERT(value, "Cannot read item {key=%s}", DC_DEBUG(KEY_DEBUG, &key));
316 return value;
317}
318
319DC_PUBLIC static bool NS(SELF, try_remove)(SELF* self, KEY key, VALUE* destination) {
320 INVARIANT_CHECK(self);
321 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
322 size_t const hash = KEY_HASH(&key);
323 size_t index = dc_math_modulus_power_of_2_capacity(hash, self->capacity);
324
325 for (;;) {
326 KEY_ENTRY* entry = &self->keys[index];
327 if (entry->present) {
328 if (KEY_EQ(&entry->key, &key)) {
329 self->items--;
330
331 *destination = self->values[index];
332 KEY_DELETE(&entry->key);
333
334 // NOTE: For robin hood hashing, we need probe chains to be unbroken
335 // - Need to find the entries that might use this chain (
336 // distance_to_desired > 0 until the next not-present or
337 // distance_to_desired=0 slot)
338 // hence we shift entries the left (being careful with modulus index)
339
340 size_t free_index = index;
341 KEY_ENTRY* free_entry = entry;
342
343 size_t check_index =
344 dc_math_modulus_power_of_2_capacity(free_index + 1, self->capacity);
345 KEY_ENTRY* check_entry = &self->keys[check_index];
346
347 while (check_entry->present && (check_entry->distance_from_desired > 0)) {
348 free_entry->key = check_entry->key;
349 free_entry->distance_from_desired = check_entry->distance_from_desired - 1;
350 self->values[free_index] = self->values[check_index];
351
352 free_index = check_index;
353 free_entry = check_entry;
354
355 check_index =
356 dc_math_modulus_power_of_2_capacity(free_index + 1, self->capacity);
357 check_entry = &self->keys[check_index];
358 }
359
360 // JUSTIFY: Only setting free entry to false
361 // - We remove, then shift down an index
362 // - The removed entry already has the flag set
363 // - the free entry was the last one removed/moved down an index, so it
364 // should be false.
365
366 free_entry->present = false;
368 &self->values[free_index], sizeof(VALUE));
369
370 return true;
371 }
372 index = dc_math_modulus_power_of_2_capacity(index + 1, self->capacity);
373 } else {
374 return false;
375 }
376 }
377}
378
379DC_PUBLIC static VALUE NS(SELF, remove)(SELF* self, KEY key) {
380 VALUE value;
381 DC_ASSERT(NS(SELF, try_remove)(self, key, &value), "Failed to remove item {key=%s}",
382 DC_DEBUG(KEY_DEBUG, &key));
383 return value;
384}
385
386DC_PUBLIC static void NS(SELF, delete_entry)(SELF* self, KEY key) {
387 VALUE value = NS(SELF, remove)(self, key);
388 VALUE_DELETE(&value);
389}
390
391DC_PUBLIC static size_t NS(SELF, size)(SELF const* self) {
392 INVARIANT_CHECK(self);
393 return self->items;
394}
395
396#define KV_PAIR NS(SELF, kv)
397
398typedef struct {
399 KEY const* key;
401} KV_PAIR;
402
403#define ITER NS(SELF, iter)
404typedef KV_PAIR NS(ITER, item);
405
406DC_PUBLIC static bool NS(ITER, empty_item)(KV_PAIR const* item) {
407 return item->key == NULL && item->value == NULL;
408}
409
410typedef struct {
412 size_t index;
415} ITER;
416
417DC_PUBLIC static KV_PAIR NS(ITER, next)(ITER* iter) {
418 DC_ASSUME(iter);
419 mutation_version_check(&iter->version);
420 if (iter->index < iter->map->capacity) {
421 iter->curr = (KV_PAIR){.key = &iter->map->keys[iter->index].key,
422 .value = &iter->map->values[iter->index]};
423 iter->index++;
424 while (iter->index < iter->map->capacity && !iter->map->keys[iter->index].present) {
425 iter->index++;
426 }
427 return iter->curr;
428 }
429 return (KV_PAIR){.key = NULL, .value = NULL};
430}
431
432DC_PUBLIC static bool NS(ITER, empty)(ITER const* iter) {
433 DC_ASSUME(iter);
434 mutation_version_check(&iter->version);
435 return iter->index >= iter->map->capacity;
436}
437
439 DC_ASSUME(self);
440 size_t first_index = 0;
441 while (first_index < self->capacity && !self->keys[first_index].present) {
442 first_index++;
443 }
444 return (ITER){
445 .map = self,
446 .index = first_index,
447 .curr = (KV_PAIR){.key = NULL, .value = NULL},
448 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
449 };
450}
451
452DC_PUBLIC static void NS(SELF, delete)(SELF* self) {
453 DC_ASSUME(self);
454
455 for (size_t i = 0; i < self->capacity; i++) {
456 if (self->keys[i].present) {
457 KEY_DELETE(&self->keys[i].key);
458 VALUE_DELETE(&self->values[i]);
459 }
460 }
461
462 NS(ALLOC, deallocate)(self->alloc_ref, (void*)self->keys, self->capacity * sizeof(KEY_ENTRY));
463 NS(ALLOC, deallocate)(self->alloc_ref, (void*)self->values, self->capacity * sizeof(VALUE));
464}
465
466#undef ITER
467#undef KV_PAIR
468
469#define KV_PAIR_CONST NS(SELF, kv_const)
470
471typedef struct {
472 KEY const* key;
473 VALUE const* value;
475
476#define ITER_CONST NS(SELF, iter_const)
478
480 return item->key == NULL && item->value == NULL;
481}
482
483typedef struct {
484 SELF const* map;
485 size_t index;
488} ITER_CONST;
489
491 DC_ASSUME(iter);
492 mutation_version_check(&iter->version);
493 if (iter->index < iter->map->capacity) {
494 iter->curr = (KV_PAIR_CONST){.key = &iter->map->keys[iter->index].key,
495 .value = &iter->map->values[iter->index]};
496 iter->index++;
497 while (iter->index < iter->map->capacity && !iter->map->keys[iter->index].present) {
498 iter->index++;
499 }
500 return iter->curr;
501 }
502 return (KV_PAIR_CONST){.key = NULL, .value = NULL};
503}
504
505DC_PUBLIC static bool NS(ITER_CONST, empty)(ITER_CONST const* iter) {
506 DC_ASSUME(iter);
507 mutation_version_check(&iter->version);
508 return iter->index >= iter->map->capacity;
509}
510
512 DC_ASSUME(self);
513 size_t first_index = 0;
514 while (first_index < self->capacity && !self->keys[first_index].present) {
515 first_index++;
516 }
517 return (ITER_CONST){
518 .map = self,
519 .index = first_index,
520 .curr = (KV_PAIR_CONST){.key = NULL, .value = NULL},
521 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
522 };
523}
524
525DC_PUBLIC static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
526 fprintf(stream, DC_EXPAND_STRING(SELF) "@%p {\n", (void*)self);
527 fmt = dc_debug_fmt_scope_begin(fmt);
528
529 dc_debug_fmt_print(fmt, stream, "capacity: %zu,\n", self->capacity);
530 dc_debug_fmt_print(fmt, stream, "size: %zu,\n", NS(SELF, size)(self));
531
532 dc_debug_fmt_print(fmt, stream, "keys: @%p,\n", (void*)self->keys);
533 dc_debug_fmt_print(fmt, stream, "values: @%p,\n", (void*)self->values);
534
535 dc_debug_fmt_print(fmt, stream, "alloc: ");
536 NS(ALLOC, debug)(NS(NS(ALLOC, ref), deref)(self->alloc_ref), fmt, stream);
537 fprintf(stream, ",\n");
538
539 dc_debug_fmt_print(fmt, stream, "items: [\n");
540 fmt = dc_debug_fmt_scope_begin(fmt);
541
542 ITER_CONST iter = NS(SELF, get_iter_const)(self);
544 item = NS(ITER_CONST, next)(&iter)) {
545 dc_debug_fmt_print(fmt, stream, "{\n");
546 fmt = dc_debug_fmt_scope_begin(fmt);
547
548 dc_debug_fmt_print(fmt, stream, "key: ");
549 KEY_DEBUG(item.key, fmt, stream);
550 fprintf(stream, ",\n");
551
552 dc_debug_fmt_print(fmt, stream, "value: ");
553 VALUE_DEBUG(item.value, fmt, stream);
554 fprintf(stream, ",\n");
555
556 fmt = dc_debug_fmt_scope_end(fmt);
557 dc_debug_fmt_print(fmt, stream, "},\n");
558 }
559
560 fmt = dc_debug_fmt_scope_end(fmt);
561 dc_debug_fmt_print(fmt, stream, "],\n");
562 fmt = dc_debug_fmt_scope_end(fmt);
563 dc_debug_fmt_print(fmt, stream, "}");
564}
565
566#undef ITER_CONST
567#undef KV_PAIR_CONST
568
569#undef INVARIANT_CHECK
570#undef KEY_ENTRY
571
572#undef VALUE_DEBUG
573#undef VALUE_CLONE
574#undef VALUE_DELETE
575#undef VALUE
576
577#undef KEY_DEBUG
578#undef KEY_CLONE
579#undef KEY_DELETE
580#undef KEY_EQ
581#undef KEY_HASH
582#undef KEY
583
585
static DC_PUBLIC void deallocate(SELF *self, void *ptr, size_t size)
Definition template.h:127
static DC_PUBLIC void debug(SELF const *self, dc_debug_fmt fmt, FILE *stream)
Definition template.h:212
static DC_PUBLIC void * allocate_zeroed(SELF *self, size_t size)
Definition template.h:117
static DC_PUBLIC void * allocate_uninit(SELF *self, size_t size)
Definition template.h:92
#define ALLOC
Definition template.h:31
#define KEY
Definition template.h:32
#define VALUE
Definition template.h:35
#define KEY_DEBUG
Definition template.h:34
#define DC_STATIC_CONSTANT
Definition attributes.h:23
static DC_PUBLIC VALUE const * try_read(SELF const *self, INDEX index)
Definition template.h:180
static DC_PUBLIC VALUE * try_write(SELF *self, INDEX index)
Definition template.h:205
#define VALUE_DEBUG
Definition template.h:41
#define INVARIANT_CHECK(self)
Definition template.h:90
static DC_PUBLIC IV_PAIR next(ITER *iter)
Definition template.h:355
static DC_PUBLIC INDEX insert(SELF *self, VALUE value)
Definition template.h:126
static DC_PUBLIC bool empty(ITER const *iter)
Definition template.h:349
static DC_PUBLIC VALUE const * read(SELF const *self, INDEX index)
Definition template.h:199
static DC_PUBLIC bool try_remove(SELF *self, INDEX index, VALUE *destination)
Definition template.h:264
ALLOC alloc_t
Definition template.h:63
static DC_PUBLIC VALUE * write(SELF *self, INDEX index)
Definition template.h:209
static DC_PUBLIC bool empty_item(IV_PAIR const *item)
Definition template.h:347
static DC_PUBLIC VALUE remove(SELF *self, INDEX index)
Definition template.h:292
#define ITER
Definition template.h:322
#define VALUE_CLONE
Definition template.h:39
static DC_PUBLIC ITER get_iter(SELF *self)
Definition template.h:373
#define VALUE_DELETE
Definition template.h:37
#define ITER_CONST
Definition template.h:411
static DC_PUBLIC ITER_CONST get_iter_const(SELF const *self)
Definition template.h:464
static DC_PUBLIC size_t size(SELF const *self)
Definition template.h:252
static DC_PUBLIC SELF clone(SELF const *self)
Definition template.h:215
IV_PAIR item
Definition template.h:281
static DC_PUBLIC SELF new_with_capacity_for(INDEX_TYPE items, ref alloc_ref)
Definition template.h:96
#define KEY_HASH
Definition template.h:26
#define KV_PAIR
Definition template.h:585
#define KEY_DELETE
Definition template.h:35
static DC_PUBLIC void extend_capacity_for(SELF *self, size_t expected_items)
Definition template.h:307
#define KEY_CLONE
Definition template.h:39
DC_STATIC_CONSTANT size_t max_capacity
Definition template.h:130
static DC_INTERNAL VALUE *PRIV try_insert_no_extend_capacity(SELF *self, KEY key, VALUE value)
Definition template.h:190
static DC_PUBLIC void delete_entry(SELF *self, KEY key)
Definition template.h:504
static DC_PUBLIC VALUE * try_insert(SELF *self, KEY key, VALUE value)
Definition template.h:318
#define KEY_EQ
Definition template.h:31
#define KEY_DEBUG
Definition template.h:43
KEY key_t
Definition template.h:68
#define KV_PAIR_CONST
Definition template.h:522
#define KEY_ENTRY
Definition template.h:70
#define KEY
A simple open-addressed hashmap using robin-hood hashing.
Definition template.h:15
#define VALUE
Definition template.h:54
#define DC_TRAIT_MAP(SELF)
Definition trait.h:5
#define TEMPLATE_ERROR(...)
With the user provided name, even in nested templates.
Definition def.h:56
#define SELF
Definition def.h:52
#define DC_DEBUG(DEBUG_FN, DEBUG_PTR)
Definition dump.h:92
static DC_PUBLIC void dc_debug_fmt_print(dc_debug_fmt fmt, FILE *stream, const char *format,...)
Definition fmt.h:32
static DC_PUBLIC dc_debug_fmt dc_debug_fmt_scope_end(dc_debug_fmt fmt)
Definition fmt.h:57
static DC_PUBLIC dc_debug_fmt dc_debug_fmt_scope_begin(dc_debug_fmt fmt)
Definition fmt.h:50
static DC_PUBLIC dc_gdb_marker dc_gdb_marker_new()
Definition gdb_marker.h:8
static size_t dc_apply_capacity_policy(size_t capacity)
Definition utils.h:7
static size_t const DC_INITIAL_CAPACITY
Definition utils.h:12
static DC_PUBLIC size_t DC_INLINE DC_CONST dc_math_modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition math.h:61
static DC_PUBLIC void dc_memory_tracker_set(dc_memory_tracker_level level, dc_memory_tracker_capability cap, const volatile void *addr, size_t size)
@ DC_MEMORY_TRACKER_LVL_CONTAINER
@ DC_MEMORY_TRACKER_CAP_WRITE
@ DC_MEMORY_TRACKER_CAP_NONE
static DC_PUBLIC void mutation_tracker_mutate(mutation_tracker *self)
static DC_PUBLIC void mutation_version_check(mutation_version const *self)
static DC_PUBLIC mutation_tracker mutation_tracker_new()
static DC_PUBLIC mutation_version mutation_tracker_get(mutation_tracker const *self)
#define DC_PUBLIC
Definition namespace.h:25
#define NS(pre, post)
Definition namespace.h:14
#define DC_EXPAND_STRING(NAME)
Definition namespace.h:6
#define PRIV(name)
Definition namespace.h:20
#define DC_ASSERT(expr,...)
Definition panic.h:37
#define DC_ASSUME(expr,...)
Definition panic.h:57
SELF const * map
Definition template.h:484
size_t index
Definition template.h:485
mutation_version version
Definition template.h:417
IV_PAIR_CONST curr
Definition template.h:378
mutation_version version
Definition template.h:328
SELF * map
Definition template.h:411
KV_PAIR curr
Definition template.h:413
size_t index
Definition template.h:412
bool present
Definition template.h:72
KEY key
Definition template.h:74
uint16_t distance_from_desired
Definition template.h:73
VALUE const * value
Definition template.h:593
mutation_tracker iterator_invalidation_tracker
Definition template.h:87
size_t items
Definition template.h:79
dc_gdb_marker derive_c_hashmap
Definition template.h:138
VALUE * values
Definition template.h:81
ref alloc_ref
Definition template.h:49
KEY_ENTRY * keys
Definition template.h:80
Debug format helpers for debug printin data structures.
Definition fmt.h:11
tracks a specific version of a value, so that this can be compared later to check modification For ex...
static DC_PUBLIC FILE * stream(SELF *self)
Definition template.h:108