Sun, 26 Oct 2025 12:44:33 +0100
add tests for cxMapDifference() and cxMapListDifference()
resolves #746
| src/Makefile | file | annotate | diff | comparison | revisions | |
| src/cx/hash_key.h | file | annotate | diff | comparison | revisions | |
| src/cx/map.h | file | annotate | diff | comparison | revisions | |
| src/hash_key.c | file | annotate | diff | comparison | revisions | |
| tests/Makefile | file | annotate | diff | comparison | revisions | |
| tests/test_hash_map.c | file | annotate | diff | comparison | revisions |
--- a/src/Makefile Sun Oct 26 12:01:28 2025 +0100 +++ b/src/Makefile Sun Oct 26 12:44:33 2025 +0100 @@ -89,7 +89,7 @@ $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/hash_key$(OBJ_EXT): hash_key.c cx/hash_key.h cx/common.h \ - cx/string.h cx/allocator.h + cx/string.h cx/allocator.h cx/compare.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -127,7 +127,8 @@ $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/map$(OBJ_EXT): map.c cx/map.h cx/common.h cx/collection.h \ - cx/allocator.h cx/iterator.h cx/compare.h cx/string.h cx/hash_key.h + cx/allocator.h cx/iterator.h cx/compare.h cx/string.h cx/hash_key.h \ + cx/list.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- a/src/cx/hash_key.h Sun Oct 26 12:01:28 2025 +0100 +++ b/src/cx/hash_key.h Sun Oct 26 12:44:33 2025 +0100 @@ -227,12 +227,14 @@ /** * Compare function for hash keys. * - * @param left the first key - * @param right the second key + * The pointers are untyped to be compatible with the cx_compare_func signature. + * + * @param left (@c CxHashKey*) the first key + * @param right (@c CxHashKey*) the second key * @return zero when the keys equal, non-zero when they differ */ cx_attr_nodiscard cx_attr_nonnull -CX_EXPORT int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right); +CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right); #ifdef __cplusplus } // extern "C"
--- a/src/cx/map.h Sun Oct 26 12:01:28 2025 +0100 +++ b/src/cx/map.h Sun Oct 26 12:44:33 2025 +0100 @@ -528,7 +528,8 @@ * Clones entries of a map if their key is not present in a list. * * Note that the list must contain keys of type @c CxKey - * (or pointers to such keys). + * (or pointers to such keys) and must use @c cx_hash_key_cmp + * as the compare function. * Generic key types cannot be processed in this case. * * @param dst the destination map
--- a/src/hash_key.c Sun Oct 26 12:01:28 2025 +0100 +++ b/src/hash_key.c Sun Oct 26 12:44:33 2025 +0100 @@ -159,7 +159,9 @@ return key; } -int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) { +int cx_hash_key_cmp(const void *l, const void *r) { + const CxHashKey *left = l; + const CxHashKey *right = r; int d; d = cx_vcmp_uint64(left->hash, right->hash); if (d != 0) return d;
--- a/tests/Makefile Sun Oct 26 12:01:28 2025 +0100 +++ b/tests/Makefile Sun Oct 26 12:44:33 2025 +0100 @@ -77,9 +77,10 @@ $(TEST_DIR)/test_hash_map$(OBJ_EXT): test_hash_map.c ../src/cx/test.h \ ../src/cx/common.h util_allocator.h ../src/cx/allocator.h \ - ../src/cx/hash_map.h ../src/cx/map.h ../src/cx/collection.h \ + ../src/cx/array_list.h ../src/cx/list.h ../src/cx/collection.h \ ../src/cx/allocator.h ../src/cx/iterator.h ../src/cx/compare.h \ - ../src/cx/string.h ../src/cx/hash_key.h + ../src/cx/list.h ../src/cx/hash_map.h ../src/cx/map.h ../src/cx/string.h \ + ../src/cx/hash_key.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -I../src -c $<
--- a/tests/test_hash_map.c Sun Oct 26 12:01:28 2025 +0100 +++ b/tests/test_hash_map.c Sun Oct 26 12:44:33 2025 +0100 @@ -28,6 +28,8 @@ #include "cx/test.h" #include "util_allocator.h" +#include "cx/array_list.h" +#include "cx/list.h" #include "cx/hash_map.h" @@ -626,6 +628,97 @@ cx_testing_allocator_destroy(&talloc); } +CX_TEST(test_hash_map_difference) { + CxMap *dst = cxHashMapCreateSimple(sizeof(int)); + + CxMap *s1 = cxHashMapCreateSimple(CX_STORE_POINTERS); + CxMap *s2 = cxHashMapCreateSimple(CX_STORE_POINTERS); + const char *s1_keys[] = {"k1", "k2", "k3"}; + int s1_values[] = {1, 3, 4}; + const char *s2_keys[] = {"k4", "k2", "k5"}; + int s2_values[] = {7, 9, 15}; + for (unsigned int i = 0 ; i < 3 ; i++) { + cxMapPut(s1, s1_keys[i], &s1_values[i]); + cxMapPut(s2, s2_keys[i], &s2_values[i]); + } + CX_TEST_DO { + int c = 4; + test_hash_map_clone_func_max_clones = 100; // no limit + CX_TEST_ASSERT(0 == cxMapDifference(dst, s1, s2, test_hash_map_clone_func, NULL, &c)); + CX_TEST_ASSERT(cxMapSize(dst) == 2); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5); + CX_TEST_ASSERT(cxMapGet(dst, "k2") == NULL); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8); + } + cxMapFree(dst); + cxMapFree(s1); + cxMapFree(s2); +} + +CX_TEST(test_hash_map_list_difference) { + CxMap *dst = cxHashMapCreateSimple(sizeof(int)); + CxMap *src = cxHashMapCreateSimple(sizeof(int)); + CxList *keys = cxArrayListCreate(NULL, cx_hash_key_cmp, sizeof(CxHashKey), 4); + + const char *src_keys[] = {"k1", "k2", "k3"}; + int src_values[] = {1, 3, 4}; + for (unsigned int i = 0 ; i < 3 ; i++) { + cxMapPut(src, src_keys[i], &src_values[i]); + } + const char *k[] = {"k4", "k2", "k5"}; + for (unsigned int i = 0 ; i < 3 ; i++) { + CxHashKey key = CX_HASH_KEY(k[i]); + cxListAdd(keys, &key); + } + CX_TEST_DO { + int c = 4; + test_hash_map_clone_func_max_clones = 100; // no limit + CX_TEST_ASSERT(0 == cxMapListDifference(dst, src, keys, test_hash_map_clone_func, NULL, &c)); + CX_TEST_ASSERT(cxMapSize(dst) == 2); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5); + CX_TEST_ASSERT(cxMapGet(dst, "k2") == NULL); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8); + } + cxMapFree(dst); + cxMapFree(src); + cxListFree(keys); +} + +CX_TEST(test_hash_map_difference_ptr) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + CxAllocator *allocator = &talloc.base; + CxMap *dst = cxHashMapCreateSimple(CX_STORE_POINTERS); + cxDefineAdvancedDestructor(dst, cxFree, allocator); + + CxMap *s1 = cxHashMapCreateSimple(CX_STORE_POINTERS); + CxMap *s2 = cxHashMapCreateSimple(CX_STORE_POINTERS); + const char *s1_keys[] = {"k1", "k2", "k3"}; + int s1_values[] = {1, 3, 4}; + const char *s2_keys[] = {"k4", "k2", "k5"}; + int s2_values[] = {7, 9, 15}; + for (unsigned int i = 0 ; i < 3 ; i++) { + cxMapPut(s1, s1_keys[i], &s1_values[i]); + cxMapPut(s2, s2_keys[i], &s2_values[i]); + } + CX_TEST_DO { + int c = 4; + test_hash_map_clone_func_max_clones = 100; // no limit + CX_TEST_ASSERT(0 == cxMapDifference(dst, s1, s2, test_hash_map_clone_func, allocator, &c)); + CX_TEST_ASSERT(cxMapSize(dst) == 2); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5); + CX_TEST_ASSERT(cxMapGet(dst, "k2") == NULL); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8); + + cxMapClear(dst); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cxMapFree(dst); + cxMapFree(s1); + cxMapFree(s2); + cx_testing_allocator_destroy(&talloc); +} + CX_TEST(test_empty_map_size) { CX_TEST_DO { CX_TEST_ASSERT(cxEmptyMap->collection.size == 0); @@ -974,6 +1067,9 @@ cx_test_register(suite, test_hash_map_clone); cx_test_register(suite, test_hash_map_clone_alloc_fail); cx_test_register(suite, test_hash_map_clone_ptr); + cx_test_register(suite, test_hash_map_difference); + cx_test_register(suite, test_hash_map_difference_ptr); + cx_test_register(suite, test_hash_map_list_difference); cx_test_register(suite, test_empty_map_no_ops); cx_test_register(suite, test_empty_map_size); cx_test_register(suite, test_empty_map_get);