# HG changeset patch # User Mike Becker # Date 1765034539 -3600 # Node ID c8dd35f3ea5339970f02060ba5f13a535ac1b414 # Parent 7822ffe658488df98b9f5fc2349d313757e8166f fixes that overwriting elements with cxMapPut() in a kv-list did not work diff -r 7822ffe65848 -r c8dd35f3ea53 CHANGELOG --- 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 diff -r 7822ffe65848 -r c8dd35f3ea53 docs/Writerside/topics/about.md --- 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 diff -r 7822ffe65848 -r c8dd35f3ea53 src/kv_list.c --- 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; diff -r 7822ffe65848 -r c8dd35f3ea53 tests/test_kv_list.c --- 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);