src/ucx/list.c

changeset 579
e10457d74fe1
parent 504
c094afcdfb27
--- a/src/ucx/list.c	Mon Feb 10 17:44:51 2025 +0100
+++ b/src/ucx/list.c	Sun Mar 02 18:10:52 2025 +0100
@@ -35,37 +35,37 @@
 static _Thread_local cx_compare_func cx_pl_cmpfunc_impl;
 
 static int cx_pl_cmpfunc(
-        void const *l,
-        void const *r
+        const void *l,
+        const void *r
 ) {
     void *const *lptr = l;
     void *const *rptr = r;
-    void const *left = lptr == NULL ? NULL : *lptr;
-    void const *right = rptr == NULL ? NULL : *rptr;
+    const void *left = lptr == NULL ? NULL : *lptr;
+    const void *right = rptr == NULL ? NULL : *rptr;
     return cx_pl_cmpfunc_impl(left, right);
 }
 
-static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) {
+static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) {
     // cast away const - this is the hacky thing
-    struct cx_list_s *l = (struct cx_list_s *) list;
+    struct cx_collection_s *l = (struct cx_collection_s*) &list->collection;
     cx_pl_cmpfunc_impl = l->cmpfunc;
     l->cmpfunc = cx_pl_cmpfunc;
 }
 
-static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) {
+static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) {
     // cast away const - this is the hacky thing
-    struct cx_list_s *l = (struct cx_list_s *) list;
+    struct cx_collection_s *l = (struct cx_collection_s*) &list->collection;
     l->cmpfunc = cx_pl_cmpfunc_impl;
 }
 
 static void cx_pl_destructor(struct cx_list_s *list) {
-    list->climpl->destructor(list);
+    list->climpl->deallocate(list);
 }
 
 static int cx_pl_insert_element(
         struct cx_list_s *list,
         size_t index,
-        void const *element
+        const void *element
 ) {
     return list->climpl->insert_element(list, index, &element);
 }
@@ -73,26 +73,39 @@
 static size_t cx_pl_insert_array(
         struct cx_list_s *list,
         size_t index,
-        void const *array,
+        const void *array,
         size_t n
 ) {
     return list->climpl->insert_array(list, index, array, n);
 }
 
+static size_t cx_pl_insert_sorted(
+        struct cx_list_s *list,
+        const void *array,
+        size_t n
+) {
+    cx_pl_hack_cmpfunc(list);
+    size_t result = list->climpl->insert_sorted(list, array, n);
+    cx_pl_unhack_cmpfunc(list);
+    return result;
+}
+
 static int cx_pl_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;
+    struct cx_list_s *list = iter->src_handle.m;
     return list->climpl->insert_iter(iter, &elem, prepend);
 }
 
-static int cx_pl_remove(
+static size_t cx_pl_remove(
         struct cx_list_s *list,
-        size_t index
+        size_t index,
+        size_t num,
+        void *targetbuf
 ) {
-    return list->climpl->remove(list, index);
+    return list->climpl->remove(list, index, num, targetbuf);
 }
 
 static void cx_pl_clear(struct cx_list_s *list) {
@@ -108,19 +121,20 @@
 }
 
 static void *cx_pl_at(
-        struct cx_list_s const *list,
+        const struct cx_list_s *list,
         size_t index
 ) {
     void **ptr = list->climpl->at(list, index);
     return ptr == NULL ? NULL : *ptr;
 }
 
-static ssize_t cx_pl_find(
-        struct cx_list_s const *list,
-        void const *elem
+static size_t cx_pl_find_remove(
+        struct cx_list_s *list,
+        const void *elem,
+        bool remove
 ) {
     cx_pl_hack_cmpfunc(list);
-    ssize_t ret = list->climpl->find(list, &elem);
+    size_t ret = list->climpl->find_remove(list, &elem, remove);
     cx_pl_unhack_cmpfunc(list);
     return ret;
 }
@@ -132,8 +146,8 @@
 }
 
 static int cx_pl_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_pl_hack_cmpfunc(list);
     int ret = list->climpl->compare(list, other);
@@ -145,14 +159,14 @@
     list->climpl->reverse(list);
 }
 
