Derive-C
Loading...
Searching...
No Matches
template.h File Reference
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <derive-c/container/queue/trait.h>
#include <derive-c/core/debug/gdb_marker.h>
#include <derive-c/core/debug/memory_tracker.h>
#include <derive-c/core/debug/mutation_tracker.h>
#include <derive-c/core/prelude.h>
#include <derive-c/core/alloc/def.h>
#include <derive-c/core/self/def.h>
#include <derive-c/core/alloc/undef.h>
#include <derive-c/core/self/undef.h>
Include dependency graph for template.h:

Go to the source code of this file.

Classes

struct  item_t
 A queue comprised of an extendable circular buffer. More...
struct  SELF
 An allocator that prints to stdout when it allocates or frees memory. More...

Macros

#define ITEM   item_t
#define ITEM_DELETE   item_delete
#define ITEM_CLONE   item_clone
#define INVARIANT_CHECK(self)

Typedefs

typedef ITEM item_t
typedef ITEMitem

Functions

static void item_delete (item_t *self)
static item_t item_clone (item_t const *self)
static SELF new (ALLOC *alloc)
static SELF new_with_capacity_for (size_t capacity_for, ALLOC *alloc)
static bool empty (SELF const *self)
static size_t size (SELF const *self)
static void PRIV set_inaccessible_memory_caps (SELF *self, memory_tracker_capability cap)
static void reserve (SELF *self, size_t new_capacity_for)
static void push_back (SELF *self, ITEM item)
static void push_front (SELF *self, ITEM item)
static ITEM pop_front (SELF *self)
static ITEM pop_back (SELF *self)
static ITEM const * try_read_from_front (SELF const *self, size_t index)
static ITEM const * read_from_front (SELF const *self, size_t index)
static ITEMtry_write_from_front (SELF *self, size_t index)
static ITEMwrite_from_front (SELF *self, size_t index)
static ITEM const * try_read_from_back (SELF const *self, size_t index)
static ITEM const * read_from_back (SELF const *self, size_t index)
static ITEMtry_write_from_back (SELF *self, size_t index)
static ITEMwrite_from_back (SELF *self, size_t index)
static bool empty_item (ITEM *const *item)
static bool empty (ITER const *iter)
static ITEMnext (ITER *iter)
static ITER get_iter (SELF *self)
static void delete (SELF *self)
static bool empty_item (ITEM const *const *item)
static bool empty (ITER_CONST const *iter)
static ITEM const * next (ITER_CONST *iter)
static ITER_CONST get_iter_const (SELF const *self)
static SELF clone (SELF const *self)
 TRAIT_QUEUE (SELF)

Macro Definition Documentation

◆ INVARIANT_CHECK

#define INVARIANT_CHECK ( self)
Value:
ASSUME(self); \
ASSUME((self)->alloc); \
ASSUME( \
WHEN(!(self)->empty, (self)->head < (self)->capacity && (self)->tail < (self)->capacity)); \
ASSUME(WHEN((self)->empty, (self)->head == (self)->tail)); \
ASSUME(WHEN(!(self)->data, (self)->head == 0 && (self)->tail == 0));
static bool empty(ITER const *iter)
Definition template.h:361
static ITEM * data(SELF *self)
Definition template.h:259
#define ASSUME(expr,...)
Definition macros.h:62
#define WHEN(cond, expr)
Definition macros.h:57

Definition at line 54 of file template.h.

54#define INVARIANT_CHECK(self) \
55 ASSUME(self); \
56 ASSUME((self)->alloc); \
57 ASSUME( \
58 WHEN(!(self)->empty, (self)->head < (self)->capacity && (self)->tail < (self)->capacity)); \
59 ASSUME(WHEN((self)->empty, (self)->head == (self)->tail)); \
60 ASSUME(WHEN(!(self)->data, (self)->head == 0 && (self)->tail == 0));

◆ ITEM

#define ITEM   item_t

Definition at line 25 of file template.h.

◆ ITEM_CLONE

#define ITEM_CLONE   item_clone

Definition at line 29 of file template.h.

◆ ITEM_DELETE

#define ITEM_DELETE   item_delete

Definition at line 27 of file template.h.

Typedef Documentation

◆ item

typedef ITEM const* item

Definition at line 323 of file template.h.

◆ item_t

typedef ITEM item_t

Definition at line 40 of file template.h.

Function Documentation

◆ clone()

SELF clone ( SELF const * self)
static

Definition at line 414 of file template.h.

