ucx/linked_list.c

changeset 852
83fdf679df99
parent 816
839fefbdedc7
--- a/ucx/linked_list.c	Thu Nov 28 17:53:13 2024 +0100
+++ b/ucx/linked_list.c	Mon Jan 06 21:18:36 2025 +0100
@@ -27,7 +27,6 @@
  */
 
 #include "cx/linked_list.h"
-#include "cx/utils.h"
 #include "cx/compare.h"
 #include <string.h>
 #include <assert.h>
@@ -41,7 +40,7 @@
 #define ll_data(node) (((char*)(node))+loc_data)
 
 void *cx_linked_list_at(
-        void const *start,
+        const void *start,
         size_t start_index,
         ptrdiff_t loc_advance,
         size_t index
@@ -49,7 +48,7 @@
     assert(start != NULL);
     assert(loc_advance >= 0);
     size_t i = start_index;
-    void const *cur = start;
+    const void *cur = start;
     while (i != index && cur != NULL) {
         cur = ll_advance(cur);
         i < index ? i++ : i--;
@@ -58,11 +57,11 @@
 }
 
 ssize_t cx_linked_list_find(
-        void const *start,
+        const void *start,
         ptrdiff_t loc_advance,
         ptrdiff_t loc_data,
         cx_compare_func cmp_func,
-        void const *elem
+        const void *elem
 ) {
     void *dummy;
     return cx_linked_list_find_node(
@@ -74,11 +73,11 @@
 
 ssize_t cx_linked_list_find_node(
         void **result,
-        void const *start,
+        const void *start,
         ptrdiff_t loc_advance,
         ptrdiff_t loc_data,
         cx_compare_func cmp_func,
-        void const *elem
+        const void *elem
 ) {
     assert(result != NULL);
     assert(start != NULL);
@@ -86,12 +85,12 @@
     assert(loc_data >= 0);
     assert(cmp_func);
 
-    void const *node = start;
+    const void *node = start;
     ssize_t index = 0;
     do {
         void *current = ll_data(node);
         if (cmp_func(current, elem) == 0) {
-            *result = (void*) node;
+            *result = (void *) node;
             return index;
         }
         node = ll_advance(node);
@@ -102,21 +101,21 @@
 }
 
 void *cx_linked_list_first(
-        void const *node,
+        const void *node,
         ptrdiff_t loc_prev
 ) {
     return cx_linked_list_last(node, loc_prev);
 }
 
 void *cx_linked_list_last(
-        void const *node,
+        const void *node,
         ptrdiff_t loc_next
 ) {
     assert(node != NULL);
     assert(loc_next >= 0);
 
-    void const *cur = node;
-    void const *last;
+    const void *cur = node;
+    const void *last;
     do {
         last = cur;
     } while ((cur = ll_next(cur)) != NULL);
@@ -125,16 +124,16 @@
 }
 
 void *cx_linked_list_prev(
-        void const *begin,
+        const void *begin,
         ptrdiff_t loc_next,
-        void const *node
+        const void *node
 ) {
     assert(begin != NULL);
     assert(node != NULL);
     assert(loc_next >= 0);
     if (begin == node) return NULL;
-    void const *cur = begin;
-    void const *next;
+    const void *cur = begin;
+    const void *next;
     while (1) {
         next = ll_next(cur);
         if (next == node) return (void *) cur;
@@ -247,19 +246,111 @@
     }
 }
 
-void cx_linked_list_remove(
+void cx_linked_list_insert_sorted(
+        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);
+    cx_linked_list_insert_sorted_chain(
+            begin, end, loc_prev, loc_next, new_node, cmp_func);
+}
+
+void cx_linked_list_insert_sorted_chain(
         void **begin,
         void **end,
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next,
-        void *node
+        void *insert_begin,
+        cx_compare_func cmp_func
+) {
+    assert(begin != NULL);
+    assert(loc_next >= 0);
+    assert(insert_begin != NULL);
+
+    // track currently observed nodes
+    void *dest_prev = NULL;
+    void *dest = *begin;
+    void *src = insert_begin;
+
+    // special case: list is empty
+    if (dest == NULL) {
+        *begin = src;
+        if (end != NULL) {
+            *end = cx_linked_list_last(src, loc_next);
+        }
+        return;
+    }
+
+    // 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;
+        }
+
+        // determine chain of elements that can be inserted
+        void *end_of_chain = src;
+        void *next_in_chain = ll_next(src);
+        while (next_in_chain != NULL) {
+            // once we become larger than the list elem, break
+            if (cmp_func(dest, next_in_chain) <= 0) {
+                break;
+            }
+            // otherwise, we can insert one more
+            end_of_chain = next_in_chain;
+            next_in_chain = ll_next(next_in_chain);
+        }
+
+        // insert the elements
+        if (dest_prev == NULL) {
+            // new begin
+            *begin = src;
+        } else {
+            cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
+        }
+        cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next);
+
+        // continue with next
+        src = next_in_chain;
+        dest_prev = dest;
+        dest = ll_next(dest);
+    }
+
+    // insert remaining items
+    if (src != NULL) {
+        cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
+    }
+
+    // determine new end of list, if requested
+    if (end != NULL) {
+        *end = cx_linked_list_last(
+                dest != NULL ? dest : dest_prev, loc_next);
+    }
+}
+
+size_t cx_linked_list_remove_chain(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *node,
+        size_t num
 ) {
     assert(node != NULL);
     assert(loc_next >= 0);
     assert(loc_prev >= 0 || begin != NULL);
 
+    // easy exit
+    if (num == 0) return 0;
+
     // find adjacent nodes
-    void *next = ll_next(node);
     void *prev;
     if (loc_prev >= 0) {
         prev = ll_prev(node);
@@ -267,6 +358,12 @@
         prev = cx_linked_list_prev(*begin, loc_next, node);
     }
 
+    void *next = ll_next(node);
+    size_t removed = 1;
+    for (; removed < num && next != NULL ; removed++) {
+        next = ll_next(next);
+    }
+
     // update next pointer of prev node, or set begin
     if (prev == NULL) {
         if (begin != NULL) {
@@ -284,10 +381,12 @@
     } else if (loc_prev >= 0) {
         ll_prev(next) = prev;
     }
+
+    return removed;
 }
 
 size_t cx_linked_list_size(
-        void const *node,
+        const void *node,
         ptrdiff_t loc_next
 ) {
     assert(loc_next >= 0);
@@ -347,13 +446,13 @@
 
     // Update pointer
     if (loc_prev >= 0) ll_prev(sorted[0]) = NULL;
-    cx_for_n (i, length - 1) {
+    for (size_t i = 0 ; i < length - 1; i++) {
         cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next);
     }
     ll_next(sorted[length - 1]) = NULL;
 
     *begin = sorted[0];
-    *end = sorted[length-1];
+    *end = sorted[length - 1];
     if (sorted != sbo) {
         free(sorted);
     }
@@ -425,17 +524,17 @@
 }
 
 int cx_linked_list_compare(
-        void const *begin_left,
-        void const *begin_right,
+        const void *begin_left,
+        const void *begin_right,
         ptrdiff_t loc_advance,
         ptrdiff_t loc_data,
         cx_compare_func cmp_func
 ) {
-    void const *left = begin_left, *right = begin_right;
+    const void *left = begin_left, *right = begin_right;
 
     while (left != NULL && right != NULL) {
-        void const *left_data = ll_data(left);
-        void const *right_data = ll_data(right);
+        const void *left_data = ll_data(left);
+        const void *right_data = ll_data(right);
         int result = cmp_func(left_data, right_data);
         if (result != 0) return result;
         left = ll_advance(left);
@@ -498,7 +597,7 @@
 } cx_linked_list;
 
 static cx_linked_list_node *cx_ll_node_at(
-        cx_linked_list const *list,
+        const cx_linked_list *list,
         size_t index
 ) {
     if (index >= list->base.collection.size) {
@@ -510,15 +609,19 @@
     }
 }
 
+static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) {
+    return cxMalloc(list->collection.allocator,
+                    sizeof(cx_linked_list_node) + list->collection.elem_size);
+}
+
 static int cx_ll_insert_at(
         struct cx_list_s *list,
         cx_linked_list_node *node,
-        void const *elem
+        const void *elem
 ) {
 
     // create the new new_node
-    cx_linked_list_node *new_node = cxMalloc(list->collection.allocator,
-                                             sizeof(cx_linked_list_node) + list->collection.elem_size);
+    cx_linked_list_node *new_node = cx_ll_malloc_node(list);
 
     // sortir if failed
     if (new_node == NULL) return 1;
@@ -543,7 +646,7 @@
 static size_t cx_ll_insert_array(
         struct cx_list_s *list,
         size_t index,
-        void const *array,
+        const void *array,
         size_t n
 ) {
     // out-of bounds and corner case check
@@ -553,9 +656,7 @@
     cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
 
     // perform first insert
-    if (0 != cx_ll_insert_at(list, node, array)) {
-        return 1;
-    }
+    if (0 != cx_ll_insert_at(list, node, array)) return 1;
 
     // is there more?
     if (n == 1) return 1;
@@ -563,13 +664,11 @@
     // we now know exactly where we are
     node = node == NULL ? ((cx_linked_list *) list)->begin : node->next;
 
-    // we can add the remaining nodes and immedately advance to the inserted node
-    char const *source = array;
+    // we can add the remaining nodes and immediately advance to the inserted node
+    const char *source = array;
     for (size_t i = 1; i < n; i++) {
         source += list->collection.elem_size;
-        if (0 != cx_ll_insert_at(list, node, source)) {
-            return i;
-        }
+        if (0 != cx_ll_insert_at(list, node, source)) return i;
         node = node->next;
     }
     return n;
@@ -578,35 +677,123 @@
 static int cx_ll_insert_element(
         struct cx_list_s *list,
         size_t index,
-        void const *element
+        const void *element
 ) {
     return 1 != cx_ll_insert_array(list, index, element, 1);
 }
 
-static int cx_ll_remove(
+static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
+
+static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) {
+    const cx_linked_list_node *left = l;
+    const cx_linked_list_node *right = r;
+    return cx_ll_insert_sorted_cmp_func(left->payload, right->payload);
+}
+
+static size_t cx_ll_insert_sorted(
         struct cx_list_s *list,
-        size_t index
+        const void *array,
+        size_t n
+) {
+    // special case
+    if (n == 0) return 0;
+
+    // create a new chain of nodes
+    cx_linked_list_node *chain = cx_ll_malloc_node(list);
+    if (chain == NULL) return 0;
+
+    memcpy(chain->payload, array, list->collection.elem_size);
+    chain->prev = NULL;
+    chain->next = NULL;
+
+    // add all elements from the array to that chain
+    cx_linked_list_node *prev = chain;
+    const char *src = array;
+    size_t inserted = 1;
+    for (; inserted < n; inserted++) {
+        cx_linked_list_node *next = cx_ll_malloc_node(list);
+        if (next == NULL) break;
+        src += list->collection.elem_size;
+        memcpy(next->payload, src, list->collection.elem_size);
+        prev->next = next;
+        next->prev = prev;
+        prev = next;
+    }
+    prev->next = NULL;
+
+    // invoke the low level function
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
+    cx_linked_list_insert_sorted_chain(
+            (void **) &ll->begin,
+            (void **) &ll->end,
+            CX_LL_LOC_PREV,
+            CX_LL_LOC_NEXT,
+            chain,
+            cx_ll_insert_sorted_cmp_helper
+    );
+
+    // adjust the list metadata
+    list->collection.size += inserted;
+
+    return inserted;
+}
+
+static size_t cx_ll_remove(
+        struct cx_list_s *list,
+        size_t index,
+        size_t num,
+        void *targetbuf
 ) {
     cx_linked_list *ll = (cx_linked_list *) list;
     cx_linked_list_node *node = cx_ll_node_at(ll, index);
 
     // out-of-bounds check
-    if (node == NULL) return 1;
-
-    // element destruction
-    cx_invoke_destructor(list, node->payload);
+    if (node == NULL) return 0;
 
     // remove
-    cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
-                          CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+    size_t removed = cx_linked_list_remove_chain(
+            (void **) &ll->begin,
+            (void **) &ll->end,
+            CX_LL_LOC_PREV,
+            CX_LL_LOC_NEXT,
+            node,
+            num
+    );
 
     // adjust size
-    list->collection.size--;
+    list->collection.size -= removed;
+
+    // copy or destroy the removed chain
+    if (targetbuf == NULL) {
+        cx_linked_list_node *n = node;
+        for (size_t i = 0; i < removed; i++) {
+            // element destruction
+            cx_invoke_destructor(list, n->payload);
 
-    // free and return
-    cxFree(list->collection.allocator, node);
+            // free the node and advance
+            void *next = n->next;
+            cxFree(list->collection.allocator, n);
+            n = next;
+        }
+    } else {
+        char *dest = targetbuf;
+        cx_linked_list_node *n = node;
+        for (size_t i = 0; i < removed; i++) {
+            // copy payload
+            memcpy(dest, n->payload, list->collection.elem_size);
 
-    return 0;
+            // advance target buffer
+            dest += list->collection.elem_size;
+
+            // free the node and advance
+            void *next = n->next;
+            cxFree(list->collection.allocator, n);
+            n = next;
+        }
+    }
+
+    return removed;
 }
 
 static void cx_ll_clear(struct cx_list_s *list) {
@@ -627,7 +814,7 @@
 #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
 #define CX_LINKED_LIST_SWAP_SBO_SIZE 128
 #endif
-unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE;
+const unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE;
 
 static int cx_ll_swap(
         struct cx_list_s *list,
@@ -648,7 +835,7 @@
         left = j;
         right = i;
     }
-    cx_linked_list_node *nleft, *nright;
+    cx_linked_list_node *nleft = NULL, *nright = NULL;
     if (left < mid && right < mid) {
         // case 1: both items left from mid
         nleft = cx_ll_node_at(ll, left);
@@ -748,7 +935,7 @@
 }
 
 static void *cx_ll_at(
-        struct cx_list_s const *list,
+        const struct cx_list_s *list,
         size_t index
 ) {
     cx_linked_list *ll = (cx_linked_list *) list;
@@ -758,7 +945,7 @@
 
 static ssize_t cx_ll_find_remove(
         struct cx_list_s *list,
-        void const *elem,
+        const void *elem,
         bool remove
 ) {
     if (remove) {
@@ -800,8 +987,8 @@
 }
 
 static int cx_ll_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
 ) {
     cx_linked_list *left = (cx_linked_list *) list;
     cx_linked_list *right = (cx_linked_list *) other;
@@ -810,8 +997,8 @@
                                   list->collection.cmpfunc);
 }
 
-static bool cx_ll_iter_valid(void const *it) {
-    struct cx_iterator_s const *iter = it;
+static bool cx_ll_iter_valid(const void *it) {
+    const struct cx_iterator_s *iter = it;
     return iter->elem_handle != NULL;
 }
 
@@ -856,21 +1043,21 @@
     }
 }
 
-static void *cx_ll_iter_current(void const *it) {
-    struct cx_iterator_s const *iter = it;
+static void *cx_ll_iter_current(const void *it) {
+    const struct cx_iterator_s *iter = it;
     cx_linked_list_node *node = iter->elem_handle;
     return node->payload;
 }
 
 static CxIterator cx_ll_iterator(
-        struct cx_list_s const *list,
+        const struct cx_list_s *list,
         size_t index,
         bool backwards
 ) {
     CxIterator iter;
     iter.index = index;
     iter.src_handle.c = list;
-    iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
+    iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index);
     iter.elem_size = list->collection.elem_size;
     iter.elem_count = list->collection.size;
     iter.base.valid = cx_ll_iter_valid;
@@ -883,7 +1070,7 @@
 
 static int cx_ll_insert_iter(
         CxIterator *iter,
-        void const *elem,
+        const void *elem,
         int prepend
 ) {
     struct cx_list_s *list = iter->src_handle.m;
@@ -892,11 +1079,19 @@
         assert(prepend >= 0 && prepend <= 1);
         cx_linked_list_node *choice[2] = {node, node->prev};
         int result = cx_ll_insert_at(list, choice[prepend], elem);
-        iter->index += prepend * (0 == result);
+        if (result == 0) {
+            iter->elem_count++;
+            if (prepend) {
+                iter->index++;
+            }
+        }
         return result;
     } else {
         int result = cx_ll_insert_element(list, list->collection.size, elem);
-        iter->index = list->collection.size;
+        if (result == 0) {
+            iter->elem_count++;
+            iter->index = list->collection.size;
+        }
         return result;
     }
 }
@@ -919,6 +1114,7 @@
         cx_ll_destructor,
         cx_ll_insert_element,
         cx_ll_insert_array,
+        cx_ll_insert_sorted,
         cx_ll_insert_iter,
         cx_ll_remove,
         cx_ll_clear,
@@ -932,7 +1128,7 @@
 };
 
 CxList *cxLinkedListCreate(
-        CxAllocator const *allocator,
+        const CxAllocator *allocator,
         cx_compare_func comparator,
         size_t elem_size
 ) {

mercurial