add support for allocators to the json parser

Thu, 28 Nov 2024 20:53:56 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 28 Nov 2024 20:53:56 +0100
changeset 996
333155f234c4
parent 995
d3d4f245b843
child 997
d14f3d5f47d1

add support for allocators to the json parser

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	Thu Nov 28 19:37:00 2024 +0100
+++ b/src/cx/json.h	Thu Nov 28 20:53:56 2024 +0100
@@ -37,6 +37,7 @@
 #define UCX_JSON_H
 
 #include "common.h"
+#include "allocator.h"
 #include "string.h"
 #include "array_list.h"
 
@@ -113,6 +114,7 @@
 };
 
 struct cx_json_s {
+    const CxAllocator *allocator;
     const char *buffer;
     size_t size;
     size_t pos;
@@ -160,6 +162,7 @@
 };
 
 struct cx_json_value_s {
+    const CxAllocator *allocator;
     CxJsonValueType type;
     union {
         CxJsonArray array;
@@ -171,10 +174,9 @@
     } value;
 };
 
-// TODO: add support for CxAllocator
 
-cx_attr_nonnull
-void cxJsonInit(CxJson *json);
+cx_attr_nonnull_arg(2)
+void cxJsonInit(const CxAllocator *allocator, CxJson *json);
 
 cx_attr_nonnull
 void cxJsonDestroy(CxJson *json);
--- a/src/json.c	Thu Nov 28 19:37:00 2024 +0100
+++ b/src/json.c	Thu Nov 28 20:53:56 2024 +0100
@@ -266,12 +266,12 @@
     return ret;
 }
 