414 {
415 INVARIANT_CHECK(self);
416 ITEM* new_data = NULL;
417 size_t tail = 0;
418 size_t new_capacity = 0;
419 size_t const old_size = NS(SELF, size)(self);
420
421 if (old_size > 0) {
422 new_capacity = next_power_of_2(old_size);
423 new_data = (ITEM*)NS(ALLOC, malloc)(self->alloc, new_capacity * sizeof(ITEM));
424 ASSERT(new_data);
425
426 ITER_CONST iter = NS(SELF, get_iter_const)(self);
427 ITEM const* item;
428 while ((item = NS(ITER_CONST, next)(&iter))) {
429 new_data[tail] = ITEM_CLONE(item);
430 tail++;
431 }
432 tail--;
433 }
434 SELF new_self = {
435 .data = new_data,
436 .capacity = new_capacity,
437 .head = 0,
438 .tail = tail,
439 .empty = self->empty,
440 .alloc = self->alloc,
441 .derive_c_circular = gdb_marker_new(),
442 .iterator_invalidation_tracker = mutation_tracker_new(),
443 };
445 return new_self;
446}
static void * malloc(SELF *self, size_t size)
Definition template.h:25
#define ALLOC
Definition template.h:59
#define ITEM
Definition template.h:58
static ITER_CONST get_iter_const(SELF const *self)
Definition template.h:465
IV_PAIR const * item
Definition template.h:350
static INDEX_TYPE size(SELF const *self)
Definition template.h:275
static IV_PAIR const * next(ITER *iter)
Definition template.h:370
#define INVARIANT_CHECK(self)
Definition template.h:142
static void PRIV set_inaccessible_memory_caps(SELF *self, memory_tracker_capability cap)
Definition template.h:119
#define ITEM_CLONE
Definition template.h:29
static gdb_marker gdb_marker_new()
Definition gdb_marker.h:7
#define ASSERT(expr,...)
Definition macros.h:42
static INLINE CONST size_t next_power_of_2(size_t x)
Definition math.h:8
@ MEMORY_TRACKER_CAP_NONE
static mutation_tracker mutation_tracker_new()
#define NS(pre, post)
Definition namespace.h:4
#define PRIV(name)
Definition namespace.h:6
#define SELF
Definition def.h:52
Here is the call graph for this function:

◆ delete()

void delete ( SELF * self)
static

Definition at line 359 of file template.h.

359 {
360 INVARIANT_CHECK(self);
361 if (self->data) {
362 ITER iter = NS(SELF, get_iter)(self);
363 ITEM* item;
364 while ((item = NS(ITER, next)(&iter))) {
367 sizeof(ITEM));
368 }
370 self->capacity * sizeof(ITEM));
371 NS(ALLOC, free)(self->alloc, self->data);
372 }
373}
static void free(SELF *self, void *ptr)
Definition template.h:58
static ITER get_iter(SELF *self)
Definition template.h:388
#define ITEM_DELETE
Definition template.h:27
@ MEMORY_TRACKER_LVL_CONTAINER
static void memory_tracker_set(memory_tracker_level level, memory_tracker_capability cap, const volatile void *addr, size_t size)
size_t capacity
Definition template.h:124
ITEM * data
Definition template.h:44
ALLOC * alloc
Definition template.h:66
Here is the call graph for this function:

◆ empty() [1/3]

bool empty ( ITER const * iter)
static

Definition at line 333 of file template.h.

333 {
334 ASSUME(iter);
335 mutation_version_check(&iter->version);
336 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
337}
static ITEM const * try_read_from_front(SELF const *self, size_t index)
Definition template.h:255
static void mutation_version_check(mutation_version const *self)
Here is the call graph for this function:

◆ empty() [2/3]

bool empty ( ITER_CONST const * iter)
static

Definition at line 388 of file template.h.

388 {
389 ASSUME(iter);
390 mutation_version_check(&iter->version);
391 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
392}
Here is the call graph for this function:

◆ empty() [3/3]

bool empty ( SELF const * self)
static

Definition at line 98 of file template.h.

98 {
99 ASSUME(self);
100 if (self->empty) {
101 ASSUME(self->head == self->tail);
102 }
103 return self->empty;
104}
Here is the call graph for this function:

◆ empty_item() [1/2]

bool empty_item ( ITEM *const * item)
static

Definition at line 325 of file template.h.

325{ return *item == NULL; }
Here is the call graph for this function:

◆ empty_item() [2/2]

bool empty_item ( ITEM const *const * item)
static

Definition at line 380 of file template.h.

380{ return *item == NULL; }
Here is the call graph for this function:

