src/cx/json.h

Mon, 18 Nov 2024 22:05:42 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 18 Nov 2024 22:05:42 +0100
changeset 988
15b3ca7ee33f
parent 985
68754c7de906
permissions
-rw-r--r--

make ucx C++ compatible again (and add tests for it) - fixes #486

/*
 * 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 "string.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
};

enum cx_json_reader_type {
    CX_JSON_READER_OBJECT_BEGIN,
    CX_JSON_READER_OBJECT_END,
    CX_JSON_READER_ARRAY_BEGIN,
    CX_JSON_READER_ARRAY_END,
    CX_JSON_READER_STRING,
    CX_JSON_READER_INTEGER,
    CX_JSON_READER_NUMBER,
    CX_JSON_READER_LITERAL
};

typedef enum cx_json_token_type CxJsonTokenType;
typedef enum cx_json_value_type CxJsonValueType;
typedef enum cx_json_reader_type CxJsonReaderType;

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_token_s {
    CxJsonTokenType tokentype;
    const char *content;
    size_t length;
    size_t alloc;
};

struct cx_json_s {
    const char *buffer;
    size_t size;
    size_t pos;

    CxJsonToken uncompleted;
    int tokenizer_escape;

    int *states;
    size_t nstates;
    size_t states_alloc;
    int states_internal[8];

    CxJsonToken reader_token;
    CxJsonReaderType reader_type;
    int value_ready;
    char *value_name;
    size_t value_name_len;
    char *value_str;
    size_t value_str_len;
    int64_t value_int;
    double value_double;

    CxJsonValue **readvalue_stack;
    unsigned readvalue_nelm;
    unsigned readvalue_alloc;
    CxJsonValue *read_value;
    int readvalue_initialized;

    unsigned reader_array_alloc;

    int error;
};

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 {
    CxJsonValueType type;
    union {
        CxJsonArray array;
        CxJsonObject object;
        CxJsonString string;
        CxJsonInteger integer;
        CxJsonNumber number;
        CxJsonLiteral literal;
    } value;
};

// TODO: add support for CxAllocator

cx_attr_nonnull
void cxJsonInit(CxJson *json);

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);

// TODO: if the CxJsonValue was a returned value, we could reference cxJsonValueFree() as deallocator
cx_attr_nonnull
int cxJsonNext(CxJson *json, CxJsonValue **value);

cx_attr_nonnull
static inline bool cxJsonIsObject(CxJsonValue *value) {
    return value->type == CX_JSON_OBJECT;
}

cx_attr_nonnull
static inline bool cxJsonIsArray(CxJsonValue *value) {
    return value->type == CX_JSON_ARRAY;
}

cx_attr_nonnull
static inline bool cxJsonIsString(CxJsonValue *value) {
    return value->type == CX_JSON_STRING;
}

cx_attr_nonnull
static inline bool cxJsonIsNumber(CxJsonValue *value) {
    return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER;
}

cx_attr_nonnull
static inline bool cxJsonIsInteger(CxJsonValue *value) {
    return value->type == CX_JSON_INTEGER;
}

cx_attr_nonnull
static inline bool cxJsonIsLiteral(CxJsonValue *value) {
    return value->type == CX_JSON_LITERAL;
}

cx_attr_nonnull
static inline bool cxJsonIsBool(CxJsonValue *value) {
    return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL;
}

cx_attr_nonnull
static inline bool cxJsonIsTrue(CxJsonValue *value) {
    return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE;
}

cx_attr_nonnull
static inline bool cxJsonIsFalse(CxJsonValue *value) {
    return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE;
}

cx_attr_nonnull
static inline bool cxJsonIsNull(CxJsonValue *value) {
    return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL;
}

cx_attr_nonnull
cx_attr_returns_nonnull
static inline char *cxJsonAsString(CxJsonValue *value) {
    return value->value.string.ptr;
}

cx_attr_nonnull
static inline cxstring cxJsonAsCxString(CxJsonValue *value) {
    return cx_strcast(value->value.string);
}

cx_attr_nonnull
static inline cxmutstr cxJsonAsCxMutStr(CxJsonValue *value) {
    return value->value.string;
}

cx_attr_nonnull
static inline double cxJsonAsDouble(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(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(CxJsonValue *value) {
    return value->value.literal == CX_JSON_TRUE;
}

cx_attr_nonnull
static inline size_t cxJsonArrSize(CxJsonValue *value) {
    return value->value.array.array_size;
}

cx_attr_nonnull
cx_attr_returns_nonnull
CxJsonValue *cxJsonArrGet(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(CxJsonValue *value, const char* name);

#ifdef __cplusplus
}
#endif

#endif /* UCX_JSON_H */

mercurial