blob: 73b7a5797f8fb8f4128d448f2841788253b8a473 [file] [log] [blame]
Alex41007a62021-04-15 13:46:25 +02001// Copyright (c) 2018-2020 Cesanta Software Limited
2// All rights reserved
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20// SOFTWARE.
21
22#include <float.h>
23#include <math.h>
24
25#include <import/mjson.h>
26
27#if defined(_MSC_VER)
28#define alloca(x) _alloca(x)
29#endif
30
31#if defined(_MSC_VER) && _MSC_VER < 1700
32#define va_copy(x, y) (x) = (y)
33#define isinf(x) !_finite(x)
34#define isnan(x) _isnan(x)
35#endif
36
37static double mystrtod(const char *str, char **end);
38
39static int mjson_esc(int c, int esc) {
40 const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
41 for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
42 if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
43 }
44 return 0;
45}
46
47static int mjson_escape(int c) {
48 return mjson_esc(c, 1);
49}
50
51static int mjson_pass_string(const char *s, int len) {
52 int i;
53 for (i = 0; i < len; i++) {
54 if (s[i] == '\\' && i + 1 < len && mjson_escape(s[i + 1])) {
55 i++;
56 } else if (s[i] == '\0') {
57 return MJSON_ERROR_INVALID_INPUT;
58 } else if (s[i] == '"') {
59 return i;
60 }
61 }
62 return MJSON_ERROR_INVALID_INPUT;
63}
64
65int mjson(const char *s, int len, mjson_cb_t cb, void *ud) {
66 enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
67 unsigned char nesting[MJSON_MAX_DEPTH];
68 int i, depth = 0;
69#define MJSONCALL(ev) \
70 if (cb != NULL && cb(ev, s, start, i - start + 1, ud)) return i + 1;
71
72// In the ascii table, the distance between `[` and `]` is 2.
73// Ditto for `{` and `}`. Hence +2 in the code below.
74#define MJSONEOO() \
75 do { \
76 if (c != nesting[depth - 1] + 2) return MJSON_ERROR_INVALID_INPUT; \
77 depth--; \
78 if (depth == 0) { \
79 MJSONCALL(tok); \
80 return i + 1; \
81 } \
82 } while (0)
83
84 for (i = 0; i < len; i++) {
85 int start = i;
86 unsigned char c = ((unsigned char *) s)[i];
87 int tok = c;
88 if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
89 // printf("- %c [%.*s] %d %d\n", c, i, s, depth, expecting);
90 switch (expecting) {
91 case S_VALUE:
92 if (c == '{') {
93 if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
94 nesting[depth++] = c;
95 expecting = S_KEY;
96 break;
97 } else if (c == '[') {
98 if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
99 nesting[depth++] = c;
100 break;
101 } else if (c == ']' && depth > 0) { // Empty array
102 MJSONEOO();
103 } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
104 i += 3;
105 tok = MJSON_TOK_TRUE;
106 } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
107 i += 3;
108 tok = MJSON_TOK_NULL;
109 } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
110 i += 4;
111 tok = MJSON_TOK_FALSE;
112 } else if (c == '-' || ((c >= '0' && c <= '9'))) {
113 char *end = NULL;
114 mystrtod(&s[i], &end);
115 if (end != NULL) i += (int) (end - &s[i] - 1);
116 tok = MJSON_TOK_NUMBER;
117 } else if (c == '"') {
118 int n = mjson_pass_string(&s[i + 1], len - i - 1);
119 if (n < 0) return n;
120 i += n + 1;
121 tok = MJSON_TOK_STRING;
122 } else {
123 return MJSON_ERROR_INVALID_INPUT;
124 }
125 if (depth == 0) {
126 MJSONCALL(tok);
127 return i + 1;
128 }
129 expecting = S_COMMA_OR_EOO;
130 break;
131
132 case S_KEY:
133 if (c == '"') {
134 int n = mjson_pass_string(&s[i + 1], len - i - 1);
135 if (n < 0) return n;
136 i += n + 1;
137 tok = MJSON_TOK_KEY;
138 expecting = S_COLON;
139 } else if (c == '}') { // Empty object
140 MJSONEOO();
141 expecting = S_COMMA_OR_EOO;
142 } else {
143 return MJSON_ERROR_INVALID_INPUT;
144 }
145 break;
146
147 case S_COLON:
148 if (c == ':') {
149 expecting = S_VALUE;
150 } else {
151 return MJSON_ERROR_INVALID_INPUT;
152 }
153 break;
154
155 case S_COMMA_OR_EOO:
156 if (depth <= 0) return MJSON_ERROR_INVALID_INPUT;
157 if (c == ',') {
158 expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
159 } else if (c == ']' || c == '}') {
160 MJSONEOO();
161 } else {
162 return MJSON_ERROR_INVALID_INPUT;
163 }
164 break;
165 }
166 MJSONCALL(tok);
167 }
168 return MJSON_ERROR_INVALID_INPUT;
169}
170
171struct msjon_get_data {
172 const char *path; // Lookup json path
173 int pos; // Current path index
174 int d1; // Current depth of traversal
175 int d2; // Expected depth of traversal
176 int i1; // Index in an array
177 int i2; // Expected index in an array
178 int obj; // If the value is array/object, offset where it starts
179 const char **tokptr; // Destination
180 int *toklen; // Destination length
181 int tok; // Returned token
182};
183
184#include <stdio.h>
185
186static int plen1(const char *s) {
187 int i = 0, n = 0;
188 while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
189 n++, i += s[i] == '\\' ? 2 : 1;
190 // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
191 return n;
192}
193
194static int plen2(const char *s) {
Christopher Faulet543ed172023-05-10 18:41:54 +0200195 int i = 0, __attribute__((unused)) n = 0;
Alex41007a62021-04-15 13:46:25 +0200196 while (s[i] != '\0' && s[i] != '.' && s[i] != '[')
197 n++, i += s[i] == '\\' ? 2 : 1;
198 // printf("PLEN: s: [%s], [%.*s] => %d\n", s, i, s, n);
199 return i;
200}
201
202static int kcmp(const char *a, const char *b, int n) {
203 int i = 0, j = 0, r = 0;
204 for (i = 0, j = 0; j < n; i++, j++) {
205 if (b[i] == '\\') i++;
206 if ((r = a[j] - b[i]) != 0) return r;
207 }
208 // printf("KCMP: a: [%.*s], b:[%.*s] ==> %d\n", n, a, i, b, r);
209 return r;
210}
211
212static int mjson_get_cb(int tok, const char *s, int off, int len, void *ud) {
213 struct msjon_get_data *data = (struct msjon_get_data *) ud;
214 // printf("--> %2x %2d %2d %2d %2d\t'%s'\t'%.*s'\t\t'%.*s'\n", tok, data->d1,
215 // data->d2, data->i1, data->i2, data->path + data->pos, off, s, len,
216 // s + off);
217 if (data->tok != MJSON_TOK_INVALID) return 1; // Found
218
219 if (tok == '{') {
220 if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
221 data->d1++;
222 } else if (tok == '[') {
223 if (data->d1 == data->d2 && data->path[data->pos] == '[') {
224 data->i1 = 0;
225 data->i2 = (int) mystrtod(&data->path[data->pos + 1], NULL);
226 if (data->i1 == data->i2) {
227 data->d2++;
228 data->pos += 3;
229 }
230 }
231 if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
232 data->d1++;
233 } else if (tok == ',') {
234 if (data->d1 == data->d2 + 1) {
235 data->i1++;
236 if (data->i1 == data->i2) {
237 while (data->path[data->pos] != ']') data->pos++;
238 data->pos++;
239 data->d2++;
240 }
241 }
242 } else if (tok == MJSON_TOK_KEY && data->d1 == data->d2 + 1 &&
243 data->path[data->pos] == '.' && s[off] == '"' &&
244 s[off + len - 1] == '"' &&
245 plen1(&data->path[data->pos + 1]) == len - 2 &&
246 kcmp(s + off + 1, &data->path[data->pos + 1], len - 2) == 0) {
247 data->d2++;
248 data->pos += plen2(&data->path[data->pos + 1]) + 1;
249 } else if (tok == MJSON_TOK_KEY && data->d1 == data->d2) {
250 return 1; // Exhausted path, not found
251 } else if (tok == '}' || tok == ']') {
252 data->d1--;
253 // data->d2--;
254 if (!data->path[data->pos] && data->d1 == data->d2 && data->obj != -1) {
255 data->tok = tok - 2;
256 if (data->tokptr) *data->tokptr = s + data->obj;
257 if (data->toklen) *data->toklen = off - data->obj + 1;
258 return 1;
259 }
260 } else if (MJSON_TOK_IS_VALUE(tok)) {
261 // printf("TOK --> %d\n", tok);
262 if (data->d1 == data->d2 && !data->path[data->pos]) {
263 data->tok = tok;
264 if (data->tokptr) *data->tokptr = s + off;
265 if (data->toklen) *data->toklen = len;
266 return 1;
267 }
268 }
269 return 0;
270}
271
272enum mjson_tok mjson_find(const char *s, int len, const char *jp,
273 const char **tokptr, int *toklen) {
274 struct msjon_get_data data = {jp, 1, 0, 0, 0,
275 0, -1, tokptr, toklen, MJSON_TOK_INVALID};
276 if (jp[0] != '$') return MJSON_TOK_INVALID;
277 if (mjson(s, len, mjson_get_cb, &data) < 0) return MJSON_TOK_INVALID;
278 return (enum mjson_tok) data.tok;
279}
280
281int mjson_get_number(const char *s, int len, const char *path, double *v) {
282 const char *p;
283 int tok, n;
284 if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
285 if (v != NULL) *v = mystrtod(p, NULL);
286 }
287 return tok == MJSON_TOK_NUMBER ? 1 : 0;
288}
289
290int mjson_get_bool(const char *s, int len, const char *path, int *v) {
291 int tok = mjson_find(s, len, path, NULL, NULL);
292 if (tok == MJSON_TOK_TRUE && v != NULL) *v = 1;
293 if (tok == MJSON_TOK_FALSE && v != NULL) *v = 0;
294 return tok == MJSON_TOK_TRUE || tok == MJSON_TOK_FALSE ? 1 : 0;
295}
296
297static unsigned char mjson_unhex_nimble(const char *s) {
298 unsigned char i, v = 0;
299 for (i = 0; i < 2; i++) {
300 int c = s[i];
301 if (i > 0) v <<= 4;
302 v |= (c >= '0' && c <= '9') ? c - '0'
303 : (c >= 'A' && c <= 'F') ? c - '7' : c - 'W';
304 }
305 return v;
306}
307
308static int mjson_unescape(const char *s, int len, char *to, int n) {
309 int i, j;
310 for (i = 0, j = 0; i < len && j < n; i++, j++) {
311 if (s[i] == '\\' && i + 5 < len && s[i + 1] == 'u') {
312 // \uXXXX escape. We could process a simple one-byte chars
313 // \u00xx from the ASCII range. More complex chars would require
314 // dragging in a UTF8 library, which is too much for us
315 if (s[i + 2] != '0' || s[i + 3] != '0') return -1; // Too much, give up
316 to[j] = mjson_unhex_nimble(s + i + 4);
317 i += 5;
318 } else if (s[i] == '\\' && i + 1 < len) {
319 int c = mjson_esc(s[i + 1], 0);
320 if (c == 0) return -1;
321 to[j] = c;
322 i++;
323 } else {
324 to[j] = s[i];
325 }
326 }
327 if (j >= n) return -1;
328 if (n > 0) to[j] = '\0';
329 return j;
330}
331
332int mjson_get_string(const char *s, int len, const char *path, char *to,
333 int n) {
334 const char *p;
335 int sz;
336 if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return -1;
337 return mjson_unescape(p + 1, sz - 2, to, n);
338}
339
340int mjson_get_hex(const char *s, int len, const char *x, char *to, int n) {
341 const char *p;
342 int i, j, sz;
343 if (mjson_find(s, len, x, &p, &sz) != MJSON_TOK_STRING) return -1;
344 for (i = j = 0; i < sz - 3 && j < n; i += 2, j++) {
345 ((unsigned char *) to)[j] = mjson_unhex_nimble(p + i + 1);
346 }
347 if (j < n) to[j] = '\0';
348 return j;
349}
350
351#if MJSON_ENABLE_BASE64
352static int mjson_base64rev(int c) {
353 if (c >= 'A' && c <= 'Z') {
354 return c - 'A';
355 } else if (c >= 'a' && c <= 'z') {
356 return c + 26 - 'a';
357 } else if (c >= '0' && c <= '9') {
358 return c + 52 - '0';
359 } else if (c == '+') {
360 return 62;
361 } else if (c == '/') {
362 return 63;
363 } else {
364 return 64;
365 }
366}
367
368int mjson_base64_dec(const char *src, int n, char *dst, int dlen) {
369 const char *end = src + n;
370 int len = 0;
371 while (src + 3 < end && len < dlen) {
372 int a = mjson_base64rev(src[0]), b = mjson_base64rev(src[1]),
373 c = mjson_base64rev(src[2]), d = mjson_base64rev(src[3]);
374 dst[len++] = (a << 2) | (b >> 4);
375 if (src[2] != '=' && len < dlen) {
376 dst[len++] = (b << 4) | (c >> 2);
377 if (src[3] != '=' && len < dlen) {
378 dst[len++] = (c << 6) | d;
379 }
380 }
381 src += 4;
382 }
383 if (len < dlen) dst[len] = '\0';
384 return len;
385}
386
387int mjson_get_base64(const char *s, int len, const char *path, char *to,
388 int n) {
389 const char *p;
390 int sz;
391 if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return 0;
392 return mjson_base64_dec(p + 1, sz - 2, to, n);
393}
394#endif // MJSON_ENABLE_BASE64
395
396#if MJSON_ENABLE_NEXT
397struct nextdata {
398 int off, len, depth, t, vo, arrayindex;
399 int *koff, *klen, *voff, *vlen, *vtype;
400};
401
402static int next_cb(int tok, const char *s, int off, int len, void *ud) {
403 struct nextdata *d = (struct nextdata *) ud;
404 // int i;
405 switch (tok) {
406 case '{':
407 case '[':
408 if (d->depth == 0 && tok == '[') d->arrayindex = 0;
409 if (d->depth == 1 && off > d->off) {
410 d->vo = off;
411 d->t = tok == '{' ? MJSON_TOK_OBJECT : MJSON_TOK_ARRAY;
412 if (d->voff) *d->voff = off;
413 if (d->vtype) *d->vtype = d->t;
414 }
415 d->depth++;
416 break;
417 case '}':
418 case ']':
419 d->depth--;
420 if (d->depth == 1 && d->vo) {
421 d->len = off + len;
422 if (d->vlen) *d->vlen = d->len - d->vo;
423 if (d->arrayindex >= 0) {
424 if (d->koff) *d->koff = d->arrayindex; // koff holds array index
425 if (d->klen) *d->klen = 0; // klen holds 0
426 }
427 return 1;
428 }
429 if (d->depth == 1 && d->arrayindex >= 0) d->arrayindex++;
430 break;
431 case ',':
432 case ':':
433 break;
434 case MJSON_TOK_KEY:
435 if (d->depth == 1 && d->off < off) {
436 if (d->koff) *d->koff = off; // And report back to the user
437 if (d->klen) *d->klen = len; // If we have to
438 }
439 break;
440 default:
441 if (d->depth != 1) break;
442 // If we're iterating over the array
443 if (off > d->off) {
444 d->len = off + len;
445 if (d->vlen) *d->vlen = len; // value length
446 if (d->voff) *d->voff = off; // value offset
447 if (d->vtype) *d->vtype = tok; // value type
448 if (d->arrayindex >= 0) {
449 if (d->koff) *d->koff = d->arrayindex; // koff holds array index
450 if (d->klen) *d->klen = 0; // klen holds 0
451 }
452 return 1;
453 }
454 if (d->arrayindex >= 0) d->arrayindex++;
455 break;
456 }
457 (void) s;
458 return 0;
459}
460
461int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
462 int *vlen, int *vtype) {
463 struct nextdata d = {off, 0, 0, 0, 0, -1, koff, klen, voff, vlen, vtype};
464 mjson(s, n, next_cb, &d);
465 return d.len;
466}
467#endif
468
469#if MJSON_ENABLE_PRINT
470int mjson_print_fixed_buf(const char *ptr, int len, void *fndata) {
471 struct mjson_fixedbuf *fb = (struct mjson_fixedbuf *) fndata;
472 int i, left = fb->size - 1 - fb->len;
473 if (left < len) len = left;
474 for (i = 0; i < len; i++) fb->ptr[fb->len + i] = ptr[i];
475 fb->len += len;
476 fb->ptr[fb->len] = '\0';
477 return len;
478}
479
480// This function allocates memory in chunks of size MJSON_DYNBUF_CHUNK
481// to decrease memory fragmentation, when many calls are executed to
482// print e.g. a base64 string or a hex string.
483int mjson_print_dynamic_buf(const char *ptr, int len, void *fndata) {
484 char *s, *buf = *(char **) fndata;
485 size_t curlen = buf == NULL ? 0 : strlen(buf);
486 size_t new_size = curlen + len + 1 + MJSON_DYNBUF_CHUNK;
487 new_size -= new_size % MJSON_DYNBUF_CHUNK;
488
489 if ((s = (char *) realloc(buf, new_size)) == NULL) {
490 return 0;
491 } else {
492 memcpy(s + curlen, ptr, len);
493 s[curlen + len] = '\0';
494 *(char **) fndata = s;
495 return len;
496 }
497}
498
499int mjson_print_null(const char *ptr, int len, void *userdata) {
500 (void) ptr;
501 (void) userdata;
502 return len;
503}
504
505int mjson_print_buf(mjson_print_fn_t fn, void *fnd, const char *buf, int len) {
506 return fn(buf, len, fnd);
507}
508
509int mjson_print_long(mjson_print_fn_t fn, void *fnd, long val, int is_signed) {
510 unsigned long v = val, s = 0, n, i;
511 char buf[20], t;
512 if (is_signed && val < 0) {
513 buf[s++] = '-', v = -val;
514 }
515 // This loop prints a number in reverse order. I guess this is because we
516 // write numbers from right to left: least significant digit comes last.
517 // Maybe because we use Arabic numbers, and Arabs write RTL?
518 for (n = 0; v > 0; v /= 10) buf[s + n++] = "0123456789"[v % 10];
519 // Reverse a string
520 for (i = 0; i < n / 2; i++)
521 t = buf[s + i], buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
522 if (val == 0) buf[n++] = '0'; // Handle special case
523 return fn(buf, s + n, fnd);
524}
525
526int mjson_print_int(mjson_print_fn_t fn, void *fnd, int v, int s) {
527 return mjson_print_long(fn, fnd, s ? (long) v : (unsigned) v, s);
528}
529
530static int addexp(char *buf, int e, int sign) {
531 int n = 0;
532 buf[n++] = 'e';
533 buf[n++] = sign;
534 if (e > 400) return 0;
535 if (e < 10) buf[n++] = '0';
536 if (e >= 100) buf[n++] = (e / 100) + '0', e -= 100 * (e / 100);
537 if (e >= 10) buf[n++] = (e / 10) + '0', e -= 10 * (e / 10);
538 buf[n++] = e + '0';
539 return n;
540}
541
542int mjson_print_dbl(mjson_print_fn_t fn, void *fnd, double d, int width) {
543 char buf[40];
544 int i, s = 0, n = 0, e = 0;
545 double t, mul, saved;
546 if (d == 0.0) return fn("0", 1, fnd);
547 if (isinf(d)) return fn(d > 0 ? "inf" : "-inf", d > 0 ? 3 : 4, fnd);
548 if (isnan(d)) return fn("nan", 3, fnd);
549 if (d < 0.0) d = -d, buf[s++] = '-';
550
551 // Round
552 saved = d;
553 mul = 1.0;
554 while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
555 while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0;
556 for (i = 0, t = mul * 5; i < width; i++) t /= 10.0;
557 d += t;
558 // Calculate exponent, and 'mul' for scientific representation
559 mul = 1.0;
560 while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++;
561 while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--;
562 // printf(" --> %g %d %g %g\n", saved, e, t, mul);
563
564 if (e >= width) {
565 struct mjson_fixedbuf fb = {buf + s, (int) sizeof(buf) - s, 0};
566 n = mjson_print_dbl(mjson_print_fixed_buf, &fb, saved / mul, width);
567 // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, fb.len, fb.ptr);
568 n += addexp(buf + s + n, e, '+');
569 return fn(buf, s + n, fnd);
570 } else if (e <= -width) {
571 struct mjson_fixedbuf fb = {buf + s, (int) sizeof(buf) - s, 0};
572 n = mjson_print_dbl(mjson_print_fixed_buf, &fb, saved / mul, width);
573 // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, fb.len, fb.ptr);
574 n += addexp(buf + s + n, -e, '-');
575 return fn(buf, s + n, fnd);
576 } else {
577 for (i = 0, t = mul; d >= 1.0 && s + n < (int) sizeof(buf); i++) {
578 int ch = (int) (d / t);
579 if (n > 0 || ch > 0) buf[s + n++] = ch + '0';
580 d -= ch * t;
581 t /= 10.0;
582 }
583 // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
584 if (n == 0) buf[s++] = '0';
585 while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0;
586 if (s + n < (int) sizeof(buf)) buf[n + s++] = '.';
587 // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
588 for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) {
589 int ch = (int) (d / t);
590 buf[s + n++] = ch + '0';
591 d -= ch * t;
592 t /= 10.0;
593 }
594 }
595 while (n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeros
596 if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
597 return fn(buf, s + n, fnd);
598}
599
600int mjson_print_str(mjson_print_fn_t fn, void *fnd, const char *s, int len) {
601 int i, n = fn("\"", 1, fnd);
602 for (i = 0; i < len; i++) {
603 char c = mjson_escape(s[i]);
604 if (c) {
605 n += fn("\\", 1, fnd);
606 n += fn(&c, 1, fnd);
607 } else {
608 n += fn(&s[i], 1, fnd);
609 }
610 }
611 return n + fn("\"", 1, fnd);
612}
613
614#if MJSON_ENABLE_BASE64
615int mjson_print_b64(mjson_print_fn_t fn, void *fnd, const unsigned char *s,
616 int n) {
617 const char *t =
618 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
619 int i, len = fn("\"", 1, fnd);
620 for (i = 0; i < n; i += 3) {
621 int a = s[i], b = i + 1 < n ? s[i + 1] : 0, c = i + 2 < n ? s[i + 2] : 0;
622 char buf[4] = {t[a >> 2], t[(a & 3) << 4 | (b >> 4)], '=', '='};
623 if (i + 1 < n) buf[2] = t[(b & 15) << 2 | (c >> 6)];
624 if (i + 2 < n) buf[3] = t[c & 63];
625 len += fn(buf, sizeof(buf), fnd);
626 }
627 return len + fn("\"", 1, fnd);
628}
629#endif /* MJSON_ENABLE_BASE64 */
630
631int mjson_vprintf(mjson_print_fn_t fn, void *fnd, const char *fmt,
632 va_list xap) {
633 int i = 0, n = 0;
634 va_list ap;
635 va_copy(ap, xap);
636 while (fmt[i] != '\0') {
637 if (fmt[i] == '%') {
638 char fc = fmt[++i];
639 int is_long = 0;
640 if (fc == 'l') {
641 is_long = 1;
642 fc = fmt[i + 1];
643 }
644 if (fc == 'Q') {
645 char *buf = va_arg(ap, char *);
646 n += mjson_print_str(fn, fnd, buf ? buf : "",
647 buf ? (int) strlen(buf) : 0);
648 } else if (strncmp(&fmt[i], ".*Q", 3) == 0) {
649 int len = va_arg(ap, int);
650 char *buf = va_arg(ap, char *);
651 n += mjson_print_str(fn, fnd, buf, len);
652 i += 2;
653 } else if (fc == 'd' || fc == 'u') {
654 int is_signed = (fc == 'd');
655 if (is_long) {
656 long val = va_arg(ap, long);
657 n += mjson_print_long(fn, fnd, val, is_signed);
658 i++;
659 } else {
660 int val = va_arg(ap, int);
661 n += mjson_print_int(fn, fnd, val, is_signed);
662 }
663 } else if (fc == 'B') {
664 const char *s = va_arg(ap, int) ? "true" : "false";
665 n += mjson_print_buf(fn, fnd, s, (int) strlen(s));
666 } else if (fc == 's') {
667 char *buf = va_arg(ap, char *);
668 n += mjson_print_buf(fn, fnd, buf, (int) strlen(buf));
669 } else if (strncmp(&fmt[i], ".*s", 3) == 0) {
670 int len = va_arg(ap, int);
671 char *buf = va_arg(ap, char *);
672 n += mjson_print_buf(fn, fnd, buf, len);
673 i += 2;
674 } else if (fc == 'g') {
675 n += mjson_print_dbl(fn, fnd, va_arg(ap, double), 6);
676 } else if (strncmp(&fmt[i], ".*g", 3) == 0) {
677 int width = va_arg(ap, int);
678 n += mjson_print_dbl(fn, fnd, va_arg(ap, double), width);
679 i += 2;
680#if MJSON_ENABLE_BASE64
681 } else if (fc == 'V') {
682 int len = va_arg(ap, int);
683 const char *buf = va_arg(ap, const char *);
684 n += mjson_print_b64(fn, fnd, (unsigned char *) buf, len);
685#endif
686 } else if (fc == 'H') {
687 const char *hex = "0123456789abcdef";
688 int i, len = va_arg(ap, int);
689 const unsigned char *p = va_arg(ap, const unsigned char *);
690 n += fn("\"", 1, fnd);
691 for (i = 0; i < len; i++) {
692 n += fn(&hex[(p[i] >> 4) & 15], 1, fnd);
693 n += fn(&hex[p[i] & 15], 1, fnd);
694 }
695 n += fn("\"", 1, fnd);
696 } else if (fc == 'M') {
697 mjson_vprint_fn_t vfn = va_arg(ap, mjson_vprint_fn_t);
698 n += vfn(fn, fnd, &ap);
699 }
700 i++;
701 } else {
702 n += mjson_print_buf(fn, fnd, &fmt[i++], 1);
703 }
704 }
705 va_end(xap);
706 va_end(ap);
707 return n;
708}
709
710int mjson_printf(mjson_print_fn_t fn, void *fnd, const char *fmt, ...) {
711 va_list ap;
712 int len;
713 va_start(ap, fmt);
714 len = mjson_vprintf(fn, fnd, fmt, ap);
715 va_end(ap);
716 return len;
717}
718#endif /* MJSON_ENABLE_PRINT */
719
720static int is_digit(int c) {
721 return c >= '0' && c <= '9';
722}
723
724/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
725static double mystrtod(const char *str, char **end) {
726 double d = 0.0;
Christopher Faulet543ed172023-05-10 18:41:54 +0200727 int sign = 1, __attribute__((unused)) n = 0;
Alex41007a62021-04-15 13:46:25 +0200728 const char *p = str, *a = str;
729
730 /* decimal part */
731 if (*p == '-') {
732 sign = -1;
733 ++p;
734 } else if (*p == '+') {
735 ++p;
736 }
737 if (is_digit(*p)) {
738 d = (double) (*p++ - '0');
739 while (*p && is_digit(*p)) {
740 d = d * 10.0 + (double) (*p - '0');
741 ++p;
742 ++n;
743 }
744 a = p;
745 } else if (*p != '.') {
746 goto done;
747 }
748 d *= sign;
749
750 /* fraction part */
751 if (*p == '.') {
752 double f = 0.0;
753 double base = 0.1;
754 ++p;
755
756 if (is_digit(*p)) {
757 while (*p && is_digit(*p)) {
758 f += base * (*p - '0');
759 base /= 10.0;
760 ++p;
761 ++n;
762 }
763 }
764 d += f * sign;
765 a = p;
766 }
767
768 /* exponential part */
769 if ((*p == 'E') || (*p == 'e')) {
770 int i, e = 0, neg = 0;
771 p++;
772 if (*p == '-') p++, neg++;
773 if (*p == '+') p++;
774 while (is_digit(*p)) e = e * 10 + *p++ - '0';
775 if (neg) e = -e;
776#if 0
777 if (d == 2.2250738585072011 && e == -308) {
778 d = 0.0;
779 a = p;
780 goto done;
781 }
782 if (d == 2.2250738585072012 && e <= -308) {
783 d *= 1.0e-308;
784 a = p;
785 goto done;
786 }
787#endif
788 for (i = 0; i < e; i++) d *= 10;
789 for (i = 0; i < -e; i++) d /= 10;
790 a = p;
791 } else if (p > str && !is_digit(*(p - 1))) {
792 a = str;
793 goto done;
794 }
795
796done:
797 if (end) *end = (char *) a;
798 return d;
799}
800
801#if MJSON_ENABLE_MERGE
802int mjson_merge(const char *s, int n, const char *s2, int n2,
803 mjson_print_fn_t fn, void *userdata) {
804 int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
805 if (n < 2) return len;
806 len += fn("{", 1, userdata);
807 while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
808 char *path = (char *) alloca(klen + 1);
809 const char *val;
810 memcpy(path, "$.", 2);
811 memcpy(path + 2, s + koff + 1, klen - 2);
812 path[klen] = '\0';
813 if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
814 if (t2 == MJSON_TOK_NULL) continue; // null deletes the key
815 } else {
816 val = s + voff; // Key is not found in the update. Copy the old value.
817 }
818 if (comma) len += fn(",", 1, userdata);
819 len += fn(s + koff, klen, userdata);
820 len += fn(":", 1, userdata);
821 if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
822 len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
823 } else {
824 if (t2 != MJSON_TOK_INVALID) vlen = k;
825 len += fn(val, vlen, userdata);
826 }
827 comma = 1;
828 }
829 // Add missing keys
830 off = 0;
831 while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
832 char *path = (char *) alloca(klen + 1);
833 const char *val;
834 if (t == MJSON_TOK_NULL) continue;
835 memcpy(path, "$.", 2);
836 memcpy(path + 2, s2 + koff + 1, klen - 2);
837 path[klen] = '\0';
838 if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID) continue;
839 if (comma) len += fn(",", 1, userdata);
840 len += fn(s2 + koff, klen, userdata);
841 len += fn(":", 1, userdata);
842 len += fn(s2 + voff, vlen, userdata);
843 comma = 1;
844 }
845 len += fn("}", 1, userdata);
846 return len;
847}
848#endif // MJSON_ENABLE_MERGE
849
850#if MJSON_ENABLE_PRETTY
851struct prettydata {
852 int level;
853 int len;
854 int prev;
855 const char *pad;
856 int padlen;
857 mjson_print_fn_t fn;
858 void *userdata;
859};
860
861static int pretty_cb(int ev, const char *s, int off, int len, void *ud) {
862 struct prettydata *d = (struct prettydata *) ud;
863 int i;
864 switch (ev) {
865 case '{':
866 case '[':
867 d->level++;
868 d->len += d->fn(s + off, len, d->userdata);
869 break;
870 case '}':
871 case ']':
872 d->level--;
873 if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
874 d->len += d->fn("\n", 1, d->userdata);
875 for (i = 0; i < d->level; i++)
876 d->len += d->fn(d->pad, d->padlen, d->userdata);
877 }
878 d->len += d->fn(s + off, len, d->userdata);
879 break;
880 case ',':
881 d->len += d->fn(s + off, len, d->userdata);
882 if (d->padlen > 0) {
883 d->len += d->fn("\n", 1, d->userdata);
884 for (i = 0; i < d->level; i++)
885 d->len += d->fn(d->pad, d->padlen, d->userdata);
886 }
887 break;
888 case ':':
889 d->len += d->fn(s + off, len, d->userdata);
890 if (d->padlen > 0) d->len += d->fn(" ", 1, d->userdata);
891 break;
892 case MJSON_TOK_KEY:
893 if (d->prev == '{' && d->padlen > 0) {
894 d->len += d->fn("\n", 1, d->userdata);
895 for (i = 0; i < d->level; i++)
896 d->len += d->fn(d->pad, d->padlen, d->userdata);
897 }
898 d->len += d->fn(s + off, len, d->userdata);
899 break;
900 default:
901 if (d->prev == '[' && d->padlen > 0) {
902 d->len += d->fn("\n", 1, d->userdata);
903 for (i = 0; i < d->level; i++)
904 d->len += d->fn(d->pad, d->padlen, d->userdata);
905 }
906 d->len += d->fn(s + off, len, d->userdata);
907 break;
908 }
909 d->prev = ev;
910 return 0;
911}
912
913int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
914 void *userdata) {
915 struct prettydata d = {0, 0, 0, pad, (int) strlen(pad), fn, userdata};
916 if (mjson(s, n, pretty_cb, &d) < 0) return -1;
917 return d.len;
918}
919#endif // MJSON_ENABLE_PRETTY
920
921#if MJSON_ENABLE_RPC
922struct jsonrpc_ctx jsonrpc_default_context;
923
924int mjson_globmatch(const char *s1, int n1, const char *s2, int n2) {
925 int i = 0, j = 0, ni = 0, nj = 0;
926 while (i < n1 || j < n2) {
927 if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
928 i++, j++;
929 } else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
930 ni = i, nj = j + 1, i++;
931 } else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
932 i = ni, j = nj;
933 } else {
934 return 0;
935 }
936 }
937 return 1;
938}
939
940void jsonrpc_return_errorv(struct jsonrpc_request *r, int code,
941 const char *message, const char *data_fmt,
942 va_list ap) {
943 if (r->id_len == 0) return;
944 mjson_printf(r->fn, r->fndata,
945 "{\"id\":%.*s,\"error\":{\"code\":%d,\"message\":%Q", r->id_len,
946 r->id, code, message == NULL ? "" : message);
947 if (data_fmt != NULL) {
948 mjson_printf(r->fn, r->fndata, ",\"data\":");
949 mjson_vprintf(r->fn, r->fndata, data_fmt, ap);
950 }
951 mjson_printf(r->fn, r->fndata, "}}\n");
952}
953
954void jsonrpc_return_error(struct jsonrpc_request *r, int code,
955 const char *message, const char *data_fmt, ...) {
956 va_list ap;
957 va_start(ap, data_fmt);
958 jsonrpc_return_errorv(r, code, message, data_fmt, ap);
959 va_end(ap);
960}
961
962void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt,
963 va_list ap) {
964 if (r->id_len == 0) return;
965 mjson_printf(r->fn, r->fndata, "{\"id\":%.*s,\"result\":", r->id_len, r->id);
966 if (result_fmt != NULL) {
967 mjson_vprintf(r->fn, r->fndata, result_fmt, ap);
968 } else {
969 mjson_printf(r->fn, r->fndata, "%s", "null");
970 }
971 mjson_printf(r->fn, r->fndata, "}\n");
972}
973
974void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
975 ...) {
976 va_list ap;
977 va_start(ap, result_fmt);
978 jsonrpc_return_successv(r, result_fmt, ap);
979 va_end(ap);
980}
981
982void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *buf, int len,
983 mjson_print_fn_t fn, void *fndata, void *ud) {
984 const char *result = NULL, *error = NULL;
985 int result_sz = 0, error_sz = 0;
986 struct jsonrpc_method *m = NULL;
987 struct jsonrpc_request r = {ctx, buf, len, 0, 0, 0, 0, 0, 0, fn, fndata, ud};
988
989 // Is is a response frame?
990 mjson_find(buf, len, "$.result", &result, &result_sz);
991 if (result == NULL) mjson_find(buf, len, "$.error", &error, &error_sz);
992 if (result_sz > 0 || error_sz > 0) {
993 if (ctx->response_cb) ctx->response_cb(buf, len, ctx->response_cb_data);
994 return;
995 }
996
997 // Method must exist and must be a string
998 if (mjson_find(buf, len, "$.method", &r.method, &r.method_len) !=
999 MJSON_TOK_STRING) {
1000 mjson_printf(fn, fndata, "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n",
1001 len, buf);
1002 return;
1003 }
1004
1005 // id and params are optional
1006 mjson_find(buf, len, "$.id", &r.id, &r.id_len);
1007 mjson_find(buf, len, "$.params", &r.params, &r.params_len);
1008
1009 for (m = ctx->methods; m != NULL; m = m->next) {
1010 if (mjson_globmatch(m->method, m->method_sz, r.method + 1,
1011 r.method_len - 2) > 0) {
1012 if (r.params == NULL) r.params = "";
1013 m->cb(&r);
1014 break;
1015 }
1016 }
1017 if (m == NULL) {
1018 jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
1019 }
1020}
1021
1022static int jsonrpc_print_methods(mjson_print_fn_t fn, void *fndata,
1023 va_list *ap) {
1024 struct jsonrpc_ctx *ctx = va_arg(*ap, struct jsonrpc_ctx *);
1025 struct jsonrpc_method *m;
1026 int len = 0;
1027 for (m = ctx->methods; m != NULL; m = m->next) {
1028 if (m != ctx->methods) len += mjson_print_buf(fn, fndata, ",", 1);
1029 len += mjson_print_str(fn, fndata, m->method, (int) strlen(m->method));
1030 }
1031 return len;
1032}
1033
1034static void rpclist(struct jsonrpc_request *r) {
1035 jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->ctx);
1036}
1037
1038void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
1039 void *response_cb_data) {
1040 ctx->response_cb = response_cb;
1041 ctx->response_cb_data = response_cb_data;
1042 jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, rpclist);
1043}
1044
1045void jsonrpc_init(mjson_print_fn_t response_cb, void *userdata) {
1046 jsonrpc_ctx_init(&jsonrpc_default_context, response_cb, userdata);
1047}
1048#endif // MJSON_ENABLE_RPC