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
15typedef struct {
16 int x;
17} item_t;
18 #define ITEM item_t
19 #define ITEM_DELETE item_delete
20static void ITEM_DELETE(item_t* /* self */) {}
21 #define ITEM_CLONE item_clone
22static item_t ITEM_CLONE(item_t const* self) { return *self; }
23 #define ITEM_DEBUG item_debug
24static void ITEM_DEBUG(ITEM const* /* self */, dc_debug_fmt /* fmt */, FILE* /* stream */) {}
25#endif
26
27#if !defined ITEM_DELETE
28 #define ITEM_DELETE DC_NO_DELETE
29#endif
30
31#if !defined ITEM_CLONE
32 #define ITEM_CLONE DC_COPY_CLONE
33#endif
34
35#if !defined ITEM_DEBUG
36 #define ITEM_DEBUG DC_DEFAULT_DEBUG
37#endif
38
39typedef size_t NS(SELF, index_t);
40typedef ITEM NS(SELF, item_t);
41
42typedef struct {
43 size_t size;
44 size_t capacity;
45 ITEM* data;
46 NS(ALLOC, ref) alloc_ref;
49} SELF;
50
51#define INVARIANT_CHECK(self) \
52 DC_ASSUME(self); \
53 DC_ASSUME((self)->size <= (self)->capacity); \
54 DC_ASSUME(DC_WHEN(!((self)->data), (self)->capacity == 0 && (self)->size == 0));
55
56DC_STATIC_CONSTANT size_t NS(SELF, max_size) = SIZE_MAX;
57
58DC_PUBLIC static SELF NS(SELF, new)(NS(ALLOC, ref) alloc_ref) {
59 SELF self = (SELF){
60 .size = 0,
61 .capacity = 0,
62 .data = NULL,
63 .alloc_ref = alloc_ref,
64 .derive_c_vector_marker = dc_gdb_marker_new(),
65 .iterator_invalidation_tracker = mutation_tracker_new(),
66 };
67 return self;
68}
69
70DC_PUBLIC static SELF NS(SELF, new_with_capacity)(size_t capacity, NS(ALLOC, ref) alloc_ref) {
71 if (capacity == 0) {
72 return NS(SELF, new)(alloc_ref);
73 }
74
75 ITEM* data = (ITEM*)NS(ALLOC, allocate_uninit)(alloc_ref, capacity * sizeof(ITEM));
77 capacity * sizeof(ITEM));
78 return (SELF){
79 .size = 0,
80 .capacity = capacity,
81 .data = data,
82 .alloc_ref = alloc_ref,
83 .derive_c_vector_marker = dc_gdb_marker_new(),
84 .iterator_invalidation_tracker = mutation_tracker_new(),
85 };
86}
87
88DC_PUBLIC static SELF NS(SELF, new_with_defaults)(size_t size, ITEM default_item,
89 NS(ALLOC, ref) alloc_ref) {
90 ITEM* data = (ITEM*)NS(ALLOC, allocate_uninit)(alloc_ref, size * sizeof(ITEM));
91 if (size > 0) {
92 // JUSTIFY: We only need to copy size-1 entries - can move the first as default.
93 data[0] = default_item;
94 for (size_t i = 1; i < size; i++) {
95 data[i] = ITEM_CLONE(&default_item);
96 }
97 }
98 return (SELF){
99 .size = size,
100 .capacity = size,
101 .data = data,
102 .alloc_ref = alloc_ref,
103 .derive_c_vector_marker = dc_gdb_marker_new(),
104 .iterator_invalidation_tracker = mutation_tracker_new(),
105 };
106}
107
108DC_PUBLIC static void NS(SELF, reserve)(SELF* self, size_t new_capacity) {
109 INVARIANT_CHECK(self);
110 if (new_capacity > self->capacity) {
111 if (self->data == NULL) {
112 DC_ASSUME(self->capacity == 0);
113
114 ITEM* new_data =
115 (ITEM*)NS(ALLOC, allocate_uninit)(self->alloc_ref, new_capacity * sizeof(ITEM));
116 self->data = new_data;
117 self->capacity = new_capacity;
119 self->data, self->capacity * sizeof(ITEM));
120 } else {
121 const size_t capacity_increase = new_capacity - self->capacity;
122 const size_t uninit_elements = self->capacity - self->size;
123
125 &self->data[self->size], uninit_elements * sizeof(ITEM));
126
127 size_t old_size = self->capacity * sizeof(ITEM);
128 size_t new_size = new_capacity * sizeof(ITEM);
129 ITEM* new_data =
130 (ITEM*)NS(ALLOC, reallocate)(self->alloc_ref, self->data, old_size, new_size);
131
132 self->data = new_data;
134 &self->data[self->size],
135 (uninit_elements + capacity_increase) * sizeof(ITEM));
136 self->capacity = new_capacity;
137 }
138 }
139}
140
141DC_PUBLIC static SELF NS(SELF, clone)(SELF const* self) {
142 INVARIANT_CHECK(self);
143 ITEM* data = (ITEM*)NS(ALLOC, allocate_uninit)(self->alloc_ref, self->capacity * sizeof(ITEM));
144
145 for (size_t index = 0; index < self->size; index++) {
146 data[index] = ITEM_CLONE(&self->data[index]);
147 }
149 &data[self->size], (self->capacity - self->size) * sizeof(ITEM));
150 return (SELF){
151 .size = self->size,
152 .capacity = self->capacity,
153 .data = data,
154 .alloc_ref = self->alloc_ref,
155 .derive_c_vector_marker = dc_gdb_marker_new(),
156 .iterator_invalidation_tracker = mutation_tracker_new(),
157 };
158}
159
160DC_PUBLIC static ITEM const* NS(SELF, try_read)(SELF const* self, size_t index) {
161 INVARIANT_CHECK(self);
162 if (DC_LIKELY(index < self->size)) {
163 return &self->data[index];
164 }
165 return NULL;
166}
167
168DC_PUBLIC static ITEM const* NS(SELF, read)(SELF const* self, size_t index) {
169 ITEM const* item = NS(SELF, try_read)(self, index);
170 DC_ASSERT(item, "Cannot read, index out of bounds {index=%lu, size=%lu}", (size_t)index,
171 (size_t)self->size);
172 return item;
173}
174
175DC_PUBLIC static ITEM* NS(SELF, try_write)(SELF* self, size_t index) {
176 INVARIANT_CHECK(self);
177 if (DC_LIKELY(index < self->size)) {
178 return &self->data[index];
179 }
180 return NULL;
181}
182
183DC_PUBLIC static ITEM* NS(SELF, write)(SELF* self, size_t index) {
184 ITEM* item = NS(SELF, try_write)(self, index);
185 DC_ASSERT(item, "Cannot write, index out of bounds {index=%lu, size=%lu}", (size_t)index,
186 (size_t)self->size);
187 return item;
188}
189
190DC_PUBLIC static ITEM* NS(SELF, try_insert_at)(SELF* self, size_t at, ITEM const* items,
191 size_t count) {
192 INVARIANT_CHECK(self);
193 DC_ASSUME(items);
194 DC_ASSERT(at <= self->size, "Cannot insert at, index out of bounds {at=%lu, size=%lu}",
195 (size_t)at, (size_t)self->size);
196 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
197
198 if (count == 0) {
199 return NULL;
200 }
201
202 NS(SELF, reserve)(self, self->size + count);
204 &self->data[self->size], count * sizeof(ITEM));
205 memmove(&self->data[at + count], &self->data[at], (self->size - at) * sizeof(ITEM));
206 memcpy(&self->data[at], items, count * sizeof(ITEM));
207 self->size += count;
208 return &self->data[at];
209}
210
211DC_PUBLIC static void NS(SELF, remove_at)(SELF* self, size_t at, size_t count) {
212 INVARIANT_CHECK(self);
213 DC_ASSERT(at + count <= self->size,
214 "Cannot remove at, index out of bounds {at=%lu, count=%lu, size=%lu}", (size_t)at,
215 (size_t)count, (size_t)self->size);
216 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
217
218 if (count == 0) {
219 return;
220 }
221
222 for (size_t i = at; i < at + count; i++) {
223 ITEM_DELETE(&self->data[i]);
224 }
225
226 memmove(&self->data[at], &self->data[at + count], (self->size - (at + count)) * sizeof(ITEM));
227 self->size -= count;
229 &self->data[self->size], count * sizeof(ITEM));
230}
231
233 INVARIANT_CHECK(self);
234 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
235
236 if (self->size == self->capacity) {
237 size_t new_capacity;
238 if (self->data == NULL) {
239 DC_ASSUME(self->capacity == 0);
240 // JUSTIFY: Allocating capacity of 4
241 // - Avoid repeat reallocations on growing a vector from
242 // size sero (from new)
243 // Otherwise an arbitrary choice (given we do not know the size of T)
244 new_capacity = 8;
245 } else {
246 // JUSTIFY: Growth factor of 2
247 // - Simple arithmetic (for debugging)
248 // - Same as used by GCC's std::vector implementation
249 new_capacity = self->capacity * 2;
250 }
251 NS(SELF, reserve)(self, new_capacity);
252 }
253
255 &self->data[self->size], sizeof(ITEM));
256
257 ITEM* entry = &self->data[self->size];
258 *entry = item;
259 self->size++;
260 return entry;
261}
262
263DC_PUBLIC static ITEM* NS(SELF, push)(SELF* self, ITEM item) {
264 ITEM* entry = NS(SELF, try_push)(self, item);
265 DC_ASSERT(entry != NULL, "Cannot push, already at max capacity {capacity=%lu, item=%s}",
266 (size_t)self->capacity, DC_DEBUG(ITEM_DEBUG, &item));
267 return entry;
268}
269
270DC_PUBLIC static bool NS(SELF, try_pop)(SELF* self, ITEM* destination) {
271 INVARIANT_CHECK(self);
272 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
273
274 if (DC_LIKELY(self->size > 0)) {
275 self->size--;
276 *destination = self->data[self->size];
278 &self->data[self->size], sizeof(ITEM));
279 return true;
280 }
281 return false;
282}
283
284DC_PUBLIC static ITEM* NS(SELF, data)(SELF* self) {
285 INVARIANT_CHECK(self);
286 return self->data;
287}
288
289DC_PUBLIC static ITEM NS(SELF, pop)(SELF* self) {
290 ITEM entry;
291 DC_ASSERT(NS(SELF, try_pop)(self, &entry), "Cannot pop, already empty {size=%lu}",
292 (size_t)self->size);
293 return entry;
294}
295
297 INVARIANT_CHECK(self);
298 mutation_tracker_mutate(&self->iterator_invalidation_tracker);
299 DC_ASSERT(self->size > 0, "Cannot pop front, already empty {size=%lu}", (size_t)self->size);
300 ITEM entry = self->data[0];
301 memmove(&self->data[0], &self->data[1], (self->size - 1) * sizeof(ITEM));
302 self->size--;
304 &self->data[self->size], sizeof(ITEM));
305 return entry;
306}
307
308DC_PUBLIC static size_t NS(SELF, size)(SELF const* self) {
309 INVARIANT_CHECK(self);
310 return self->size;
311}
312
313DC_PUBLIC static void NS(SELF, delete)(SELF* self) {
314 INVARIANT_CHECK(self);
315 if (self->data) {
316 for (size_t i = 0; i < self->size; i++) {
317 ITEM_DELETE(&self->data[i]);
318 // JUSTIFY: Setting items as inaccessible
319 // - Incase a destructor of one item accesses the memory of another.
321 &self->data[i], sizeof(ITEM));
322 }
323
324 // JUSTIFY: Return to write level before passing to allocator
325 // - Is uninitialised, but still valid memory
326 size_t const size = self->capacity * sizeof(ITEM);
328 self->data, size);
329 NS(ALLOC, deallocate)(self->alloc_ref, self->data, size);
330 }
331}
332
348DC_PUBLIC static void NS(SELF, transfer_reverse)(SELF* source, SELF* target, size_t to_move) {
349 INVARIANT_CHECK(source);
350 INVARIANT_CHECK(target);
351
352 NS(SELF, reserve)(target, target->size + to_move);
353
355 &target->data[target->size], to_move * sizeof(ITEM));
356 memmove(&target->data[to_move], target->data, target->size * sizeof(ITEM));
357
358 for (size_t i = 0; i < to_move; i++) {
359 target->data[to_move - 1 - i] = source->data[i];
360 }
361
362 memmove(source->data, &source->data[to_move], (source->size - to_move) * sizeof(ITEM));
363
364 source->size -= to_move;
365 target->size += to_move;
367 &source->data[source->size], to_move * sizeof(ITEM));
368}
369
370#define ITER NS(SELF, iter)
371typedef ITEM* NS(ITER, item);
372
373DC_PUBLIC static DC_INLINE bool NS(ITER, empty_item)(ITEM* const* item) { return *item == NULL; }
374
375typedef struct {
376 ITEM* data; // Direct pointer to data buffer
377 size_t pos;
378 size_t size; // Cached size for fast bounds checking
380} ITER;
381
383 DC_ASSUME(iter);
384 mutation_version_check(&iter->version);
385
386 if (iter->pos < iter->size) {
387 ITEM* item = &iter->data[iter->pos];
388 iter->pos++;
389 return item;
390 }
391 return NULL;
392}
393
394DC_PUBLIC static DC_INLINE size_t NS(ITER, position)(ITER const* DC_RESTRICT iter) {
395 DC_ASSUME(iter);
396 mutation_version_check(&iter->version);
397 return iter->pos;
398}
399
400DC_PUBLIC static DC_INLINE bool NS(ITER, empty)(ITER const* DC_RESTRICT iter) {
401 DC_ASSUME(iter);
402 mutation_version_check(&iter->version);
403 return iter->pos >= iter->size;
404}
405
407 DC_ASSUME(self);
408 return (ITER){
409 .data = self->data,
410 .pos = 0,
411 .size = self->size,
412 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
413 };
414}
415#undef ITER
416
417#define ITER_CONST NS(SELF, iter_const)
418typedef ITEM const* NS(ITER_CONST, item);
419
420DC_PUBLIC static DC_INLINE bool NS(ITER_CONST, empty_item)(ITEM const* const* item) {
421 return *item == NULL;
422}
423
424typedef struct {
425 ITEM const* data; // Direct pointer to data buffer
426 size_t pos;
427 size_t size; // Cached size for fast bounds checking
429} ITER_CONST;
430
432 DC_ASSUME(iter);
433 mutation_version_check(&iter->vec_version);
434 if (iter->pos < iter->size) {
435 ITEM const* item = &iter->data[iter->pos];
436 iter->pos++;
437 return item;
438 }
439 return NULL;
440}
441
443 DC_ASSUME(iter);
444 mutation_version_check(&iter->vec_version);
445 return iter->pos;
446}
447
449 DC_ASSUME(iter);
450 mutation_version_check(&iter->vec_version);
451 return iter->pos >= iter->size;
452}
453
455 DC_ASSUME(self);
456 return (ITER_CONST){
457 .data = self->data,
458 .pos = 0,
459 .size = self->size,
460 .vec_version = mutation_tracker_get(&self->iterator_invalidation_tracker),
461 };
462}
463
464DC_PUBLIC static void NS(SELF, debug)(SELF const* self, dc_debug_fmt fmt, FILE* stream) {
465 fprintf(stream, DC_EXPAND_STRING(SELF) "@%p {\n", (void*)self);
466 fmt = dc_debug_fmt_scope_begin(fmt);
467 dc_debug_fmt_print(fmt, stream, "size: %lu,\n", self->size);
468 dc_debug_fmt_print(fmt, stream, "capacity: %lu,\n", self->capacity);
469
470 dc_debug_fmt_print(fmt, stream, "alloc: ");
471 NS(ALLOC, debug)(NS(NS(ALLOC, ref), deref)(self->alloc_ref), fmt, stream);
472 fprintf(stream, ",\n");
473
474 dc_debug_fmt_print(fmt, stream, "items: @%p [\n", (void*)self->data);
475 fmt = dc_debug_fmt_scope_begin(fmt);
476
477 ITER_CONST iter = NS(SELF, get_iter_const)(self);
478 ITEM const* item;
479 while ((item = NS(ITER_CONST, next)(&iter))) {
481 ITEM_DEBUG(item, fmt, stream);
482 fprintf(stream, ",\n");
483 }
484 fmt = dc_debug_fmt_scope_end(fmt);
485 dc_debug_fmt_print(fmt, stream, "],\n");
486 fmt = dc_debug_fmt_scope_end(fmt);
487 dc_debug_fmt_print(fmt, stream, "}");
488}
489
490#undef ITER_CONST
491#undef INVARIANT_CHECK
492#undef ITEM_DEBUG
493#undef ITEM_CLONE
494#undef ITEM_DELETE
495#undef ITEM
496
498
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
#define ITEM_DEBUG
Definition template.h:39
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 DC_RESTRICT
Definition attributes.h:15
#define DC_INLINE
Definition attributes.h:3
#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 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
static DC_PUBLIC VALUE const * read(SELF const *self, INDEX index)
Definition template.h:199
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
#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
INDEX_TYPE index_t
Definition template.h:26
#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 pop_front(SELF *self)
Definition template.h:219
#define ITEM_CLONE
Definition template.h:22
#define ITEM_DEBUG
Definition template.h:24
static DC_PUBLIC SELF new_with_capacity(size_t front_and_back_capacity, ref alloc_ref)
Definition template.h:78
static DC_PUBLIC ITEM * data(SELF *self)
Definition template.h:284
static DC_PUBLIC ITEM * try_push(SELF *self, ITEM item)
Definition template.h:232
DC_STATIC_CONSTANT size_t max_size
Definition template.h:56
static DC_PUBLIC bool try_pop(SELF *self, ITEM *destination)
Definition template.h:270
static DC_PUBLIC ITEM * try_insert_at(SELF *self, size_t at, ITEM const *items, size_t count)
Definition template.h:190
static DC_PUBLIC void remove_at(SELF *self, size_t at, size_t count)
Definition template.h:211
static DC_PUBLIC ITEM * push(SELF *self, ITEM item)
Definition template.h:263
static DC_PUBLIC DC_INLINE size_t position(ITER const *DC_RESTRICT iter)
Definition template.h:394
static DC_PUBLIC ITEM pop(SELF *self)
Definition template.h:289
static DC_PUBLIC void transfer_reverse(SELF *source, SELF *target, size_t to_move)
Definition template.h:348
static DC_PUBLIC SELF new_with_defaults(size_t size, ITEM default_item, ref alloc_ref)
Definition template.h:88
#define ITEM
Definition template.h:25
#define DC_TRAIT_VECTOR(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 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
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 DC_ASSERT(expr,...)
Definition panic.h:37
#define DC_ASSUME(expr,...)
Definition panic.h:57
#define DC_LIKELY(x)
Definition panic.h:48
size_t pos
Definition template.h:280
ITEM const * data
Definition template.h:425
mutation_version vec_version
Definition template.h:428
size_t size
Definition template.h:427
mutation_version version
Definition template.h:328
size_t size
Definition template.h:378
ITEM * data
Definition template.h:376
size_t pos
Definition template.h:233
mutation_tracker iterator_invalidation_tracker
Definition template.h:87
dc_gdb_marker derive_c_vector_marker
Definition template.h:47
Debug format helpers for debug printin data structures.
Definition fmt.h:11
A queue comprised of an extendable circular buffer.
Definition template.h:16
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