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> |