Skip to content

Commit 8b084a4

Browse files
update JSON parser
1 parent 6835d35 commit 8b084a4

File tree

3 files changed

+360
-35
lines changed

3 files changed

+360
-35
lines changed

code/logic/fossil/media/json.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ struct fossil_media_json_value {
9090
*
9191
* @note The returned value must be freed with fossil_media_json_free().
9292
*/
93-
fossil_media_json_value_t *
94-
fossil_media_json_parse(const char *json_text, fossil_media_json_error_t *err_out);
93+
fossil_media_json_value_t *fossil_media_json_parse(const char *json_text, fossil_media_json_error_t *err_out);
9594

9695
/**
9796
* @brief Free a JSON DOM tree.

code/logic/json.c

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,20 @@ static fossil_media_json_value_t *parse_array(ctx_t *c, fossil_media_json_error_
338338
skip_ws(c);
339339
fossil_media_json_value_t *arr = fossil_media_json_new_array();
340340
if (!arr) { set_error(err,1,c->i,"OOM"); return NULL; }
341+
skip_ws(c);
341342
if (c->s[c->i] == ']') { c->i++; return arr; }
342343
while (1) {
343344
skip_ws(c);
344345
fossil_media_json_value_t *elem = parse_value(c, err);
345346
if (!elem) { fossil_media_json_free(arr); return NULL; }
346347
if (fossil_media_json_array_append(arr, elem) != 0) { fossil_media_json_free(elem); fossil_media_json_free(arr); set_error(err,1,c->i,"OOM"); return NULL; }
347348
skip_ws(c);
348-
if (c->s[c->i] == ',') { c->i++; continue; }
349+
if (c->s[c->i] == ',') {
350+
c->i++;
351+
skip_ws(c);
352+
if (c->s[c->i] == ']') { fossil_media_json_free(arr); set_error(err,1,c->i,"Trailing comma in array"); return NULL; }
353+
continue;
354+
}
349355
else if (c->s[c->i] == ']') { c->i++; break; }
350356
else { fossil_media_json_free(arr); set_error(err,1,c->i,"Expected ',' or ']' in array"); return NULL; }
351357
}
@@ -361,6 +367,7 @@ static fossil_media_json_value_t *parse_object(ctx_t *c, fossil_media_json_error
361367
skip_ws(c);
362368
fossil_media_json_value_t *obj = fossil_media_json_new_object();
363369
if (!obj) { set_error(err,1,c->i,"OOM"); return NULL; }
370+
skip_ws(c);
364371
if (c->s[c->i] == '}') { c->i++; return obj; }
365372
while (1) {
366373
skip_ws(c);
@@ -390,7 +397,12 @@ static fossil_media_json_value_t *parse_object(ctx_t *c, fossil_media_json_error
390397
obj->u.object.values[obj->u.object.count] = val;
391398
obj->u.object.count++;
392399
skip_ws(c);
393-
if (c->s[c->i] == ',') { c->i++; continue; }
400+
if (c->s[c->i] == ',') {
401+
c->i++;
402+
skip_ws(c);
403+
if (c->s[c->i] == '}') { fossil_media_json_free(obj); set_error(err,1,c->i,"Trailing comma in object"); return NULL; }
404+
continue;
405+
}
394406
else if (c->s[c->i] == '}') { c->i++; break; }
395407
else { fossil_media_json_free(obj); set_error(err,1,c->i,"Expected ',' or '}' in object"); return NULL; }
396408
}
@@ -451,10 +463,10 @@ static void append_escaped(char **bufp, size_t *lenp, size_t *cap, const char *s
451463
add = 6;
452464
}
453465
if (esc) {
454-
if (*lenp + add + 1 > *cap) { *cap = (*lenp + add + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
466+
if (*lenp + add + 1 > *cap) { *cap = (*lenp + add + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return; }
455467
memcpy(*bufp + *lenp, esc, add); *lenp += add; continue;
456468
}
457-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
469+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return; }
458470
(*bufp)[(*lenp)++] = c;
459471
}
460472
}
@@ -465,82 +477,83 @@ static int stringify_value(const fossil_media_json_value_t *v, char **bufp, size
465477
if (*bufp == NULL) { *cap = 256; *bufp = fm_malloc(*cap); if (!*bufp) return -1; *lenp = 0; }
466478
switch (v->type) {
467479
case FOSSIL_MEDIA_JSON_NULL:
468-
if (*lenp + 5 > *cap) { *cap = (*lenp + 5) * 2; *bufp = fm_realloc(*bufp, *cap); }
480+
if (*lenp + 5 > *cap) { *cap = (*lenp + 5) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
469481
memcpy(*bufp + *lenp, "null", 4); *lenp += 4;
470482
break;
471483
case FOSSIL_MEDIA_JSON_BOOL: {
472484
const char *t = v->u.boolean ? "true" : "false";
473485
size_t n = v->u.boolean ? 4 : 5;
474-
if (*lenp + n + 1 > *cap) { *cap = (*lenp + n + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
486+
if (*lenp + n + 1 > *cap) { *cap = (*lenp + n + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
475487
memcpy(*bufp + *lenp, t, n); *lenp += n;
476488
break;
477489
}
478490
case FOSSIL_MEDIA_JSON_NUMBER: {
479491
char tmp[64];
480492
int n = snprintf(tmp, sizeof(tmp), "%.17g", v->u.number);
481-
if (*lenp + (size_t)n + 1 > *cap) { *cap = (*lenp + n + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
493+
if (n < 0) return -1;
494+
if (*lenp + (size_t)n + 1 > *cap) { *cap = (*lenp + n + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
482495
memcpy(*bufp + *lenp, tmp, n); *lenp += n;
483496
break;
484497
}
485498
case FOSSIL_MEDIA_JSON_STRING: {
486-
if (*lenp + 3 > *cap) { *cap = (*lenp + 3) * 2; *bufp = fm_realloc(*bufp, *cap); }
499+
if (*lenp + 3 > *cap) { *cap = (*lenp + 3) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
487500
(*bufp)[(*lenp)++] = '"';
488501
append_escaped(bufp, lenp, cap, v->u.string ? v->u.string : "");
489-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
502+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
490503
(*bufp)[(*lenp)++] = '"';
491504
break;
492505
}
493506
case FOSSIL_MEDIA_JSON_ARRAY: {
494-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
507+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
495508
(*bufp)[(*lenp)++] = '[';
496509
for (size_t i = 0; i < v->u.array.count; ++i) {
497510
if (i) {
498-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
511+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
499512
(*bufp)[(*lenp)++] = ',';
500513
}
501514
if (pretty) {
502-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
515+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
503516
(*bufp)[(*lenp)++] = '\n';
504517
for (int d = 0; d < depth+1; ++d) {
505-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
518+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
506519
(*bufp)[(*lenp)++] = '\t';
507520
}
508521
}
509522
if (stringify_value(v->u.array.items[i], bufp, lenp, cap, pretty, depth+1) != 0) return -1;
510523
}
511524
if (pretty && v->u.array.count) {
512-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
525+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
513526
(*bufp)[(*lenp)++] = '\n';
514527
for (int d = 0; d < depth; ++d) {
515-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
528+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
516529
(*bufp)[(*lenp)++] = '\t';
517530
}
518531
}
519-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
532+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
520533
(*bufp)[(*lenp)++] = ']';
521534
break;
522535
}
523536
case FOSSIL_MEDIA_JSON_OBJECT: {
524-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
537+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
525538
(*bufp)[(*lenp)++] = '{';
526539
for (size_t i = 0; i < v->u.object.count; ++i) {
527540
if (i) {
528-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
541+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
529542
(*bufp)[(*lenp)++] = ',';
530543
}
531544
if (pretty) {
532-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
545+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
533546
(*bufp)[(*lenp)++] = '\n';
534547
for (int d = 0; d < depth+1; ++d) {
535-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
548+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
536549
(*bufp)[(*lenp)++] = '\t';
537550
}
538551
}
539552
/* key */
540-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
553+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
541554
(*bufp)[(*lenp)++] = '"';
542555
append_escaped(bufp, lenp, cap, v->u.object.keys[i]);
543-
if (*lenp + 3 > *cap) { *cap = (*lenp + 3) * 2; *bufp = fm_realloc(*bufp, *cap); }
556+
if (*lenp + 3 > *cap) { *cap = (*lenp + 3) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
544557
(*bufp)[(*lenp)++] = '"';
545558
(*bufp)[(*lenp)++] = ':';
546559
if (pretty) {
@@ -549,14 +562,14 @@ static int stringify_value(const fossil_media_json_value_t *v, char **bufp, size
549562
if (stringify_value(v->u.object.values[i], bufp, lenp, cap, pretty, depth+1) != 0) return -1;
550563
}
551564
if (pretty && v->u.object.count) {
552-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
565+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
553566
(*bufp)[(*lenp)++] = '\n';
554567
for (int d = 0; d < depth; ++d) {
555-
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); }
568+
if (*lenp + 2 > *cap) { *cap = (*lenp + 2) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
556569
(*bufp)[(*lenp)++] = '\t';
557570
}
558571
}
559-
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); }
572+
if (*lenp + 1 > *cap) { *cap = (*lenp + 1) * 2; *bufp = fm_realloc(*bufp, *cap); if (!*bufp) return -1; }
560573
(*bufp)[(*lenp)++] = '}';
561574
break;
562575
}
@@ -580,7 +593,7 @@ char *fossil_media_json_stringify(const fossil_media_json_value_t *v, int pretty
580593
}
581594

