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
11
12#define OA_HASH_HEADER
13#include "oa_hash.h"
14#undef OA_HASH_HEADER
15
16#define JSMNF_PAIR_ATTRS_const \
17 \
18 OA_HASH_ATTRS(const); \
19 \
20 const struct jsmnf_pair *const fields; \
21 \
22 const jsmntok_t *const k; \
23 \
24 const jsmntok_t *const v
25#define JSMNF_PAIR_ATTRS_mut \
26 \
27 OA_HASH_ATTRS(mut); \
28 \
29 struct jsmnf_pair *fields; \
30 \
31 jsmntok_t *k; \
32 \
33 jsmntok_t *v
34#define JSMNF_PAIR_ATTRS(_qualifier) JSMNF_PAIR_ATTRS_##_qualifier
35
36typedef struct jsmnf_pair {
37 JSMNF_PAIR_ATTRS(const);
38} jsmnf_pair;
39
42typedef struct jsmnf_loader {
44 jsmn_parser parser;
46 unsigned pairnext;
48 const jsmnf_pair *root;
49} jsmnf_loader;
50
52typedef struct jsmnf_table {
54 const struct jsmntok _;
55 const struct jsmnf_pair __;
56 const struct oa_hash_entry ___;
57} jsmnf_table;
58
64JSMN_API void jsmnf_init(jsmnf_loader *loader);
65
78JSMN_API long jsmnf_load(jsmnf_loader *loader,
79 const char js[],
80 const size_t len,
81 jsmnf_table table[],
82 const size_t table_len);
83
93JSMN_API const jsmnf_pair *jsmnf_find(const jsmnf_pair *const head,
94 const char key[],
95 const size_t length);
96
106JSMN_API const jsmnf_pair *jsmnf_find_path(const jsmnf_pair *const head,
107 char *const path[],
108 unsigned depth);
109
124JSMN_API long jsmnf_load_auto(jsmnf_loader *loader,
125 const char js[],
126 const size_t len,
127 jsmnf_table **p_table,
128 size_t *num_pairs);
129
142JSMN_API long jsmn_parse_auto(jsmn_parser *parser,
143 const char js[],
144 const size_t len,
145 jsmntok_t **p_tokens,
146 unsigned *num_tokens);
147
158JSMN_API long jsmnf_unescape(char buf[],
159 size_t bufsize,
160 const char src[],
161 size_t length);
162
163#ifndef JSMN_HEADER
164
165#include <stdio.h>
166#include <stdlib.h>
167#include <string.h>
168
169#define OA_HASH_STATIC
170#include "oa_hash.h"
171#undef OA_HASH_STATIC
172
173struct _jsmnf_pair_mut {
174 JSMNF_PAIR_ATTRS(mut);
175};
176
177JSMN_API void
178jsmnf_init(jsmnf_loader *loader)
179{
180 jsmn_init(&loader->parser);
181 loader->pairnext = 0;
182}
183
184static long
185_jsmnf_load_pairs(struct jsmnf_loader *loader,
186 const char js[],
187 struct _jsmnf_pair_mut *curr,
188 const size_t num_tokens,
189 struct _jsmnf_pair_mut pairs[],
190 struct oa_hash_entry buckets[],
191 const size_t table_len)
192{
193 int offset = 0;
194
195 if (!num_tokens) return 0;
196
197 switch (curr->v->type) {
198 case JSMN_STRING:
199 case JSMN_PRIMITIVE:
200 break;
201 case JSMN_OBJECT:
202 case JSMN_ARRAY: {
203 const unsigned value_size = (unsigned)curr->v->size,
204 top_idx = loader->pairnext + (1 + value_size),
205 bottom_idx = loader->pairnext;
206 int ret;
207
208 if (value_size > (table_len - bottom_idx)
209 || top_idx > (table_len - bottom_idx))
210 {
211 return JSMN_ERROR_NOMEM;
212 }
213
214 loader->pairnext = top_idx;
215
216 oa_hash_init((struct oa_hash *)curr, &buckets[bottom_idx],
217 top_idx - bottom_idx);
218 if (curr == NULL) {
219 abort();
220 }
221 if (JSMN_OBJECT == curr->v->type) {
222 while (curr->length < value_size) {
223 struct _jsmnf_pair_mut *fields = pairs + bottom_idx,
224 *element = fields + curr->length;
225 element->k = curr->v + 1 + (offset++);
226 if (element->k->size > 0) {
227 element->v = curr->v + 1 + offset;
228 oa_hash_set((struct oa_hash *)curr, js + element->k->start,
229 element->k->end - element->k->start, element);
230 if ((ret = _jsmnf_load_pairs(loader, js, element,
231 num_tokens - offset, pairs,
232 buckets, table_len))
233 < 0)
234 {
235 return ret;
236 }
237 curr->fields = (struct jsmnf_pair *)fields;
238 offset += ret;
239 }
240 else {
241 oa_hash_set((struct oa_hash *)curr, js + element->k->start,
242 element->k->end - element->k->start, NULL);
243 }
244 }
245 }
246 else if (JSMN_ARRAY == curr->v->type) {
247 for (; curr->length < value_size; ++curr->length) {
248 static jsmntok_t empty_key = { 0 };
249 struct oa_hash_entry *entry = curr->buckets + curr->length;
250 struct _jsmnf_pair_mut *fields = pairs + bottom_idx,
251 *element = fields + curr->length;
253 entry->value = element;
254 element->v = curr->v + 1 + offset;
255 element->k = &empty_key;
256 if ((ret = _jsmnf_load_pairs(loader, js, element,
257 num_tokens - offset, pairs,
258 buckets, table_len))
259 < 0)
260 {
261 return ret;
262 }
263 curr->fields = (struct jsmnf_pair *)fields;
264 offset += ret;
265 }
266 }
267 break;
268 }
269 default:
270 case JSMN_UNDEFINED:
271 return JSMN_ERROR_INVAL;
272 }
273
274 return offset + 1;
275}
276
277JSMN_API long
278jsmnf_load(struct jsmnf_loader *loader,
279 const char js[],
280 const size_t len,
281 struct jsmnf_table table[],
282 const size_t table_len)
283{
284 struct jsmntok *tokens = (struct jsmntok *)table;
285 struct _jsmnf_pair_mut
286 *pairs = (struct _jsmnf_pair_mut *)(((char *)tokens)
287 + (table_len * sizeof *tokens)),
288 *mut_root = &pairs[0];
289 struct oa_hash_entry *buckets =
290 (struct oa_hash_entry *)(((char *)pairs)
291 + (table_len * sizeof *pairs));
292 long ret;
293
294 if (loader->pairnext == 0) { /* first run, initialize pairs */
295 /* initialize tokens if not already initialized */
296 if (loader->parser.toknext == 0) {
297 memset(tokens, 0, table_len * sizeof *tokens);
298 if ((ret = jsmn_parse(&loader->parser, js, len, tokens, table_len))
299 < 0)
300 {
301 return jsmn_init(&loader->parser), ret;
302 }
303 }
304 memset(pairs, 0, table_len * sizeof *pairs);
305 memset(buckets, 0, table_len * sizeof *buckets);
306 mut_root->v = tokens + loader->pairnext++;
307 loader->root = (struct jsmnf_pair *)mut_root;
308 }
309 if ((ret = _jsmnf_load_pairs(loader, js, mut_root, loader->parser.toknext,
310 pairs, buckets, table_len))
311 < 0)
312 {
313 /* TODO: rather than reseting pairnext keep the last 'bucket' ptr
314 * stored, so it can continue from there in the next try */
315 loader->pairnext = 0;
316 loader->root = NULL;
317 }
318 return ret;
319}
320
321JSMN_API const struct jsmnf_pair *
322jsmnf_find(const struct jsmnf_pair *head,
323 const char key[],
324 const size_t length)
325{
326 if (!head || !head->v) return NULL;
327 if (!key && !length) return head;
328
329 if (JSMN_OBJECT == head->v->type) {
330 return oa_hash_get((struct oa_hash *)head, key, length);
331 }
332 if (JSMN_ARRAY == head->v->type) {
333 char *endptr;
334 const unsigned idx = (unsigned)strtoul(key, &endptr, 10);
335 if (endptr != key && (idx < head->length)
336 && head->buckets[idx].state == OA_HASH_ENTRY_OCCUPIED)
337 {
338 return head->buckets[idx].value;
339 }
340 }
341 return NULL;
342}
343
344JSMN_API const struct jsmnf_pair *
345jsmnf_find_path(const struct jsmnf_pair *head,
346 char *const path[],
347 unsigned depth)
348{
349 const struct jsmnf_pair *iter = head, *found = NULL;
350 unsigned i;
351 for (i = 0; i < depth; ++i) {
352 if (!iter || !(found = jsmnf_find(iter, path[i], strlen(path[i]))))
353 break;
354 iter = found;
355 }
356 return found;
357}
358
359#define RECALLOC_OR_ERROR(ptr, prev_size) \
360 do { \
361 const unsigned new_size = *(prev_size) * 2; \
362 void *tmp = realloc((ptr), new_size * sizeof *(ptr)); \
363 if (!tmp) return JSMN_ERROR_NOMEM; \
364 (ptr) = tmp; \
365 memset((ptr) + *(prev_size), 0, \
366 (new_size - *(prev_size)) * sizeof *(ptr)); \
367 *(prev_size) = new_size; \
368 } while (0)
369
370JSMN_API long
371jsmn_parse_auto(struct jsmn_parser *parser,
372 const char js[],
373 const size_t len,
374 struct jsmntok **p_tokens,
375 unsigned *num_tokens)
376{
377 int ret;
378
379 if (NULL == *p_tokens || 0 == *num_tokens) {
380 *p_tokens = calloc(1, sizeof **p_tokens);
381 *num_tokens = 1;
382 }
383 while ((ret = jsmn_parse(parser, js, len, *p_tokens, *num_tokens))
385 {
386 RECALLOC_OR_ERROR(*p_tokens, num_tokens);
387 }
388 return ret;
389}
390
391JSMN_API long
392jsmnf_load_auto(struct jsmnf_loader *loader,
393 const char js[],
394 const size_t len,
395 struct jsmnf_table **p_table,
396 size_t *table_len)
397{
398 int ret;
399
400 if (NULL == *p_table || 0 == *table_len) {
401 if (!(*p_table = calloc(1, sizeof **p_table))) {
402 return JSMN_ERROR_NOMEM;
403 }
404 *table_len = 1;
405 }
406 while ((ret = jsmnf_load(loader, js, len, *p_table, *table_len))
408 {
409 RECALLOC_OR_ERROR(*p_table, table_len);
410 }
411 return ret;
412}
413
414#undef RECALLOC_OR_ERROR
415
416static int
417_jsmnf_read_4_digits(char *s, const char *end, unsigned *p_hex)
418{
419 char buf[5] = { 0 };
420 int i;
421
422 if (end - s < 4) return JSMN_ERROR_PART;
423
424 for (i = 0; i < 4; i++) {
425 buf[i] = s[i];
426 if (('0' <= s[i] && s[i] <= '9') || ('A' <= s[i] && s[i] <= 'F')
427 || ('a' <= s[i] && s[i] <= 'f'))
428 {
429 continue;
430 }
431 return JSMN_ERROR_INVAL;
432 }
433
434 *p_hex = (unsigned)strtoul(buf, NULL, 16);
435
436 return 4;
437}
438
439#define _JSMNF_UTF16_IS_FIRST_SURROGATE(c) \
440 (0xD800 <= (unsigned)c && (unsigned)c <= 0xDBFF)
441#define _JSMNF_UTF16_IS_SECOND_SURROGATE(c) \
442 (0xDC00 <= (unsigned)c && (unsigned)c <= 0xDFFF)
443#define _JSMNF_UTF16_JOIN_SURROGATE(c1, c2) \
444 (((((unsigned long)c1 & 0x3FF) << 10) | ((unsigned)c2 & 0x3FF)) + 0x10000)
445#define _JSMNF_UTF8_IS_VALID(c) \
446 (((unsigned long)c <= 0x10FFFF) \
447 && ((unsigned long)c < 0xD800 || (unsigned long)c > 0xDFFF))
448#define _JSMNF_UTF8_IS_TRAIL(c) (((unsigned char)c & 0xC0) == 0x80)
449#define _JSMNF_UTF_ILLEGAL 0xFFFFFFFFu
450
451static int
452_jsmnf_utf8_trail_length(unsigned char c)
453{
454 if (c < 128) return 0;
455 if (c < 194) return -1;
456 if (c < 224) return 1;
457 if (c < 240) return 2;
458 if (c <= 244) return 3;
459 return -1;
460}
461
462static int
463_jsmnf_utf8_width(unsigned long value)
464{
465 if (value <= 0x7F) return 1;
466 if (value <= 0x7FF) return 2;
467 if (value <= 0xFFFF) return 3;
468 return 4;
469}
470
471/* See RFC 3629
472 Based on: http://www.w3.org/International/questions/qa-forms-utf-8 */
473static unsigned long
474_jsmnf_utf8_next(char **p, const char *end)
475{
476 unsigned char lead, tmp;
477 int trail_size;
478 unsigned long c;
479
480 if (*p == end) return _JSMNF_UTF_ILLEGAL;
481
482 lead = **p;
483 (*p)++;
484
485 /* First byte is fully validated here */
486 trail_size = _jsmnf_utf8_trail_length(lead);
487
488 if (trail_size < 0) return _JSMNF_UTF_ILLEGAL;
489
490 /* Ok as only ASCII may be of size = 0 also optimize for ASCII text */
491 if (trail_size == 0) return lead;
492
493 c = lead & ((1 << (6 - trail_size)) - 1);
494
495 /* Read the rest */
496 switch (trail_size) {
497 case 3:
498 if (*p == end) return _JSMNF_UTF_ILLEGAL;
499 tmp = **p;
500 (*p)++;
501 if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL;
502 c = (c << 6) | (tmp & 0x3F);
503 /* fall-through */
504 case 2:
505 if (*p == end) return _JSMNF_UTF_ILLEGAL;
506 tmp = **p;
507 (*p)++;
508 if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL;
509 c = (c << 6) | (tmp & 0x3F);
510 /* fall-through */
511 case 1:
512 if (*p == end) return _JSMNF_UTF_ILLEGAL;
513 tmp = **p;
514 (*p)++;
515 if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL;
516 c = (c << 6) | (tmp & 0x3F);
517 }
518
519 /* Check code point validity: no surrogates and valid range */
520 if (!_JSMNF_UTF8_IS_VALID(c)) return _JSMNF_UTF_ILLEGAL;
521
522 /* make sure it is the most compact representation */
523 if (_jsmnf_utf8_width(c) != trail_size + 1) return _JSMNF_UTF_ILLEGAL;
524
525 return c;
526}
527
528static long
529_jsmnf_utf8_validate(char *p, const char *end)
530{
531 const char *start = p;
532 while (p != end) {
533 if (_jsmnf_utf8_next(&p, end) == _JSMNF_UTF_ILLEGAL)
534 return JSMN_ERROR_INVAL;
535 }
536 return (long)(end - start);
537}
538
539static unsigned
540_jsmnf_utf8_encode(unsigned long value, char utf8_seq[4])
541{
542 if (value <= 0x7F) {
543 utf8_seq[0] = value;
544 return 1;
545 }
546 if (value <= 0x7FF) {
547 utf8_seq[0] = (value >> 6) | 0xC0;
548 utf8_seq[1] = (value & 0x3F) | 0x80;
549 return 2;
550 }
551 if (value <= 0xFFFF) {
552 utf8_seq[0] = (value >> 12) | 0xE0;
553 utf8_seq[1] = ((value >> 6) & 0x3F) | 0x80;
554 utf8_seq[2] = (value & 0x3F) | 0x80;
555 return 3;
556 }
557 utf8_seq[0] = (value >> 18) | 0xF0;
558 utf8_seq[1] = ((value >> 12) & 0x3F) | 0x80;
559 utf8_seq[2] = ((value >> 6) & 0x3F) | 0x80;
560 utf8_seq[3] = (value & 0x3F) | 0x80;
561 return 4;
562}
563
564static int
565_jsmnf_utf8_append(unsigned long hex, char *buf_tok, const char *buf_end)
566{
567 char utf8_seq[4];
568 unsigned utf8_seqlen = _jsmnf_utf8_encode(hex, utf8_seq);
569 unsigned i;
570
571 if ((buf_tok + utf8_seqlen) >= buf_end) return JSMN_ERROR_NOMEM;
572
573 for (i = 0; i < utf8_seqlen; ++i)
574 buf_tok[i] = utf8_seq[i];
575 return utf8_seqlen;
576}
577
578#define BUF_PUSH(buf_tok, c, buf_end) \
579 do { \
580 if (buf_tok >= buf_end) return JSMN_ERROR_NOMEM; \
581 *buf_tok++ = c; \
582 } while (0)
583
584JSMN_API long
585jsmnf_unescape(char buf[], size_t bufsize, const char src[], size_t len)
586{
587 char *src_tok = (char *)src, *const src_end = src_tok + len;
588 char *buf_tok = buf, *const buf_end = buf + bufsize;
589 int second_surrogate_expected = 0;
590 unsigned first_surrogate = 0;
591
592 while (*src_tok && src_tok < src_end) {
593 char c = *src_tok++;
594
595 if (0 <= c && c <= 0x1F) return JSMN_ERROR_INVAL;
596
597 if (c != '\\') {
598 if (second_surrogate_expected) return JSMN_ERROR_INVAL;
599 BUF_PUSH(buf_tok, c, buf_end);
600 continue;
601 }
602
603 /* expects escaping but src is a well-formed string */
604 if (!*src_tok || src_tok >= src_end) return JSMN_ERROR_PART;
605
606 c = *src_tok++;
607
608 if (second_surrogate_expected && c != 'u') return JSMN_ERROR_INVAL;
609
610 switch (c) {
611 case '"':
612 case '\\':
613 case '/':
614 BUF_PUSH(buf_tok, c, buf_end);
615 break;
616 case 'b':
617 BUF_PUSH(buf_tok, '\b', buf_end);
618 break;
619 case 'f':
620 BUF_PUSH(buf_tok, '\f', buf_end);
621 break;
622 case 'n':
623 BUF_PUSH(buf_tok, '\n', buf_end);
624 break;
625 case 'r':
626 BUF_PUSH(buf_tok, '\r', buf_end);
627 break;
628 case 't':
629 BUF_PUSH(buf_tok, '\t', buf_end);
630 break;
631 case 'u': {
632 unsigned hex;
633 int ret = _jsmnf_read_4_digits(src_tok, src_end, &hex);
634
635 if (ret != 4) return ret;
636
637 src_tok += ret;
638
639 if (second_surrogate_expected) {
640 if (!_JSMNF_UTF16_IS_SECOND_SURROGATE(hex))
641 return JSMN_ERROR_INVAL;
642
643 ret = _jsmnf_utf8_append(
644 _JSMNF_UTF16_JOIN_SURROGATE(first_surrogate, hex), buf_tok,
645 buf_end);
646 if (ret < 0) return ret;
647
648 buf_tok += ret;
649
650 second_surrogate_expected = 0;
651 }
652 else if (_JSMNF_UTF16_IS_FIRST_SURROGATE(hex)) {
653 second_surrogate_expected = 1;
654 first_surrogate = hex;
655 }
656 else {
657 ret = _jsmnf_utf8_append(hex, buf_tok, buf_end);
658 if (ret < 0) return ret;
659
660 buf_tok += ret;
661 }
662 } break;
663 default:
664 return JSMN_ERROR_INVAL;
665 }
666 }
667 return _jsmnf_utf8_validate(buf, buf_tok);
668}
669
670#undef BUF_PUSH
671
672#endif /* JSMN_HEADER */
673#endif /* JSMN_H */
674
675#undef JSMNF_PAIR_ATTRS_const
676#undef JSMNF_PAIR_ATTRS_mut
677#undef JSMNF_PAIR_ATTRS
678
679#ifdef __cplusplus
680}
681#endif
682
683#endif /* JSMN_FIND_H */
@ 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
void jsmn_init(jsmn_parser *parser)
Definition: jsmn.h:459
#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
void * oa_hash_set(struct oa_hash *ht, const char key[], const size_t len, void *value)
Insert or update entry (wrapper around oa_hash_set_entry)
Definition: oa_hash.h:266
void * oa_hash_get(const struct oa_hash *ht, const char key[], const size_t len)
Retrieve value by key (wrapper around oa_hash_get_entry)
Definition: oa_hash.h:213
@ OA_HASH_ENTRY_OCCUPIED
Definition: oa_hash.h:19
void oa_hash_init(struct oa_hash *ht, struct oa_hash_entry buckets[], const size_t capacity)
Initialize hash table with given buckets array.
Definition: oa_hash.h:151
Definition: jsmn.h:83
Definition: jsmn.h:69
Entry holding key-value pair in hash table.
Definition: oa_hash.h:24
void * value
Definition: oa_hash.h:30
size_t length
Definition: oa_hash.h:28
enum oa_hash_entry_state state
Definition: oa_hash.h:25
struct oa_hash_entry::@14 key
const char * buf
Definition: oa_hash.h:27
Open addressing hash table.
Definition: oa_hash.h:45