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 ITEM
12 #if !defined DC_PLACEHOLDERS
13TEMPLATE_ERROR("No ITEM")
14 #endif
15
16typedef struct {
17 int x;
18} item_t;
19 #define ITEM item_t
20 #define ITEM_DELETE item_delete
21static void ITEM_DELETE(item_t* /* self */) {}
22 #define ITEM_CLONE item_clone
23static item_t ITEM_CLONE(item_t const* self) { return *self; }
24 #define ITEM_DEBUG item_debug
25static void ITEM_DEBUG(ITEM const* /* self */, dc_debug_fmt /* fmt */, FILE* /* stream */) {}
26#endif
27
28#if !defined ITEM_DELETE
29 #define ITEM_DELETE DC_NO_DELETE
30#endif
31
32#if !defined ITEM_CLONE
33 #define ITEM_CLONE DC_COPY_CLONE
34#endif
35
36#if !defined ITEM_DEBUG
37 #define ITEM_DEBUG DC_DEFAULT_DEBUG
38#endif
39
40typedef ITEM NS(SELF, item_t);
41typedef ALLOC NS(SELF, alloc_t);
42
43typedef struct {
45 size_t capacity;
46 size_t size; /* Cached size to avoid recalculation */
47 size_t head; /* Index of the first element */
48 size_t tail; /* Index of the last element */
49 bool empty; /* Used to differentiate between head == tail when 1 element, or empty */
50 NS(ALLOC, ref) alloc_ref;
51 dc_gdb_marker derive_c_circular;
53} SELF;
54
55#define INVARIANT_CHECK(self) \
56 DC_ASSUME(self); \
57 DC_ASSUME(DC_WHEN(!(self)->empty, \
58 (self)->head < (self)->capacity && (self)->tail < (self)->capacity)); \
59 DC_ASSUME(DC_WHEN((self)->empty, (self)->head == (self)->tail)); \
60 DC_ASSUME(DC_WHEN(!(self)->data, (self)->head == 0 && (self)->tail == 0));
61
62DC_PUBLIC static SELF NS(SELF, new)(NS(ALLOC, ref) alloc_ref) {
63 return (SELF){
64 .data = NULL,
65 .capacity = 0,
66 .size = 0,
67 .head = 0,
68 .tail = 0,
69 .empty = true,
70 .alloc_ref = alloc_ref,
71 .derive_c_circular = dc_gdb_marker_new(),
72 .iterator_invalidation_tracker = mutation_tracker_new(),
73 };
74}
75
76DC_PUBLIC static SELF NS(SELF, new_with_capacity_for)(size_t capacity_for,
77 NS(ALLOC, ref) alloc_ref) {
78 if (capacity_for == 0) {
79 return NS(SELF, new)(alloc_ref);
80 }
81 size_t const capacity = dc_math_next_power_of_2(capacity_for);
83 ITEM* data = (ITEM*)NS(ALLOC, allocate_uninit)(alloc_ref, capacity * sizeof(ITEM));
84
86 capacity * sizeof(ITEM));
87
88 return (SELF){
89 .data = data,
90 .capacity = capacity,
91 .size = 0,
92 .head = 0,
93 .tail = 0,
94 .empty = true,
95 .alloc_ref = alloc_ref,
96 .derive_c_circular = dc_gdb_marker_new(),
97 .iterator_invalidation_tracker = mutation_tracker_new(),
98 };
99}
100
101DC_PUBLIC static bool NS(SELF, empty)(SELF const* self) {
102 DC_ASSUME(self);
103 if (self->empty) {
104 DC_ASSUME(self->head == self->tail);
105 }
106 return self->empty;
107}
108
109DC_PUBLIC static size_t NS(SELF, size)(SELF const* self) {
110 DC_ASSUME(self);
111 return self->size;
112}
113
116 if (self->empty) {
118 self->capacity * sizeof(ITEM));
119 } else if (self->head <= self->tail) {
120 dc_memory_tracker_set(DC_MEMORY_TRACKER_LVL_CONTAINER, cap, &self->data[self->tail + 1],
121 (self->capacity - (self->tail + 1)) * sizeof(ITEM));
123 self->head * sizeof(ITEM));
124 } else {
125 dc_memory_tracker_set(DC_MEMORY_TRACKER_LVL_CONTAINER, cap, &self->data[self->tail + 1],
126 (self->head - (self->tail + 1)) * sizeof(ITEM));
127 }
128}
129
130DC_PUBLIC static void NS(SELF, reserve)(SELF* self, size_t new_capacity_for) {
131 INVARIANT_CHECK(self);
132 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
133
134 if (new_capacity_for > self->capacity) {
135 size_t const new_capacity = dc_math_next_power_of_2(new_capacity_for * 2);
136
137 // We set the capability to write for the allocator, and only touch the
138 // state for memory that is inaccessible
140
141 ITEM* new_data;
142 if (self->data == NULL) {
143 new_data =
144 (ITEM*)NS(ALLOC, allocate_uninit)(self->alloc_ref, new_capacity * sizeof(ITEM));
145 } else {
146 new_data = (ITEM*)NS(ALLOC, reallocate)(self->alloc_ref, self->data,
147 self->capacity * sizeof(ITEM),
148 new_capacity * sizeof(ITEM));
149 }
150
151 if (self->head > self->tail) {
152 // The queue wraps at the old end, so we need to either:
153 // - shift elements of the tail around into the new area at the end of the buffer
154 // - shift the head of the queue along to the new arena
155 // need to move the wrapped around part to the end of the new buffer
156 size_t const old_capacity = self->capacity;
157 size_t const additional_capacity = new_capacity - old_capacity;
158 size_t const front_tail_items = self->tail + 1;
159 size_t const back_head_items = old_capacity - self->head;
160
161 if (front_tail_items > back_head_items) {
162 size_t const new_head = self->head + additional_capacity;
163 memmove(&new_data[new_head], &new_data[self->head], back_head_items * sizeof(ITEM));
164 self->head = new_head;
165 } else {
166 // as we go to the next power of 2 each time, the additional capacity is always >=
167 // the number of items at the front. As a result, we never need to consider:
168 // - Only copying the first n from the front
169 // - Shifting some of the front items to the start of the buffer
170 DC_ASSUME(front_tail_items <= additional_capacity);
171
172 memcpy(&new_data[old_capacity], &new_data[0], front_tail_items * sizeof(ITEM));
173 self->tail = old_capacity + front_tail_items - 1;
174 }
175 }
176 self->capacity = new_capacity;
177 self->data = new_data;
178
179 // Set the new inaccessible memory
181 }
182 INVARIANT_CHECK(self);
183}
184
185DC_PUBLIC static void NS(SELF, push_back)(SELF* self, ITEM item) {
186 INVARIANT_CHECK(self);
187 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
188 NS(SELF, reserve)(self, self->size + 1);
189
190 if (!self->empty) {
191 self->tail = dc_math_modulus_power_of_2_capacity(self->tail + 1, self->capacity);
192 }
194 &self->data[self->tail], sizeof(ITEM));
195 self->data[self->tail] = item;
196 self->empty = false;
197 self->size++;
198}
199
200DC_PUBLIC static void NS(SELF, push_front)(SELF* self, ITEM item) {
201 INVARIANT_CHECK(self);
202 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
203 NS(SELF, reserve)(self, self->size + 1);
204
205 if (!self->empty) {
206 if (self->head == 0) {
207 self->head = self->capacity - 1;
208 } else {
209 self->head--;
210 }
211 }
213 &self->data[self->head], sizeof(ITEM));
214 self->data[self->head] = item;
215 self->empty = false;
216 self->size++;
217}
218
220 INVARIANT_CHECK(self);
221 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
222 DC_ASSERT(!NS(SELF, empty)(self), "Cannot pop front, already empty {size=%lu}",
223 (size_t)self->size);
224
225 ITEM value = self->data[self->head];
227 &self->data[self->head], sizeof(ITEM));
229 &self->data[self->head], sizeof(ITEM));
230
231 self->size--;
232 if (self->head == self->tail) {
233 self->empty = true;
234 } else {
235 self->head = dc_math_modulus_power_of_2_capacity(self->head + 1, self->capacity);
236 }
237 return value;
238}
239
241 INVARIANT_CHECK(self);
242 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
243 DC_ASSERT(!NS(SELF, empty)(self), "Cannot pop back, already empty {size=%lu}",
244 (size_t)self->size);
245
246 ITEM value = self->data[self->tail];
248 &self->data[self->tail], sizeof(ITEM));
249 self->size--;
250 if (self->head == self->tail) {
251 self->empty = true;
252 } else {
253 if (self->tail == 0) {
254 self->tail = self->capacity - 1;
255 } else {
256 self->tail--;
257 }
258 }
259 return value;
260}
261
262DC_PUBLIC static ITEM const* NS(SELF, try_read_from_front)(SELF const* self, size_t index) {
263 INVARIANT_CHECK(self);
264 if (index < self->size) {
265 size_t const real_index =
266 dc_math_modulus_power_of_2_capacity(self->head + index, self->capacity);
267 return &self->data[real_index];
268 }
269 return NULL;
270}
271
272DC_PUBLIC static ITEM const* NS(SELF, try_read_from_back)(SELF const* self, size_t index) {
273 INVARIANT_CHECK(self);
274 if (index >= self->size) {
275 return NULL;
276 }
277 if (index <= self->tail) {
278 return &self->data[self->tail - index];
279 }
280 size_t const from_end = index - self->tail;
281 return &self->data[self->capacity - from_end];
282}
283
284DC_PUBLIC static ITEM* NS(SELF, try_write_from_front)(SELF* self, size_t index) {
285 return (ITEM*)NS(SELF, try_read_from_front)(self, index);
286}
287
288DC_PUBLIC static ITEM* NS(SELF, try_write_from_back)(SELF* self, size_t index) {
289 return (ITEM*)NS(SELF, try_read_from_back)(self, index);
290}
291
292#define ITER NS(SELF, iter)
293typedef ITEM* NS(ITER, item);
294
295DC_PUBLIC static bool NS(ITER, empty_item)(ITEM* const* item) { return *item == NULL; }
296
297typedef struct {
299 size_t position;
301} ITER;
302
303DC_PUBLIC static bool NS(ITER, empty)(ITER const* iter) {
304 DC_ASSUME(iter);
305 mutation_version_check(&iter->version);
306 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
307}
308
309DC_PUBLIC static ITEM* NS(ITER, next)(ITER* iter) {
310 DC_ASSUME(iter);
311 mutation_version_check(&iter->version);
312 ITEM* item = NS(SELF, try_write_from_front)(iter->circular, iter->position);
313 if (!item) {
314 return NULL;
315 }
316 iter->position++;
317 return item;
318}
319
321 INVARIANT_CHECK(self);
322 return (ITER){
323 .circular = self,
324 .position = 0,
325 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
326 };
327}
328
329DC_PUBLIC static void NS(SELF, delete)(SELF* self) {
330 INVARIANT_CHECK(self);
331 if (self->data) {
332 ITER iter = NS(SELF, get_iter)(self);
333 ITEM* item;
334 while ((item = NS(ITER, next)(&iter))) {
337 sizeof(ITEM));
338 }
339 size_t const size = self->capacity * sizeof(ITEM);
341 self->data, size);
342 NS(ALLOC, deallocate)(self->alloc_ref, self->data, size);
343 }
344}
345
346#undef ITER
347
348#define ITER_CONST NS(SELF, iter_const)
349typedef ITEM const* NS(ITER_CONST, item);
350
351DC_PUBLIC static bool NS(ITER_CONST, empty_item)(ITEM const* const* item) { return *item == NULL; }
352
353typedef struct {
355 size_t position;
357} ITER_CONST;
358
359DC_PUBLIC static bool NS(ITER_CONST, empty)(ITER_CONST const* iter) {
360 DC_ASSUME(iter);
361 mutation_version_check(&iter->version);
362 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
363}
364
365DC_PUBLIC static ITEM const* NS(ITER_CONST, next)(ITER_CONST* iter) {
366 DC_ASSUME(iter);
367 mutation_version_check(&iter->version);
368 ITEM const* item = NS(SELF, try_read_from_front)(iter->circular, iter->position);
369 if (!item) {
370 return NULL;
371 }
372 iter->position++;
373 return item;
374}
375
377 INVARIANT_CHECK(self);
378 return (ITER_CONST){
379 .circular = self,
380 .position = 0,
381 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
382 };
383}
384
385DC_PUBLIC static SELF NS(SELF, clone)(SELF const* self) {
386 INVARIANT_CHECK(self);
387 ITEM* new_data = NULL;
388 size_t tail = 0;
389 size_t new_capacity = 0;
390 size_t const old_size = self->size;
391
392 if (old_size > 0) {
393 new_capacity = dc_math_next_power_of_2(old_size);
394 new_data = (ITEM*)NS(ALLOC, allocate_uninit)(self->alloc_ref, new_capacity * sizeof(ITEM));
395
396 ITER_CONST iter = NS(SELF, get_iter_const)(self);
397 ITEM const* item;
398 while ((item = NS(ITER_CONST, next)(&iter))) {
399 new_data[tail] = ITEM_CLONE(item);
400 tail++;
401 }
402 tail--;
403 }
404 SELF new_self = {
405 .data = new_data,
406 .capacity = new_capacity,
407 .size = old_size,
408 .head = 0,
409 .tail = tail,
410 .empty = self->empty,
411 .alloc_ref = self->alloc_ref,
412 .derive_c_circular = dc_gdb_marker_new(),
413 .iterator_invalidation_tracker = mutation_tracker_new(),
414 };
416 return new_self;
417}
418
419DC_PUBLIC static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
420 fprintf(stream, DC_EXPAND_STRING(SELF) "@%p {\n", (void*)self);
421 fmt = dc_debug_fmt_scope_begin(fmt);
422 dc_debug_fmt_print(fmt, stream, "capacity: %lu,\n", self->capacity);
423 dc_debug_fmt_print(fmt, stream, "size: %lu,\n", self->size);
424 dc_debug_fmt_print(fmt, stream, "head: %lu,\n", self->head);
425 dc_debug_fmt_print(fmt, stream, "tail: %lu,\n", self->tail);
426
427 dc_debug_fmt_print(fmt, stream, "alloc: ");
428 NS(ALLOC, debug)(NS(NS(ALLOC, ref), deref)(self->alloc_ref), fmt, stream);
429 fprintf(stream, ",\n");
430
431 dc_debug_fmt_print(fmt, stream, "queue: @%p [\n", (void*)self->data);
432 fmt = dc_debug_fmt_scope_begin(fmt);
433
434 ITER_CONST iter = NS(SELF, get_iter_const)(self);
435 ITEM const* item;
436 while ((item = NS(ITER_CONST, next)(&iter))) {
438 ITEM_DEBUG(item, fmt, stream);
439 fprintf(stream, ",\n");
440 }
441 fmt = dc_debug_fmt_scope_end(fmt);
442 dc_debug_fmt_print(fmt, stream, "],\n");
443 fmt = dc_debug_fmt_scope_end(fmt);
444 dc_debug_fmt_print(fmt, stream, "}");
445}
446
447#undef ITER_CONST
448#undef INVARIANT_CHECK
449#undef ITEM_DEBUG
450#undef ITEM_CLONE
451#undef ITEM_DELETE
452#undef ITEM
453
455
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
#define ITEM
Definition template.h:36
static DC_PUBLIC void * allocate_uninit(SELF *self, size_t size)
Definition template.h:92
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 INVARIANT_CHECK(self)
Definition template.h:90
static DC_PUBLIC IV_PAIR next(ITER *iter)
Definition template.h:355
static DC_PUBLIC bool empty(ITER const *iter)
Definition template.h:349
ALLOC alloc_t
Definition template.h:63
static DC_PUBLIC bool empty_item(IV_PAIR const *item)
Definition template.h:347
#define ITER
Definition template.h:322
static DC_PUBLIC ITER get_iter(SELF *self)
Definition template.h:373
#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 ITEM_DELETE
Definition template.h:20
static DC_PUBLIC void reserve(SELF *self, size_t new_capacity_for)
Definition template.h:130
static DC_PUBLIC ITEM * try_write_from_back(SELF *self, size_t index)
Definition template.h:288
static DC_PUBLIC ITEM pop_front(SELF *self)
Definition template.h:219
static DC_PUBLIC ITEM const * try_read_from_front(SELF const *self, size_t index)
Definition template.h:262
static DC_PUBLIC ITEM * try_write_from_front(SELF *self, size_t index)
Definition template.h:284
static DC_PUBLIC void push_front(SELF *self, ITEM item)
Definition template.h:200
static DC_PUBLIC ITEM const * try_read_from_back(SELF const *self, size_t index)
Definition template.h:272
#define ITEM_CLONE
Definition template.h:22
#define ITEM_DEBUG
Definition template.h:24
static DC_PUBLIC void push_back(SELF *self, ITEM item)
Definition template.h:185
static void PRIV set_inaccessible_memory_caps(SELF *self, dc_memory_tracker_capability cap)
Definition template.h:114
static DC_PUBLIC ITEM pop_back(SELF *self)
Definition template.h:240
#define DC_TRAIT_QUEUE(SELF)
Definition trait.h:5
static DC_PUBLIC ITEM * data(SELF *self)
Definition template.h:284
#define TEMPLATE_ERROR(...)
With the user provided name, even in nested templates.
Definition def.h:56
#define SELF
Definition def.h:52
static DC_PUBLIC void dc_debug_fmt_print(dc_debug_fmt fmt, FILE *stream, const char *format,...)
Definition fmt.h:32
static DC_PUBLIC void dc_debug_fmt_print_indents(dc_debug_fmt fmt, FILE *stream)
Definition fmt.h:20
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
#define DC_MATH_IS_POWER_OF_2(x)
Definition math.h:14
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_INLINE DC_CONST size_t dc_math_next_power_of_2(size_t x)
Definition math.h:46
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)
static DC_PUBLIC void dc_memory_tracker_check(dc_memory_tracker_level level, dc_memory_tracker_capability cap, const void *addr, size_t size)
@ DC_MEMORY_TRACKER_LVL_CONTAINER
dc_memory_tracker_capability
a wrapper over asan & msan Containers and allocators can use this for custom asan & msan poisoning,...
@ 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
size_t position
Definition template.h:355
SELF const * circular
Definition template.h:354
mutation_version version
Definition template.h:417
SELF * circular
Definition template.h:298
mutation_version version
Definition template.h:328
size_t position
Definition template.h:299
size_t head
Definition template.h:47
size_t capacity
Definition template.h:72
mutation_tracker iterator_invalidation_tracker
Definition template.h:87
dc_gdb_marker derive_c_circular
Definition template.h:51
size_t size
Definition template.h:75
ITEM * data
Definition template.h:44
bool empty
Definition template.h:49
size_t tail
Definition template.h:48
ref alloc_ref
Definition template.h:49
Debug format helpers for debug printin data structures.
Definition fmt.h:11
A queue comprised of an extendable circular buffer.
Definition template.h:16
int x
Definition template.h:17
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