1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3 * Copyright (C) 2021-2024 Cyril Hrubis <metan@ucw.cz>
4 */
5
6 /**
7 * @file ujson_reader.h
8 * @brief A recursive descend JSON parser.
9 *
10 * All the function that parse JSON return zero on success and non-zero on a
11 * failure. Once an error has happened all subsequent attempts to parse more
12 * return with non-zero exit status immediatelly. This is designed so that we
13 * can parse several values without checking each return value and only check
14 * if error has happened at the end of the sequence.
15 */
16
17 #ifndef UJSON_READER_H
18 #define UJSON_READER_H
19
20 #include <stdio.h>
21 #include <ujson_common.h>
22
23 /**
24 * @brief An ujson_reader initializer with default values.
25 *
26 * @param buf A pointer to a buffer with JSON data.
27 * @param buf_len A JSON data buffer lenght.
28 * @param rflags enum ujson_reader_flags.
29 *
30 * @return An ujson_reader initialized with default values.
31 */
32 #define UJSON_READER_INIT(buf, buf_len, rflags) { \
33 .max_depth = UJSON_RECURSION_MAX, \
34 .err_print = UJSON_ERR_PRINT, \
35 .err_print_priv = UJSON_ERR_PRINT_PRIV, \
36 .json = buf, \
37 .len = buf_len, \
38 .flags = rflags \
39 }
40
41 /** @brief Reader flags. */
42 enum ujson_reader_flags {
43 /** @brief If set warnings are treated as errors. */
44 UJSON_READER_STRICT = 0x01,
45 };
46
47 /**
48 * @brief A JSON parser internal state.
49 */
50 struct ujson_reader {
51 /** Pointer to a null terminated JSON string */
52 const char *json;
53 /** A length of the JSON string */
54 size_t len;
55 /** A current offset into the JSON string */
56 size_t off;
57 /** An offset to the start of the last array or object */
58 size_t sub_off;
59 /** Recursion depth increased when array/object is entered decreased on leave */
60 unsigned int depth;
61 /** Maximal recursion depth */
62 unsigned int max_depth;
63
64 /** Reader flags. */
65 enum ujson_reader_flags flags;
66
67 /** Handler to print errors and warnings */
68 void (*err_print)(void *err_print_priv, const char *line);
69 void *err_print_priv;
70
71 char err[UJSON_ERR_MAX];
72 char buf[];
73 };
74
75 /**
76 * @brief An ujson_val initializer.
77 *
78 * @param sbuf A pointer to a buffer used for string values.
79 * @param sbuf_size A length of the buffer used for string values.
80 *
81 * @return An ujson_val initialized with default values.
82 */
83 #define UJSON_VAL_INIT(sbuf, sbuf_size) { \
84 .buf = sbuf, \
85 .buf_size = sbuf_size, \
86 }
87
88 /**
89 * @brief A parsed JSON key value pair.
90 */
91 struct ujson_val {
92 /**
93 * @brief A value type
94 *
95 * UJSON_VALUE_VOID means that no value was parsed.
96 */
97 enum ujson_type type;
98
99 /** An user supplied buffer and size to store a string values to. */
100 char *buf;
101 size_t buf_size;
102
103 /**
104 * @brief An index to attribute list.
105 *
106 * This is set by the ujson_obj_first_filter() and
107 * ujson_obj_next_filter() functions.
108 */
109 size_t idx;
110
111 /** An union to store the parsed value into. */
112 union {
113 /** @brief A boolean value. */
114 int val_bool;
115 /** @brief An integer value. */
116 long long val_int;
117 /** @brief A string value. */
118 const char *val_str;
119 };
120
121 /**
122 * @brief A floating point value.
123 *
124 * Since integer values are subset of floating point values val_float
125 * is always set when val_int was set.
126 */
127 double val_float;
128
129 /** @brief An ID for object values */
130 char id[UJSON_ID_MAX];
131
132 char buf__[];
133 };
134
135 /**
136 * @brief Allocates a JSON value.
137 *
138 * @param buf_size A maximal buffer size for a string value, pass 0 for default.
139 * @return A newly allocated JSON value.
140 */
141 ujson_val *ujson_val_alloc(size_t buf_size);
142
143 /**
144 * @brief Frees a JSON value.
145 *
146 * @param self A JSON value previously allocated by ujson_val_alloc().
147 */
148 void ujson_val_free(ujson_val *self);
149
150 /**
151 * @brief Checks is result has valid type.
152 *
153 * @param res An ujson value.
154 * @return Zero if result is not valid, non-zero otherwise.
155 */
ujson_val_valid(struct ujson_val * res)156 static inline int ujson_val_valid(struct ujson_val *res)
157 {
158 return !!res->type;
159 }
160
161 /**
162 * @brief Fills the reader error.
163 *
164 * Once buffer error is set all parsing functions return immediatelly with type
165 * set to UJSON_VOID.
166 *
167 * @param self An ujson_reader
168 * @param fmt A printf like format string
169 * @param ... A printf like parameters
170 */
171 void ujson_err(ujson_reader *self, const char *fmt, ...)
172 __attribute__((format(printf, 2, 3)));
173
174 /**
175 * @brief Prints error stored in the buffer.
176 *
177 * The error takes into consideration the current offset in the buffer and
178 * prints a few preceding lines along with the exact position of the error.
179 *
180 * The error is passed to the err_print() handler.
181 *
182 * @param self A ujson_reader
183 */
184 void ujson_err_print(ujson_reader *self);
185
186 /**
187 * @brief Prints a warning.
188 *
189 * Uses the print handler in the buffer to print a warning along with a few
190 * lines of context from the JSON at the current position.
191 *
192 * @param self A ujson_reader
193 * @param fmt A printf-like error string.
194 * @param ... A printf-like parameters.
195 */
196 void ujson_warn(ujson_reader *self, const char *fmt, ...)
197 __attribute__((format(printf, 2, 3)));
198
199 /**
200 * @brief Returns true if error was encountered.
201 *
202 * @param self A ujson_reader
203 * @return True if error was encountered false otherwise.
204 */
ujson_reader_err(ujson_reader * self)205 static inline int ujson_reader_err(ujson_reader *self)
206 {
207 return !!self->err[0];
208 }
209
210 /**
211 * @brief Returns the type of next element in buffer.
212 *
213 * @param self An ujson_reader
214 * @return A type of next element in the buffer.
215 */
216 enum ujson_type ujson_next_type(ujson_reader *self);
217
218 /**
219 * @brief Returns if first element in JSON is object or array.
220 *
221 * @param self A ujson_reader
222 * @return On success returns UJSON_OBJ or UJSON_ARR. On failure UJSON_VOID.
223 */
224 enum ujson_type ujson_reader_start(ujson_reader *self);
225
226 /**
227 * @brief Starts parsing of a JSON object.
228 *
229 * @param self An ujson_reader
230 * @param res An ujson_val to store the parsed value to.
231 *
232 * @return Zero on success, non-zero otherwise.
233 */
234 int ujson_obj_first(ujson_reader *self, struct ujson_val *res);
235
236 /**
237 * @brief Parses next value from a JSON object.
238 *
239 * If the res->type is UJSON_OBJ or UJSON_ARR it has to be parsed or skipped
240 * before next call to this function.
241 *
242 * @param self An ujson_reader.
243 * @param res A ujson_val to store the parsed value to.
244 *
245 * @return Zero on success, non-zero otherwise.
246 */
247 int ujson_obj_next(ujson_reader *self, struct ujson_val *res);
248
249 /**
250 * @brief A loop over a JSON object.
251 *
252 * @code
253 * UJSON_OBJ_FOREACH(reader, val) {
254 * printf("Got value id '%s' type '%s'", val->id, ujson_type_name(val->type));
255 * ...
256 * }
257 * @endcode
258 *
259 * @param self An ujson_reader.
260 * @param res An ujson_val to store the next parsed value to.
261 */
262 #define UJSON_OBJ_FOREACH(self, res) \
263 for (ujson_obj_first(self, res); ujson_val_valid(res); ujson_obj_next(self, res))
264
265 /**
266 * @brief Utility function for log(n) lookup in a sorted array.
267 *
268 * @param list Analphabetically sorted array.
269 * @param list_len Array length.
270 *
271 * @return An array index or (size_t)-1 if key wasn't found.
272 */
273 size_t ujson_lookup(const void *arr, size_t memb_size, size_t list_len,
274 const char *key);
275
276 /**
277 * @brief A JSON object attribute description i.e. key and type.
278 */
279 typedef struct ujson_obj_attr {
280 /** @brief A JSON object key name. */
281 const char *key;
282 /**
283 * @brief A JSON object value type.
284 *
285 * Note that because integer numbers are subset of floating point
286 * numbers if requested type was UJSON_FLOAT it will match if parsed
287 * type was UJSON_INT and the val_float will be set in addition to
288 * val_int.
289 */
290 enum ujson_type type;
291 } ujson_obj_attr;
292
293 /** @brief A JSON object description */
294 typedef struct ujson_obj {
295 /**
296 * @brief A list of attributes.
297 *
298 * Attributes we are looking for, the parser sets the val->idx for these.
299 */
300 const ujson_obj_attr *attrs;
301 /** @brief A size of attrs array. */
302 size_t attr_cnt;
303 } ujson_obj;
304
ujson_obj_lookup(const ujson_obj * obj,const char * key)305 static inline size_t ujson_obj_lookup(const ujson_obj *obj, const char *key)
306 {
307 return ujson_lookup(obj->attrs, sizeof(*obj->attrs), obj->attr_cnt, key);
308 }
309
310 /** @brief An ujson_obj_attr initializer. */
311 #define UJSON_OBJ_ATTR(keyv, typev) \
312 {.key = keyv, .type = typev}
313
314 /** @brief An ujson_obj_attr intializer with an array index. */
315 #define UJSON_OBJ_ATTR_IDX(key_idx, keyv, typev) \
316 [key_idx] = {.key = keyv, .type = typev}
317
318 /**
319 * @brief Starts parsing of a JSON object with attribute lists.
320 *
321 * @param self An ujson_reader.
322 * @param res An ujson_val to store the parsed value to.
323 * @param obj An ujson_obj object description.
324 * @param ign A list of keys to ignore.
325 *
326 * @return Zero on success, non-zero otherwise.
327 */
328 int ujson_obj_first_filter(ujson_reader *self, struct ujson_val *res,
329 const struct ujson_obj *obj, const struct ujson_obj *ign);
330
331 /**
332 * @brief An empty object attribute list.
333 *
334 * To be passed to UJSON_OBJ_FOREACH_FITLER() as ignore list.
335 */
336 extern const struct ujson_obj *ujson_empty_obj;
337
338 /**
339 * @brief Parses next value from a JSON object with attribute lists.
340 *
341 * If the res->type is UJSON_OBJ or UJSON_ARR it has to be parsed or skipped
342 * before next call to this function.
343 *
344 * @param self An ujson_reader.
345 * @param res An ujson_val to store the parsed value to.
346 * @param obj An ujson_obj object description.
347 * @param ign A list of keys to ignore. If set to NULL all unknown keys are
348 * ignored, if set to ujson_empty_obj all unknown keys produce warnings.
349 *
350 * @return Zero on success, non-zero otherwise.
351 */
352 int ujson_obj_next_filter(ujson_reader *self, struct ujson_val *res,
353 const struct ujson_obj *obj, const struct ujson_obj *ign);
354
355 /**
356 * @brief A loop over a JSON object with a pre-defined list of expected attributes.
357 *
358 * @code
359 * static struct ujson_obj_attr attrs[] = {
360 * UJSON_OBJ_ATTR("bool", UJSON_BOOL),
361 * UJSON_OBJ_ATTR("number", UJSON_INT),
362 * };
363 *
364 * static struct ujson_obj obj = {
365 * .attrs = filter_attrs,
366 * .attr_cnt = UJSON_ARRAY_SIZE(filter_attrs)
367 * };
368 *
369 * UJSON_OBJ_FOREACH_FILTER(reader, val, &obj, NULL) {
370 * printf("Got value id '%s' type '%s'",
371 * attrs[val->idx].id, ujson_type_name(val->type));
372 * ...
373 * }
374 * @endcode
375 *
376 * @param self An ujson_reader.
377 * @param res An ujson_val to store the next parsed value to.
378 * @param obj An ujson_obj with a description of attributes to parse.
379 * @param ign An ujson_obj with a description of attributes to ignore.
380 */
381 #define UJSON_OBJ_FOREACH_FILTER(self, res, obj, ign) \
382 for (ujson_obj_first_filter(self, res, obj, ign); \
383 ujson_val_valid(res); \
384 ujson_obj_next_filter(self, res, obj, ign))
385
386 /**
387 * @brief Skips parsing of a JSON object.
388 *
389 * @param self An ujson_reader.
390 *
391 * @return Zero on success, non-zero otherwise.
392 */
393 int ujson_obj_skip(ujson_reader *self);
394
395 /**
396 * @brief Starts parsing of a JSON array.
397 *
398 * @param self An ujson_reader.
399 * @param res An ujson_val to store the parsed value to.
400 *
401 * @return Zero on success, non-zero otherwise.
402 */
403 int ujson_arr_first(ujson_reader *self, struct ujson_val *res);
404
405 /**
406 * @brief Parses next value from a JSON array.
407 *
408 * If the res->type is UJSON_OBJ or UJSON_ARR it has to be parsed or skipped
409 * before next call to this function.
410 *
411 * @param self An ujson_reader.
412 * @param res An ujson_val to store the parsed value to.
413 *
414 * @return Zero on success, non-zero otherwise.
415 */
416 int ujson_arr_next(ujson_reader *self, struct ujson_val *res);
417
418 /**
419 * @brief A loop over a JSON array.
420 *
421 * @code
422 * UJSON_ARR_FOREACH(reader, val) {
423 * printf("Got value type '%s'", ujson_type_name(val->type));
424 * ...
425 * }
426 * @endcode
427 *
428 * @param self An ujson_reader.
429 * @param res An ujson_val to store the next parsed value to.
430 */
431 #define UJSON_ARR_FOREACH(self, res) \
432 for (ujson_arr_first(self, res); ujson_val_valid(res); ujson_arr_next(self, res))
433
434 /**
435 * @brief Skips parsing of a JSON array.
436 *
437 * @param self A ujson_reader.
438 *
439 * @return Zero on success, non-zero otherwise.
440 */
441 int ujson_arr_skip(ujson_reader *self);
442
443 /**
444 * @brief A JSON reader state.
445 */
446 typedef struct ujson_reader_state {
447 size_t off;
448 unsigned int depth;
449 } ujson_reader_state;
450
451 /**
452 * @brief Returns a parser state at the start of current object/array.
453 *
454 * This function could be used for the parser to return to the start of the
455 * currently parsed object or array.
456 *
457 * @param self A ujson_reader
458 * @return A state that points to a start of the last object or array.
459 */
ujson_reader_state_save(ujson_reader * self)460 static inline ujson_reader_state ujson_reader_state_save(ujson_reader *self)
461 {
462 struct ujson_reader_state ret = {
463 .off = self->sub_off,
464 .depth = self->depth,
465 };
466
467 return ret;
468 }
469
470 /**
471 * @brief Returns the parser to a saved state.
472 *
473 * This function could be used for the parser to return to the start of
474 * object or array saved by t the ujson_reader_state_get() function.
475 *
476 * @param self A ujson_reader
477 * @param state An parser state as returned by the ujson_reader_state_get().
478 */
ujson_reader_state_load(ujson_reader * self,ujson_reader_state state)479 static inline void ujson_reader_state_load(ujson_reader *self, ujson_reader_state state)
480 {
481 if (ujson_reader_err(self))
482 return;
483
484 self->off = state.off;
485 self->sub_off = state.off;
486 self->depth = state.depth;
487 }
488
489 /**
490 * @brief Resets the parser to a start.
491 *
492 * @param self A ujson_reader
493 */
ujson_reader_reset(ujson_reader * self)494 static inline void ujson_reader_reset(ujson_reader *self)
495 {
496 self->off = 0;
497 self->sub_off = 0;
498 self->depth = 0;
499 self->err[0] = 0;
500 }
501
502 /**
503 * @brief Loads a file into an ujson_reader buffer.
504 *
505 * The reader has to be later freed by ujson_reader_free().
506 *
507 * @param path A path to a file.
508 * @return A ujson_reader or NULL in a case of a failure.
509 */
510 ujson_reader *ujson_reader_load(const char *path);
511
512 /**
513 * @brief Frees an ujson_reader buffer.
514 *
515 * @param self A ujson_reader allocated by ujson_reader_load() function.
516 */
517 void ujson_reader_free(ujson_reader *self);
518
519 /**
520 * @brief Prints errors and warnings at the end of parsing.
521 *
522 * Checks if self->err is set and prints the error with ujson_reader_err()
523 *
524 * Checks if there is any text left after the parser has finished with
525 * ujson_reader_consumed() and prints a warning if there were any non-whitespace
526 * characters left.
527 *
528 * @param self A ujson_reader
529 */
530 void ujson_reader_finish(ujson_reader *self);
531
532 /**
533 * @brief Returns non-zero if whole buffer has been consumed.
534 *
535 * @param self A ujson_reader.
536 * @return Non-zero if whole buffer was consumed.
537 */
ujson_reader_consumed(ujson_reader * self)538 static inline int ujson_reader_consumed(ujson_reader *self)
539 {
540 return self->off >= self->len;
541 }
542
543 #endif /* UJSON_H */
544