remove token_escape bool from CxJson struct default tip

Wed, 22 Jan 2025 21:02:46 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 22 Jan 2025 21:02:46 +0100
changeset 1139
7dfa5bcf39ee
parent 1138
29672c777a28

remove token_escape bool from CxJson struct

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/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
 };
 
 /**
--- 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;
                 }
             }
         }
--- 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);

mercurial