ucx/array_list.c

changeset 852
83fdf679df99
parent 816
839fefbdedc7
equal deleted inserted replaced
850:bbe2925eb590 852:83fdf679df99
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,
168 599
169 typedef struct { 600 typedef struct {
170 struct cx_list_s base; 601 struct cx_list_s base;
171 void *data; 602 void *data;
172 size_t capacity; 603 size_t capacity;
173 struct cx_array_reallocator_s reallocator; 604 CxArrayReallocator reallocator;
174 } cx_array_list; 605 } cx_array_list;
175
176 static void *cx_arl_realloc(
177 void *array,
178 size_t capacity,
179 size_t elem_size,
180 struct cx_array_reallocator_s *alloc
181 ) {
182 // retrieve the pointer to the list allocator
183 CxAllocator const *al = alloc->ptr1;
184
185 // use the list allocator to reallocate the memory
186 return cxRealloc(al, array, capacity * elem_size);
187 }
188 606
189 static void cx_arl_destructor(struct cx_list_s *list) { 607 static void cx_arl_destructor(struct cx_list_s *list) {
190 cx_array_list *arl = (cx_array_list *) list; 608 cx_array_list *arl = (cx_array_list *) list;
191 609
192 char *ptr = arl->data; 610 char *ptr = arl->data;
209 } 627 }
210 628
211 static size_t cx_arl_insert_array( 629 static size_t cx_arl_insert_array(
212 struct cx_list_s *list, 630 struct cx_list_s *list,
213 size_t index, 631 size_t index,
214 void const *array, 632 const void *array,
215 size_t n 633 size_t n
216 ) { 634 ) {
217 // out of bounds and special case check 635 // out of bounds and special case check
218 if (index > list->collection.size || n == 0) return 0; 636 if (index > list->collection.size || n == 0) return 0;
219 637
220 // get a correctly typed pointer to the list 638 // get a correctly typed pointer to the list
221 cx_array_list *arl = (cx_array_list *) list; 639 cx_array_list *arl = (cx_array_list *) list;
222 640
223 // do we need to move some elements? 641 // do we need to move some elements?
224 if (index < list->collection.size) { 642 if (index < list->collection.size) {
225 char const *first_to_move = (char const *) arl->data; 643 const char *first_to_move = (const char *) arl->data;
226 first_to_move += index * list->collection.elem_size; 644 first_to_move += index * list->collection.elem_size;
227 size_t elems_to_move = list->collection.size - index; 645 size_t elems_to_move = list->collection.size - index;
228 size_t start_of_moved = index + n; 646 size_t start_of_moved = index + n;
229 647
230 if (CX_ARRAY_SUCCESS != cx_array_copy( 648 if (cx_array_copy(
231 &arl->data, 649 &arl->data,
232 &list->collection.size, 650 &list->collection.size,
233 &arl->capacity, 651 &arl->capacity,
652 0,
234 start_of_moved, 653 start_of_moved,
235 first_to_move, 654 first_to_move,
236 list->collection.elem_size, 655 list->collection.elem_size,
237 elems_to_move, 656 elems_to_move,
238 &arl->reallocator 657 &arl->reallocator
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
370 cx_array_swap(arl->data, list->collection.elem_size, i, j); 842 cx_array_swap(arl->data, list->collection.elem_size, i, j);
371 return 0; 843 return 0;
372 } 844 }
373 845
374 static void *cx_arl_at( 846 static void *cx_arl_at(
375 struct cx_list_s const *list, 847 const struct cx_list_s *list,
376 size_t index 848 size_t index
377 ) { 849 ) {
378 if (index < list->collection.size) { 850 if (index < list->collection.size) {
379 cx_array_list const *arl = (cx_array_list const *) list; 851 const cx_array_list *arl = (const cx_array_list *) list;
380 char *space = arl->data; 852 char *space = arl->data;
381 return space + index * list->collection.elem_size; 853 return space + index * list->collection.elem_size;
382 } else { 854 } else {
383 return NULL; 855 return NULL;
384 } 856 }
385 } 857 }
386 858
387 static ssize_t cx_arl_find_remove( 859 static ssize_t cx_arl_find_remove(
388 struct cx_list_s *list, 860 struct cx_list_s *list,
389 void const *elem, 861 const void *elem,
390 bool remove 862 bool remove
391 ) { 863 ) {
392 assert(list->collection.cmpfunc != NULL); 864 assert(list->collection.cmpfunc != NULL);
393 assert(list->collection.size < SIZE_MAX / 2); 865 assert(list->collection.size < SIZE_MAX / 2);
394 char *cur = ((cx_array_list const *) list)->data; 866 char *cur = ((const cx_array_list *) list)->data;
395 867
396 for (ssize_t i = 0; i < (ssize_t) list->collection.size; i++) { 868 for (ssize_t i = 0; i < (ssize_t) list->collection.size; i++) {
397 if (0 == list->collection.cmpfunc(elem, cur)) { 869 if (0 == list->collection.cmpfunc(elem, cur)) {
398 if (remove) { 870 if (remove) {
399 if (0 == cx_arl_remove(list, i)) { 871 if (1 == cx_arl_remove(list, i, 1, NULL)) {
400 return i; 872 return i;
401 } else { 873 } else {
402 return -1; 874 // should be unreachable
875 return -1; // LCOV_EXCL_LINE
403 } 876 }
404 } else { 877 } else {
405 return i; 878 return i;
406 } 879 }
407 } 880 }
419 list->collection.cmpfunc 892 list->collection.cmpfunc
420 ); 893 );
421 } 894 }
422 895
423 static int cx_arl_compare( 896 static int cx_arl_compare(
424 struct cx_list_s const *list, 897 const struct cx_list_s *list,
425 struct cx_list_s const *other 898 const struct cx_list_s *other
426 ) { 899 ) {
427 assert(list->collection.cmpfunc != NULL); 900 assert(list->collection.cmpfunc != NULL);
428 if (list->collection.size == other->collection.size) { 901 if (list->collection.size == other->collection.size) {
429 char const *left = ((cx_array_list const *) list)->data; 902 const char *left = ((const cx_array_list *) list)->data;
430 char const *right = ((cx_array_list const *) other)->data; 903 const char *right = ((const cx_array_list *) other)->data;
431 for (size_t i = 0; i < list->collection.size; i++) { 904 for (size_t i = 0; i < list->collection.size; i++) {
432 int d = list->collection.cmpfunc(left, right); 905 int d = list->collection.cmpfunc(left, right);
433 if (d != 0) { 906 if (d != 0) {
434 return d; 907 return d;
435 } 908 }
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
513 986
514 static cx_list_class cx_array_list_class = { 987 static cx_list_class cx_array_list_class = {
515 cx_arl_destructor, 988 cx_arl_destructor,
516 cx_arl_insert_element, 989 cx_arl_insert_element,
517 cx_arl_insert_array, 990 cx_arl_insert_array,
991 cx_arl_insert_sorted,
518 cx_arl_insert_iter, 992 cx_arl_insert_iter,
519 cx_arl_remove, 993 cx_arl_remove,
520 cx_arl_clear, 994 cx_arl_clear,
521 cx_arl_swap, 995 cx_arl_swap,
522 cx_arl_at, 996 cx_arl_at,
526 cx_arl_reverse, 1000 cx_arl_reverse,
527 cx_arl_iterator, 1001 cx_arl_iterator,
528 }; 1002 };
529 1003
530 CxList *cxArrayListCreate( 1004 CxList *cxArrayListCreate(
531 CxAllocator const *allocator, 1005 const CxAllocator *allocator,
532 cx_compare_func comparator, 1006 cx_compare_func comparator,
533 size_t elem_size, 1007 size_t elem_size,
534 size_t initial_capacity 1008 size_t initial_capacity
535 ) { 1009 ) {
536 if (allocator == NULL) { 1010 if (allocator == NULL) {
553 cxListStorePointers((CxList *) list); 1027 cxListStorePointers((CxList *) list);
554 } 1028 }
555 1029
556 // allocate the array after the real elem_size is known 1030 // allocate the array after the real elem_size is known
557 list->data = cxCalloc(allocator, initial_capacity, elem_size); 1031 list->data = cxCalloc(allocator, initial_capacity, elem_size);
558 if (list->data == NULL) { 1032 if (list->data == NULL) { // LCOV_EXCL_START
559 cxFree(allocator, list); 1033 cxFree(allocator, list);
560 return NULL; 1034 return NULL;
561 } 1035 } // LCOV_EXCL_STOP
562 1036
563 // configure the reallocator 1037 // configure the reallocator
564 list->reallocator.realloc = cx_arl_realloc; 1038 list->reallocator = cx_array_reallocator(allocator, NULL);
565 list->reallocator.ptr1 = (void *) allocator;
566 1039
567 return (CxList *) list; 1040 return (CxList *) list;
568 } 1041 }

mercurial