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