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