blob: 53f41dcb7a24f2beca33b5aee0bf358b2d6e072f [file] [log] [blame]
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001/**
2 * @file json.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko09e04632023-03-22 14:34:10 +01004 * @author Michal Vasko <mvasko@cesnet.cz>
Radek Krejci50f0c6b2020-06-18 16:31:48 +02005 * @brief Generic JSON format parser for libyang
6 *
Michal Vasko09e04632023-03-22 14:34:10 +01007 * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
Radek Krejci50f0c6b2020-06-18 16:31:48 +02008 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#include <assert.h>
17#include <ctype.h>
18#include <errno.h>
Radek Krejci50f0c6b2020-06-18 16:31:48 +020019#include <stdlib.h>
Radek Krejci47fab892020-11-05 17:02:41 +010020#include <string.h>
Radek Krejci50f0c6b2020-06-18 16:31:48 +020021#include <sys/types.h>
22
23#include "common.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020024#include "in_internal.h"
Radek Krejci47fab892020-11-05 17:02:41 +010025#include "json.h"
aPiecek704f8e92021-08-25 13:35:05 +020026#include "tree_schema_internal.h"
Radek Krejci50f0c6b2020-06-18 16:31:48 +020027
Michal Vasko22df3f02020-08-24 13:29:22 +020028const char *
Radek Krejci50f0c6b2020-06-18 16:31:48 +020029lyjson_token2str(enum LYJSON_PARSER_STATUS status)
30{
31 switch (status) {
32 case LYJSON_ERROR:
33 return "error";
Radek Krejci50f0c6b2020-06-18 16:31:48 +020034 case LYJSON_OBJECT:
35 return "object";
Michal Vasko09e04632023-03-22 14:34:10 +010036 case LYJSON_OBJECT_NEXT:
37 return "object next";
Radek Krejci50f0c6b2020-06-18 16:31:48 +020038 case LYJSON_OBJECT_CLOSED:
39 return "object closed";
Radek Krejci50f0c6b2020-06-18 16:31:48 +020040 case LYJSON_ARRAY:
41 return "array";
Michal Vasko09e04632023-03-22 14:34:10 +010042 case LYJSON_ARRAY_NEXT:
43 return "array next";
Radek Krejci50f0c6b2020-06-18 16:31:48 +020044 case LYJSON_ARRAY_CLOSED:
45 return "array closed";
Michal Vasko09e04632023-03-22 14:34:10 +010046 case LYJSON_OBJECT_NAME:
47 return "object name";
Radek Krejci50f0c6b2020-06-18 16:31:48 +020048 case LYJSON_NUMBER:
49 return "number";
50 case LYJSON_STRING:
51 return "string";
Michal Vasko09e04632023-03-22 14:34:10 +010052 case LYJSON_TRUE:
53 return "true";
54 case LYJSON_FALSE:
55 return "false";
56 case LYJSON_NULL:
57 return "null";
Radek Krejci50f0c6b2020-06-18 16:31:48 +020058 case LYJSON_END:
59 return "end of input";
60 }
61
62 return "";
63}
64
Michal Vasko09e04632023-03-22 14:34:10 +010065enum LYJSON_PARSER_STATUS
66lyjson_ctx_status(struct lyjson_ctx *jsonctx)
Radek Krejci50f0c6b2020-06-18 16:31:48 +020067{
Michal Vasko09e04632023-03-22 14:34:10 +010068 assert(jsonctx);
69
70 if (!jsonctx->status.count) {
71 return LYJSON_END;
72 }
73
74 return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - 1];
75}
76
77uint32_t
78lyjson_ctx_depth(struct lyjson_ctx *jsonctx)
79{
80 return jsonctx->status.count;
81}
82
83/**
84 * @brief Skip WS in the JSON context.
85 *
86 * @param[in] jsonctx JSON parser context.
87 */
88static void
89lyjson_skip_ws(struct lyjson_ctx *jsonctx)
90{
91 /* skip whitespaces */
92 while (is_jsonws(*jsonctx->in->current)) {
Radek Krejcidd713ce2021-01-04 23:12:12 +010093 if (*jsonctx->in->current == '\n') {
94 LY_IN_NEW_LINE(jsonctx->in);
95 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +020096 ly_in_skip(jsonctx->in, 1);
97 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +020098}
99
Michal Vasko09e04632023-03-22 14:34:10 +0100100/**
101 * @brief Set value in the JSON context.
102 *
103 * @param[in] jsonctx JSON parser context.
104 * @param[in] value Value to set.
105 * @param[in] value_len Length of @p value.
106 * @param[in] dynamic Whether @p value is dynamically-allocated.
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200107 */
108static void
Radek Krejci857189e2020-09-01 13:26:36 +0200109lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200110{
111 assert(jsonctx);
112
Juraj Vijtiukec285cd2021-01-14 11:41:20 +0100113 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200114 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200115 }
116 jsonctx->value = value;
117 jsonctx->value_len = value_len;
118 jsonctx->dynamic = dynamic;
119}
120
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200121/**
Michal Vasko09e04632023-03-22 14:34:10 +0100122 * @brief Parse a JSON string (starting after double quotes) and store it in the context.
123 *
124 * @param[in] jsonctx JSON parser context.
125 * @return LY_ERR value.
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200126 */
127static LY_ERR
Michal Vasko09e04632023-03-22 14:34:10 +0100128lyjson_string(struct lyjson_ctx *jsonctx)
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200129{
Michal Vasko09e04632023-03-22 14:34:10 +0100130 const char *in = jsonctx->in->current, *start, *c;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200131 char *buf = NULL;
132 size_t offset; /* read offset in input buffer */
133 size_t len; /* length of the output string (write offset in output buffer) */
134 size_t size = 0; /* size of the output buffer */
135 size_t u;
136 uint64_t start_line;
Michal Vasko09e04632023-03-22 14:34:10 +0100137 uint32_t value;
138 uint8_t i;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200139
140 assert(jsonctx);
141
142 /* init */
143 start = in;
Radek Krejcid54412f2020-12-17 20:25:35 +0100144 start_line = jsonctx->in->line;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200145 offset = len = 0;
146
147 /* parse */
148 while (in[offset]) {
Michal Vasko09e04632023-03-22 14:34:10 +0100149 switch (in[offset]) {
150 case '\\':
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200151 /* escape sequence */
Michal Vasko09e04632023-03-22 14:34:10 +0100152 c = &in[offset];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200153 if (!buf) {
154 /* prepare output buffer */
Michal Vasko09e04632023-03-22 14:34:10 +0100155 buf = malloc(LYJSON_STRING_BUF_START);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200156 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
Michal Vasko09e04632023-03-22 14:34:10 +0100157 size = LYJSON_STRING_BUF_START;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200158 }
159
160 /* allocate enough for the offset and next character,
161 * we will need 4 bytes at most since we support only the predefined
162 * (one-char) entities and character references */
163 if (len + offset + 4 >= size) {
Juraj Vijtiukd746a352021-01-15 11:33:33 +0100164 size_t increment;
Michal Vasko2bf4af42023-01-04 12:08:38 +0100165
Michal Vasko09e04632023-03-22 14:34:10 +0100166 for (increment = LYJSON_STRING_BUF_STEP; len + offset + 4 >= size + increment; increment += LYJSON_STRING_BUF_STEP) {}
Juraj Vijtiukd746a352021-01-15 11:33:33 +0100167 buf = ly_realloc(buf, size + increment);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200168 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
Michal Vasko09e04632023-03-22 14:34:10 +0100169 size += LYJSON_STRING_BUF_STEP;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200170 }
171
172 if (offset) {
173 /* store what we have so far */
174 memcpy(&buf[len], in, offset);
175 len += offset;
176 in += offset;
177 offset = 0;
178 }
179
Michal Vasko09e04632023-03-22 14:34:10 +0100180 i = 1;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200181 switch (in[++offset]) {
182 case '"':
183 /* quotation mark */
184 value = 0x22;
185 break;
186 case '\\':
187 /* reverse solidus */
188 value = 0x5c;
189 break;
190 case '/':
191 /* solidus */
192 value = 0x2f;
193 break;
194 case 'b':
195 /* backspace */
196 value = 0x08;
197 break;
198 case 'f':
199 /* form feed */
200 value = 0x0c;
201 break;
202 case 'n':
203 /* line feed */
204 value = 0x0a;
205 break;
206 case 'r':
207 /* carriage return */
208 value = 0x0d;
209 break;
210 case 't':
211 /* tab */
212 value = 0x09;
213 break;
214 case 'u':
215 /* Basic Multilingual Plane character \uXXXX */
216 offset++;
217 for (value = i = 0; i < 4; i++) {
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100218 if (!in[offset + i]) {
Michal Vasko09e04632023-03-22 14:34:10 +0100219 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", c);
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100220 goto error;
221 } else if (isdigit(in[offset + i])) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200222 u = (in[offset + i] - '0');
223 } else if (in[offset + i] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100224 u = LY_BASE_DEC + (in[offset + i] - 'a');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200225 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100226 u = LY_BASE_DEC + (in[offset + i] - 'A');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200227 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100228 value = (LY_BASE_HEX * value) + u;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200229 }
230 break;
231 default:
232 /* invalid escape sequence */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100233 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200234 goto error;
235
236 }
237
238 offset += i; /* add read escaped characters */
239 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100240 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
Michal Vasko09e04632023-03-22 14:34:10 +0100241 (int)(&in[offset] - c), c, value),
Michal Vasko69730152020-10-09 16:30:07 +0200242 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200243 len += u; /* update number of bytes in buffer */
244 in += offset; /* move the input by the processed bytes stored in the buffer ... */
245 offset = 0; /* ... and reset the offset index for future moving data into buffer */
Michal Vasko09e04632023-03-22 14:34:10 +0100246 break;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200247
Michal Vasko09e04632023-03-22 14:34:10 +0100248 case '"':
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200249 /* end of string */
250 if (buf) {
251 /* realloc exact size string */
252 buf = ly_realloc(buf, len + offset + 1);
253 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
254 size = len + offset + 1;
Michal Vasko08e9b112021-06-11 15:41:17 +0200255 if (offset) {
256 memcpy(&buf[len], in, offset);
257 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200258
259 /* set terminating NULL byte */
260 buf[len + offset] = '\0';
261 }
262 len += offset;
263 ++offset;
264 in += offset;
265 goto success;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200266
Michal Vasko09e04632023-03-22 14:34:10 +0100267 default:
268 /* get it as UTF-8 character for check */
269 c = &in[offset];
270 LY_CHECK_ERR_GOTO(ly_getutf8(&c, &value, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100271 LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200272
Michal Vasko09e04632023-03-22 14:34:10 +0100273 LY_CHECK_ERR_GOTO(!is_jsonstrchar(value),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100274 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
Michal Vasko09e04632023-03-22 14:34:10 +0100275 (int)(&in[offset] - start + u), start, value),
Michal Vasko69730152020-10-09 16:30:07 +0200276 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200277
278 /* character is ok, continue */
Michal Vasko09e04632023-03-22 14:34:10 +0100279 offset += u;
280 break;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200281 }
282 }
283
284 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100285 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
286 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 +0200287
288error:
289 free(buf);
290 return LY_EVALID;
291
292success:
Radek Krejcid54412f2020-12-17 20:25:35 +0100293 jsonctx->in->current = in;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200294 if (buf) {
295 lyjson_ctx_set_value(jsonctx, buf, len, 1);
296 } else {
297 lyjson_ctx_set_value(jsonctx, start, len, 0);
298 }
299
300 return LY_SUCCESS;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200301}
302
aPieceke87c0a12021-05-13 15:43:26 +0200303/**
aPiecek76034c32021-06-08 15:03:11 +0200304 * @brief Calculate how many @p c characters there are in a row.
305 *
306 * @param[in] str Count from this position.
307 * @param[in] end Position after the last checked character.
308 * @param[in] c Checked character.
309 * @param[in] backwards Set to 1, if to proceed from end-1 to str.
310 * @return Number of characters in a row.
311 */
312static uint32_t
313lyjson_count_in_row(const char *str, const char *end, char c, ly_bool backwards)
314{
315 uint32_t cnt;
316
317 assert(str && end);
318
319 if (str >= end) {
320 return 0;
321 }
322
323 if (!backwards) {
324 for (cnt = 0; (str != end) && (*str == c); ++str, ++cnt) {}
325 } else {
326 --end;
327 --str;
328 for (cnt = 0; (str != end) && (*end == c); --end, ++cnt) {}
329 }
330
331 return cnt;
332}
333
334/**
335 * @brief Check if the number can be shortened to zero.
336 *
aPiecek76034c32021-06-08 15:03:11 +0200337 * @param[in] in Start of input string;
338 * @param[in] end End of input string;
339 * @return 1 if number is zero, otherwise 0.
340 */
341static ly_bool
342lyjson_number_is_zero(const char *in, const char *end)
343{
aPiecek28e101a2021-06-10 09:09:31 +0200344 assert(in < end);
aPiecek76034c32021-06-08 15:03:11 +0200345
346 if ((in[0] == '-') || (in[0] == '+')) {
347 in++;
aPiecek28e101a2021-06-10 09:09:31 +0200348 assert(in < end);
aPiecek76034c32021-06-08 15:03:11 +0200349 }
350 if ((in[0] == '0') && (in[1] == '.')) {
351 in += 2;
aPiecek28e101a2021-06-10 09:09:31 +0200352 if (!(in < end)) {
353 return 1;
354 }
aPiecek76034c32021-06-08 15:03:11 +0200355 }
356
357 return lyjson_count_in_row(in, end, '0', 0) == end - in;
358}
359
360/**
aPieceke87c0a12021-05-13 15:43:26 +0200361 * @brief Allocate buffer for number in string format.
362 *
363 * @param[in] jsonctx JSON context.
364 * @param[in] num_len Required space in bytes for a number.
365 * Terminating null byte is added by default.
366 * @param[out] buffer Output allocated buffer.
367 * @return LY_ERR value.
368 */
369static LY_ERR
aPiecek0ba088e2021-06-15 12:53:17 +0200370lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **buffer)
aPieceke87c0a12021-05-13 15:43:26 +0200371{
372 *buffer = NULL;
373
aPiecek76034c32021-06-08 15:03:11 +0200374 LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(ctx, LYVE_SEMANTICS,
aPieceke87c0a12021-05-13 15:43:26 +0200375 "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
376
aPiecek76034c32021-06-08 15:03:11 +0200377 /* allocate buffer for the result (add NULL-byte) */
aPieceke87c0a12021-05-13 15:43:26 +0200378 *buffer = malloc(num_len + 1);
aPiecek76034c32021-06-08 15:03:11 +0200379 LY_CHECK_ERR_RET(!(*buffer), LOGMEM(ctx), LY_EMEM);
380 return LY_SUCCESS;
381}
382
383/**
384 * @brief Copy the 'numeric part' (@p num) except its decimal point
385 * (@p dec_point) and insert the new decimal point (@p dp_position)
386 * only if it is to be placed in the 'numeric part' range (@p num).
387 *
388 * @param[in] num Begin of the 'numeric part'.
389 * @param[in] num_len Length of the 'numeric part'.
390 * @param[in] dec_point Pointer to the old decimal point.
391 * If it has a NULL value, it is ignored.
392 * @param[in] dp_position Position of the new decimal point.
393 * If it has a negative value, it is ignored.
394 * @param[out] dst Memory into which the copied result is written.
395 * @return Number of characters written to the @p dst.
396 */
397static uint32_t
Michal Vasko09e04632023-03-22 14:34:10 +0100398lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, char *dec_point, int32_t dp_position, char *dst)
aPiecek76034c32021-06-08 15:03:11 +0200399{
400 int32_t dec_point_idx;
401 int32_t n, d;
402
403 assert(num && dst);
404
405 dec_point_idx = dec_point ? dec_point - num : INT32_MAX;
406 assert((dec_point_idx >= 0) && (dec_point_idx != dp_position));
407
408 for (n = 0, d = 0; (uint32_t)n < num_len; n++) {
409 if (n == dec_point_idx) {
410 continue;
411 } else if (d == dp_position) {
412 dst[d++] = '.';
413 dst[d++] = num[n];
414 } else {
415 dst[d++] = num[n];
416 }
417 }
418
419 return d;
420}
421
422/**
423 * @brief Convert JSON number with exponent into the representation
424 * used by YANG.
425 *
426 * The input numeric string must be syntactically valid. Also, before
427 * calling this function, checks should be performed using the
428 * ::lyjson_number_is_zero().
429 *
430 * @param[in] ctx Context for the error message.
431 * @param[in] in Beginning of the string containing the number.
432 * @param[in] exponent Pointer to the letter E/e.
433 * @param[in] total_len Total size of the input number.
434 * @param[out] res Conversion result.
435 * @param[out] res_len Length of the result.
436 * @return LY_ERR value.
437 */
438static LY_ERR
Michal Vasko09e04632023-03-22 14:34:10 +0100439lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, uint64_t total_len, char **res,
440 size_t *res_len)
aPiecek76034c32021-06-08 15:03:11 +0200441{
442
443#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
444 if (FLAG) { \
445 ARRAY[INDEX++] = '-'; \
446 }
447
448/* Length of leading zero followed by the decimal point. */
449#define LEADING_ZERO 1
450
451/* Flags for the ::lyjson_count_in_row() */
452#define FORWARD 0
453#define BACKWARD 1
454
455 /* Buffer where the result is stored. */
456 char *buf;
457 /* Size without space for terminating NULL-byte. */
aPiecek0ba088e2021-06-15 12:53:17 +0200458 uint64_t buf_len;
aPiecek76034c32021-06-08 15:03:11 +0200459 /* Index to buf. */
460 uint32_t i = 0;
461 /* A 'numeric part' doesn't contain a minus sign or an leading zero.
462 * For example, in 0.45, there is the leading zero.
463 */
464 const char *num;
465 /* Length of the 'numeric part' ends before E/e. */
aPiecek0ba088e2021-06-15 12:53:17 +0200466 uint16_t num_len;
aPiecek76034c32021-06-08 15:03:11 +0200467 /* Position of decimal point in the num. */
468 char *dec_point;
469 /* Final position of decimal point in the buf. */
470 int32_t dp_position;
471 /* Exponent as integer. */
Michal Vasko2bf4af42023-01-04 12:08:38 +0100472 long long e_val;
aPiecek76034c32021-06-08 15:03:11 +0200473 /* Byte for the decimal point. */
474 int8_t dot;
475 /* Required additional byte for the minus sign. */
476 uint8_t minus;
477 /* The number of zeros. */
478 long zeros;
479 /* If the number starts with leading zero followed by the decimal point. */
480 ly_bool leading_zero;
481
482 assert(ctx && in && exponent && res && res_len && (total_len > 2));
483 assert((in < exponent) && ((*exponent == 'e') || (*exponent == 'E')));
484
aPiecek0ba088e2021-06-15 12:53:17 +0200485 if ((exponent - in) > UINT16_MAX) {
486 LOGVAL(ctx, LYVE_SEMANTICS, "JSON number is too long.");
487 return LY_EVALID;
488 }
489
aPiecek76034c32021-06-08 15:03:11 +0200490 /* Convert exponent. */
491 errno = 0;
Michal Vasko73d77ab2021-07-23 12:45:55 +0200492 e_val = strtoll(exponent + 1, NULL, LY_BASE_DEC);
aPieceke4e0d6c2021-07-01 12:02:54 +0200493 if (errno || (e_val > UINT16_MAX) || (e_val < -UINT16_MAX)) {
aPiecek76034c32021-06-08 15:03:11 +0200494 LOGVAL(ctx, LYVE_SEMANTICS,
495 "Exponent out-of-bounds in a JSON Number value (%.*s).",
Michal Vasko54ba8912021-07-23 12:46:23 +0200496 (int)total_len, in);
aPiecek76034c32021-06-08 15:03:11 +0200497 return LY_EVALID;
498 }
499
500 minus = in[0] == '-';
501 if (in[minus] == '0') {
502 assert(in[minus + 1] == '.');
503 leading_zero = 1;
504 /* The leading zero has been found, it will be skipped. */
505 num = &in[minus + 1];
506 } else {
507 leading_zero = 0;
508 /* Set to the first number. */
509 num = &in[minus];
510 }
511 num_len = exponent - num;
512
513 /* Find the location of the decimal points. */
514 dec_point = ly_strnchr(num, '.', num_len);
515 dp_position = dec_point ?
516 dec_point - num + e_val :
517 num_len + e_val;
518
519 /* Remove zeros after the decimal point from the end of
520 * the 'numeric part' because these are useless.
521 * (For example, in 40.001000 these are the last 3).
522 */
523 num_len -= dp_position > 0 ?
524 lyjson_count_in_row(num + dp_position - 1, exponent, '0', BACKWARD) :
525 lyjson_count_in_row(num, exponent, '0', BACKWARD);
526
527 /* Decide what to do with the dot from the 'numeric part'. */
528 if (dec_point && ((int32_t)(num_len - 1) == dp_position)) {
529 /* Decimal point in the last place is useless. */
530 dot = -1;
531 } else if (dec_point) {
532 /* Decimal point is shifted. */
533 dot = 0;
534 } else {
535 /* Additional byte for the decimal point is requred. */
536 dot = 1;
537 }
538
539 /* Final composition of the result. */
540 if (dp_position <= 0) {
541 /* Adding decimal point before the integer with adding additional zero(s). */
542
543 zeros = labs(dp_position);
544 buf_len = minus + LEADING_ZERO + dot + zeros + num_len;
545 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
546 MAYBE_WRITE_MINUS(buf, i, minus);
547 buf[i++] = '0';
548 buf[i++] = '.';
549 memset(buf + i, '0', zeros);
550 i += zeros;
551 dp_position = -1;
552 lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
553 } else if (leading_zero && (dp_position < (ssize_t)num_len)) {
554 /* Insert decimal point between the integer's digits. */
555
556 /* Set a new range of 'numeric part'. Old decimal point is skipped. */
557 num++;
558 num_len--;
559 dp_position--;
560 /* Get the number of useless zeros between the old
561 * and new decimal point. For example, in the number 0.005E1,
562 * there is one useless zero.
563 */
564 zeros = lyjson_count_in_row(num, num + dp_position + 1, '0', FORWARD);
565 /* If the new decimal point will be in the place of the first non-zero subnumber. */
566 if (zeros == (dp_position + 1)) {
567 /* keep one zero as leading zero */
568 zeros--;
569 /* new decimal point will be behind the leading zero */
570 dp_position = 1;
571 dot = 1;
572 } else {
573 dot = 0;
574 }
575 buf_len = minus + dot + (num_len - zeros);
576 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
577 MAYBE_WRITE_MINUS(buf, i, minus);
578 /* Skip useless zeros and copy. */
579 lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
580 } else if (dp_position < (ssize_t)num_len) {
581 /* Insert decimal point between the integer's digits. */
582
583 buf_len = minus + dot + num_len;
584 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
585 MAYBE_WRITE_MINUS(buf, i, minus);
586 lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
587 } else if (leading_zero) {
588 /* Adding decimal point after the decimal value make the integer result. */
589
590 /* Set a new range of 'numeric part'. Old decimal point is skipped. */
591 num++;
592 num_len--;
593 /* Get the number of useless zeros. */
594 zeros = lyjson_count_in_row(num, num + num_len, '0', FORWARD);
595 buf_len = minus + dp_position - zeros;
596 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
597 MAYBE_WRITE_MINUS(buf, i, minus);
598 /* Skip useless zeros and copy. */
599 i += lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
600 /* Add multiples of ten behind the 'numeric part'. */
601 memset(buf + i, '0', buf_len - i);
602 } else {
603 /* Adding decimal point after the decimal value make the integer result. */
604
605 buf_len = minus + dp_position;
606 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
607 MAYBE_WRITE_MINUS(buf, i, minus);
608 i += lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
609 /* Add multiples of ten behind the 'numeric part'. */
610 memset(buf + i, '0', buf_len - i);
611 }
612
613 buf[buf_len] = '\0';
614 *res = buf;
615 *res_len = buf_len;
616
617#undef MAYBE_WRITE_MINUS
618#undef LEADING_ZERO
619#undef FORWARD
620#undef BACKWARD
621
aPieceke87c0a12021-05-13 15:43:26 +0200622 return LY_SUCCESS;
623}
624
Michal Vasko09e04632023-03-22 14:34:10 +0100625/**
626 * @brief Parse a JSON number and store it in the context.
627 *
628 * @param[in] jsonctx JSON parser context.
629 * @return LY_ERR value.
630 */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200631static LY_ERR
632lyjson_number(struct lyjson_ctx *jsonctx)
633{
aPiecek76034c32021-06-08 15:03:11 +0200634 size_t offset = 0, num_len;
635 const char *in = jsonctx->in->current, *exponent = NULL;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200636 uint8_t minus = 0;
aPiecek76034c32021-06-08 15:03:11 +0200637 char *num;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200638
639 if (in[offset] == '-') {
640 ++offset;
641 minus = 1;
642 }
643
644 if (in[offset] == '0') {
645 ++offset;
646 } else if (isdigit(in[offset])) {
647 ++offset;
648 while (isdigit(in[offset])) {
649 ++offset;
650 }
651 } else {
652invalid_character:
653 if (in[offset]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100654 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200655 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100656 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200657 }
658 return LY_EVALID;
659 }
660
661 if (in[offset] == '.') {
662 ++offset;
663 if (!isdigit(in[offset])) {
664 goto invalid_character;
665 }
666 while (isdigit(in[offset])) {
667 ++offset;
668 }
669 }
670
671 if ((in[offset] == 'e') || (in[offset] == 'E')) {
aPiecek76034c32021-06-08 15:03:11 +0200672 exponent = &in[offset];
673 ++offset;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200674 if ((in[offset] == '+') || (in[offset] == '-')) {
675 ++offset;
676 }
677 if (!isdigit(in[offset])) {
678 goto invalid_character;
679 }
680 while (isdigit(in[offset])) {
681 ++offset;
682 }
683 }
684
aPiecek76034c32021-06-08 15:03:11 +0200685 if (lyjson_number_is_zero(in, exponent ? exponent : &in[offset])) {
686 lyjson_ctx_set_value(jsonctx, in, minus + 1, 0);
687 } else if (exponent && lyjson_number_is_zero(exponent + 1, &in[offset])) {
688 lyjson_ctx_set_value(jsonctx, in, exponent - in, 0);
689 } else if (exponent) {
690 LY_CHECK_RET(lyjson_exp_number(jsonctx->ctx, in, exponent, offset, &num, &num_len));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200691 lyjson_ctx_set_value(jsonctx, num, num_len, 1);
692 } else {
aPiecek5b6dd182021-06-10 09:11:58 +0200693 if (offset > LY_NUMBER_MAXLEN) {
694 LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
695 "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.");
696 return LY_EVALID;
697 }
aPiecek76034c32021-06-08 15:03:11 +0200698 lyjson_ctx_set_value(jsonctx, in, offset, 0);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200699 }
700 ly_in_skip(jsonctx->in, offset);
701
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200702 return LY_SUCCESS;
703}
704
705LY_ERR
Michal Vasko09e04632023-03-22 14:34:10 +0100706lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200707{
708 LY_ERR ret = LY_SUCCESS;
709 struct lyjson_ctx *jsonctx;
710
Michal Vasko09e04632023-03-22 14:34:10 +0100711 assert(ctx && in && jsonctx_p);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200712
713 /* new context */
714 jsonctx = calloc(1, sizeof *jsonctx);
715 LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
716 jsonctx->ctx = ctx;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200717 jsonctx->in = in;
718
Michal Vaskof8ebf132022-11-21 14:06:48 +0100719 LOG_LOCSET(NULL, NULL, NULL, in);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100720
Michal Vasko09e04632023-03-22 14:34:10 +0100721 /* WS are always expected to be skipped */
722 lyjson_skip_ws(jsonctx);
723
724 if (jsonctx->in->current[0] == '\0') {
725 /* empty file, invalid */
726 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON file.");
727 ret = LY_EVALID;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200728 goto cleanup;
729 }
730
Michal Vasko09e04632023-03-22 14:34:10 +0100731 /* start JSON parsing */
732 LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200733
734cleanup:
735 if (ret) {
736 lyjson_ctx_free(jsonctx);
737 } else {
738 *jsonctx_p = jsonctx;
739 }
740 return ret;
741}
742
Michal Vasko09e04632023-03-22 14:34:10 +0100743/**
744 * @brief Parse next JSON token, object-name is expected.
745 *
746 * @param[in] jsonctx JSON parser context.
747 * @return LY_ERR value.
748 */
749static LY_ERR
750lyjson_next_object_name(struct lyjson_ctx *jsonctx)
751{
752 switch (*jsonctx->in->current) {
753 case '\0':
754 /* EOF */
755 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
756 return LY_EVALID;
757
758 case '"':
759 /* object name */
760 ly_in_skip(jsonctx->in, 1);
761 LY_CHECK_RET(lyjson_string(jsonctx));
762 lyjson_skip_ws(jsonctx);
763
764 if (*jsonctx->in->current != ':') {
765 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
766 "a JSON value name-separator ':'");
767 return LY_EVALID;
768 }
769 ly_in_skip(jsonctx->in, 1);
770 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME);
771 break;
772
773 case '}':
774 /* object end */
775 ly_in_skip(jsonctx->in, 1);
776 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
777 break;
778
779 default:
780 /* unexpected value */
781 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
782 jsonctx->in->current, "a JSON object name");
783 return LY_EVALID;
784 }
785
786 return LY_SUCCESS;
787}
788
789/**
790 * @brief Parse next JSON token, value is expected.
791 *
792 * @param[in] jsonctx JSON parser context.
793 * @param[in] array_end Whether array-end is accepted or not.
794 * @return LY_ERR value.
795 */
796static LY_ERR
797lyjson_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end)
798{
799 switch (*jsonctx->in->current) {
800 case '\0':
801 /* EOF */
802 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
803 return LY_EVALID;
804
805 case '"':
806 /* string */
807 ly_in_skip(jsonctx->in, 1);
808 LY_CHECK_RET(lyjson_string(jsonctx));
809 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
810 break;
811
812 case '-':
813 case '0':
814 case '1':
815 case '2':
816 case '3':
817 case '4':
818 case '5':
819 case '6':
820 case '7':
821 case '8':
822 case '9':
823 /* number */
824 LY_CHECK_RET(lyjson_number(jsonctx));
825 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
826 break;
827
828 case '{':
829 /* object */
830 ly_in_skip(jsonctx->in, 1);
831 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
832 break;
833
834 case '[':
835 /* array */
836 ly_in_skip(jsonctx->in, 1);
837 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
838 break;
839
840 case 't':
841 if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) {
842 goto unexpected_value;
843 }
844
845 /* true */
846 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
847 ly_in_skip(jsonctx->in, ly_strlen_const("true"));
848 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
849 break;
850
851 case 'f':
852 if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) {
853 goto unexpected_value;
854 }
855
856 /* false */
857 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
858 ly_in_skip(jsonctx->in, ly_strlen_const("false"));
859 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE);
860 break;
861
862 case 'n':
863 if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) {
864 goto unexpected_value;
865 }
866
867 /* null */
868 lyjson_ctx_set_value(jsonctx, "", 0, 0);
869 ly_in_skip(jsonctx->in, ly_strlen_const("null"));
870 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
871 break;
872
873 case ']':
874 if (!array_end) {
875 goto unexpected_value;
876 }
877
878 /* array end */
879 ly_in_skip(jsonctx->in, 1);
880 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
881 break;
882
883 default:
884unexpected_value:
885 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
886 jsonctx->in->current, "a JSON value");
887 return LY_EVALID;
888 }
889
890 if (jsonctx->status.count > LY_MAX_BLOCK_DEPTH * 10) {
891 LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of nestings has been exceeded.", LY_MAX_BLOCK_DEPTH * 10);
892 return LY_EINVAL;
893 }
894
895 return LY_SUCCESS;
896}
897
898/**
899 * @brief Parse next JSON token, object-next-item is expected.
900 *
901 * @param[in] jsonctx JSON parser context.
902 * @return LY_ERR value.
903 */
904static LY_ERR
905lyjson_next_object_item(struct lyjson_ctx *jsonctx)
906{
907 switch (*jsonctx->in->current) {
908 case '\0':
909 /* EOF */
910 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
911 return LY_EVALID;
912
913 case '}':
914 /* object end */
915 ly_in_skip(jsonctx->in, 1);
916 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
917 break;
918
919 case ',':
920 /* next object item */
921 ly_in_skip(jsonctx->in, 1);
922 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT);
923 break;
924
925 default:
926 /* unexpected value */
927 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
928 jsonctx->in->current, "a JSON object-end or next item");
929 return LY_EVALID;
930 }
931
932 return LY_SUCCESS;
933}
934
935/**
936 * @brief Parse next JSON token, array-next-item is expected.
937 *
938 * @param[in] jsonctx JSON parser context.
939 * @return LY_ERR value.
940 */
941static LY_ERR
942lyjson_next_array_item(struct lyjson_ctx *jsonctx)
943{
944 switch (*jsonctx->in->current) {
945 case '\0':
946 /* EOF */
947 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
948 return LY_EVALID;
949
950 case ']':
951 /* array end */
952 ly_in_skip(jsonctx->in, 1);
953 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
954 break;
955
956 case ',':
957 /* next array item */
958 ly_in_skip(jsonctx->in, 1);
959 LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_NEXT);
960 break;
961
962 default:
963 /* unexpected value */
964 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
965 jsonctx->in->current, "a JSON array-end or next item");
966 return LY_EVALID;
967 }
968
969 return LY_SUCCESS;
970}
971
972LY_ERR
973lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
974{
975 LY_ERR ret = LY_SUCCESS;
976 enum LYJSON_PARSER_STATUS cur;
977
978 assert(jsonctx);
979
980 cur = lyjson_ctx_status(jsonctx);
981 switch (cur) {
982 case LYJSON_OBJECT:
983 LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
984 break;
985 case LYJSON_ARRAY:
986 LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 1), cleanup);
987 break;
988 case LYJSON_OBJECT_NEXT:
989 LYJSON_STATUS_POP(jsonctx);
990 LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
991 break;
992 case LYJSON_ARRAY_NEXT:
993 LYJSON_STATUS_POP(jsonctx);
994 LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
995 break;
996 case LYJSON_OBJECT_NAME:
997 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
998 LYJSON_STATUS_POP(jsonctx);
999 LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
1000 break;
1001 case LYJSON_OBJECT_CLOSED:
1002 case LYJSON_ARRAY_CLOSED:
1003 LYJSON_STATUS_POP(jsonctx);
1004 /* fallthrough */
1005 case LYJSON_NUMBER:
1006 case LYJSON_STRING:
1007 case LYJSON_TRUE:
1008 case LYJSON_FALSE:
1009 case LYJSON_NULL:
1010 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
1011 LYJSON_STATUS_POP(jsonctx);
1012 cur = lyjson_ctx_status(jsonctx);
1013
1014 if (cur == LYJSON_OBJECT) {
1015 LY_CHECK_GOTO(ret = lyjson_next_object_item(jsonctx), cleanup);
1016 break;
1017 } else if (cur == LYJSON_ARRAY) {
1018 LY_CHECK_GOTO(ret = lyjson_next_array_item(jsonctx), cleanup);
1019 break;
1020 }
1021
1022 assert(cur == LYJSON_END);
1023 goto cleanup;
1024 case LYJSON_END:
1025 LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
1026 break;
1027 case LYJSON_ERROR:
1028 LOGINT(jsonctx->ctx);
1029 ret = LY_EINT;
1030 goto cleanup;
1031 }
1032
1033 /* skip WS */
1034 lyjson_skip_ws(jsonctx);
1035
1036cleanup:
1037 if (!ret && status) {
1038 *status = lyjson_ctx_status(jsonctx);
1039 }
1040 return ret;
1041}
1042
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001043void
1044lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
1045{
1046 if (jsonctx->backup.dynamic) {
1047 free((char *)jsonctx->backup.value);
1048 }
Michal Vasko09e04632023-03-22 14:34:10 +01001049 jsonctx->backup.status = lyjson_ctx_status(jsonctx);
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001050 jsonctx->backup.status_count = jsonctx->status.count;
1051 jsonctx->backup.value = jsonctx->value;
1052 jsonctx->backup.value_len = jsonctx->value_len;
1053 jsonctx->backup.input = jsonctx->in->current;
1054 jsonctx->backup.dynamic = jsonctx->dynamic;
1055 jsonctx->dynamic = 0;
1056}
1057
1058void
1059lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
1060{
1061 if (jsonctx->dynamic) {
1062 free((char *)jsonctx->value);
1063 }
1064 jsonctx->status.count = jsonctx->backup.status_count;
Michal Vasko22df3f02020-08-24 13:29:22 +02001065 jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001066 jsonctx->value = jsonctx->backup.value;
1067 jsonctx->value_len = jsonctx->backup.value_len;
1068 jsonctx->in->current = jsonctx->backup.input;
1069 jsonctx->dynamic = jsonctx->backup.dynamic;
1070 jsonctx->backup.dynamic = 0;
1071}
1072
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001073void
1074lyjson_ctx_free(struct lyjson_ctx *jsonctx)
1075{
1076 if (!jsonctx) {
1077 return;
1078 }
1079
Radek Krejciddace2c2021-01-08 11:30:56 +01001080 LOG_LOCBACK(0, 0, 0, 1);
Radek Krejci2efc45b2020-12-22 16:25:44 +01001081
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001082 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +02001083 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001084 }
1085 if (jsonctx->backup.dynamic) {
1086 free((char *)jsonctx->backup.value);
1087 }
1088
1089 ly_set_erase(&jsonctx->status, NULL);
1090
1091 free(jsonctx);
1092}