1 # Array List
2
3 Next to an array list implementation of the list interface,
4 UCX offers several functions to work with plain C arrays equipped with a size and a capacity.
5
6 The high-level [list interface](list.h.md) is documented on a separate page and explains how lists are used
7 that are created by one of the following functions.
8
9 ```C
10 #include <cx/array_list.h>
11
12 CxList *cxArrayListCreate(const CxAllocator *allocator,
13 size_t elem_size, size_t initial_capacity);
14 ```
15
16 The remaining documentation on this page concentrates on dealing with plain C arrays.
17
18 ## Declare Array with Size and Capacity
19
20 ```C
21 #include <cx/array_list.h>
22
23 #define CX_ARRAY_DECLARE_SIZED(type, name, size_type)
24
25 #define CX_ARRAY_DECLARE(type, name)
26
27 #define cx_array_initialize(ARRAY, capacity)
28
29 #define cx_array_initialize_a(allocator, ARRAY, capacity)
30 ```
31
32 The purpose of the UCX array functions is to keep track of the size and capacity of a plain C array.
33
34 The raw functions do not need this information packed into a structure, but working with independent variables is quite cumbersome.
35 Therefore, UCX defines several macros that call the raw functions assuming certain variable names.
36
37 This is what the `CX_ARRAY_DECLARE_SIZED()` and `CX_ARRAY_DECLARE()` macros are for.
38 They take a `type` for the array members, a `name` for the array variable, and a `size_type` for the type of the size and capacity variables (`size_t` by default when you use `CX_ARRAY_DECLARE()`),
39 and generate three variables named `name`, `name_size`, and `name_capacity`.
40
41 For example, you can abbreviate the following code
42 ```C
43 struct my_data {
44 int other_int;
45 float other_float;
46 int *my_array;
47 size_t my_array_size;
48 size_t my_array_capacity;
49 }
50 ```
51 by substituting the three members for the array with `CX_ARRAY_DECLARE()`.
52 ```C
53 struct my_data {
54 int other_int;
55 float other_float;
56 CX_ARRAY_DECLARE(int, my_array);
57 }
58 ```
59
60 > Using `CX_ARRAY_DECLARE_SIZED()` can save some space when you are using a size type that is _half_ as wide as `sizeof(void*)`.
61 > On 64-bit architectures, having the possibility to store more than four billion items is rarely necessary, hence using 32-bit integers for size and capacity
62 > saves eight bytes (assuming proper alignment in your struct).
63
64 Once the array is declared, you can use one of the macros `cx_array_initialize()` or `cx_array_initialize_a()` to allocate memory.
65 The former uses the [default allocator](allocator.h.md#default-allocator) and the latter allows you to use a specific allocator.
66 Important to note is, that the `ARRAY` argument expects the variable's _name_.
67 The macros set `ARRAY_size` to zero, `ARRAY_capacity` to the specified initial capacity, and invoke the allocator's `malloc()` function to allocate the memory.
68
69 Using the example from above, and the UCX [memory pool](mempool.h.md), this could look like this:
70
71 ```C
72 CxMempool *mpool = cxMempoolCreateSimple(128);
73
74 struct my_data data;
75 cx_array_initialize_a(mpool->allocator, data.my_array, 16);
76 ```
77
78 > The aforementioned macros simplify your life, but keep in mind that using them is not mandatory.
79 > All other macros continue to work perfectly if you declare and initialize your array manually, as long as you use
80 > the naming convention to suffix the size variable with `_size` and the capacity variable with `_capacity`.
81 > Also, you can freely decide in which order you want to declare the variables.
82 >
83 > For example, when you have multiple arrays in your struct with 8-bit `size_type` (because in your use case you don't expect more than 255 elements),
84 > it is favorable to group all pointers and then declare the size and capacity variables to avoid padding between the array declarations.
85
86 ## Array Reallocator
87
88 ```C
89 #include <cx/array_list.h>
90
91 typedef struct {
92 void *(*realloc)(void *array,
93 size_t old_capacity, size_t new_capacity,
94 size_t elem_size, CxArrayReallocator *alloc);
95 const CxAllocator *allocator;
96 const void *stack_ptr;
97 } CxArrayReallocator;
98
99 CxArrayReallocator cx_array_reallocator(
100 const struct cx_allocator_s *allocator,
101 const void *stack_ptr
102 );
103
104 extern CxArrayReallocator* cx_array_default_reallocator;
105 ```
106
107 The main purpose of the functions defined in the array list header
108 is to make it easier to write to an array without caring too much about a possibly necessary reallocation.
109
110 This is realized by passing a reallocator to the various functions that specify how an array shall be reallocated when needed.
111
112 The default `cx_array_default_reallocator` simply defers to the [default allocator](allocator.h.md#default-allocator).
113
114 A reallocator created with the `cx_array_reallocator()` function uses a more sophisticated approach.
115 On the one hand, it can use an arbitrary UCX [allocator](allocator.h.md) for the reallocation,
116 and on the other hand, it can optionally keep track of a stack memory pointer.
117 If you pass a non-`NULL` pointer to `stack_ptr`, the reallocator will _always_ allocate _new_ memory for the array,
118 if the current location equals the `stack_ptr` address.
119
120 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.
121 Combined with a UCX [memory pool](mempool.h.md) this can be a powerful tool.
122 For example, you can add small arrays to your structs plus a memory pool and then use that memory pool to reallocate the arrays on the heap when needed.
123 When you are done with the arrays, you can then use the memory pool to free the memory for those arrays that needed to be moved to the heap.
124
125 ## Reserve
126
127 ```C
128 #include <cx/array_list.h>
129
130 int cx_array_reserve(
131 void **array, void *size, void *capacity, unsigned width,
132 size_t elem_size, size_t count,
133 CxArrayReallocator *reallocator);
134
135 #define cx_array_simple_reserve(ARRAY, count)
136 #define cx_array_simple_reserve_a(reallocator, ARRAY, count)
137 ```
138
139 The function `cx_array_reserve()` guarantees that at least `count` _additional_ elements
140 can be stored in the array pointed to by `array`.
141
142 The `array` argument is a pointer to the location of the target array pointer.
143 The reason for this additional indirection is that this function writes
144 a new pointer back to that variable if the array needed to be reallocated.
145
146 If reallocation fails, this function returns non-zero, leaving the array as it was.
147 Otherwise, the function returns zero.
148
149 If `*size + elem_count` does not exceed the current `*capacity`, this function does nothing and simply returns zero.
150 Otherwise, the specified `reallocator` is used to reserve the necessary space.
151 If reallocation was necessary but failed, this function returns non-zero.
152
153 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.
154 On 32-bit platforms the widths 1, 2, and 4 are supported, and on 64-bit platforms a width of 8 is also supported.
155 Passing zero as argument makes the function assume the native width for size arguments `sizeof(size_t)`.
156
157 The convenience macros take the _name_ of an array variable and invoke the function by deducing the other arguments
158 (including the width of the size and capacity).
159 The reallocator used by the `cx_array_simple_reserve()` macro is the `cx_array_default_reallocator`.
160
161 > While the actual integer type for `size` and `capacity` can be chosen freely, it must be _the same_ for both variables.
162 > 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.
163
164 ## Copy
165
166 ```C
167 #include <cx/array_list.h>
168
169 int cx_array_copy(
170 void **target, void *size, void *capacity, unsigned width,
171 size_t index, const void *src,
172 size_t elem_size, size_t elem_count,
173 CxArrayReallocator *reallocator);
174
175 #define cx_array_simple_copy(ARRAY, index, src, count)
176
177 #define cx_array_simple_copy_a(reallocator, ARRAY, index, src, count)
178 ```
179
180 The function `cx_array_copy()` first [reserves](#reserve) enough memory to copy `elem_count` number of elements from the `src` array
181 to the target array at the specified `index`, and then copies the data with one call to `memmove()`.
182
183 A few things to note:
184 * `*target` and `src` can point to the same memory region, since the underlying copy operation is a `memmove()`
185 * `*target` does not need to point to the start of the array, but `size` and `capacity` always start counting from the
186 position `*target` points to - in this scenario, the need for reallocation must be avoided for obvious reasons
187 * `index` does not need to be within size and not even within the capacity of the current array
188 * if `index` equals `*size`, this function effectively appends the data to the target array
189 * 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
190
191 > 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.
192 > In other words: if you already guarantee, by any means that no reallocation is necessary when writing to your array,
193 > it is strongly recommended to use standard library functions or direct index-based access instead of the UCX functions,
194 > which only purpose is to make it easier for you to guarantee that your array's capacity is large enough to hold new elements.
195
196 ## Add Elements
197
198 ```C
199 #include <cx/array_list.h>
200
201 #define cx_array_add(target, size, capacity, elem_size, elem,
202 reallocator);
203
204 #define cx_array_simple_add(ARRAY, elem)
205
206 #define cx_array_simple_add_a(reallocator, ARRAY, elem)
207 ```
208
209 The macros for adding an element are all convenience macros that invoke `cx_array_copy()`
210 interpreting the `elem` as an array of size one, copied to the past-by-one index of the target array.
211
212 ## Insertion Sort
213
214 ```C
215 #include <cx/array_list.h>
216
217 int cx_array_insert_sorted(
218 void **target, size_t *size, size_t *capacity,
219 cx_compare_func cmp_func,
220 const void *src, size_t elem_size, size_t elem_count,
221 CxArrayReallocator *reallocator);
222
223 #define cx_array_simple_insert_sorted(ARRAY,
224 src, elem_count, cmp_func)
225
226 #define cx_array_simple_insert_sorted_a(reallocator, ARRAY,
227 src, elem_count, cmp_func)
228
229 #define cx_array_add_sorted(target, size, capacity,
230 elem_size, elem, cx_compare_func cmp_func, reallocator);
231
232 #define cx_array_simple_add_sorted(ARRAY,
233 elem, cmp_func)
234
235 #define cx_array_simple_add_sorted_a(reallocator, ARRAY,
236 elem, cmp_func)
237 ```
238
239 The function `cx_array_insert_sorted()` inserts the `elem_count` number of already sorted elements from the `src` array
240 into the target array, comparing the elements with the given `cmp_func`.
241
242 The arguments of this function are used similar to [`cx_array_copy()`](#copy) with two notable exceptions:
243 first, this function actually inserts the items, moving existing items when necessary, and second,
244 no particular index is required because this function determines the insertion points automatically using [binary search](#binary-search).
245
246 If either the target or the source array is not already sorted with respect to the given compare function, the behavior is undefined.
247
248 The convenience macros are all calling `cx_array_insert_sorted()` by deducing the missing arguments.
249 The `cx_array_add_sorted()` family of macros are interpreting the `elem` as a `src` array with an `elem_count` of one.
250
251 ## Sets of Unique Elements
252
253 ```C
254 #include <cx/array_list.h>
255
256 int cx_array_insert_unique(
257 void **target, size_t *size, size_t *capacity,
258 cx_compare_func cmp_func,
259 const void *src, size_t elem_size, size_t elem_count,
260 CxArrayReallocator *reallocator);
261
262 #define cx_array_simple_insert_unique(ARRAY,
263 src, elem_count, cmp_func)
264
265 #define cx_array_simple_insert_unique_a(reallocator, ARRAY,
266 src, elem_count, cmp_func)
267
268 #define cx_array_add_unique(target, size, capacity,
269 elem_size, elem, cx_compare_func cmp_func, reallocator);
270
271 #define cx_array_simple_add_unique(ARRAY,
272 elem, cmp_func)
273
274 #define cx_array_simple_add_unique_a(reallocator, ARRAY,
275 elem, cmp_func)
276 ```
277
278 The above functions are similar to `cx_array_insert_sorted()` and its [relatives](#insertion-sort),
279 except that they skip insertion of elements that are already present in the target array.
280
281 ## Binary Search
282
283 ```C
284 #include <cx/array_list.h>
285
286 size_t cx_array_binary_search(
287 const void *arr, size_t size, size_t elem_size,
288 const void *elem, cx_compare_func cmp_func);
289
290 size_t cx_array_binary_search_inf(
291 const void *arr, size_t size, size_t elem_size,
292 const void *elem, cx_compare_func cmp_func);
293
294 size_t cx_array_binary_search_sup(
295 const void *arr, size_t size, size_t elem_size,
296 const void *elem, cx_compare_func cmp_func);
297 ```
298
299 The function `cx_array_binary_search()` searches the array `arr` for the data pointed to by `elem` using the compare function `cmp_func`.
300
301 If the element is found (i.e. `cmp_func` returns zero), the function returns the index of the element.
302 Otherwise, the function returns `size`.
303
304 The functions `cx_array_binary_search_inf()` and `cx_array_binary_search_sup()` are equivalent,
305 except that they return the index of the closest element if the searched element is not found.
306 The former function returns the closest element that is less or equal (the greatest lower bound / infimum),
307 and the latter function returns the closest element that is larger or equal (the least upper bound / supremum)
308 than the searched element.
309
310 When the found element appears more than once in the array,
311 the binary search and the infimum report the largest index
312 and the supremum reports the smallest index of the identical items, respectively.
313
314 > Note that all the above functions are only well-defined on arrays which are sorted with respect to the given compare function.
315 >
316 > This can, for example, easily be achieved by calling the standard library's `qsort()` function.
317 >{style="note"}
318
319 > Despite the name, neither C nor POSIX standards require the standard library's `bsearch()` function to be implemented using binary search.
320 > But observations show that it usually is.
321 > This makes `cx_array_binary_search()` likely to be equivalent to `bsearch()`, except that it returns an index rather than a pointer.
322
323 ## Iterators
324
325 ```C
326 #include <cx/iterator.h>
327
328 CxIterator cxIterator(const void *array,
329 size_t elem_size, size_t elem_count,
330 bool remove_keeps_order);
331
332 CxIterator cxIteratorPtr(const void *array,
333 size_t elem_count,
334 bool remove_keeps_order);
335 ```
336
337 Iterators over plain C arrays are defined in [iterator.h](iterator.h.md#creating-an-iterator).
338
339 ## Other
340
341 ```C
342 #include <cx/array_list.h>
343
344 void cx_array_swap(
345 void *arr,
346 size_t elem_size,
347 size_t idx1,
348 size_t idx2
349 );
350 ```
351
352 The function `cx_array_swap()` exchanges two items with a three-way swap.
353
354 No memory is allocated if the element size `elem_size` is not larger than `CX_ARRAY_SWAP_SBO_SIZE` (cf. [](install.md#small-buffer-optimizations)).
355
356 You have to make sure that both indices are within bounds of the array `arr`.
357
358 <seealso>
359 <category ref="apidoc">
360 <a href="https://ucx.sourceforge.io/api/array__list_8h.html">array_list.h</a>
361 </category>
362 </seealso>
363