21#define DEFINE_LIST(name, type) DEFINE_LIST_WITH_DTOR(name, type, name##_noop_)
29#define DEFINE_LIST_WITH_DTOR(name, type, dtor) \
47 static inline UNUSED void name##_noop_(type item) { (void)item; } \
50 static inline size_t name##_size(const name##_t *list) { \
51 assert(list != NULL); \
56 static inline UNUSED bool name##_is_empty(const name##_t *list) { \
57 assert(list != NULL); \
58 return name##_size(list) == 0; \
61 static inline int name##_try_append(name##_t *list, type item) { \
62 assert(list != NULL); \
65 if (list->size == list->capacity) { \
66 const size_t c = list->capacity == 0 ? 1 : (list->capacity * 2); \
69 if (SIZE_MAX / c < sizeof(type)) { \
73 type *base = (type *)realloc(list->base, c * sizeof(type)); \
79 memset((char *)base + list->capacity * sizeof(type), 0, \
80 (c - list->capacity) * sizeof(type)); \
93 if (list->head + list->size > list->capacity) { \
94 const size_t prefix = list->capacity - list->head; \
95 const size_t new_head = c - prefix; \
96 memmove(&base[new_head], &base[list->head], prefix * sizeof(type)); \
97 list->head = new_head; \
101 list->capacity = c; \
104 list->base[(list->head + list->size) % list->capacity] = item; \
110 static inline void name##_append(name##_t *list, type item) { \
111 int rc = name##_try_append(list, item); \
113 fprintf(stderr, "realloc failed: %s\n", strerror(rc)); \
114 graphviz_exit(EXIT_FAILURE); \
124 static inline type name##_get(const name##_t *list, size_t index) { \
125 assert(list != NULL); \
126 assert(index < list->size && "index out of bounds"); \
127 return list->base[(list->head + index) % list->capacity]; \
142 static inline type *name##_at(name##_t *list, size_t index) { \
143 assert(list != NULL); \
144 assert(index < list->size && "index out of bounds"); \
145 return &list->base[(list->head + index) % list->capacity]; \
149 static inline UNUSED type *name##_front(name##_t *list) { \
150 assert(list != NULL); \
151 assert(!name##_is_empty(list)); \
152 return name##_at(list, 0); \
156 static inline UNUSED type *name##_back(name##_t *list) { \
157 assert(list != NULL); \
158 assert(!name##_is_empty(list)); \
159 return name##_at(list, name##_size(list) - 1); \
168 static inline void name##_set(name##_t *list, size_t index, type item) { \
169 assert(list != NULL); \
170 assert(index < name##_size(list) && "index out of bounds"); \
171 type *target = name##_at(list, index); \
181 static inline UNUSED void name##_remove(name##_t *list, const type item) { \
182 assert(list != NULL); \
184 for (size_t i = 0; i < list->size; ++i) { \
186 type *candidate = name##_at(list, i); \
187 if (memcmp(candidate, &item, sizeof(type)) == 0) { \
193 for (size_t j = i + 1; j < list->size; ++j) { \
194 type *replacement = name##_at(list, j); \
195 *candidate = *replacement; \
196 candidate = replacement; \
205 static inline void name##_clear(name##_t *list) { \
206 assert(list != NULL); \
208 for (size_t i = 0; i < list->size; ++i) { \
209 dtor(name##_get(list, i)); \
223 static inline UNUSED void name##_reserve(name##_t *list, size_t capacity) { \
224 assert(list != NULL); \
227 if (list->capacity >= capacity) { \
231 list->base = (type *)gv_recalloc(list->base, list->capacity, capacity, \
245 if (list->head + list->size > list->capacity) { \
246 const size_t prefix = list->capacity - list->head; \
247 const size_t new_head = capacity - prefix; \
248 memmove(&list->base[new_head], &list->base[list->head], \
249 prefix * sizeof(type)); \
250 list->head = new_head; \
253 list->capacity = capacity; \
262 static inline UNUSED void name##_resize(name##_t *list, size_t size, \
264 assert(list != NULL); \
266 if (list->size < size) { \
268 while (list->size < size) { \
269 name##_append(list, value); \
271 } else if (list->size > size) { \
273 while (list->size > size) { \
274 dtor(name##_get(list, list->size - 1)); \
281 static inline UNUSED bool name##_contains( \
282 const name##_t *haystack, const type needle, \
283 bool (*eq)(const type a, const type b)) { \
284 assert(haystack != NULL); \
285 assert(eq != NULL); \
287 for (size_t i = 0; i < name##_size(haystack); ++i) { \
288 if (eq(name##_get(haystack, i), needle)) { \
296 static inline UNUSED name##_t name##_copy(const name##_t *source) { \
297 assert(source != NULL); \
299 name##_t destination = {(type *)gv_calloc(source->capacity, sizeof(type)), \
300 0, 0, source->capacity}; \
301 for (size_t i = 0; i < source->size; ++i) { \
302 name##_append(&destination, name##_get(source, i)); \
304 return destination; \
321 static inline UNUSED bool name##_is_contiguous(const name##_t *list) { \
322 assert(list != NULL); \
323 return list->head + list->size <= list->capacity; \
327 static inline void name##_sync(name##_t *list) { \
328 assert(list != NULL); \
332 while (list->head != 0) { \
334 assert(list->capacity > 0); \
335 type replacement = list->base[0]; \
336 for (size_t i = list->capacity - 1; i != SIZE_MAX; --i) { \
337 type temp = list->base[i]; \
338 list->base[i] = replacement; \
339 replacement = temp; \
345 assert(name##_is_contiguous(list)); \
349 static inline UNUSED void name##_sort( \
350 name##_t *list, int (*cmp)(const type *a, const type *b)) { \
351 assert(list != NULL); \
352 assert(cmp != NULL); \
356 int (*compar)(const void *, const void *) = \
357 (int (*)(const void *, const void *))cmp; \
358 if (list->size > 0) { \
359 qsort(list->base, list->size, sizeof(type), compar); \
364 static inline UNUSED void name##_reverse(name##_t *list) { \
365 assert(list != NULL); \
367 for (size_t i = 0; i < name##_size(list) / 2; ++i) { \
368 type const temp1 = name##_get(list, i); \
369 type const temp2 = name##_get(list, name##_size(list) - i - 1); \
370 name##_set(list, i, temp2); \
371 name##_set(list, name##_size(list) - i - 1, temp1); \
376 static inline UNUSED void name##_shrink_to_fit(name##_t *list) { \
377 assert(list != NULL); \
381 if (list->capacity > list->size) { \
382 list->base = (type *)gv_recalloc(list->base, list->capacity, list->size, \
384 list->capacity = list->size; \
389 static inline UNUSED void name##_free(name##_t *list) { \
390 assert(list != NULL); \
391 name##_clear(list); \
393 memset(list, 0, sizeof(*list)); \
397 static inline UNUSED void name##_push_back(name##_t *list, type value) { \
398 name##_append(list, value); \
402 static inline UNUSED type name##_pop_front(name##_t *list) { \
403 assert(list != NULL); \
404 assert(list->size > 0); \
406 type value = name##_get(list, 0); \
411 list->head = (list->head + 1) % list->capacity; \
418 static inline UNUSED type name##_pop_back(name##_t *list) { \
419 assert(list != NULL); \
420 assert(list->size > 0); \
422 type value = name##_get(list, list->size - 1); \
442 static inline UNUSED name##_t name##_attach(type *data, size_t size) { \
443 assert(data != NULL || size == 0); \
444 name##_t list = {data, 0, size, size}; \
457 static inline UNUSED type *name##_detach(name##_t *list) { \
458 assert(list != NULL); \
460 type *data = list->base; \
461 memset(list, 0, sizeof(*list)); \
Memory allocation wrappers that exit on failure.
abstraction for squashing compiler warnings for unused symbols