-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdynarray.h
More file actions
245 lines (221 loc) · 8.44 KB
/
Copy pathdynarray.h
File metadata and controls
245 lines (221 loc) · 8.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#ifndef DYNARRAY
#define DYNARRAY
#include <stdlib.h> // malloc
#include <string.h> // memcpy
/**
* @mainpage Dynamic Array Library: dynarray
*
* @section overview Overview
* A generic dynamic array implementation in C.
* A dynarray of type T can be passed to any function that operates on vanilla T arrays.
* Inspiration taken from this article by solarianprogrammer.
*
* @section features Features
* - Automatic resizing
* - Generic storage using void pointers
* - Bounds-checked access (optional API)
*
* @section usage Basic Usage
* @code
* #include "dynarray.h"
*
* int *v = dynarray_create(int);
*
* for (int i = 0; i < 10; i++) {
* dynarray_push(v, i);
* dynarray_push_rval(v, 100 - i);
* }
*
* for (int i = 0; i < dynarray_length(v); i++) {
* printf("v[%d] -> %d\n", i, v[i]);
* }
*
* dynarray_destroy(v);
* @endcode
* Output:
* @code
* v[0] -> 0
* v[1] -> 100
* v[2] -> 1
* v[3] -> 99
* ...
* v[18] -> 9
* v[19] -> 91
* @endcode
* @section Memory Layout
* The array is heap-allocated and is prefixed with a three-field header containing the buffer's capacity, length, and stride.
*
* - The stride is calculated at creation time as sizeof(T) where T is the datatype you intend to store.
* - The capacity field stores the buffer's size.
* - The length field keeps track of the number of elements stored in the buffer.
*
* Macros defined in dynarray.h allow the capacity, length, and stride attributes to be accessed.
*
* A dynarray is referred to only by a pointer to the first element in its buffer.
* This allows a dynarray to be passed to any function that operates on regular C-arrays.
*/
/* Structure of a dynarray:
* size_t capacity
* size_t length
* size_t stride
* void *memory
*/
enum dynarray_fields {
DYNARRAY_CAPACITY_FIELD,
DYNARRAY_LENGTH_FIELD,
DYNARRAY_STRIDE_FIELD,
DYNARRAY_FIELDS
};
void *_dynarray_create(size_t length, size_t stride);
void _dynarray_destroy(void *arr);
size_t _dynarray_field_get(void *arr, size_t field);
void _dynarray_field_set(void *arr, size_t field, size_t value);
void *_dynarray_resize(void *arr);
void *_dynarray_push(void *arr, void *xptr);
void _dynarray_pop(void *arr, void *dest);
void _dynarray_swap_remove(void *arr, size_t index, void *dest);
const void *dynarray_get(void *arr, size_t index);
void *_dynarray_set(void *arr, size_t index, void *new_value);
#define DYNARRAY_DEFAULT_CAP 1
#define DYNARRAY_RESIZE_FACTOR 2
/**
* @brief Creates a new dynamic array.
*
* Allocates and initializes a new dynamic array with an initial capacity of DYNARRAY_DEFAULT_CAP elements,
* each the size of the element type.
* The returned pointer refers to the start of the usable array memory
*
* @param type Type of elements in array.
* @return Pointer to the newly created dynamic array, or NULL if allocation fails.
*/
#define dynarray_create(type) _dynarray_create(DYNARRAY_DEFAULT_CAP, sizeof(type))
/**
* @brief Creates a new dynamic array with custom size.
*
* Allocates and initializes a new dynamic array with an initial capacity of @p capacity elements,
* each the size of the element type.
* The returned pointer refers to the start of the usable array memory
*
* @param type Type of elements in array.
* @param capacity Initial number of elements the array can hold.
* @return Pointer to the newly created dynamic array, or NULL if allocation fails.
*/
#define dynarray_create_prealloc(type, capacity) _dynarray_create(capacity, sizeof(type))
/**
* @brief Frees a dynamic array.
*
* Deallocates the memory block associated with the dynamic array.
*
* @param arr Pointer to the dynamic array to be destroyed.
*
* @note This function does not modify the caller's pointer; it is the caller's
* responsibility to set it to NULL if needed.
*/
#define dynarray_destroy(arr) _dynarray_destroy(arr)
/**
* @brief Appends a new element to the dynamic array.
*
* Inserts the value pointed to by @p x into the next available position
* in the dynamic array. If the array is full, it will be reallocated with
* increased capacity before insertion.
*
* @param arr Pointer to the dynamic array.
* @param x Pointer to the value to be inserted.
* @return Pointer to the (possibly reallocated) dynamic array, or NULL if
* reallocation fails.
*
* @note If reallocation fails, the original array remains unchanged.
*/
#define dynarray_push(arr, x) arr = _dynarray_push(arr, &x)
/**
* @brief Appends a value expression to the dynamic array.
*
* Evaluates @p x and inserts a copy of that value into the next available position in the dynamic
* array. If the array is full, it will be reallocated with increased capacity before insertion.
*
* Unlike ::dynarray_push, this macro accepts rvalues and arbitrary
* expressions, including literals, function calls, and computed values.
*
* @param arr Pointer to the dynamic array.
* @param x Value or expression to be inserted.
*
* @note The expression @p x is evaluated exactly once.
*
* @note Array expressions passed as @p x undergo array-to-pointer decay,
* which may produce different behavior compared to ::dynarray_push.
*
* @note If reallocation fails, the original array remains unchanged.
*/
#define dynarray_push_rval(arr, x) \
do { \
__auto_type temp = x; \
arr = _dynarray_push(arr, &temp); \
} while (0)
/**
* @brief Modifies the value of an element at a given index in the dynamic array to a newly given value.
* @param arr Initialized dynamic array instance.
* @param index Element index in the array.
* @param new_value New value of the element at the given index, which is strictly an lvalue.
* @return Pointer to the modified array element, or NULL if the operation fails.
* @note The caller must ensure @p arr points to a valid memory location containing a dynamic array.
*/
#define dynarray_set(arr, index, new_value) _dynarray_set(arr, index, &new_value)
/**
* @brief Modifies the value of an element at a given index in the dynamic array to a newly given value.
* Unlike dynarray_set, this macro accepts rvalues and arbitrary
* expressions, including literals, function calls, and computed values.
* @param arr Initialized dynamic array instance.
* @param index Element index in the array.
* @param new_value New value of the element at the given index.
* @return Pointer to the modified array element, or NULL if the operation fails.
* @note The caller must ensure @p arr points to a valid memory location containing a dynamic array.
*/
#define dynarray_set_rval(arr, index, new_value) ({ \
__auto_type _new_value = (new_value); \
extern void dynarray_set_rval(void) __attribute__((error("Type of new_value does not match the type of the dynamic array elements"))); \
if (__builtin_types_compatible_p(__typeof__(*(arr)), __typeof__((new_value))) == 0) dynarray_set_rval(); \
_dynarray_set((arr), (index), &_new_value); \
})
/**
* @brief Removes the last element from the dynamic array.
*
* Pops the last element in the dynamic array and copies its value into
* the memory pointed by @p xptr before removal.
*
* @param arr Pointer to the dynamic array.
* @param xptr Pointer to memory where the removed elements will be stored.
*
* @note The caller must ensure @p xptr points to a valid memory location
* large enough to hold one element of the array's element type.
*/
#define dynarray_pop(arr, xptr) _dynarray_pop(arr, xptr)
/**
* @brief Removes an element from the vector and returns it.
*
* The removed element is replaced by the last element of the vector.
* This does not preserve ordering of the remaining elements
*
* @param arr Pointer to the dynamic array.
* @param idx Element in the array.
* @param xptr Pointer to memory where the removed elements will be stored.
*
* @note The caller must ensure @p xptr points to a valid memory location
* large enough to hold one element of the array's element type.
*/
#define dynarray_swap_remove(arr, idx, xptr) _dynarray_swap_remove(arr, idx, xptr)
#define dynarray_capacity(arr) _dynarray_field_get(arr, DYNARRAY_CAPACITY_FIELD)
/**
* @brief Returns the number of elements in the dynamic array.
*
* @param arr Pointer to the dynamic array.
* @return The number of elements.
*/
#define dynarray_length(arr) _dynarray_field_get(arr, DYNARRAY_LENGTH_FIELD)
/**
* @brief Returns the size of elements in the dynamic array.
*
* @param arr Pointer to the dynamic array.
* @return The size of elements.
*/
#define dynarray_stride(arr) _dynarray_field_get(arr, DYNARRAY_STRIDE_FIELD)
#endif // DYNARRAY