blob: f24b349d45e13d83227c2e7f0f250ccd98b11057 [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 Krejcif13b87b2020-12-01 22:02:17 +010074 if (*jsonctx->in->current == '\n') {
75 /* new line */
Radek Krejci50f0c6b2020-06-18 16:31:48 +020076 jsonctx->line++;
77 }
78 ly_in_skip(jsonctx->in, 1);
79 }
80 if (*jsonctx->in->current == '\0') {
81 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_END);
82 }
83
84 return LY_SUCCESS;
85}
86
87/*
88 * @brief Set value corresponding to the current context's status
89 */
90static void
Radek Krejci857189e2020-09-01 13:26:36 +020091lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
Radek Krejci50f0c6b2020-06-18 16:31:48 +020092{
93 assert(jsonctx);
94
95 if (dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +020096 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +020097 }
98 jsonctx->value = value;
99 jsonctx->value_len = value_len;
100 jsonctx->dynamic = dynamic;
101}
102
103static LY_ERR
104lyjson_check_next(struct lyjson_ctx *jsonctx)
105{
106 if (jsonctx->status.count == 1) {
107 /* top level value (JSON-text), ws expected */
Michal Vasko69730152020-10-09 16:30:07 +0200108 if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200109 return LY_SUCCESS;
110 }
111 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
112 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200113 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200114 return LY_SUCCESS;
115 }
116 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
117 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200118 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200119 return LY_SUCCESS;
120 }
121 } else {
122 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX,
Michal Vasko69730152020-10-09 16:30:07 +0200123 "Unexpected character \"%c\" after JSON %s.", *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200124 }
125
126 return LY_EVALID;
127}
128
129/**
130 * Input is expected to start after the opening quotation-mark.
131 * When succeeds, input is moved after the closing quotation-mark.
132 */
133static LY_ERR
134lyjson_string_(struct lyjson_ctx *jsonctx)
135{
136#define BUFSIZE 24
137#define BUFSIZE_STEP 128
138
139 const char *in = jsonctx->in->current, *start;
140 char *buf = NULL;
141 size_t offset; /* read offset in input buffer */
142 size_t len; /* length of the output string (write offset in output buffer) */
143 size_t size = 0; /* size of the output buffer */
144 size_t u;
145 uint64_t start_line;
146
147 assert(jsonctx);
148
149 /* init */
150 start = in;
151 start_line = jsonctx->line;
152 offset = len = 0;
153
154 /* parse */
155 while (in[offset]) {
156 if (in[offset] == '\\') {
157 /* escape sequence */
158 size_t slash = offset;
159 uint32_t value;
160 uint8_t i = 1;
161
162 if (!buf) {
163 /* prepare output buffer */
164 buf = malloc(BUFSIZE);
165 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
166 size = BUFSIZE;
167 }
168
169 /* allocate enough for the offset and next character,
170 * we will need 4 bytes at most since we support only the predefined
171 * (one-char) entities and character references */
172 if (len + offset + 4 >= size) {
173 buf = ly_realloc(buf, size + BUFSIZE_STEP);
174 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]) {
224 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX,
Radek IÅ¡a14b357e2020-11-26 17:55:11 +0100225 "Invalid basic multilingual plane character \"%s\".", &in[slash]);
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100226 goto error;
227 } else if (isdigit(in[offset + i])) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200228 u = (in[offset + i] - '0');
229 } else if (in[offset + i] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100230 u = LY_BASE_DEC + (in[offset + i] - 'a');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200231 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100232 u = LY_BASE_DEC + (in[offset + i] - 'A');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200233 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100234 value = (LY_BASE_HEX * value) + u;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200235 }
236 break;
237 default:
238 /* invalid escape sequence */
239 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX,
Michal Vasko69730152020-10-09 16:30:07 +0200240 "Invalid character escape sequence \\%c.", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200241 goto error;
242
243 }
244
245 offset += i; /* add read escaped characters */
246 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
Michal Vasko69730152020-10-09 16:30:07 +0200247 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX,
248 "Invalid character reference \"%.*s\" (0x%08x).", offset - slash, &in[slash], value),
249 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200250 len += u; /* update number of bytes in buffer */
251 in += offset; /* move the input by the processed bytes stored in the buffer ... */
252 offset = 0; /* ... and reset the offset index for future moving data into buffer */
253
254 } else if (in[offset] == '"') {
255 /* end of string */
256 if (buf) {
257 /* realloc exact size string */
258 buf = ly_realloc(buf, len + offset + 1);
259 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
260 size = len + offset + 1;
261 memcpy(&buf[len], in, offset);
262
263 /* set terminating NULL byte */
264 buf[len + offset] = '\0';
265 }
266 len += offset;
267 ++offset;
268 in += offset;
269 goto success;
270 } else {
271 /* get it as UTF-8 character for check */
272 const char *c = &in[offset];
273 uint32_t code = 0;
274 size_t code_len = 0;
275
276 LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
Michal Vasko69730152020-10-09 16:30:07 +0200277 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_INCHAR, in[offset]), error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200278
279 LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
Michal Vasko69730152020-10-09 16:30:07 +0200280 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX,
281 "Invalid character in JSON string \"%.*s\" (0x%08x).", &in[offset] - start + code_len, start, code),
282 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200283
284 /* character is ok, continue */
285 offset += code_len;
286 }
287 }
288
289 /* EOF reached before endchar */
290 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_EOF);
291 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &start_line, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
292
293error:
294 free(buf);
295 return LY_EVALID;
296
297success:
298 ly_in_skip(jsonctx->in, in - jsonctx->in->current);
299 if (buf) {
300 lyjson_ctx_set_value(jsonctx, buf, len, 1);
301 } else {
302 lyjson_ctx_set_value(jsonctx, start, len, 0);
303 }
304
305 return LY_SUCCESS;
306
307#undef BUFSIZE
308#undef BUFSIZE_STEP
309}
310
311/*
312 *
313 * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
314 */
315static LY_ERR
316lyjson_string(struct lyjson_ctx *jsonctx)
317{
318 LY_CHECK_RET(lyjson_string_(jsonctx));
319
320 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_STRING);
321 LY_CHECK_RET(lyjson_check_next(jsonctx));
322
323 return LY_SUCCESS;
324}
325
326static LY_ERR
327lyjson_number(struct lyjson_ctx *jsonctx)
328{
329 size_t offset = 0, exponent = 0;
330 const char *in = jsonctx->in->current;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200331 uint8_t minus = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200332
333 if (in[offset] == '-') {
334 ++offset;
335 minus = 1;
336 }
337
338 if (in[offset] == '0') {
339 ++offset;
340 } else if (isdigit(in[offset])) {
341 ++offset;
342 while (isdigit(in[offset])) {
343 ++offset;
344 }
345 } else {
346invalid_character:
347 if (in[offset]) {
348 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
349 } else {
350 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_EOF);
351 }
352 return LY_EVALID;
353 }
354
355 if (in[offset] == '.') {
356 ++offset;
357 if (!isdigit(in[offset])) {
358 goto invalid_character;
359 }
360 while (isdigit(in[offset])) {
361 ++offset;
362 }
363 }
364
365 if ((in[offset] == 'e') || (in[offset] == 'E')) {
366 exponent = offset++;
367 if ((in[offset] == '+') || (in[offset] == '-')) {
368 ++offset;
369 }
370 if (!isdigit(in[offset])) {
371 goto invalid_character;
372 }
373 while (isdigit(in[offset])) {
374 ++offset;
375 }
376 }
377
378 if (exponent) {
379 /* convert JSON number with exponent into the representation used by YANG */
380 long int e_val;
381 char *ptr, *dec_point, *num;
382 const char *e_ptr = &in[exponent + 1];
383 size_t num_len, i;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200384 int64_t dp_position; /* final position of the deciaml point */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200385
386 errno = 0;
Radek Krejcif13b87b2020-12-01 22:02:17 +0100387 e_val = strtol(e_ptr, &ptr, LY_BASE_DEC);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200388 if (errno) {
389 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SEMANTICS,
Michal Vasko69730152020-10-09 16:30:07 +0200390 "Exponent out-of-bounds in a JSON Number value (%.*s).", offset - minus - (e_ptr - in), e_ptr);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200391 return LY_EVALID;
392 }
393
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200394 dec_point = ly_strnchr(in, '.', exponent);
395 if (!dec_point) {
396 /* value is integer, we are just ... */
397 if (e_val >= 0) {
398 /* adding zeros at the end */
399 num_len = exponent + e_val;
400 dp_position = num_len; /* decimal point is behind the actual value */
401 } else if ((size_t)labs(e_val) < exponent) {
402 /* adding decimal point between the integer's digits */
403 num_len = exponent + 1;
404 dp_position = exponent + e_val;
405 } else {
406 /* adding decimal point before the integer with adding leading zero(s) */
407 num_len = labs(e_val) + 2;
408 dp_position = exponent + e_val;
409 }
410 dp_position -= minus;
411 } else {
412 /* value is decimal, we are moving the decimal point */
413 dp_position = dec_point - in + e_val - minus;
414 if (dp_position > (ssize_t)exponent) {
415 /* moving decimal point after the decimal value make the integer result */
416 num_len = dp_position;
417 } else if (dp_position < 0) {
418 /* moving decimal point before the decimal value requires additional zero(s)
419 * (decimal point is already count in exponent value) */
420 num_len = exponent + labs(dp_position) + 1;
421 } else {
422 /* moving decimal point just inside the decimal value does not make any change in length */
423 num_len = exponent;
424 }
425 }
426
427 /* allocate buffer for the result (add terminating NULL-byte */
428 num = malloc(num_len + 1);
429 LY_CHECK_ERR_RET(!num, LOGMEM(jsonctx->ctx), LY_EMEM);
430
431 /* compose the resulting vlaue */
432 i = 0;
433 if (minus) {
434 num[i++] = '-';
435 }
436 /* add leading zeros */
437 if (dp_position <= 0) {
438 num[i++] = '0';
439 num[i++] = '.';
Michal Vaskod989ba02020-08-24 10:59:24 +0200440 for ( ; dp_position; dp_position++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200441 num[i++] = '0';
442 }
443 }
444 /* copy the value */
Radek Krejci857189e2020-09-01 13:26:36 +0200445 ly_bool dp_placed;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200446 size_t j;
447 for (dp_placed = dp_position ? 0 : 1, j = minus; j < exponent; j++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200448 if (in[j] == '.') {
449 continue;
450 }
451 if (!dp_placed) {
452 if (!dp_position) {
453 num[i++] = '.';
454 dp_placed = 1;
455 } else {
456 dp_position--;
457 if (in[j] == '0') {
458 num_len--;
459 continue;
460 }
461 }
462 }
463
464 num[i++] = in[j];
465 }
466 /* trailing zeros */
467 while (dp_position--) {
468 num[i++] = '0';
469 }
470 /* terminating NULL byte */
471 num[i] = '\0';
472
473 /* store the modified number */
474 lyjson_ctx_set_value(jsonctx, num, num_len, 1);
475 } else {
476 /* store the number */
477 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, offset, 0);
478 }
479 ly_in_skip(jsonctx->in, offset);
480
481 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NUMBER);
482 LY_CHECK_RET(lyjson_check_next(jsonctx));
483
484 return LY_SUCCESS;
485}
486
487static LY_ERR
488lyjson_object_name(struct lyjson_ctx *jsonctx)
489{
490 if (*jsonctx->in->current != '"') {
491 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200492 jsonctx->in->current, "a JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200493 return LY_EVALID;
494 }
495 ly_in_skip(jsonctx->in, 1);
496
497 LY_CHECK_RET(lyjson_string_(jsonctx));
498 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko08dc70b2020-10-07 13:58:47 +0200499 if (*jsonctx->in->current != ':') {
500 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_INSTREXP,
501 LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current, "a JSON object's name-separator ':'");
502 return LY_EVALID;
503 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200504 ly_in_skip(jsonctx->in, 1);
505 LY_CHECK_RET(skip_ws(jsonctx));
506
507 return LY_SUCCESS;
508}
509
510static LY_ERR
511lyjson_object(struct lyjson_ctx *jsonctx)
512{
513 LY_CHECK_RET(skip_ws(jsonctx));
514
515 if (*jsonctx->in->current == '}') {
516 /* empty object */
517 ly_in_skip(jsonctx->in, 1);
518 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
519 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT_EMPTY);
520 return LY_SUCCESS;
521 }
522
523 LY_CHECK_RET(lyjson_object_name(jsonctx));
524
525 /* output data are set by lyjson_string_() */
526 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT);
527
528 return LY_SUCCESS;
529}
530
531/*
532 * @brief Process JSON array envelope
533 *
534 *
535 *
536 * @param[in] jsonctx JSON parser context
537 * @return LY_SUCCESS or LY_EMEM
538 */
539static LY_ERR
540lyjson_array(struct lyjson_ctx *jsonctx)
541{
542 LY_CHECK_RET(skip_ws(jsonctx));
543
544 if (*jsonctx->in->current == ']') {
545 /* empty array */
546 ly_in_skip(jsonctx->in, 1);
547 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY_EMPTY);
548 } else {
549 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY);
550 }
551
552 /* erase previous values, array has no value on its own */
553 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
554
555 return LY_SUCCESS;
556}
557
558static LY_ERR
559lyjson_value(struct lyjson_ctx *jsonctx)
560{
Michal Vasko69730152020-10-09 16:30:07 +0200561 if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200562 return LY_SUCCESS;
563 }
564
Radek Krejcif13b87b2020-12-01 22:02:17 +0100565 if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200566 /* false */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100567 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
568 ly_in_skip(jsonctx->in, ly_strlen_const("false"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200569 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_FALSE);
570 LY_CHECK_RET(lyjson_check_next(jsonctx));
571
Radek Krejcif13b87b2020-12-01 22:02:17 +0100572 } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200573 /* true */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100574 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
575 ly_in_skip(jsonctx->in, ly_strlen_const("true"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200576 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_TRUE);
577 LY_CHECK_RET(lyjson_check_next(jsonctx));
578
Radek Krejcif13b87b2020-12-01 22:02:17 +0100579 } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200580 /* none */
Radek Krejci201963a2020-12-03 11:43:40 +0100581 lyjson_ctx_set_value(jsonctx, "", 0, 0);
Radek Krejcif13b87b2020-12-01 22:02:17 +0100582 ly_in_skip(jsonctx->in, ly_strlen_const("null"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200583 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NULL);
584 LY_CHECK_RET(lyjson_check_next(jsonctx));
585
586 } else if (*jsonctx->in->current == '"') {
587 /* string */
588 ly_in_skip(jsonctx->in, 1);
589 LY_CHECK_RET(lyjson_string(jsonctx));
590
591 } else if (*jsonctx->in->current == '[') {
592 /* array */
593 ly_in_skip(jsonctx->in, 1);
594 LY_CHECK_RET(lyjson_array(jsonctx));
595
596 } else if (*jsonctx->in->current == '{') {
597 /* object */
598 ly_in_skip(jsonctx->in, 1);
599 LY_CHECK_RET(lyjson_object(jsonctx));
600
Michal Vasko69730152020-10-09 16:30:07 +0200601 } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200602 /* number */
603 LY_CHECK_RET(lyjson_number(jsonctx));
604
605 } else {
606 /* unexpected value */
607 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200608 jsonctx->in->current, "a JSON value");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200609 return LY_EVALID;
610 }
611
612 return LY_SUCCESS;
613}
614
615LY_ERR
616lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
617{
618 LY_ERR ret = LY_SUCCESS;
619 struct lyjson_ctx *jsonctx;
620
621 assert(ctx);
622 assert(in);
623 assert(jsonctx_p);
624
625 /* new context */
626 jsonctx = calloc(1, sizeof *jsonctx);
627 LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
628 jsonctx->ctx = ctx;
629 jsonctx->line = 1;
630 jsonctx->in = in;
631
632 /* parse JSON value, if any */
633 LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
634 if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
635 /* empty data input */
636 goto cleanup;
637 }
638
639 ret = lyjson_value(jsonctx);
640
Michal Vasko69730152020-10-09 16:30:07 +0200641 if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200642 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_EOF);
643 ret = LY_EVALID;
644 }
645
646cleanup:
647 if (ret) {
648 lyjson_ctx_free(jsonctx);
649 } else {
650 *jsonctx_p = jsonctx;
651 }
652 return ret;
653}
654
655void
656lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
657{
658 if (jsonctx->backup.dynamic) {
659 free((char *)jsonctx->backup.value);
660 }
661 jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
662 jsonctx->backup.status_count = jsonctx->status.count;
663 jsonctx->backup.value = jsonctx->value;
664 jsonctx->backup.value_len = jsonctx->value_len;
665 jsonctx->backup.input = jsonctx->in->current;
666 jsonctx->backup.dynamic = jsonctx->dynamic;
667 jsonctx->dynamic = 0;
668}
669
670void
671lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
672{
673 if (jsonctx->dynamic) {
674 free((char *)jsonctx->value);
675 }
676 jsonctx->status.count = jsonctx->backup.status_count;
Michal Vasko22df3f02020-08-24 13:29:22 +0200677 jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200678 jsonctx->value = jsonctx->backup.value;
679 jsonctx->value_len = jsonctx->backup.value_len;
680 jsonctx->in->current = jsonctx->backup.input;
681 jsonctx->dynamic = jsonctx->backup.dynamic;
682 jsonctx->backup.dynamic = 0;
683}
684
685LY_ERR
686lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
687{
688 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200689 ly_bool toplevel = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200690 enum LYJSON_PARSER_STATUS prev;
691
692 assert(jsonctx);
693
694 prev = lyjson_ctx_status(jsonctx, 0);
695
Michal Vasko69730152020-10-09 16:30:07 +0200696 if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200697 /* get value for the object's member OR the first value in the array */
698 ret = lyjson_value(jsonctx);
699 goto result;
700 } else {
701 /* the previous token is closed and should be completely processed */
702 JSON_POP_STATUS_RET(jsonctx);
703 prev = lyjson_ctx_status(jsonctx, 0);
704 }
705
706 if (!jsonctx->status.count) {
707 /* we are done with the top level value */
708 toplevel = 1;
709 }
710 LY_CHECK_RET(skip_ws(jsonctx));
711 if (toplevel && !jsonctx->status.count) {
712 /* EOF expected, but there are some data after the top level token */
713 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LYVE_SYNTAX,
Michal Vasko69730152020-10-09 16:30:07 +0200714 "Expecting end-of-input, but some data follows the top level JSON value.");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200715 return LY_EVALID;
716 }
717
718 if (toplevel) {
719 /* we are done */
720 return LY_SUCCESS;
721 }
722
723 /* continue with the next token */
724 assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
725
726 if (*jsonctx->in->current == ',') {
727 /* sibling item in the ... */
728 ly_in_skip(jsonctx->in, 1);
729 LY_CHECK_RET(skip_ws(jsonctx));
730
731 if (prev == LYJSON_OBJECT) {
732 /* ... object - get another object's member */
733 ret = lyjson_object_name(jsonctx);
734 } else { /* LYJSON_ARRAY */
735 /* ... array - get another complete value */
736 ret = lyjson_value(jsonctx);
737 }
Michal Vasko69730152020-10-09 16:30:07 +0200738 } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200739 ly_in_skip(jsonctx->in, 1);
740 JSON_POP_STATUS_RET(jsonctx);
741 JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
742 } else {
743 /* unexpected value */
744 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200745 jsonctx->in->current, prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200746 return LY_EVALID;
747 }
748
749result:
Michal Vasko69730152020-10-09 16:30:07 +0200750 if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200751 LOGVAL(jsonctx->ctx, LY_VLOG_LINE, &jsonctx->line, LY_VCODE_EOF);
752 ret = LY_EVALID;
753 }
754
Michal Vasko69730152020-10-09 16:30:07 +0200755 if ((ret == LY_SUCCESS) && status) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200756 *status = lyjson_ctx_status(jsonctx, 0);
757 }
758
759 return ret;
760}
761
762enum LYJSON_PARSER_STATUS
763lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
764{
765 assert(jsonctx);
766
767 if (jsonctx->status.count < index) {
768 return LYJSON_ERROR;
769 } else if (jsonctx->status.count == index) {
770 return LYJSON_ROOT;
771 } else {
Michal Vasko27915722020-08-31 14:54:42 +0200772 return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200773 }
774}
775
776void
777lyjson_ctx_free(struct lyjson_ctx *jsonctx)
778{
779 if (!jsonctx) {
780 return;
781 }
782
783 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200784 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200785 }
786 if (jsonctx->backup.dynamic) {
787 free((char *)jsonctx->backup.value);
788 }
789
790 ly_set_erase(&jsonctx->status, NULL);
791
792 free(jsonctx);
793}