src/json.c

changeset 1007
81b2986d2b04
parent 1002
1483c47063a8
child 1008
3b69f025f083
--- a/src/json.c	Tue Dec 10 00:09:55 2024 +0100
+++ b/src/json.c	Tue Dec 10 00:19:45 2024 +0100
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <assert.h>
+#include <stdio.h>
 
 #include "cx/json.h"
 
@@ -108,13 +109,13 @@
 }
 
 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) {
-    cxmutstr str = cx_mutstrn((char*)json->buffer + start, end - start);
+    cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start);
     bool allocated = false;
     if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) {
         allocated = true;
         str = cx_strcat_m(json->uncompleted.content, 1, str);
         if (str.ptr == NULL) {
-            return (CxJsonToken){CX_JSON_NO_TOKEN, false, 0, 0};
+            return (CxJsonToken){CX_JSON_NO_TOKEN, false, {0, 0}};
         }
     }
     json->uncompleted = (CxJsonToken){0};
@@ -132,7 +133,7 @@
         if (allocated) {
             cx_strfree(&str);
         }
-        return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, 0, 0};
+        return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {0, 0}};
     }
     return (CxJsonToken){ttype, allocated, str};
 }
@@ -171,22 +172,17 @@
 
 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
     // check if there is data in the buffer
-    if (json->pos >= json->size) {
+    if (cxBufferEof(&json->buffer)) {
         return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ?
             CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA;
     }
 
-    // sanity check
-    if (json->buffer == NULL) {
-        return CX_JSON_NULL_INPUT;
-    }
-
     // current token type and start index
     CxJsonTokenType ttype = json->uncompleted.tokentype;
-    size_t token_start = json->pos;
+    size_t token_start = json->buffer.pos;
 
-    for (size_t i = json->pos; i < json->size; i++) {
-        char c = json->buffer[i];
+    for (size_t i = json->buffer.pos; i < json->buffer.size; i++) {
+        char c = json->buffer.space[i];
         if (ttype != CX_JSON_TOKEN_STRING) {
             // currently non-string token
             CxJsonTokenType ctype = char2ttype(c); // start of new token?
@@ -199,8 +195,8 @@
                     token_start = i;
                 } else if (ctype != CX_JSON_NO_TOKEN) {
                     // single-char token
-                    json->pos = i + 1;
-                    *result = (CxJsonToken){ctype, NULL, 0, 0};
+                    json->buffer.pos = i + 1;
+                    *result = (CxJsonToken){ctype, NULL, {0, 0}};
                     return CX_JSON_NO_ERROR;
                 } else {
                     ttype = CX_JSON_TOKEN_LITERAL; // number or literal
@@ -216,7 +212,7 @@
                     if (result->tokentype == CX_JSON_TOKEN_ERROR) {
                         return CX_JSON_FORMAT_ERROR_NUMBER;
                     }
-                    json->pos = i;
+                    json->buffer.pos = i;
                     return CX_JSON_NO_ERROR;
                 }
             }
@@ -230,7 +226,7 @@
                     if (result->tokentype == CX_JSON_NO_TOKEN) {
                         return CX_JSON_BUFFER_ALLOC_FAILED;
                     }
-                    json->pos = i + 1;
+                    json->buffer.pos = i + 1;
                     return CX_JSON_NO_ERROR;
                 } else if (c == '\\') {
                     json->tokenizer_escape = true;
@@ -241,13 +237,13 @@
 
     if (ttype != CX_JSON_NO_TOKEN) {
         // uncompleted token
-        size_t uncompeted_len = json->size - token_start;
+        size_t uncompeted_len = json->buffer.size - token_start;
         if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
             // current token is uncompleted
             // save current token content
             CxJsonToken uncompleted = {
                 ttype, true,
-                cx_strdup(cx_strn(json->buffer + token_start, uncompeted_len))
+                cx_strdup(cx_strn(json->buffer.space + token_start, uncompeted_len))
             };
             if (uncompleted.content.ptr == NULL) {
                 return CX_JSON_BUFFER_ALLOC_FAILED;
@@ -258,7 +254,7 @@
             // combine the uncompleted token with the current token
             assert(json->uncompleted.allocated);
             cxmutstr str = cx_strcat_m(json->uncompleted.content, 1,
-                cx_strn(json->buffer + token_start, uncompeted_len));
+                cx_strn(json->buffer.space + token_start, uncompeted_len));
             if (str.ptr == NULL) {
                 return CX_JSON_BUFFER_ALLOC_FAILED;
             }
@@ -305,24 +301,26 @@
 
 static int parse_number(cxmutstr str, void *value, bool asint) {
     char *endptr = NULL;
-    char buf[32];
     if (str.length > 30) {
         return 1;
     }
-    // TODO: if we can guarantee that we are working on a copied string already, we can avoid this memcpy
-    memcpy(buf, str.ptr, str.length);
-    buf[str.length] = 0;
+    // the buffer guarantees that we are working on a copied string
+    char c = str.ptr[str.length];
+    str.ptr[str.length] = 0;
 
     if (asint) {
-        long long v = strtoll(buf, &endptr, 10);
+        long long v = strtoll(str.ptr, &endptr, 10);
         *((int64_t*)value) = (int64_t) v;
     } else {
         // TODO: proper JSON spec number parser
-        double v = strtod(buf, &endptr);
+        double v = strtod(str.ptr, &endptr);
         *((double*)value) = v;
     }
 
-    return (endptr != &buf[str.length]);
+    // recover from the hack
+    str.ptr[str.length] = c;
+
+    return endptr != &str.ptr[str.length];
 }
 
 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) {
@@ -417,9 +415,12 @@
 
     json->vbuf = json->vbuf_internal;
     json->vbuf_capacity = cx_nmemb(json->vbuf_internal);
+
+    cxBufferInit(&json->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND);
 }
 
 void cxJsonDestroy(CxJson *json) {
+    cxBufferDestroy(&json->buffer);
     if (json->states != json->states_internal) {
         free(json->states);
     }
@@ -431,11 +432,16 @@
 }
 
 int cxJsonFilln(CxJson *json, const char *buf, size_t size) {
-    // TODO: implement rescue buffer like in CxProperties to allow subsequent fills
-    json->buffer = buf;
-    json->size = size;
-    json->pos = 0;
-    return 0;
+    // we use the UCX buffer to write the data
+    // but reset the position immediately to enable parsing
+    size_t old_pos = json->buffer.pos;
+    cxBufferSeek(&json->buffer, 0, SEEK_END);
+    size_t written = cxBufferWrite(buf, 1, size, &json->buffer);
+    if (0 == cxBufferTerminate(&json->buffer)) {
+        written++;
+    }
+    json->buffer.pos = old_pos;
+    return written != size + 1;
 }
 
 static void json_add_state(CxJson *json, int state) {

mercurial