Sun, 15 Dec 2024 11:11:16 +0100
and changelog entry for issue #523
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * \file json.h * \brief Interface for parsing data from JSON files. * \author Mike Becker * \author Olaf Wintermann * \copyright 2-Clause BSD License */ #ifndef UCX_JSON_H #define UCX_JSON_H #include "common.h" #include "allocator.h" #include "string.h" #include "buffer.h" #include "array_list.h" #ifdef __cplusplus extern "C" { #endif enum cx_json_token_type { CX_JSON_NO_TOKEN, CX_JSON_TOKEN_ERROR, CX_JSON_TOKEN_BEGIN_ARRAY, CX_JSON_TOKEN_BEGIN_OBJECT, CX_JSON_TOKEN_END_ARRAY, CX_JSON_TOKEN_END_OBJECT, CX_JSON_TOKEN_NAME_SEPARATOR, CX_JSON_TOKEN_VALUE_SEPARATOR, CX_JSON_TOKEN_STRING, CX_JSON_TOKEN_INTEGER, CX_JSON_TOKEN_NUMBER, CX_JSON_TOKEN_LITERAL, CX_JSON_TOKEN_SPACE }; enum cx_json_value_type { CX_JSON_NOTHING, // this allows us to always return non-NULL values CX_JSON_OBJECT, CX_JSON_ARRAY, CX_JSON_STRING, CX_JSON_INTEGER, CX_JSON_NUMBER, CX_JSON_LITERAL }; enum cx_json_literal { CX_JSON_NULL, CX_JSON_TRUE, CX_JSON_FALSE }; typedef enum cx_json_token_type CxJsonTokenType; typedef enum cx_json_value_type CxJsonValueType; typedef struct cx_json_s CxJson; typedef struct cx_json_token_s CxJsonToken; typedef struct cx_json_value_s CxJsonValue; typedef struct cx_json_array_s CxJsonArray; typedef struct cx_json_object_s CxJsonObject; typedef struct cx_mutstr_s CxJsonString; typedef int64_t CxJsonInteger; typedef double CxJsonNumber; typedef enum cx_json_literal CxJsonLiteral; typedef struct cx_json_obj_value_s CxJsonObjValue; struct cx_json_array_s { CX_ARRAY_DECLARE(CxJsonValue*, array); }; struct cx_json_object_s { CX_ARRAY_DECLARE(CxJsonObjValue, values); }; struct cx_json_obj_value_s { char *name; CxJsonValue *value; }; struct cx_json_value_s { const CxAllocator *allocator; CxJsonValueType type; union { CxJsonArray array; CxJsonObject object; CxJsonString string; CxJsonInteger integer; CxJsonNumber number; CxJsonLiteral literal; } value; }; struct cx_json_token_s { CxJsonTokenType tokentype; bool allocated; cxmutstr content; }; struct cx_json_s { const CxAllocator *allocator; CxBuffer buffer; CxJsonToken uncompleted; /** * A pointer to an intermediate state of the currently parsed value. * * Never access this value manually. */ CxJsonValue *parsed; /** * State stack. */ CX_ARRAY_DECLARE_SIZED(int, states, unsigned); /** * Value buffer stack. */ CX_ARRAY_DECLARE_SIZED(CxJsonValue*, vbuf, unsigned); /** * Internally reserved memory for the state stack. */ int states_internal[8]; /** * Internally reserved memory for the value buffer stack. */ CxJsonValue* vbuf_internal[8]; int error; // TODO: currently unused bool tokenizer_escape; // TODO: check if it can be replaced with look-behind }; /** * Status codes for the json interface. */ enum cx_json_status { /** * Everything is fine. */ CX_JSON_NO_ERROR, /** * The input buffer does not contain more data. */ CX_JSON_NO_DATA, /** * The input ends unexpectedly. * * Refill the buffer with cxJsonFill() to complete the json data. */ CX_JSON_INCOMPLETE_DATA, /** * Not used as a status and never returned by any function. * * You can use this enumerator to check for all "good" status results * by checking if the status is less than \c CX_JSON_OK. * * A "good" status means, that you can refill data and continue parsing. */ CX_JSON_OK, /** * Allocating memory for the internal buffer failed. */ CX_JSON_BUFFER_ALLOC_FAILED, /** * Allocating memory for a json value failed. */ CX_JSON_VALUE_ALLOC_FAILED, /** * A number value is incorrectly formatted. */ CX_JSON_FORMAT_ERROR_NUMBER, /** * The tokenizer found something unexpected. */ CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN }; /** * Typedef for the json status enum. */ typedef enum cx_json_status CxJsonStatus; cx_attr_nonnull_arg(1) void cxJsonInit(CxJson *json, const CxAllocator *allocator); cx_attr_nonnull void cxJsonDestroy(CxJson *json); cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonFilln(CxJson *json, const char *buf, size_t len); #ifdef __cplusplus } // extern "C" cx_attr_nonnull static inline int cxJsonFill( CxJson *json, cxstring str ) { return cxJsonFilln(json, str.ptr, str.length); } cx_attr_nonnull static inline int cxJsonFill( CxJson *json, cxmutstr str ) { return cxJsonFilln(json, str.ptr, str.length); } cx_attr_nonnull cx_attr_cstr_arg(2) static inline int cxJsonFill( CxJson *json, const char *str ) { return cxJsonFilln(json, str, strlen(str)); } extern "C" { #else // __cplusplus #define cxJsonFill(json, str) _Generic((str), \ cxstring: cx_json_fill_cxstr, \ cxmutstr: cx_json_fill_mutstr, \ char*: cx_json_fill_str, \ const char*: cx_json_fill_str) \ (json, str) cx_attr_nonnull static inline int cx_json_fill_cxstr( CxJson *json, cxstring str ) { return cxJsonFilln(json, str.ptr, str.length); } cx_attr_nonnull static inline int cx_json_fill_mutstr( CxJson *json, cxmutstr str ) { return cxJsonFilln(json, str.ptr, str.length); } cx_attr_nonnull cx_attr_cstr_arg(2) static inline int cx_json_fill_str( CxJson *json, const char *str ) { return cxJsonFilln(json, str, strlen(str)); } #endif void cxJsonValueFree(CxJsonValue *value); cx_attr_nonnull CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); cx_attr_nonnull static inline bool cxJsonIsObject(const CxJsonValue *value) { return value->type == CX_JSON_OBJECT; } cx_attr_nonnull static inline bool cxJsonIsArray(const CxJsonValue *value) { return value->type == CX_JSON_ARRAY; } cx_attr_nonnull static inline bool cxJsonIsString(const CxJsonValue *value) { return value->type == CX_JSON_STRING; } cx_attr_nonnull static inline bool cxJsonIsNumber(const CxJsonValue *value) { return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER; } cx_attr_nonnull static inline bool cxJsonIsInteger(const CxJsonValue *value) { return value->type == CX_JSON_INTEGER; } cx_attr_nonnull static inline bool cxJsonIsLiteral(const CxJsonValue *value) { return value->type == CX_JSON_LITERAL; } cx_attr_nonnull static inline bool cxJsonIsBool(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL; } cx_attr_nonnull static inline bool cxJsonIsTrue(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE; } cx_attr_nonnull static inline bool cxJsonIsFalse(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE; } cx_attr_nonnull static inline bool cxJsonIsNull(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL; } cx_attr_nonnull cx_attr_returns_nonnull static inline char *cxJsonAsString(const CxJsonValue *value) { return value->value.string.ptr; } cx_attr_nonnull static inline cxstring cxJsonAsCxString(const CxJsonValue *value) { return cx_strcast(value->value.string); } cx_attr_nonnull static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { return value->value.string; } cx_attr_nonnull static inline double cxJsonAsDouble(const CxJsonValue *value) { if (value->type == CX_JSON_INTEGER) { return (double) value->value.integer; } else { return value->value.number; } } cx_attr_nonnull static inline int64_t cxJsonAsInteger(const CxJsonValue *value) { if (value->type == CX_JSON_INTEGER) { return value->value.integer; } else { return (int64_t) value->value.number; } } cx_attr_nonnull static inline bool cxJsonAsBool(const CxJsonValue *value) { return value->value.literal == CX_JSON_TRUE; } cx_attr_nonnull static inline size_t cxJsonArrSize(const CxJsonValue *value) { return value->value.array.array_size; } cx_attr_nonnull cx_attr_returns_nonnull CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); // TODO: add cxJsonArrIter() // TODO: implement cxJsonObjGet as a _Generic with support for cxstring cx_attr_nonnull cx_attr_returns_nonnull cx_attr_cstr_arg(2) CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char* name); #ifdef __cplusplus } #endif #endif /* UCX_JSON_H */