Concord - C Discord API library
A Discord API wrapper library written in C
jsmn-find.h
Go to the documentation of this file.
1#ifndef JSMN_FIND_H
2#define JSMN_FIND_H
3
4#ifdef __cplusplus
5extern "C" {
6#endif
7
8#ifndef JSMN_H
9#error "jsmn-find.h should be included after jsmn.h"
10#endif
11
13struct jsmnftok {
15 int pos;
17 size_t len;
18};
19
21typedef struct jsmnf_pair {
25 int size;
31 struct jsmnftok k;
33 struct jsmnftok v;
35 int state;
37
40typedef struct jsmnf_loader {
42 unsigned pairnext;
44
50JSMN_API void jsmnf_init(jsmnf_loader *loader);
51
66 const char *js,
67 const jsmntok_t tokens[],
68 unsigned num_tokens,
69 jsmnf_pair pairs[],
70 unsigned num_pairs);
71
83 const char *js,
84 const char key[],
85 int length);
86
98 const char *js,
99 char *const path[],
100 unsigned depth);
101
120 const char *js,
121 const jsmntok_t tokens[],
122 unsigned num_tokens,
123 jsmnf_pair **p_pairs,
124 unsigned *num_pairs);
125
139 const char *js,
140 size_t length,
141 jsmntok_t **p_tokens,
142 unsigned *num_tokens);
143
154JSMN_API long jsmnf_unescape(char buf[],
155 size_t bufsize,
156 const char src[],
157 size_t length);
158
159#ifndef JSMN_HEADER
160
161#include <stdio.h>
162#include <stdlib.h>
163#include <string.h>
164
165/* key */
166#define CHASH_KEY_FIELD k
167/* value */
168#define CHASH_VALUE_FIELD v
169/* fields */
170#define CHASH_BUCKETS_FIELD fields
171/* members count */
172#define CHASH_LENGTH_FIELD size
173
174#include "chash.h"
175
176#define _jsmnf_key_hash(key, hash) \
177 5031; \
178 do { \
179 unsigned __CHASH_HINDEX; \
180 for (__CHASH_HINDEX = 0; __CHASH_HINDEX < (key).len; \
181 ++__CHASH_HINDEX) { \
182 (hash) = (((hash) << 1) + (hash)) \
183 + _JSMNF_STRING_B[(key).pos + __CHASH_HINDEX]; \
184 } \
185 } while (0)
186
187/* compare jsmnf keys */
188#define _jsmnf_key_compare(cmp_a, cmp_b) \
189 ((cmp_a).len == (cmp_b).len \
190 && !strncmp(_JSMNF_STRING_B + (cmp_a).pos, \
191 _JSMNF_STRING_A + (cmp_b).pos, (cmp_a).len))
192
193#define _JSMNF_TABLE_HEAP 0
194#define _JSMNF_TABLE_BUCKET struct jsmnf_pair
195#define _JSMNF_TABLE_FREE_KEY(_key)
196#define _JSMNF_TABLE_HASH(_key, _hash) _jsmnf_key_hash(_key, _hash)
197#define _JSMNF_TABLE_FREE_VALUE(_value)
198#define _JSMNF_TABLE_COMPARE(_cmp_a, _cmp_b) _jsmnf_key_compare(_cmp_a, _cmp_b)
199#define _JSMNF_TABLE_INIT(_bucket, _key, _value) \
200 chash_default_init(_bucket, _key, _value)
201
202JSMN_API void
204{
205 loader->pairnext = 0;
206}
207
208#define _JSMNF_STRING_A js
209#define _JSMNF_STRING_B js
210
211static int
212_jsmnf_load_pairs(struct jsmnf_loader *loader,
213 const char *js,
214 struct jsmnf_pair *curr,
215 const struct jsmntok *tok,
216 unsigned num_tokens,
217 struct jsmnf_pair *pairs,
218 unsigned num_pairs)
219{
220 int offset = 0;
221
222 if (!num_tokens) return 0;
223
224 switch (tok->type) {
225 case JSMN_STRING:
226 case JSMN_PRIMITIVE:
227 break;
228 case JSMN_OBJECT:
229 case JSMN_ARRAY: {
230 const unsigned top_idx = loader->pairnext + (1 + tok->size),
231 bottom_idx = loader->pairnext;
232 int ret;
233
234 if (tok->size > (int)(num_pairs - bottom_idx)
235 || top_idx > (num_pairs - bottom_idx))
236 {
237 return JSMN_ERROR_NOMEM;
238 }
239
240 loader->pairnext = top_idx;
241
242 (void)chash_init_stack(curr, &pairs[bottom_idx], top_idx - bottom_idx,
243 _JSMNF_TABLE);
244
245 if (JSMN_OBJECT == tok->type) {
246 while (curr->size < tok->size) {
247 const struct jsmntok *_key = tok + 1 + offset;
248 struct jsmnftok key, value = { 0 };
249
250 key.pos = _key->start;
251 key.len = _key->end - _key->start;
252
253 /* skip Key token */
254 offset += 1;
255
256 /* _key->size > 0 means either an Object or Array */
257 if (_key->size > 0) {
258 const struct jsmntok *_value = tok + 1 + offset;
259 struct jsmnf_pair *found = NULL;
260
261 value.pos = _value->start;
262 value.len = _value->end - _value->start;
263
264 chash_assign(curr, key, value, _JSMNF_TABLE);
265 (void)chash_lookup_bucket(curr, key, found, _JSMNF_TABLE);
266
267 ret = _jsmnf_load_pairs(loader, js, found, _value,
268 num_tokens - offset, pairs,
269 num_pairs);
270 if (ret < 0) return ret;
271
272 offset += ret;
273 }
274 else {
275 chash_assign(curr, key, value, _JSMNF_TABLE);
276 }
277 }
278 }
279 else if (JSMN_ARRAY == tok->type) {
280 for (; curr->size < tok->size; ++curr->size) {
281 const struct jsmntok *_value = tok + 1 + offset;
282 struct jsmnf_pair *element = curr->fields + curr->size;
283 struct jsmnftok value;
284
285 value.pos = _value->start;
286 value.len = _value->end - _value->start;
287
288 /* assign array element */
289 element->v = value;
290 element->state = CHASH_FILLED;
291 /* unused for array elements */
292 element->k.pos = 0;
293 element->k.len = 0;
294
295 ret = _jsmnf_load_pairs(loader, js, element, _value,
296 num_tokens - offset, pairs, num_pairs);
297 if (ret < 0) return ret;
298
299 offset += ret;
300 }
301 }
302 break;
303 }
304 default:
305 case JSMN_UNDEFINED:
306 fputs("Error: JSMN_UNDEFINED token detected, jsmn_parse() failure\n",
307 stderr);
308 return JSMN_ERROR_INVAL;
309 }
310
311 curr->type = tok->type;
312
313 return offset + 1;
314}
315
316#undef _JSMNF_STRING_A
317#undef _JSMNF_STRING_B
318
319JSMN_API int
321 const char *js,
322 const struct jsmntok tokens[],
323 unsigned num_tokens,
324 struct jsmnf_pair pairs[],
325 unsigned num_pairs)
326{
327 int ret;
328
329 if (!loader->pairnext) { /* first run, initialize pairs */
330 static const struct jsmnf_pair blank_pair = { 0 };
331 unsigned i = 0;
332
333 for (; i < num_pairs; ++i)
334 pairs[i] = blank_pair;
335 /* root */
336 pairs[0].v.pos = tokens->start;
337 pairs[0].v.len = tokens->end - tokens->start;
338
339 ++loader->pairnext;
340 }
341
342 ret = _jsmnf_load_pairs(loader, js, pairs, tokens, num_tokens, pairs,
343 num_pairs);
344
345 /* TODO: rather than reseting pairnext keep the last 'bucket' ptr stored,
346 * so it can continue from the in the next try */
347 if (ret < 0) loader->pairnext = 0;
348 return ret;
349}
350
351#define _JSMNF_STRING_A js
352#define _JSMNF_STRING_B key
353
354JSMN_API struct jsmnf_pair *
355jsmnf_find(const struct jsmnf_pair *head,
356 const char *js,
357 const char key[],
358 int length)
359{
360 struct jsmnf_pair *found = NULL;
361
362 if (!key || !head) return NULL;
363
364 if (JSMN_OBJECT == head->type) {
365 struct jsmnftok _key;
366 int contains;
367
368 _key.pos = 0;
369 _key.len = length;
370
371 contains = chash_contains(head, _key, contains, _JSMNF_TABLE);
372 if (contains) {
373 (void)chash_lookup_bucket(head, _key, found, _JSMNF_TABLE);
374 }
375 }
376 else if (JSMN_ARRAY == head->type) {
377 char *endptr;
378 int idx = (int)strtol(key, &endptr, 10);
379 if (endptr != key && idx < head->size) found = head->fields + idx;
380 }
381 return found;
382}
383
384#undef _JSMNF_STRING_A
385#undef _JSMNF_STRING_B
386
387JSMN_API struct jsmnf_pair *
388jsmnf_find_path(const struct jsmnf_pair *head,
389 const char *js,
390 char *const path[],
391 unsigned depth)
392{
393 const struct jsmnf_pair *iter = head;
394 struct jsmnf_pair *found = NULL;
395 unsigned i;
396
397 for (i = 0; i < depth; ++i) {
398 if (!iter) continue;
399 found = jsmnf_find(iter, js, path[i], strlen(path[i]));
400 if (!found) break;
401 iter = found;
402 }
403 return found;
404}
405
406#define RECALLOC_OR_ERROR(ptr, prev_size) \
407 do { \
408 const unsigned new_size = *prev_size * 2; \
409 void *tmp = realloc((ptr), new_size * sizeof *(ptr)); \
410 if (!tmp) return JSMN_ERROR_NOMEM; \
411 \
412 *prev_size = new_size; \
413 memset((ptr) + *(prev_size), 0, \
414 (new_size - *(prev_size)) * sizeof *(ptr)); \
415 (ptr) = tmp; \
416 } while (0)
417
418JSMN_API int
420 const char *js,
421 size_t length,
422 struct jsmntok **p_tokens,
423 unsigned *num_tokens)
424{
425 int ret;
426
427 if (NULL == *p_tokens || !*num_tokens) {
428 *p_tokens = calloc(1, sizeof **p_tokens);
429 *num_tokens = 1;
430 }
431
432 while (1) {
433 ret = jsmn_parse(parser, js, length, *p_tokens, *num_tokens);
434 if (ret != JSMN_ERROR_NOMEM)
435 break;
436 else
437 RECALLOC_OR_ERROR(*p_tokens, num_tokens);
438 }
439 return ret;
440}
441
442JSMN_API int
444 const char *js,
445 const struct jsmntok tokens[],
446 unsigned num_tokens,
447 struct jsmnf_pair **p_pairs,
448 unsigned *num_pairs)
449{
450 int ret;
451
452 if (NULL == *p_pairs || !*num_pairs) {
453 *p_pairs = calloc(1, sizeof **p_pairs);
454 *num_pairs = 1;
455 }
456
457 while (1) {
458 ret = jsmnf_load(loader, js, tokens, num_tokens, *p_pairs, *num_pairs);
459 if (ret != JSMN_ERROR_NOMEM)
460 break;
461 else
462 RECALLOC_OR_ERROR(*p_pairs, num_pairs);
463 }
464 return ret;
465}
466
467#undef RECALLOC_OR_ERROR
468
469static int
470_jsmnf_read_4_digits(char *s, const char *end, unsigned *p_hex)
471{
472 char buf[5] = { 0 };
473 int i;
474
475 if (end - s < 4) return JSMN_ERROR_PART;
476
477 for (i = 0; i < 4; i++) {
478 buf[i] = s[i];
479 if (('0' <= s[i] && s[i] <= '9') || ('A' <= s[i] && s[i] <= 'F')
480 || ('a' <= s[i] && s[i] <= 'f'))
481 {
482 continue;
483 }
484 return JSMN_ERROR_INVAL;
485 }
486
487 *p_hex = (unsigned)strtoul(buf, NULL, 16);
488
489 return 4;
490}
491
492#define _JSMNF_UTF16_IS_FIRST_SURROGATE(c) \
493 (0xD800 <= (unsigned)c && (unsigned)c <= 0xDBFF)
494#define _JSMNF_UTF16_IS_SECOND_SURROGATE(c) \
495 (0xDC00 <= (unsigned)c && (unsigned)c <= 0xDFFF)
496#define _JSMNF_UTF16_JOIN_SURROGATE(c1, c2) \
497 (((((unsigned long)c1 & 0x3FF) << 10) | ((unsigned)c2 & 0x3FF)) + 0x10000)
498#define _JSMNF_UTF8_IS_VALID(c) \
499 (((unsigned long)c <= 0x10FFFF) \
500 && ((unsigned long)c < 0xD800 || (unsigned long)c > 0xDFFF))
501#define _JSMNF_UTF8_IS_TRAIL(c) (((unsigned char)c & 0xC0) == 0x80)
502#define _JSMNF_UTF_ILLEGAL 0xFFFFFFFFu
503
504static int
505_jsmnf_utf8_trail_length(unsigned char c)
506{
507 if (c < 128) return 0;
508 if (c < 194) return -1;
509 if (c < 224) return 1;
510 if (c < 240) return 2;
511 if (c <= 244) return 3;
512 return -1;
513}
514
515static int
516_jsmnf_utf8_width(unsigned long value)
517{
518 if (value <= 0x7F) return 1;
519 if (value <= 0x7FF) return 2;
520 if (value <= 0xFFFF) return 3;
521 return 4;
522}
523
524/* See RFC 3629
525 Based on: http://www.w3.org/International/questions/qa-forms-utf-8 */
526static unsigned long
527_jsmnf_utf8_next(char **p, const char *end)
528{
529 unsigned char lead, tmp;
530 int trail_size;
531 unsigned long c;
532
533 if (*p == end) return _JSMNF_UTF_ILLEGAL;
534
535 lead = **p;
536 (*p)++;
537
538 /* First byte is fully validated here */
539 trail_size = _jsmnf_utf8_trail_length(lead);
540
541 if (trail_size < 0) return _JSMNF_UTF_ILLEGAL;
542
543 /* Ok as only ASCII may be of size = 0 also optimize for ASCII text */
544 if (trail_size == 0) return lead;
545
546 c = lead & ((1 << (6 - trail_size)) - 1);
547
548 /* Read the rest */
549 switch (trail_size) {
550 case 3:
551 if (*p == end) return _JSMNF_UTF_ILLEGAL;
552 tmp = **p;
553 (*p)++;
555 c = (c << 6) | (tmp & 0x3F);
556 /* fall-through */
557 case 2:
558 if (*p == end) return _JSMNF_UTF_ILLEGAL;
559 tmp = **p;
560 (*p)++;
562 c = (c << 6) | (tmp & 0x3F);
563 /* fall-through */
564 case 1:
565 if (*p == end) return _JSMNF_UTF_ILLEGAL;
566 tmp = **p;
567 (*p)++;
569 c = (c << 6) | (tmp & 0x3F);
570 }
571
572 /* Check code point validity: no surrogates and valid range */
574
575 /* make sure it is the most compact representation */
576 if (_jsmnf_utf8_width(c) != trail_size + 1) return _JSMNF_UTF_ILLEGAL;
577
578 return c;
579}
580
581static long
582_jsmnf_utf8_validate(char *p, const char *end)
583{
584 const char *start = p;
585 while (p != end) {
586 if (_jsmnf_utf8_next(&p, end) == _JSMNF_UTF_ILLEGAL)
587 return JSMN_ERROR_INVAL;
588 }
589 return (long)(end - start);
590}
591
592static unsigned
593_jsmnf_utf8_encode(unsigned long value, char utf8_seq[4])
594{
595 if (value <= 0x7F) {
596 utf8_seq[0] = value;
597 return 1;
598 }
599 if (value <= 0x7FF) {
600 utf8_seq[0] = (value >> 6) | 0xC0;
601 utf8_seq[1] = (value & 0x3F) | 0x80;
602 return 2;
603 }
604 if (value <= 0xFFFF) {
605 utf8_seq[0] = (value >> 12) | 0xE0;
606 utf8_seq[1] = ((value >> 6) & 0x3F) | 0x80;
607 utf8_seq[2] = (value & 0x3F) | 0x80;
608 return 3;
609 }
610 utf8_seq[0] = (value >> 18) | 0xF0;
611 utf8_seq[1] = ((value >> 12) & 0x3F) | 0x80;
612 utf8_seq[2] = ((value >> 6) & 0x3F) | 0x80;
613 utf8_seq[3] = (value & 0x3F) | 0x80;
614 return 4;
615}
616
617static int
618_jsmnf_utf8_append(unsigned long hex, char *buf_tok, const char *buf_end)
619{
620 char utf8_seq[4];
621 unsigned utf8_seqlen = _jsmnf_utf8_encode(hex, utf8_seq);
622 unsigned i;
623
624 if ((buf_tok + utf8_seqlen) >= buf_end) return JSMN_ERROR_NOMEM;
625
626 for (i = 0; i < utf8_seqlen; ++i)
627 buf_tok[i] = utf8_seq[i];
628 return utf8_seqlen;
629}
630
631#define BUF_PUSH(buf_tok, c, buf_end) \
632 do { \
633 if (buf_tok >= buf_end) return JSMN_ERROR_NOMEM; \
634 *buf_tok++ = c; \
635 } while (0)
636
637JSMN_API long
638jsmnf_unescape(char buf[], size_t bufsize, const char src[], size_t len)
639{
640 char *src_tok = (char *)src, *const src_end = src_tok + len;
641 char *buf_tok = buf, *const buf_end = buf + bufsize;
642 int second_surrogate_expected = 0;
643 unsigned first_surrogate = 0;
644
645 while (*src_tok && src_tok < src_end) {
646 char c = *src_tok++;
647
648 if (0 <= c && c <= 0x1F) return JSMN_ERROR_INVAL;
649
650 if (c != '\\') {
651 if (second_surrogate_expected) return JSMN_ERROR_INVAL;
652 BUF_PUSH(buf_tok, c, buf_end);
653 continue;
654 }
655
656 /* expects escaping but src is a well-formed string */
657 if (!*src_tok || src_tok >= src_end) return JSMN_ERROR_PART;
658
659 c = *src_tok++;
660
661 if (second_surrogate_expected && c != 'u') return JSMN_ERROR_INVAL;
662
663 switch (c) {
664 case '"':
665 case '\\':
666 case '/':
667 BUF_PUSH(buf_tok, c, buf_end);
668 break;
669 case 'b':
670 BUF_PUSH(buf_tok, '\b', buf_end);
671 break;
672 case 'f':
673 BUF_PUSH(buf_tok, '\f', buf_end);
674 break;
675 case 'n':
676 BUF_PUSH(buf_tok, '\n', buf_end);
677 break;
678 case 'r':
679 BUF_PUSH(buf_tok, '\r', buf_end);
680 break;
681 case 't':
682 BUF_PUSH(buf_tok, '\t', buf_end);
683 break;
684 case 'u': {
685 unsigned hex;
686 int ret = _jsmnf_read_4_digits(src_tok, src_end, &hex);
687
688 if (ret != 4) return ret;
689
690 src_tok += ret;
691
692 if (second_surrogate_expected) {
694 return JSMN_ERROR_INVAL;
695
696 ret = _jsmnf_utf8_append(
697 _JSMNF_UTF16_JOIN_SURROGATE(first_surrogate, hex), buf_tok,
698 buf_end);
699 if (ret < 0) return ret;
700
701 buf_tok += ret;
702
703 second_surrogate_expected = 0;
704 }
705 else if (_JSMNF_UTF16_IS_FIRST_SURROGATE(hex)) {
706 second_surrogate_expected = 1;
707 first_surrogate = hex;
708 }
709 else {
710 ret = _jsmnf_utf8_append(hex, buf_tok, buf_end);
711 if (ret < 0) return ret;
712
713 buf_tok += ret;
714 }
715 } break;
716 default:
717 return JSMN_ERROR_INVAL;
718 }
719 }
720 return _jsmnf_utf8_validate(buf, buf_tok);
721}
722
723#undef BUF_PUSH
724
725#endif /* JSMN_HEADER */
726
727#ifdef __cplusplus
728}
729#endif
730
731#endif /* JSMN_FIND_H */
#define chash_init_stack(hashtable, buffer, _length, namespace)
Definition: chash.h:282
#define chash_contains(hashtable, _key, storage, namespace)
Definition: chash.h:378
#define CHASH_FILLED
Definition: chash.h:104
#define chash_lookup_bucket(hashtable, _key, storage, namespace)
Definition: chash.h:396
#define chash_assign(hashtable, _key, _value, namespace)
Definition: chash.h:297
struct jsmnf_pair jsmnf_pair
JSON object.
JSMN_API void jsmnf_init(jsmnf_loader *loader)
Initialize a jsmnf_loader.
Definition: jsmn-find.h:203
#define RECALLOC_OR_ERROR(ptr, prev_size)
Definition: jsmn-find.h:406
#define _JSMNF_UTF_ILLEGAL
Definition: jsmn-find.h:502
#define BUF_PUSH(buf_tok, c, buf_end)
Definition: jsmn-find.h:631
JSMN_API int jsmnf_load(jsmnf_loader *loader, const char *js, const jsmntok_t tokens[], unsigned num_tokens, jsmnf_pair pairs[], unsigned num_pairs)
Populate the jsmnf_pair pairs from jsmn tokens.
Definition: jsmn-find.h:320
struct jsmnf_loader jsmnf_loader
Bucket (jsmnf_pair) loader, keeps track of pair array position.
JSMN_API jsmnf_pair * jsmnf_find_path(const jsmnf_pair *head, const char *js, char *const path[], unsigned depth)
Find a jsmnf_pair token by its full key path.
#define _JSMNF_UTF16_JOIN_SURROGATE(c1, c2)
Definition: jsmn-find.h:496
JSMN_API jsmnf_pair * jsmnf_find(const jsmnf_pair *head, const char *js, const char key[], int length)
Find a jsmnf_pair token by its associated key.
JSMN_API int jsmnf_load_auto(jsmnf_loader *loader, const char *js, const jsmntok_t tokens[], unsigned num_tokens, jsmnf_pair **p_pairs, unsigned *num_pairs)
Populate and automatically allocate the jsmnf_pair pairs from jsmn tokens.
Definition: jsmn-find.h:443
#define _JSMNF_UTF16_IS_SECOND_SURROGATE(c)
Definition: jsmn-find.h:494
#define _JSMNF_UTF8_IS_VALID(c)
Definition: jsmn-find.h:498
JSMN_API int jsmn_parse_auto(jsmn_parser *parser, const char *js, size_t length, jsmntok_t **p_tokens, unsigned *num_tokens)
jsmn_parse() counterpart that automatically allocates the necessary amount of tokens necessary for pa...
Definition: jsmn-find.h:419
JSMN_API long jsmnf_unescape(char buf[], size_t bufsize, const char src[], size_t length)
Utility function for unescaping a Unicode string.
Definition: jsmn-find.h:638
#define _JSMNF_UTF16_IS_FIRST_SURROGATE(c)
Definition: jsmn-find.h:492
#define _JSMNF_UTF8_IS_TRAIL(c)
Definition: jsmn-find.h:501
jsmntype_t
Definition: jsmn.h:46
@ JSMN_PRIMITIVE
Definition: jsmn.h:51
@ JSMN_OBJECT
Definition: jsmn.h:48
@ JSMN_UNDEFINED
Definition: jsmn.h:47
@ JSMN_ARRAY
Definition: jsmn.h:49
@ JSMN_STRING
Definition: jsmn.h:50
#define JSMN_API
Definition: jsmn.h:36
int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, jsmntok_t *tokens, const unsigned int num_tokens)
Definition: jsmn.h:268
@ JSMN_ERROR_INVAL
Definition: jsmn.h:58
@ JSMN_ERROR_PART
Definition: jsmn.h:60
@ JSMN_ERROR_NOMEM
Definition: jsmn.h:56
Definition: jsmn.h:83
Bucket (jsmnf_pair) loader, keeps track of pair array position.
Definition: jsmn-find.h:40
unsigned pairnext
Definition: jsmn-find.h:42
JSON object.
Definition: jsmn-find.h:21
int state
Definition: jsmn-find.h:35
int size
Definition: jsmn-find.h:25
struct jsmnftok k
Definition: jsmn-find.h:31
jsmntype_t type
Definition: jsmn-find.h:23
struct jsmnf_pair * fields
Definition: jsmn-find.h:29
int capacity
Definition: jsmn-find.h:27
struct jsmnftok v
Definition: jsmn-find.h:33
JSON token description.
Definition: jsmn-find.h:13
int pos
Definition: jsmn-find.h:15
size_t len
Definition: jsmn-find.h:17
Definition: jsmn.h:69
int end
Definition: jsmn.h:72
int size
Definition: jsmn.h:73
int start
Definition: jsmn.h:71
jsmntype_t type
Definition: jsmn.h:70