Graphviz
12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
list.h
Go to the documentation of this file.
1
3
#pragma once
4
5
#include <assert.h>
6
#include <
cgraph/alloc.h
>
7
#include <
cgraph/exit.h
>
8
#include <errno.h>
9
#include <stdbool.h>
10
#include <stdint.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
15
#ifdef __GNUC__
16
#define LIST_UNUSED __attribute__((unused))
17
#else
18
#define LIST_UNUSED
/* nothing */
19
#endif
20
26
#define DEFINE_LIST(name, type) DEFINE_LIST_WITH_DTOR(name, type, NULL)
27
34
#define DEFINE_LIST_WITH_DTOR(name, type, dtor) \
35
\
36
\
41
typedef struct { \
42
type *data;
/* backing storage */
\
43
size_t size;
/* number of elements in the list */
\
44
size_t capacity;
/* available storage slots */
\
45
} name##_t; \
46
\
47
\
48
static inline LIST_UNUSED size_t name##_size(const name##_t *list) { \
49
assert(list != NULL); \
50
return list->size; \
51
} \
52
\
53
\
54
static inline LIST_UNUSED bool name##_is_empty(const name##_t *list) { \
55
assert(list != NULL); \
56
return name##_size(list) == 0; \
57
} \
58
\
59
static inline int name##_try_append(name##_t *list, type item) { \
60
assert(list != NULL); \
61
\
62
/* do we need to expand the backing storage? */
\
63
if (list->size == list->capacity) { \
64
const size_t c = list->capacity == 0 ? 1 : (list->capacity * 2); \
65
\
66
/* will the calculation of the new size overflow? */
\
67
if (SIZE_MAX / c < sizeof(type)) { \
68
return ERANGE; \
69
} \
70
\
71
type *data = (type *)realloc(list->data, c * sizeof(type)); \
72
if (data == NULL) { \
73
return ENOMEM; \
74
} \
75
\
76
/* zero the new memory */
\
77
memset((char *)data + list->capacity * sizeof(type), 0, \
78
(c - list->capacity) * sizeof(type)); \
79
\
80
list->data = data; \
81
list->capacity = c; \
82
} \
83
\
84
list->data[list->size] = item; \
85
++list->size; \
86
\
87
return 0; \
88
} \
89
\
90
static inline LIST_UNUSED void name##_append(name##_t *list, type item) { \
91
int rc = name##_try_append(list, item); \
92
if (rc != 0) { \
93
fprintf(stderr, "realloc failed: %s\n", strerror(rc)); \
94
graphviz_exit(EXIT_FAILURE); \
95
} \
96
} \
97
\
98
\
104
static inline LIST_UNUSED type name##_get(const name##_t *list, \
105
size_t index) { \
106
assert(list != NULL); \
107
assert(index < list->size && "index out of bounds"); \
108
return list->data[index]; \
109
} \
110
\
111
\
117
static inline LIST_UNUSED void name##_set(name##_t *list, size_t index, \
118
type item) { \
119
assert(list != NULL); \
120
assert(index < list->size && "index out of bounds"); \
121
void (*dtor_)(type) = (void (*)(type))(dtor); \
122
if (dtor_ != NULL) { \
123
dtor_(list->data[index]); \
124
} \
125
list->data[index] = item; \
126
} \
127
\
128
\
133
static inline LIST_UNUSED void name##_remove(name##_t *list, \
134
const type item) { \
135
assert(list != NULL); \
136
\
137
for (size_t i = 0; i < list->size; ++i) { \
138
/* is this the element we are looking for? */
\
139
if (memcmp(&list->data[i], &item, sizeof(type)) == 0) { \
140
\
141
/* destroy the element we are about to remove */
\
142
void (*dtor_)(type) = (void (*)(type))(dtor); \
143
if (dtor_ != NULL) { \
144
dtor_(list->data[i]); \
145
} \
146
\
147
/* shrink the list */
\
148
size_t remainder = (list->size - i - 1) * sizeof(type); \
149
memmove(&list->data[i], &list->data[i + 1], remainder); \
150
--list->size; \
151
return; \
152
} \
153
} \
154
} \
155
\
156
\
168
static inline LIST_UNUSED type *name##_at(name##_t *list, size_t index) { \
169
assert(list != NULL); \
170
assert(index < list->size && "index out of bounds"); \
171
return &list->data[index]; \
172
} \
173
\
174
\
175
static inline LIST_UNUSED void name##_clear(name##_t *list) { \
176
assert(list != NULL); \
177
\
178
void (*dtor_)(type) = (void (*)(type))(dtor); \
179
if (dtor_ != NULL) { \
180
for (size_t i = 0; i < list->size; ++i) { \
181
dtor_(list->data[i]); \
182
} \
183
} \
184
\
185
list->size = 0; \
186
} \
187
\
188
\
193
static inline LIST_UNUSED void name##_reserve(name##_t *list, \
194
size_t capacity) { \
195
assert(list != NULL); \
196
\
197
/* if we can already fit enough items, nothing to do */
\
198
if (list->capacity >= capacity) { \
199
return; \
200
} \
201
\
202
list->data = (type *)gv_recalloc(list->data, list->capacity, capacity, \
203
sizeof(type)); \
204
list->capacity = capacity; \
205
} \
206
\
207
\
213
static inline LIST_UNUSED void name##_resize(name##_t *list, size_t size, \
214
type value) { \
215
assert(list != NULL); \
216
\
217
if (list->size < size) { \
218
/* we are expanding the list */
\
219
while (list->size < size) { \
220
name##_append(list, value); \
221
} \
222
} else if (list->size > size) { \
223
/* we are shrinking the list */
\
224
while (list->size > size) { \
225
void (*dtor_)(type) = (void (*)(type))(dtor); \
226
if (dtor_ != NULL) { \
227
dtor_(list->data[list->size - 1]); \
228
} \
229
--list->size; \
230
} \
231
} \
232
} \
233
\
234
\
235
static inline LIST_UNUSED bool name##_contains( \
236
const name##_t *haystack, const type needle, \
237
bool (*eq)(const type a, const type b)) { \
238
assert(haystack != NULL); \
239
assert(eq != NULL); \
240
\
241
for (size_t i = 0; i < haystack->size; ++i) { \
242
if (eq(haystack->data[i], needle)) { \
243
return true; \
244
} \
245
} \
246
return false; \
247
} \
248
\
249
\
250
static inline LIST_UNUSED name##_t name##_copy(const name##_t *source) { \
251
assert(source != NULL); \
252
\
253
name##_t destination = {(type *)gv_calloc(source->capacity, sizeof(type)), \
254
source->size, source->capacity}; \
255
memcpy(destination.data, source->data, source->size * sizeof(type)); \
256
return destination; \
257
} \
258
\
259
\
260
static inline LIST_UNUSED void name##_sort( \
261
name##_t *list, int (*cmp)(const type *a, const type *b)) { \
262
assert(list != NULL); \
263
assert(cmp != NULL); \
264
\
265
int (*compar)(const void *, const void *) = \
266
(int (*)(const void *, const void *))cmp; \
267
if (list->size > 0) { \
268
qsort(list->data, list->size, sizeof(type), compar); \
269
} \
270
} \
271
\
272
\
273
static inline LIST_UNUSED void name##_reverse(name##_t *list) { \
274
assert(list != NULL); \
275
\
276
if (list->size == 0) { \
277
return; \
278
} \
279
\
280
size_t left = 0; \
281
size_t right = list->size - 1; \
282
while (left < right) { \
283
type temp = list->data[left]; \
284
list->data[left] = list->data[right]; \
285
list->data[right] = temp; \
286
++left; \
287
--right; \
288
} \
289
} \
290
\
291
\
292
static inline LIST_UNUSED void name##_shrink_to_fit(name##_t *list) { \
293
assert(list != NULL); \
294
\
295
if (list->capacity > list->size) { \
296
list->data = (type *)gv_recalloc(list->data, list->capacity, list->size, \
297
sizeof(type)); \
298
list->capacity = list->size; \
299
} \
300
} \
301
\
302
\
303
static inline LIST_UNUSED void name##_free(name##_t *list) { \
304
assert(list != NULL); \
305
name##_clear(list); \
306
free(list->data); \
307
memset(list, 0, sizeof(*list)); \
308
} \
309
\
310
\
311
static inline LIST_UNUSED void name##_push(name##_t *list, type value) { \
312
name##_append(list, value); \
313
} \
314
\
315
\
316
static inline LIST_UNUSED type name##_pop(name##_t *list) { \
317
assert(list != NULL); \
318
assert(list->size > 0); \
319
\
320
type value = list->data[list->size - 1]; \
321
\
322
/* do not call `dtor` because we are transferring ownership of the removed \
323
* element to the caller \
324
*/
\
325
--list->size; \
326
\
327
return value; \
328
} \
329
\
330
\
340
static inline LIST_UNUSED name##_t name##_attach(type *data, size_t size) { \
341
assert(data != NULL || size == 0); \
342
name##_t list = {data, size, size}; \
343
return list; \
344
} \
345
\
346
\
355
static inline LIST_UNUSED type *name##_detach(name##_t *list) { \
356
assert(list != NULL); \
357
type *data = list->data; \
358
memset(list, 0, sizeof(*list)); \
359
return data; \
360
}
alloc.h
Memory allocation wrappers that exit on failure.
exit.h
lib
cgraph
list.h
Generated by
1.9.8