| 103 |
103 |
| 104 CxArrayReallocator cx_array_reallocator( |
104 CxArrayReallocator cx_array_reallocator( |
| 105 const struct cx_allocator_s *allocator, |
105 const struct cx_allocator_s *allocator, |
| 106 const void *stackmem |
106 const void *stackmem |
| 107 ); |
107 ); |
| 108 ``` |
108 |
| 109 |
109 extern CxArrayReallocator* cx_array_default_reallocator; |
| 110 <warning> |
110 ``` |
| 111 TODO: document |
111 |
| 112 </warning> |
112 The main purpose of the functions defined in the array list header, |
| 113 |
113 is to make it easier to write to an array without caring too much about a possibly needed reallocation. |
| 114 ## Add Elements |
114 |
| 115 |
115 This is realized by passing a reallocator to the various functions which specifies how an array shall be reallocated when needed. |
| 116 ```C |
116 |
| 117 #include <cx/array_list.h> |
117 The default `cx_array_default_reallocator` simply defers to the standard library `realloc()`. |
| 118 |
118 |
| 119 int cx_array_add(void **target, void *size, void *capacity, |
119 A reallocator created with the `cx_array_reallocator()` function uses a more sophisticated approach. |
| 120 size_t elem_size, const void *elem, |
120 On the one hand, it can use an arbitrary UCX [allocator](allocator.h.md) for the reallocation, |
| 121 CxArrayReallocator *reallocator); |
121 and on the other hand, it can optionally keep track of a stack memory pointer. |
| 122 |
122 If you pass a non-`NULL` pointer to `stackmem`, the reallocator will _always_ allocate _new_ memory for the array, |
| 123 #define cx_array_simple_add(ARRAY, elem) |
123 if the current location equals the `stackmem` address. |
| 124 |
124 |
| 125 #define cx_array_simple_add_a(reallocator, ARRAY, elem) |
125 This allows you to allocate an array on the stack and instruct UCX to automatically move it to heap memory when the capacity is exceeded. |
| 126 ``` |
126 Combined with a UCX [memory pool](mempool.h.md) this can be a powerful tool for local arrays |
| 127 |
127 which are expected to stay within the bounds of the stack memory most of the time, but are also allowed to sometimes grow their capacity when needed. |
| 128 <warning> |
|
| 129 TODO: document |
|
| 130 </warning> |
|
| 131 |
128 |
| 132 ## Reserve |
129 ## Reserve |
| 133 |
130 |
| 134 ```C |
131 ```C |
| 135 #include <cx/array_list.h> |
132 #include <cx/array_list.h> |
| 136 |
133 |
| 137 int cx_array_reserve( |
134 int cx_array_reserve( |
| 138 void **array, void *size, void *capacity, unsigned width, |
135 void **array, void *size, void *capacity, unsigned width, |
| 139 size_t elem_size, size_t elem_count, |
136 size_t elem_size, size_t count, |
| 140 CxArrayReallocator *reallocator); |
137 CxArrayReallocator *reallocator); |
| 141 |
138 |
| 142 #define cx_array_simple_reserve(ARRAY, count) |
139 #define cx_array_simple_reserve(ARRAY, count) |
| 143 #define cx_array_simple_reserve_a(reallocator, ARRAY, count) |
140 #define cx_array_simple_reserve_a(reallocator, ARRAY, count) |
| 144 ``` |
141 ``` |
| 145 |
142 |
| 146 <warning> |
143 The function `cx_array_reserve()` guarantees that at least `count` _additional_ elements |
| 147 TODO: document |
144 can be stored in the array pointed to by `array`. |
| 148 </warning> |
145 |
| |
146 The `array` argument is a pointer to the location of the target array pointer. |
| |
147 The reason for this additional indirection is that this function writes |
| |
148 a new pointer back to that variable, if the array needed to be reallocated. |
| |
149 |
| |
150 If reallocation fails, this function returns non-zero leaving the array as it was. |
| |
151 Otherwise, the function returns zero. |
| |
152 |
| |
153 If `*size + elem_count` does not exceed the current `*capacity`, this function does nothing and simply returns zero. |
| |
154 Otherwise, the specified `reallocator` is used to reserve the necessary space. |
| |
155 If reallocation was necessary but failed, this function returns non-zero. |
| |
156 |
| |
157 The actual data type of `size` and `capacity` can be a pointer to an arbitrary integer whose byte-width is determined by the `width` argument. |
| |
158 On 32-bit platforms the widths 1, 2, and 4 are supported and on 64-bit platforms, additionally a width of 8 is supported. |
| |
159 Passing zero as argument makes the function assume the native width for size arguments `sizeof(size_t)`. |
| |
160 |
| |
161 The convenience macros take the _name_ of an array variable and invoke the function by deducing the other arguments |
| |
162 (including the width of the size and capacity). |
| |
163 The reallocator used by the `cx_array_simple_reserve()` macro is the `cx_array_default_reallocator`. |
| |
164 |
| |
165 > While the actual integer type for `size` and `capacity` can be chosen freely, it must be _the same_ for both variables. |
| |
166 > For example it is not allowed, and it does not make any sense either, to use a 16-bit integer for the size, but a 32-bit integer for the capacity. |
| 149 |
167 |
| 150 ## Copy |
168 ## Copy |
| 151 |
169 |
| 152 ```C |
170 ```C |
| 153 #include <cx/array_list.h> |
171 #include <cx/array_list.h> |
| 161 #define cx_array_simple_copy(ARRAY, index, src, count) |
179 #define cx_array_simple_copy(ARRAY, index, src, count) |
| 162 |
180 |
| 163 #define cx_array_simple_copy_a(reallocator, ARRAY, index, src, count) |
181 #define cx_array_simple_copy_a(reallocator, ARRAY, index, src, count) |
| 164 ``` |
182 ``` |
| 165 |
183 |
| 166 <warning> |
184 The function `cx_array_copy()` first [reserves](#reserve) enough memory to copy `elem_count` number of elements from the `src` array |
| 167 TODO: outdated - rewrite |
185 to the target array at the specified `index`, and then copies the data with one call to `memmove()`. |
| 168 </warning> |
|
| 169 |
|
| 170 The `target` argument is a pointer to the target array pointer. |
|
| 171 The reason for this additional indirection is that this function writes |
|
| 172 back the pointer to the possibly reallocated array. |
|
| 173 The next two arguments are pointers to the `size` and `capacity` of the target array for which the width |
|
| 174 (in bits) is specified in the `width` argument. |
|
| 175 |
|
| 176 On a successful invocation, the function copies `elem_count` number of elements, each of size `elem_size` from |
|
| 177 `src` to `*target` and uses the `reallocator` to extend the array when necessary. |
|
| 178 Finally, the size, capacity, and the pointer to the array are all updated and the function returns zero. |
|
| 179 |
186 |
| 180 A few things to note: |
187 A few things to note: |
| 181 * `*target` and `src` can point to the same memory region, effectively copying elements within the array with `memmove` |
188 * `*target` and `src` can point to the same memory region, since the underlying copy operation is a `memmove()` |
| 182 * `*target` does not need to point to the start of the array, but `size` and `capacity` always start counting from the |
189 * `*target` does not need to point to the start of the array, but `size` and `capacity` always start counting from the |
| 183 position, `*target` points to - in this scenario, the need for reallocation must be avoided for obvious reasons |
190 position `*target` points to - in this scenario, the need for reallocation must be avoided for obvious reasons |
| 184 * `index` does not need to be within size of the current array |
191 * `index` does not need to be within size and not even within the capacity of the current array |
| 185 * `index` does not even need to be within the capacity of the array |
192 * if `index` equals `*size`, this function effectively appends the data to the target array |
| 186 * `width` must be one of 8, 16, 32, 64 (only on 64-bit systems), or zero (in which case the native word width is used) |
193 * the data in the target array is overwritten - if you want to insert data, you first need to copy the existing data to the end of the array and then copy the new data in a separate call |
| |
194 |
| |
195 > If you are using `cx_array_reserve()` already in your program, there is no need to call `cx_array_copy()` or any of the convenience macros anymore. |
| |
196 > In other words: if you already guarantee, by any means, that no reallocation is necessary when writing to your array, |
| |
197 > it is strongly recommended to use standard library functions or direct index-based access instead of the UCX functions, |
| |
198 > which only purpose is to make it easier for you to guarantee that your array's capacity is large enough to hold new elements. |
| |
199 |
| |
200 ## Add Elements |
| |
201 |
| |
202 ```C |
| |
203 #include <cx/array_list.h> |
| |
204 |
| |
205 #define cx_array_add(target, size, capacity, elem_size, elem, |
| |
206 reallocator); |
| |
207 |
| |
208 #define cx_array_simple_add(ARRAY, elem) |
| |
209 |
| |
210 #define cx_array_simple_add_a(reallocator, ARRAY, elem) |
| |
211 ``` |
| |
212 |
| |
213 The macros for adding an element are all convenience macros that invoke `cx_array_copy()` |
| |
214 interpreting the `elem` as an array of size one, copied to the past-by-one index of the target array. |
| 187 |
215 |
| 188 ## Insertion Sort |
216 ## Insertion Sort |
| 189 |
217 |
| 190 ```C |
218 ```C |
| 191 int cx_array_insert_sorted( |
219 int cx_array_insert_sorted( |
| 198 src, elem_count, cmp_func) |
226 src, elem_count, cmp_func) |
| 199 |
227 |
| 200 #define cx_array_simple_insert_sorted_a(reallocator, ARRAY, |
228 #define cx_array_simple_insert_sorted_a(reallocator, ARRAY, |
| 201 src, elem_count, cmp_func) |
229 src, elem_count, cmp_func) |
| 202 |
230 |
| 203 int cx_array_add_sorted( |
231 #define cx_array_add_sorted(target, size, capacity, |
| 204 void **target, size_t *size, size_t *capacity, |
232 elem_size, elem, cx_compare_func cmp_func, reallocator); |
| 205 size_t elem_size, const void *elem, |
|
| 206 cx_compare_func cmp_func, |
|
| 207 CxArrayReallocator *reallocator); |
|
| 208 |
233 |
| 209 #define cx_array_simple_add_sorted(ARRAY, |
234 #define cx_array_simple_add_sorted(ARRAY, |
| 210 elem, cmp_func) |
235 elem, cmp_func) |
| 211 |
236 |
| 212 #define cx_array_simple_add_sorted_a(reallocator, ARRAY, |
237 #define cx_array_simple_add_sorted_a(reallocator, ARRAY, |
| 213 elem, cmp_func) |
238 elem, cmp_func) |
| 214 ``` |
239 ``` |
| 215 |
240 |
| 216 <warning> |
241 The function `cx_array_insert_sorted()` inserts the `elem_count` number of already sorted elements from the `src` array |
| 217 TODO: document |
242 into the target array, comparing the elements with the given `cmp_func`. |
| 218 </warning> |
243 |
| |
244 The arguments of this function are used similar to [`cx_array_copy()`](#copy) with two notable exceptions: |
| |
245 first, this function actually inserts the items, moving existing items when necessary, and second, |
| |
246 no particular index is required because this function determines the insertion points automatically using [binary search](#binary-search). |
| |
247 |
| |
248 If either the target or the source array is not already sorted with respect to the given compare function, the behavior is undefined. |
| |
249 |
| |
250 The convenience macros are all calling `cx_array_insert_sorted()` by deducing the missing arguments. |
| |
251 The `cx_array_add_sorted()` family of macros are interpreting the `elem` as a `src` array with an `elem_count` of one. |
| 219 |
252 |
| 220 ## Binary Search |
253 ## Binary Search |
| 221 |
254 |
| 222 ```C |
255 ```C |
| 223 #include <cx/array_list.h> |
256 #include <cx/array_list.h> |
| 233 size_t cx_array_binary_search_sup( |
266 size_t cx_array_binary_search_sup( |
| 234 const void *arr, size_t size, size_t elem_size, |
267 const void *arr, size_t size, size_t elem_size, |
| 235 const void *elem, cx_compare_func cmp_func); |
268 const void *elem, cx_compare_func cmp_func); |
| 236 ``` |
269 ``` |
| 237 |
270 |
| 238 <warning> |
271 The function `cx_array_binary_search()` searches the array `arr` for the data pointed to by `elem` using the compare function `cmp_func`. |
| 239 TODO: document |
272 |
| 240 </warning> |
273 If the element is found (i.e. `cmp_func` returns zero), the function returns the index of the element. |
| |
274 Otherwise, the function returns `size`. |
| |
275 |
| |
276 The functions `cx_array_binary_search_inf()` and `cx_array_binary_search_sup()` are equivalent, |
| |
277 except that they return the index of the closest element, if the searched element is not found. |
| |
278 The former function returns the closest element that is less or equal (greatest lower bound / infimum), |
| |
279 and the latter function returns the closest element that is larger or equal (least upper bound / supremum) |
| |
280 than the searched element. |
| |
281 |
| |
282 > Note, that all of the above functions are only well-defined on arrays which are sorted with respect to the given compare function. |
| |
283 > |
| |
284 > This can, for example, easily be achieved by calling the standard library's `qsort()` function. |
| |
285 >{style="note"} |
| |
286 |
| |
287 > Despite the name, neither C nor POSIX standards require the standard library's `bsearch()` function to be implemented using binary search. |
| |
288 > But observations show that it usually is. |
| |
289 > This makes `cx_array_binary_search()` likely to be equivalent to `bsearch()`, except that it returns an index rather than a pointer. |
| 241 |
290 |
| 242 ## Iterators |
291 ## Iterators |
| 243 |
292 |
| 244 ```C |
293 ```C |
| 245 #include <cx/iterator.h> |
294 #include <cx/iterator.h> |