582595
char *fossil_media_json_roundtrip(const char *json_text, int pretty, fossil_media_json_error_t *err_out) {
583-
fossil_media_json_error_t err;
596+
fossil_media_json_error_t err = {0,0,""};
584597
fossil_media_json_value_t *v = fossil_media_json_parse(json_text, &err);
585598
if (!v) { if (err_out) *err_out = err; return NULL; }
586599
char *s = fossil_media_json_stringify(v, pretty, &err);
@@ -646,6 +659,8 @@ static fossil_media_json_value_t *fossil_media_json_clone_internal(const fossil_
646659
}
647660
}
648661
break;
662+
default:
663+
break;
649664
}
650665
return copy;
651666
}
@@ -657,7 +672,8 @@ fossil_media_json_clone(const fossil_media_json_value_t *src) {
657672

658673
int fossil_media_json_equals(const fossil_media_json_value_t *a,
659674
const fossil_media_json_value_t *b) {
660-
if (!a || !b) return -1;
675+
if (!a && !b) return -1;
676+
if (!a || !b) return 0;
661677
if (a->type != b->type) return 0;
662678

663679
switch (a->type) {
@@ -668,6 +684,8 @@ int fossil_media_json_equals(const fossil_media_json_value_t *a,
668684
case FOSSIL_MEDIA_JSON_NUMBER:
669685
return a->u.number == b->u.number;
670686
case FOSSIL_MEDIA_JSON_STRING:
687+
if (!a->u.string && !b->u.string) return 1;
688+
if (!a->u.string || !b->u.string) return 0;
671689
return strcmp(a->u.string, b->u.string) == 0;
672690
case FOSSIL_MEDIA_JSON_ARRAY:
673691
if (a->u.array.count != b->u.array.count) return 0;
@@ -683,7 +701,14 @@ int fossil_media_json_equals(const fossil_media_json_value_t *a,
683701
if (!val_b || !fossil_media_json_equals(a->u.object.values[i], val_b))
684702
return 0;
685703
}
704+
for (size_t i = 0; i < b->u.object.count; i++) {
705+
fossil_media_json_value_t *val_a = fossil_media_json_object_get(a, b->u.object.keys[i]);
706+
if (!val_a || !fossil_media_json_equals(b->u.object.values[i], val_a))
707+
return 0;
708+
}
686709
return 1;
710+
default:
711+
break;
687712
}
688713
return 0;
689714
}
@@ -713,7 +738,7 @@ int fossil_media_json_array_reserve(fossil_media_json_value_t *arr, size_t capac
713738
if (capacity <= arr->u.array.capacity) return 0;
714739

715740
fossil_media_json_value_t **new_items =
716-
realloc(arr->u.array.items, capacity * sizeof(*new_items));
741+
fm_realloc(arr->u.array.items, capacity * sizeof(*new_items));
717742
if (!new_items) return -1;
718743

719744
arr->u.array.items = new_items;
@@ -725,9 +750,9 @@ int fossil_media_json_object_reserve(fossil_media_json_value_t *obj, size_t capa
725750
if (!obj || obj->type != FOSSIL_MEDIA_JSON_OBJECT) return -1;
726751
if (capacity <= obj->u.object.capacity) return 0;
727752

728-
char **new_keys = realloc(obj->u.object.keys, capacity * sizeof(*new_keys));
753+
char **new_keys = fm_realloc(obj->u.object.keys, capacity * sizeof(*new_keys));
729754
fossil_media_json_value_t **new_vals =
730-
realloc(obj->u.object.values, capacity * sizeof(*new_vals));
755+
fm_realloc(obj->u.object.values, capacity * sizeof(*new_vals));
731756
if (!new_keys || !new_vals) return -1;
732757

733758
obj->u.object.keys = new_keys;
@@ -823,7 +848,7 @@ void fossil_media_json_debug_dump(const fossil_media_json_value_t *v, int indent
823848
printf("%*sValue: %g\n", indent + 2, "", v->u.number);
824849
break;
825850
case FOSSIL_MEDIA_JSON_STRING:
826-
printf("%*sValue: \"%s\"\n", indent + 2, "", v->u.string);
851+
printf("%*sValue: \"%s\"\n", indent + 2, "", v->u.string ? v->u.string : "(null)");
827852
break;
828853
case FOSSIL_MEDIA_JSON_ARRAY:
829854
for (size_t i = 0; i < v->u.array.count; i++) {
@@ -841,9 +866,14 @@ void fossil_media_json_debug_dump(const fossil_media_json_value_t *v, int indent
841866
}
842867

843868
int fossil_media_json_validate(const char *json_text, fossil_media_json_error_t *err_out) {
844-
fossil_media_json_value_t *v = fossil_media_json_parse(json_text, err_out);
845-
if (!v) return -1;
869+
fossil_media_json_error_t errtmp = {0,0,""};
870+
fossil_media_json_value_t *v = fossil_media_json_parse(json_text, &errtmp);
871+
if (!v) {
872+
if (err_out) *err_out = errtmp;
873+
return 1;
874+
}
846875
fossil_media_json_free(v);
876+
if (err_out) *err_out = errtmp;
847877
return 0;
848878
}
849879

0 commit comments

Comments
 (0)