| 13 size_t elem_size, size_t initial_capacity); |
13 size_t elem_size, size_t initial_capacity); |
| 14 ``` |
14 ``` |
| 15 |
15 |
| 16 The remaining documentation on this page concentrates on dealing with plain C arrays. |
16 The remaining documentation on this page concentrates on dealing with plain C arrays. |
| 17 |
17 |
| 18 ## Declare Array with Size and Capacity |
18 ## Declaring and Initializing |
| 19 |
19 |
| 20 ```C |
20 ```C |
| 21 #include <cx/array_list.h> |
21 #include <cx/array_list.h> |
| 22 |
22 |
| 23 #define CX_ARRAY_DECLARE_SIZED(type, name, size_type) |
23 #define CX_ARRAY(type, name) |
| 24 |
24 |
| 25 #define CX_ARRAY_DECLARE(type, name) |
25 int cx_array_init(CxArray array, size_t capacity); |
| 26 |
26 |
| 27 #define cx_array_initialize(ARRAY, capacity) |
27 int cx_array_init_a(const CxAllocator *allocator, |
| 28 |
28 CxArray array, size_t capacity); |
| 29 #define cx_array_initialize_a(allocator, ARRAY, capacity) |
29 |
| |
30 void cx_array_init_fixed(CxArray array, |
| |
31 void *fixed_size_array, size_t num_initialized); |
| 30 ``` |
32 ``` |
| 31 |
33 |
| 32 The purpose of the UCX array functions is to keep track of the size and capacity of a plain C array. |
34 The purpose of the UCX array functions is to keep track of the size and capacity of a plain C array. |
| 33 |
35 |
| 34 The raw functions do not need this information packed into a structure, but working with independent variables is quite cumbersome. |
36 The `CX_ARRAY()` macro declares a variable over an anonymous struct which is compatible to `CxArray`. |
| 35 Therefore, UCX defines several macros that call the raw functions assuming certain variable names. |
37 The thus created struct contains the three members `data`, `size`, and `capacity`. |
| 36 |
38 You can then pass a pseudo-reference to this variable to any of the UCX array functions. |
| 37 This is what the `CX_ARRAY_DECLARE_SIZED()` and `CX_ARRAY_DECLARE()` macros are for. |
39 This is realized by macros which will automatically take the address of your variable and cast it to a pointer of `CxArray`. |
| 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()`), |
40 |
| 39 and generate three variables named `name`, `name_size`, and `name_capacity`. |
41 Once the array is declared, you can use one of `cx_array_init()`, `cx_array_init_a()`, or `cx_array_init_fixed()` to initialize it. |
| 40 |
42 |
| 41 For example, you can abbreviate the following code |
43 The former two functions allocate memory for the array using the [default allocator](allocator.h.md#default-allocator) or the allocator passed to the function. |
| 42 ```C |
44 They return zero on success and non-zero on allocation failure. |
| 43 struct my_data { |
45 |
| 44 int other_int; |
46 The latter function initializes the array with a fixed-sized array. |
| 45 float other_float; |
47 The `num_initialized` argument specifies the number of elements that are already initialized in the array. |
| 46 int *my_array; |
48 This is useful, for example, if you want to initialize the array with stack memory and only want to |
| 47 size_t my_array_size; |
49 allocate memory if the capacity you reserved on the stack is not sufficient. |
| 48 size_t my_array_capacity; |
50 More on this in the following section, when discussing `cx_array_copy_to_new()`. |
| 49 } |
51 |
| 50 ``` |
52 ## Reallocation |
| 51 by substituting the three members for the array with `CX_ARRAY_DECLARE()`. |
53 |
| 52 ```C |
54 ```C |
| 53 struct my_data { |
55 #include <cx/array_list.h> |
| 54 int other_int; |
56 |
| 55 float other_float; |
57 int cx_array_reserve(CxArray array, size_t capacity); |
| 56 CX_ARRAY_DECLARE(int, my_array); |
58 |
| 57 } |
59 int cx_array_copy_to_new(CxArray array, size_t capacity); |
| 58 ``` |
60 |
| 59 |
61 int cx_array_reserve_a(const CxAllocator *allocator, |
| 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*)`. |
62 CxArray array, size_t capacity); |
| 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 |
63 |
| 62 > saves eight bytes (assuming proper alignment in your struct). |
64 int cx_array_copy_to_new_a(const CxAllocator *allocator, |
| 63 |
65 CxArray array, size_t capacity); |
| 64 Once the array is declared, you can use one of the macros `cx_array_initialize()` or `cx_array_initialize_a()` to allocate memory. |
66 ``` |
| 65 The former uses the [default allocator](allocator.h.md#default-allocator) and the latter allows you to use a specific allocator. |
67 |
| 66 Important to note is, that the `ARRAY` argument expects the variable's _name_. |
68 The functions `cx_array_reserve()` and `cx_array_reserve_a()` change the capacity of the array to exactly `capacity` elements. |
| 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. |
69 If the current `size` is larger than `capacity`, the array is truncated to `capacity`. |
| 68 |
70 |
| 69 Using the example from above, and the UCX [memory pool](mempool.h.md), this could look like this: |
71 The functions `cx_array_copy_to_new()` and `cx_array_copy_to_new_a()` allocate a new memory block with the specified capacity |
| 70 |
72 and copy the elements of the old memory to the new memory. |
| 71 ```C |
73 |
| 72 CxMempool *mpool = cxMempoolCreateSimple(128); |
74 > The functions `cx_array_copy_to_new()` and `cx_array_copy_to_new_a()` do **not** free the old memory. |
| 73 |
75 > If the old memory is not located on the stack, or you don't have another reference to it, it will be leaked. |
| 74 struct my_data data; |
76 > The functions are particularly useful when the array was initialized with a fixed-sized array. |
| 75 cx_array_initialize_a(mpool->allocator, data.my_array, 16); |
77 >{style="note"} |
| 76 ``` |
78 |
| 77 |
79 ## Add or Insert Elements |
| 78 > The aforementioned macros simplify your life, but keep in mind that using them is not mandatory. |
80 |
| 79 > All other macros continue to work perfectly if you declare and initialize your array manually, as long as you use |
81 ```C |
| 80 > the naming convention to suffix the size variable with `_size` and the capacity variable with `_capacity`. |
82 #include <cx/array_list.h> |
| 81 > Also, you can freely decide in which order you want to declare the variables. |
83 |
| 82 > |
84 int cx_array_add(CxArray array, const void *element); |
| 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), |
85 |
| 84 > it is favorable to group all pointers and then declare the size and capacity variables to avoid padding between the array declarations. |
86 int cx_array_add_array(CxArray array, const void *other, size_t n); |
| 85 |
87 |
| 86 ## Array Reallocator |
88 int cx_array_insert(CxArray array, size_t index, const void *element); |
| 87 |
89 |
| 88 ```C |
90 int cx_array_insert_array(CxArray array, |
| 89 #include <cx/array_list.h> |
91 size_t index, const void *other, size_t n); |
| 90 |
92 |
| 91 typedef struct { |
93 int cx_array_add_a(const CxAllocator* allocator, |
| 92 void *(*realloc)(void *array, |
94 CxArray array, const void *element); |
| 93 size_t old_capacity, size_t new_capacity, |
95 |
| 94 size_t elem_size, CxArrayReallocator *alloc); |
96 int cx_array_add_array(const CxAllocator* allocator, |
| 95 const CxAllocator *allocator; |
97 CxArray array, const void *other, size_t n); |
| 96 const void *stack_ptr; |
98 |
| 97 } CxArrayReallocator; |
99 int cx_array_insert_a(const CxAllocator* allocator, |
| 98 |
100 CxArray array, size_t index, const void *element); |
| 99 CxArrayReallocator cx_array_reallocator( |
101 |
| 100 const struct cx_allocator_s *allocator, |
102 int cx_array_insert_array_a(const CxAllocator* allocator, |
| 101 const void *stack_ptr |
103 CxArray array, size_t index, const void *other, size_t n); |
| 102 ); |
104 ``` |
| 103 |
105 |
| 104 extern CxArrayReallocator* cx_array_default_reallocator; |
106 The above functions insert one or more elements into the array at the specified `index` |
| 105 ``` |
107 or add them to the end of the array. |
| 106 |
108 |
| 107 The main purpose of the functions defined in the array list header |
109 When the array capacity is not sufficient, a re-allocation is attempted. |
| 108 is to make it easier to write to an array without caring too much about a possibly necessary reallocation. |
110 If the allocation fails, the function returns non-zero. |
| 109 |
111 |
| 110 This is realized by passing a reallocator to the various functions that specify how an array shall be reallocated when needed. |
112 > Be careful when using these functions on an array that was initialized with fixed-sized memory. |
| 111 |
113 > In this case, you MUST make sure that the capacity is sufficient or reallocate the array |
| 112 The default `cx_array_default_reallocator` simply defers to the [default allocator](allocator.h.md#default-allocator). |
114 > with `cx_array_copy_to_new()` or `cx_array_copy_to_new_a()` before adding or inserting more elements. |
| 113 |
115 >{style="note"} |
| 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 |
116 |
| 212 ## Insertion Sort |
117 ## Insertion Sort |
| 213 |
118 |
| 214 ```C |
119 ```C |
| 215 #include <cx/array_list.h> |
120 #include <cx/array_list.h> |
| 216 |
121 |
| 217 int cx_array_insert_sorted( |
122 int cx_array_insert_sorted(CxArray array, |
| 218 void **target, size_t *size, size_t *capacity, |
123 cx_compare_func cmp_func, const void *element); |
| 219 cx_compare_func cmp_func, |
124 |
| 220 const void *src, size_t elem_size, size_t elem_count, |
125 int cx_array_insert_sorted_array(CxArray array, |
| 221 CxArrayReallocator *reallocator); |
126 cx_compare_func cmp_func, const void *sorted_data, size_t n); |
| 222 |
127 |
| 223 #define cx_array_simple_insert_sorted(ARRAY, |
128 int cx_array_insert_sorted_a(const CxAllocator *allocator, |
| 224 src, elem_count, cmp_func) |
129 CxArray array, cx_compare_func cmp_func, const void *element); |
| 225 |
130 |
| 226 #define cx_array_simple_insert_sorted_a(reallocator, ARRAY, |
131 int cx_array_insert_sorted_array_a(const CxAllocator *allocator, |
| 227 src, elem_count, cmp_func) |
132 CxArray array, cx_compare_func cmp_func, |
| 228 |
133 const void *sorted_data, size_t n); |
| 229 #define cx_array_add_sorted(target, size, capacity, |
134 ``` |
| 230 elem_size, elem, cx_compare_func cmp_func, reallocator); |
135 |
| 231 |
136 The above functions are equivalent to `cx_array_insert()` and `cx_array_insert_array()`, |
| 232 #define cx_array_simple_add_sorted(ARRAY, |
137 except that they only work on sorted arrays and insert the element at the correct position with respect to the sort order. |
| 233 elem, cmp_func) |
138 If either the array or the `sorted_data` is not sorted according to the given `cmp_func`, the behavior is undefined. |
| 234 |
139 |
| 235 #define cx_array_simple_add_sorted_a(reallocator, ARRAY, |
140 ## Insert Unique Elements |
| 236 elem, cmp_func) |
141 |
| 237 ``` |
142 ```C |
| 238 |
143 #include <cx/array_list.h> |
| 239 The function `cx_array_insert_sorted()` inserts the `elem_count` number of already sorted elements from the `src` array |
144 |
| 240 into the target array, comparing the elements with the given `cmp_func`. |
145 int cx_array_insert_unique(CxArray array, |
| 241 |
146 cx_compare_func cmp_func, const void *element); |
| 242 The arguments of this function are used similar to [`cx_array_copy()`](#copy) with two notable exceptions: |
147 |
| 243 first, this function actually inserts the items, moving existing items when necessary, and second, |
148 int cx_array_insert_unique_array(CxArray array, |
| 244 no particular index is required because this function determines the insertion points automatically using [binary search](#binary-search). |
149 cx_compare_func cmp_func, const void *sorted_data, size_t n); |
| 245 |
150 |
| 246 If either the target or the source array is not already sorted with respect to the given compare function, the behavior is undefined. |
151 int cx_array_insert_unique_a(const CxAllocator *allocator, |
| 247 |
152 CxArray array, cx_compare_func cmp_func, const void *element); |
| 248 The convenience macros are all calling `cx_array_insert_sorted()` by deducing the missing arguments. |
153 |
| 249 The `cx_array_add_sorted()` family of macros are interpreting the `elem` as a `src` array with an `elem_count` of one. |
154 int cx_array_insert_unique_array_a(const CxAllocator *allocator, |
| 250 |
155 CxArray array, cx_compare_func cmp_func, |
| 251 ## Sets of Unique Elements |
156 const void *sorted_data, size_t n); |
| 252 |
157 ``` |
| 253 ```C |
158 |
| 254 #include <cx/array_list.h> |
159 The above functions are similar to the functions for insertion sort, |
| 255 |
160 except that they only insert elements that are not already present in the array. |
| 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 |
161 |
| 281 ## Binary Search |
162 ## Binary Search |
| 282 |
163 |
| 283 ```C |
164 ```C |
| 284 #include <cx/array_list.h> |
165 #include <cx/array_list.h> |