implement cx_kvl_find_remove() default tip

Sat, 13 Sep 2025 20:57:59 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 13 Sep 2025 20:57:59 +0200
changeset 1375
e0ba5e20e77a
parent 1374
4dceda670b05

implement cx_kvl_find_remove()

relates to #461

src/kv_list.c file | annotate | diff | comparison | revisions
tests/test_kv_list.c file | annotate | diff | comparison | revisions
--- a/src/kv_list.c	Sat Sep 13 20:55:56 2025 +0200
+++ b/src/kv_list.c	Sat Sep 13 20:57:59 2025 +0200
@@ -54,7 +54,7 @@
     void *map_destr_data;
 };
 
-static void cx_kv_list_destructor_wrapper_list(void *list_ptr, void *elem) {
+static void cx_kv_list_destructor_wrapper(void *list_ptr, void *elem) {
     const cx_kv_list *kv_list = list_ptr;
     // list destructor is already called with proper deref of the elem
     if (kv_list->list_destr) {
@@ -78,10 +78,10 @@
         list->list_destr = list->list.base.collection.simple_destructor;
         list->list.base.collection.simple_destructor = NULL;
     }
-    if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper_list) {
+    if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper) {
         list->list_destr2 = list->list.base.collection.advanced_destructor;
         list->list_destr_data = list->list.base.collection.destructor_data;
-        list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper_list;
+        list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper;
         list->list.base.collection.destructor_data = list;
     }
     if (list->map->map_base.base.collection.simple_destructor != NULL) {
@@ -204,9 +204,35 @@
         bool remove
 ) {
     cx_kv_list *kv_list = (cx_kv_list*)list;
-    cx_kv_list_update_destructors(kv_list);
-    // TODO: implement removal of the key in the map
-    return kv_list->list_methods->find_remove(list, elem, remove);
+    // we do not use the original list methods,
+    // because that would need two passes for removal
+    // (the first to find the index, the second to get a pointer)
+    if (list->collection.size == 0) return 0;
+
+    size_t index;
+    cx_linked_list *ll = &kv_list->list;
+    char *node = cx_linked_list_find(
+            ll->begin,
+            ll->loc_next, ll->loc_data,
+            list->collection.cmpfunc, elem,
+            &index
+    );
+    if (node == NULL) {
+        return list->collection.size;
+    }
+    if (remove) {
+        cx_kv_list_update_destructors(kv_list);
+        cx_invoke_advanced_destructor(list, node + ll->loc_data);
+        cx_linked_list_remove(&ll->begin, &ll->end,
+                              ll->loc_prev, ll->loc_next, node);
+        CxHashKey *key = cx_kv_list_loc_key(kv_list, node + ll->loc_data);
+        if (key->hash != 0) {
+            kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+        }
+        list->collection.size--;
+        cxFree(list->collection.allocator, node);
+    }
+    return index;
 }
 
 static void cx_kvl_sort(struct cx_list_s *list) {
--- a/tests/test_kv_list.c	Sat Sep 13 20:55:56 2025 +0200
+++ b/tests/test_kv_list.c	Sat Sep 13 20:57:59 2025 +0200
@@ -108,6 +108,52 @@
     cxListFree(list);
 }
 
+CX_TEST(test_kv_list_find_and_remove) {
+    CxList *list = cxKvListCreateSimple(sizeof(int));
+    list->collection.cmpfunc = cx_cmp_int;
+    int x, y;
+    CX_TEST_DO {
+        CxMap *map = cxKvListAsMap(list);
+
+        x = 13;
+        CX_TEST_ASSERT(0 == cxMapPut(map, "xyz", &x));
+        x = 37;
+        CX_TEST_ASSERT(0 == cxMapPut(map, "abc", &x));
+        x = 47;
+        CX_TEST_ASSERT(0 == cxMapPut(map, "efg", &x));
+        x = 90;
+        CX_TEST_ASSERT(0 == cxMapPut(map, "hij", &x));
+
+        CX_TEST_ASSERT(cxMapSize(map) == 4);
+        CX_TEST_ASSERT(cxListSize(list) == 4);
+
+        CX_TEST_ASSERT(*(int*)cxMapGet(map, "efg") == 47);
+        y = 47;
+        CX_TEST_ASSERT(2 == cxListFindRemove(list, &y));
+        CX_TEST_ASSERT(cxListSize(list) == 3);
+        CX_TEST_ASSERT(cxMapSize(map) == 3);
+        CX_TEST_ASSERT(cxMapGet(map, "efg") == NULL);
+
+        CX_TEST_ASSERT(*(int*)cxMapGet(map, "xyz") == 13);
+        y = 13;
+        CX_TEST_ASSERT(0 == cxListFindRemove(list, &y));
+        CX_TEST_ASSERT(cxListSize(list) == 2);
+        CX_TEST_ASSERT(cxMapSize(map) == 2);
+        CX_TEST_ASSERT(cxMapGet(map, "xyz") == NULL);
+
+        CX_TEST_ASSERT(*(int*)cxMapGet(map, "hij") == 90);
+        y = 90;
+        CX_TEST_ASSERT(1 == cxListFindRemove(list, &y));
+        CX_TEST_ASSERT(cxListSize(list) == 1);
+        CX_TEST_ASSERT(cxMapSize(map) == 1);
+        CX_TEST_ASSERT(cxMapGet(map, "hij") == NULL);
+
+        y = 666;
+        CX_TEST_ASSERT(cxListSize(list) == cxListFindRemove(list, &y));
+    }
+    cxListFree(list);
+}
+
 CX_TEST(test_kv_list_remove_and_get) {
     CxList *list = cxKvListCreateSimple(sizeof(int));
     int x, y;
@@ -479,6 +525,7 @@
     cx_test_register(suite, test_kv_list_free_as_map);
     cx_test_register(suite, test_kv_list_free_as_list);
     cx_test_register(suite, test_kv_list_remove);
+    cx_test_register(suite, test_kv_list_find_and_remove);
     cx_test_register(suite, test_kv_list_remove_and_get);
     cx_test_register(suite, test_kv_list_remove_array);
     cx_test_register(suite, test_kv_list_remove_array_and_get);

mercurial