Sun, 29 Dec 2024 18:03:21 +0100
optimize cxJsonObjGet() part 1 - binary search
part 2 will be the index buffer to preserve the order
relates to #462
src/Makefile | file | annotate | diff | comparison | revisions | |
src/cx/json.h | file | annotate | diff | comparison | revisions | |
src/json.c | file | annotate | diff | comparison | revisions |
--- a/src/Makefile Sun Dec 29 17:45:56 2024 +0100 +++ b/src/Makefile Sun Dec 29 18:03:21 2024 +0100 @@ -98,7 +98,7 @@ $(build_dir)/json$(OBJ_EXT): json.c cx/json.h cx/common.h cx/allocator.h \ cx/string.h cx/buffer.h cx/array_list.h cx/list.h cx/collection.h \ - cx/iterator.h cx/compare.h + cx/iterator.h cx/compare.h cx/compare.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- a/src/cx/json.h Sun Dec 29 17:45:56 2024 +0100 +++ b/src/cx/json.h Sun Dec 29 18:03:21 2024 +0100 @@ -345,6 +345,13 @@ CxJsonValue *parsed; /** + * A pointer to an intermediate state of a currently parsed object member. + * + * Never access this value manually. + */ + CxJsonObjValue uncompleted_member; + + /** * State stack. */ CX_ARRAY_DECLARE_SIZED(int, states, unsigned);
--- a/src/json.c Sun Dec 29 17:45:56 2024 +0100 +++ b/src/json.c Sun Dec 29 18:03:21 2024 +0100 @@ -27,6 +27,7 @@ */ #include "cx/json.h" +#include "cx/compare.h" #include <string.h> #include <ctype.h> @@ -41,6 +42,39 @@ static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING}; +static int json_cmp_objvalue(const void *l, const void *r) { + const CxJsonObjValue *left = l; + const CxJsonObjValue *right = r; + return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name)); +} + +static CxJsonObjValue *json_find_objvalue(const CxJsonValue *obj, cxstring name) { + assert(obj->type == CX_JSON_OBJECT); + CxJsonObjValue kv_dummy; + kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length); + size_t index = cx_array_binary_search( + obj->value.object.values, + obj->value.object.values_size, + sizeof(CxJsonObjValue), + &kv_dummy, + json_cmp_objvalue + ); + if (index == obj->value.object.values_size) { + return NULL; + } else { + return &obj->value.object.values[index]; + } +} + +static int json_add_objvalue(CxJsonValue *obj, CxJsonObjValue member) { + assert(obj->type == CX_JSON_OBJECT); + CxArrayReallocator value_realloc = cx_array_reallocator(obj->allocator, NULL); + return cx_array_simple_add_sorted_a( + &value_realloc, obj->value.object.values, + member, json_cmp_objvalue + ); +} + static void token_destroy(CxJsonToken *token) { if (token->allocated) { cx_strfree(&token->content); @@ -294,16 +328,10 @@ // initialize the value if (type == CX_JSON_ARRAY) { cx_array_initialize_a(json->allocator, v->value.array.array, 16); - if (v->value.array.array == NULL) { // LCOV_EXCL_START - cxFree(json->allocator, v); - return NULL; - } // LCOV_EXCL_STOP + if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE } else if (type == CX_JSON_OBJECT) { cx_array_initialize_a(json->allocator, v->value.object.values, 16); - if (v->value.object.values == NULL) { // LCOV_EXCL_START - cxFree(json->allocator, v); - return NULL; - } // LCOV_EXCL_STOP + if (v->value.object.values == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE } else { memset(v, 0, sizeof(CxJsonValue)); } @@ -311,15 +339,22 @@ v->allocator = json->allocator; // add the new value to a possible parent - CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); if (json->vbuf_size > 0) { CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; + assert(parent != NULL); if (parent->type == CX_JSON_ARRAY) { - cx_array_simple_add_a(&value_realloc, parent->value.array.array, v); + CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); + if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) { + goto create_json_value_exit_error; // LCOV_EXCL_LINE + } } else if (parent->type == CX_JSON_OBJECT) { - assert(parent->value.object.values_size > 0); - assert(parent->value.object.values[parent->value.object.values_size - 1].value == NULL); - parent->value.object.values[parent->value.object.values_size - 1].value = v; + // the member was already created after parsing the name + assert(json->uncompleted_member.name.ptr != NULL); + json->uncompleted_member.value = v; + if (json_add_objvalue(parent, json->uncompleted_member)) { + goto create_json_value_exit_error; // LCOV_EXCL_LINE + } + json->uncompleted_member.name = (cxmutstr) {NULL, 0}; } else { assert(false); // LCOV_EXCL_LINE } @@ -329,10 +364,8 @@ if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { - // LCOV_EXCL_START - cxFree(json->allocator, v); - return NULL; - } // LCOV_EXCL_STOP + goto create_json_value_exit_error; // LCOV_EXCL_LINE + } } // if currently no value is parsed, this is now the value of interest @@ -341,6 +374,11 @@ } return v; + // LCOV_EXCL_START +create_json_value_exit_error: + cxFree(json->allocator, v); + return NULL; + // LCOV_EXCL_STOP } #define JP_STATE_VALUE_BEGIN 0 @@ -530,15 +568,9 @@ if (name.ptr == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } - CxJsonObjValue kv = {name, NULL}; + assert(json->uncompleted_member.name.ptr == NULL); + json->uncompleted_member.name = name; assert(json->vbuf_size > 0); - CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; - assert(parent != NULL); - assert(parent->type == CX_JSON_OBJECT); - CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); - if (cx_array_simple_add_a(&value_realloc, parent->value.object.values, kv)) { - return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE - } // next state json_add_state(json, JP_STATE_OBJ_COLON); @@ -795,12 +827,10 @@ } } - CxArrayReallocator value_realloc = cx_array_reallocator(obj->allocator, NULL); - assert(obj->type == CX_JSON_OBJECT); cxmutstr k = cx_strdup_a(obj->allocator, name); if (k.ptr == NULL) return -1; CxJsonObjValue kv = {k, child}; - return cx_array_simple_add_a(&value_realloc, obj->value.object.values, kv); + return json_add_objvalue(obj, kv); } CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) { @@ -893,12 +923,10 @@ } CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) { - const CxJsonObject *obj = &(value->value.object); - // TODO: think about sorting the object so that we can use binary search here - for (size_t i = 0; i < obj->values_size; i++) { - if (0 == cx_strcmp(name, cx_strcast(obj->values[i].name))) { - return obj->values[i].value; - } + CxJsonObjValue *member = json_find_objvalue(value, name); + if (member == NULL) { + return &cx_json_value_nothing; + } else { + return member->value; } - return &cx_json_value_nothing; }