src/array_list.c

changeset 1618
ef7cab6eb131
parent 1611
de21dd0d1426
--- a/src/array_list.c	Tue Dec 16 21:33:58 2025 +0100
+++ b/src/array_list.c	Wed Dec 17 19:05:50 2025 +0100
@@ -127,14 +127,15 @@
     return 0;
 }
 
-int cx_array_insert_sorted_(
+int cx_array_insert_sorted_s_(
         const CxAllocator *allocator,
         CxArray *array,
         size_t elem_size,
-        cx_compare_func cmp_func,
         const void *sorted_data,
         size_t n,
-        bool allow_duplicates
+        bool allow_duplicates,
+        cx_compare_func2 cmp_func,
+        void *context
 ) {
     // assert pointers
     assert(allocator != NULL);
@@ -177,7 +178,7 @@
     char *dest = array->data;
 
     // find the first insertion point
-    di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func);
+    di = cx_array_binary_search_sup_c(dest, old_size, elem_size, src, cmp_func, context);
     dest += di * elem_size;
 
     // move the remaining elements in the array completely to the right
@@ -203,8 +204,8 @@
         // Therefore, the buffer can never contain an element that is smaller
         // than any element in the source and the infimum exists.
         size_t copy_len, bytes_copied;
-        copy_len = cx_array_binary_search_inf(
-            src, n - si, elem_size, bptr, cmp_func
+        copy_len = cx_array_binary_search_inf_c(
+            src, n - si, elem_size, bptr, cmp_func, context
         );
         copy_len++;
 
@@ -223,7 +224,7 @@
                 // for being a duplicate of the bptr
                 const char *end_of_src = src + (copy_len - 1) * elem_size;
                 size_t skip_len = 0;
-                while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) {
+                while (copy_len > 0 && cmp_func(bptr, end_of_src, context) == 0) {
                     end_of_src -= elem_size;
                     skip_len++;
                     copy_len--;
@@ -233,7 +234,7 @@
                 // and skip all duplicates with the last element in the array
                 size_t more_skipped = 0;
                 for (unsigned j = 0; j < copy_len; j++) {
-                    if (last != NULL && cmp_func(last, src) == 0) {
+                    if (last != NULL && cmp_func(last, src, context) == 0) {
                         // duplicate - skip
                         src += elem_size;
                         si++;
@@ -260,12 +261,13 @@
         if (si >= n) break;
 
         // determine how many buffered elements need to be restored
-        copy_len = cx_array_binary_search_sup(
+        copy_len = cx_array_binary_search_sup_c(
                 bptr,
                 new_size - bi,
                 elem_size,
                 src,
-                cmp_func
+                cmp_func,
+                context
         );
 
         // restore the buffered elements
@@ -295,7 +297,7 @@
                     const char *left_src = src;
                     while (si + copy_len + skip_len < n) {
                         const char *right_src = left_src + elem_size;
-                        int d = cmp_func(left_src,  right_src);
+                        int d = cmp_func(left_src,  right_src, context);
                         if (d < 0) {
                             if (skip_len > 0) {
                                 // new larger element found;
@@ -333,6 +335,20 @@
     return 0;
 }
 
+int cx_array_insert_sorted_(
+        const CxAllocator *allocator,
+        CxArray *array,
+        size_t elem_size,
+        cx_compare_func cmp_func,
+        const void *sorted_data,
+        size_t n,
+        bool allow_duplicates
+) {
+    cx_compare_func_wrapper wrapper = {cmp_func};
+    return cx_array_insert_sorted_s_(allocator, array, elem_size, sorted_data,
+        n, allow_duplicates, cx_acmp_wrap, &wrapper);
+}
+
 CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) {
     return cxIterator(array->data, elem_size, array->size);
 }
@@ -354,7 +370,8 @@
         size_t size,
         size_t elem_size,
         const void *elem,
-        cx_compare_func cmp_func
+        cx_compare_func2 cmp_func,
+        void *context
 ) {
     // special case: empty array
     if (size == 0) return 0;
@@ -366,7 +383,7 @@
     const char *array = arr;
 
     // check the first array element
-    result = cmp_func(elem, array);
+    result = cmp_func(elem, array, context);
     if (result < 0) {
         return size;
     } else if (result == 0) {
@@ -377,7 +394,7 @@
     if (size == 1) return 0;
 
     // check the last array element
-    result = cmp_func(elem, array + elem_size * (size - 1));
+    result = cmp_func(elem, array + elem_size * (size - 1), context);
     if (result >= 0) {
         return size - 1;
     }
@@ -386,12 +403,12 @@
     // so start the binary search
     size_t left_index = 1;
     size_t right_index = size - 1;
-    size_t pivot_index;
+    size_t pivot_index = 0;
 
     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);
+        result = cmp_func(elem, arr_elem, context);
         if (result == 0) {
             // found it!
             return pivot_index;
@@ -408,6 +425,74 @@
     return result < 0 ? (pivot_index - 1) : pivot_index;
 }
 
+size_t cx_array_binary_search_inf_c(
+        const void *arr,
+        size_t size,
+        size_t elem_size,
+        const void *elem,
+        cx_compare_func2 cmp_func,
+        void *context
+) {
+    size_t index = cx_array_binary_search_inf_impl(
+        arr, size, elem_size, elem, cmp_func, context);
+    // in case of equality, report the largest index
+    const char *e = ((const char *) arr) + (index + 1) * elem_size;
+    while (index + 1 < size && cmp_func(e, elem, context) == 0) {
+        e += elem_size;
+        index++;
+    }
+    return index;
+}
+
+size_t cx_array_binary_search_c(
+        const void *arr,
+        size_t size,
+        size_t elem_size,
+        const void *elem,
+        cx_compare_func2 cmp_func,
+        void *context
+) {
+    size_t index = cx_array_binary_search_inf_c(
+            arr, size, elem_size, elem, cmp_func, context
+    );
+    if (index < size && cmp_func(((const char *) arr) + index * elem_size,
+        elem, context) == 0) {
+        return index;
+    } else {
+        return size;
+    }
+}
+
+size_t cx_array_binary_search_sup_c(
+        const void *arr,
+        size_t size,
+        size_t elem_size,
+        const void *elem,
+        cx_compare_func2 cmp_func,
+        void *context
+) {
+    size_t index = cx_array_binary_search_inf_impl(
+            arr, size, elem_size, elem, cmp_func, context
+    );
+    const char *e = ((const char *) arr) + index * elem_size;
+    if (index == size) {
+        // no infimum means the first element is supremum
+        return 0;
+    } else if (cmp_func(e, elem, context) == 0) {
+        // found an equal element, search the smallest index
+        e -= elem_size; // e now contains the element at index-1
+        while (index > 0 && cmp_func(e, elem, context) == 0) {
+            e -= elem_size;
+            index--;
+        }
+        return index;
+    } else {
+        // we already have the largest index of the infimum (by design)
+        // the next element is the supremum (or there is no supremum)
+        return index + 1;
+    }
+}
+
 size_t cx_array_binary_search_inf(
         const void *arr,
         size_t size,
@@ -415,15 +500,8 @@
         const void *elem,
         cx_compare_func cmp_func
 ) {
-    size_t index = cx_array_binary_search_inf_impl(
-        arr, size, elem_size, elem, cmp_func);
-    // in case of equality, report the largest index
-    const char *e = ((const char *) arr) + (index + 1) * elem_size;
-    while (index + 1 < size && cmp_func(e, elem) == 0) {
-        e += elem_size;
-        index++;
-    }
-    return index;
+    cx_compare_func_wrapper wrapper = {cmp_func};
+    return cx_array_binary_search_inf_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper);
 }
 
 size_t cx_array_binary_search(
@@ -433,15 +511,8 @@
         const void *elem,
         cx_compare_func cmp_func
 ) {
-    size_t index = cx_array_binary_search_inf(
-            arr, size, elem_size, elem, cmp_func
-    );
-    if (index < size &&
-            cmp_func(((const char *) arr) + index * elem_size, elem) == 0) {
-        return index;
-    } else {
-        return size;
-    }
+    cx_compare_func_wrapper wrapper = {cmp_func};
+    return cx_array_binary_search_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper);
 }
 
 size_t cx_array_binary_search_sup(
@@ -451,26 +522,8 @@
         const void *elem,
         cx_compare_func cmp_func
 ) {
-    size_t index = cx_array_binary_search_inf_impl(
-            arr, size, elem_size, elem, cmp_func
-    );
-    const char *e = ((const char *) arr) + index * elem_size;
-    if (index == size) {
-        // no infimum means the first element is supremum
-        return 0;
-    } else if (cmp_func(e, elem) == 0) {
-        // found an equal element, search the smallest index
-        e -= elem_size; // e now contains the element at index-1
-        while (index > 0 && cmp_func(e, elem) == 0) {
-            e -= elem_size;
-            index--;
-        }
-        return index;
-    } else {
-        // we already have the largest index of the infimum (by design)
-        // the next element is the supremum (or there is no supremum)
-        return index + 1;
-    }
+    cx_compare_func_wrapper wrapper = {cmp_func};
+    return cx_array_binary_search_sup_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper);
 }
 
 #ifndef CX_ARRAY_SWAP_SBO_SIZE
@@ -578,14 +631,15 @@
         arl->data, list->collection.size, arl->capacity
     };
 
-    if (cx_array_insert_sorted_(
+    if (cx_array_insert_sorted_s_(
             list->collection.allocator,
             &wrap,
             list->collection.elem_size,
-            list->collection.cmpfunc,
             sorted_data,
             n,
-            allow_duplicates
+            allow_duplicates,
+            cx_list_compare_wrapper,
+            list
     )) {
         // array list implementation is "all or nothing"
         return 0;  // LCOV_EXCL_LINE
@@ -763,18 +817,18 @@
         bool remove
 ) {
     assert(list != NULL);
-    assert(list->collection.cmpfunc != NULL);
     if (list->collection.size == 0) return 0;
     char *cur = ((const cx_array_list *) list)->data;
 
     // optimize with binary search, when sorted
     if (list->collection.sorted) {
-        size_t i = cx_array_binary_search(
+        size_t i = cx_array_binary_search_c(
             cur,
             list->collection.size,
             list->collection.elem_size,
             elem,
-            list->collection.cmpfunc
+            cx_list_compare_wrapper,
+            list
         );
         if (remove && i < list->collection.size) {
             cx_arl_remove(list, i, 1, NULL);
@@ -784,7 +838,7 @@
 
     // fallback: linear search
     for (size_t i = 0; i < list->collection.size; i++) {
-        if (0 == list->collection.cmpfunc(elem, cur)) {
+        if (0 == cx_list_compare_wrapper(elem, cur, list)) {
             if (remove) {
                 cx_arl_remove(list, i, 1, NULL);
             }
@@ -795,12 +849,19 @@
     return list->collection.size;
 }
 
+// TODO: remove this hack once we have a solution for qsort() / qsort_s()
+static _Thread_local CxList *cx_hack_for_qsort_list;
+static int cx_hack_cmp_for_qsort(const void *l, const void *r) {
+    return cx_list_compare_wrapper(l, r, cx_hack_for_qsort_list);
+}
+
 static void cx_arl_sort(struct cx_list_s *list) {
-    assert(list->collection.cmpfunc != NULL);
+    // TODO: think about if we can somehow use qsort()_s
+    cx_hack_for_qsort_list = list;
     qsort(((cx_array_list *) list)->data,
           list->collection.size,
           list->collection.elem_size,
-          list->collection.cmpfunc
+          cx_hack_cmp_for_qsort
     );
 }
 
@@ -808,12 +869,11 @@
         const struct cx_list_s *list,
         const struct cx_list_s *other
 ) {
-    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);
+            int d = cx_list_compare_wrapper(left, right, (void*)list);
             if (d != 0) {
                 return d;
             }

mercurial