fixes that overwriting elements with cxMapPut() in a kv-list did not work

Sat, 06 Dec 2025 16:22:19 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 06 Dec 2025 16:22:19 +0100
changeset 1546
c8dd35f3ea53
parent 1545
7822ffe65848
child 1547
12da0654e4a9

fixes that overwriting elements with cxMapPut() in a kv-list did not work

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
src/kv_list.c file | annotate | diff | comparison | revisions
tests/test_kv_list.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Sat Dec 06 15:55:53 2025 +0100
+++ b/CHANGELOG	Sat Dec 06 16:22:19 2025 +0100
@@ -5,6 +5,7 @@
  * changes cxBufferReserve() to allow reducing the capacity
  * changes the members of CxJson and CxJsonValue
  * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors
+ * fixes that overwriting items with cxMapPut() in a kv-list did not work
  * fixes that cxReallocate(), cxReallocateArray(), cx_reallocate(), and cx_reallocatearray()
    were not returning zero after freeing the memory when passed a size of zero
 
--- a/docs/Writerside/topics/about.md	Sat Dec 06 15:55:53 2025 +0100
+++ b/docs/Writerside/topics/about.md	Sat Dec 06 16:22:19 2025 +0100
@@ -32,6 +32,7 @@
 * changes cxBufferReserve() to allow reducing the capacity
 * changes the members of CxJson and CxJsonValue
 * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors
+* fixes that overwriting items with cxMapPut() in a kv-list did not work
 * fixes that cxReallocate(), cxReallocateArray(), cx_reallocate(), and cx_reallocatearray()
   were not returning zero after freeing the memory when passed a size of zero
 
--- a/src/kv_list.c	Sat Dec 06 15:55:53 2025 +0100
+++ b/src/kv_list.c	Sat Dec 06 16:22:19 2025 +0100
@@ -297,41 +297,7 @@
     kv_list->map_methods->clear(map);
 }
 
-static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
-    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
-    // if the hash has not yet been computed, do it now
-    if (key.hash == 0) {
-        cx_hash_murmur(&key);
-    }
-
-    // reserve memory in the map first
-    void **map_data = kv_list->map_methods->put(map, key, NULL);
-    if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
-
-    // insert the data into the list (which most likely destroys the sorted property)
-    kv_list->list.base.collection.sorted = false;
-    void *node_data = kv_list->list_methods->insert_element(
-        &kv_list->list.base, kv_list->list.base.collection.size,
-        kv_list->list.base.collection.store_pointer ? &value : value);
-    if (node_data == NULL) { // LCOV_EXCL_START
-        // non-destructively remove the key again
-        kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
-        return NULL;
-    } // LCOV_EXCL_STOP
-
-    // write the node pointer to the map entry
-    *map_data = node_data;
-
-    // copy the key to the node data
-    CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
-    *key_ptr = key;
-
-    // we must return node_data here and not map_data,
-    // because the node_data is the actual element of this collection
-    return node_data;
-}
-
-void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
+static void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
     cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
     void *node_data = kv_list->map_methods->get(map, key);
     if (node_data == NULL) return NULL; // LCOV_EXCL_LINE
@@ -339,7 +305,7 @@
     return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data;
 }
 
-int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
+static int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
     cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
 
     void *node_data;
@@ -382,6 +348,43 @@
     return 0;
 }
 
+static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    // if the hash has not yet been computed, do it now
+    if (key.hash == 0) {
+        cx_hash_murmur(&key);
+    }
+
+    // remove any existing element first
+    cx_kvl_map_remove(map, key, NULL);
+
+    // now reserve new memory in the map
+    void **map_data = kv_list->map_methods->put(map, key, NULL);
+    if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
+
+    // insert the data into the list (which most likely destroys the sorted property)
+    kv_list->list.base.collection.sorted = false;
+    void *node_data = kv_list->list_methods->insert_element(
+        &kv_list->list.base, kv_list->list.base.collection.size,
+        kv_list->list.base.collection.store_pointer ? &value : value);
+    if (node_data == NULL) { // LCOV_EXCL_START
+        // non-destructively remove the key again
+        kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+        return NULL;
+    } // LCOV_EXCL_STOP
+
+    // write the node pointer to the map entry
+    *map_data = node_data;
+
+    // copy the key to the node data
+    CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
+    *key_ptr = key;
+
+    // we must return node_data here and not map_data,
+    // because the node_data is the actual element of this collection
+    return node_data;
+}
+
 static void *cx_kvl_iter_current_entry(const void *it) {
     const CxMapIterator *iter = it;
     return (void*)&iter->entry;
@@ -456,7 +459,7 @@
     return iter->elem != NULL;
 }
 
-CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
+static CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
     CxMapIterator iter = {0};
 
     iter.type = type;
--- a/tests/test_kv_list.c	Sat Dec 06 15:55:53 2025 +0100
+++ b/tests/test_kv_list.c	Sat Dec 06 16:22:19 2025 +0100
@@ -339,6 +339,36 @@
     cxListFree(list);
 }
 
+CX_TEST(test_kv_list_map_put_overwrite) {
+    CxList *list = cxKvListCreateSimple(CX_STORE_POINTERS);
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *al = &talloc.base;
+    int *x, *y;
+    CX_TEST_DO {
+        CxMap *map = cxKvListAsMap(list);
+        cxDefineAdvancedDestructor(map, cxFree, al);
+        x = cxMalloc(al, sizeof(int));
+        y = cxMalloc(al, sizeof(int));
+        *x = 13;
+        *y = 37;
+        CX_TEST_ASSERT(0 == cxMapPut(map, "xyz", x));
+        CX_TEST_ASSERT(cxCollectionSize(map) == 1);
+        CX_TEST_ASSERT(cxCollectionSize(list) == 1);
+        CX_TEST_ASSERT(*(int*)cxListAt(list, 0) == 13);
+        CX_TEST_ASSERT(*(int*)cxMapGet(map, "xyz") == 13);
+        CX_TEST_ASSERT(0 == cxMapPut(map, "xyz", y));
+        CX_TEST_ASSERT(cxCollectionSize(map) == 1);
+        CX_TEST_ASSERT(cxCollectionSize(list) == 1);
+        CX_TEST_ASSERT(*(int*)cxListAt(list, 0) == 37);
+        CX_TEST_ASSERT(*(int*)cxMapGet(map, "xyz") == 37);
+        cxMapClear(map);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+    cxListFree(list);
+}
+
 CX_TEST(test_kv_list_map_remove) {
     CxList *list = cxKvListCreateSimple(sizeof(int));
     int x;
@@ -1021,6 +1051,7 @@
     cx_test_register(suite, test_kv_list_map_put);
     cx_test_register(suite, test_kv_list_map_put_ptr);
     cx_test_register(suite, test_kv_list_map_put_not_hashed);
+    cx_test_register(suite, test_kv_list_map_put_overwrite);
     cx_test_register(suite, test_kv_list_map_remove);
     cx_test_register(suite, test_kv_list_map_remove_and_get);
     cx_test_register(suite, test_kv_list_set_key);

mercurial