◆ get_iter()

ITER get_iter ( SELF * self)
static

Definition at line 350 of file template.h.

350 {
351 INVARIANT_CHECK(self);
352 return (ITER){
353 .circular = self,
354 .position = 0,
356 };
357}
static mutation_version mutation_tracker_get(mutation_tracker const *self)
mutation_tracker iterator_invalidation_tracker
Definition template.h:139
Here is the call graph for this function:

◆ get_iter_const()

ITER_CONST get_iter_const ( SELF const * self)
static

Definition at line 405 of file template.h.

405 {
406 INVARIANT_CHECK(self);
407 return (ITER_CONST){
408 .circular = self,
409 .position = 0,
410 .version = mutation_tracker_get(&self->iterator_invalidation_tracker),
411 };
412}
Here is the call graph for this function:

◆ item_clone()

item_t item_clone ( item_t const * self)
static

Definition at line 28 of file template.h.

28{ return *self; }

◆ item_delete()

void item_delete ( item_t * self)
static

Definition at line 26 of file template.h.

26{ (void)self; }

◆ new()

SELF new ( ALLOC * alloc)
static

Definition at line 62 of file template.h.

62 {
63 return (SELF){
64 .data = NULL,
65 .head = 0,
66 .tail = 0,
67 .empty = true,
68 .alloc = alloc,
69 .derive_c_circular = gdb_marker_new(),
70 .iterator_invalidation_tracker = mutation_tracker_new(),
71 };
72}
Here is the call graph for this function:

◆ new_with_capacity_for()

SELF new_with_capacity_for ( size_t capacity_for,
ALLOC * alloc )
static

Definition at line 74 of file template.h.

74 {
75 if (capacity_for == 0) {
76 return NS(SELF, new)(alloc);
77 }
78 size_t const capacity = next_power_of_2(capacity_for);
79 ASSERT(is_power_of_2(capacity));
80 ITEM* data = (ITEM*)NS(ALLOC, malloc)(alloc, capacity * sizeof(ITEM));
81 ASSERT(data);
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 = gdb_marker_new(),
94 .iterator_invalidation_tracker = mutation_tracker_new(),
95 };
96}
static bool INLINE CONST is_power_of_2(size_t x)
Definition math.h:23
Here is the call graph for this function:

◆ next() [1/2]

ITEM * next ( ITER * iter)
static

Definition at line 339 of file template.h.

339 {
340 ASSUME(iter);
341 mutation_version_check(&iter->version);
342 ITEM* item = NS(SELF, try_write_from_front)(iter->circular, iter->position);
343 if (!item) {
344 return NULL;
345 }
346 iter->position++;
347 return item;
348}
static ITEM * try_write_from_front(SELF *self, size_t index)
Definition template.h:270
Here is the call graph for this function:

◆ next() [2/2]

ITEM const * next ( ITER_CONST * iter)
static

Definition at line 394 of file template.h.

394 {
395 ASSUME(iter);
396 mutation_version_check(&iter->version);
397 ITEM const* item = NS(SELF, try_read_from_front)(iter->circular, iter->position);
398 if (!item) {
399 return NULL;
400 }
401 iter->position++;
402 return item;
403}
Here is the call graph for this function:

◆ pop_back()

ITEM pop_back ( SELF * self)
static

Definition at line 235 of file template.h.

235 {
236 INVARIANT_CHECK(self);
238 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}
static void mutation_tracker_mutate(mutation_tracker *self)
size_t head
Definition template.h:46
bool empty
Definition template.h:48
size_t tail
Definition template.h:47
Here is the call graph for this function:
Here is the caller graph for this function:

◆ pop_front()

ITEM pop_front ( SELF * self)
static

Definition at line 216 of file template.h.

216 {
217 INVARIANT_CHECK(self);
219 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 = modulus_power_of_2_capacity(self->head + 1, self->capacity);
231 }
232 return value;
233}
static size_t INLINE CONST modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition math.h:25
static void memory_tracker_check(memory_tracker_level level, memory_tracker_capability cap, const void *addr, size_t size)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ push_back()

void push_back ( SELF * self,
ITEM item )
static

Definition at line 184 of file template.h.

