diff -r 3da24640513a -r e57ca2747782 ucx/cx/json.h --- a/ucx/cx/json.h Sun Dec 07 20:00:33 2025 +0100 +++ b/ucx/cx/json.h Sat Dec 13 15:58:58 2025 +0100 @@ -41,6 +41,7 @@ #include "string.h" #include "buffer.h" #include "array_list.h" +#include "map.h" #include @@ -188,9 +189,10 @@ */ typedef struct cx_json_array_s CxJsonArray; /** - * Type alias for the JSON object struct. + * Type alias for the map representing a JSON object. + * The map contains pointers of type @c CxJsonValue. */ -typedef struct cx_json_object_s CxJsonObject; +typedef CxMap* CxJsonObject; /** * Type alias for a JSON string. */ @@ -209,46 +211,13 @@ typedef enum cx_json_literal CxJsonLiteral; /** - * Type alias for a key/value pair in a JSON object. - */ -typedef struct cx_json_obj_value_s CxJsonObjValue; - -/** * JSON array structure. */ struct cx_json_array_s { /** * The array data. */ - CX_ARRAY_DECLARE(CxJsonValue*, array); -}; - -/** - * JSON object structure. - */ -struct cx_json_object_s { - /** - * The key/value entries. - */ - CX_ARRAY_DECLARE(CxJsonObjValue, values); - /** - * The original indices to reconstruct the order in which the members were added. - */ - size_t *indices; -}; - -/** - * Structure for a key/value entry in a JSON object. - */ -struct cx_json_obj_value_s { - /** - * The key (or name in JSON terminology) of the value. - */ - cxmutstr name; - /** - * The value. - */ - CxJsonValue *value; + CX_ARRAY_DECLARE(CxJsonValue*, data); }; /** @@ -295,7 +264,7 @@ * The literal type if the type is #CX_JSON_LITERAL. */ CxJsonLiteral literal; - } value; + }; }; /** @@ -349,11 +318,11 @@ CxJsonValue *parsed; /** - * A pointer to an intermediate state of a currently parsed object member. + * The name of a not yet completely parsed object member. * * Never access this value manually. */ - CxJsonObjValue uncompleted_member; + cxmutstr uncompleted_member_name; /** * State stack. @@ -439,10 +408,6 @@ */ bool pretty; /** - * Set false to output the members in the order in which they were added. - */ - bool sort_members; - /** * The maximum number of fractional digits in a number value. * The default value is 6 and values larger than 15 are reduced to 15. * Note that the actual number of digits may be lower, depending on the concrete number. @@ -508,6 +473,33 @@ CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value, cx_write_func wfunc, const CxJsonWriter* settings); + +/** + * Produces a compact string representation of the specified JSON value. + * + * @param value the JSON value + * @param allocator the allocator for the string + * @return the produced string + * @see cxJsonWrite() + * @see cxJsonWriterCompact() + * @see cxJsonToPrettyString() + */ +cx_attr_nonnull_arg(1) +CX_EXPORT cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator); + +/** + * Produces a pretty string representation of the specified JSON value. + * + * @param value the JSON value + * @param allocator the allocator for the string + * @return the produced string + * @see cxJsonWrite() + * @see cxJsonWriterPretty() + * @see cxJsonToString() + */ +cx_attr_nonnull_arg(1) +CX_EXPORT cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator); + /** * Initializes the JSON interface. * @@ -530,8 +522,8 @@ /** * Destroys and re-initializes the JSON interface. * - * You might want to use this to reset the parser after - * encountering a syntax error. + * You must use this to reset the parser after encountering a syntax error + * if you want to continue using it. * * @param json the JSON interface */ @@ -556,7 +548,7 @@ * @retval non-zero internal allocation error * @see cxJsonFill() */ -cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_nonnull_arg(1) cx_attr_access_r(2, 3) CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len); @@ -592,6 +584,36 @@ */ #define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str)) + +/** + * Internal function - use cxJsonFromString() instead. + * + * @param allocator the allocator for the JSON value + * @param str the string to parse + * @param value a pointer where the JSON value shall be stored to + * @return status code + */ +cx_attr_nonnull_arg(3) +CX_EXPORT CxJsonStatus cx_json_from_string(const CxAllocator *allocator, + cxstring str, CxJsonValue **value); + +/** + * Parses a string into a JSON value. + * + * @param allocator (@c CxAllocator*) the allocator for the JSON value + * @param str (any string) the string to parse + * @param value (@c CxJsonValue**) a pointer where the JSON value shall be stored to + * @retval CX_JSON_NO_ERROR success + * @retval CX_JSON_NO_DATA the string was empty or blank + * @retval CX_JSON_INCOMPLETE_DATA the string unexpectedly ended + * @retval CX_JSON_BUFFER_ALLOC_FAILED allocating internal buffer space failed + * @retval CX_JSON_VALUE_ALLOC_FAILED allocating memory for the CxJsonValue failed + * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number + * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error + */ +#define cxJsonFromString(allocator, str, value) \ + cx_json_from_string(allocator, cx_strcast(str), value) + /** * Creates a new (empty) JSON object. * @@ -641,28 +663,27 @@ /** * Creates a new JSON string. * + * Internal function - use cxJsonCreateString() instead. + * * @param allocator the allocator to use * @param str the string data * @return the new JSON value or @c NULL if allocation fails - * @see cxJsonCreateString() * @see cxJsonObjPutString() - * @see cxJsonArrAddStrings() + * @see cxJsonArrAddCxStrings() */ -cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2) -CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); +cx_attr_nodiscard +CX_EXPORT CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str); /** * Creates a new JSON string. * - * @param allocator the allocator to use - * @param str the string data - * @return the new JSON value or @c NULL if allocation fails - * @see cxJsonCreateCxString() - * @see cxJsonObjPutCxString() + * @param allocator (@c CxAllocator*) the allocator to use + * @param str the string + * @return (@c CxJsonValue*) the new JSON value or @c NULL if allocation fails + * @see cxJsonObjPutString() * @see cxJsonArrAddCxStrings() */ -cx_attr_nodiscard -CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); +#define cxJsonCreateString(allocator, str) cx_json_create_string(allocator, cx_strcast(str)) /** * Creates a new JSON literal. @@ -760,10 +781,7 @@ /** * Adds or replaces a value within a JSON object. * - * The value will be directly added and not copied. - * - * @note If a value with the specified @p name already exists, - * it will be (recursively) freed with its own allocator. + * Internal function - use cxJsonObjPut(). * * @param obj the JSON object * @param name the name of the value @@ -772,11 +790,29 @@ * @retval non-zero allocation failure */ cx_attr_nonnull -CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); +CX_EXPORT int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child); + +/** + * Adds or replaces a value within a JSON object. + * + * The value will be directly added and not copied. + * + * @note If a value with the specified @p name already exists, + * it will be (recursively) freed with its own allocator. + * + * @param obj (@c CxJsonValue*) the JSON object + * @param name (any string) the name of the value + * @param child (@c CxJsonValue*) the value + * @retval zero success + * @retval non-zero allocation failure + */ +#define cxJsonObjPut(obj, name, child) cx_json_obj_put(obj, cx_strcast(name), child) /** * Creates a new JSON object and adds it to an existing object. * + * Internal function - use cxJsonObjPutObj(). + * * @param obj the target JSON object * @param name the name of the new value * @return the new value or @c NULL if allocation fails @@ -784,11 +820,24 @@ * @see cxJsonCreateObj() */ cx_attr_nonnull -CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name); + +/** + * Creates a new JSON object and adds it to an existing object. + * + * @param obj (@c CxJsonValue*) the target JSON object + * @param name (any string) the name of the new value + * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails + * @see cxJsonObjPut() + * @see cxJsonCreateObj() + */ +#define cxJsonObjPutObj(obj, name) cx_json_obj_put_obj(obj, cx_strcast(name)) /** * Creates a new JSON array and adds it to an object. * + * Internal function - use cxJsonObjPutArr(). + * * @param obj the target JSON object * @param name the name of the new value * @return the new value or @c NULL if allocation fails @@ -796,11 +845,24 @@ * @see cxJsonCreateArr() */ cx_attr_nonnull -CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name); + +/** + * Creates a new JSON array and adds it to an object. + * + * @param obj (@c CxJsonValue*) the target JSON object + * @param name (any string) the name of the new value + * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails + * @see cxJsonObjPut() + * @see cxJsonCreateArr() + */ +#define cxJsonObjPutArr(obj, name) cx_json_obj_put_arr(obj, cx_strcast(name)) /** * Creates a new JSON number and adds it to an object. * + * Internal function - use cxJsonObjPutNumber(). + * * @param obj the target JSON object * @param name the name of the new value * @param num the numeric value @@ -809,11 +871,25 @@ * @see cxJsonCreateNumber() */ cx_attr_nonnull -CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); +CX_EXPORT CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num); + +/** + * Creates a new JSON number and adds it to an object. + * + * @param obj (@c CxJsonValue*) the target JSON object + * @param name (any string) the name of the new value + * @param num (@c double) the numeric value + * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails + * @see cxJsonObjPut() + * @see cxJsonCreateNumber() + */ +#define cxJsonObjPutNumber(obj, name, num) cx_json_obj_put_number(obj, cx_strcast(name), num) /** * Creates a new JSON number, based on an integer, and adds it to an object. * + * Internal function - use cxJsonObjPutInteger(). + * * @param obj the target JSON object * @param name the name of the new value * @param num the numeric value @@ -822,12 +898,24 @@ * @see cxJsonCreateInteger() */ cx_attr_nonnull -CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); +CX_EXPORT CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num); + +/** + * Creates a new JSON number, based on an integer, and adds it to an object. + * + * @param obj (@c CxJsonValue*) the target JSON object + * @param name (any string) the name of the new value + * @param num (@c int64_t) the numeric value + * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails + * @see cxJsonObjPut() + * @see cxJsonCreateInteger() + */ +#define cxJsonObjPutInteger(obj, name, num) cx_json_obj_put_integer(obj, cx_strcast(name), num) /** * Creates a new JSON string and adds it to an object. * - * The string data is copied. + * Internal function - use cxJsonObjPutString() * * @param obj the target JSON object * @param name the name of the new value @@ -836,27 +924,28 @@ * @see cxJsonObjPut() * @see cxJsonCreateString() */ -cx_attr_nonnull cx_attr_cstr_arg(3) -CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); +cx_attr_nonnull +CX_EXPORT CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str); /** * Creates a new JSON string and adds it to an object. * * The string data is copied. * - * @param obj the target JSON object - * @param name the name of the new value - * @param str the string data - * @return the new value or @c NULL if allocation fails + * @param obj (@c CxJsonValue*) the target JSON object + * @param name (any string) the name of the new value + * @param str (any string) the string data + * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails * @see cxJsonObjPut() - * @see cxJsonCreateCxString() + * @see cxJsonCreateString() */ -cx_attr_nonnull -CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); +#define cxJsonObjPutString(obj, name, str) cx_json_obj_put_string(obj, cx_strcast(name), cx_strcast(str)) /** * Creates a new JSON literal and adds it to an object. * + * Internal function - use cxJsonObjPutLiteral(). + * * @param obj the target JSON object * @param name the name of the new value * @param lit the type of literal @@ -865,7 +954,19 @@ * @see cxJsonCreateLiteral() */ cx_attr_nonnull -CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); +CX_EXPORT CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); + +/** + * Creates a new JSON literal and adds it to an object. + * + * @param obj (@c CxJsonValue*) the target JSON object + * @param name (any string) the name of the new value + * @param lit (@c CxJsonLiteral) the type of literal + * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails + * @see cxJsonObjPut() + * @see cxJsonCreateLiteral() + */ +#define cxJsonObjPutLiteral(obj, name, lit) cx_json_obj_put_literal(obj, cx_strcast(name), lit) /** * Recursively deallocates the memory of a JSON value. @@ -998,7 +1099,7 @@ */ cx_attr_nonnull CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) { - return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL; + return cxJsonIsLiteral(value) && value->literal != CX_JSON_NULL; } /** @@ -1015,7 +1116,7 @@ */ cx_attr_nonnull CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) { - return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE; + return cxJsonIsLiteral(value) && value->literal == CX_JSON_TRUE; } /** @@ -1032,7 +1133,7 @@ */ cx_attr_nonnull CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) { - return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE; + return cxJsonIsLiteral(value) && value->literal == CX_JSON_FALSE; } /** @@ -1045,7 +1146,7 @@ */ cx_attr_nonnull CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) { - return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL; + return cxJsonIsLiteral(value) && value->literal == CX_JSON_NULL; } /** @@ -1123,7 +1224,7 @@ */ cx_attr_nonnull CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) { - return value->value.literal == CX_JSON_TRUE; + return value->literal == CX_JSON_TRUE; } /** @@ -1137,7 +1238,7 @@ */ cx_attr_nonnull CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) { - return value->value.array.array_size; + return value->array.data_size; } /** @@ -1188,10 +1289,24 @@ CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value); /** - * Returns an iterator over the JSON object members. + * Returns the size of a JSON object. + * + * If the @p value is not a JSON object, the behavior is undefined. * - * The iterator yields values of type @c CxJsonObjValue* which - * contain the name and value of the member. + * @param value the JSON value + * @return the size of the object, i.e., the number of key/value pairs + * @see cxJsonIsObject() + */ +cx_attr_nonnull +CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) { + return cxCollectionSize(value->object); +} + +/** + * Returns a map iterator over the JSON object members. + * + * The iterator yields values of type @c CxMapEntry* which + * contain the name and the @c CxJsonObjValue* of the member. * * If the @p value is not a JSON object, the behavior is undefined. * @@ -1200,7 +1315,7 @@ * @see cxJsonIsObject() */ cx_attr_nonnull cx_attr_nodiscard -CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value); +CX_EXPORT CxMapIterator cxJsonObjIter(const CxJsonValue *value); /** * Internal function, do not use.