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