ucx/array_list.c

changeset 943
9b5948aa5b90
parent 870
e167cf006213
equal deleted inserted replaced
942:488178e3e328 943:9b5948aa5b90
40 size_t new_capacity, 40 size_t new_capacity,
41 size_t elem_size, 41 size_t elem_size,
42 cx_attr_unused CxArrayReallocator *alloc 42 cx_attr_unused CxArrayReallocator *alloc
43 ) { 43 ) {
44 size_t n; 44 size_t n;
45 // LCOV_EXCL_START
45 if (cx_szmul(new_capacity, elem_size, &n)) { 46 if (cx_szmul(new_capacity, elem_size, &n)) {
46 errno = EOVERFLOW; 47 errno = EOVERFLOW;
47 return NULL; 48 return NULL;
48 } 49 } // LCOV_EXCL_STOP
49 return cxReallocDefault(array, n); 50 return cxReallocDefault(array, n);
50 } 51 }
51 52
52 CxArrayReallocator cx_array_default_reallocator_impl = { 53 CxArrayReallocator cx_array_default_reallocator_impl = {
53 cx_array_default_realloc, NULL, NULL 54 cx_array_default_realloc, NULL, NULL
64 size_t elem_size, 65 size_t elem_size,
65 cx_attr_unused CxArrayReallocator *alloc 66 cx_attr_unused CxArrayReallocator *alloc
66 ) { 67 ) {
67 // check for overflow 68 // check for overflow
68 size_t n; 69 size_t n;
70 // LCOV_EXCL_START
69 if (cx_szmul(new_capacity, elem_size, &n)) { 71 if (cx_szmul(new_capacity, elem_size, &n)) {
70 errno = EOVERFLOW; 72 errno = EOVERFLOW;
71 return NULL; 73 return NULL;
72 } 74 } // LCOV_EXCL_STOP
73 75
74 // retrieve the pointer to the actual allocator 76 // retrieve the pointer to the actual allocator
75 const CxAllocator *al = alloc->allocator; 77 const CxAllocator *al = alloc->allocator;
76 78
77 // check if the array is still located on the stack 79 // check if the array is still located on the stack
100 }; 102 };
101 } 103 }
102 104
103 // LOW LEVEL ARRAY LIST FUNCTIONS 105 // LOW LEVEL ARRAY LIST FUNCTIONS
104 106
105 static size_t cx_array_align_capacity( 107 /**
106 size_t cap, 108 * Intelligently calculates a new capacity, reserving some more
107 size_t alignment, 109 * elements than required to prevent too many allocations.
108 size_t max 110 *
109 ) { 111 * @param current_capacity the current capacity of the array
110 if (cap > max - alignment) { 112 * @param needed_capacity the required capacity of the array
111 return cap; 113 * @return the new capacity
112 } else { 114 */
113 return cap - (cap % alignment) + alignment; 115 static size_t cx_array_grow_capacity(
114 } 116 size_t current_capacity,
117 size_t needed_capacity
118 ) {
119 if (current_capacity >= needed_capacity) {
120 return current_capacity;
121 }
122 size_t cap = needed_capacity;
123 size_t alignment;
124 if (cap < 128) alignment = 16;
125 else if (cap < 1024) alignment = 64;
126 else if (cap < 8192) alignment = 512;
127 else alignment = 1024;
128 return cap - (cap % alignment) + alignment;
115 } 129 }
116 130
117 int cx_array_reserve( 131 int cx_array_reserve(
118 void **array, 132 void **array,
119 void *size, 133 void *size,
174 // determine new capacity 188 // determine new capacity
175 size_t newcap = oldsize + elem_count; 189 size_t newcap = oldsize + elem_count;
176 190
177 // reallocate if possible 191 // reallocate if possible
178 if (newcap > oldcap) { 192 if (newcap > oldcap) {
179 // calculate new capacity (next number divisible by 16)
180 newcap = cx_array_align_capacity(newcap, 16, max_size);
181
182 // perform reallocation
183 void *newmem = reallocator->realloc( 193 void *newmem = reallocator->realloc(
184 *array, oldcap, newcap, elem_size, reallocator 194 *array, oldcap, newcap, elem_size, reallocator
185 ); 195 );
186 if (newmem == NULL) { 196 if (newmem == NULL) {
187 return 1; // LCOV_EXCL_LINE 197 return 1; // LCOV_EXCL_LINE
267 errno = EOVERFLOW; 277 errno = EOVERFLOW;
268 return 1; 278 return 1;
269 } 279 }
270 280
271 // check if resize is required 281 // check if resize is required
272 size_t minsize = index + elem_count; 282 const size_t minsize = index + elem_count;
273 size_t newsize = oldsize < minsize ? minsize : oldsize; 283 const size_t newsize = oldsize < minsize ? minsize : oldsize;
274 284
275 // reallocate if possible 285 // reallocate if necessary
276 size_t newcap = oldcap; 286 const size_t newcap = cx_array_grow_capacity(oldcap, newsize);
277 if (newsize > oldcap) { 287 if (newcap > oldcap) {
278 // check, if we need to repair the src pointer 288 // check if we need to repair the src pointer
279 uintptr_t targetaddr = (uintptr_t) *target; 289 uintptr_t targetaddr = (uintptr_t) *target;
280 uintptr_t srcaddr = (uintptr_t) src; 290 uintptr_t srcaddr = (uintptr_t) src;
281 bool repairsrc = targetaddr <= srcaddr 291 bool repairsrc = targetaddr <= srcaddr
282 && srcaddr < targetaddr + oldcap * elem_size; 292 && srcaddr < targetaddr + oldcap * elem_size;
283
284 // calculate new capacity (next number divisible by 16)
285 newcap = cx_array_align_capacity(newsize, 16, max_size);
286 assert(newcap > newsize);
287 293
288 // perform reallocation 294 // perform reallocation
289 void *newmem = reallocator->realloc( 295 void *newmem = reallocator->realloc(
290 *target, oldcap, newcap, elem_size, reallocator 296 *target, oldcap, newcap, elem_size, reallocator
291 ); 297 );
359 365
360 // corner case 366 // corner case
361 if (elem_count == 0) return 0; 367 if (elem_count == 0) return 0;
362 368
363 // overflow check 369 // overflow check
370 // LCOV_EXCL_START
364 if (elem_count > SIZE_MAX - *size) { 371 if (elem_count > SIZE_MAX - *size) {
365 errno = EOVERFLOW; 372 errno = EOVERFLOW;
366 return 1; 373 return 1;
367 } 374 }
375 // LCOV_EXCL_STOP
368 376
369 // store some counts 377 // store some counts
370 size_t old_size = *size; 378 const size_t old_size = *size;
371 size_t old_capacity = *capacity; 379 const size_t old_capacity = *capacity;
372 // the necessary capacity is the worst case assumption, including duplicates 380 // the necessary capacity is the worst case assumption, including duplicates
373 size_t needed_capacity = old_size + elem_count; 381 const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + elem_count);
374 382
375 // if we need more than we have, try a reallocation 383 // if we need more than we have, try a reallocation
376 if (needed_capacity > old_capacity) { 384 if (needed_capacity > old_capacity) {
377 size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX);
378 void *new_mem = reallocator->realloc( 385 void *new_mem = reallocator->realloc(
379 *target, old_capacity, new_capacity, elem_size, reallocator 386 *target, old_capacity, needed_capacity, elem_size, reallocator
380 ); 387 );
381 if (new_mem == NULL) { 388 if (new_mem == NULL) {
382 // give it up right away, there is no contract 389 // give it up right away, there is no contract
383 // that requires us to insert as much as we can 390 // that requires us to insert as much as we can
384 return 1; // LCOV_EXCL_LINE 391 return 1; // LCOV_EXCL_LINE
385 } 392 }
386 *target = new_mem; 393 *target = new_mem;
387 *capacity = new_capacity; 394 *capacity = needed_capacity;
388 } 395 }
389 396
390 // now we have guaranteed that we can insert everything 397 // now we have guaranteed that we can insert everything
391 size_t new_size = old_size + elem_count; 398 size_t new_size = old_size + elem_count;
392 *size = new_size; 399 *size = new_size;
408 memmove(bptr, dest, buf_size * elem_size); 415 memmove(bptr, dest, buf_size * elem_size);
409 416
410 // while there are both source and buffered elements left, 417 // while there are both source and buffered elements left,
411 // copy them interleaving 418 // copy them interleaving
412 while (si < elem_count && bi < new_size) { 419 while (si < elem_count && bi < new_size) {
413 // determine how many source elements can be inserted 420 // determine how many source elements can be inserted.
421 // the first element that shall not be inserted is the smallest element
422 // that is strictly larger than the first buffered element
423 // (located at the index of the infimum plus one).
424 // the infimum is guaranteed to exist:
425 // - if all src elements are larger,
426 // there is no buffer, and this loop is skipped
427 // - if any src element is smaller or equal, the infimum exists
428 // - when all src elements that are smaller are copied, the second part
429 // of this loop body will copy the remaining buffer (emptying it)
430 // Therefore, the buffer can never contain an element that is smaller
431 // than any element in the source and the infimum exists.
414 size_t copy_len, bytes_copied; 432 size_t copy_len, bytes_copied;
415 copy_len = cx_array_binary_search_sup( 433 copy_len = cx_array_binary_search_inf(
416 src, 434 src, elem_count - si, elem_size, bptr, cmp_func
417 elem_count - si,
418 elem_size,
419 bptr,
420 cmp_func
421 ); 435 );
422 // binary search gives us the smallest index; 436 copy_len++;
423 // we also want to include equal elements here
424 while (si + copy_len < elem_count
425 && cmp_func(bptr, src+copy_len*elem_size) == 0) {
426 copy_len++;
427 }
428 437
429 // copy the source elements 438 // copy the source elements
430 if (copy_len > 0) { 439 if (copy_len > 0) {
431 if (allow_duplicates) { 440 if (allow_duplicates) {
432 // we can copy the entire chunk 441 // we can copy the entire chunk
499 if (si < elem_count) { 508 if (si < elem_count) {
500 if (allow_duplicates) { 509 if (allow_duplicates) {
501 // duplicates allowed or nothing inserted yet: simply copy everything 510 // duplicates allowed or nothing inserted yet: simply copy everything
502 memcpy(dest, src, elem_size * (elem_count - si)); 511 memcpy(dest, src, elem_size * (elem_count - si));
503 } else { 512 } else {
504 if (dest != *target) { 513 // we must check the remaining source elements one by one
505 // skip all source elements that equal the last element 514 // to skip the duplicates.
506 char *last = dest - elem_size; 515 // Note that no source element can equal the last element in the
507 while (si < elem_count) { 516 // destination, because that would have created an insertion point
508 if (last != NULL && cmp_func(last, src) == 0) { 517 // and a buffer, s.t. the above loop already handled the duplicates
509 src += elem_size;
510 si++;
511 (*size)--;
512 } else {
513 break;
514 }
515 }
516 }
517 // we must check the elements in the chunk one by one
518 while (si < elem_count) { 518 while (si < elem_count) {
519 // find a chain of elements that can be copied 519 // find a chain of elements that can be copied
520 size_t copy_len = 1, skip_len = 0; 520 size_t copy_len = 1, skip_len = 0;
521 { 521 {
522 const char *left_src = src; 522 const char *left_src = src;
523 while (si + copy_len < elem_count) { 523 while (si + copy_len + skip_len < elem_count) {
524 const char *right_src = left_src + elem_size; 524 const char *right_src = left_src + elem_size;
525 int d = cmp_func(left_src, right_src); 525 int d = cmp_func(left_src, right_src);
526 if (d < 0) { 526 if (d < 0) {
527 if (skip_len > 0) { 527 if (skip_len > 0) {
528 // new larger element found; 528 // new larger element found;
586 ) { 586 ) {
587 return cx_array_insert_sorted_impl(target, size, capacity, 587 return cx_array_insert_sorted_impl(target, size, capacity,
588 cmp_func, sorted_data, elem_size, elem_count, reallocator, false); 588 cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
589 } 589 }
590 590
591 size_t cx_array_binary_search_inf( 591 // implementation that finds ANY index
592 static size_t cx_array_binary_search_inf_impl(
592 const void *arr, 593 const void *arr,
593 size_t size, 594 size_t size,
594 size_t elem_size, 595 size_t elem_size,
595 const void *elem, 596 const void *elem,
596 cx_compare_func cmp_func 597 cx_compare_func cmp_func
631 pivot_index = left_index + (right_index - left_index) / 2; 632 pivot_index = left_index + (right_index - left_index) / 2;
632 const char *arr_elem = array + pivot_index * elem_size; 633 const char *arr_elem = array + pivot_index * elem_size;
633 result = cmp_func(elem, arr_elem); 634 result = cmp_func(elem, arr_elem);
634 if (result == 0) { 635 if (result == 0) {
635 // found it! 636 // found it!
636 // check previous elements;
637 // when they are equal, report the smallest index
638 arr_elem -= elem_size;
639 while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
640 pivot_index--;
641 arr_elem -= elem_size;
642 }
643 return pivot_index; 637 return pivot_index;
644 } else if (result < 0) { 638 } else if (result < 0) {
645 // element is smaller than pivot, continue search left 639 // element is smaller than pivot, continue search left
646 right_index = pivot_index - 1; 640 right_index = pivot_index - 1;
647 } else { 641 } else {
650 } 644 }
651 } 645 }
652 646
653 // report the largest upper bound 647 // report the largest upper bound
654 return result < 0 ? (pivot_index - 1) : pivot_index; 648 return result < 0 ? (pivot_index - 1) : pivot_index;
649 }
650
651 size_t cx_array_binary_search_inf(
652 const void *arr,
653 size_t size,
654 size_t elem_size,
655 const void *elem,
656 cx_compare_func cmp_func
657 ) {
658 size_t index = cx_array_binary_search_inf_impl(
659 arr, size, elem_size, elem, cmp_func);
660 // in case of equality, report the largest index
661 const char *e = ((const char *) arr) + (index + 1) * elem_size;
662 while (index + 1 < size && cmp_func(e, elem) == 0) {
663 e += elem_size;
664 index++;
665 }
666 return index;
655 } 667 }
656 668
657 size_t cx_array_binary_search( 669 size_t cx_array_binary_search(
658 const void *arr, 670 const void *arr,
659 size_t size, 671 size_t size,
677 size_t size, 689 size_t size,
678 size_t elem_size, 690 size_t elem_size,
679 const void *elem, 691 const void *elem,
680 cx_compare_func cmp_func 692 cx_compare_func cmp_func
681 ) { 693 ) {
682 size_t inf = cx_array_binary_search_inf( 694 size_t index = cx_array_binary_search_inf_impl(
683 arr, size, elem_size, elem, cmp_func 695 arr, size, elem_size, elem, cmp_func
684 ); 696 );
685 if (inf == size) { 697 const char *e = ((const char *) arr) + index * elem_size;
686 // no infimum means, first element is supremum 698 if (index == size) {
699 // no infimum means the first element is supremum
687 return 0; 700 return 0;
688 } else if (cmp_func(((const char *) arr) + inf * elem_size, elem) == 0) { 701 } else if (cmp_func(e, elem) == 0) {
689 return inf; 702 // found an equal element, search the smallest index
703 e -= elem_size; // e now contains the element at index-1
704 while (index > 0 && cmp_func(e, elem) == 0) {
705 e -= elem_size;
706 index--;
707 }
708 return index;
690 } else { 709 } else {
691 return inf + 1; 710 // we already have the largest index of the infimum (by design)
711 // the next element is the supremum (or there is no supremum)
712 return index + 1;
692 } 713 }
693 } 714 }
694 715
695 #ifndef CX_ARRAY_SWAP_SBO_SIZE 716 #ifndef CX_ARRAY_SWAP_SBO_SIZE
696 #define CX_ARRAY_SWAP_SBO_SIZE 128 717 #define CX_ARRAY_SWAP_SBO_SIZE 128
779 // get a correctly typed pointer to the list 800 // get a correctly typed pointer to the list
780 cx_array_list *arl = (cx_array_list *) list; 801 cx_array_list *arl = (cx_array_list *) list;
781 802
782 // guarantee enough capacity 803 // guarantee enough capacity
783 if (arl->capacity < list->collection.size + n) { 804 if (arl->capacity < list->collection.size + n) {
784 size_t new_capacity = list->collection.size + n; 805 const size_t new_capacity = cx_array_grow_capacity(arl->capacity,list->collection.size + n);
785 new_capacity = new_capacity - (new_capacity % 16) + 16;
786 if (cxReallocateArray( 806 if (cxReallocateArray(
787 list->collection.allocator, 807 list->collection.allocator,
788 &arl->data, new_capacity, 808 &arl->data, new_capacity,
789 list->collection.elem_size) 809 list->collection.elem_size)
790 ) { 810 ) {
791 return 0; 811 return 0; // LCOV_EXCL_LINE
792 } 812 }
793 arl->capacity = new_capacity; 813 arl->capacity = new_capacity;
794 } 814 }
795 815
796 // determine insert position 816 // determine insert position
830 list->collection.elem_size, 850 list->collection.elem_size,
831 n, 851 n,
832 &arl->reallocator 852 &arl->reallocator
833 )) { 853 )) {
834 // array list implementation is "all or nothing" 854 // array list implementation is "all or nothing"
835 return 0; 855 return 0; // LCOV_EXCL_LINE
836 } else { 856 } else {
837 return n; 857 return n;
838 } 858 }
839 } 859 }
840 860
855 list->collection.elem_size, 875 list->collection.elem_size,
856 n, 876 n,
857 &arl->reallocator 877 &arl->reallocator
858 )) { 878 )) {
859 // array list implementation is "all or nothing" 879 // array list implementation is "all or nothing"
860 return 0; 880 return 0; // LCOV_EXCL_LINE
861 } else { 881 } else {
862 return n; 882 return n;
863 } 883 }
864 } 884 }
865 885
882 ) { 902 ) {
883 struct cx_list_s *list = iter->src_handle; 903 struct cx_list_s *list = iter->src_handle;
884 if (iter->index < list->collection.size) { 904 if (iter->index < list->collection.size) {
885 if (cx_arl_insert_element(list, 905 if (cx_arl_insert_element(list,
886 iter->index + 1 - prepend, elem) == NULL) { 906 iter->index + 1 - prepend, elem) == NULL) {
887 return 1; 907 return 1; // LCOV_EXCL_LINE
888 } 908 }
889 iter->elem_count++; 909 iter->elem_count++;
890 if (prepend != 0) { 910 if (prepend != 0) {
891 iter->index++; 911 iter->index++;
892 iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size; 912 iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size;
893 } 913 }
894 return 0; 914 return 0;
895 } else { 915 } else {
896 if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) { 916 if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) {
897 return 1; 917 return 1; // LCOV_EXCL_LINE
898 } 918 }
899 iter->elem_count++; 919 iter->elem_count++;
900 iter->index = list->collection.size; 920 iter->index = list->collection.size;
901 return 0; 921 return 0;
902 } 922 }
1127 iter->elem_handle = ((char *) list->data) 1147 iter->elem_handle = ((char *) list->data)
1128 + iter->index * list->base.collection.elem_size; 1148 + iter->index * list->base.collection.elem_size;
1129 } 1149 }
1130 } 1150 }
1131 1151
1152 static int cx_arl_change_capacity(
1153 struct cx_list_s *list,
1154 size_t new_capacity
1155 ) {
1156 cx_array_list *arl = (cx_array_list *)list;
1157 return cxReallocateArray(list->collection.allocator,
1158 &arl->data, new_capacity, list->collection.elem_size);
1159 }
1132 1160
1133 static struct cx_iterator_s cx_arl_iterator( 1161 static struct cx_iterator_s cx_arl_iterator(
1134 const struct cx_list_s *list, 1162 const struct cx_list_s *list,
1135 size_t index, 1163 size_t index,
1136 bool backwards 1164 bool backwards
1164 cx_arl_at, 1192 cx_arl_at,
1165 cx_arl_find_remove, 1193 cx_arl_find_remove,
1166 cx_arl_sort, 1194 cx_arl_sort,
1167 cx_arl_compare, 1195 cx_arl_compare,
1168 cx_arl_reverse, 1196 cx_arl_reverse,
1197 cx_arl_change_capacity,
1169 cx_arl_iterator, 1198 cx_arl_iterator,
1170 }; 1199 };
1171 1200
1172 CxList *cxArrayListCreate( 1201 CxList *cxArrayListCreate(
1173 const CxAllocator *allocator, 1202 const CxAllocator *allocator,

mercurial