--- a/ucx/array_list.c Thu Oct 03 18:54:19 2024 +0200 +++ b/ucx/array_list.c Sun Oct 06 12:00:31 2024 +0200 @@ -55,7 +55,7 @@ size_t *size, size_t *capacity, size_t index, - void const *src, + const void *src, size_t elem_size, size_t elem_count, struct cx_array_reallocator_s *reallocator @@ -120,6 +120,173 @@ return CX_ARRAY_SUCCESS; } +enum cx_array_result cx_array_insert_sorted( + void **target, + size_t *size, + size_t *capacity, + cx_compare_func cmp_func, + const void *sorted_data, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +) { + // assert pointers + assert(target != NULL); + assert(size != NULL); + assert(capacity != NULL); + assert(cmp_func != NULL); + assert(sorted_data != NULL); + assert(reallocator != NULL); + + // corner case + if (elem_count == 0) return 0; + + // store some counts + size_t old_size = *size; + size_t needed_capacity = old_size + elem_count; + + // if we need more than we have, try a reallocation + if (needed_capacity > *capacity) { + size_t new_capacity = needed_capacity - (needed_capacity % 16) + 16; + void *new_mem = reallocator->realloc( + *target, new_capacity, elem_size, reallocator + ); + if (new_mem == NULL) { + // give it up right away, there is no contract + // that requires us to insert as much as we can + return CX_ARRAY_REALLOC_FAILED; + } + *target = new_mem; + *capacity = new_capacity; + } + + // now we have guaranteed that we can insert everything + size_t new_size = old_size + elem_count; + *size = new_size; + + // declare the source and destination indices/pointers + size_t si = 0, di = 0; + const char *src = sorted_data; + char *dest = *target; + + // find the first insertion point + di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func); + dest += di * elem_size; + + // move the remaining elements in the array completely to the right + // we will call it the "buffer" for parked elements + size_t buf_size = old_size - di; + size_t bi = new_size - buf_size; + char *bptr = ((char *) *target) + bi * elem_size; + memmove(bptr, dest, buf_size * elem_size); + + // while there are both source and buffered elements left, + // copy them interleaving + while (si < elem_count && bi < new_size) { + // determine how many source elements can be inserted + size_t copy_len, bytes_copied; + copy_len = cx_array_binary_search_sup( + src, + elem_count - si, + elem_size, + bptr, + cmp_func + ); + + // copy the source elements + bytes_copied = copy_len * elem_size; + memcpy(dest, src, bytes_copied); + dest += bytes_copied; + src += bytes_copied; + si += copy_len; + + // when all source elements are in place, we are done + if (si >= elem_count) break; + + // determine how many buffered elements need to be restored + copy_len = cx_array_binary_search_sup( + bptr, + new_size - bi, + elem_size, + src, + cmp_func + ); + + // restore the buffered elements + bytes_copied = copy_len * elem_size; + memmove(dest, bptr, bytes_copied); + dest += bytes_copied; + bptr += bytes_copied; + bi += copy_len; + } + + // still source elements left? simply append them + if (si < elem_count) { + memcpy(dest, src, elem_size * (elem_count - si)); + } + + // still buffer elements left? + // don't worry, we already moved them to the correct place + + return CX_ARRAY_SUCCESS; +} + +size_t cx_array_binary_search_inf( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func cmp_func +) { + // special case: empty array + if (size == 0) return 0; + + // declare a variable that will contain the compare results + int result; + + // cast the array pointer to something we can use offsets with + const char *array = arr; + + // check the first array element + result = cmp_func(elem, array); + if (result < 0) { + return size; + } else if (result == 0) { + return 0; + } + + // check the last array element + result = cmp_func(elem, array + elem_size * (size - 1)); + if (result >= 0) { + return size - 1; + } + + // the element is now guaranteed to be somewhere in the list + // so start the binary search + size_t left_index = 1; + size_t right_index = size - 1; + size_t pivot_index; + + while (left_index <= right_index) { + pivot_index = left_index + (right_index - left_index) / 2; + const char *arr_elem = array + pivot_index * elem_size; + result = cmp_func(elem, arr_elem); + if (result == 0) { + // found it! + return pivot_index; + } else if (result < 0) { + // element is smaller than pivot, continue search left + right_index = pivot_index - 1; + } else { + // element is larger than pivot, continue search right + left_index = pivot_index + 1; + } + } + + // report the largest upper bound + return result < 0 ? (pivot_index - 1) : pivot_index; +} + #ifndef CX_ARRAY_SWAP_SBO_SIZE #define CX_ARRAY_SWAP_SBO_SIZE 128 #endif @@ -180,7 +347,7 @@ struct cx_array_reallocator_s *alloc ) { // retrieve the pointer to the list allocator - CxAllocator const *al = alloc->ptr1; + const CxAllocator *al = alloc->ptr1; // use the list allocator to reallocate the memory return cxRealloc(al, array, capacity * elem_size); @@ -191,49 +358,49 @@ char *ptr = arl->data; - if (list->simple_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->collection.simple_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { cx_invoke_simple_destructor(list, ptr); - ptr += list->item_size; + ptr += list->collection.elem_size; } } - if (list->advanced_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->collection.advanced_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { cx_invoke_advanced_destructor(list, ptr); - ptr += list->item_size; + ptr += list->collection.elem_size; } } - cxFree(list->allocator, arl->data); - cxFree(list->allocator, list); + cxFree(list->collection.allocator, arl->data); + cxFree(list->collection.allocator, list); } static size_t cx_arl_insert_array( struct cx_list_s *list, size_t index, - void const *array, + const void *array, size_t n ) { // out of bounds and special case check - if (index > list->size || n == 0) return 0; + if (index > list->collection.size || n == 0) return 0; // get a correctly typed pointer to the list cx_array_list *arl = (cx_array_list *) list; // do we need to move some elements? - if (index < list->size) { - char const *first_to_move = (char const *) arl->data; - first_to_move += index * list->item_size; - size_t elems_to_move = list->size - index; + if (index < list->collection.size) { + const char *first_to_move = (const char *) arl->data; + first_to_move += index * list->collection.elem_size; + size_t elems_to_move = list->collection.size - index; size_t start_of_moved = index + n; if (CX_ARRAY_SUCCESS != cx_array_copy( &arl->data, - &list->size, + &list->collection.size, &arl->capacity, start_of_moved, first_to_move, - list->item_size, + list->collection.elem_size, elems_to_move, &arl->reallocator )) { @@ -249,11 +416,36 @@ // place the new elements if (CX_ARRAY_SUCCESS == cx_array_copy( &arl->data, - &list->size, + &list->collection.size, &arl->capacity, index, array, - list->item_size, + list->collection.elem_size, + n, + &arl->reallocator + )) { + return n; + } else { + // array list implementation is "all or nothing" + return 0; + } +} + +static size_t cx_arl_insert_sorted( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + // get a correctly typed pointer to the list + cx_array_list *arl = (cx_array_list *) list; + + if (CX_ARRAY_SUCCESS == cx_array_insert_sorted( + &arl->data, + &list->collection.size, + &arl->capacity, + list->collection.cmpfunc, + sorted_data, + list->collection.elem_size, n, &arl->reallocator )) { @@ -267,31 +459,37 @@ static int cx_arl_insert_element( struct cx_list_s *list, size_t index, - void const *element + const void *element ) { return 1 != cx_arl_insert_array(list, index, element, 1); } static int cx_arl_insert_iter( - struct cx_mut_iterator_s *iter, - void const *elem, + struct cx_iterator_s *iter, + const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle; - if (iter->index < list->size) { + struct cx_list_s *list = iter->src_handle.m; + if (iter->index < list->collection.size) { int result = cx_arl_insert_element( list, iter->index + 1 - prepend, elem ); - if (result == 0 && prepend != 0) { - iter->index++; - iter->elem_handle = ((char *) iter->elem_handle) + list->item_size; + if (result == 0) { + iter->elem_count++; + if (prepend != 0) { + iter->index++; + iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size; + } } return result; } else { - int result = cx_arl_insert_element(list, list->size, elem); - iter->index = list->size; + int result = cx_arl_insert_element(list, list->collection.size, elem); + if (result == 0) { + iter->elem_count++; + iter->index = list->collection.size; + } return result; } } @@ -303,28 +501,28 @@ cx_array_list *arl = (cx_array_list *) list; // out-of-bounds check - if (index >= list->size) { + if (index >= list->collection.size) { return 1; } // content destruction - cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size); + cx_invoke_destructor(list, ((char *) arl->data) + index * list->collection.elem_size); // short-circuit removal of last element - if (index == list->size - 1) { - list->size--; + if (index == list->collection.size - 1) { + list->collection.size--; return 0; } // just move the elements starting at index to the left int result = cx_array_copy( &arl->data, - &list->size, + &list->collection.size, &arl->capacity, index, - ((char *) arl->data) + (index + 1) * list->item_size, - list->item_size, - list->size - index - 1, + ((char *) arl->data) + (index + 1) * list->collection.elem_size, + list->collection.elem_size, + list->collection.size - index - 1, &arl->reallocator ); @@ -332,32 +530,32 @@ assert(result == 0); // decrease the size - list->size--; + list->collection.size--; return 0; } static void cx_arl_clear(struct cx_list_s *list) { - if (list->size == 0) return; + if (list->collection.size == 0) return; cx_array_list *arl = (cx_array_list *) list; char *ptr = arl->data; - if (list->simple_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->collection.simple_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { cx_invoke_simple_destructor(list, ptr); - ptr += list->item_size; + ptr += list->collection.elem_size; } } - if (list->advanced_destructor) { - for (size_t i = 0; i < list->size; i++) { + if (list->collection.advanced_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { cx_invoke_advanced_destructor(list, ptr); - ptr += list->item_size; + ptr += list->collection.elem_size; } } - memset(arl->data, 0, list->size * list->item_size); - list->size = 0; + memset(arl->data, 0, list->collection.size * list->collection.elem_size); + list->collection.size = 0; } static int cx_arl_swap( @@ -365,20 +563,20 @@ size_t i, size_t j ) { - if (i >= list->size || j >= list->size) return 1; + if (i >= list->collection.size || j >= list->collection.size) return 1; cx_array_list *arl = (cx_array_list *) list; - cx_array_swap(arl->data, list->item_size, i, j); + cx_array_swap(arl->data, list->collection.elem_size, i, j); return 0; } static void *cx_arl_at( - struct cx_list_s const *list, + const struct cx_list_s *list, size_t index ) { - if (index < list->size) { - cx_array_list const *arl = (cx_array_list const *) list; + if (index < list->collection.size) { + const cx_array_list *arl = (const cx_array_list *) list; char *space = arl->data; - return space + index * list->item_size; + return space + index * list->collection.elem_size; } else { return NULL; } @@ -386,15 +584,15 @@ static ssize_t cx_arl_find_remove( struct cx_list_s *list, - void const *elem, + const void *elem, bool remove ) { - assert(list->cmpfunc != NULL); - assert(list->size < SIZE_MAX / 2); - char *cur = ((cx_array_list const *) list)->data; + assert(list->collection.cmpfunc != NULL); + assert(list->collection.size < SIZE_MAX / 2); + char *cur = ((const cx_array_list *) list)->data; - for (ssize_t i = 0; i < (ssize_t) list->size; i++) { - if (0 == list->cmpfunc(elem, cur)) { + for (ssize_t i = 0; i < (ssize_t) list->collection.size; i++) { + if (0 == list->collection.cmpfunc(elem, cur)) { if (remove) { if (0 == cx_arl_remove(list, i)) { return i; @@ -405,117 +603,106 @@ return i; } } - cur += list->item_size; + cur += list->collection.elem_size; } return -1; } static void cx_arl_sort(struct cx_list_s *list) { - assert(list->cmpfunc != NULL); + assert(list->collection.cmpfunc != NULL); qsort(((cx_array_list *) list)->data, - list->size, - list->item_size, - list->cmpfunc + list->collection.size, + list->collection.elem_size, + list->collection.cmpfunc ); } static int cx_arl_compare( - struct cx_list_s const *list, - struct cx_list_s const *other + const struct cx_list_s *list, + const struct cx_list_s *other ) { - assert(list->cmpfunc != NULL); - if (list->size == other->size) { - char const *left = ((cx_array_list const *) list)->data; - char const *right = ((cx_array_list const *) other)->data; - for (size_t i = 0; i < list->size; i++) { - int d = list->cmpfunc(left, right); + assert(list->collection.cmpfunc != NULL); + if (list->collection.size == other->collection.size) { + const char *left = ((const cx_array_list *) list)->data; + const char *right = ((const cx_array_list *) other)->data; + for (size_t i = 0; i < list->collection.size; i++) { + int d = list->collection.cmpfunc(left, right); if (d != 0) { return d; } - left += list->item_size; - right += other->item_size; + left += list->collection.elem_size; + right += other->collection.elem_size; } return 0; } else { - return list->size < other->size ? -1 : 1; + return list->collection.size < other->collection.size ? -1 : 1; } } static void cx_arl_reverse(struct cx_list_s *list) { - if (list->size < 2) return; - void *data = ((cx_array_list const *) list)->data; - size_t half = list->size / 2; + if (list->collection.size < 2) return; + void *data = ((const cx_array_list *) list)->data; + size_t half = list->collection.size / 2; for (size_t i = 0; i < half; i++) { - cx_array_swap(data, list->item_size, i, list->size - 1 - i); + cx_array_swap(data, list->collection.elem_size, i, list->collection.size - 1 - i); } } -static bool cx_arl_iter_valid(void const *it) { - struct cx_iterator_s const *iter = it; - struct cx_list_s const *list = iter->src_handle; - return iter->index < list->size; +static bool cx_arl_iter_valid(const void *it) { + const struct cx_iterator_s *iter = it; + const struct cx_list_s *list = iter->src_handle.c; + return iter->index < list->collection.size; } -static void *cx_arl_iter_current(void const *it) { - struct cx_iterator_s const *iter = it; +static void *cx_arl_iter_current(const void *it) { + const struct cx_iterator_s *iter = it; return iter->elem_handle; } static void cx_arl_iter_next(void *it) { - struct cx_iterator_base_s *itbase = it; - if (itbase->remove) { - struct cx_mut_iterator_s *iter = it; - itbase->remove = false; - cx_arl_remove(iter->src_handle, iter->index); + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + iter->base.remove = false; + cx_arl_remove(iter->src_handle.m, iter->index); } else { - struct cx_iterator_s *iter = it; iter->index++; iter->elem_handle = ((char *) iter->elem_handle) - + ((struct cx_list_s const *) iter->src_handle)->item_size; + + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size; } } static void cx_arl_iter_prev(void *it) { - struct cx_iterator_base_s *itbase = it; - struct cx_mut_iterator_s *iter = it; - cx_array_list *const list = iter->src_handle; - if (itbase->remove) { - itbase->remove = false; - cx_arl_remove(iter->src_handle, iter->index); + struct cx_iterator_s *iter = it; + const cx_array_list *list = iter->src_handle.c; + if (iter->base.remove) { + iter->base.remove = false; + cx_arl_remove(iter->src_handle.m, iter->index); } iter->index--; - if (iter->index < list->base.size) { + if (iter->index < list->base.collection.size) { iter->elem_handle = ((char *) list->data) - + iter->index * list->base.item_size; + + iter->index * list->base.collection.elem_size; } } -static bool cx_arl_iter_flag_rm(void *it) { - struct cx_iterator_base_s *iter = it; - if (iter->mutating) { - iter->remove = true; - return true; - } else { - return false; - } -} static struct cx_iterator_s cx_arl_iterator( - struct cx_list_s const *list, + const struct cx_list_s *list, size_t index, bool backwards ) { struct cx_iterator_s iter; iter.index = index; - iter.src_handle = list; + iter.src_handle.c = list; iter.elem_handle = cx_arl_at(list, index); + iter.elem_size = list->collection.elem_size; + iter.elem_count = list->collection.size; iter.base.valid = cx_arl_iter_valid; iter.base.current = cx_arl_iter_current; iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; - iter.base.flag_removal = cx_arl_iter_flag_rm; iter.base.remove = false; iter.base.mutating = false; @@ -526,6 +713,7 @@ cx_arl_destructor, cx_arl_insert_element, cx_arl_insert_array, + cx_arl_insert_sorted, cx_arl_insert_iter, cx_arl_remove, cx_arl_clear, @@ -539,9 +727,9 @@ }; CxList *cxArrayListCreate( - CxAllocator const *allocator, + const CxAllocator *allocator, cx_compare_func comparator, - size_t item_size, + size_t elem_size, size_t initial_capacity ) { if (allocator == NULL) { @@ -552,20 +740,20 @@ if (list == NULL) return NULL; list->base.cl = &cx_array_list_class; - list->base.allocator = allocator; + list->base.collection.allocator = allocator; list->capacity = initial_capacity; - if (item_size > 0) { - list->base.item_size = item_size; - list->base.cmpfunc = comparator; + if (elem_size > 0) { + list->base.collection.elem_size = elem_size; + list->base.collection.cmpfunc = comparator; } else { - item_size = sizeof(void *); - list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; + elem_size = sizeof(void *); + list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; cxListStorePointers((CxList *) list); } - // allocate the array after the real item_size is known - list->data = cxCalloc(allocator, initial_capacity, item_size); + // allocate the array after the real elem_size is known + list->data = cxCalloc(allocator, initial_capacity, elem_size); if (list->data == NULL) { cxFree(allocator, list); return NULL;