implement simple versions of the clone functions default tip

Sat, 08 Nov 2025 23:45:19 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 08 Nov 2025 23:45:19 +0100
changeset 1479
ac1baaed2fd7
parent 1478
bba2e5efdca0

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));
+}

mercurial