# HG changeset patch # User Mike Becker # Date 1737576166 -3600 # Node ID 7dfa5bcf39ee04dacaa23636d6901832dc40c0d8 # Parent 29672c777a28e7d8469a89f6cc9f7c628903911c remove token_escape bool from CxJson struct diff -r 29672c777a28 -r 7dfa5bcf39ee src/cx/json.h --- a/src/cx/json.h Wed Jan 22 20:36:10 2025 +0100 +++ b/src/cx/json.h Wed Jan 22 21:02:46 2025 +0100 @@ -374,11 +374,6 @@ * Internally reserved memory for the value buffer stack. */ CxJsonValue* vbuf_internal[8]; - - /** - * Used internally. - */ - bool tokenizer_escape; // TODO: check if it can be replaced with look-behind }; /** diff -r 29672c777a28 -r 7dfa5bcf39ee src/json.c --- a/src/json.c Wed Jan 22 20:36:10 2025 +0100 +++ b/src/json.c Wed Jan 22 21:02:46 2025 +0100 @@ -263,6 +263,9 @@ CxJsonTokenType ttype = json->uncompleted.tokentype; size_t token_part_start = json->buffer.pos; + bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING + && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\'; + for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { char c = json->buffer.space[i]; if (ttype != CX_JSON_TOKEN_STRING) { @@ -301,8 +304,8 @@ } } else { // currently inside a string - if (json->tokenizer_escape) { - json->tokenizer_escape = false; + if (escape_end_of_string) { + escape_end_of_string = false; } else { if (c == '"') { *result = token_create(json, true, token_part_start, i + 1); @@ -312,7 +315,7 @@ json->buffer.pos = i + 1; return CX_JSON_NO_ERROR; } else if (c == '\\') { - json->tokenizer_escape = true; + escape_end_of_string = true; } } } diff -r 29672c777a28 -r 7dfa5bcf39ee tests/test_json.c --- a/tests/test_json.c Wed Jan 22 20:36:10 2025 +0100 +++ b/tests/test_json.c Wed Jan 22 21:02:46 2025 +0100 @@ -143,6 +143,39 @@ cxJsonDestroy(&json); } +CX_TEST(test_json_escaped_end_of_string) { + CxJson json; + cxJsonInit(&json, NULL); + CX_TEST_DO { + // first test, normal scenario + cxJsonFill(&json, "\"a \\\"test\\\" string\""); + CxJsonValue *val; + CxJsonStatus result = cxJsonNext(&json, &val); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsString(val)); + CX_TEST_ASSERT(0 == cx_strcmp( + cxJsonAsCxString(val), + cx_str("a \"test\" string")) + ); + cxJsonValueFree(val); + + // second test - uncompleted token with hanging escape char + cxJsonFill(&json, "\"a \\\"test\\"); + result = cxJsonNext(&json, &val); + CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); + cxJsonFill(&json, "\" string\""); + result = cxJsonNext(&json, &val); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsString(val)); + CX_TEST_ASSERT(0 == cx_strcmp( + cxJsonAsCxString(val), + cx_str("a \"test\" string")) + ); + cxJsonValueFree(val); + } + cxJsonDestroy(&json); +} + CX_TEST(test_json_object_incomplete_token) { cxstring text = cx_str( "{\"message\":\"success\" , \"__timestamp\":1729348561}"); @@ -1009,6 +1042,7 @@ cx_test_register(suite, test_json_init_default); cx_test_register(suite, test_json_simple_object); cx_test_register(suite, test_json_escaped_strings); + 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_token_wrongly_completed); cx_test_register(suite, test_json_object_error);