# HG changeset patch # User Mike Becker # Date 1765655227 -3600 # Node ID 4b9537f932396c7430cab602707da4f0accde6bb # Parent 0df5128ab675d906a1fb0e2a05ab99003d26a68c add cxJsonClone - resolves #785 except for the tests diff -r 0df5128ab675 -r 4b9537f93239 CHANGELOG --- 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 diff -r 0df5128ab675 -r 4b9537f93239 docs/Writerside/topics/about.md --- 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 diff -r 0df5128ab675 -r 4b9537f93239 docs/Writerside/topics/json.h.md --- 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 + +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 diff -r 0df5128ab675 -r 4b9537f93239 src/cx/json.h --- 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 diff -r 0df5128ab675 -r 4b9537f93239 src/json.c --- 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 +}