src/array_list.c

changeset 1419
e46406fd1b3c
parent 1387
9bdd053820b7
--- a/src/array_list.c	Fri Oct 10 12:26:43 2025 +0200
+++ b/src/array_list.c	Fri Oct 10 17:24:19 2025 +0200
@@ -335,7 +335,7 @@
     return 0;
 }
 
-int cx_array_insert_sorted(
+static int cx_array_insert_sorted_impl(
         void **target,
         size_t *size,
         size_t *capacity,
@@ -343,7 +343,8 @@
         const void *sorted_data,
         size_t elem_size,
         size_t elem_count,
-        CxArrayReallocator *reallocator
+        CxArrayReallocator *reallocator,
+        bool allow_duplicates
 ) {
     // assert pointers
     assert(target != NULL);
@@ -369,6 +370,7 @@
     // store some counts
     size_t old_size = *size;
     size_t old_capacity = *capacity;
+    // the necessary capacity is the worst case assumption, including duplicates
     size_t needed_capacity = old_size + elem_count;
 
     // if we need more than we have, try a reallocation
@@ -418,6 +420,16 @@
                 bptr,
                 cmp_func
         );
+        // handle the identical elements
+        size_t skip_len = 0;
+        while (copy_len < elem_count - si
+                && cmp_func(bptr, src+copy_len*elem_size) == 0) {
+            copy_len++;
+            if (!allow_duplicates) {
+                skip_len++;
+            }
+        }
+        copy_len -= skip_len;
 
         // copy the source elements
         bytes_copied = copy_len * elem_size;
@@ -425,6 +437,12 @@
         dest += bytes_copied;
         src += bytes_copied;
         si += copy_len;
+        di += copy_len;
+
+        // skip duplicates when they are not allowed
+        src += skip_len * elem_size;
+        si += skip_len;
+        *size -= skip_len;
 
         // when all source elements are in place, we are done
         if (si >= elem_count) break;
@@ -443,7 +461,18 @@
         memmove(dest, bptr, bytes_copied);
         dest += bytes_copied;
         bptr += bytes_copied;
+        di += copy_len;
         bi += copy_len;
+
+        // check if the last restored element is a duplicate of the next source
+        if (!allow_duplicates && copy_len > 0) {
+            char *last = dest - elem_size;
+            while (si < elem_count && cmp_func(last, src) == 0) {
+                src += elem_size;
+                si++;
+                (*size)--;
+            }
+        }
     }
 
     // still source elements left? simply append them
@@ -451,12 +480,44 @@
         memcpy(dest, src, elem_size * (elem_count - si));
     }
 
-    // still buffer elements left?
-    // don't worry, we already moved them to the correct place
+    // buffered elements need to be moved when we skipped duplicates
+    size_t total_skipped = new_size - *size;
+    if (bi < new_size && total_skipped > 0) {
+        // move the remaining buffer to the end of the array
+        memmove(dest, bptr, elem_size * (new_size - bi));
+    }
 
     return 0;
 }
 
+int 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,
+        CxArrayReallocator *reallocator
+) {
+    return cx_array_insert_sorted_impl(target, size, capacity,
+        cmp_func, sorted_data, elem_size, elem_count, reallocator, true);
+}
+
+int cx_array_insert_unique(
+        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,
+        CxArrayReallocator *reallocator
+) {
+    return cx_array_insert_sorted_impl(target, size, capacity,
+        cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
+}
+
 size_t cx_array_binary_search_inf(
         const void *arr,
         size_t size,
@@ -502,6 +563,13 @@
         result = cmp_func(elem, arr_elem);
         if (result == 0) {
             // found it!
+            // check previous elements;
+            // when they are equal, report the smallest index
+            arr_elem -= elem_size;
+            while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
+                pivot_index--;
+                arr_elem -= elem_size;
+            }
             return pivot_index;
         } else if (result < 0) {
             // element is smaller than pivot, continue search left
@@ -700,6 +768,31 @@
     }
 }
 
+static size_t cx_arl_insert_unique(
+        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_insert_unique(
+            &arl->data,
+            &list->collection.size,
+            &arl->capacity,
+            list->collection.cmpfunc,
+            sorted_data,
+            list->collection.elem_size,
+            n,
+            &arl->reallocator
+    )) {
+        // array list implementation is "all or nothing"
+        return 0;
+    } else {
+        return n;
+    }
+}
+
 static void *cx_arl_insert_element(
         struct cx_list_s *list,
         size_t index,
@@ -993,6 +1086,7 @@
         cx_arl_insert_element,
         cx_arl_insert_array,
         cx_arl_insert_sorted,
+        cx_arl_insert_unique,
         cx_arl_insert_iter,
         cx_arl_remove,
         cx_arl_clear,

mercurial