implement better strings for json.c + complete test coverage default tip

Tue, 25 Nov 2025 20:35:27 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 25 Nov 2025 20:35:27 +0100
changeset 1513
4d641c6a2f82
parent 1512
0dc866c7863b

implement better strings for json.c + complete test coverage

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
tests/test_json.c file | annotate | diff | comparison | revisions
--- a/docs/Writerside/topics/json.h.md	Mon Nov 24 22:39:18 2025 +0100
+++ b/docs/Writerside/topics/json.h.md	Tue Nov 25 20:35:27 2025 +0100
@@ -106,6 +106,8 @@
 
 CxJsonValue *cxJsonArrRemove(const CxJsonValue *value, size_t index);
 
+size_t cxJsonObjSize(const CxJsonValue *value);
+
 CxJsonValue *cxJsonObjGet(const CxJsonValue *value, AnyStr name);
 
 CxJsonValue *cxJsonObjRemove(const CxJsonValue *value, AnyStr name);
@@ -182,10 +184,7 @@
         const CxAllocator* allocator, int64_t num);
 
 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator,
-        const char *str);
-
-CxJsonValue* cxJsonCreateCxString(
-        const CxAllocator* allocator, cxstring str);
+        AnyStr str);
 
 CxJsonValue* cxJsonCreateLiteral(
         const CxAllocator* allocator, CxJsonLiteral lit);
@@ -208,26 +207,23 @@
 int cxJsonArrAddValues(CxJsonValue* arr,
         CxJsonValue* const* val, size_t count);
 
-int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+int cxJsonObjPut(CxJsonValue* obj, AnyStr name, CxJsonValue* child);
 
-CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
+CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, AnyStr name);
 
-CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
+CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, AnyStr name);
 
 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj,
-        cxstring name, double num);
+        AnyStr name, double num);
 
 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj,
-        cxstring name, int64_t num);
+        AnyStr name, int64_t num);
 
 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj,
-        cxstring name, const char* str);
-
-CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj,
-        cxstring name, cxstring str);
+        AnyStr name, AnyStr str);
 
 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj,
-        cxstring name, CxJsonLiteral lit);
+        AnyStr name, CxJsonLiteral lit);
 ```
 
 The `cxJsonCreateXY()`-family of functions can be used to create JSON values which are allocated by the specified `allocator`.
@@ -256,33 +252,33 @@
 ```C
 CxJsonValue *obj = cxJsonCreateObj(NULL);
 
-cxJsonObjPutLiteral(obj, CX_STR("bool"), CX_JSON_FALSE);
-cxJsonObjPutInteger(obj, CX_STR("int"), 47);
+cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE);
+cxJsonObjPutInteger(obj, "int", 47);
 
-CxJsonValue *strings = cxJsonObjPutArr(obj, CX_STR("strings"));
+CxJsonValue *strings = cxJsonObjPutArr(obj, "strings");
 cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2);
 
-CxJsonValue *nested = cxJsonObjPutObj(obj, CX_STR("nested"));
-CxJsonValue *objects = cxJsonObjPutArr(nested, CX_STR("objects"));
+CxJsonValue *nested = cxJsonObjPutObj(obj, "nested");
+CxJsonValue *objects = cxJsonObjPutArr(nested, "objects");
 CxJsonValue *obj_in_arr[2] = {
     cxJsonCreateObj(NULL),
     cxJsonCreateObj(NULL),
 };
-cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name1"), 1);
-cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name2"), 3);
+cxJsonObjPutInteger(obj_in_arr[0], "name1", 1);
+cxJsonObjPutInteger(obj_in_arr[0], "name2", 3);
 
-cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name2"), 7);
-cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name1"), 3);
+cxJsonObjPutInteger(obj_in_arr[1], "name2", 7);
+cxJsonObjPutInteger(obj_in_arr[1], "name1", 3);
 
 cxJsonArrAddValues(objects, obj_in_arr, 2);
 
-cxJsonArrAddNumbers(cxJsonObjPutArr(nested, CX_STR("floats")),
+cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"),
     (double[]){3.1415, 47.11, 8.15}, 3);
     
-cxJsonArrAddLiterals(cxJsonObjPutArr(nested, CX_STR("literals")),
+cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"),
     (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3);
  
-CxJsonValue *ints = cxJsonObjPutArr(nested, CX_STR("ints"));
+CxJsonValue *ints = cxJsonObjPutArr(nested, "ints");
 cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3);
 
 CxJsonValue *nested_array = cxJsonCreateArr(NULL);
--- a/src/cx/json.h	Mon Nov 24 22:39:18 2025 +0100
+++ b/src/cx/json.h	Tue Nov 25 20:35:27 2025 +0100
@@ -556,7 +556,7 @@
  * @retval non-zero internal allocation error
  * @see cxJsonFill()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
+cx_attr_nonnull_arg(1) cx_attr_access_r(2, 3)
 CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
 
 
@@ -641,28 +641,27 @@
 /**
  * Creates a new JSON string.
  *
+ * Internal function - use cxJsonCreateString() instead.
+ *
  * @param allocator the allocator to use
  * @param str the string data
  * @return the new JSON value or @c NULL if allocation fails
- * @see cxJsonCreateString()
  * @see cxJsonObjPutString()
- * @see cxJsonArrAddStrings()
+ * @see cxJsonArrAddCxStrings()
  */
-cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2)
-CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
+cx_attr_nodiscard
+CX_EXPORT CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
 
 /**
  * Creates a new JSON string.
  *
- * @param allocator the allocator to use
- * @param str the string data
- * @return the new JSON value or @c NULL if allocation fails
- * @see cxJsonCreateCxString()
- * @see cxJsonObjPutCxString()
+ * @param allocator (@c CxAllocator*) the allocator to use
+ * @param str the string
+ * @return (@c CxJsonValue*) the new JSON value or @c NULL if allocation fails
+ * @see cxJsonObjPutString()
  * @see cxJsonArrAddCxStrings()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
+#define cxJsonCreateString(allocator, str) cx_json_create_string(allocator, cx_strcast(str))
 
 /**
  * Creates a new JSON literal.
@@ -760,10 +759,7 @@
 /**
  * Adds or replaces a value within a JSON object.
  *
- * The value will be directly added and not copied.
- *
- * @note If a value with the specified @p name already exists,
- * it will be (recursively) freed with its own allocator.
+ * Internal function - use cxJsonObjPut().
  *
  * @param obj the JSON object
  * @param name the name of the value
@@ -772,11 +768,29 @@
  * @retval non-zero allocation failure
  */
 cx_attr_nonnull
-CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+CX_EXPORT int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+
+/**
+ * Adds or replaces a value within a JSON object.
+ *
+ * The value will be directly added and not copied.
+ *
+ * @note If a value with the specified @p name already exists,
+ * it will be (recursively) freed with its own allocator.
+ *
+ * @param obj (@c CxJsonValue*) the JSON object
+ * @param name (any string) the name of the value
+ * @param child (@c CxJsonValue*) the value
+ * @retval zero success
+ * @retval non-zero allocation failure
+ */
+#define cxJsonObjPut(obj, name, child) cx_json_obj_put(obj, cx_strcast(name), child)
 
 /**
  * Creates a new JSON object and adds it to an existing object.
  *
+ * Internal function - use cxJsonObjPutObj().
+ *
  * @param obj the target JSON object
  * @param name the name of the new value
  * @return the new value or @c NULL if allocation fails
@@ -784,11 +798,24 @@
  * @see cxJsonCreateObj()
  */
 cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
+
+/**
+ * Creates a new JSON object and adds it to an existing object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateObj()
+ */
+#define cxJsonObjPutObj(obj, name) cx_json_obj_put_obj(obj, cx_strcast(name))
 
 /**
  * Creates a new JSON array and adds it to an object.
  *
+ * Internal function - use cxJsonObjPutArr().
+ *
  * @param obj the target JSON object
  * @param name the name of the new value
  * @return the new value or @c NULL if allocation fails
@@ -796,11 +823,24 @@
  * @see cxJsonCreateArr()
  */
 cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name);
+
+/**
+ * Creates a new JSON array and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateArr()
+ */
+#define cxJsonObjPutArr(obj, name) cx_json_obj_put_arr(obj, cx_strcast(name))
 
 /**
  * Creates a new JSON number and adds it to an object.
  *
+ * Internal function - use cxJsonObjPutNumber().
+ *
  * @param obj the target JSON object
  * @param name the name of the new value
  * @param num the numeric value
@@ -809,11 +849,25 @@
  * @see cxJsonCreateNumber()
  */
 cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
+CX_EXPORT CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
+
+/**
+ * Creates a new JSON number and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param num (@c double) the numeric value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateNumber()
+ */
+#define cxJsonObjPutNumber(obj, name, num) cx_json_obj_put_number(obj, cx_strcast(name), num)
 
 /**
  * Creates a new JSON number, based on an integer, and adds it to an object.
  *
+ * Internal function - use cxJsonObjPutInteger().
+ *
  * @param obj the target JSON object
  * @param name the name of the new value
  * @param num the numeric value
@@ -822,12 +876,24 @@
  * @see cxJsonCreateInteger()
  */
 cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
+CX_EXPORT CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
+
+/**
+ * Creates a new JSON number, based on an integer, and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param num (@c int64_t) the numeric value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateInteger()
+ */
+#define cxJsonObjPutInteger(obj, name, num) cx_json_obj_put_integer(obj, cx_strcast(name), num)
 
 /**
  * Creates a new JSON string and adds it to an object.
  *
- * The string data is copied.
+ * Internal function - use cxJsonObjPutString()
  *
  * @param obj the target JSON object
  * @param name the name of the new value
@@ -836,27 +902,28 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateString()
  */
-cx_attr_nonnull cx_attr_cstr_arg(3)
-CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
+cx_attr_nonnull
+CX_EXPORT CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
 
 /**
  * Creates a new JSON string and adds it to an object.
  *
  * The string data is copied.
  *
- * @param obj the target JSON object
- * @param name the name of the new value
- * @param str the string data
- * @return the new value or @c NULL if allocation fails
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param str (any string) the string data
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
  * @see cxJsonObjPut()
- * @see cxJsonCreateCxString()
+ * @see cxJsonCreateString()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
+#define cxJsonObjPutString(obj, name, str) cx_json_obj_put_string(obj, cx_strcast(name), cx_strcast(str))
 
 /**
  * Creates a new JSON literal and adds it to an object.
  *
+ * Internal function - use cxJsonObjPutLiteral().
+ *
  * @param obj the target JSON object
  * @param name the name of the new value
  * @param lit the type of literal
@@ -865,7 +932,19 @@
  * @see cxJsonCreateLiteral()
  */
 cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+CX_EXPORT CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+
+/**
+ * Creates a new JSON literal and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param lit (@c CxJsonLiteral) the type of literal
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateLiteral()
+ */
+#define cxJsonObjPutLiteral(obj, name, lit) cx_json_obj_put_literal(obj, cx_strcast(name), lit)
 
 /**
  * Recursively deallocates the memory of a JSON value.
@@ -1188,6 +1267,20 @@
 CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
 
 /**
+ * Returns the size of a JSON object.
+ *
+ * If the @p value is not a JSON object, the behavior is undefined.
+ *
+ * @param value the JSON value
+ * @return the size of the object, i.e., the number of key/value pairs
+ * @see cxJsonIsObject()
+ */
+cx_attr_nonnull
+CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
+    return value->value.object.values_size;
+}
+
+/**
  * Returns an iterator over the JSON object members.
  *
  * The iterator yields values of type @c CxJsonObjValue* which
--- a/src/json.c	Mon Nov 24 22:39:18 2025 +0100
+++ b/src/json.c	Tue Nov 25 20:35:27 2025 +0100
@@ -94,7 +94,7 @@
     size_t newcap = obj->values_capacity;
     if (newcap > oldcap) {
         if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) {
-            return 1;
+            return 1; // LCOV_EXCL_LINE
         }
     }
 
@@ -437,7 +437,7 @@
             } else if (c == 'u') {
                 char utf8buf[4];
                 unsigned utf8len = unescape_unicode_string(
-                    cx_strn(str.ptr + i - 1, str.length + 1 - i),
+                    cx_strn(str.ptr + i - 1, str.length - i),
                     utf8buf
                 );
                 if(utf8len > 0) {
@@ -456,7 +456,7 @@
             } else {
                 // TODO: discuss the behavior for unrecognized escape sequences
                 //       most parsers throw an error here - we just ignore it
-                result.ptr[result.length++] = '\\';
+                result.ptr[result.length++] = '\\'; // LCOV_EXCL_LINE
             }
 
             result.ptr[result.length++] = c;
@@ -640,6 +640,7 @@
     if (cxBufferEof(&json->buffer)) {
         // reinitialize the buffer
         cxBufferDestroy(&json->buffer);
+        if (buf == NULL) buf = ""; // buffer must not be initialized with NULL
         cxBufferInit(&json->buffer, (char*) buf, size,
             NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
         json->buffer.size = size;
@@ -734,7 +735,8 @@
                     }
                 } else {
                     if (cx_strtod(token.content, &vbuf->value.number)) {
-                        return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
+                        // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
+                        return_rec(CX_JSON_FORMAT_ERROR_NUMBER);  // LCOV_EXCL_LINE
                     }
                 }
                 return_rec(CX_JSON_NO_ERROR);
@@ -815,19 +817,19 @@
     } else {
         // should be unreachable
         assert(false);
-        return_rec(-1);
+        return_rec(-1); // LCOV_EXCL_LINE
     }
 }
 
 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
-    // check if buffer has been filled
+    // initialize output value
+    *value = &cx_json_value_nothing;
+
+    // check if the buffer has been filled
     if (json->buffer.space == NULL) {
         return CX_JSON_NULL_DATA;
     }
 
-    // initialize output value
-    *value = &cx_json_value_nothing;
-
     // parse data
     CxJsonStatus result;
     do {
@@ -943,11 +945,7 @@
     return v;
 }
 
-CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) {
-    return cxJsonCreateCxString(allocator, cx_str(str));
-}
-
-CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) {
+CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) {
     if (allocator == NULL) allocator = cxDefaultAllocator;
     CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
     if (v == NULL) return NULL;
@@ -1020,7 +1018,7 @@
     CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
     if (values == NULL) return -1;
     for (size_t i = 0; i < count; i++) {
-        values[i] = cxJsonCreateCxString(arr->allocator, str[i]);
+        values[i] = cxJsonCreateString(arr->allocator, str[i]);
         if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
     }
     int ret = cxJsonArrAddValues(arr, values, count);
@@ -1050,61 +1048,56 @@
     );
 }
 
-int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
+int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
     cxmutstr k = cx_strdup_a(obj->allocator, name);
     if (k.ptr == NULL) return -1;
     CxJsonObjValue kv = {k, child};
     if (json_add_objvalue(obj, kv)) {
+        // LCOV_EXCL_START
         cx_strfree_a(obj->allocator, &k);
         return 1;
+        // LCOV_EXCL_STOP
     } else {
         return 0;
     }
 }
 
-CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) {
+CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
     CxJsonValue* v = cxJsonCreateObj(obj->allocator);
     if (v == NULL) return NULL;
     if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
     return v;
 }
 
-CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name) {
+CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name) {
     CxJsonValue* v = cxJsonCreateArr(obj->allocator);
     if (v == NULL) return NULL;
     if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
     return v;
 }
 
-CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num) {
+CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num) {
     CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num);
     if (v == NULL) return NULL;
     if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
     return v;
 }
 
-CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num) {
+CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num) {
     CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num);
     if (v == NULL) return NULL;
     if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
     return v;
 }
 
-CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str) {
+CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str) {
     CxJsonValue* v = cxJsonCreateString(obj->allocator, str);
     if (v == NULL) return NULL;
     if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
     return v;
 }
 
-CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) {
-    CxJsonValue* v = cxJsonCreateCxString(obj->allocator, str);
-    if (v == NULL) return NULL;
-    if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
-    return v;
-}
-
-CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
+CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
     CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit);
     if (v == NULL) return NULL;
     if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;}
@@ -1286,9 +1279,6 @@
                                       ? look_idx
                                       : value->value.object.indices[look_idx];
                 CxJsonObjValue *member = &value->value.object.values[elem_idx];
-                if (settings->sort_members) {
-                    depth++;depth--;
-                }
 
                 // possible indentation
                 if (settings->pretty) {
@@ -1350,7 +1340,9 @@
                 if (cx_json_write_rec(
                         target, element,
                         wfunc, settings, depth)
-                ) return 1;
+                ) {
+                    return 1; // LCOV_EXCL_LINE
+                }
 
                 if (iter.index < iter.elem_count - 1) {
                     const char *arr_value_sep = ", ";
--- a/tests/test_json.c	Mon Nov 24 22:39:18 2025 +0100
+++ b/tests/test_json.c	Tue Nov 25 20:35:27 2025 +0100
@@ -78,7 +78,7 @@
         CX_TEST_ASSERT(cxJsonIsString(message));
         CX_TEST_ASSERT(0 == cx_strcmp(
                 cxJsonAsCxString(message),
-                cx_str("success"))
+                "success")
         );
 
         CxJsonValue *position = cxJsonObjGet(obj, "position");
@@ -117,6 +117,32 @@
     }
 }
 
+CX_TEST(test_json_large_object) {
+    CxJsonValue *obj = cxJsonCreateObj(NULL);
+    CX_TEST_DO {
+        cxJsonObjPutString(obj, "mystring", "test");
+        char buf[10];
+        for (unsigned i = 0 ; i < 300 ; i++) {
+            sprintf(buf, "key %d", i);
+            cxJsonObjPutInteger(obj, buf, i);
+        }
+        CX_TEST_ASSERT(301 == cxJsonObjSize(obj));
+        // some samples
+        CxJsonValue *v;
+        v = cxJsonObjGet(obj, "key 64");
+        CX_TEST_ASSERT(cxJsonIsInteger(v));
+        CX_TEST_ASSERT(cxJsonAsInteger(v) == 64);
+        v = cxJsonObjGet(obj, "key 228");
+        CX_TEST_ASSERT(cxJsonIsInteger(v));
+        CX_TEST_ASSERT(cxJsonAsInteger(v) == 228);
+
+        v = cxJsonObjGet(obj, "mystring");
+        CX_TEST_ASSERT(cxJsonIsString(v));
+        CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxMutStr(v), "test"));
+    }
+    cxJsonValueFree(obj);
+}
+
 CX_TEST(test_json_escaped_strings) {
     cxstring text = cx_str(
             "{\n"
@@ -137,13 +163,13 @@
         CX_TEST_ASSERT(cxJsonIsString(object));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(object),
-            CX_STR("{\n\t\"object\":null\n}"))
+            "{\n\t\"object\":null\n}")
         );
         CxJsonValue *ctrl = cxJsonObjGet(obj, "ctrl-chars");
         CX_TEST_ASSERT(cxJsonIsString(ctrl));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(ctrl),
-            CX_STR("\\foo\r\nbar\f*ring/ring*\b"))
+            "\\foo\r\nbar\f*ring/ring*\b")
         );
         cxJsonValueFree(obj);
     }
@@ -176,39 +202,39 @@
         CX_TEST_ASSERT(cxJsonIsString(ascii));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(ascii),
-            CX_STR("ASCII"))
+            "ASCII")
         );
         
         CxJsonValue *unicode = cxJsonObjGet(obj, "unicode");
         CX_TEST_ASSERT(cxJsonIsString(unicode));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(unicode),
-            CX_STR("ßß"))
+            "ßß")
         );
         
         CxJsonValue *mixed = cxJsonObjGet(obj, "mixed");
         CX_TEST_ASSERT(cxJsonIsString(mixed));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(mixed),
-            CX_STR("mixed ä ö ä ö"))
+            "mixed ä ö ä ö")
         );
         
         CxJsonValue *wide = cxJsonObjGet(obj, "wide");
         CX_TEST_ASSERT(cxJsonIsString(wide));
-        CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxString(wide), CX_STR("Σ⦰")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxString(wide), "Σ⦰"));
         
         CxJsonValue *surrogatepair1 = cxJsonObjGet(obj, "surrogatepair1");
         CX_TEST_ASSERT(cxJsonIsString(surrogatepair1));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(surrogatepair1),
-            CX_STR("\xf0\x9f\xaf\xb5"))
+            "\xf0\x9f\xaf\xb5")
         );
         
         CxJsonValue *surrogatepair2 = cxJsonObjGet(obj, "surrogatepair2");
         CX_TEST_ASSERT(cxJsonIsString(surrogatepair2));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(surrogatepair2),
-            CX_STR("test\xf0\x9f\xaf\xb1" "AA"))
+            "test\xf0\x9f\xaf\xb1" "AA")
         );
         
         CxJsonValue *mixed2 = cxJsonObjGet(obj, "mixed2");
@@ -217,7 +243,7 @@
        CX_TEST_ASSERT(cxJsonIsString(mixed2));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(mixed2),
-            CX_STR("123\xce\xa3\xf0\x9f\xaf\x85ß"))
+            "123\xce\xa3\xf0\x9f\xaf\x85ß")
         );
         
         cxJsonValueFree(obj);
@@ -231,22 +257,22 @@
     CxJsonValue *obj;
     CxJsonStatus result;
     CX_TEST_DO {
-        cxJsonFill(&json, "\"too few \\u123 digits\"");
+        cxJsonFill(&json, "\"too few digits \\u123\"");
         result = cxJsonNext(&json, &obj);
         CX_TEST_ASSERT(result == CX_JSON_NO_ERROR);
         CX_TEST_ASSERT(cxJsonIsString(obj));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(obj),
-            CX_STR("too few \\u123 digits")
+            "too few digits \\u123"
         ));
         cxJsonValueFree(obj);
-        cxJsonFill(&json, "\"too many \\u00E456 digits\"");
+        cxJsonFill(&json, "\"too many digits \\u00E456\"");
         result = cxJsonNext(&json, &obj);
         CX_TEST_ASSERT(result == CX_JSON_NO_ERROR);
         CX_TEST_ASSERT(cxJsonIsString(obj));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(obj),
-            CX_STR("too many ä56 digits")
+            "too many digits ä56"
         ));
         cxJsonValueFree(obj);
         cxJsonFill(&json, "\"only high \\uD800 surrogate\"");
@@ -255,7 +281,7 @@
         CX_TEST_ASSERT(cxJsonIsString(obj));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(obj),
-            CX_STR("only high \\uD800 surrogate")
+            "only high \\uD800 surrogate"
         ));
         cxJsonValueFree(obj);
         cxJsonFill(&json, "\"only low \\uDC00 surrogate\"");
@@ -264,7 +290,7 @@
         CX_TEST_ASSERT(cxJsonIsString(obj));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(obj),
-            CX_STR("only low \\uDC00 surrogate")
+            "only low \\uDC00 surrogate"
         ));
         cxJsonValueFree(obj);
         cxJsonFill(&json, "\"two high \\uD800\\uD800 surrogates\"");
@@ -273,7 +299,7 @@
         CX_TEST_ASSERT(cxJsonIsString(obj));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(obj),
-            CX_STR("two high \\uD800\\uD800 surrogates")
+            "two high \\uD800\\uD800 surrogates"
         ));
         cxJsonValueFree(obj);
         cxJsonFill(&json, "\"high plus bullshit \\uD800\\u567 foo\"");
@@ -282,7 +308,7 @@
         CX_TEST_ASSERT(cxJsonIsString(obj));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(obj),
-            CX_STR("high plus bullshit \\uD800\\u567 foo")
+            "high plus bullshit \\uD800\\u567 foo"
         ));
         cxJsonValueFree(obj);
     }
@@ -301,7 +327,7 @@
         CX_TEST_ASSERT(cxJsonIsString(val));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(val),
-            cx_str("a \"test\" string"))
+            "a \"test\" string")
         );
         cxJsonValueFree(val);
 
@@ -315,7 +341,7 @@
         CX_TEST_ASSERT(cxJsonIsString(val));
         CX_TEST_ASSERT(0 == cx_strcmp(
             cxJsonAsCxString(val),
-            cx_str("a \"test\" string"))
+            "a \"test\" string")
         );
         cxJsonValueFree(val);
     }
@@ -354,7 +380,7 @@
         CX_TEST_ASSERT(cxJsonIsString(message));
         CX_TEST_ASSERT(0 == cx_strcmp(
                 cxJsonAsCxString(message),
-                cx_str("success"))
+                "success")
         );
         CxJsonValue *timestamp = cxJsonObjGet(obj, "__timestamp");
         CX_TEST_ASSERT(message->type == CX_JSON_STRING);
@@ -402,6 +428,55 @@
     cx_testing_allocator_destroy(&talloc);
 }
 
+CX_TEST(test_json_parenthesis_mismatch) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    const CxAllocator *alloc = &talloc.base;
+
+    CX_TEST_DO {
+        CxJson json;
+        cxJsonInit(&json, alloc);
+
+        CxJsonStatus result;
+        CxJsonValue *obj;
+
+        cxJsonFill(&json, "[0, 1, 2, 3}");
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+
+        cxJsonReset(&json);
+        cxJsonFill(&json, "{\"test\": 42]");
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+
+        cxJsonDestroy(&json);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
+CX_TEST(test_json_object_name_is_no_string) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    const CxAllocator *alloc = &talloc.base;
+
+    CX_TEST_DO {
+        CxJson json;
+        cxJsonInit(&json, alloc);
+
+        CxJsonStatus result;
+        CxJsonValue *obj;
+
+        cxJsonFill(&json, "{42: \"test\"}");
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+
+        cxJsonDestroy(&json);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
 CX_TEST(test_json_subsequent_fill) {
     cxstring text = cx_str(
             "{\"message\":\"success\"  ,     \"__timestamp\":1729348561}");
@@ -424,7 +499,7 @@
         CX_TEST_ASSERT(cxJsonIsString(message));
         CX_TEST_ASSERT(0 == cx_strcmp(
                 cxJsonAsCxString(message),
-                cx_str("success"))
+                "success")
         );
         CxJsonValue *timestamp = cxJsonObjGet(obj, "__timestamp");
         CX_TEST_ASSERT(message->type == CX_JSON_STRING);
@@ -439,6 +514,63 @@
     }
 }
 
+CX_TEST(test_json_no_fill) {
+    CxJson json;
+    cxJsonInit(&json, NULL);
+    CX_TEST_DO {
+        CxJsonValue *obj = NULL;
+        CxJsonStatus result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_NULL_DATA);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+    }
+    cxJsonDestroy(&json);
+}
+
+CX_TEST(test_json_null_fill) {
+    CxJson json;
+    cxJsonInit(&json, NULL);
+    CX_TEST_DO {
+        CxJsonStatus result;
+        CxJsonValue *obj = NULL;
+        cxstring nullstr = cx_strn(NULL, 0);
+        cxJsonFill(&json, nullstr);
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_NO_DATA);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+        obj = NULL;
+
+        cxJsonFill(&json, "[0, 1");
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+        obj = NULL;
+
+        cxJsonFill(&json, nullstr);
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+        obj = NULL;
+
+        cxJsonFill(&json, ", 2]");
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_NO_ERROR);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(cxJsonIsArray(obj));
+        cxJsonValueFree(obj);
+
+        cxJsonFill(&json, nullstr);
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == CX_JSON_NO_DATA);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+    }
+    cxJsonDestroy(&json);
+}
+
 CX_TEST(test_json_object_error) {
     cxstring text0 = cx_str(
             "{\n"
@@ -489,7 +621,7 @@
     CX_TEST_DO {
         CxJson json;
         cxJsonInit(&json, alloc);
-        cxJsonFill(&json, cx_str(
+        cxJsonFill(&json,
             "{\n"
             "\t\"message\":\"success\",\n"
             "\t\"data\":{\n"
@@ -499,7 +631,7 @@
             "\t},\n"
             "\t\"timestamp\":1729348561\n"
             "}"
-        ));
+        );
         CxJsonValue *obj;
         CX_TEST_ASSERT(CX_JSON_NO_ERROR == cxJsonNext(&json, &obj));
         cxJsonDestroy(&json);
@@ -704,7 +836,7 @@
         result = cxJsonNext(&json, &v);
         CX_TEST_ASSERT(result == CX_JSON_NO_ERROR);
         CX_TEST_ASSERT(cxJsonIsString(v));
-        CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v), CX_STR("hello world")));
+        CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v), "hello world"));
         cxJsonValueFree(v);
         // don't process the remaining newline this time
         // read obj
@@ -904,24 +1036,24 @@
 
         // add the members
         {
-            cxJsonObjPutLiteral(obj, CX_STR("bool"), CX_JSON_FALSE);
-            cxJsonObjPutInteger(obj, CX_STR("int"), 47);
-            CxJsonValue *strings = cxJsonObjPutArr(obj, CX_STR("strings"));
+            cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE);
+            cxJsonObjPutInteger(obj, "int", 47);
+            CxJsonValue *strings = cxJsonObjPutArr(obj, "strings");
             CX_TEST_ASSERT(strings != NULL);
             CX_TEST_ASSERT(cxJsonIsArray(strings));
             const char* str[] = {"hello", "world"};
             CX_TEST_ASSERT(0 == cxJsonArrAddStrings(strings, str, 2));
 
-            CxJsonValue *nested = cxJsonObjPutObj(obj, CX_STR("nested"));
+            CxJsonValue *nested = cxJsonObjPutObj(obj, "nested");
             CX_TEST_ASSERT(nested != NULL);
             CX_TEST_ASSERT(cxJsonIsObject(nested));
-            cxJsonObjPutCxString(nested, CX_STR("string"), CX_STR("test"));
+            cxJsonObjPutString(nested, "string", "test");
 
-            cxJsonArrAddNumbers(cxJsonObjPutArr(nested, CX_STR("floats")),
+            cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"),
                 (double[]){3.1415, 47.11, 8.15}, 3);
-            cxJsonArrAddIntegers(cxJsonObjPutArr(nested, CX_STR("ints")),
+            cxJsonArrAddIntegers(cxJsonObjPutArr(nested, "ints"),
                 (int64_t[]){4, 8, 15, 16, 23, 42}, 6);
-            cxJsonArrAddLiterals(cxJsonObjPutArr(nested, CX_STR("literals")),
+            cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"),
                 (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3);
         }
 
@@ -932,8 +1064,8 @@
             CxJsonValue *strings = cxJsonObjGet(obj, "strings");
             CX_TEST_ASSERT(cxJsonIsArray(strings));
             CX_TEST_ASSERT(2 == cxJsonArrSize(strings));
-            CX_TEST_ASSERT(0 == cx_strcmp(CX_STR("hello"), cxJsonAsCxString(cxJsonArrGet(strings, 0))));
-            CX_TEST_ASSERT(0 == cx_strcmp(CX_STR("world"), cxJsonAsCxString(cxJsonArrGet(strings, 1))));
+            CX_TEST_ASSERT(0 == cx_strcmp("hello", cxJsonAsString(cxJsonArrGet(strings, 0))));
+            CX_TEST_ASSERT(0 == cx_strcmp("world", cxJsonAsString(cxJsonArrGet(strings, 1))));
 
             CxJsonValue *nested = cxJsonObjGet(obj, "nested");
             CX_TEST_ASSERT(cxJsonIsObject(nested));
@@ -968,6 +1100,43 @@
     cx_testing_allocator_destroy(&talloc);
 }
 
+CX_TEST(test_json_overwrite_value) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *allocator = &talloc.base;
+    CX_TEST_DO {
+        CxJsonValue *obj = cxJsonCreateObj(allocator);
+
+        // put some values
+        cxJsonObjPutInteger(obj, "test1", 1);
+        cxJsonObjPutInteger(obj, "test2", 2);
+        cxJsonObjPutInteger(obj, "test3", 3);
+
+        // overwrite test2
+        cxJsonObjPutInteger(obj, "test2", 0);
+
+        // verify the values
+        CxIterator iter = cxJsonObjIter(obj);
+        bool found[5] = {0};
+        cx_foreach(CxJsonObjValue *, ov, iter) {
+            CxJsonValue *v = ov->value;
+            CX_TEST_ASSERT(cxJsonIsInteger(v));
+            int64_t i = cxJsonAsInteger(v);
+            CX_TEST_ASSERT(i >= 0 && i <= 4);
+            found[i] = true;
+        }
+        CX_TEST_ASSERT(found[0]);
+        CX_TEST_ASSERT(found[1]);
+        CX_TEST_ASSERT(!found[2]);
+        CX_TEST_ASSERT(found[3]);
+        CX_TEST_ASSERT(!found[4]);
+
+        // destroy the value and verify the allocations
+        cxJsonValueFree(obj);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
 
 CX_TEST_SUBROUTINE(test_json_write_sub,
         const CxAllocator *allocator,
@@ -976,23 +1145,23 @@
 ) {
     // create the value
     CxJsonValue *obj = cxJsonCreateObj(allocator);
-    cxJsonObjPutLiteral(obj, CX_STR("bool"), CX_JSON_FALSE);
-    cxJsonObjPutNumber(obj, CX_STR("int"), 47); // purposely use PutNumber to put an int
-    CxJsonValue *strings = cxJsonObjPutArr(obj, CX_STR("strings"));
+    cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE);
+    cxJsonObjPutNumber(obj, "int", 47); // purposely use PutNumber to put an int
+    CxJsonValue *strings = cxJsonObjPutArr(obj, "strings");
     cxJsonArrAddCxStrings(strings, (cxstring[]) {CX_STR("hello"), CX_STR("world")}, 2);
-    CxJsonValue *nested = cxJsonObjPutObj(obj, CX_STR("nested"));
-    CxJsonValue *objects = cxJsonObjPutArr(nested, CX_STR("objects"));
+    CxJsonValue *nested = cxJsonObjPutObj(obj, "nested");
+    CxJsonValue *objects = cxJsonObjPutArr(nested, "objects");
     CxJsonValue *obj_in_arr[2] = {cxJsonCreateObj(allocator), cxJsonCreateObj(allocator)};
-    cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name1"), 1);
-    cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name2"), 3);
-    cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name2"), 7);
-    cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name1"), 3);
+    cxJsonObjPutInteger(obj_in_arr[0], "name1", 1);
+    cxJsonObjPutInteger(obj_in_arr[0], "name2", 3);
+    cxJsonObjPutInteger(obj_in_arr[1], "name2", 7);
+    cxJsonObjPutInteger(obj_in_arr[1], "name1", 3);
     cxJsonArrAddValues(objects, obj_in_arr, 2);
-    cxJsonArrAddNumbers(cxJsonObjPutArr(nested, CX_STR("floats")),
+    cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"),
                         (double[]){3.1415, 47.11, 8.15}, 3);
-    cxJsonArrAddLiterals(cxJsonObjPutArr(nested, CX_STR("literals")),
+    cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"),
                          (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3);
-    CxJsonValue *ints = cxJsonObjPutArr(nested, CX_STR("ints"));
+    CxJsonValue *ints = cxJsonObjPutArr(nested, "ints");
     cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3);
     CxJsonValue *nested_array = cxJsonCreateArr(allocator);
     cxJsonArrAddValues(ints, &nested_array, 1);
@@ -1020,7 +1189,7 @@
     CxAllocator *allocator = &talloc.base;
     CX_TEST_DO {
         // expected value
-        cxstring expected = CX_STR(
+        cxstring expected = cx_str(
 "{\"bool\":false,"
 "\"int\":47,"
 "\"nested\":{"
@@ -1041,6 +1210,8 @@
 
         CxJsonWriter writer = cxJsonWriterCompact();
         CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer);
+        // try again, but this time with implicitly defaulted writer
+        CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, NULL);
         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
     }
     cx_testing_allocator_destroy(&talloc);
@@ -1051,7 +1222,7 @@
     cx_testing_allocator_init(&talloc);
     CxAllocator *allocator = &talloc.base;
     CX_TEST_DO {
-        cxstring expected = CX_STR(
+        cxstring expected = cx_str(
 "{\n"
 "    \"bool\": false,\n"
 "    \"int\": 47,\n"
@@ -1083,7 +1254,7 @@
     cx_testing_allocator_init(&talloc);
     CxAllocator *allocator = &talloc.base;
     CX_TEST_DO {
-        cxstring expected = CX_STR(
+        cxstring expected = cx_str(
 "{\n"
 "\t\"bool\": false,\n"
 "\t\"int\": 47,\n"
@@ -1114,7 +1285,7 @@
     cx_testing_allocator_init(&talloc);
     CxAllocator *allocator = &talloc.base;
     CX_TEST_DO {
-        cxstring expected = CX_STR(
+        cxstring expected = cx_str(
 "{\n"
 "    \"bool\": false,\n"
 "    \"int\": 47,\n"
@@ -1142,6 +1313,54 @@
     cx_testing_allocator_destroy(&talloc);
 }
 
+CX_TEST(test_json_write_pretty_deep_nesting) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *allocator = &talloc.base;
+    CX_TEST_DO {
+        cxstring expected = cx_str(
+"{\n"
+"        \"test\": {\n"
+"                \"test\": {\n"
+"                        \"test\": {\n"
+"                                \"test\": {\n"
+"                                        \"test\": {\n"
+"                                                \"test\": 47\n"
+"                                        }\n"
+"                                }\n"
+"                        }\n"
+"                }\n"
+"        }\n"
+"}"
+        );
+
+        CxJsonValue *obj = cxJsonCreateObj(allocator);
+        CxJsonValue *test = obj;
+        for (unsigned i = 0 ; i < 5 ; i++) {
+            test = cxJsonObjPutObj(test, "test");
+        }
+        cxJsonObjPutInteger(test, "test", 47);
+
+        CxJsonWriter writer = cxJsonWriterPretty(true);
+        writer.indent = 8;
+        CxBuffer buf;
+        cxBufferInit(&buf, NULL, 512, NULL, CX_BUFFER_DEFAULT);
+        int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer);
+        cxBufferTerminate(&buf); // makes debugging easier
+        CX_TEST_ASSERT(result == 0);
+
+        // compare the string
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), expected));
+
+        // destroy everything
+        cxBufferDestroy(&buf);
+        cxJsonValueFree(obj);
+
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
 CX_TEST(test_json_write_frac_max_digits) {
     CxJsonValue* num = cxJsonCreateNumber(NULL, 3.141592653589793);
     CxJsonWriter writer = cxJsonWriterCompact();
@@ -1150,45 +1369,45 @@
     CX_TEST_DO {
         // test default settings (6 digits)
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.141592")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141592"));
 
         // test too many digits
         cxBufferReset(&buf);
         writer.frac_max_digits = 200;
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.141592653589793")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141592653589793"));
         
         // test 0 digits
         cxBufferReset(&buf);
         writer.frac_max_digits = 0;
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3"));
 
         // test 2 digits
         cxBufferReset(&buf);
         writer.frac_max_digits = 2;
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.14")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.14"));
 
         // test 3 digits
         cxBufferReset(&buf);
         writer.frac_max_digits = 3;
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.141")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141"));
 
         // test 6 digits, but two are left of the decimal point
         num->value.number = 47.110815;
         cxBufferReset(&buf);
         writer.frac_max_digits = 6;
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("47.110815")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "47.110815"));
 
         // test 4 digits with exponent
         num->value.number = 5.11223344e23;
         cxBufferReset(&buf);
         writer.frac_max_digits = 4;
         cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("5.1122e+23")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "5.1122e+23"));
     }
     cxBufferDestroy(&buf);
     cxJsonValueFree(num);
@@ -1216,7 +1435,7 @@
     CX_TEST_DO {
         cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer);
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size),
-            CX_STR("\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\"")));
+            "\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\""));
     }
     cxBufferDestroy(&buf);
     cxJsonValueFree(str);
@@ -1225,14 +1444,14 @@
 CX_TEST(test_json_write_name_escape) {
     CxJsonValue* obj = cxJsonCreateObj(NULL);
     cxJsonObjPutLiteral(obj,
-        CX_STR("hello\twörld\r\nthis is\\a \"string\"\b in \a string\f"), CX_JSON_TRUE);
+        "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f", CX_JSON_TRUE);
     CxJsonWriter writer = cxJsonWriterCompact();
     CxBuffer buf;
     cxBufferInit(&buf, NULL, 128, NULL, 0);
     CX_TEST_DO {
         cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer);
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size),
-            CX_STR("{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}")));
+            "{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}"));
     }
     cxBufferDestroy(&buf);
     cxJsonValueFree(obj);
@@ -1246,32 +1465,49 @@
     CX_TEST_DO {
         // default: do not escape
         cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("\"test/solidus\"")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test/solidus\""));
 
         // enable escaping
         writer.escape_slash = true;
         cxBufferReset(&buf);
         cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer);
-        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("\"test\\/solidus\"")));
+        CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test\\/solidus\""));
     }
     cxBufferDestroy(&buf);
     cxJsonValueFree(str);
 }
 
+CX_TEST(test_json_write_nothing) {
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 16, NULL, 0);
+    CX_TEST_DO {
+        CxJsonValue nothing;
+        nothing.type = CX_JSON_NOTHING;
+        cxJsonWrite(&buf, &nothing, cxBufferWriteFunc, NULL);
+        CX_TEST_ASSERT(buf.size == 0);
+    }
+    cxBufferDestroy(&buf);
+}
+
 CxTestSuite *cx_test_suite_json(void) {
     CxTestSuite *suite = cx_test_suite_new("json");
 
     cx_test_register(suite, test_json_init_default);
     cx_test_register(suite, test_json_simple_object);
+    cx_test_register(suite, test_json_large_object);
     cx_test_register(suite, test_json_escaped_strings);
     cx_test_register(suite, test_json_escaped_unicode_strings);
     cx_test_register(suite, test_json_escaped_unicode_malformed);
     cx_test_register(suite, test_json_escaped_end_of_string);
     cx_test_register(suite, test_json_object_incomplete_token);
+    cx_test_register(suite, test_json_parenthesis_mismatch);
+    cx_test_register(suite, test_json_object_name_is_no_string);
     cx_test_register(suite, test_json_token_wrongly_completed);
     cx_test_register(suite, test_json_object_error);
     cx_test_register(suite, test_json_object_remove_member);
     cx_test_register(suite, test_json_subsequent_fill);
+    cx_test_register(suite, test_json_no_fill);
+    cx_test_register(suite, test_json_null_fill);
     cx_test_register(suite, test_json_large_nesting_depth);
     cx_test_register(suite, test_json_number);
     cx_test_register(suite, test_json_number_format_errors);
@@ -1281,14 +1517,17 @@
     cx_test_register(suite, test_json_allocator);
     cx_test_register(suite, test_json_allocator_parse_error);
     cx_test_register(suite, test_json_create_value);
+    cx_test_register(suite, test_json_overwrite_value);
     cx_test_register(suite, test_json_write_default_format);
     cx_test_register(suite, test_json_write_pretty_default_spaces);
     cx_test_register(suite, test_json_write_pretty_default_tabs);
     cx_test_register(suite, test_json_write_pretty_preserve_order);
+    cx_test_register(suite, test_json_write_pretty_deep_nesting);
     cx_test_register(suite, test_json_write_frac_max_digits);
     cx_test_register(suite, test_json_write_string_escape);
     cx_test_register(suite, test_json_write_name_escape);
     cx_test_register(suite, test_json_write_solidus);
+    cx_test_register(suite, test_json_write_nothing);
     
     return suite;
 }

mercurial