ucx/json.c

changeset 888
af685cc9d623
parent 854
1c8401ece69e
--- 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 <assert.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <ctype.h>
 
 /*
  * 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;
     }
 }
 

mercurial