src/linked_list.c

changeset 1419
e46406fd1b3c
parent 1387
9bdd053820b7
--- a/src/linked_list.c	Fri Oct 10 12:26:43 2025 +0200
+++ b/src/linked_list.c	Fri Oct 10 17:24:19 2025 +0200
@@ -30,6 +30,7 @@
 #include "cx/compare.h"
 #include <string.h>
 #include <assert.h>
+#include <unistd.h>
 
 // LOW LEVEL LINKED LIST FUNCTIONS
 
@@ -244,19 +245,22 @@
             begin, end, loc_prev, loc_next, new_node, cmp_func);
 }
 
-void cx_linked_list_insert_sorted_chain(
+static void *cx_linked_list_insert_sorted_chain_impl(
         void **begin,
         void **end,
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next,
         void *insert_begin,
-        cx_compare_func cmp_func
+        cx_compare_func cmp_func,
+        bool allow_duplicates
 ) {
     assert(begin != NULL);
     assert(loc_next >= 0);
     assert(insert_begin != NULL);
 
     // track currently observed nodes
+    void *dup_start = NULL;
+    void *dup_last = NULL;
     void *dest_prev = NULL;
     void *dest = *begin;
     void *src = insert_begin;
@@ -267,17 +271,38 @@
         if (end != NULL) {
             *end = cx_linked_list_last(src, loc_next);
         }
-        return;
+        return NULL;
     }
 
     // search the list for insertion points
     while (dest != NULL && src != NULL) {
         // compare current list node with source node
         // if less or equal, skip
-        if (cmp_func(dest, src) <= 0) {
-            dest_prev = dest;
-            dest = ll_next(dest);
-            continue;
+        {
+            int d = cmp_func(dest, src);
+            if (d <= 0) {
+                if (!allow_duplicates && d == 0) {
+                    if (dup_last == NULL) {
+                        dup_start = src;
+                        dup_last = src;
+                        if (loc_prev >= 0) {
+                            ll_prev(dup_start) = NULL;
+                        }
+                    } else {
+                        cx_linked_list_link(dup_last, src, loc_prev, loc_next);
+                        dup_last = src;
+                    }
+                    src = ll_next(src);
+                    while (src != NULL && cmp_func(dest, src) == 0) {
+                        dup_last = src;
+                        src = ll_next(src);
+                    }
+                }
+
+                dest_prev = dest;
+                dest = ll_next(dest);
+                continue;
+            }
         }
 
         // determine chain of elements that can be inserted
@@ -308,6 +333,27 @@
         dest = ll_next(dest);
     }
 
+    // skip duplicates in the remaining items
+    if (!allow_duplicates && src != NULL) {
+        if (cmp_func(dest_prev, src) == 0) {
+            if (dup_last == NULL) {
+                dup_start = src;
+                dup_last = src;
+                if (loc_prev >= 0) {
+                    ll_prev(dup_start) = NULL;
+                }
+            } else {
+                cx_linked_list_link(dup_last, src, loc_prev, loc_next);
+                dup_last = src;
+            }
+            src = ll_next(src);
+            while (src != NULL && cmp_func(dest_prev, src) == 0) {
+                dup_last = src;
+                src = ll_next(src);
+            }
+        }
+    }
+
     // insert remaining items
     if (src != NULL) {
         cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
@@ -318,6 +364,51 @@
         *end = cx_linked_list_last(
                 dest != NULL ? dest : dest_prev, loc_next);
     }
+
+    if (dup_last != NULL) {
+        ll_next(dup_last) = NULL;
+    }
+    return dup_start;
+}
+
+void cx_linked_list_insert_sorted_chain(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *insert_begin,
+        cx_compare_func cmp_func
+) {
+    void *ret = cx_linked_list_insert_sorted_chain_impl(
+            begin, end, loc_prev, loc_next,
+            insert_begin, cmp_func, true);
+    assert(ret == NULL);
+}
+
+int cx_linked_list_insert_unique(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node,
+        cx_compare_func cmp_func
+) {
+    assert(ll_next(new_node) == NULL);
+    return NULL != cx_linked_list_insert_unique_chain(
+            begin, end, loc_prev, loc_next, new_node, cmp_func);
+}
+
+void *cx_linked_list_insert_unique_chain(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *insert_begin,
+        cx_compare_func cmp_func
+) {
+    return cx_linked_list_insert_sorted_chain_impl(
+            begin, end, loc_prev, loc_next,
+            insert_begin, cmp_func, false);
 }
 
 size_t cx_linked_list_remove_chain(
@@ -677,10 +768,11 @@
     return cx_ll_insert_sorted_cmp_func(left, right);
 }
 
-static size_t cx_ll_insert_sorted(
+static size_t cx_ll_insert_sorted_impl(
         struct cx_list_s *list,
         const void *array,
-        size_t n
+        size_t n,
+        bool allow_duplicates
 ) {
     cx_linked_list *ll = (cx_linked_list *) list;
 
@@ -711,21 +803,54 @@
     // invoke the low level function
     cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
     cx_ll_insert_sorted_loc_data = ll->loc_data;
-    cx_linked_list_insert_sorted_chain(
-            &ll->begin,
-            &ll->end,
-            ll->loc_prev,
-            ll->loc_next,
-            chain,
-            cx_ll_insert_sorted_cmp_helper
-    );
-
-    // adjust the list metadata
-    list->collection.size += inserted;
+    if (allow_duplicates) {
+        cx_linked_list_insert_sorted_chain(
+                &ll->begin,
+                &ll->end,
+                ll->loc_prev,
+                ll->loc_next,
+                chain,
+                cx_ll_insert_sorted_cmp_helper
+        );
+        list->collection.size += inserted;
+    } else {
+        void *duplicates = cx_linked_list_insert_unique_chain(
+                &ll->begin,
+                &ll->end,
+                ll->loc_prev,
+                ll->loc_next,
+                chain,
+                cx_ll_insert_sorted_cmp_helper
+        );
+        list->collection.size += inserted;
+        // free the nodes that did not make it into the list
+        while (duplicates != NULL) {
+            void *next = CX_LL_PTR(duplicates, ll->loc_next);
+            cxFree(list->collection.allocator, duplicates);
+            duplicates = next;
+            list->collection.size--;
+        }
+    }
 
     return inserted;
 }
 
+static size_t cx_ll_insert_sorted(
+        struct cx_list_s *list,
+        const void *array,
+        size_t n
+) {
+    return cx_ll_insert_sorted_impl(list, array, n, true);
+}
+
+static size_t cx_ll_insert_unique(
+        struct cx_list_s *list,
+        const void *array,
+        size_t n
+) {
+    return cx_ll_insert_sorted_impl(list, array, n, false);
+}
+
 static size_t cx_ll_remove(
         struct cx_list_s *list,
         size_t index,
@@ -1094,6 +1219,7 @@
         cx_ll_insert_element,
         cx_ll_insert_array,
         cx_ll_insert_sorted,
+        cx_ll_insert_unique,
         cx_ll_insert_iter,
         cx_ll_remove,
         cx_ll_clear,

mercurial