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 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 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);
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;
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 ALLOC* alloc;
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
97 DC_ASSERT(items > 0);
98 size_t capacity = dc_math_next_power_of_2(items);
99 DC_ASSERT(capacity <= CAPACITY_EXCLUSIVE_UPPER);
100 SLOT* slots = (SLOT*)NS(ALLOC, calloc)(alloc, capacity, sizeof(SLOT));
101 DC_ASSERT(slots);
102
103 for (INDEX_TYPE index = 0; index < capacity; index++) {
104 NS(SLOT, memory_tracker_empty)(&slots[index]);
105 }
106
107 return (SELF){
108 .slots = slots,
109 .capacity = (INDEX_TYPE)capacity,
110 .free_list = INDEX_NONE,
111 .exclusive_end = 0,
112 .count = 0,
113 .alloc = alloc,
114 .derive_c_arena_basic = dc_gdb_marker_new(),
115 .iterator_invalidation_tracker = mutation_tracker_new(),
116 };
117}
118
119static INDEX NS(SELF, insert)(SELF* self, VALUE value) {
120 INVARIANT_CHECK(self);
121 DC_ASSERT(self->count < MAX_INDEX);
122
123 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
124 if (self->free_list != INDEX_NONE) {
125 INDEX_TYPE free_index = self->free_list;
126 SLOT* slot = &self->slots[free_index];
127 DC_ASSUME(!slot->present);
128 self->free_list = slot->next_free;
129 slot->present = true;
131 slot->value = value;
132 self->count++;
133 return (INDEX){.index = free_index};
134 }
135
136 if (self->exclusive_end == self->capacity) {
137 DC_ASSERT(self->capacity <= (CAPACITY_EXCLUSIVE_UPPER / RESIZE_FACTOR));
138 self->capacity *= RESIZE_FACTOR;
139 SLOT* new_alloc =
140 (SLOT*)NS(ALLOC, realloc)(self->alloc, self->slots, self->capacity * sizeof(SLOT));
141 DC_ASSERT(new_alloc);
142 self->slots = new_alloc;
143
144 for (size_t index = self->exclusive_end; index < self->capacity; index++) {
145 NS(SLOT, memory_tracker_empty)(&self->slots[index]);
146 }
147 }
148
149 INDEX_TYPE new_index = self->exclusive_end;
150 SLOT* slot = &self->slots[new_index];
151 slot->present = true;
153 slot->value = value;
154 self->count++;
155 self->exclusive_end++;
156 return (INDEX){.index = new_index};
157}
158
159static VALUE* NS(SELF, try_write)(SELF* self, INDEX index) {
160 INVARIANT_CHECK(self);
161 if (!CHECK_ACCESS_INDEX(self, index)) {
162 return NULL;
163 }
164 SLOT* slot = &self->slots[index.index];
165 if (!slot->present) {
166 return NULL;
167 }
168 return &slot->value;
169}
170
171static VALUE* NS(SELF, write)(SELF* self, INDEX index) {
172 VALUE* value = NS(SELF, try_write)(self, index);
173 DC_ASSERT(value);
174 return value;
175}
176
177static VALUE const* NS(SELF, try_read)(SELF const* self, INDEX index) {
178 INVARIANT_CHECK(self);
179 if (!CHECK_ACCESS_INDEX(self, index)) {
180 return NULL;
181 }
182 SLOT* slot = &self->slots[index.index];
183 if (!slot->present) {
184 return NULL;
185 }
186 return &slot->value;
187}
188
189static VALUE const* NS(SELF, read)(SELF const* self, INDEX index) {
190 VALUE const* value = NS(SELF, try_read)(self, index);
191 DC_ASSERT(value);
192 return value;
193}
194
195static SELF NS(SELF, clone)(SELF const* self) {
196 INVARIANT_CHECK(self);
197 SLOT* slots = (SLOT*)NS(ALLOC, calloc)(self->alloc, self->capacity, sizeof(SLOT));
198 DC_ASSERT(slots);
199
200 for (INDEX_TYPE index = 0; index < self->exclusive_end; index++) {
201 if (self->slots[index].present) {
202 NS(SLOT, memory_tracker_present)(&slots[index]);
203 slots[index].present = true;
204 slots[index].value = VALUE_CLONE(&self->slots[index].value);
205 } else {
206 NS(SLOT, memory_tracker_empty)(&slots[index]);
207 slots[index].present = false;
208 slots[index].next_free = self->slots[index].next_free;
209 }
210 }
211
212 return (SELF){
213 .slots = slots,
214 .capacity = self->capacity,
215 .free_list = self->free_list,
216 .exclusive_end = self->exclusive_end,
217 .count = self->count,
218 .alloc = self->alloc,
219 .derive_c_arena_basic = dc_gdb_marker_new(),
220 .iterator_invalidation_tracker = mutation_tracker_new(),
221 };
222}
223
224static INDEX_TYPE NS(SELF, size)(SELF const* self) {
225 INVARIANT_CHECK(self);
226 return self->count;
227}
228
229static bool NS(SELF, full)(SELF const* self) {
230 INVARIANT_CHECK(self);
231 if (self->capacity == CAPACITY_EXCLUSIVE_UPPER) {
232 if (self->free_list == INDEX_NONE) {
233 return true;
234 }
235 }
236 return false;
237}
238
239static const size_t NS(SELF, max_entries) = MAX_INDEX;
240
241static bool NS(SELF, try_remove)(SELF* self, INDEX index, VALUE* destination) {
242 INVARIANT_CHECK(self);
243 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
244
245 if (!CHECK_ACCESS_INDEX(self, index)) {
246 return false;
247 }
248
249 SLOT* entry = &self->slots[index.index];
250 if (entry->present) {
251 *destination = entry->value;
252 entry->present = false;
254 entry->next_free = self->free_list;
255 self->free_list = index.index;
256 self->count--;
257 return true;
258 }
259 return false;
260}
261
262static VALUE NS(SELF, remove)(SELF* self, INDEX index) {
263 INVARIANT_CHECK(self);
264 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
265
266 VALUE value;
267 DC_ASSERT(NS(SELF, try_remove)(self, index, &value));
268 return value;
269}
270
271#define IV_PAIR NS(SELF, iv)
272typedef struct {
273 INDEX index;
274 VALUE* value;
275} IV_PAIR;
276
277// Provide an empty IV_PAIR sentinel
279 return (IV_PAIR){.index = (INDEX){.index = INDEX_NONE}, .value = NULL};
280}
281
282#define ITER NS(SELF, iter)
283typedef IV_PAIR NS(ITER, item);
284
285static bool NS(ITER, empty_item)(IV_PAIR const* item) { return item->value == NULL; }
286
287typedef struct {
288 SELF* arena;
289 INDEX_TYPE next_index;
290 mutation_version version;
291} ITER;
292
293static bool NS(ITER, empty)(ITER const* iter) {
294 DC_ASSUME(iter);
295 mutation_version_check(&iter->version);
296 // JUSTIFY: If no entries are left, then the previous '.._next' call moved
297 // the index to the exclusive end
298 // NOTE: `index + 1 > exclusive_end` implies `index >= exclusive_end`
299 return iter->next_index == INDEX_NONE || iter->next_index >= iter->arena->exclusive_end;
300}
301
302static IV_PAIR NS(ITER, next)(ITER* iter) {
303 DC_ASSUME(iter);
304 mutation_version_check(&iter->version);
305
306 while (iter->next_index < INDEX_NONE && iter->next_index < iter->arena->exclusive_end) {
307 IV_PAIR result = {
308 .index = (INDEX){.index = iter->next_index},
309 .value = &iter->arena->slots[iter->next_index].value,
310 };
311
312 iter->next_index++;
313 // advance to next present entry
314 while (iter->next_index < INDEX_NONE && iter->next_index < iter->arena->exclusive_end &&
315 !iter->arena->slots[iter->next_index].present) {
316 iter->next_index++;
317 }
318
319 if (result.value && result.value == &iter->arena->slots[result.index.index].value &&
320 iter->arena->slots[result.index.index].present) {
321 return result;
322 }
323 }
324
325 return NS(SELF, iv_empty)();
326}
327
328static ITER NS(SELF, get_iter)(SELF* self) {
329 INVARIANT_CHECK(self);
330 INDEX_TYPE index = 0;
331 while (index < INDEX_NONE && index < self->exclusive_end && !self->slots[index].present) {
332 index++;
333 }
334
335 return (ITER){
336 .arena = self,
337 .next_index = index,
338 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
339 };
340}
341
342static void NS(SELF, delete)(SELF* self) {
343 INVARIANT_CHECK(self);
344 ITER iter = NS(SELF, get_iter)(self);
345
346 for (IV_PAIR entry = NS(ITER, next)(&iter); !NS(ITER, empty_item)(&entry);
347 entry = NS(ITER, next)(&iter)) {
348 VALUE_DELETE(entry.value);
349 }
350
351 NS(ALLOC, free)(self->alloc, self->slots);
352}
353
354#undef ITER
355#undef IV_PAIR
356
357#define IV_PAIR_CONST NS(SELF, iv_const)
358typedef struct {
359 INDEX index;
360 VALUE const* value;
362
363// Provide an empty IV_PAIR_CONST sentinel
365 return (IV_PAIR_CONST){.index = (INDEX){.index = INDEX_NONE}, .value = NULL};
366}
367
368#define ITER_CONST NS(SELF, iter_const)
369// Item is now a value (not pointer)
371
372// Empty-item predicate takes a pointer-to-value
373static bool NS(ITER_CONST, empty_item)(IV_PAIR_CONST const* item) { return item->value == NULL; }
374
375typedef struct {
376 SELF const* arena;
377 INDEX_TYPE next_index;
380} ITER_CONST;
381
382static 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
411static ITER_CONST NS(SELF, get_iter_const)(SELF const* self) {
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
425static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
426 fprintf(stream, EXPAND_STRING(SELF) "@%p {\n", 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", self->slots);
431
432 dc_debug_fmt_print(fmt, stream, "alloc: ");
433 NS(ALLOC, debug)(self->alloc, 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 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 * realloc(SELF *self, void *ptr, size_t size)
Definition template.h:45
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 bool full(SELF const *self)
Definition template.h:257
static ITER_CONST get_iter_const(SELF const *self)
Definition template.h:449
static bool empty(ITER const *iter)
Definition template.h:346
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
#define IV_PAIR
Definition template.h:321
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
static IV_PAIR_CONST iv_const_empty()
Definition template.h:419
static VALUE * try_write(SELF *self, INDEX index)
Definition template.h:205
#define IV_PAIR_CONST
Definition template.h:399
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 const size_t max_entries
Definition template.h:262
static SELF clone(SELF const *self)
Definition template.h:215
static VALUE const * try_read(SELF const *self, INDEX index)
Definition template.h:180
static IV_PAIR iv_empty()
Definition template.h:341
IV_PAIR item
Definition template.h:283
static SELF new_with_capacity_for(INDEX_TYPE items, ALLOC *alloc)
Definition template.h:96
#define CHECK_ACCESS_INDEX(self, index)
Definition template.h:51
#define RESIZE_FACTOR
Definition template.h:55
#define DC_TRAIT_ARENA(SELF)
Definition trait.h:5
#define INDEX_TYPE
Definition template.h:74
static size_t capacity()
Definition template.h:66
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 DC_INLINE DC_CONST size_t dc_math_next_power_of_2(size_t x)
Definition math.h:48
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_STATIC_ASSERT
Definition panic.h:21
#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
mutation_version version
Definition template.h:404
IV_PAIR_CONST curr
Definition template.h:378
VALUE const * value
Definition template.h:416
VALUE * value
Definition template.h:338
INDEX index
Definition template.h:337
size_t capacity
Definition template.h:72
mutation_tracker iterator_invalidation_tracker
Definition template.h:85
INDEX_TYPE free_list
Definition template.h:77
size_t exclusive_end
Definition template.h:79
dc_gdb_marker derive_c_arena_basic
Definition template.h:84
SLOT * slots
Definition template.h:71
size_t count
Definition template.h:76
ALLOC * alloc
Definition template.h:71
VALUE value
Definition template.h:78
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...
int x
Definition template.h:23
static void memory_tracker_present(SELF const *slot)
Definition template.h:68
static void memory_tracker_empty(SELF const *slot)
Definition template.h:53
static FILE * stream(SELF *self)
Opens a file for.
Definition template.h:107