28 |
28 |
29 #include "cx/array_list.h" |
29 #include "cx/array_list.h" |
30 #include "cx/compare.h" |
30 #include "cx/compare.h" |
31 #include <assert.h> |
31 #include <assert.h> |
32 #include <string.h> |
32 #include <string.h> |
|
33 #include <errno.h> |
33 |
34 |
34 // Default array reallocator |
35 // Default array reallocator |
35 |
36 |
36 static void *cx_array_default_realloc( |
37 static void *cx_array_default_realloc( |
37 void *array, |
38 void *array, |
38 size_t capacity, |
39 size_t capacity, |
39 size_t elem_size, |
40 size_t elem_size, |
40 __attribute__((__unused__)) struct cx_array_reallocator_s *alloc |
41 cx_attr_unused CxArrayReallocator *alloc |
41 ) { |
42 ) { |
42 return realloc(array, capacity * elem_size); |
43 size_t n; |
43 } |
44 if (cx_szmul(capacity, elem_size, &n)) { |
44 |
45 errno = EOVERFLOW; |
45 struct cx_array_reallocator_s cx_array_default_reallocator_impl = { |
46 return NULL; |
|
47 } |
|
48 return realloc(array, n); |
|
49 } |
|
50 |
|
51 CxArrayReallocator cx_array_default_reallocator_impl = { |
46 cx_array_default_realloc, NULL, NULL, 0, 0 |
52 cx_array_default_realloc, NULL, NULL, 0, 0 |
47 }; |
53 }; |
48 |
54 |
49 struct cx_array_reallocator_s *cx_array_default_reallocator = &cx_array_default_reallocator_impl; |
55 CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl; |
|
56 |
|
57 // Stack-aware array reallocator |
|
58 |
|
59 static void *cx_array_advanced_realloc( |
|
60 void *array, |
|
61 size_t capacity, |
|
62 size_t elem_size, |
|
63 cx_attr_unused CxArrayReallocator *alloc |
|
64 ) { |
|
65 // check for overflow |
|
66 size_t n; |
|
67 if (cx_szmul(capacity, elem_size, &n)) { |
|
68 errno = EOVERFLOW; |
|
69 return NULL; |
|
70 } |
|
71 |
|
72 // retrieve the pointer to the actual allocator |
|
73 const CxAllocator *al = alloc->ptr1; |
|
74 |
|
75 // check if the array is still located on the stack |
|
76 void *newmem; |
|
77 if (array == alloc->ptr2) { |
|
78 newmem = cxMalloc(al, n); |
|
79 if (newmem != NULL && array != NULL) { |
|
80 memcpy(newmem, array, n); |
|
81 } |
|
82 } else { |
|
83 newmem = cxRealloc(al, array, n); |
|
84 } |
|
85 return newmem; |
|
86 } |
|
87 |
|
88 struct cx_array_reallocator_s cx_array_reallocator( |
|
89 const struct cx_allocator_s *allocator, |
|
90 const void *stackmem |
|
91 ) { |
|
92 if (allocator == NULL) { |
|
93 allocator = cxDefaultAllocator; |
|
94 } |
|
95 return (struct cx_array_reallocator_s) { |
|
96 cx_array_advanced_realloc, |
|
97 (void*) allocator, (void*) stackmem, |
|
98 0, 0 |
|
99 }; |
|
100 } |
50 |
101 |
51 // LOW LEVEL ARRAY LIST FUNCTIONS |
102 // LOW LEVEL ARRAY LIST FUNCTIONS |
52 |
103 |
53 enum cx_array_result cx_array_copy( |
104 static size_t cx_array_align_capacity( |
54 void **target, |
105 size_t cap, |
55 size_t *size, |
106 size_t alignment, |
56 size_t *capacity, |
107 size_t max |
57 size_t index, |
108 ) { |
58 void const *src, |
109 if (cap > max - alignment) { |
|
110 return cap; |
|
111 } else { |
|
112 return cap - (cap % alignment) + alignment; |
|
113 } |
|
114 } |
|
115 |
|
116 int cx_array_reserve( |
|
117 void **array, |
|
118 void *size, |
|
119 void *capacity, |
|
120 unsigned width, |
59 size_t elem_size, |
121 size_t elem_size, |
60 size_t elem_count, |
122 size_t elem_count, |
61 struct cx_array_reallocator_s *reallocator |
123 CxArrayReallocator *reallocator |
|
124 ) { |
|
125 // assert pointers |
|
126 assert(array != NULL); |
|
127 assert(size != NULL); |
|
128 assert(capacity != NULL); |
|
129 |
|
130 // default reallocator |
|
131 if (reallocator == NULL) { |
|
132 reallocator = cx_array_default_reallocator; |
|
133 } |
|
134 |
|
135 // determine size and capacity |
|
136 size_t oldcap; |
|
137 size_t oldsize; |
|
138 size_t max_size; |
|
139 if (width == 0 || width == sizeof(size_t)) { |
|
140 oldcap = *(size_t*) capacity; |
|
141 oldsize = *(size_t*) size; |
|
142 max_size = SIZE_MAX; |
|
143 } else if (width == sizeof(uint16_t)) { |
|
144 oldcap = *(uint16_t*) capacity; |
|
145 oldsize = *(uint16_t*) size; |
|
146 max_size = UINT16_MAX; |
|
147 } else if (width == sizeof(uint8_t)) { |
|
148 oldcap = *(uint8_t*) capacity; |
|
149 oldsize = *(uint8_t*) size; |
|
150 max_size = UINT8_MAX; |
|
151 } |
|
152 #if CX_WORDSIZE == 64 |
|
153 else if (width == sizeof(uint32_t)) { |
|
154 oldcap = *(uint32_t*) capacity; |
|
155 oldsize = *(uint32_t*) size; |
|
156 max_size = UINT32_MAX; |
|
157 } |
|
158 #endif |
|
159 else { |
|
160 errno = EINVAL; |
|
161 return 1; |
|
162 } |
|
163 |
|
164 // assert that the array is allocated when it has capacity |
|
165 assert(*array != NULL || oldcap == 0); |
|
166 |
|
167 // check for overflow |
|
168 if (elem_count > max_size - oldsize) { |
|
169 errno = EOVERFLOW; |
|
170 return 1; |
|
171 } |
|
172 |
|
173 // determine new capacity |
|
174 size_t newcap = oldsize + elem_count; |
|
175 |
|
176 // reallocate if possible |
|
177 if (newcap > oldcap) { |
|
178 // calculate new capacity (next number divisible by 16) |
|
179 newcap = cx_array_align_capacity(newcap, 16, max_size); |
|
180 |
|
181 // perform reallocation |
|
182 void *newmem = reallocator->realloc( |
|
183 *array, newcap, elem_size, reallocator |
|
184 ); |
|
185 if (newmem == NULL) { |
|
186 return 1; // LCOV_EXCL_LINE |
|
187 } |
|
188 |
|
189 // store new pointer |
|
190 *array = newmem; |
|
191 |
|
192 // store new capacity |
|
193 if (width == 0 || width == sizeof(size_t)) { |
|
194 *(size_t*) capacity = newcap; |
|
195 } else if (width == sizeof(uint16_t)) { |
|
196 *(uint16_t*) capacity = (uint16_t) newcap; |
|
197 } else if (width == sizeof(uint8_t)) { |
|
198 *(uint8_t*) capacity = (uint8_t) newcap; |
|
199 } |
|
200 #if CX_WORDSIZE == 64 |
|
201 else if (width == sizeof(uint32_t)) { |
|
202 *(uint32_t*) capacity = (uint32_t) newcap; |
|
203 } |
|
204 #endif |
|
205 } |
|
206 |
|
207 return 0; |
|
208 } |
|
209 |
|
210 int cx_array_copy( |
|
211 void **target, |
|
212 void *size, |
|
213 void *capacity, |
|
214 unsigned width, |
|
215 size_t index, |
|
216 const void *src, |
|
217 size_t elem_size, |
|
218 size_t elem_count, |
|
219 CxArrayReallocator *reallocator |
62 ) { |
220 ) { |
63 // assert pointers |
221 // assert pointers |
64 assert(target != NULL); |
222 assert(target != NULL); |
65 assert(size != NULL); |
223 assert(size != NULL); |
|
224 assert(capacity != NULL); |
66 assert(src != NULL); |
225 assert(src != NULL); |
67 |
226 |
68 // determine capacity |
227 // default reallocator |
69 size_t cap = capacity == NULL ? *size : *capacity; |
228 if (reallocator == NULL) { |
|
229 reallocator = cx_array_default_reallocator; |
|
230 } |
|
231 |
|
232 // determine size and capacity |
|
233 size_t oldcap; |
|
234 size_t oldsize; |
|
235 size_t max_size; |
|
236 if (width == 0 || width == sizeof(size_t)) { |
|
237 oldcap = *(size_t*) capacity; |
|
238 oldsize = *(size_t*) size; |
|
239 max_size = SIZE_MAX; |
|
240 } else if (width == sizeof(uint16_t)) { |
|
241 oldcap = *(uint16_t*) capacity; |
|
242 oldsize = *(uint16_t*) size; |
|
243 max_size = UINT16_MAX; |
|
244 } else if (width == sizeof(uint8_t)) { |
|
245 oldcap = *(uint8_t*) capacity; |
|
246 oldsize = *(uint8_t*) size; |
|
247 max_size = UINT8_MAX; |
|
248 } |
|
249 #if CX_WORDSIZE == 64 |
|
250 else if (width == sizeof(uint32_t)) { |
|
251 oldcap = *(uint32_t*) capacity; |
|
252 oldsize = *(uint32_t*) size; |
|
253 max_size = UINT32_MAX; |
|
254 } |
|
255 #endif |
|
256 else { |
|
257 errno = EINVAL; |
|
258 return 1; |
|
259 } |
|
260 |
|
261 // assert that the array is allocated when it has capacity |
|
262 assert(*target != NULL || oldcap == 0); |
|
263 |
|
264 // check for overflow |
|
265 if (index > max_size || elem_count > max_size - index) { |
|
266 errno = EOVERFLOW; |
|
267 return 1; |
|
268 } |
70 |
269 |
71 // check if resize is required |
270 // check if resize is required |
72 size_t minsize = index + elem_count; |
271 size_t minsize = index + elem_count; |
73 size_t newsize = *size < minsize ? minsize : *size; |
272 size_t newsize = oldsize < minsize ? minsize : oldsize; |
74 bool needrealloc = newsize > cap; |
|
75 |
273 |
76 // reallocate if possible |
274 // reallocate if possible |
77 if (needrealloc) { |
275 size_t newcap = oldcap; |
78 // a reallocator and a capacity variable must be available |
276 if (newsize > oldcap) { |
79 if (reallocator == NULL || capacity == NULL) { |
|
80 return CX_ARRAY_REALLOC_NOT_SUPPORTED; |
|
81 } |
|
82 |
|
83 // check, if we need to repair the src pointer |
277 // check, if we need to repair the src pointer |
84 uintptr_t targetaddr = (uintptr_t) *target; |
278 uintptr_t targetaddr = (uintptr_t) *target; |
85 uintptr_t srcaddr = (uintptr_t) src; |
279 uintptr_t srcaddr = (uintptr_t) src; |
86 bool repairsrc = targetaddr <= srcaddr |
280 bool repairsrc = targetaddr <= srcaddr |
87 && srcaddr < targetaddr + cap * elem_size; |
281 && srcaddr < targetaddr + oldcap * elem_size; |
88 |
282 |
89 // calculate new capacity (next number divisible by 16) |
283 // calculate new capacity (next number divisible by 16) |
90 cap = newsize - (newsize % 16) + 16; |
284 newcap = cx_array_align_capacity(newsize, 16, max_size); |
91 assert(cap > newsize); |
285 assert(newcap > newsize); |
92 |
286 |
93 // perform reallocation |
287 // perform reallocation |
94 void *newmem = reallocator->realloc( |
288 void *newmem = reallocator->realloc( |
95 *target, cap, elem_size, reallocator |
289 *target, newcap, elem_size, reallocator |
96 ); |
290 ); |
97 if (newmem == NULL) { |
291 if (newmem == NULL) { |
98 return CX_ARRAY_REALLOC_FAILED; |
292 return 1; |
99 } |
293 } |
100 |
294 |
101 // repair src pointer, if necessary |
295 // repair src pointer, if necessary |
102 if (repairsrc) { |
296 if (repairsrc) { |
103 src = ((char *) newmem) + (srcaddr - targetaddr); |
297 src = ((char *) newmem) + (srcaddr - targetaddr); |
104 } |
298 } |
105 |
299 |
106 // store new pointer and capacity |
300 // store new pointer |
107 *target = newmem; |
301 *target = newmem; |
108 *capacity = cap; |
|
109 } |
302 } |
110 |
303 |
111 // determine target pointer |
304 // determine target pointer |
112 char *start = *target; |
305 char *start = *target; |
113 start += index * elem_size; |
306 start += index * elem_size; |
114 |
307 |
115 // copy elements and set new size |
308 // copy elements and set new size |
|
309 // note: no overflow check here, b/c we cannot get here w/o allocation |
116 memmove(start, src, elem_count * elem_size); |
310 memmove(start, src, elem_count * elem_size); |
117 *size = newsize; |
311 |
|
312 // if any of size or capacity changed, store them back |
|
313 if (newsize != oldsize || newcap != oldcap) { |
|
314 if (width == 0 || width == sizeof(size_t)) { |
|
315 *(size_t*) capacity = newcap; |
|
316 *(size_t*) size = newsize; |
|
317 } else if (width == sizeof(uint16_t)) { |
|
318 *(uint16_t*) capacity = (uint16_t) newcap; |
|
319 *(uint16_t*) size = (uint16_t) newsize; |
|
320 } else if (width == sizeof(uint8_t)) { |
|
321 *(uint8_t*) capacity = (uint8_t) newcap; |
|
322 *(uint8_t*) size = (uint8_t) newsize; |
|
323 } |
|
324 #if CX_WORDSIZE == 64 |
|
325 else if (width == sizeof(uint32_t)) { |
|
326 *(uint32_t*) capacity = (uint32_t) newcap; |
|
327 *(uint32_t*) size = (uint32_t) newsize; |
|
328 } |
|
329 #endif |
|
330 } |
118 |
331 |
119 // return successfully |
332 // return successfully |
120 return CX_ARRAY_SUCCESS; |
333 return 0; |
|
334 } |
|
335 |
|
336 int cx_array_insert_sorted( |
|
337 void **target, |
|
338 size_t *size, |
|
339 size_t *capacity, |
|
340 cx_compare_func cmp_func, |
|
341 const void *sorted_data, |
|
342 size_t elem_size, |
|
343 size_t elem_count, |
|
344 CxArrayReallocator *reallocator |
|
345 ) { |
|
346 // assert pointers |
|
347 assert(target != NULL); |
|
348 assert(size != NULL); |
|
349 assert(capacity != NULL); |
|
350 assert(cmp_func != NULL); |
|
351 assert(sorted_data != NULL); |
|
352 |
|
353 // default reallocator |
|
354 if (reallocator == NULL) { |
|
355 reallocator = cx_array_default_reallocator; |
|
356 } |
|
357 |
|
358 // corner case |
|
359 if (elem_count == 0) return 0; |
|
360 |
|
361 // overflow check |
|
362 if (elem_count > SIZE_MAX - *size) { |
|
363 errno = EOVERFLOW; |
|
364 return 1; |
|
365 } |
|
366 |
|
367 // store some counts |
|
368 size_t old_size = *size; |
|
369 size_t needed_capacity = old_size + elem_count; |
|
370 |
|
371 // if we need more than we have, try a reallocation |
|
372 if (needed_capacity > *capacity) { |
|
373 size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); |
|
374 void *new_mem = reallocator->realloc( |
|
375 *target, new_capacity, elem_size, reallocator |
|
376 ); |
|
377 if (new_mem == NULL) { |
|
378 // give it up right away, there is no contract |
|
379 // that requires us to insert as much as we can |
|
380 return 1; // LCOV_EXCL_LINE |
|
381 } |
|
382 *target = new_mem; |
|
383 *capacity = new_capacity; |
|
384 } |
|
385 |
|
386 // now we have guaranteed that we can insert everything |
|
387 size_t new_size = old_size + elem_count; |
|
388 *size = new_size; |
|
389 |
|
390 // declare the source and destination indices/pointers |
|
391 size_t si = 0, di = 0; |
|
392 const char *src = sorted_data; |
|
393 char *dest = *target; |
|
394 |
|
395 // find the first insertion point |
|
396 di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func); |
|
397 dest += di * elem_size; |
|
398 |
|
399 // move the remaining elements in the array completely to the right |
|
400 // we will call it the "buffer" for parked elements |
|
401 size_t buf_size = old_size - di; |
|
402 size_t bi = new_size - buf_size; |
|
403 char *bptr = ((char *) *target) + bi * elem_size; |
|
404 memmove(bptr, dest, buf_size * elem_size); |
|
405 |
|
406 // while there are both source and buffered elements left, |
|
407 // copy them interleaving |
|
408 while (si < elem_count && bi < new_size) { |
|
409 // determine how many source elements can be inserted |
|
410 size_t copy_len, bytes_copied; |
|
411 copy_len = cx_array_binary_search_sup( |
|
412 src, |
|
413 elem_count - si, |
|
414 elem_size, |
|
415 bptr, |
|
416 cmp_func |
|
417 ); |
|
418 |
|
419 // copy the source elements |
|
420 bytes_copied = copy_len * elem_size; |
|
421 memcpy(dest, src, bytes_copied); |
|
422 dest += bytes_copied; |
|
423 src += bytes_copied; |
|
424 si += copy_len; |
|
425 |
|
426 // when all source elements are in place, we are done |
|
427 if (si >= elem_count) break; |
|
428 |
|
429 // determine how many buffered elements need to be restored |
|
430 copy_len = cx_array_binary_search_sup( |
|
431 bptr, |
|
432 new_size - bi, |
|
433 elem_size, |
|
434 src, |
|
435 cmp_func |
|
436 ); |
|
437 |
|
438 // restore the buffered elements |
|
439 bytes_copied = copy_len * elem_size; |
|
440 memmove(dest, bptr, bytes_copied); |
|
441 dest += bytes_copied; |
|
442 bptr += bytes_copied; |
|
443 bi += copy_len; |
|
444 } |
|
445 |
|
446 // still source elements left? simply append them |
|
447 if (si < elem_count) { |
|
448 memcpy(dest, src, elem_size * (elem_count - si)); |
|
449 } |
|
450 |
|
451 // still buffer elements left? |
|
452 // don't worry, we already moved them to the correct place |
|
453 |
|
454 return 0; |
|
455 } |
|
456 |
|
457 size_t cx_array_binary_search_inf( |
|
458 const void *arr, |
|
459 size_t size, |
|
460 size_t elem_size, |
|
461 const void *elem, |
|
462 cx_compare_func cmp_func |
|
463 ) { |
|
464 // special case: empty array |
|
465 if (size == 0) return 0; |
|
466 |
|
467 // declare a variable that will contain the compare results |
|
468 int result; |
|
469 |
|
470 // cast the array pointer to something we can use offsets with |
|
471 const char *array = arr; |
|
472 |
|
473 // check the first array element |
|
474 result = cmp_func(elem, array); |
|
475 if (result < 0) { |
|
476 return size; |
|
477 } else if (result == 0) { |
|
478 return 0; |
|
479 } |
|
480 |
|
481 // special case: there is only one element and that is smaller |
|
482 if (size == 1) return 0; |
|
483 |
|
484 // check the last array element |
|
485 result = cmp_func(elem, array + elem_size * (size - 1)); |
|
486 if (result >= 0) { |
|
487 return size - 1; |
|
488 } |
|
489 |
|
490 // the element is now guaranteed to be somewhere in the list |
|
491 // so start the binary search |
|
492 size_t left_index = 1; |
|
493 size_t right_index = size - 1; |
|
494 size_t pivot_index; |
|
495 |
|
496 while (left_index <= right_index) { |
|
497 pivot_index = left_index + (right_index - left_index) / 2; |
|
498 const char *arr_elem = array + pivot_index * elem_size; |
|
499 result = cmp_func(elem, arr_elem); |
|
500 if (result == 0) { |
|
501 // found it! |
|
502 return pivot_index; |
|
503 } else if (result < 0) { |
|
504 // element is smaller than pivot, continue search left |
|
505 right_index = pivot_index - 1; |
|
506 } else { |
|
507 // element is larger than pivot, continue search right |
|
508 left_index = pivot_index + 1; |
|
509 } |
|
510 } |
|
511 |
|
512 // report the largest upper bound |
|
513 return result < 0 ? (pivot_index - 1) : pivot_index; |
|
514 } |
|
515 |
|
516 size_t cx_array_binary_search( |
|
517 const void *arr, |
|
518 size_t size, |
|
519 size_t elem_size, |
|
520 const void *elem, |
|
521 cx_compare_func cmp_func |
|
522 ) { |
|
523 size_t index = cx_array_binary_search_inf( |
|
524 arr, size, elem_size, elem, cmp_func |
|
525 ); |
|
526 if (index < size && |
|
527 cmp_func(((const char *) arr) + index * elem_size, elem) == 0) { |
|
528 return index; |
|
529 } else { |
|
530 return size; |
|
531 } |
|
532 } |
|
533 |
|
534 size_t cx_array_binary_search_sup( |
|
535 const void *arr, |
|
536 size_t size, |
|
537 size_t elem_size, |
|
538 const void *elem, |
|
539 cx_compare_func cmp_func |
|
540 ) { |
|
541 size_t inf = cx_array_binary_search_inf( |
|
542 arr, size, elem_size, elem, cmp_func |
|
543 ); |
|
544 if (inf == size) { |
|
545 // no infimum means, first element is supremum |
|
546 return 0; |
|
547 } else if (cmp_func(((const char *) arr) + inf * elem_size, elem) == 0) { |
|
548 return inf; |
|
549 } else { |
|
550 return inf + 1; |
|
551 } |
121 } |
552 } |
122 |
553 |
123 #ifndef CX_ARRAY_SWAP_SBO_SIZE |
554 #ifndef CX_ARRAY_SWAP_SBO_SIZE |
124 #define CX_ARRAY_SWAP_SBO_SIZE 128 |
555 #define CX_ARRAY_SWAP_SBO_SIZE 128 |
125 #endif |
556 #endif |
126 unsigned cx_array_swap_sbo_size = CX_ARRAY_SWAP_SBO_SIZE; |
557 const unsigned cx_array_swap_sbo_size = CX_ARRAY_SWAP_SBO_SIZE; |
127 |
558 |
128 void cx_array_swap( |
559 void cx_array_swap( |
129 void *arr, |
560 void *arr, |
130 size_t elem_size, |
561 size_t elem_size, |
131 size_t idx1, |
562 size_t idx1, |
245 // note that if we had to move the elements, the following operation |
664 // note that if we had to move the elements, the following operation |
246 // is guaranteed to succeed, because we have the memory already allocated |
665 // is guaranteed to succeed, because we have the memory already allocated |
247 // therefore, it is impossible to leave this function with an invalid array |
666 // therefore, it is impossible to leave this function with an invalid array |
248 |
667 |
249 // place the new elements |
668 // place the new elements |
250 if (CX_ARRAY_SUCCESS == cx_array_copy( |
669 if (cx_array_copy( |
251 &arl->data, |
670 &arl->data, |
252 &list->collection.size, |
671 &list->collection.size, |
253 &arl->capacity, |
672 &arl->capacity, |
|
673 0, |
254 index, |
674 index, |
255 array, |
675 array, |
256 list->collection.elem_size, |
676 list->collection.elem_size, |
257 n, |
677 n, |
258 &arl->reallocator |
678 &arl->reallocator |
259 )) { |
679 )) { |
260 return n; |
|
261 } else { |
|
262 // array list implementation is "all or nothing" |
680 // array list implementation is "all or nothing" |
263 return 0; |
681 return 0; |
|
682 } else { |
|
683 return n; |
|
684 } |
|
685 } |
|
686 |
|
687 static size_t cx_arl_insert_sorted( |
|
688 struct cx_list_s *list, |
|
689 const void *sorted_data, |
|
690 size_t n |
|
691 ) { |
|
692 // get a correctly typed pointer to the list |
|
693 cx_array_list *arl = (cx_array_list *) list; |
|
694 |
|
695 if (cx_array_insert_sorted( |
|
696 &arl->data, |
|
697 &list->collection.size, |
|
698 &arl->capacity, |
|
699 list->collection.cmpfunc, |
|
700 sorted_data, |
|
701 list->collection.elem_size, |
|
702 n, |
|
703 &arl->reallocator |
|
704 )) { |
|
705 // array list implementation is "all or nothing" |
|
706 return 0; |
|
707 } else { |
|
708 return n; |
264 } |
709 } |
265 } |
710 } |
266 |
711 |
267 static int cx_arl_insert_element( |
712 static int cx_arl_insert_element( |
268 struct cx_list_s *list, |
713 struct cx_list_s *list, |
269 size_t index, |
714 size_t index, |
270 void const *element |
715 const void *element |
271 ) { |
716 ) { |
272 return 1 != cx_arl_insert_array(list, index, element, 1); |
717 return 1 != cx_arl_insert_array(list, index, element, 1); |
273 } |
718 } |
274 |
719 |
275 static int cx_arl_insert_iter( |
720 static int cx_arl_insert_iter( |
276 struct cx_iterator_s *iter, |
721 struct cx_iterator_s *iter, |
277 void const *elem, |
722 const void *elem, |
278 int prepend |
723 int prepend |
279 ) { |
724 ) { |
280 struct cx_list_s *list = iter->src_handle.m; |
725 struct cx_list_s *list = iter->src_handle.m; |
281 if (iter->index < list->collection.size) { |
726 if (iter->index < list->collection.size) { |
282 int result = cx_arl_insert_element( |
727 int result = cx_arl_insert_element( |
283 list, |
728 list, |
284 iter->index + 1 - prepend, |
729 iter->index + 1 - prepend, |
285 elem |
730 elem |
286 ); |
731 ); |
287 if (result == 0 && prepend != 0) { |
732 if (result == 0) { |
288 iter->index++; |
733 iter->elem_count++; |
289 iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size; |
734 if (prepend != 0) { |
|
735 iter->index++; |
|
736 iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size; |
|
737 } |
290 } |
738 } |
291 return result; |
739 return result; |
292 } else { |
740 } else { |
293 int result = cx_arl_insert_element(list, list->collection.size, elem); |
741 int result = cx_arl_insert_element(list, list->collection.size, elem); |
294 iter->index = list->collection.size; |
742 if (result == 0) { |
|
743 iter->elem_count++; |
|
744 iter->index = list->collection.size; |
|
745 } |
295 return result; |
746 return result; |
296 } |
747 } |
297 } |
748 } |
298 |
749 |
299 static int cx_arl_remove( |
750 static size_t cx_arl_remove( |
300 struct cx_list_s *list, |
751 struct cx_list_s *list, |
301 size_t index |
752 size_t index, |
|
753 size_t num, |
|
754 void *targetbuf |
302 ) { |
755 ) { |
303 cx_array_list *arl = (cx_array_list *) list; |
756 cx_array_list *arl = (cx_array_list *) list; |
304 |
757 |
305 // out-of-bounds check |
758 // out-of-bounds check |
|
759 size_t remove; |
306 if (index >= list->collection.size) { |
760 if (index >= list->collection.size) { |
307 return 1; |
761 remove = 0; |
308 } |
762 } else if (index + num > list->collection.size) { |
309 |
763 remove = list->collection.size - index; |
310 // content destruction |
764 } else { |
311 cx_invoke_destructor(list, ((char *) arl->data) + index * list->collection.elem_size); |
765 remove = num; |
312 |
766 } |
313 // short-circuit removal of last element |
767 |
314 if (index == list->collection.size - 1) { |
768 // easy exit |
315 list->collection.size--; |
769 if (remove == 0) return 0; |
316 return 0; |
770 |
317 } |
771 // destroy or copy contents |
318 |
772 if (targetbuf == NULL) { |
319 // just move the elements starting at index to the left |
773 for (size_t idx = index; idx < index + remove; idx++) { |
320 int result = cx_array_copy( |
774 cx_invoke_destructor( |
|
775 list, |
|
776 ((char *) arl->data) + idx * list->collection.elem_size |
|
777 ); |
|
778 } |
|
779 } else { |
|
780 memcpy( |
|
781 targetbuf, |
|
782 ((char *) arl->data) + index * list->collection.elem_size, |
|
783 remove * list->collection.elem_size |
|
784 ); |
|
785 } |
|
786 |
|
787 // short-circuit removal of last elements |
|
788 if (index + remove == list->collection.size) { |
|
789 list->collection.size -= remove; |
|
790 return remove; |
|
791 } |
|
792 |
|
793 // just move the elements to the left |
|
794 cx_array_copy( |
321 &arl->data, |
795 &arl->data, |
322 &list->collection.size, |
796 &list->collection.size, |
323 &arl->capacity, |
797 &arl->capacity, |
|
798 0, |
324 index, |
799 index, |
325 ((char *) arl->data) + (index + 1) * list->collection.elem_size, |
800 ((char *) arl->data) + (index + remove) * list->collection.elem_size, |
326 list->collection.elem_size, |
801 list->collection.elem_size, |
327 list->collection.size - index - 1, |
802 list->collection.size - index - remove, |
328 &arl->reallocator |
803 &arl->reallocator |
329 ); |
804 ); |
330 |
805 |
331 // cx_array_copy cannot fail, array cannot grow |
|
332 assert(result == 0); |
|
333 |
|
334 // decrease the size |
806 // decrease the size |
335 list->collection.size--; |
807 list->collection.size -= remove; |
336 |
808 |
337 return 0; |
809 return remove; |
338 } |
810 } |
339 |
811 |
340 static void cx_arl_clear(struct cx_list_s *list) { |
812 static void cx_arl_clear(struct cx_list_s *list) { |
341 if (list->collection.size == 0) return; |
813 if (list->collection.size == 0) return; |
342 |
814 |
442 } |
915 } |
443 } |
916 } |
444 |
917 |
445 static void cx_arl_reverse(struct cx_list_s *list) { |
918 static void cx_arl_reverse(struct cx_list_s *list) { |
446 if (list->collection.size < 2) return; |
919 if (list->collection.size < 2) return; |
447 void *data = ((cx_array_list const *) list)->data; |
920 void *data = ((const cx_array_list *) list)->data; |
448 size_t half = list->collection.size / 2; |
921 size_t half = list->collection.size / 2; |
449 for (size_t i = 0; i < half; i++) { |
922 for (size_t i = 0; i < half; i++) { |
450 cx_array_swap(data, list->collection.elem_size, i, list->collection.size - 1 - i); |
923 cx_array_swap(data, list->collection.elem_size, i, list->collection.size - 1 - i); |
451 } |
924 } |
452 } |
925 } |
453 |
926 |
454 static bool cx_arl_iter_valid(void const *it) { |
927 static bool cx_arl_iter_valid(const void *it) { |
455 struct cx_iterator_s const *iter = it; |
928 const struct cx_iterator_s *iter = it; |
456 struct cx_list_s const *list = iter->src_handle.c; |
929 const struct cx_list_s *list = iter->src_handle.c; |
457 return iter->index < list->collection.size; |
930 return iter->index < list->collection.size; |
458 } |
931 } |
459 |
932 |
460 static void *cx_arl_iter_current(void const *it) { |
933 static void *cx_arl_iter_current(const void *it) { |
461 struct cx_iterator_s const *iter = it; |
934 const struct cx_iterator_s *iter = it; |
462 return iter->elem_handle; |
935 return iter->elem_handle; |
463 } |
936 } |
464 |
937 |
465 static void cx_arl_iter_next(void *it) { |
938 static void cx_arl_iter_next(void *it) { |
466 struct cx_iterator_s *iter = it; |
939 struct cx_iterator_s *iter = it; |
467 if (iter->base.remove) { |
940 if (iter->base.remove) { |
468 iter->base.remove = false; |
941 iter->base.remove = false; |
469 cx_arl_remove(iter->src_handle.m, iter->index); |
942 cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); |
470 } else { |
943 } else { |
471 iter->index++; |
944 iter->index++; |
472 iter->elem_handle = |
945 iter->elem_handle = |
473 ((char *) iter->elem_handle) |
946 ((char *) iter->elem_handle) |
474 + ((struct cx_list_s const *) iter->src_handle.c)->collection.elem_size; |
947 + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size; |
475 } |
948 } |
476 } |
949 } |
477 |
950 |
478 static void cx_arl_iter_prev(void *it) { |
951 static void cx_arl_iter_prev(void *it) { |
479 struct cx_iterator_s *iter = it; |
952 struct cx_iterator_s *iter = it; |
480 cx_array_list const* list = iter->src_handle.c; |
953 const cx_array_list *list = iter->src_handle.c; |
481 if (iter->base.remove) { |
954 if (iter->base.remove) { |
482 iter->base.remove = false; |
955 iter->base.remove = false; |
483 cx_arl_remove(iter->src_handle.m, iter->index); |
956 cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); |
484 } |
957 } |
485 iter->index--; |
958 iter->index--; |
486 if (iter->index < list->base.collection.size) { |
959 if (iter->index < list->base.collection.size) { |
487 iter->elem_handle = ((char *) list->data) |
960 iter->elem_handle = ((char *) list->data) |
488 + iter->index * list->base.collection.elem_size; |
961 + iter->index * list->base.collection.elem_size; |
489 } |
962 } |
490 } |
963 } |
491 |
964 |
492 |
965 |
493 static struct cx_iterator_s cx_arl_iterator( |
966 static struct cx_iterator_s cx_arl_iterator( |
494 struct cx_list_s const *list, |
967 const struct cx_list_s *list, |
495 size_t index, |
968 size_t index, |
496 bool backwards |
969 bool backwards |
497 ) { |
970 ) { |
498 struct cx_iterator_s iter; |
971 struct cx_iterator_s iter; |
499 |
972 |