Derive-C
Loading...
Searching...
No Matches
template.h
Go to the documentation of this file.
1
2
3#include <stdbool.h>
4#include <stddef.h>
5#include <stdint.h>
6#include <string.h>
7
10
13
14#if !defined ITEM
15 #if !defined __clang_analyzer__
16 #error "The contained type must be defined for a queue template"
17 #endif
18
19typedef struct {
20 int x;
21} item_t;
22 #define ITEM item_t
23static void item_delete(item_t* UNUSED(self)) {}
24 #define ITEM_DELETE item_delete
25static item_t item_clone(item_t const* self) { return *self; }
26 #define ITEM_CLONE item_clone
27#endif
28
29#if !defined ITEM_DELETE
30 #define ITEM_DELETE(value)
31#endif
32
33#if !defined ITEM_CLONE
34 #define ITEM_CLONE(value) (*(value))
35#endif
36
37typedef struct {
39 size_t capacity;
40 size_t head; /* Index of the first element */
41 size_t tail; /* Index of the last element */
42 bool empty; /* Used to differentiate between head == tail when 1 element, or empty */
43 ALLOC* alloc;
45} SELF;
46
47static SELF NS(SELF, new)(ALLOC* alloc) {
48 return (SELF){.data = NULL,
49 .head = 0,
50 .tail = 0,
51 .empty = true,
52 .alloc = alloc,
53 .derive_c_circular = (gdb_marker){}};
54}
55
56static SELF NS(SELF, new_with_capacity_for)(size_t capacity_for, ALLOC* alloc) {
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}
75
76static bool NS(SELF, empty)(SELF const* self) {
77 DEBUG_ASSERT(self);
78 if (self->empty) {
79 DEBUG_ASSERT(self->head == self->tail);
80 }
81 return self->empty;
82}
83
84static size_t NS(SELF, size)(SELF const* self) {
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}
96
97static void NS(SELF, reserve)(SELF* self, size_t new_capacity_for) {
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}
134
135static void NS(SELF, push_back)(SELF* self, ITEM item) {
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}
145
146static void NS(SELF, push_front)(SELF* self, ITEM item) {
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}
160
161static ITEM NS(SELF, pop_front)(SELF* self) {
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}
173
174static ITEM NS(SELF, pop_back)(SELF* self) {
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}
190
191static ITEM const* NS(SELF, try_read_from_front)(SELF const* self, size_t index) {
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}
199
200static ITEM const* NS(SELF, read_from_front)(SELF const* self, size_t index) {
201 DEBUG_ASSERT(self);
202 ITEM const* item = NS(SELF, try_read_from_front)(self, index);
203 ASSERT(item);
204 return item;
205}
206
207static ITEM* NS(SELF, try_write_from_front)(SELF* self, size_t index) {
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}
215
216static ITEM* NS(SELF, write_from_front)(SELF* self, size_t index) {
217 DEBUG_ASSERT(self);
218 ITEM* value = NS(SELF, try_write_from_front)(self, index);
219 ASSERT(value);
220 return value;
221}
222
223#define ITER NS(SELF, iter)
224typedef struct {
225 SELF* circular;
226 size_t position;
227} ITER;
228
229static bool NS(ITER, empty)(ITER const* iter) {
230 DEBUG_ASSERT(iter);
231 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
232}
233
234static ITEM* NS(ITER, next)(ITER* iter) {
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}
243
244static ITER NS(SELF, get_iter)(SELF* self) {
245 DEBUG_ASSERT(self);
246 return (ITER){
247 .circular = self,
248 .position = 0,
249 };
250}
251
252static void NS(SELF, delete)(SELF* self) {
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}
263
264#undef ITER
265
266#define ITER_CONST NS(SELF, iter_const)
267typedef struct {
268 SELF const* circular;
269 size_t position;
270} ITER_CONST;
271
272static bool NS(ITER_CONST, empty)(ITER_CONST const* iter) {
273 DEBUG_ASSERT(iter);
274 return !NS(SELF, try_read_from_front)(iter->circular, iter->position);
275}
276
277static ITEM const* NS(ITER_CONST, next)(ITER_CONST* iter) {
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}
286
287static ITER_CONST NS(SELF, get_iter_const)(SELF const* self) {
288 DEBUG_ASSERT(self);
289 return (ITER_CONST){
290 .circular = self,
291 .position = 0,
292 };
293}
294
295static SELF NS(SELF, clone)(SELF const* self) {
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}
325
326#undef ITER_CONST
327
328#undef ITEM
329#undef ITEM_DELETE
330#undef ITEM_CLONE
331
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:56
#define ITEM
Definition template.h:55
static bool is_power_of_2(size_t x)
Definition helpers.h:46
#define UNUSED(ident)
Definition helpers.h:16
#define NS(pre, post)
Definition helpers.h:6
static size_t next_power_of_2(size_t x)
Definition helpers.h:31
static size_t modulus_power_of_2_capacity(size_t index, size_t capacity)
Definition helpers.h:48
#define ASSERT(expr,...)
Definition panic.h:15
#define DEBUG_ASSERT(expr)
Definition panic.h:34
#define SELF
Definition def.h:51
size_t head
Definition template.h:40
size_t capacity
Definition template.h:97
gdb_marker derive_c_circular
Definition template.h:44
ITEM * data
Definition template.h:38
bool empty
Definition template.h:42
size_t tail
Definition template.h:41
ALLOC * alloc
Definition template.h:63
A queue comprised of an extendable circular buffer.
Definition template.h:19
int x
Definition template.h:20
static ITER_CONST get_iter_const(SELF const *self)
Definition template.h:387
static bool empty(ITER const *iter)
Definition template.h:293
static ITER get_iter(SELF *self)
Definition template.h:318
static SELF new_with_capacity_for(INDEX_TYPE items, ALLOC *alloc)
Definition template.h:113
static INDEX_TYPE size(SELF const *self)
Definition template.h:220
static IV_PAIR const * next(ITER *iter)
Definition template.h:301
static SELF clone(SELF const *self)
Definition template.h:195
static ITEM const * read_from_front(SELF const *self, size_t index)
Definition template.h:200
static ITEM pop_front(SELF *self)
Definition template.h:161
#define ITEM_DELETE
Definition template.h:24
static ITEM pop_back(SELF *self)
Definition template.h:174
static void reserve(SELF *self, size_t new_capacity_for)
Definition template.h:97
static ITEM * try_write_from_front(SELF *self, size_t index)
Definition template.h:207
static ITEM * write_from_front(SELF *self, size_t index)
Definition template.h:216
static void push_back(SELF *self, ITEM item)
Definition template.h:135
static void item_delete(item_t *UNUSED(self))
Definition template.h:23
#define ITEM_CLONE
Definition template.h:26
static ITEM const * try_read_from_front(SELF const *self, size_t index)
Definition template.h:191
static item_t item_clone(item_t const *self)
Definition template.h:25
static void push_front(SELF *self, ITEM item)
Definition template.h:146
static size_t position(ITER const *iter)
Definition template.h:191
#define ITEM
Definition template.h:22
static ITEM * data(SELF *self)
Definition template.h:215