184 {
185 INVARIANT_CHECK(self);
187 NS(SELF, reserve)(self, NS(SELF, size)(self) + 1);
188
189 if (!self->empty) {
190 self->tail = 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}
static void reserve(SELF *self, size_t new_capacity_for)
Definition template.h:135
@ MEMORY_TRACKER_CAP_WRITE
Here is the call graph for this function:
Here is the caller graph for this function:

◆ push_front()

void push_front ( SELF * self,
ITEM item )
static

Definition at line 198 of file template.h.

198 {
199 INVARIANT_CHECK(self);
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}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_from_back()

ITEM const * read_from_back ( SELF const * self,
size_t index )
static

Definition at line 297 of file template.h.

297 {
298 ITEM const* item = NS(SELF, try_read_from_back)(self, index);
299 ASSERT(item);
300 return item;
301}
static ITEM const * try_read_from_back(SELF const *self, size_t index)
Definition template.h:285
Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_from_front()

ITEM const * read_from_front ( SELF const * self,
size_t index )
static

Definition at line 264 of file template.h.

264 {
265 ITEM const* item = NS(SELF, try_read_from_front)(self, index);
266 ASSERT(item);
267 return item;
268}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reserve()

void reserve ( SELF * self,
size_t new_capacity_for )
static

Definition at line 135 of file template.h.

135 {
136 INVARIANT_CHECK(self);
138
139 if (new_capacity_for > self->capacity) {
140 size_t const new_capacity = 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 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 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}
static void * realloc(SELF *self, void *ptr, size_t size)
Definition template.h:47
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_inaccessible_memory_caps()

void PRIV set_inaccessible_memory_caps ( SELF * self,
memory_tracker_capability cap )
static

Definition at line 119 of file template.h.

120 {
121 if (self->empty) {
123 self->capacity * sizeof(ITEM));
124 } else if (self->head <= self->tail) {
126 (self->capacity - (self->tail + 1)) * sizeof(ITEM));
128 self->head * sizeof(ITEM));
129 } else {
131 (self->head - (self->tail + 1)) * sizeof(ITEM));
132 }
133}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ size()

size_t size ( SELF const * self)
static

Definition at line 106 of file template.h.

106 {
107 ASSUME(self);
108 if (self->empty) {
109 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}
Here is the call graph for this function:

◆ TRAIT_QUEUE()

TRAIT_QUEUE ( SELF )

◆ try_read_from_back()

ITEM const * try_read_from_back ( SELF const * self,
size_t index )
static

Definition at line 285 of file template.h.

285 {
286 INVARIANT_CHECK(self);
287 if (index >= NS(SELF, size)(self)) {
288 return NULL;
289 }
290 if (index <= self->tail) {
291 return &self->data[self->tail - index];
292 }
293 size_t const from_end = index - self->tail;
294 return &self->data[self->capacity - from_end];
295}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ try_read_from_front()

ITEM const * try_read_from_front ( SELF const * self,
size_t index )
static

Definition at line 255 of file template.h.

255 {
256 INVARIANT_CHECK(self);
257 if (index < NS(SELF, size)(self)) {
258 size_t const real_index = modulus_power_of_2_capacity(self->head + index, self->capacity);
259 return &self->data[real_index];
260 }
261 return NULL;
262}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ try_write_from_back()

ITEM * try_write_from_back ( SELF * self,
size_t index )
static

Definition at line 303 of file template.h.

303 {
304 INVARIANT_CHECK(self);
305 if (index >= NS(SELF, size)(self)) {
306 return NULL;
307 }
308 if (index <= self->tail) {
309 return &self->data[self->tail - index];
310 }
311 size_t const from_end = index - self->tail;
312 return &self->data[self->capacity - from_end];
313}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ try_write_from_front()

ITEM * try_write_from_front ( SELF * self,
size_t index )
static

Definition at line 270 of file template.h.

270 {
271 INVARIANT_CHECK(self);
272 if (index < NS(SELF, size)(self)) {
273 size_t const real_index = modulus_power_of_2_capacity(self->head + index, self->capacity);
274 return &self->data[real_index];
275 }
276 return NULL;
277}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ write_from_back()

ITEM * write_from_back ( SELF * self,
size_t index )
static

Definition at line 315 of file template.h.

315 {
316 INVARIANT_CHECK(self);
317 ITEM* value = NS(SELF, try_write_from_back)(self, index);
318 ASSERT(value);
319 return value;
320}
static ITEM * try_write_from_back(SELF *self, size_t index)
Definition template.h:303
Here is the call graph for this function:
Here is the caller graph for this function:

◆ write_from_front()

ITEM * write_from_front ( SELF * self,
size_t index )
static

Definition at line 279 of file template.h.

279 {
280 ITEM* value = NS(SELF, try_write_from_front)(self, index);
281 ASSERT(value);
282 return value;
283}
Here is the call graph for this function:
Here is the caller graph for this function: