# HG changeset patch # User Mike Becker # Date 1765039868 -3600 # Node ID 12315ee158ad9dbbfa401bb419d2217ab51f9a5b # Parent 12da0654e4a96ac72787a40fd95577a3d13dde09 fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped diff -r 12da0654e4a9 -r 12315ee158ad CHANGELOG --- 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() diff -r 12da0654e4a9 -r 12315ee158ad docs/Writerside/topics/about.md --- 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() diff -r 12da0654e4a9 -r 12315ee158ad src/json.c --- 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: { diff -r 12da0654e4a9 -r 12315ee158ad tests/test_json.c --- 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, ¬hing, cxBufferWriteFunc, NULL); + CX_TEST_ASSERT(0 == cxJsonWrite(&buf, ¬hing, cxBufferWriteFunc, NULL)); CX_TEST_ASSERT(buf.size == 0); } cxBufferDestroy(&buf);