-static cxmutstr unescape_string(const char *str, size_t len) {
+static cxmutstr unescape_string(const CxAllocator *a, const char *str, size_t len) {
     // TODO: support more escape sequences
     // we know that the unescaped string will be shorter by at least 2 chars
     cxmutstr result;
     result.length = 0;
-    result.ptr = malloc(len - 1);
+    result.ptr = cxMalloc(a, len - 1);
     if (result.ptr == NULL) {
         // TODO: check if this actually leads to correct error handling
         return result;
@@ -437,7 +437,7 @@
             }
             case CX_JSON_TOKEN_STRING: {
                 p->reader_type = CX_JSON_READER_STRING;
-                cxmutstr str = unescape_string(token.content, token.length);
+                cxmutstr str = unescape_string(p->allocator, token.content, token.length);
                 if (str.ptr) {
                     p->value_str = str.ptr;
                     p->value_str_len = str.length;
@@ -487,7 +487,7 @@
             if (token.tokentype != CX_JSON_TOKEN_STRING) return -1;
 
             if (p->value_name) free(p->value_name);
-            cxmutstr valname = unescape_string(token.content, token.length);
+            cxmutstr valname = unescape_string(p->allocator, token.content, token.length);
             p->value_name = valname.ptr;
             p->value_name_len = valname.length;
 
@@ -549,7 +549,8 @@
     if (!parent) {
         return -1; // shouldn't happen but who knows
     }
-
+    
+    CxArrayReallocator reallocator = cx_array_reallocator(p->allocator, NULL);
     if (parent->type == CX_JSON_OBJECT) {
         if (!p->value_name || p->value_name_len == 0) {
             return -1;
@@ -561,9 +562,21 @@
         newvalue.name = valuename;
         newvalue.value = v;
 
-        return cx_array_simple_add(parent->value.object.values, newvalue);
+        return cx_array_add(
+                &parent->value.object.values,
+                &parent->value.object.values_size,
+                &parent->value.object.values_capacity, 
+                sizeof(CxJsonObjValue),
+                &newvalue,
+                &reallocator);
     } else if (parent->type == CX_JSON_ARRAY) {
-        return cx_array_simple_add(parent->value.array.array, v);
+        return cx_array_add(
+                &parent->value.array.array,
+                &parent->value.array.array_size,
+                &parent->value.array.array_capacity, 
+                sizeof(CxJsonValue*),
+                &v,
+                &reallocator);
     } else {
         return -1; // should also never happen
     }
@@ -581,8 +594,13 @@
     return 0;
 }
 
-void cxJsonInit(CxJson *json) {
+void cxJsonInit(const CxAllocator *allocator, CxJson *json) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
+    }
+    
     memset(json, 0, sizeof(CxJson));
+    json->allocator = allocator;
     json->states = json->states_internal;
     json->states_alloc = cx_nmemb(json->states_internal);
     // TODO: find better way to configure the initial allocation size for arrays and objects
@@ -618,8 +636,9 @@
     while (p->readvalue_nelm > 0 || !p->read_value) {
         if (p->value_ready) {
             // value available without another read
-            CxJsonValue *v = calloc(1, sizeof(CxJsonValue));
+            CxJsonValue *v = cxCalloc(p->allocator, 1, sizeof(CxJsonValue));
             if (!v) return -1;
+            v->allocator = p->allocator;
 
             if (p->readvalue_nelm > 0) {
                 if (add_to_parent(p, p->readvalue_stack[p->readvalue_nelm - 1], v)) {
@@ -704,15 +723,14 @@
 void cxJsonValueFree(CxJsonValue *value) {
     if (value == NULL || value == &cx_json_value_nothing) return;
 
-    // TODO: discuss if we should keep freeing the stuff recursively
     switch (value->type) {
         case CX_JSON_OBJECT: {
             CxJsonObject obj = value->value.object;
             for (size_t i = 0; i < obj.values_size; i++) {
                 cxJsonValueFree(obj.values[i].value);
-                free(obj.values[i].name);
+                cxFree(value->allocator, obj.values[i].name);
             }
-            free(obj.values);
+            cxFree(value->allocator, obj.values);
             break;
         }
         case CX_JSON_ARRAY: {
@@ -720,18 +738,18 @@
             for (size_t i = 0; i < array.array_size; i++) {
                 cxJsonValueFree(array.array[i]);
             }
-            free(array.array);
+            cxFree(value->allocator, array.array);
             break;
         }
         case CX_JSON_STRING: {
-            free(value->value.string.ptr);
+            cxFree(value->allocator, value->value.string.ptr);
             break;
         }
         default: {
             break;
         }
     }
-    free(value);
+    cxFree(value->allocator, value);
 }
 
 CxJsonValue *cxJsonArrGet(CxJsonValue *value, size_t index) {
--- a/tests/test_json.c	Thu Nov 28 19:37:00 2024 +0100
+++ b/tests/test_json.c	Thu Nov 28 20:53:56 2024 +0100
@@ -29,11 +29,12 @@
 #include "cx/test.h"
 
 #include "cx/json.h"
+#include "cx/mempool.h"
 
 CX_TEST(test_json_init_default) {
     CxJson json;
     CX_TEST_DO {
-        cxJsonInit(&json);
+        cxJsonInit(NULL, &json);
         CX_TEST_ASSERT(json.states == json.states_internal);
         CX_TEST_ASSERT(json.nstates == 0);
         CX_TEST_ASSERT(json.states_alloc == 8);
@@ -58,7 +59,7 @@
         int result;
 
         CxJson json;
-        cxJsonInit(&json);
+        cxJsonInit(NULL, &json);
         cxJsonFill(&json, text);
 
         // parse the big fat object
@@ -125,7 +126,7 @@
         int result;
 
         CxJson json;
-        cxJsonInit(&json);
+        cxJsonInit(NULL, &json);
         CxJsonValue *obj;
         
         size_t part = 0;
@@ -190,7 +191,7 @@
         CxJsonValue *obj = NULL;
         
         for(int i=0;i<5;i++) {
-            cxJsonInit(&json);
+            cxJsonInit(NULL, &json);
             cxJsonFill(&json, tests[i]);
             result = cxJsonNext(&json, &obj);
 
@@ -206,7 +207,7 @@
     CxJsonValue *d1;
     cxstring text = cx_str("{\"test\": [{},{\"foo\": [[{\"bar\":[4, 2, [null, {\"key\": 47}]]}]]}]}");
     CX_TEST_DO {
-        cxJsonInit(&json);
+        cxJsonInit(NULL, &json);
         cxJsonFill(&json, text);
         cxJsonNext(&json, &d1);
 
@@ -249,7 +250,7 @@
 
 CX_TEST(test_json_number) {
     CxJson json;
-    cxJsonInit(&json);
+    cxJsonInit(NULL, &json);
     CX_TEST_DO {
         // TODO: find a better way to terminate values that are not arrays/objects
         CxJsonValue *v;
@@ -272,7 +273,7 @@
 
 CX_TEST(test_json_multiple_values) {
     CxJson json;
-    cxJsonInit(&json);
+    cxJsonInit(NULL, &json);
     CX_TEST_DO {
         CxJsonValue *v;
         int result;
@@ -332,6 +333,36 @@
     cxJsonDestroy(&json);
 }
 
+CX_TEST(test_json_allocator) {
+    CxMempool *mp = cxMempoolCreate(64, NULL);
+    CxJson json;
+    cxJsonInit(mp->allocator, &json);
+    
+    cxstring text = cx_str(
+            "{\n"
+            "\t\"message\":\"success\",\n"
+            "\t\"data\":[\"value1\",{\"x\":123, \"y\":523 }]\n"
+            "}"
+    );
+
+    CX_TEST_DO {
+        int result;
+
+        CxJson json;
+        cxJsonInit(mp->allocator, &json);
+        cxJsonFill(&json, text);
+        
+        CxJsonValue *obj;
+        result = cxJsonNext(&json, &obj);
+        CX_TEST_ASSERT(result == 1);
+        CX_TEST_ASSERT(obj->allocator == mp->allocator);
+        
+        // this recursively frees everything 
+        cxJsonValueFree(obj);
+        cxJsonDestroy(&json);
+    }
+}
+
 CxTestSuite *cx_test_suite_json(void) {
     CxTestSuite *suite = cx_test_suite_new("json");
 
@@ -342,6 +373,7 @@
     cx_test_register(suite, test_json_large_nesting_depth);
     cx_test_register(suite, test_json_number);
     cx_test_register(suite, test_json_multiple_values);
+    cx_test_register(suite, test_json_allocator);
     
     return suite;
 }

mercurial