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/core/helpers.h>
#include <derive-c/core/panic.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

Functions

static void item_delete (item_t *UNUSED(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 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 bool empty (ITER const *iter)
static ITEMnext (ITER *iter)
static ITER get_iter (SELF *self)
static void delete (SELF *self)
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)

Macro Definition Documentation

◆ ITEM

#define ITEM   item_t

Definition at line 22 of file template.h.

◆ ITEM_CLONE

#define ITEM_CLONE   item_clone

Definition at line 26 of file template.h.

◆ ITEM_DELETE

#define ITEM_DELETE   item_delete

Definition at line 24 of file template.h.

Function Documentation

◆ clone()

SELF clone ( SELF const * self)
static

Definition at line 295 of file template.h.

295 {
296 DEBUG_ASSERT(self);
297 ITEM* new_data = NULL;
298 size_t tail = 0;
299 size_t new_capacity = 0;
300 size_t old_size = NS(SELF, size)(self);
301
302 if (old_size > 0) {
303 new_capacity = next_power_of_2(old_size);
304 new_data = (ITEM*)NS(ALLOC, malloc)(self->alloc, new_capacity * sizeof(ITEM));
305 ASSERT(new_data);
306
307 ITER_CONST iter = NS(SELF, get_iter_const)(self);
308 ITEM const* item;
309 while ((item = NS(ITER_CONST, next)(&iter))) {
310 new_data[tail] = ITEM_CLONE(item);
311 tail++;
312 }
313 tail--;
314 }
315 return (SELF){
316 .data = new_data,
317 .capacity = new_capacity,
318 .head = 0,
319 .tail = tail,
320 .empty = self->empty,
321 .alloc = self->alloc,
322 .derive_c_circular = (gdb_marker){},
323 };
324}
static void * malloc(SELF *self, size_t size)
Definition template.h:23
#define ALLOC
Definition template.h:56
#define ITEM
Definition template.h:55
#define NS(pre, post)
Definition helpers.h:6
static size_t next_power_of_2(size_t x)
Definition helpers.h:31
#define ASSERT(expr,...)
Definition panic.h:15
#define DEBUG_ASSERT(expr)
Definition panic.h:34
#define SELF
Definition def.h:51
static ITER_CONST get_iter_const(SELF const *self)
Definition template.h:387
static INDEX_TYPE size(SELF const *self)
Definition template.h:220
static IV_PAIR const * next(ITER *iter)
Definition template.h:301
#define ITEM_CLONE
Definition template.h:26
Here is the call graph for this function:

◆ delete()

void delete ( SELF * self)
static

Definition at line 252 of file template.h.

252 {
253 DEBUG_ASSERT(self);
254 if (self->data) {
255 ITER iter = NS(SELF, get_iter)(self);
256 ITEM* item;
257 while ((item = NS(ITER, next)(&iter))) {
258 ITEM_DELETE(item);
259 }
260 NS(ALLOC, free)(self->alloc, self->data);
261 }
262}
static void free(SELF *self, void *ptr)
Definition template.h:56
ITEM * data
Definition template.h:38
ALLOC * alloc
Definition template.h:63
static ITER get_iter(SELF *self)
Definition template.h:318
#define ITEM_DELETE
Definition template.h:24
Here is the call graph for this function:

◆ empty() [1/3]

bool empty ( ITER const * iter)
static

Definition at line 229 of file template.h.

229 {
230 DEBUG_ASSERT(iter);
231 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
232}
static ITEM const * try_read_from_front(SELF const *self, size_t index)
Definition template.h:191
Here is the call graph for this function:

◆ empty() [2/3]

bool empty ( ITER_CONST const * iter)
static

Definition at line 272 of file template.h.

272 {
273 DEBUG_ASSERT(iter);
274 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
275}
Here is the call graph for this function:

◆ empty() [3/3]

bool empty ( SELF const * self)
static

Definition at line 76 of file template.h.

76 {
77 DEBUG_ASSERT(self);
78 if (self->empty) {
79 DEBUG_ASSERT(self->head == self->tail);
80 }
81 return self->empty;
82}
Here is the call graph for this function:

◆ get_iter()

ITER get_iter ( SELF * self)
static

Definition at line 244 of file template.h.

244 {
245 DEBUG_ASSERT(self);
246 return (ITER){
247 .circular = self,
248 .position = 0,
249 };
250}
Here is the call graph for this function:

◆ get_iter_const()

ITER_CONST get_iter_const ( SELF const * self)
static

Definition at line 287 of file template.h.

287 {
288 DEBUG_ASSERT(self);
289 return (ITER_CONST){
290 .circular = self,
291 .position = 0,
292 };
293}
Here is the call graph for this function:

◆ item_clone()

item_t item_clone ( item_t const * self)
static

Definition at line 25 of file template.h.

25{ return *self; }

◆ item_delete()

void item_delete ( item_t * UNUSEDself)
static

Definition at line 23 of file template.h.

23{}

◆ new()

SELF new ( ALLOC * alloc)
static

Definition at line 47 of file template.h.

47 {
48 return (SELF){.data = NULL,
49 .head = 0,
50 .tail = 0,
51 .empty = true,
52 .alloc = alloc,
53 .derive_c_circular = (gdb_marker){}};
54}

◆ new_with_capacity_for()

SELF new_with_capacity_for ( size_t capacity_for,
ALLOC * alloc )
static

Definition at line 56 of file template.h.

56 {
57 if (capacity_for == 0) {
58 return NS(SELF, new)(alloc);
59 }
60 size_t capacity = next_power_of_2(capacity_for);
61 ASSERT(is_power_of_2(capacity));
62 ITEM* data = (ITEM*)NS(ALLOC, malloc)(alloc, capacity * sizeof(ITEM));
63 ASSERT(data);
64
65 return (SELF){
66 .data = data,
67 .capacity = capacity,
68 .head = 0,
69 .tail = 0,
70 .empty = true,
71 .alloc = alloc,
72 .derive_c_circular = (gdb_marker){},
73 };
74}
static bool is_power_of_2(size_t x)
Definition helpers.h:46
static ITEM * data(SELF *self)
Definition template.h:215
Here is the call graph for this function:

◆ next() [1/2]

ITEM * next ( ITER * iter)
static

Definition at line 234 of file template.h.

234 {
235 DEBUG_ASSERT(iter);
236 ITEM* item = NS(SELF, try_write_from_front)(iter->circular, iter->position);
237 if (!item) {
238 return NULL;
239 }
240 iter->position++;
241 return item;
242}
static ITEM * try_write_from_front(SELF *self, size_t index)
Definition template.h:207
Here is the call graph for this function:

◆ next() [2/2]

ITEM const * next ( ITER_CONST * iter)
static

Definition at line 277 of file template.h.

277 {
278 DEBUG_ASSERT(iter);
279 ITEM const* item = NS(SELF, try_read_from_front)(iter->circular, iter->position);
280 if (!item) {
281 return NULL;
282 }
283 iter->position++;
284 return item;
285}
Here is the call graph for this function:

◆ pop_back()

ITEM pop_back ( SELF * self)
static

Definition at line 174 of file template.h.

174 {
175 DEBUG_ASSERT(self);
176 ASSERT(!NS(SELF, empty)(self));
177
178 ITEM value = self->data[self->tail];
179 if (self->head == self->tail) {
180 self->empty = true;
181 } else {
182 if (self->tail == 0) {
183 self->tail = self->capacity - 1;
184 } else {
185 self->tail--;
186 }
187 }
188 return value;
189}
size_t head
Definition template.h:40
size_t capacity
Definition template.h:97
bool empty
Definition template.h:42
size_t tail
Definition template.h:41
static bool empty(ITER const *iter)
Definition template.h:293
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 161 of file template.h.

161 {
162 DEBUG_ASSERT(self);
163 ASSERT(!NS(SELF, empty)(self));
164
165 ITEM value = self->data[self->head];
166 if (self->head == self->tail) {
167 self->empty = true;
168 } else {
169 self->head = modulus_power_of_2_capacity(self->head + 1, self->capacity);
170 }
171 return value;
172}
static size_t modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition helpers.h:48
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 135 of file template.h.

135 {
136 DEBUG_ASSERT(self);
137 NS(SELF, reserve)(self, NS(SELF, size)(self) + 1);
138
139 if (!self->empty) {
140 self->tail = modulus_power_of_2_capacity(self->tail + 1, self->capacity);
141 }
142 self->data[self->tail] = item;
143 self->empty = false;
144}
static void reserve(SELF *self, size_t new_capacity_for)
Definition template.h:97
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 146 of file template.h.

146 {
147 DEBUG_ASSERT(self);
148 NS(SELF, reserve)(self, NS(SELF, size)(self) + 1);
149
150 if (!self->empty) {
151 if (self->head == 0) {
152 self->head = self->capacity - 1;
153 } else {
154 self->head--;
155 }
156 }
157 self->data[self->head] = item;
158 self->empty = false;
159}
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 200 of file template.h.

200 {
201 DEBUG_ASSERT(self);
202 ITEM const* item = NS(SELF, try_read_from_front)(self, index);
203 ASSERT(item);
204 return item;
205}
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 97 of file template.h.

97 {
98 DEBUG_ASSERT(self);
99
100 if (new_capacity_for > self->capacity) {
101 size_t new_capacity = next_power_of_2(new_capacity_for * 2);
102 ITEM* new_data =
103 (ITEM*)NS(ALLOC, realloc)(self->alloc, self->data, new_capacity * sizeof(ITEM));
104 ASSERT(new_data);
105 if (self->head > self->tail) {
106 // The queue wraps at the old end, so we need to either:
107 // - shift elements of the tail around into the new area at the end of the buffer
108 // - shift the head of the queue along to the new arena
109 // need to move the wrapped around part to the end of the new buffer
110 size_t old_capacity = self->capacity;
111 size_t additional_capacity = new_capacity - old_capacity;
112 size_t front_tail_items = self->tail + 1;
113 size_t back_head_items = old_capacity - self->head;
114
115 if (front_tail_items > back_head_items) {
116 size_t new_head = self->head + additional_capacity;
117 memmove(&new_data[new_head], &new_data[self->head], back_head_items * sizeof(ITEM));
118 self->head = new_head;
119 } else {
120 // as we go to the next power of 2 each time, the additional capacity is always >=
121 // the number of items at the front. As a result, we never need to consider:
122 // - Only copying the first n from the front
123 // - Shifting some of the front items to the start of the buffer
124 DEBUG_ASSERT(front_tail_items <= additional_capacity);
125
126 memcpy(&new_data[old_capacity], &new_data[0], front_tail_items * sizeof(ITEM));
127 self->tail = old_capacity + front_tail_items - 1;
128 }
129 }
130 self->capacity = new_capacity;
131 self->data = new_data;
132 }
133}
static void * realloc(SELF *self, void *ptr, size_t size)
Definition template.h:45
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 84 of file template.h.

84 {
85 DEBUG_ASSERT(self);
86 if (self->empty) {
87 DEBUG_ASSERT(self->tail == self->head);
88 return 0;
89 }
90
91 if (self->head <= self->tail) {
92 return (self->tail - self->head) + 1;
93 }
94 return (self->capacity - self->head) + self->tail + 1;
95}
Here is the call graph for this function:

◆ try_read_from_front()

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

Definition at line 191 of file template.h.

191 {
192 DEBUG_ASSERT(self);
193 if (index < NS(SELF, size)(self)) {
194 size_t real_index = modulus_power_of_2_capacity(self->head + index, self->capacity);
195 return &self->data[real_index];
196 }
197 return NULL;
198}
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 207 of file template.h.

207 {
208 DEBUG_ASSERT(self);
209 if (index < NS(SELF, size)(self)) {
210 size_t real_index = modulus_power_of_2_capacity(self->head + index, self->capacity);
211 return &self->data[real_index];
212 }
213 return NULL;
214}
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 216 of file template.h.

216 {
217 DEBUG_ASSERT(self);
218 ITEM* value = NS(SELF, try_write_from_front)(self, index);
219 ASSERT(value);
220 return value;
221}
Here is the call graph for this function:
Here is the caller graph for this function: