diff -r b60487c3ec36 -r af685cc9d623 ucx/json.c --- a/ucx/json.c Sun Aug 31 14:39:13 2025 +0200 +++ b/ucx/json.c Sat Nov 08 23:06:11 2025 +0100 @@ -32,6 +32,7 @@ #include #include #include +#include /* * RFC 8259 @@ -46,22 +47,17 @@ return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name)); } -static CxJsonObjValue *json_find_objvalue(const CxJsonValue *obj, cxstring name) { +static size_t 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( + return 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 *objv, CxJsonObjValue member) { @@ -132,16 +128,6 @@ } } -static bool json_isdigit(char c) { - // TODO: remove once UCX has public API for this - return c >= '0' && c <= '9'; -} - -static bool json_isspace(char c) { - // TODO: remove once UCX has public API for this - return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; -} - static int num_isexp(const char *content, size_t length, size_t pos) { if (pos >= length) { return 0; @@ -150,7 +136,7 @@ int ok = 0; for (size_t i = pos; i < length; i++) { char c = content[i]; - if (json_isdigit(c)) { + if (isdigit((unsigned char)c)) { ok = 1; } else if (i == pos) { if (!(c == '+' || c == '-')) { @@ -167,7 +153,7 @@ static CxJsonTokenType token_numbertype(const char *content, size_t length) { if (length == 0) return CX_JSON_TOKEN_ERROR; - if (content[0] != '-' && !json_isdigit(content[0])) { + if (content[0] != '-' && !isdigit((unsigned char)content[0])) { return CX_JSON_TOKEN_ERROR; } @@ -180,7 +166,7 @@ type = CX_JSON_TOKEN_NUMBER; } else if (content[i] == 'e' || content[i] == 'E') { return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR; - } else if (!json_isdigit(content[i])) { + } else if (!isdigit((unsigned char)content[i])) { return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep } } @@ -244,7 +230,7 @@ return CX_JSON_TOKEN_STRING; } default: { - if (json_isspace(c)) { + if (isspace((unsigned char)c)) { return CX_JSON_TOKEN_SPACE; } } @@ -500,7 +486,7 @@ if (all_printable && escape) { size_t capa = str.length + 32; - char *space = malloc(capa); + char *space = cxMallocDefault(capa); if (space == NULL) return cx_mutstrn(NULL, 0); cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); cxBufferWrite(str.ptr, 1, i, &buf); @@ -631,10 +617,10 @@ void cxJsonDestroy(CxJson *json) { cxBufferDestroy(&json->buffer); if (json->states != json->states_internal) { - free(json->states); + cxFreeDefault(json->states); } if (json->vbuf != json->vbuf_internal) { - free(json->vbuf); + cxFreeDefault(json->vbuf); } cxJsonValueFree(json->parsed); json->parsed = NULL; @@ -644,6 +630,12 @@ } } +void cxJsonReset(CxJson *json) { + const CxAllocator *allocator = json->allocator; + cxJsonDestroy(json); + cxJsonInit(json, allocator); +} + int cxJsonFilln(CxJson *json, const char *buf, size_t size) { if (cxBufferEof(&json->buffer)) { // reinitialize the buffer @@ -984,67 +976,67 @@ if (values[i] == NULL) break; cxJsonValueFree(values[i]); } - free(values); + cxFreeDefault(values); } // LCOV_EXCL_STOP int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { - CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); + CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateNumber(arr->allocator, num[i]); if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); - free(values); + cxFreeDefault(values); return ret; } int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) { - CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); + CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateInteger(arr->allocator, num[i]); if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); - free(values); + cxFreeDefault(values); return ret; } int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) { - CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); + CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateString(arr->allocator, str[i]); if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); - free(values); + cxFreeDefault(values); return ret; } int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) { - CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); + CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateCxString(arr->allocator, str[i]); if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); - free(values); + cxFreeDefault(values); return ret; } int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) { - CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); + CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]); if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); - free(values); + cxFreeDefault(values); return ret; } @@ -1126,10 +1118,53 @@ return value->value.array.array[index]; } +CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { + if (index >= value->value.array.array_size) { + return NULL; + } + CxJsonValue *ret = value->value.array.array[index]; + // TODO: replace with a low level cx_array_remove() + size_t count = value->value.array.array_size - index - 1; + if (count > 0) { + memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); + } + value->value.array.array_size--; + return ret; +} + +char *cxJsonAsString(const CxJsonValue *value) { + return value->value.string.ptr; +} + +cxstring cxJsonAsCxString(const CxJsonValue *value) { + return cx_strcast(value->value.string); +} + +cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { + return value->value.string; +} + +double cxJsonAsDouble(const CxJsonValue *value) { + if (value->type == CX_JSON_INTEGER) { + return (double) value->value.integer; + } else { + return value->value.number; + } +} + +int64_t cxJsonAsInteger(const CxJsonValue *value) { + if (value->type == CX_JSON_INTEGER) { + return value->value.integer; + } else { + return (int64_t) value->value.number; + } +} + CxIterator cxJsonArrIter(const CxJsonValue *value) { return cxIteratorPtr( value->value.array.array, - value->value.array.array_size + value->value.array.array_size, + true // arrays need to keep order ); } @@ -1137,16 +1172,31 @@ return cxIterator( value->value.object.values, sizeof(CxJsonObjValue), - value->value.object.values_size + value->value.object.values_size, + true // TODO: objects do not always need to keep order ); } -CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) { - CxJsonObjValue *member = json_find_objvalue(value, name); - if (member == NULL) { +CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { + size_t index = json_find_objvalue(value, name); + if (index >= value->value.object.values_size) { return &cx_json_value_nothing; } else { - return member->value; + return value->value.object.values[index].value; + } +} + +CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { + size_t index = json_find_objvalue(value, name); + if (index >= value->value.object.values_size) { + return NULL; + } else { + CxJsonObjValue kv = value->value.object.values[index]; + cx_strfree_a(value->allocator, &kv.name); + // TODO: replace with cx_array_remove() / cx_array_remove_fast() + value->value.object.values_size--; + memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); + return kv.value; } }