summaryrefslogtreecommitdiffstats
path: root/src/json.h
blob: 53efe2ac73af826d40917339ba2ffda185102def (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
 * @file json.h
 * @author Radek Krejci <rkrejci@cesnet.cz>
 * @brief Generic JSON format parser routines.
 *
 * Copyright (c) 2020 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#ifndef LY_JSON_H_
#define LY_JSON_H_

#include <stddef.h>
#include <stdint.h>

#include "log.h"
#include "set.h"

struct ly_ctx;
struct ly_in;

/* Macro to test if character is whitespace */
#define is_jsonws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)

/* Macro to test if character is valid string character */
#define is_jsonstrchar(c) (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x10ffff))

/* Macro to push JSON parser status */
#define LYJSON_STATUS_PUSH_RET(CTX, STATUS) \
    LY_CHECK_RET(ly_set_add(&CTX->status, (void *)(uintptr_t)(STATUS), 1, NULL))

/* Macro to pop JSON parser status */
#define LYJSON_STATUS_POP_RET(CTX) \
    assert(CTX->status.count); CTX->status.count--;

/**
 * @brief Status of the parser providing information what is expected next (which function is supposed to be called).
 */
enum LYJSON_PARSER_STATUS {
    LYJSON_ERROR,          /* JSON parser error - value is used as an error return code */
    LYJSON_ROOT,           /* JSON document root, used internally */
    LYJSON_OBJECT,         /* JSON object */
    LYJSON_OBJECT_CLOSED,  /* JSON object closed */
    LYJSON_OBJECT_EMPTY,   /* empty JSON object { } */
    LYJSON_ARRAY,          /* JSON array */
    LYJSON_ARRAY_CLOSED,   /* JSON array closed */
    LYJSON_ARRAY_EMPTY,    /* empty JSON array */
    LYJSON_NUMBER,         /* JSON number value */
    LYJSON_STRING,         /* JSON string value */
    LYJSON_FALSE,          /* JSON false value */
    LYJSON_TRUE,           /* JSON true value */
    LYJSON_NULL,           /* JSON null value */
    LYJSON_END             /* end of input data */
};

struct lyjson_ctx {
    const struct ly_ctx *ctx;
    struct ly_in *in;       /* input structure */

    struct ly_set status;   /* stack of ::LYJSON_PARSER_STATUS values corresponding to the JSON items being processed */

    const char *value;      /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
    size_t value_len;       /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
    ly_bool dynamic;        /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
    uint32_t depth;         /* current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */

    struct {
        enum LYJSON_PARSER_STATUS status;
        uint32_t status_count;
        const char *value;
        size_t value_len;
        ly_bool dynamic;
        uint32_t depth;
        const char *input;
    } backup;
};

/**
 * @brief Create a new JSON parser context and start parsing.
 *
 * @param[in] ctx libyang context.
 * @param[in] in JSON string data to parse.
 * @param[in] subtree Whether this is a special case of parsing a subtree (starting with object name).
 * @param[out] jsonctx New JSON context with status referring the parsed value.
 * @return LY_ERR value.
 */
LY_ERR lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx);

/**
 * @brief Get status of the parser as the last/previous parsed token
 *
 * @param[in] jsonctx JSON context to check.
 * @param[in] index Index of the token, starting by 0 for the last token
 * @return ::LYJSON_ERROR in case of invalid index, other ::LYJSON_PARSER_STATUS corresponding to the token.
 */
enum LYJSON_PARSER_STATUS lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index);

/**
 * @brief Get string representation of the JSON context status (token).
 *
 * @param[in] status Context status (aka JSON token)
 * @return String representation of the @p status.
 */
const char *lyjson_token2str(enum LYJSON_PARSER_STATUS status);

/**
 * @brief Move to the next JSON artifact and update parser status.
 *
 * @param[in] jsonctx XML context to move.
 * @param[out] status Optional parameter to provide new parser status
 * @return LY_ERR value.
 */
LY_ERR lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status);

/**
 * @brief Backup the JSON parser context's state To restore the backup, use ::lyjson_ctx_restore().
 * @param[in] jsonctx JSON parser context to backup.
 */
void lyjson_ctx_backup(struct lyjson_ctx *jsonctx);

/**
 * @brief REstore the JSON parser context's state from the backup created by ::lyjson_ctx_backup().
 * @param[in] jsonctx JSON parser context to restore.
 */
void lyjson_ctx_restore(struct lyjson_ctx *jsonctx);

/**
 * @brief Remove the allocated working memory of the context.
 *
 * @param[in] jsonctx JSON context to clear.
 */
void lyjson_ctx_free(struct lyjson_ctx *jsonctx);

#endif /* LY_JSON_H_ */