-static void *cx_pl_iter_current(void const *it) {
-    struct cx_iterator_s const *iter = it;
+static void *cx_pl_iter_current(const void *it) {
+    const struct cx_iterator_s *iter = it;
     void **ptr = iter->base.current_impl(it);
     return ptr == NULL ? NULL : *ptr;
 }
 
 static struct cx_iterator_s cx_pl_iterator(
-        struct cx_list_s const *list,
+        const struct cx_list_s *list,
         size_t index,
         bool backwards
 ) {
@@ -166,74 +180,52 @@
         cx_pl_destructor,
         cx_pl_insert_element,
         cx_pl_insert_array,
+        cx_pl_insert_sorted,
         cx_pl_insert_iter,
         cx_pl_remove,
         cx_pl_clear,
         cx_pl_swap,
         cx_pl_at,
-        cx_pl_find,
+        cx_pl_find_remove,
         cx_pl_sort,
         cx_pl_compare,
         cx_pl_reverse,
         cx_pl_iterator,
 };
-
-void cxListStoreObjects(CxList *list) {
-    list->store_pointer = false;
-    if (list->climpl != NULL) {
-        list->cl = list->climpl;
-        list->climpl = NULL;
-    }
-}
-
-void cxListStorePointers(CxList *list) {
-    list->item_size = sizeof(void *);
-    list->store_pointer = true;
-    list->climpl = list->cl;
-    list->cl = &cx_pointer_list_class;
-}
-
 // </editor-fold>
 
 // <editor-fold desc="empty list implementation">
 
-static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) {
+static void cx_emptyl_noop(cx_attr_unused CxList *list) {
     // this is a noop, but MUST be implemented
 }
 
 static void *cx_emptyl_at(
-        __attribute__((__unused__)) struct cx_list_s const *list,
-        __attribute__((__unused__)) size_t index
+        cx_attr_unused const struct cx_list_s *list,
+        cx_attr_unused size_t index
 ) {
     return NULL;
 }
 
-static ssize_t cx_emptyl_find(
-        __attribute__((__unused__)) struct cx_list_s const *list,
-        __attribute__((__unused__)) void const *elem
+static size_t cx_emptyl_find_remove(
+        cx_attr_unused struct cx_list_s *list,
+        cx_attr_unused const void *elem,
+        cx_attr_unused bool remove
 ) {
-    return -1;
+    return 0;
 }
 
-static int cx_emptyl_compare(
-        __attribute__((__unused__)) struct cx_list_s const *list,
-        struct cx_list_s const *other
-) {
-    if (other->size == 0) return 0;
-    return -1;
-}
-
-static bool cx_emptyl_iter_valid(__attribute__((__unused__)) void const *iter) {
+static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) {
     return false;
 }
 
 static CxIterator cx_emptyl_iterator(
-        struct cx_list_s const *list,
+        const struct cx_list_s *list,
         size_t index,
-        __attribute__((__unused__)) bool backwards
+        cx_attr_unused bool backwards
 ) {
     CxIterator iter = {0};
-    iter.src_handle = list;
+    iter.src_handle.c = list;
     iter.index = index;
     iter.base.valid = cx_emptyl_iter_valid;
     return iter;
@@ -245,17 +237,19 @@
         NULL,
         NULL,
         NULL,
+        NULL,
         cx_emptyl_noop,
         NULL,
         cx_emptyl_at,
-        cx_emptyl_find,
+        cx_emptyl_find_remove,
         cx_emptyl_noop,
-        cx_emptyl_compare,
+        NULL,
         cx_emptyl_noop,
         cx_emptyl_iterator,
 };
 
 CxList cx_empty_list = {
+    {
         NULL,
         NULL,
         0,
@@ -264,41 +258,204 @@
         NULL,
         NULL,
         false,
-        &cx_empty_list_class,
-        NULL
+        true,
+    },
+    &cx_empty_list_class,
+    NULL
 };
 
 CxList *const cxEmptyList = &cx_empty_list;
 
 // </editor-fold>
 
-void cxListDestroy(CxList *list) {
-    list->cl->destructor(list);
+#define invoke_list_func(name, list, ...) \
+    ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \
+    (list, __VA_ARGS__)
+
+size_t cx_list_default_insert_array(
+        struct cx_list_s *list,
+        size_t index,
+        const void *data,
+        size_t n
+) {
+    size_t elem_size = list->collection.elem_size;
+    const char *src = data;
+    size_t i = 0;
+    for (; i < n; i++) {
+        if (0 != invoke_list_func(
+            insert_element, list, index + i,
+            src + (i * elem_size))) return i;
+    }
+    return i;
+}
+
+size_t cx_list_default_insert_sorted(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+) {
+    // corner case
+    if (n == 0) return 0;
+
+    size_t elem_size = list->collection.elem_size;
+    cx_compare_func cmp = list->collection.cmpfunc;
+    const char *src = sorted_data;
+
+    // track indices and number of inserted items
+    size_t di = 0, si = 0, inserted = 0;
+
+    // search the list for insertion points
+    for (; di < list->collection.size; di++) {
+        const void *list_elm = invoke_list_func(at, list, di);
+
+        // compare current list element with first source element
+        // if less or equal, skip
+        if (cmp(list_elm, src) <= 0) {
+            continue;
+        }
+
+        // determine number of consecutive elements that can be inserted
+        size_t ins = 1;
+        const char *next = src;
+        while (++si < n) {
+            next += elem_size;
+            // once we become larger than the list elem, break
+            if (cmp(list_elm, next) <= 0) {
+                break;
+            }
+            // otherwise, we can insert one more
+            ins++;
+        }
+
+        // insert the elements at location si
+        if (ins == 1) {
+            if (0 != invoke_list_func(
+                insert_element, list, di, src)) return inserted;
+        } else {
+            size_t r = invoke_list_func(insert_array, list, di, src, ins);
+            if (r < ins) return inserted + r;
+        }
+        inserted += ins;
+        di += ins;
+
+        // everything inserted?
+        if (inserted == n) return inserted;
+        src = next;
+    }
+
+    // insert remaining items
+    if (si < n) {
+        inserted += invoke_list_func(insert_array, list, di, src, n - si);
+    }
+
+    return inserted;
+}
+
+void cx_list_default_sort(struct cx_list_s *list) {
+    size_t elem_size = list->collection.elem_size;
+    size_t list_size = list->collection.size;
+    void *tmp = malloc(elem_size * list_size);
+    if (tmp == NULL) abort();
+
+    // copy elements from source array
+    char *loc = tmp;
+    for (size_t i = 0; i < list_size; i++) {
+        void *src = invoke_list_func(at, list, i);
+        memcpy(loc, src, elem_size);
+        loc += elem_size;
+    }
+
+    // qsort
+    qsort(tmp, list_size, elem_size,
+          list->collection.cmpfunc);
+
+    // copy elements back
+    loc = tmp;
+    for (size_t i = 0; i < list_size; i++) {
+        void *dest = invoke_list_func(at, list, i);
+        memcpy(dest, loc, elem_size);
+        loc += elem_size;
+    }
+
+    free(tmp);
+}
+
+int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
+    if (i == j) return 0;
+    if (i >= list->collection.size) return 1;
+    if (j >= list->collection.size) return 1;
+
+    size_t elem_size = list->collection.elem_size;
+
+    void *tmp = malloc(elem_size);
+    if (tmp == NULL) return 1;
+
+    void *ip = invoke_list_func(at, list, i);
+    void *jp = invoke_list_func(at, list, j);
+
+    memcpy(tmp, ip, elem_size);
+    memcpy(ip, jp, elem_size);
+    memcpy(jp, tmp, elem_size);
+
+    free(tmp);
+
+    return 0;
+}
+
+void cx_list_init(
+    struct cx_list_s *list,
+    struct cx_list_class_s *cl,
+    const struct cx_allocator_s *allocator,
+    cx_compare_func comparator,
+    size_t elem_size
+) {
+    list->cl = cl;
+    list->collection.allocator = allocator;
+    list->collection.cmpfunc = comparator;
+    if (elem_size > 0) {
+        list->collection.elem_size = elem_size;
+    } else {
+        list->collection.elem_size = sizeof(void *);
+        if (list->collection.cmpfunc == NULL) {
+            list->collection.cmpfunc = cx_cmp_ptr;
+        }
+        list->collection.store_pointer = true;
+        list->climpl = list->cl;
+        list->cl = &cx_pointer_list_class;
+    }
 }
 
 int cxListCompare(
-        CxList const *list,
-        CxList const *other
+        const CxList *list,
+        const CxList *other
 ) {
-    if (
-        // if one is storing pointers but the other is not
-        (list->store_pointer ^ other->store_pointer) ||
+    bool cannot_optimize = false;
 
-        // if one class is wrapped but the other is not
-        ((list->climpl == NULL) ^ (other->climpl == NULL)) ||
+    // if one is storing pointers but the other is not
+    cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer;
+
+    // if one class is wrapped but the other is not
+    cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL);
 
-        // if the resolved compare functions are not the same
-        ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) !=
-         (other->climpl != NULL ? other->climpl->compare : other->cl->compare))
-    ) {
+    // if the compare functions do not match or both are NULL
+    if (!cannot_optimize) {
+        cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ?
+                                                      list->climpl->compare : list->cl->compare);
+        cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ?
+                                                       other->climpl->compare : other->cl->compare);
+        cannot_optimize |= list_cmp != other_cmp;
+        cannot_optimize |= list_cmp == NULL;
+    }
+
+    if (cannot_optimize) {
         // lists are definitely different - cannot use internal compare function
-        if (list->size == other->size) {
-            CxIterator left = cxListIterator(list);
-            CxIterator right = cxListIterator(other);
-            for (size_t i = 0; i < list->size; i++) {
+        if (list->collection.size == other->collection.size) {
+            CxIterator left = list->cl->iterator(list, 0, false);
+            CxIterator right = other->cl->iterator(other, 0, false);
+            for (size_t i = 0; i < list->collection.size; i++) {
                 void *leftValue = cxIteratorCurrent(left);
                 void *rightValue = cxIteratorCurrent(right);
-                int d = list->cmpfunc(leftValue, rightValue);
+                int d = list->collection.cmpfunc(leftValue, rightValue);
                 if (d != 0) {
                     return d;
                 }
@@ -307,7 +464,7 @@
             }
             return 0;
         } else {
-            return list->size < other->size ? -1 : 1;
+            return list->collection.size < other->collection.size ? -1 : 1;
         }
     } else {
         // lists are compatible
@@ -315,28 +472,25 @@
     }
 }
 
-CxMutIterator cxListMutIteratorAt(
+CxIterator cxListMutIteratorAt(
         CxList *list,
         size_t index
 ) {
     CxIterator it = list->cl->iterator(list, index, false);
     it.base.mutating = true;
-
-    // we know the iterators share the same memory layout
-    CxMutIterator iter;
-    memcpy(&iter, &it, sizeof(CxMutIterator));
-    return iter;
+    return it;
 }
 
-CxMutIterator cxListMutBackwardsIteratorAt(
+CxIterator cxListMutBackwardsIteratorAt(
         CxList *list,
         size_t index
 ) {
     CxIterator it = list->cl->iterator(list, index, true);
     it.base.mutating = true;
+    return it;
+}
 
-    // we know the iterators share the same memory layout
-    CxMutIterator iter;
-    memcpy(&iter, &it, sizeof(CxMutIterator));
-    return iter;
+void cxListFree(CxList *list) {
+    if (list == NULL) return;
+    list->cl->deallocate(list);
 }

mercurial