src/cx/json.h

Tue, 10 Dec 2024 00:19:45 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 10 Dec 2024 00:19:45 +0100
changeset 1007
81b2986d2b04
parent 1002
1483c47063a8
child 1009
7650e722437e
permissions
-rw-r--r--

fix that cxBufferSeek() cannot move pos past the end - fixes #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(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