add cxJsonClone - resolves #785 except for the tests default tip

Sat, 13 Dec 2025 20:47:07 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 13 Dec 2025 20:47:07 +0100
changeset 1594
4b9537f93239
parent 1593
0df5128ab675

add cxJsonClone - resolves #785 except for the tests

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/json.h.md file | annotate | diff | comparison | revisions
src/cx/json.h file | annotate | diff | comparison | revisions
src/json.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Sat Dec 13 20:44:57 2025 +0100
+++ b/CHANGELOG	Sat Dec 13 20:47:07 2025 +0100
@@ -3,6 +3,7 @@
 
  * adds cx_system_page_size() to allocator.h
  * adds cxJsonFromString(), cxJsonToString(), and cxJsonToPrettyString()
+ * adds cxJsonClone()
  * adds cxJsonCompare()
  * adds cxMapCompare()
  * adds line continuation support to CxProperties / CxPropertiesConfig
--- a/docs/Writerside/topics/about.md	Sat Dec 13 20:44:57 2025 +0100
+++ b/docs/Writerside/topics/about.md	Sat Dec 13 20:47:07 2025 +0100
@@ -30,6 +30,7 @@
 
 * adds cx_system_page_size() to allocator.h
 * adds cxJsonFromString(), cxJsonToString(), and cxJsonToPrettyString()
+* adds cxJsonClone()
 * adds cxJsonCompare()
 * adds cxMapCompare()
 * adds line continuation support to CxProperties / CxPropertiesConfig
--- a/docs/Writerside/topics/json.h.md	Sat Dec 13 20:44:57 2025 +0100
+++ b/docs/Writerside/topics/json.h.md	Sat Dec 13 20:47:07 2025 +0100
@@ -340,6 +340,23 @@
 }
 ```
 
+## Clone Values
+
+```C
+#include <cx/json.h>
+
+CxJsonValue* cxJsonClone(const CxJsonValue* value,
+        const CxAllocator* allocator);
+
+CxJsonValue* cxJsonCloneFunc(
+        CxJsonValue* target, const CxJsonValue* source,
+        const CxAllocator* allocator, void *data);
+```
+
+The function `cxJsonClone()` creates a deep clone of the specified value using the specified allocator.
+The function `cxJsonCloneFunc()` is a `cx_clone_func` compatible version of the same function
+(the `data` argument is unused).
+
 ## Writer
 
 ```C
--- a/src/cx/json.h	Sat Dec 13 20:44:57 2025 +0100
+++ b/src/cx/json.h	Sat Dec 13 20:47:07 2025 +0100
@@ -296,6 +296,7 @@
      * The allocator used for produced JSON values.
      */
     const CxAllocator *allocator;
+
     /**
      * The input buffer.
      */
@@ -1376,6 +1377,54 @@
  */
 CX_EXPORT int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other);
 
+
+/**
+ * Creates a deep copy of the specified JSON value.
+ *
+ * If you need a @c cx_clone_func compatible version, see cxJsonCloneFunc().
+ *
+ * @note when you are cloning @c NULL, you will get a pointer to a statically
+ * allocated value which represents nothing.
+ *
+ * @param value the value to be cloned
+ * @param allocator the allocator for the new value
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonCloneFunc()
+ */
+cx_attr_nodiscard
+CX_EXPORT CxJsonValue* cxJsonClone(const CxJsonValue* value,
+        const CxAllocator* allocator);
+
+
+/**
+ * A @c cx_clone_func compatible version of cxJsonClone().
+ *
+ * Internal function - use cxJsonCloneFunc() to get a properly casted function pointer.
+ *
+ * @param target the target memory or @c NULL
+ * @param source the value to be cloned
+ * @param allocator the allocator for the new value
+ * @param data unused
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonClone()
+ */
+cx_attr_nodiscard
+CX_EXPORT CxJsonValue* cx_json_clone_func(
+        CxJsonValue* target, const CxJsonValue* source,
+        const CxAllocator* allocator, void *data);
+
+/**
+ * A @c cx_clone_func compatible version of cxJsonClone().
+ *
+ * @param target (@c CxJsonValue*) the target memory or @c NULL
+ * @param source (@c CxJsonValue*) the value to be cloned
+ * @param allocator (@c CxAllocator*) the allocator for the new value
+ * @param data unused
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonClone()
+ */
+#define cxJsonCloneFunc  ((cx_clone_func) cx_json_clone_func)
+
 #ifdef __cplusplus
 }
 #endif
--- a/src/json.c	Sat Dec 13 20:44:57 2025 +0100
+++ b/src/json.c	Sat Dec 13 20:47:07 2025 +0100
@@ -1482,3 +1482,78 @@
             // LCOV_EXCL_STOP
     }
 }
+
+CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) {
+    return cx_json_clone_func(NULL, value, allocator, NULL);
+}
+
+CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source,
+        const CxAllocator* allocator, cx_attr_unused void *data) {
+    if (source == NULL || source->type == CX_JSON_NOTHING) {
+        return &cx_json_value_nothing;
+    }
+
+#define return_value(v) { \
+        CxJsonValue *ret = v; \
+        if (target == NULL) { \
+            return ret; \
+        } else { \
+            *target = *ret; \
+            cxFree(allocator, ret); \
+            return target; \
+        } \
+    }
+
+    switch (source->type) {
+        case CX_JSON_OBJECT: {
+            CxJsonValue *obj = cxJsonCreateObj(allocator);
+            if (obj == NULL) return NULL; // LCOV_EXCL_LINE
+            if (cxMapClone(obj->object, source->object, cxJsonCloneFunc, allocator, NULL)) {
+                // LCOV_EXCL_START
+                cxJsonValueFree(obj);
+                return NULL;
+                // LCOV_EXCL_STOP
+            }
+            return obj;
+        }
+        case CX_JSON_ARRAY: {
+            const size_t elem_count = source->array.data_size;
+            CxArrayReallocator reallocator = cx_array_reallocator(allocator, NULL);
+            CxJsonValue *arr = cxJsonCreateArr(allocator);
+            if (arr == NULL) return NULL; // LCOV_EXCL_LINE
+            if (cx_array_simple_reserve_a(&reallocator, arr->array.data, elem_count)) {
+                // LCOV_EXCL_START
+                cxJsonValueFree(arr);
+                return NULL;
+                // LCOV_EXCL_STOP
+            }
+            arr->array.data_size = elem_count;
+            for (size_t i = 0 ; i < elem_count ; i++) {
+                CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL);
+                if (e == NULL) {
+                    // LCOV_EXCL_START
+                    cxJsonValueFree(arr);
+                    return NULL;
+                    // LCOV_EXCL_STOP
+                }
+                arr->array.data[i] = e;
+            }
+            return arr;
+        }
+        case CX_JSON_STRING:
+            return_value(cxJsonCreateString(allocator, source->string));
+        case CX_JSON_INTEGER:
+            return_value(cxJsonCreateNumber(allocator, source->integer));
+        case CX_JSON_NUMBER:
+            return_value(cxJsonCreateNumber(allocator, source->number));
+        case CX_JSON_LITERAL:
+            return_value(cxJsonCreateLiteral(allocator, source->literal));
+        default:
+            // LCOV_EXCL_START
+            // unreachable
+            assert(false);
+            return NULL;
+            // LCOV_EXCL_STOP
+    }
+#undef return_value
+}

mercurial