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 INDEX_BITS
12 #if !defined DC_PLACEHOLDERS
13TEMPLATE_ERROR("The number of bits (8,16,32,64) to use for the arena's key")
14 #endif
15 #define INDEX_BITS 32
16#endif
17
18#if !defined VALUE
19 #if !defined DC_PLACEHOLDERS
20TEMPLATE_ERROR("The value type to place in the arena must be defined")
21 #endif
22typedef struct {
23 int x;
24} value_t;
25 #define VALUE value_t
26 #define VALUE_DELETE value_delete
27static void VALUE_DELETE(value_t* /* self */) {}
28 #define VALUE_CLONE value_clone
29static value_t VALUE_CLONE(value_t const* self) { return *self; }
30 #define VALUE_DEBUG value_debug
31static void VALUE_DEBUG(VALUE const* /* self */, dc_debug_fmt /* fmt */, FILE* /* stream */) {}
32#endif
33
34DC_STATIC_ASSERT(sizeof(VALUE), "VALUE must be a non-zero sized type");
35
36#if !defined VALUE_DELETE
37 #define VALUE_DELETE DC_NO_DELETE
38#endif
39
40#if !defined VALUE_CLONE
41 #define VALUE_CLONE DC_COPY_CLONE
42#endif
43
44#if !defined VALUE_DEBUG
45 #define VALUE_DEBUG DC_DEFAULT_DEBUG
46#endif
47
50
51#define CHECK_ACCESS_INDEX(self, index) ((index).index < (self)->exclusive_end)
52
53// JUSTIFY: Macro rather than static
54// - Avoids the need to cast to the INDEX_TYPE
55#define RESIZE_FACTOR 2
56
57typedef VALUE NS(SELF, value_t);
58typedef ALLOC NS(SELF, alloc_t);
59
60#define SLOT NS(NAME, slot)
61
62#define SLOT_INDEX_TYPE INDEX_TYPE // [DERIVE-C] for template
63#define SLOT_VALUE VALUE // [DERIVE-C] for template
64#define SLOT_VALUE_CLONE VALUE_CLONE // [DERIVE-C] for template
65#define SLOT_VALUE_CLONE VALUE_CLONE // [DERIVE-C] for template
66#define SLOT_VALUE_DELETE VALUE_DELETE // [DERIVE-C] for template
67#define INTERNAL_NAME SLOT // [DERIVE-C] for template
69
70typedef struct {
72 size_t capacity;
73 INDEX_TYPE free_list;
74
75 // JUSTIFY: Separately recording the first_uninit_entry
76 // - Allows us to not use calloc for the buffer (for < first_uninit_entry
77 // we can check represent, for >= first_uninit_entry we know none are
78 // present so having uninitialised entries is fine)
80
81 // INVARIANT: If free_list == EMPTY_INDEX, then all values from [0, count)
82 // are present
83 size_t count;
84
85 NS(ALLOC, ref) alloc_ref;
86 dc_gdb_marker derive_c_arena_basic;
88} SELF;
89
90#define INVARIANT_CHECK(self) \
91 DC_ASSUME(self); \
92 DC_ASSUME((self)->count <= (self)->capacity); \
93 DC_ASSUME((self)->exclusive_end >= (self)->count); \
94 DC_ASSUME((self)->count <= MAX_INDEX);
95
96DC_PUBLIC static SELF NS(SELF, new_with_capacity_for)(INDEX_TYPE items, NS(ALLOC, ref) alloc_ref) {
97 DC_ASSERT(items > 0, "Cannot create arena with capacity for 0 items {items=%lu}",
98 (size_t)items);
99 size_t capacity = dc_math_next_power_of_2(items);
100 DC_ASSERT(capacity <= CAPACITY_EXCLUSIVE_UPPER,
101 "Index capacity too large {capacity=%lu, max_capacity=%lu}", (size_t)capacity,
102 (size_t)CAPACITY_EXCLUSIVE_UPPER);
103 SLOT* slots = (SLOT*)NS(ALLOC, allocate_zeroed)(alloc_ref, capacity * sizeof(SLOT));
104
105 for (INDEX_TYPE index = 0; index < capacity; index++) {
106 NS(SLOT, memory_tracker_empty)(&slots[index]);
107 }
108
109 return (SELF){
110 .slots = slots,
111 .capacity = (INDEX_TYPE)capacity,
112 .free_list = INDEX_NONE,
113 .exclusive_end = 0,
114 .count = 0,
115 .alloc_ref = alloc_ref,
116 .derive_c_arena_basic = dc_gdb_marker_new(),
117 .iterator_invalidation_tracker = mutation_tracker_new(),
118 };
119}
120
121DC_PUBLIC static INDEX NS(SELF, insert)(SELF* self, VALUE value) {
122 INVARIANT_CHECK(self);
123 DC_ASSERT(self->count < MAX_INDEX,
124 "Arena is full, cannot insert {count=%lu, max_index=%lu, value=%s}",
125 (size_t)self->count, (size_t)MAX_INDEX, DC_DEBUG(VALUE_DEBUG, &value));
126
127 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
128 if (self->free_list != INDEX_NONE) {
129 INDEX_TYPE free_index = self->free_list;
130 SLOT* slot = &self->slots[free_index];
131 DC_ASSUME(!slot->present);
132
133 self->free_list = slot->next_free;
134 NS(SLOT, fill)(slot, value);
135
136 self->count++;
137 return (INDEX){.index = free_index};
138 }
139
140 if (self->exclusive_end == self->capacity) {
141 DC_ASSERT(self->capacity <= (CAPACITY_EXCLUSIVE_UPPER / RESIZE_FACTOR),
142 "Cannot increase capacity for new item {capacity=%lu, max_capacity=%lu, item=%s}",
143 (size_t)self->capacity, (size_t)CAPACITY_EXCLUSIVE_UPPER,
144 DC_DEBUG(VALUE_DEBUG, &value));
145 size_t old_size = self->capacity * sizeof(SLOT);
146 self->capacity *= RESIZE_FACTOR;
147 SLOT* new_alloc = (SLOT*)NS(ALLOC, reallocate)(self->alloc_ref, self->slots, old_size,
148 self->capacity * sizeof(SLOT));
149 self->slots = new_alloc;
150
151 for (size_t index = self->exclusive_end; index < self->capacity; index++) {
152 NS(SLOT, memory_tracker_empty)(&self->slots[index]);
153 }
154 }
155
156 INDEX_TYPE new_index = (INDEX_TYPE)self->exclusive_end;
157 SLOT* slot = &self->slots[new_index];
158 NS(SLOT, fill)(slot, value);
159
160 self->count++;
161 self->exclusive_end++;
162 return (INDEX){.index = new_index};
163}
164
165DC_PUBLIC static VALUE* NS(SELF, try_write)(SELF* self, INDEX index) {
166 INVARIANT_CHECK(self);
167 if (!CHECK_ACCESS_INDEX(self, index)) {
168 return NULL;
169 }
170 SLOT* slot = &self->slots[index.index];
171 if (!slot->present) {
172 return NULL;
173 }
174 return &slot->value;
175}
176
177DC_PUBLIC static VALUE* NS(SELF, write)(SELF* self, INDEX index) {
178 VALUE* value = NS(SELF, try_write)(self, index);
179 DC_ASSERT(value, "Cannot write item, index not found {index=%lu}", (size_t)index.index);
180 return value;
181}
182
183DC_PUBLIC static VALUE const* NS(SELF, try_read)(SELF const* self, INDEX index) {
184 INVARIANT_CHECK(self);
185 if (!CHECK_ACCESS_INDEX(self, index)) {
186 return NULL;
187 }
188 SLOT* slot = &self->slots[index.index];
189 if (!slot->present) {
190 return NULL;
191 }
192 return &slot->value;
193}
194
195DC_PUBLIC static VALUE const* NS(SELF, read)(SELF const* self, INDEX index) {
196 VALUE const* value = NS(SELF, try_read)(self, index);
197 DC_ASSERT(value, "Cannot read item, index not found {index=%lu}", (size_t)index.index);
198 return value;
199}
200
201DC_PUBLIC static SELF NS(SELF, clone)(SELF const* self) {
202 INVARIANT_CHECK(self);
203 SLOT* slots = (SLOT*)NS(ALLOC, allocate_zeroed)(self->alloc_ref, self->capacity * sizeof(SLOT));
204
205 for (INDEX_TYPE index = 0; index < self->exclusive_end; index++) {
206 NS(SLOT, clone_from)(&self->slots[index], &slots[index]);
207 }
208
209 return (SELF){
210 .slots = slots,
211 .capacity = self->capacity,
212 .free_list = self->free_list,
213 .exclusive_end = self->exclusive_end,
214 .count = self->count,
215 .alloc_ref = self->alloc_ref,
216 .derive_c_arena_basic = dc_gdb_marker_new(),
217 .iterator_invalidation_tracker = mutation_tracker_new(),
218 };
219}
220
221DC_PUBLIC static size_t NS(SELF, size)(SELF const* self) {
222 INVARIANT_CHECK(self);
223 return self->count;
224}
225
226DC_PUBLIC static bool NS(SELF, full)(SELF const* self) {
227 INVARIANT_CHECK(self);
228 if (self->capacity == CAPACITY_EXCLUSIVE_UPPER) {
229 if (self->free_list == INDEX_NONE) {
230 return true;
231 }
232 }
233 return false;
234}
235
236DC_PUBLIC static const size_t NS(SELF, max_entries) = MAX_INDEX;
237
238DC_PUBLIC static bool NS(SELF, try_remove)(SELF* self, INDEX index, VALUE* destination) {
239 INVARIANT_CHECK(self);
240 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
241
242 if (!CHECK_ACCESS_INDEX(self, index)) {
243 return false;
244 }
245
246 SLOT* entry = &self->slots[index.index];
247 if (entry->present) {
248 *destination = entry->value;
249
250 NS(SLOT, set_empty)(entry, self->free_list);
251
252 self->free_list = index.index;
253 self->count--;
254 return true;
255 }
256 return false;
257}
258
259DC_PUBLIC static VALUE NS(SELF, remove)(SELF* self, INDEX index) {
260 INVARIANT_CHECK(self);
261 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
262
263 VALUE value;
264 DC_ASSERT(NS(SELF, try_remove)(self, index, &value),
265 "Failed to remove item, index not found {index=%lu}", (size_t)index.index);
266 return value;
267}
268
269#define IV_PAIR NS(SELF, iv)
270typedef struct {
271 INDEX index;
272 VALUE* value;
273} IV_PAIR;
274
275// Provide an empty IV_PAIR sentinel
277 return (IV_PAIR){.index = (INDEX){.index = INDEX_NONE}, .value = NULL};
278}
279
280#define ITER NS(SELF, iter)
281typedef IV_PAIR NS(ITER, item);
282
283DC_PUBLIC static bool NS(ITER, empty_item)(IV_PAIR const* item) { return item->value == NULL; }
284
285typedef struct {
286 SELF* arena;
287 INDEX_TYPE next_index;
288 mutation_version version;
289} ITER;
290
291DC_PUBLIC static bool NS(ITER, empty)(ITER const* iter) {
292 DC_ASSUME(iter);
293 mutation_version_check(&iter->version);
294 // JUSTIFY: If no entries are left, then the previous '.._next' call moved
295 // the index to the exclusive end
296 // NOTE: `index + 1 > exclusive_end` implies `index >= exclusive_end`
297 return iter->next_index == INDEX_NONE || iter->next_index >= iter->arena->exclusive_end;
298}
299
300DC_PUBLIC static IV_PAIR NS(ITER, next)(ITER* iter) {
301 DC_ASSUME(iter);
302 mutation_version_check(&iter->version);
303
304 while (iter->next_index < INDEX_NONE && iter->next_index < iter->arena->exclusive_end) {
305 IV_PAIR result = {
306 .index = (INDEX){.index = iter->next_index},
307 .value = &iter->arena->slots[iter->next_index].value,
308 };
309
310 iter->next_index++;
311 // advance to next present entry
312 while (iter->next_index < INDEX_NONE && iter->next_index < iter->arena->exclusive_end &&
313 !iter->arena->slots[iter->next_index].present) {
314 iter->next_index++;
315 }
316
317 if (result.value && result.value == &iter->arena->slots[result.index.index].value &&
318 iter->arena->slots[result.index.index].present) {
319 return result;
320 }
321 }
322
323 return NS(SELF, iv_empty)();
324}
325
327 INVARIANT_CHECK(self);
328 INDEX_TYPE index = 0;
329 while (index < INDEX_NONE && index < self->exclusive_end && !self->slots[index].present) {
330 index++;
331 }
332
333 return (ITER){
334 .arena = self,
335 .next_index = index,
336 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
337 };
338}
339
340DC_PUBLIC static void NS(SELF, delete)(SELF* self) {
341 INVARIANT_CHECK(self);
342 ITER iter = NS(SELF, get_iter)(self);
343
344 for (IV_PAIR entry = NS(ITER, next)(&iter); !NS(ITER, empty_item)(&entry);
345 entry = NS(ITER, next)(&iter)) {
346 VALUE_DELETE(entry.value);
347 }
348
349 NS(ALLOC, deallocate)(self->alloc_ref, self->slots, self->capacity * sizeof(SLOT));
350}
351
352#undef ITER
353#undef IV_PAIR
354
355#define IV_PAIR_CONST NS(SELF, iv_const)
356typedef struct {
357 INDEX index;
358 VALUE const* value;
360
361// Provide an empty IV_PAIR_CONST sentinel
363 return (IV_PAIR_CONST){.index = (INDEX){.index = INDEX_NONE}, .value = NULL};
364}
365
366#define ITER_CONST NS(SELF, iter_const)
367// Item is now a value (not pointer)
369
370// Empty-item predicate takes a pointer-to-value
372 return item->value == NULL;
373}
374
375typedef struct {
376 SELF const* arena;
377 INDEX_TYPE next_index;
380} ITER_CONST;
381
382DC_PUBLIC static bool NS(ITER_CONST, empty)(ITER_CONST const* iter) {
383 DC_ASSUME(iter);
384 mutation_version_check(&iter->version);
385 return iter->next_index == INDEX_NONE || iter->next_index >= iter->arena->exclusive_end;
386}
387
389 DC_ASSUME(iter);
390 mutation_version_check(&iter->version);
391
392 while (iter->next_index < INDEX_NONE && iter->next_index < iter->arena->exclusive_end) {
393 IV_PAIR_CONST result = {
394 .index = (INDEX){.index = iter->next_index},
395 .value = &iter->arena->slots[iter->next_index].value,
396 };
397 iter->next_index++;
398 while (iter->next_index != INDEX_NONE && iter->next_index < iter->arena->exclusive_end &&
399 !iter->arena->slots[iter->next_index].present) {
400 iter->next_index++;
401 }
402
403 if (result.value && iter->arena->slots[result.index.index].present) {
404 return result;
405 }
406 }
407
408 return NS(SELF, iv_const_empty)();
409}
410
412 INVARIANT_CHECK(self);
413 INDEX_TYPE index = 0;
414 while (index < INDEX_NONE && index < self->exclusive_end && !self->slots[index].present) {
415 index++;
416 }
417
418 return (ITER_CONST){
419 .arena = self,
420 .next_index = index,
421 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
422 };
423}
424
425DC_PUBLIC static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
426 fprintf(stream, DC_EXPAND_STRING(SELF) "@%p {\n", (void*)self);
427 fmt = dc_debug_fmt_scope_begin(fmt);
428 dc_debug_fmt_print(fmt, stream, "capacity: %lu,\n", self->capacity);
429 dc_debug_fmt_print(fmt, stream, "count: %lu,\n", self->count);
430 dc_debug_fmt_print(fmt, stream, "slots: %p,\n", (void*)self->slots);
431
432 dc_debug_fmt_print(fmt, stream, "alloc: ");
433 NS(ALLOC, debug)(NS(NS(ALLOC, ref), deref)(self->alloc_ref), fmt, stream);
434 fprintf(stream, ",\n");
435
436 dc_debug_fmt_print(fmt, stream, "items: [");
437 fmt = dc_debug_fmt_scope_begin(fmt);
438
439 ITER_CONST iter = NS(SELF, get_iter_const)(self);
441 item = NS(ITER_CONST, next)(&iter)) {
442 dc_debug_fmt_print(fmt, stream, "{\n");
443 fmt = dc_debug_fmt_scope_begin(fmt);
444
445 dc_debug_fmt_print(fmt, stream, "key: ");
446 NS(INDEX, debug)(&item.index, fmt, stream);
447 fprintf(stream, ",\n");
448
449 dc_debug_fmt_print(fmt, stream, "value: ");
450 VALUE_DEBUG(item.value, fmt, stream);
451 fprintf(stream, ",\n");
452
453 fmt = dc_debug_fmt_scope_end(fmt);
454 dc_debug_fmt_print(fmt, stream, "},\n");
455 }
456
457 fmt = dc_debug_fmt_scope_end(fmt);
458 dc_debug_fmt_print(fmt, stream, "],\n");
459 fmt = dc_debug_fmt_scope_end(fmt);
460 dc_debug_fmt_print(fmt, stream, "}");
461}
462
463#undef ITER_CONST
464#undef IV_PAIR_CONST
465
466#undef INVARIANT_CHECK
467#undef SLOT
468#undef RESIZE_FACTOR
469#undef CHECK_ACCESS_INDEX
470
473
474#undef VALUE_DEBUG
475#undef VALUE_CLONE
476#undef VALUE_DELETE
477#undef VALUE
478
479#undef INDEX_BITS
480
482
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 * reallocate(SELF *self, void *ptr, size_t old_size, size_t new_size)
Definition template.h:137
#define ALLOC
Definition template.h:31
#define VALUE
Definition template.h:35
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
static DC_PUBLIC IV_PAIR iv_empty()
Definition template.h:343
static DC_PUBLIC const size_t max_entries
Definition template.h:262
#define VALUE_DEBUG
Definition template.h:41
#define IV_PAIR
Definition template.h:323
#define SLOT
Definition template.h:65
#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 bool full(SELF const *self)
Definition template.h:257
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
#define IV_PAIR_CONST
Definition template.h:412
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 IV_PAIR_CONST iv_const_empty()
Definition template.h:432
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
#define CHECK_ACCESS_INDEX(self, index)
Definition template.h:51
static DC_PUBLIC SELF new_with_capacity_for(INDEX_TYPE items, ref alloc_ref)
Definition template.h:96
#define RESIZE_FACTOR
Definition template.h:55
#define DC_TRAIT_ARENA(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 DC_INLINE DC_CONST size_t dc_math_next_power_of_2(size_t x)
Definition math.h:46
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 DC_ASSERT(expr,...)
Definition panic.h:37
#define DC_STATIC_ASSERT
Definition panic.h:22
#define DC_ASSUME(expr,...)
Definition panic.h:57
mutation_version version
Definition template.h:417
IV_PAIR_CONST curr
Definition template.h:378
VALUE const * value
Definition template.h:429
VALUE * value
Definition template.h:340
INDEX index
Definition template.h:339
size_t capacity
Definition template.h:72
mutation_tracker iterator_invalidation_tracker
Definition template.h:87
INDEX_TYPE free_list
Definition template.h:79
size_t exclusive_end
Definition template.h:79
dc_gdb_marker derive_c_arena_basic
Definition template.h:86
SLOT * slots
Definition template.h:71
size_t count
Definition template.h:78
ref alloc_ref
Definition template.h:49
VALUE value
Definition template.h:78
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...
int x
Definition template.h:23
static DC_INTERNAL void clone_from(SELF const *from_slot, SELF *to_slot)
Definition template.h:94
static DC_INTERNAL void set_empty(SELF *slot, SLOT_INDEX_TYPE next_free)
Definition template.h:73
static DC_INTERNAL void fill(SELF *slot, SLOT_VALUE value)
Definition template.h:88
static DC_INTERNAL void memory_tracker_empty(SELF const *slot)
Definition template.h:64
static DC_PUBLIC FILE * stream(SELF *self)
Definition template.h:108