• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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