ucx/array_list.c

changeset 23
b26390e77237
parent 22
112b85020dc9
equal deleted inserted replaced
22:112b85020dc9 23:b26390e77237
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
106 * Intelligently calculates a new capacity, reserving some more 108 * Intelligently calculates a new capacity, reserving some more
107 * elements than required to prevent too many allocations. 109 * elements than required to prevent too many allocations.
108 * 110 *
109 * @param current_capacity the current capacity of the array 111 * @param current_capacity the current capacity of the array
110 * @param needed_capacity the required capacity of the array 112 * @param needed_capacity the required capacity of the array
111 * @param maximum_capacity the maximum capacity (given by the data type)
112 * @return the new capacity 113 * @return the new capacity
113 */ 114 */
114 static size_t cx_array_grow_capacity( 115 static size_t cx_array_grow_capacity(
115 size_t current_capacity, 116 size_t current_capacity,
116 size_t needed_capacity, 117 size_t needed_capacity
117 size_t maximum_capacity
118 ) { 118 ) {
119 if (current_capacity >= needed_capacity) { 119 if (current_capacity >= needed_capacity) {
120 return current_capacity; 120 return current_capacity;
121 } 121 }
122 size_t cap = needed_capacity; 122 size_t cap = needed_capacity;
123 size_t alignment; 123 size_t alignment;
124 if (cap < 128) alignment = 16; 124 if (cap < 128) alignment = 16;
125 else if (cap < 1024) alignment = 64; 125 else if (cap < 1024) alignment = 64;
126 else if (cap < 8192) alignment = 512; 126 else if (cap < 8192) alignment = 512;
127 else alignment = 1024; 127 else alignment = 1024;
128 128 return cap - (cap % alignment) + alignment;
129 if (cap - 1 > maximum_capacity - alignment) {
130 return maximum_capacity;
131 } else {
132 return cap - (cap % alignment) + alignment;
133 }
134 } 129 }
135 130
136 int cx_array_reserve( 131 int cx_array_reserve(
137 void **array, 132 void **array,
138 void *size, 133 void *size,
286 // check if resize is required 281 // check if resize is required
287 const size_t minsize = index + elem_count; 282 const size_t minsize = index + elem_count;
288 const size_t newsize = oldsize < minsize ? minsize : oldsize; 283 const size_t newsize = oldsize < minsize ? minsize : oldsize;
289 284
290 // reallocate if necessary 285 // reallocate if necessary
291 const size_t newcap = cx_array_grow_capacity(oldcap, newsize, max_size); 286 const size_t newcap = cx_array_grow_capacity(oldcap, newsize);
292 if (newcap > oldcap) { 287 if (newcap > oldcap) {
293 // check if we need to repair the src pointer 288 // check if we need to repair the src pointer
294 uintptr_t targetaddr = (uintptr_t) *target; 289 uintptr_t targetaddr = (uintptr_t) *target;
295 uintptr_t srcaddr = (uintptr_t) src; 290 uintptr_t srcaddr = (uintptr_t) src;
296 bool repairsrc = targetaddr <= srcaddr 291 bool repairsrc = targetaddr <= srcaddr
370 365
371 // corner case 366 // corner case
372 if (elem_count == 0) return 0; 367 if (elem_count == 0) return 0;
373 368
374 // overflow check 369 // overflow check
370 // LCOV_EXCL_START
375 if (elem_count > SIZE_MAX - *size) { 371 if (elem_count > SIZE_MAX - *size) {
376 errno = EOVERFLOW; 372 errno = EOVERFLOW;
377 return 1; 373 return 1;
378 } 374 }
375 // LCOV_EXCL_STOP
379 376
380 // store some counts 377 // store some counts
381 const size_t old_size = *size; 378 const size_t old_size = *size;
382 const size_t old_capacity = *capacity; 379 const size_t old_capacity = *capacity;
383 // the necessary capacity is the worst case assumption, including duplicates 380 // the necessary capacity is the worst case assumption, including duplicates
384 const size_t needed_capacity = cx_array_grow_capacity(old_capacity, 381 const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + elem_count);
385 old_size + elem_count, SIZE_MAX);
386 382
387 // if we need more than we have, try a reallocation 383 // if we need more than we have, try a reallocation
388 if (needed_capacity > old_capacity) { 384 if (needed_capacity > old_capacity) {
389 void *new_mem = reallocator->realloc( 385 void *new_mem = reallocator->realloc(
390 *target, old_capacity, needed_capacity, elem_size, reallocator 386 *target, old_capacity, needed_capacity, elem_size, reallocator
419 memmove(bptr, dest, buf_size * elem_size); 415 memmove(bptr, dest, buf_size * elem_size);
420 416
421 // while there are both source and buffered elements left, 417 // while there are both source and buffered elements left,
422 // copy them interleaving 418 // copy them interleaving
423 while (si < elem_count && bi < new_size) { 419 while (si < elem_count && bi < new_size) {
424 // 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.
425 size_t copy_len, bytes_copied; 432 size_t copy_len, bytes_copied;
426 copy_len = cx_array_binary_search_sup( 433 copy_len = cx_array_binary_search_inf(
427 src, 434 src, elem_count - si, elem_size, bptr, cmp_func
428 elem_count - si,
429 elem_size,
430 bptr,
431 cmp_func
432 ); 435 );
433 // binary search gives us the smallest index; 436 copy_len++;
434 // we also want to include equal elements here
435 while (si + copy_len < elem_count
436 && cmp_func(bptr, src+copy_len*elem_size) == 0) {
437 copy_len++;
438 }
439 437
440 // copy the source elements 438 // copy the source elements
441 if (copy_len > 0) { 439 if (copy_len > 0) {
442 if (allow_duplicates) { 440 if (allow_duplicates) {
443 // we can copy the entire chunk 441 // we can copy the entire chunk
510 if (si < elem_count) { 508 if (si < elem_count) {
511 if (allow_duplicates) { 509 if (allow_duplicates) {
512 // duplicates allowed or nothing inserted yet: simply copy everything 510 // duplicates allowed or nothing inserted yet: simply copy everything
513 memcpy(dest, src, elem_size * (elem_count - si)); 511 memcpy(dest, src, elem_size * (elem_count - si));
514 } else { 512 } else {
515 if (dest != *target) { 513 // we must check the remaining source elements one by one
516 // skip all source elements that equal the last element 514 // to skip the duplicates.
517 char *last = dest - elem_size; 515 // Note that no source element can equal the last element in the
518 while (si < elem_count) { 516 // destination, because that would have created an insertion point
519 if (last != NULL && cmp_func(last, src) == 0) { 517 // and a buffer, s.t. the above loop already handled the duplicates
520 src += elem_size;
521 si++;
522 (*size)--;
523 } else {
524 break;
525 }
526 }
527 }
528 // we must check the elements in the chunk one by one
529 while (si < elem_count) { 518 while (si < elem_count) {
530 // find a chain of elements that can be copied 519 // find a chain of elements that can be copied
531 size_t copy_len = 1, skip_len = 0; 520 size_t copy_len = 1, skip_len = 0;
532 { 521 {
533 const char *left_src = src; 522 const char *left_src = src;
534 while (si + copy_len < elem_count) { 523 while (si + copy_len + skip_len < elem_count) {
535 const char *right_src = left_src + elem_size; 524 const char *right_src = left_src + elem_size;
536 int d = cmp_func(left_src, right_src); 525 int d = cmp_func(left_src, right_src);
537 if (d < 0) { 526 if (d < 0) {
538 if (skip_len > 0) { 527 if (skip_len > 0) {
539 // new larger element found; 528 // new larger element found;
597 ) { 586 ) {
598 return cx_array_insert_sorted_impl(target, size, capacity, 587 return cx_array_insert_sorted_impl(target, size, capacity,
599 cmp_func, sorted_data, elem_size, elem_count, reallocator, false); 588 cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
600 } 589 }
601 590
602 size_t cx_array_binary_search_inf( 591 // implementation that finds ANY index
592 static size_t cx_array_binary_search_inf_impl(
603 const void *arr, 593 const void *arr,
604 size_t size, 594 size_t size,
605 size_t elem_size, 595 size_t elem_size,
606 const void *elem, 596 const void *elem,
607 cx_compare_func cmp_func 597 cx_compare_func cmp_func
642 pivot_index = left_index + (right_index - left_index) / 2; 632 pivot_index = left_index + (right_index - left_index) / 2;
643 const char *arr_elem = array + pivot_index * elem_size; 633 const char *arr_elem = array + pivot_index * elem_size;
644 result = cmp_func(elem, arr_elem); 634 result = cmp_func(elem, arr_elem);
645 if (result == 0) { 635 if (result == 0) {
646 // found it! 636 // found it!
647 // check previous elements;
648 // when they are equal, report the smallest index
649 arr_elem -= elem_size;
650 while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
651 pivot_index--;
652 arr_elem -= elem_size;
653 }
654 return pivot_index; 637 return pivot_index;
655 } else if (result < 0) { 638 } else if (result < 0) {
656 // element is smaller than pivot, continue search left 639 // element is smaller than pivot, continue search left
657 right_index = pivot_index - 1; 640 right_index = pivot_index - 1;
658 } else { 641 } else {
661 } 644 }
662 } 645 }
663 646
664 // report the largest upper bound 647 // report the largest upper bound
665 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;
666 } 667 }
667 668
668 size_t cx_array_binary_search( 669 size_t cx_array_binary_search(
669 const void *arr, 670 const void *arr,
670 size_t size, 671 size_t size,
688 size_t size, 689 size_t size,
689 size_t elem_size, 690 size_t elem_size,
690 const void *elem, 691 const void *elem,
691 cx_compare_func cmp_func 692 cx_compare_func cmp_func
692 ) { 693 ) {
693 size_t inf = cx_array_binary_search_inf( 694 size_t index = cx_array_binary_search_inf_impl(
694 arr, size, elem_size, elem, cmp_func 695 arr, size, elem_size, elem, cmp_func
695 ); 696 );
696 if (inf == size) { 697 const char *e = ((const char *) arr) + index * elem_size;
697 // no infimum means, first element is supremum 698 if (index == size) {
699 // no infimum means the first element is supremum
698 return 0; 700 return 0;
699 } else if (cmp_func(((const char *) arr) + inf * elem_size, elem) == 0) { 701 } else if (cmp_func(e, elem) == 0) {
700 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;
701 } else { 709 } else {
702 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;
703 } 713 }
704 } 714 }
705 715
706 #ifndef CX_ARRAY_SWAP_SBO_SIZE 716 #ifndef CX_ARRAY_SWAP_SBO_SIZE
707 #define CX_ARRAY_SWAP_SBO_SIZE 128 717 #define CX_ARRAY_SWAP_SBO_SIZE 128
790 // get a correctly typed pointer to the list 800 // get a correctly typed pointer to the list
791 cx_array_list *arl = (cx_array_list *) list; 801 cx_array_list *arl = (cx_array_list *) list;
792 802
793 // guarantee enough capacity 803 // guarantee enough capacity
794 if (arl->capacity < list->collection.size + n) { 804 if (arl->capacity < list->collection.size + n) {
795 const size_t new_capacity = cx_array_grow_capacity(arl->capacity, 805 const size_t new_capacity = cx_array_grow_capacity(arl->capacity,list->collection.size + n);
796 list->collection.size + n, SIZE_MAX);
797 if (cxReallocateArray( 806 if (cxReallocateArray(
798 list->collection.allocator, 807 list->collection.allocator,
799 &arl->data, new_capacity, 808 &arl->data, new_capacity,
800 list->collection.elem_size) 809 list->collection.elem_size)
801 ) { 810 ) {
802 return 0; 811 return 0; // LCOV_EXCL_LINE
803 } 812 }
804 arl->capacity = new_capacity; 813 arl->capacity = new_capacity;
805 } 814 }
806 815
807 // determine insert position 816 // determine insert position
841 list->collection.elem_size, 850 list->collection.elem_size,
842 n, 851 n,
843 &arl->reallocator 852 &arl->reallocator
844 )) { 853 )) {
845 // array list implementation is "all or nothing" 854 // array list implementation is "all or nothing"
846 return 0; 855 return 0; // LCOV_EXCL_LINE
847 } else { 856 } else {
848 return n; 857 return n;
849 } 858 }
850 } 859 }
851 860
866 list->collection.elem_size, 875 list->collection.elem_size,
867 n, 876 n,
868 &arl->reallocator 877 &arl->reallocator
869 )) { 878 )) {
870 // array list implementation is "all or nothing" 879 // array list implementation is "all or nothing"
871 return 0; 880 return 0; // LCOV_EXCL_LINE
872 } else { 881 } else {
873 return n; 882 return n;
874 } 883 }
875 } 884 }
876 885
893 ) { 902 ) {
894 struct cx_list_s *list = iter->src_handle; 903 struct cx_list_s *list = iter->src_handle;
895 if (iter->index < list->collection.size) { 904 if (iter->index < list->collection.size) {
896 if (cx_arl_insert_element(list, 905 if (cx_arl_insert_element(list,
897 iter->index + 1 - prepend, elem) == NULL) { 906 iter->index + 1 - prepend, elem) == NULL) {
898 return 1; 907 return 1; // LCOV_EXCL_LINE
899 } 908 }
900 iter->elem_count++; 909 iter->elem_count++;
901 if (prepend != 0) { 910 if (prepend != 0) {
902 iter->index++; 911 iter->index++;
903 iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size; 912 iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size;
904 } 913 }
905 return 0; 914 return 0;
906 } else { 915 } else {
907 if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) { 916 if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) {
908 return 1; 917 return 1; // LCOV_EXCL_LINE
909 } 918 }
910 iter->elem_count++; 919 iter->elem_count++;
911 iter->index = list->collection.size; 920 iter->index = list->collection.size;
912 return 0; 921 return 0;
913 } 922 }

mercurial