fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped

Sat, 06 Dec 2025 17:51:08 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 06 Dec 2025 17:51:08 +0100
changeset 1548
12315ee158ad
parent 1547
12da0654e4a9
child 1549
72ad8a78378a

fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
src/json.c file | annotate | diff | comparison | revisions
tests/test_json.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Sat Dec 06 16:30:11 2025 +0100
+++ b/CHANGELOG	Sat Dec 06 17:51:08 2025 +0100
@@ -6,6 +6,7 @@
  * changes the members of CxJson and CxJsonValue
  * changes the return value of cxJsonObjIter() to CxMapIterator
  * removes the sort_members feature from CxJsonWriter
+ * fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped
  * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors
  * fixes that overwriting items with cxMapPut() in a kv-list did not work
  * fixes that cxReallocate(), cxReallocateArray(), cx_reallocate(), and cx_reallocatearray()
--- a/docs/Writerside/topics/about.md	Sat Dec 06 16:30:11 2025 +0100
+++ b/docs/Writerside/topics/about.md	Sat Dec 06 17:51:08 2025 +0100
@@ -33,6 +33,7 @@
 * changes the members of CxJson and CxJsonValue
 * changes the return value of cxJsonObjIter() to CxMapIterator
 * removes the sort_members feature from CxJsonWriter
+* fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped
 * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors
 * fixes that overwriting items with cxMapPut() in a kv-list did not work
 * fixes that cxReallocate(), cxReallocateArray(), cx_reallocate(), and cx_reallocatearray()
--- a/src/json.c	Sat Dec 06 16:30:11 2025 +0100
+++ b/src/json.c	Sat Dec 06 17:51:08 2025 +0100
@@ -1178,19 +1178,17 @@
                 cxstring key = cx_strn(member->key->data, member->key->len);
                 cxmutstr name = escape_string(key, settings->escape_slash);
                 actual += wfunc(name.ptr, 1, name.length, target);
-                if (name.ptr != key.ptr) {
-                    cx_strfree(&name);
-                }
                 actual += wfunc("\"", 1, 1, target);
                 const char *obj_name_sep = ": ";
                 if (settings->pretty) {
                     actual += wfunc(obj_name_sep, 1, 2, target);
-                    // FIXME: is this really correct? should be the (escaped) name.length
-                    expected += 4 + key.length;
+                    expected += 4 + name.length;
                 } else {
                     actual += wfunc(obj_name_sep, 1, 1, target);
-                    // FIXME: is this really correct? should be the (escaped) name.length
-                    expected += 3 + key.length;
+                    expected += 3 + name.length;
+                }
+                if (name.ptr != key.ptr) {
+                    cx_strfree(&name);
                 }
 
                 // the value
@@ -1253,11 +1251,11 @@
             cxmutstr str = escape_string(cx_strcast(value->string),
                 settings->escape_slash);
             actual += wfunc(str.ptr, 1, str.length, target);
+            actual += wfunc("\"", 1, 1, target);
+            expected += 2 + str.length;
             if (str.ptr != value->string.ptr) {
                 cx_strfree(&str);
             }
-            actual += wfunc("\"", 1, 1, target);
-            expected += 2 + value->string.length;
             break;
         }
         case CX_JSON_NUMBER: {
--- a/tests/test_json.c	Sat Dec 06 16:30:11 2025 +0100
+++ b/tests/test_json.c	Sat Dec 06 17:51:08 2025 +0100
@@ -1335,45 +1335,45 @@
     cxBufferInit(&buf, NULL, 32, NULL, 0);
     CX_TEST_DO {
         // test default settings (6 digits)
-        cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         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 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         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 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         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 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         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 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         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->number = 47.110815;
         cxBufferReset(&buf);
         writer.frac_max_digits = 6;
-        cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "47.110815"));
 
         // test 4 digits with exponent
         num->number = 5.11223344e23;
         cxBufferReset(&buf);
         writer.frac_max_digits = 4;
-        cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "5.1122e+23"));
     }
     cxBufferDestroy(&buf);
@@ -1400,7 +1400,7 @@
     CxBuffer buf;
     cxBufferInit(&buf, NULL, 128, NULL, 0);
     CX_TEST_DO {
-        cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer));
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size),
             "\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\""));
     }
@@ -1416,7 +1416,7 @@
     CxBuffer buf;
     cxBufferInit(&buf, NULL, 128, NULL, 0);
     CX_TEST_DO {
-        cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer));
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size),
             "{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}"));
     }
@@ -1431,13 +1431,13 @@
     cxBufferInit(&buf, NULL, 16, NULL, 0);
     CX_TEST_DO {
         // default: do not escape
-        cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer));
         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 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer));
         CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test\\/solidus\""));
     }
     cxBufferDestroy(&buf);
@@ -1450,7 +1450,7 @@
     CX_TEST_DO {
         CxJsonValue nothing;
         nothing.type = CX_JSON_NOTHING;
-        cxJsonWrite(&buf, &nothing, cxBufferWriteFunc, NULL);
+        CX_TEST_ASSERT(0 == cxJsonWrite(&buf, &nothing, cxBufferWriteFunc, NULL));
         CX_TEST_ASSERT(buf.size == 0);
     }
     cxBufferDestroy(&buf);

mercurial