Sat, 08 Nov 2025 23:45:19 +0100
implement simple versions of the clone functions
partially resolves #757 except for the tests
| CHANGELOG | file | annotate | diff | comparison | revisions | |
| docs/Writerside/topics/about.md | file | annotate | diff | comparison | revisions | |
| docs/Writerside/topics/list.h.md | file | annotate | diff | comparison | revisions | |
| docs/Writerside/topics/map.h.md | file | annotate | diff | comparison | revisions | |
| src/cx/list.h | file | annotate | diff | comparison | revisions | |
| src/cx/map.h | file | annotate | diff | comparison | revisions | |
| src/list.c | file | annotate | diff | comparison | revisions | |
| src/map.c | file | annotate | diff | comparison | revisions |
--- a/CHANGELOG Fri Nov 07 19:23:21 2025 +0100 +++ b/CHANGELOG Sat Nov 08 23:45:19 2025 +0100 @@ -6,10 +6,7 @@ * adds new key-value-based list implementation + adds support for integer keys to CxHashKey * adds support for comparing arbitrary strings without explicit call to cx_strcast() - * adds cxListClone() and cxMapClone() - * adds cxListUnion(), and cxMapUnion() - * adds cxListDifference(), cxMapDifference(), and cxMapListDifference() - * adds cxListIntersection(), cxMapIntersection(), and cxMapListIntersection() + * adds clone, union, difference, and intersection functions for CxList and CxMap * adds cxListContains() and cxMapContains() * adds cxListSet() * adds cxListFirst() and cxListLast()
--- a/docs/Writerside/topics/about.md Fri Nov 07 19:23:21 2025 +0100 +++ b/docs/Writerside/topics/about.md Sat Nov 08 23:45:19 2025 +0100 @@ -33,10 +33,7 @@ * adds new key-value-based list implementation * adds support for integer keys to CxHashKey * adds support for comparing arbitrary strings without explicit call to cx_strcast() -* adds cxListClone() and cxMapClone() -* adds cxListUnion(), and cxMapUnion() -* adds cxListDifference(), cxMapDifference(), and cxMapListDifference() -* adds cxListIntersection(), cxMapIntersection(), and cxMapListIntersection() +* adds clone, union, difference, and intersection functions for CxList and CxMap * adds cxListContains() and cxMapContains() * adds cxListSet() * adds cxListFirst() and cxListLast()
--- a/docs/Writerside/topics/list.h.md Fri Nov 07 19:23:21 2025 +0100 +++ b/docs/Writerside/topics/list.h.md Sat Nov 08 23:45:19 2025 +0100 @@ -369,23 +369,34 @@ const CxAllocator *clone_allocator, void *data); +int cxListCloneSimple(CxList *dst, const CxList *src); + int cxListDifference(CxList *dst, const CxList *minuend, const CxList *subtrahend, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); - + +int cxListDifferenceSimple(CxList *dst, + const CxList *minuend, const CxList *subtrahend); + int cxListIntersection(CxList *dst, const CxList *src, const CxList *other, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); - + +int cxListIntersectionSimple(CxList *dst, + const CxList *src, const CxList *other); + int cxListUnion(CxList *dst, const CxList *src, const CxList *other, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +int cxListUnionSimple(CxList *dst, + const CxList *src, const CxList *other); ``` With `cxListClone()` you can create deep copies of the elements in a list and insert them into another list. @@ -407,6 +418,10 @@ Refer to the documentation of the [clone-function callback](allocator.h.md#clone-function) to learn how to implement it. +The _simple_ versions of the above functions use an internal shallow clone function +which uses `memcpy()` to copy the elements. +If the destination map is storing pointers, this internal clone function uses the current default allocator to allocate the memory. + The functions return zero if and only if all clone operations were successful. > It is perfectly possible to clone items into a list of a different type.
--- a/docs/Writerside/topics/map.h.md Fri Nov 07 19:23:21 2025 +0100 +++ b/docs/Writerside/topics/map.h.md Sat Nov 08 23:45:19 2025 +0100 @@ -300,6 +300,8 @@ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +int cxMapCloneSimple(CxMap *dst, const CxMap *src); int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend, @@ -307,28 +309,42 @@ const CxAllocator *clone_allocator, void *data); +int cxMapDifferenceSimple(CxMap *dst, + const CxMap *minuend, const CxMap *subtrahend); + int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); +int cxMapListDifferenceSimple(CxMap *dst, + const CxMap *src, const CxList *keys); + int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); +int cxMapIntersectionSimple(CxMap *dst, + const CxMap *src, const CxMap *other); + int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); +int cxMapListIntersectionSimple(CxMap *dst, + const CxMap *src, const CxList *keys); + int cxMapUnion(CxMap *dst, const CxMap *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +int cxMapUnionSimple(CxMap *dst, const CxMap *src); ``` With `cxMapClone()` you can create deep copies of the values in one map and insert them into another map. @@ -350,6 +366,10 @@ Refer to the documentation of the [clone-function callback](allocator.h.md#clone-function) to learn how to implement it. +The _simple_ versions of the above functions use an internal shallow clone function +which uses `memcpy()` to copy the values. +If the destination map is storing pointers, this internal clone function uses the current default allocator to allocate the memory. + The functions return zero if and only if all clone operations were successful. > It is perfectly possible to clone items into a map of a different type.
--- a/src/cx/list.h Fri Nov 07 19:23:21 2025 +0100 +++ b/src/cx/list.h Sat Nov 08 23:45:19 2025 +0100 @@ -978,7 +978,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when all elements were successfully cloned * @retval non-zero when an allocation error occurred - * @see cxListUnion() + * @see cxListCloneSimple() */ cx_attr_nonnull_arg(1, 2, 3) CX_EXPORT int cxListClone(CxList *dst, const CxList *src, @@ -1001,6 +1001,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when the elements were successfully cloned * @retval non-zero when an allocation error occurred + * @see cxListDifferenceSimple() */ cx_attr_nonnull_arg(1, 2, 3, 4) CX_EXPORT int cxListDifference(CxList *dst, @@ -1024,6 +1025,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when the elements were successfully cloned * @retval non-zero when an allocation error occurred + * @see cxListIntersectionSimple() */ cx_attr_nonnull_arg(1, 2, 3, 4) CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other, @@ -1048,12 +1050,102 @@ * @param data optional additional data that is passed to the clone function * @retval zero when the elements were successfully cloned * @retval non-zero when an allocation error occurred - * @see cxListClone() + * @see cxListUnionSimple() */ cx_attr_nonnull_arg(1, 2, 3, 4) CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); +/** + * Performs a shallow clone of one list into another. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If the destination list already contains elements, the cloned elements + * are appended to that list. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination list also uses this + * destructor function. + * + * @param dst the destination list + * @param src the source list + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListClone() + */ +cx_attr_nonnull +CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src); + +/** + * Clones elements from a list only if they are not present in another list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If the @p minuend does not contain duplicates, this is equivalent to adding + * the set difference to the destination list. + * + * This function is optimized for the case when both the @p minuend and the + * @p subtrahend are sorted. + * + * @param dst the destination list + * @param minuend the list to subtract elements from + * @param subtrahend the elements that shall be subtracted + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListDifference() + */ +cx_attr_nonnull +CX_EXPORT int cxListDifferenceSimple(CxList *dst, + const CxList *minuend, const CxList *subtrahend); + +/** + * Clones elements from a list only if they are also present in another list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * + * If the destination list already contains elements, the intersection is appended + * to that list. + * + * @param dst the destination list + * @param src the list to clone the elements from + * @param other the list to check the elements for existence + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListIntersection() + */ +cx_attr_nonnull +CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other); + +/** + * Performs a deep clone of one list into another, skipping duplicates. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * In that case, the union will also be sorted. + * + * If the destination list already contains elements, the union is appended + * to that list. In that case the destination is not necessarily sorted. + * + * @param dst the destination list + * @param src the primary source list + * @param other the other list, where elements are only cloned from + * when they are not in @p src + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListUnion() + */ +cx_attr_nonnull +CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other); #ifdef __cplusplus } // extern "C"
--- a/src/cx/map.h Fri Nov 07 19:23:21 2025 +0100 +++ b/src/cx/map.h Sat Nov 08 23:45:19 2025 +0100 @@ -603,6 +603,123 @@ CX_EXPORT int cxMapUnion(CxMap *dst, const CxMap *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); +/** + * Performs a shallow clone of one map into another. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If the destination map already contains entries, the cloned entries + * are added to that map, possibly overwriting existing elements when + * the keys already exist. + * + * When elements in the destination map need to be replaced, any destructor + * function is called on the replaced elements before replacing them. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination map also uses this + * destructor function. + * + * @param dst the destination map + * @param src the source map + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxMapClone() + */ +cx_attr_nonnull +CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src); + +/** + * Clones entries of a map if their key is not present in another map. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * @param dst the destination map + * @param minuend the map to subtract the entries from + * @param subtrahend the map containing the elements to be subtracted + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend); + +/** + * Clones entries of a map if their key is not present in a list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * Note that the list must contain keys of type @c CxKey + * (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 + * @param src the source map + * @param keys the list of @c CxKey items + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxMapListDifference() + */ +cx_attr_nonnull +CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys); + + +/** + * Clones entries of a map only if their key is present in another map. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * @param dst the destination map + * @param src the map to clone the entries from + * @param other the map to check for existence of the keys + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other); + +/** + * Clones entries of a map only if their key is present in a list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * Note that the list must contain keys of type @c CxKey + * (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 + * @param src the source map + * @param keys the list of @c CxKey items + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys); + +/** + * Clones entries into a map if their key does not exist yet. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If you want to calculate the union of two maps into a fresh new map, + * you can proceed as follows: + * 1. Clone the first map into a fresh, empty map. + * 2. Use this function to clone the second map into the result from step 1. + * + * @param dst the destination map + * @param src the map to clone the entries from + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapUnionSimple(CxMap *dst, const CxMap *src); + #ifdef __cplusplus } // extern "C" #endif
--- a/src/list.c Fri Nov 07 19:23:21 2025 +0100 +++ b/src/list.c Sat Nov 08 23:45:19 2025 +0100 @@ -819,6 +819,15 @@ list->collection.advanced_destructor = destr2_bak; } +static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { + size_t elem_size = *(size_t*)data; + if (dst == NULL) dst = cxMalloc(al, elem_size); + if (dst != NULL) memcpy(dst, src, elem_size); + return dst; +} + +#define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size) + int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; @@ -1078,3 +1087,19 @@ return 0; } + +int cxListCloneSimple(CxList *dst, const CxList *src) { + return cxListClone(dst, src, use_simple_clone_func(src)); +} + +int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) { + return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +} + +int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) { + return cxListIntersection(dst, src, other, use_simple_clone_func(src)); +} + +int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) { + return cxListUnion(dst, src, other, use_simple_clone_func(src)); +}
--- a/src/map.c Fri Nov 07 19:23:21 2025 +0100 +++ b/src/map.c Sat Nov 08 23:45:19 2025 +0100 @@ -140,6 +140,15 @@ map->collection.advanced_destructor = destr2_bak; } +static void* cx_map_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { + size_t elem_size = *(size_t*)data; + if (dst == NULL) dst = cxMalloc(al, elem_size); + if (dst != NULL) memcpy(dst, src, elem_size); + return dst; +} + +#define use_simple_clone_func(map) cx_map_simple_clone_func, NULL, (void*)&((map)->collection.elem_size) + int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; @@ -293,3 +302,27 @@ } return 0; } + +int cxMapCloneSimple(CxMap *dst, const CxMap *src) { + return cxMapClone(dst, src, use_simple_clone_func(src)); +} + +int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) { + return cxMapDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +} + +int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListDifference(dst, src, keys, use_simple_clone_func(src)); +} + +int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other) { + return cxMapIntersection(dst, src, other, use_simple_clone_func(src)); +} + +int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListIntersection(dst, src, keys, use_simple_clone_func(src)); +} + +int cxMapUnionSimple(CxMap *dst, const CxMap *src) { + return cxMapUnion(dst, src, use_simple_clone_func(src)); +}