--- a/src/json.c Fri Jan 03 17:16:49 2025 +0100 +++ b/src/json.c Fri Jan 03 19:18:00 2025 +0100 @@ -67,13 +67,66 @@ } } -static int json_add_objvalue(CxJsonValue *obj, CxJsonObjValue member) { +static int json_add_objvalue(CxJsonValue *objv, 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 + const CxAllocator * const al = objv->allocator; + CxJsonObject *obj = &(objv->value.object); + + // determine the index where we need to insert the new member + size_t index = cx_array_binary_search_sup( + obj->values, + obj->values_size, + sizeof(CxJsonObjValue), + &member, json_cmp_objvalue ); + + // is the name already present? + if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) { + // free the original value + cx_strfree_a(al, &obj->values[index].name); + cxJsonValueFree(obj->values[index].value); + // replace the item + obj->values[index] = member; + + // nothing more to do + return 0; + } + + // determine the old capacity and reserve for one more element + CxArrayReallocator arealloc = cx_array_reallocator(al, NULL); + size_t oldcap = obj->values_capacity; + if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1; + + // check the new capacity, if we need to realloc the index array + size_t newcap = obj->values_capacity; + if (newcap > oldcap) { + if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) { + return 1; + } + } + + // check if append or insert + if (index < obj->values_size) { + // move the other elements + memmove( + &obj->values[index+1], + &obj->values[index], + (obj->values_size - index) * sizeof(CxJsonObjValue) + ); + // increase indices for the moved elements + for (size_t i = 0; i < obj->values_size ; i++) { + if (obj->indices[i] >= index) { + obj->indices[i]++; + } + } + } + + // insert the element and set the index + obj->values[index] = member; + obj->indices[obj->values_size] = index; + obj->values_size++; + + return 0; } static void token_destroy(CxJsonToken *token) { @@ -323,21 +376,22 @@ } static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { - CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); + CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); if (v == NULL) return NULL; // LCOV_EXCL_LINE // initialize the value + v->type = type; + v->allocator = json->allocator; if (type == CX_JSON_ARRAY) { cx_array_initialize_a(json->allocator, v->value.array.array, 16); 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) goto create_json_value_exit_error; // LCOV_EXCL_LINE - } else { - memset(v, 0, sizeof(CxJsonValue)); + v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t)); + if (v->value.object.values == NULL || + v->value.object.indices == NULL) + goto create_json_value_exit_error; // LCOV_EXCL_LINE } - v->type = type; - v->allocator = json->allocator; // add the new value to a possible parent if (json->vbuf_size > 0) { @@ -377,7 +431,7 @@ return v; // LCOV_EXCL_START create_json_value_exit_error: - cxFree(json->allocator, v); + cxJsonValueFree(v); return NULL; // LCOV_EXCL_STOP } @@ -657,6 +711,7 @@ cx_strfree_a(value->allocator, &obj.values[i].name); } cxFree(value->allocator, obj.values); + cxFree(value->allocator, obj.indices); break; } case CX_JSON_ARRAY: { @@ -684,7 +739,18 @@ v->allocator = allocator; v->type = CX_JSON_OBJECT; cx_array_initialize_a(allocator, v->value.object.values, 16); - if (v->value.object.values == NULL) { cxFree(allocator, v); return NULL; } + if (v->value.object.values == NULL) { // LCOV_EXCL_START + cxFree(allocator, v); + return NULL; + // LCOV_EXCL_STOP + } + v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t)); + if (v->value.object.indices == NULL) { // LCOV_EXCL_START + cxFree(allocator, v->value.object.values); + cxFree(allocator, v); + return NULL; + // LCOV_EXCL_STOP + } return v; } @@ -822,20 +888,15 @@ } int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) { - // TODO: optimize - issue #462 - for (size_t i = 0; i < obj->value.object.values_size; i++) { - if (0 == cx_strcmp(name, cx_strcast(obj->value.object.values[i].name))) { - // free the original value - cxJsonValueFree(obj->value.object.values[i].value); - obj->value.object.values[i].value = child; - return 0; - } - } - cxmutstr k = cx_strdup_a(obj->allocator, name); if (k.ptr == NULL) return -1; CxJsonObjValue kv = {k, child}; - return json_add_objvalue(obj, kv); + if (json_add_objvalue(obj, kv)) { + cx_strfree_a(obj->allocator, &k); + return 1; + } else { + return 0; + } } CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) { @@ -997,12 +1058,22 @@ expected++; } depth++; - CxIterator iter = cxJsonObjIter(value); - // TODO: unsorted output - realize after implementing index array - cx_foreach(CxJsonObjValue*, member, iter) { + size_t elem_count = value->value.object.values_size; + for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { + // get the member either via index array or directly + size_t elem_idx = settings->sort_members + ? look_idx + : value->value.object.indices[look_idx]; + CxJsonObjValue *member = &value->value.object.values[elem_idx]; + if (settings->sort_members) { + depth++;depth--; + } + // possible indentation if (settings->pretty) { - if (cx_json_writer_indent(target, wfunc, settings, depth)) return 1; + if (cx_json_writer_indent(target, wfunc, settings, depth)) { + return 1; // LCOV_EXCL_LINE + } } // the name @@ -1024,7 +1095,7 @@ if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; // end of object-value - if (iter.index < iter.elem_count - 1) { + if (look_idx < elem_count - 1) { const char *obj_value_sep = ",\n"; if (settings->pretty) { actual += wfunc(obj_value_sep, 1, 2, target); @@ -1049,7 +1120,6 @@ break; } case CX_JSON_ARRAY: { - // TODO: implement array wrapping actual += wfunc("[", 1, 1, target); expected++; CxIterator iter = cxJsonArrIter(value);