src/cx/json.h

Sat, 19 Oct 2024 17:25:11 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 19 Oct 2024 17:25:11 +0200
changeset 937
10123f4d5618
permissions
-rw-r--r--

add first draft of json implementation - relates to #431

/*
 * 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"

#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, // TODO: the spec does not know integer types
    CX_JSON_NUMBER,
    CX_JSON_LITERAL
};

enum cx_json_literal_type {
    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_literal_type CxJsonLiteralType;
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 struct cx_json_integer_s CxJsonInteger;
typedef struct cx_json_number_s CxJsonNumber;
typedef struct cx_json_literal_s 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;
    int nstates;
    int states_alloc;

    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;
    int readvalue_nelm;
    int readvalue_alloc;
    CxJsonValue *read_value;
    int readvalue_initialized;

    int reader_array_alloc;

    int error;
};

struct cx_json_array_s {
    CxJsonValue **array;
    size_t alloc;
    size_t size;
};

struct cx_json_object_s {
    CxJsonObjValue *values;
    size_t alloc;
    size_t size;
};

struct cx_json_obj_value_s {
    char *name;
    CxJsonValue *value;
};

// TODO: remove single member structs

struct cx_json_integer_s {
    int64_t value;
};

struct cx_json_number_s {
    double value;
};

struct cx_json_literal_s {
    CxJsonLiteralType literal;
};

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

__attribute__((__nonnull__))
void cxJsonInit(CxJson *json);

__attribute__((__nonnull__))
void cxJsonDestroy(CxJson *json);

__attribute__((__nonnull__))
void cxJsonFill(CxJson *json, const char *buf, size_t len);

// TODO: discuss if it is intentional that cxJsonNext() will usually parse an entire file in one go
__attribute__((__nonnull__))
int cxJsonNext(CxJson *json, CxJsonValue **value);

void cxJsonValueFree(CxJsonValue *value);

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

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

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

__attribute__((__nonnull__))
static inline bool cxJsonIsNumber(CxJsonValue *value) {
    // TODO: this is not good, because an integer is also a number
    return value->type == CX_JSON_NUMBER;
}

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

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

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

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

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

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

__attribute__((__nonnull__))
static inline cxmutstr cxJsonAsString(CxJsonValue *value) {
    // TODO: do we need a separate method to return this directly as cxstring?
    return value->value.string;
}

__attribute__((__nonnull__))
static inline double cxJsonAsDouble(CxJsonValue *value) {
    return value->value.number.value;
}

__attribute__((__nonnull__))
static inline int64_t cxJsonAsInteger(CxJsonValue *value) {
    return value->value.integer.value;
}

__attribute__((__nonnull__))
static inline bool cxJsonAsBool(CxJsonValue *value) {
    return value->value.literal.literal == CX_JSON_TRUE;
}

__attribute__((__nonnull__))
static inline size_t cxJsonArrSize(CxJsonValue *value) {
    return value->value.array.size;
}

__attribute__((__nonnull__, __returns_nonnull__))
CxJsonValue *cxJsonArrGet(CxJsonValue *value, size_t index);

// TODO: add cxJsonArrIter()

// TODO: implement cxJsonObjGet as a _Generic with support for cxstring
__attribute__((__nonnull__, __returns_nonnull__))
CxJsonValue *cxJsonObjGet(CxJsonValue *value, const char* name);

#ifdef __cplusplus
}
#endif

#endif /* UCX_JSON_H */

mercurial