|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 /** |
|
29 * \file json.h |
|
30 * \brief Interface for parsing data from JSON files. |
|
31 * \author Mike Becker |
|
32 * \author Olaf Wintermann |
|
33 * \copyright 2-Clause BSD License |
|
34 */ |
|
35 |
|
36 #ifndef UCX_JSON_H |
|
37 #define UCX_JSON_H |
|
38 |
|
39 #include "common.h" |
|
40 #include "string.h" |
|
41 |
|
42 #ifdef __cplusplus |
|
43 extern "C" { |
|
44 #endif |
|
45 |
|
46 enum cx_json_token_type { |
|
47 CX_JSON_NO_TOKEN, |
|
48 CX_JSON_TOKEN_ERROR, |
|
49 CX_JSON_TOKEN_BEGIN_ARRAY, |
|
50 CX_JSON_TOKEN_BEGIN_OBJECT, |
|
51 CX_JSON_TOKEN_END_ARRAY, |
|
52 CX_JSON_TOKEN_END_OBJECT, |
|
53 CX_JSON_TOKEN_NAME_SEPARATOR, |
|
54 CX_JSON_TOKEN_VALUE_SEPARATOR, |
|
55 CX_JSON_TOKEN_STRING, |
|
56 CX_JSON_TOKEN_INTEGER, |
|
57 CX_JSON_TOKEN_NUMBER, |
|
58 CX_JSON_TOKEN_LITERAL, |
|
59 CX_JSON_TOKEN_SPACE |
|
60 }; |
|
61 |
|
62 enum cx_json_value_type { |
|
63 CX_JSON_NOTHING, // this allows us to always return non-NULL values |
|
64 CX_JSON_OBJECT, |
|
65 CX_JSON_ARRAY, |
|
66 CX_JSON_STRING, |
|
67 CX_JSON_INTEGER, // TODO: the spec does not know integer types |
|
68 CX_JSON_NUMBER, |
|
69 CX_JSON_LITERAL |
|
70 }; |
|
71 |
|
72 enum cx_json_literal_type { |
|
73 CX_JSON_NULL, |
|
74 CX_JSON_TRUE, |
|
75 CX_JSON_FALSE |
|
76 }; |
|
77 |
|
78 enum cx_json_reader_type { |
|
79 CX_JSON_READER_OBJECT_BEGIN, |
|
80 CX_JSON_READER_OBJECT_END, |
|
81 CX_JSON_READER_ARRAY_BEGIN, |
|
82 CX_JSON_READER_ARRAY_END, |
|
83 CX_JSON_READER_STRING, |
|
84 CX_JSON_READER_INTEGER, |
|
85 CX_JSON_READER_NUMBER, |
|
86 CX_JSON_READER_LITERAL |
|
87 }; |
|
88 |
|
89 typedef enum cx_json_token_type CxJsonTokenType; |
|
90 typedef enum cx_json_value_type CxJsonValueType; |
|
91 typedef enum cx_json_literal_type CxJsonLiteralType; |
|
92 typedef enum cx_json_reader_type CxJsonReaderType; |
|
93 |
|
94 typedef struct cx_json_s CxJson; |
|
95 typedef struct cx_json_token_s CxJsonToken; |
|
96 |
|
97 typedef struct cx_json_value_s CxJsonValue; |
|
98 |
|
99 typedef struct cx_json_array_s CxJsonArray; |
|
100 typedef struct cx_json_object_s CxJsonObject; |
|
101 typedef struct cx_mutstr_s CxJsonString; |
|
102 typedef struct cx_json_integer_s CxJsonInteger; |
|
103 typedef struct cx_json_number_s CxJsonNumber; |
|
104 typedef struct cx_json_literal_s CxJsonLiteral; |
|
105 |
|
106 typedef struct cx_json_obj_value_s CxJsonObjValue; |
|
107 |
|
108 struct cx_json_token_s { |
|
109 CxJsonTokenType tokentype; |
|
110 const char *content; |
|
111 size_t length; |
|
112 size_t alloc; |
|
113 }; |
|
114 |
|
115 struct cx_json_s { |
|
116 const char *buffer; |
|
117 size_t size; |
|
118 size_t pos; |
|
119 |
|
120 CxJsonToken uncompleted; |
|
121 int tokenizer_escape; |
|
122 |
|
123 int *states; |
|
124 int nstates; |
|
125 int states_alloc; |
|
126 |
|
127 CxJsonToken reader_token; |
|
128 CxJsonReaderType reader_type; |
|
129 int value_ready; |
|
130 char *value_name; |
|
131 size_t value_name_len; |
|
132 char *value_str; |
|
133 size_t value_str_len; |
|
134 int64_t value_int; |
|
135 double value_double; |
|
136 |
|
137 CxJsonValue **readvalue_stack; |
|
138 int readvalue_nelm; |
|
139 int readvalue_alloc; |
|
140 CxJsonValue *read_value; |
|
141 int readvalue_initialized; |
|
142 |
|
143 int reader_array_alloc; |
|
144 |
|
145 int error; |
|
146 }; |
|
147 |
|
148 struct cx_json_array_s { |
|
149 CxJsonValue **array; |
|
150 size_t alloc; |
|
151 size_t size; |
|
152 }; |
|
153 |
|
154 struct cx_json_object_s { |
|
155 CxJsonObjValue *values; |
|
156 size_t alloc; |
|
157 size_t size; |
|
158 }; |
|
159 |
|
160 struct cx_json_obj_value_s { |
|
161 char *name; |
|
162 CxJsonValue *value; |
|
163 }; |
|
164 |
|
165 // TODO: remove single member structs |
|
166 |
|
167 struct cx_json_integer_s { |
|
168 int64_t value; |
|
169 }; |
|
170 |
|
171 struct cx_json_number_s { |
|
172 double value; |
|
173 }; |
|
174 |
|
175 struct cx_json_literal_s { |
|
176 CxJsonLiteralType literal; |
|
177 }; |
|
178 |
|
179 struct cx_json_value_s { |
|
180 CxJsonValueType type; |
|
181 union { |
|
182 CxJsonArray array; |
|
183 CxJsonObject object; |
|
184 CxJsonString string; |
|
185 CxJsonInteger integer; |
|
186 CxJsonNumber number; |
|
187 CxJsonLiteral literal; |
|
188 } value; |
|
189 }; |
|
190 |
|
191 // TODO: add support for CxAllocator |
|
192 |
|
193 __attribute__((__nonnull__)) |
|
194 void cxJsonInit(CxJson *json); |
|
195 |
|
196 __attribute__((__nonnull__)) |
|
197 void cxJsonDestroy(CxJson *json); |
|
198 |
|
199 __attribute__((__nonnull__)) |
|
200 void cxJsonFill(CxJson *json, const char *buf, size_t len); |
|
201 |
|
202 // TODO: discuss if it is intentional that cxJsonNext() will usually parse an entire file in one go |
|
203 __attribute__((__nonnull__)) |
|
204 int cxJsonNext(CxJson *json, CxJsonValue **value); |
|
205 |
|
206 void cxJsonValueFree(CxJsonValue *value); |
|
207 |
|
208 __attribute__((__nonnull__)) |
|
209 static inline bool cxJsonIsObject(CxJsonValue *value) { |
|
210 return value->type == CX_JSON_OBJECT; |
|
211 } |
|
212 |
|
213 __attribute__((__nonnull__)) |
|
214 static inline bool cxJsonIsArray(CxJsonValue *value) { |
|
215 return value->type == CX_JSON_ARRAY; |
|
216 } |
|
217 |
|
218 __attribute__((__nonnull__)) |
|
219 static inline bool cxJsonIsString(CxJsonValue *value) { |
|
220 return value->type == CX_JSON_STRING; |
|
221 } |
|
222 |
|
223 __attribute__((__nonnull__)) |
|
224 static inline bool cxJsonIsNumber(CxJsonValue *value) { |
|
225 // TODO: this is not good, because an integer is also a number |
|
226 return value->type == CX_JSON_NUMBER; |
|
227 } |
|
228 |
|
229 __attribute__((__nonnull__)) |
|
230 static inline bool cxJsonIsInteger(CxJsonValue *value) { |
|
231 return value->type == CX_JSON_INTEGER; |
|
232 } |
|
233 |
|
234 __attribute__((__nonnull__)) |
|
235 static inline bool cxJsonIsLiteral(CxJsonValue *value) { |
|
236 return value->type == CX_JSON_LITERAL; |
|
237 } |
|
238 |
|
239 __attribute__((__nonnull__)) |
|
240 static inline bool cxJsonIsBool(CxJsonValue *value) { |
|
241 return cxJsonIsLiteral(value) && value->value.literal.literal != CX_JSON_NULL; |
|
242 } |
|
243 |
|
244 __attribute__((__nonnull__)) |
|
245 static inline bool cxJsonIsTrue(CxJsonValue *value) { |
|
246 return cxJsonIsLiteral(value) && value->value.literal.literal == CX_JSON_TRUE; |
|
247 } |
|
248 |
|
249 __attribute__((__nonnull__)) |
|
250 static inline bool cxJsonIsFalse(CxJsonValue *value) { |
|
251 return cxJsonIsLiteral(value) && value->value.literal.literal == CX_JSON_FALSE; |
|
252 } |
|
253 |
|
254 __attribute__((__nonnull__)) |
|
255 static inline bool cxJsonIsNull(CxJsonValue *value) { |
|
256 return cxJsonIsLiteral(value) && value->value.literal.literal == CX_JSON_NULL; |
|
257 } |
|
258 |
|
259 __attribute__((__nonnull__)) |
|
260 static inline cxmutstr cxJsonAsString(CxJsonValue *value) { |
|
261 // TODO: do we need a separate method to return this directly as cxstring? |
|
262 return value->value.string; |
|
263 } |
|
264 |
|
265 __attribute__((__nonnull__)) |
|
266 static inline double cxJsonAsDouble(CxJsonValue *value) { |
|
267 return value->value.number.value; |
|
268 } |
|
269 |
|
270 __attribute__((__nonnull__)) |
|
271 static inline int64_t cxJsonAsInteger(CxJsonValue *value) { |
|
272 return value->value.integer.value; |
|
273 } |
|
274 |
|
275 __attribute__((__nonnull__)) |
|
276 static inline bool cxJsonAsBool(CxJsonValue *value) { |
|
277 return value->value.literal.literal == CX_JSON_TRUE; |
|
278 } |
|
279 |
|
280 __attribute__((__nonnull__)) |
|
281 static inline size_t cxJsonArrSize(CxJsonValue *value) { |
|
282 return value->value.array.size; |
|
283 } |
|
284 |
|
285 __attribute__((__nonnull__, __returns_nonnull__)) |
|
286 CxJsonValue *cxJsonArrGet(CxJsonValue *value, size_t index); |
|
287 |
|
288 // TODO: add cxJsonArrIter() |
|
289 |
|
290 // TODO: implement cxJsonObjGet as a _Generic with support for cxstring |
|
291 __attribute__((__nonnull__, __returns_nonnull__)) |
|
292 CxJsonValue *cxJsonObjGet(CxJsonValue *value, const char* name); |
|
293 |
|
294 #ifdef __cplusplus |
|
295 } |
|
296 #endif |
|
297 |
|
298 #endif /* UCX_JSON_H */ |
|
299 |