blob: 77fdf748bb59b3e029f5fc635a61e190c917169f [file] [log] [blame]
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001/**
2 * @file json.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Generic JSON format parser for libyang
5 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
15#include <assert.h>
16#include <ctype.h>
17#include <errno.h>
Radek Krejci50f0c6b2020-06-18 16:31:48 +020018#include <stdlib.h>
Radek Krejci47fab892020-11-05 17:02:41 +010019#include <string.h>
Radek Krejci50f0c6b2020-06-18 16:31:48 +020020#include <sys/types.h>
21
22#include "common.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020023#include "in_internal.h"
Radek Krejci47fab892020-11-05 17:02:41 +010024#include "json.h"
Radek Krejci50f0c6b2020-06-18 16:31:48 +020025
26#define JSON_PUSH_STATUS_RET(CTX, STATUS) \
Radek Krejci3d92e442020-10-12 12:48:13 +020027 LY_CHECK_RET(ly_set_add(&CTX->status, (void*)STATUS, 1, NULL))
Radek Krejci50f0c6b2020-06-18 16:31:48 +020028
29#define JSON_POP_STATUS_RET(CTX) \
30 assert(CTX->status.count); CTX->status.count--;
31
Michal Vasko22df3f02020-08-24 13:29:22 +020032const char *
Radek Krejci50f0c6b2020-06-18 16:31:48 +020033lyjson_token2str(enum LYJSON_PARSER_STATUS status)
34{
35 switch (status) {
36 case LYJSON_ERROR:
37 return "error";
38 case LYJSON_ROOT:
39 return "document root";
40 case LYJSON_FALSE:
41 return "false";
42 case LYJSON_TRUE:
43 return "true";
44 case LYJSON_NULL:
45 return "null";
46 case LYJSON_OBJECT:
47 return "object";
48 case LYJSON_OBJECT_CLOSED:
49 return "object closed";
50 case LYJSON_OBJECT_EMPTY:
51 return "empty object";
52 case LYJSON_ARRAY:
53 return "array";
54 case LYJSON_ARRAY_CLOSED:
55 return "array closed";
56 case LYJSON_ARRAY_EMPTY:
57 return "empty array";
58 case LYJSON_NUMBER:
59 return "number";
60 case LYJSON_STRING:
61 return "string";
62 case LYJSON_END:
63 return "end of input";
64 }
65
66 return "";
67}
68
69static LY_ERR
70skip_ws(struct lyjson_ctx *jsonctx)
71{
72 /* skip leading whitespaces */
73 while (*jsonctx->in->current != '\0' && is_jsonws(*jsonctx->in->current)) {
Radek Krejcidd713ce2021-01-04 23:12:12 +010074 if (*jsonctx->in->current == '\n') {
75 LY_IN_NEW_LINE(jsonctx->in);
76 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +020077 ly_in_skip(jsonctx->in, 1);
78 }
79 if (*jsonctx->in->current == '\0') {
80 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_END);
81 }
82
83 return LY_SUCCESS;
84}
85
86/*
87 * @brief Set value corresponding to the current context's status
88 */
89static void
Radek Krejci857189e2020-09-01 13:26:36 +020090lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
Radek Krejci50f0c6b2020-06-18 16:31:48 +020091{
92 assert(jsonctx);
93
Juraj Vijtiukec285cd2021-01-14 11:41:20 +010094 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +020095 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +020096 }
97 jsonctx->value = value;
98 jsonctx->value_len = value_len;
99 jsonctx->dynamic = dynamic;
100}
101
102static LY_ERR
103lyjson_check_next(struct lyjson_ctx *jsonctx)
104{
105 if (jsonctx->status.count == 1) {
106 /* top level value (JSON-text), ws expected */
Michal Vasko69730152020-10-09 16:30:07 +0200107 if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200108 return LY_SUCCESS;
109 }
110 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
111 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200112 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200113 return LY_SUCCESS;
114 }
115 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
116 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200117 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200118 return LY_SUCCESS;
119 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200120 }
121
Radek Krejcie7010dc2021-03-04 15:54:24 +0100122 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
123 *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200124 return LY_EVALID;
125}
126
127/**
128 * Input is expected to start after the opening quotation-mark.
129 * When succeeds, input is moved after the closing quotation-mark.
130 */
131static LY_ERR
132lyjson_string_(struct lyjson_ctx *jsonctx)
133{
134#define BUFSIZE 24
135#define BUFSIZE_STEP 128
136
137 const char *in = jsonctx->in->current, *start;
138 char *buf = NULL;
139 size_t offset; /* read offset in input buffer */
140 size_t len; /* length of the output string (write offset in output buffer) */
141 size_t size = 0; /* size of the output buffer */
142 size_t u;
143 uint64_t start_line;
144
145 assert(jsonctx);
146
147 /* init */
148 start = in;
Radek Krejcid54412f2020-12-17 20:25:35 +0100149 start_line = jsonctx->in->line;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200150 offset = len = 0;
151
152 /* parse */
153 while (in[offset]) {
154 if (in[offset] == '\\') {
155 /* escape sequence */
Michal Vasko2be1d762021-03-11 16:53:15 +0100156 const char *slash = &in[offset];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200157 uint32_t value;
158 uint8_t i = 1;
159
160 if (!buf) {
161 /* prepare output buffer */
162 buf = malloc(BUFSIZE);
163 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
164 size = BUFSIZE;
165 }
166
167 /* allocate enough for the offset and next character,
168 * we will need 4 bytes at most since we support only the predefined
169 * (one-char) entities and character references */
170 if (len + offset + 4 >= size) {
Juraj Vijtiukd746a352021-01-15 11:33:33 +0100171 size_t increment;
Radek Krejcidf549132021-01-21 10:32:32 +0100172 for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {}
Juraj Vijtiukd746a352021-01-15 11:33:33 +0100173 buf = ly_realloc(buf, size + increment);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200174 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
175 size += BUFSIZE_STEP;
176 }
177
178 if (offset) {
179 /* store what we have so far */
180 memcpy(&buf[len], in, offset);
181 len += offset;
182 in += offset;
183 offset = 0;
184 }
185
186 switch (in[++offset]) {
187 case '"':
188 /* quotation mark */
189 value = 0x22;
190 break;
191 case '\\':
192 /* reverse solidus */
193 value = 0x5c;
194 break;
195 case '/':
196 /* solidus */
197 value = 0x2f;
198 break;
199 case 'b':
200 /* backspace */
201 value = 0x08;
202 break;
203 case 'f':
204 /* form feed */
205 value = 0x0c;
206 break;
207 case 'n':
208 /* line feed */
209 value = 0x0a;
210 break;
211 case 'r':
212 /* carriage return */
213 value = 0x0d;
214 break;
215 case 't':
216 /* tab */
217 value = 0x09;
218 break;
219 case 'u':
220 /* Basic Multilingual Plane character \uXXXX */
221 offset++;
222 for (value = i = 0; i < 4; i++) {
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100223 if (!in[offset + i]) {
Michal Vasko2be1d762021-03-11 16:53:15 +0100224 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash);
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100225 goto error;
226 } else if (isdigit(in[offset + i])) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200227 u = (in[offset + i] - '0');
228 } else if (in[offset + i] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100229 u = LY_BASE_DEC + (in[offset + i] - 'a');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200230 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100231 u = LY_BASE_DEC + (in[offset + i] - 'A');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200232 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100233 value = (LY_BASE_HEX * value) + u;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200234 }
235 break;
236 default:
237 /* invalid escape sequence */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100238 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200239 goto error;
240
241 }
242
243 offset += i; /* add read escaped characters */
244 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100245 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
Michal Vasko2be1d762021-03-11 16:53:15 +0100246 (int)(&in[offset] - slash), slash, value),
Michal Vasko69730152020-10-09 16:30:07 +0200247 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200248 len += u; /* update number of bytes in buffer */
249 in += offset; /* move the input by the processed bytes stored in the buffer ... */
250 offset = 0; /* ... and reset the offset index for future moving data into buffer */
251
252 } else if (in[offset] == '"') {
253 /* end of string */
254 if (buf) {
255 /* realloc exact size string */
256 buf = ly_realloc(buf, len + offset + 1);
257 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
258 size = len + offset + 1;
259 memcpy(&buf[len], in, offset);
260
261 /* set terminating NULL byte */
262 buf[len + offset] = '\0';
263 }
264 len += offset;
265 ++offset;
266 in += offset;
267 goto success;
268 } else {
269 /* get it as UTF-8 character for check */
270 const char *c = &in[offset];
271 uint32_t code = 0;
272 size_t code_len = 0;
273
274 LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100275 LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200276
277 LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100278 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
Radek Krejci422afb12021-03-04 16:38:16 +0100279 (int)(&in[offset] - start + code_len), start, code),
Michal Vasko69730152020-10-09 16:30:07 +0200280 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200281
282 /* character is ok, continue */
283 offset += code_len;
284 }
285 }
286
287 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100288 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
289 LOGVAL_LINE(jsonctx->ctx, start_line, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200290
291error:
292 free(buf);
293 return LY_EVALID;
294
295success:
Radek Krejcid54412f2020-12-17 20:25:35 +0100296 jsonctx->in->current = in;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200297 if (buf) {
298 lyjson_ctx_set_value(jsonctx, buf, len, 1);
299 } else {
300 lyjson_ctx_set_value(jsonctx, start, len, 0);
301 }
302
303 return LY_SUCCESS;
304
305#undef BUFSIZE
306#undef BUFSIZE_STEP
307}
308
309/*
310 *
311 * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
312 */
313static LY_ERR
314lyjson_string(struct lyjson_ctx *jsonctx)
315{
316 LY_CHECK_RET(lyjson_string_(jsonctx));
317
318 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_STRING);
319 LY_CHECK_RET(lyjson_check_next(jsonctx));
320
321 return LY_SUCCESS;
322}
323
aPieceke87c0a12021-05-13 15:43:26 +0200324/**
325 * @brief Allocate buffer for number in string format.
326 *
327 * @param[in] jsonctx JSON context.
328 * @param[in] num_len Required space in bytes for a number.
329 * Terminating null byte is added by default.
330 * @param[out] buffer Output allocated buffer.
331 * @return LY_ERR value.
332 */
333static LY_ERR
334lyjson_get_buffer_for_number(struct lyjson_ctx *jsonctx, size_t num_len, char **buffer)
335{
336 *buffer = NULL;
337
338 LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
339 "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
340
341 /* allocate buffer for the result (add terminating NULL-byte) */
342 *buffer = malloc(num_len + 1);
343 LY_CHECK_ERR_RET(!(*buffer), LOGMEM(jsonctx->ctx), LY_EMEM);
344 return LY_SUCCESS;
345}
346
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200347static LY_ERR
348lyjson_number(struct lyjson_ctx *jsonctx)
349{
350 size_t offset = 0, exponent = 0;
351 const char *in = jsonctx->in->current;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200352 uint8_t minus = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200353
354 if (in[offset] == '-') {
355 ++offset;
356 minus = 1;
357 }
358
359 if (in[offset] == '0') {
360 ++offset;
361 } else if (isdigit(in[offset])) {
362 ++offset;
363 while (isdigit(in[offset])) {
364 ++offset;
365 }
366 } else {
367invalid_character:
368 if (in[offset]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100369 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200370 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100371 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200372 }
373 return LY_EVALID;
374 }
375
376 if (in[offset] == '.') {
377 ++offset;
378 if (!isdigit(in[offset])) {
379 goto invalid_character;
380 }
381 while (isdigit(in[offset])) {
382 ++offset;
383 }
384 }
385
386 if ((in[offset] == 'e') || (in[offset] == 'E')) {
387 exponent = offset++;
388 if ((in[offset] == '+') || (in[offset] == '-')) {
389 ++offset;
390 }
391 if (!isdigit(in[offset])) {
392 goto invalid_character;
393 }
394 while (isdigit(in[offset])) {
395 ++offset;
396 }
397 }
398
399 if (exponent) {
400 /* convert JSON number with exponent into the representation used by YANG */
401 long int e_val;
402 char *ptr, *dec_point, *num;
403 const char *e_ptr = &in[exponent + 1];
404 size_t num_len, i;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200405 int64_t dp_position; /* final position of the deciaml point */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200406
407 errno = 0;
Radek Krejcif13b87b2020-12-01 22:02:17 +0100408 e_val = strtol(e_ptr, &ptr, LY_BASE_DEC);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200409 if (errno) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100410 LOGVAL(jsonctx->ctx, LYVE_SEMANTICS, "Exponent out-of-bounds in a JSON Number value (%.*s).",
Radek Krejci422afb12021-03-04 16:38:16 +0100411 (int)(offset - minus - (e_ptr - in)), e_ptr);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200412 return LY_EVALID;
413 }
414
aPieceka40b4662021-05-13 15:47:03 +0200415 if (!e_val) {
416 /* exponent is zero, so just cut the part with the exponent */
417 num_len = exponent;
418 LY_CHECK_RET(lyjson_get_buffer_for_number(jsonctx, num_len, &num));
419 memcpy(num, in, num_len);
420 num[num_len] = '\0';
421 goto store_exp_number;
422 }
423
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200424 dec_point = ly_strnchr(in, '.', exponent);
425 if (!dec_point) {
426 /* value is integer, we are just ... */
427 if (e_val >= 0) {
428 /* adding zeros at the end */
429 num_len = exponent + e_val;
430 dp_position = num_len; /* decimal point is behind the actual value */
aPiecek350a6bf2021-05-25 07:59:10 +0200431 } else if ((size_t)labs(e_val) < (exponent - minus)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200432 /* adding decimal point between the integer's digits */
433 num_len = exponent + 1;
434 dp_position = exponent + e_val;
435 } else {
436 /* adding decimal point before the integer with adding leading zero(s) */
Juraj Vijtiukc6166a02021-03-01 11:38:40 +0100437 num_len = labs(e_val) + 2 + minus;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200438 dp_position = exponent + e_val;
439 }
440 dp_position -= minus;
441 } else {
442 /* value is decimal, we are moving the decimal point */
443 dp_position = dec_point - in + e_val - minus;
444 if (dp_position > (ssize_t)exponent) {
445 /* moving decimal point after the decimal value make the integer result */
446 num_len = dp_position;
447 } else if (dp_position < 0) {
448 /* moving decimal point before the decimal value requires additional zero(s)
449 * (decimal point is already count in exponent value) */
450 num_len = exponent + labs(dp_position) + 1;
aPiecek58e46a52021-05-05 10:04:47 +0200451 } else if (dp_position == 0) {
452 /* moving the decimal point exactly to the beginning will cause a zero character to be added. */
453 num_len = exponent + 1;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200454 } else {
455 /* moving decimal point just inside the decimal value does not make any change in length */
456 num_len = exponent;
457 }
458 }
459
aPieceke87c0a12021-05-13 15:43:26 +0200460 LY_CHECK_RET(lyjson_get_buffer_for_number(jsonctx, num_len, &num));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200461
462 /* compose the resulting vlaue */
463 i = 0;
464 if (minus) {
465 num[i++] = '-';
466 }
467 /* add leading zeros */
468 if (dp_position <= 0) {
469 num[i++] = '0';
470 num[i++] = '.';
Michal Vaskod989ba02020-08-24 10:59:24 +0200471 for ( ; dp_position; dp_position++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200472 num[i++] = '0';
473 }
474 }
475 /* copy the value */
Radek Krejci857189e2020-09-01 13:26:36 +0200476 ly_bool dp_placed;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200477 size_t j;
478 for (dp_placed = dp_position ? 0 : 1, j = minus; j < exponent; j++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200479 if (in[j] == '.') {
480 continue;
481 }
482 if (!dp_placed) {
483 if (!dp_position) {
484 num[i++] = '.';
485 dp_placed = 1;
486 } else {
487 dp_position--;
488 if (in[j] == '0') {
489 num_len--;
490 continue;
491 }
492 }
493 }
494
495 num[i++] = in[j];
496 }
497 /* trailing zeros */
498 while (dp_position--) {
499 num[i++] = '0';
500 }
501 /* terminating NULL byte */
502 num[i] = '\0';
503
aPieceka40b4662021-05-13 15:47:03 +0200504store_exp_number:
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200505 /* store the modified number */
506 lyjson_ctx_set_value(jsonctx, num, num_len, 1);
507 } else {
508 /* store the number */
509 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, offset, 0);
510 }
511 ly_in_skip(jsonctx->in, offset);
512
513 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NUMBER);
514 LY_CHECK_RET(lyjson_check_next(jsonctx));
515
516 return LY_SUCCESS;
517}
518
519static LY_ERR
520lyjson_object_name(struct lyjson_ctx *jsonctx)
521{
522 if (*jsonctx->in->current != '"') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100523 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200524 jsonctx->in->current, "a JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200525 return LY_EVALID;
526 }
527 ly_in_skip(jsonctx->in, 1);
528
529 LY_CHECK_RET(lyjson_string_(jsonctx));
530 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko08dc70b2020-10-07 13:58:47 +0200531 if (*jsonctx->in->current != ':') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100532 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
533 "a JSON object's name-separator ':'");
Michal Vasko08dc70b2020-10-07 13:58:47 +0200534 return LY_EVALID;
535 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200536 ly_in_skip(jsonctx->in, 1);
537 LY_CHECK_RET(skip_ws(jsonctx));
538
539 return LY_SUCCESS;
540}
541
542static LY_ERR
543lyjson_object(struct lyjson_ctx *jsonctx)
544{
545 LY_CHECK_RET(skip_ws(jsonctx));
546
547 if (*jsonctx->in->current == '}') {
aPiecek93582ed2021-05-25 14:49:06 +0200548 assert(jsonctx->depth);
549 jsonctx->depth--;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200550 /* empty object */
551 ly_in_skip(jsonctx->in, 1);
552 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
553 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT_EMPTY);
554 return LY_SUCCESS;
555 }
556
557 LY_CHECK_RET(lyjson_object_name(jsonctx));
558
559 /* output data are set by lyjson_string_() */
560 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT);
561
562 return LY_SUCCESS;
563}
564
565/*
566 * @brief Process JSON array envelope
567 *
568 *
569 *
570 * @param[in] jsonctx JSON parser context
571 * @return LY_SUCCESS or LY_EMEM
572 */
573static LY_ERR
574lyjson_array(struct lyjson_ctx *jsonctx)
575{
576 LY_CHECK_RET(skip_ws(jsonctx));
577
578 if (*jsonctx->in->current == ']') {
579 /* empty array */
580 ly_in_skip(jsonctx->in, 1);
581 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY_EMPTY);
582 } else {
583 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY);
584 }
585
586 /* erase previous values, array has no value on its own */
587 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
588
589 return LY_SUCCESS;
590}
591
592static LY_ERR
593lyjson_value(struct lyjson_ctx *jsonctx)
594{
Michal Vasko69730152020-10-09 16:30:07 +0200595 if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200596 return LY_SUCCESS;
597 }
598
Radek Krejcif13b87b2020-12-01 22:02:17 +0100599 if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200600 /* false */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100601 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
602 ly_in_skip(jsonctx->in, ly_strlen_const("false"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200603 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_FALSE);
604 LY_CHECK_RET(lyjson_check_next(jsonctx));
605
Radek Krejcif13b87b2020-12-01 22:02:17 +0100606 } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200607 /* true */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100608 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
609 ly_in_skip(jsonctx->in, ly_strlen_const("true"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200610 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_TRUE);
611 LY_CHECK_RET(lyjson_check_next(jsonctx));
612
Radek Krejcif13b87b2020-12-01 22:02:17 +0100613 } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200614 /* none */
Radek Krejci201963a2020-12-03 11:43:40 +0100615 lyjson_ctx_set_value(jsonctx, "", 0, 0);
Radek Krejcif13b87b2020-12-01 22:02:17 +0100616 ly_in_skip(jsonctx->in, ly_strlen_const("null"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200617 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NULL);
618 LY_CHECK_RET(lyjson_check_next(jsonctx));
619
620 } else if (*jsonctx->in->current == '"') {
621 /* string */
622 ly_in_skip(jsonctx->in, 1);
623 LY_CHECK_RET(lyjson_string(jsonctx));
624
625 } else if (*jsonctx->in->current == '[') {
626 /* array */
627 ly_in_skip(jsonctx->in, 1);
628 LY_CHECK_RET(lyjson_array(jsonctx));
629
630 } else if (*jsonctx->in->current == '{') {
aPiecek93582ed2021-05-25 14:49:06 +0200631 jsonctx->depth++;
632 if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) {
633 LOGERR(jsonctx->ctx, LY_EINVAL,
634 "The maximum number of block nestings has been exceeded.");
635 return LY_EINVAL;
636 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200637 /* object */
638 ly_in_skip(jsonctx->in, 1);
639 LY_CHECK_RET(lyjson_object(jsonctx));
640
Michal Vasko69730152020-10-09 16:30:07 +0200641 } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200642 /* number */
643 LY_CHECK_RET(lyjson_number(jsonctx));
644
645 } else {
646 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100647 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200648 jsonctx->in->current, "a JSON value");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200649 return LY_EVALID;
650 }
651
652 return LY_SUCCESS;
653}
654
655LY_ERR
656lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
657{
658 LY_ERR ret = LY_SUCCESS;
659 struct lyjson_ctx *jsonctx;
660
661 assert(ctx);
662 assert(in);
663 assert(jsonctx_p);
664
665 /* new context */
666 jsonctx = calloc(1, sizeof *jsonctx);
667 LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
668 jsonctx->ctx = ctx;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200669 jsonctx->in = in;
670
Radek Krejciddace2c2021-01-08 11:30:56 +0100671 LOG_LOCINIT(NULL, NULL, NULL, in);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100672
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200673 /* parse JSON value, if any */
674 LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
675 if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
676 /* empty data input */
677 goto cleanup;
678 }
679
680 ret = lyjson_value(jsonctx);
681
Michal Vasko69730152020-10-09 16:30:07 +0200682 if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100683 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200684 ret = LY_EVALID;
685 }
686
687cleanup:
688 if (ret) {
689 lyjson_ctx_free(jsonctx);
690 } else {
691 *jsonctx_p = jsonctx;
692 }
693 return ret;
694}
695
696void
697lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
698{
699 if (jsonctx->backup.dynamic) {
700 free((char *)jsonctx->backup.value);
701 }
702 jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
703 jsonctx->backup.status_count = jsonctx->status.count;
704 jsonctx->backup.value = jsonctx->value;
705 jsonctx->backup.value_len = jsonctx->value_len;
706 jsonctx->backup.input = jsonctx->in->current;
707 jsonctx->backup.dynamic = jsonctx->dynamic;
aPiecek93582ed2021-05-25 14:49:06 +0200708 jsonctx->backup.depth = jsonctx->depth;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200709 jsonctx->dynamic = 0;
710}
711
712void
713lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
714{
715 if (jsonctx->dynamic) {
716 free((char *)jsonctx->value);
717 }
718 jsonctx->status.count = jsonctx->backup.status_count;
Michal Vasko22df3f02020-08-24 13:29:22 +0200719 jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200720 jsonctx->value = jsonctx->backup.value;
721 jsonctx->value_len = jsonctx->backup.value_len;
722 jsonctx->in->current = jsonctx->backup.input;
723 jsonctx->dynamic = jsonctx->backup.dynamic;
aPiecek93582ed2021-05-25 14:49:06 +0200724 jsonctx->depth = jsonctx->backup.depth;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200725 jsonctx->backup.dynamic = 0;
726}
727
728LY_ERR
729lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
730{
731 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200732 ly_bool toplevel = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200733 enum LYJSON_PARSER_STATUS prev;
734
735 assert(jsonctx);
736
737 prev = lyjson_ctx_status(jsonctx, 0);
738
Michal Vasko69730152020-10-09 16:30:07 +0200739 if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200740 /* get value for the object's member OR the first value in the array */
741 ret = lyjson_value(jsonctx);
742 goto result;
743 } else {
744 /* the previous token is closed and should be completely processed */
745 JSON_POP_STATUS_RET(jsonctx);
746 prev = lyjson_ctx_status(jsonctx, 0);
747 }
748
749 if (!jsonctx->status.count) {
750 /* we are done with the top level value */
751 toplevel = 1;
752 }
753 LY_CHECK_RET(skip_ws(jsonctx));
754 if (toplevel && !jsonctx->status.count) {
755 /* EOF expected, but there are some data after the top level token */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100756 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value.");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200757 return LY_EVALID;
758 }
759
760 if (toplevel) {
761 /* we are done */
762 return LY_SUCCESS;
763 }
764
765 /* continue with the next token */
766 assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
767
768 if (*jsonctx->in->current == ',') {
769 /* sibling item in the ... */
770 ly_in_skip(jsonctx->in, 1);
771 LY_CHECK_RET(skip_ws(jsonctx));
772
773 if (prev == LYJSON_OBJECT) {
774 /* ... object - get another object's member */
775 ret = lyjson_object_name(jsonctx);
776 } else { /* LYJSON_ARRAY */
777 /* ... array - get another complete value */
778 ret = lyjson_value(jsonctx);
779 }
Michal Vasko69730152020-10-09 16:30:07 +0200780 } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
aPiecek93582ed2021-05-25 14:49:06 +0200781 if (*jsonctx->in->current == '}') {
782 assert(jsonctx->depth);
783 jsonctx->depth--;
784 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200785 ly_in_skip(jsonctx->in, 1);
786 JSON_POP_STATUS_RET(jsonctx);
787 JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
788 } else {
789 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100790 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
791 prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200792 return LY_EVALID;
793 }
794
795result:
Michal Vasko69730152020-10-09 16:30:07 +0200796 if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100797 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200798 ret = LY_EVALID;
799 }
800
Michal Vasko69730152020-10-09 16:30:07 +0200801 if ((ret == LY_SUCCESS) && status) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200802 *status = lyjson_ctx_status(jsonctx, 0);
803 }
804
805 return ret;
806}
807
808enum LYJSON_PARSER_STATUS
809lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
810{
811 assert(jsonctx);
812
813 if (jsonctx->status.count < index) {
814 return LYJSON_ERROR;
815 } else if (jsonctx->status.count == index) {
816 return LYJSON_ROOT;
817 } else {
Michal Vasko27915722020-08-31 14:54:42 +0200818 return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200819 }
820}
821
822void
823lyjson_ctx_free(struct lyjson_ctx *jsonctx)
824{
825 if (!jsonctx) {
826 return;
827 }
828
Radek Krejciddace2c2021-01-08 11:30:56 +0100829 LOG_LOCBACK(0, 0, 0, 1);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100830
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200831 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200832 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200833 }
834 if (jsonctx->backup.dynamic) {
835 free((char *)jsonctx->backup.value);
836 }
837
838 ly_set_erase(&jsonctx->status, NULL);
839
840 free(jsonctx);
841}