Thu, 28 Nov 2024 20:53:56 +0100
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; }