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