yeptris 0.6.45.0-arm-linux-eabihf → 0.6.46.0-arm-linux-eabihf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/yeptris_native/version.h +2 -2
- data/lib/yeptris.rb +1 -1
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/include/yeptris/marshal.h +12 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +29 -11
- data/vendor/libyeptris/src/yeptris/dom/dom.h +10 -6
- data/vendor/libyeptris/src/yeptris/emit/writer.c +562 -27
- data/vendor/libyeptris/src/yeptris/parse/engine.c +18 -7
- data/vendor/libyeptris/src/yeptris/scan/scan.c +39 -1
- data/vendor/libyeptris/src/yeptris/scan/scan.h +6 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ba6e0359c7ce80fe647880e6917a7acd6590207d5566e33fb14fe13d9016368
|
|
4
|
+
data.tar.gz: 266c14566415ef1a11979fd6db82400f4be5a53c3932d0809d7ed38e83677393
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6eb1a6c429713ceb24a2e071de720cd37ff7b6deb91cabc18ff6f369de4c7cf7af78fa73510f54de6b93afcefe118fa134223aed543b584c574bcd7a09843af0
|
|
7
|
+
data.tar.gz: df2847bf2aa28d99b9a5d444ce9602a110ce9cb0ed0d7685c31d031ce2a7131303f583189874af962afaab42a84b3bb09ed134e60d60fa41f657b5b9a984d22b
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
#define YEPTRIS_VERSION_MAJOR 0
|
|
13
13
|
#define YEPTRIS_VERSION_MINOR 6
|
|
14
|
-
#define YEPTRIS_VERSION_PATCH
|
|
15
|
-
#define YEPTRIS_VERSION_STRING "0.6.
|
|
14
|
+
#define YEPTRIS_VERSION_PATCH 46
|
|
15
|
+
#define YEPTRIS_VERSION_STRING "0.6.46"
|
|
16
16
|
|
|
17
17
|
#ifdef __cplusplus
|
|
18
18
|
extern "C" {
|
data/lib/yeptris.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Yeptris
|
|
4
4
|
# The gem's version lives in the parent namespace's file — the last
|
|
5
5
|
# internal require (yeptris/version) retired with it.
|
|
6
|
-
VERSION = "0.6.
|
|
6
|
+
VERSION = "0.6.46.0".freeze
|
|
7
7
|
|
|
8
8
|
# Informational notices are OPT-IN (yeptris-ruby#217): consumers
|
|
9
9
|
# embed yeptris inside CLIs that assert clean stderr, and a
|
data/libyeptris.so
CHANGED
|
Binary file
|
|
@@ -56,6 +56,18 @@ YEPTRIS_API YeptrisStatus yeptris_marshal_node_ex(YeptrisNode node, uint32_t fla
|
|
|
56
56
|
|
|
57
57
|
YEPTRIS_API void yeptris_marshal_free(char* out);
|
|
58
58
|
|
|
59
|
+
/* yeptris-ruby#322: the dump face's marshal reader — Ruby
|
|
60
|
+
* Marshal.dump bytes IN, JSON text OUT, one C call (no per-scalar
|
|
61
|
+
* crossings, no object reconstruction). Handles the JSON-relevant
|
|
62
|
+
* subset (nil/bool/int/string/float/array/hash + symbols/links/ivars);
|
|
63
|
+
* anything else — objects, structs, user marshal, bignums beyond i64,
|
|
64
|
+
* non-finite floats, cycles — returns YEPTRIS_ERROR_UNSUPPORTED and
|
|
65
|
+
* the binding falls back to its per-node walk. *out is malloc'd, free
|
|
66
|
+
* with yeptris_marshal_free exactly once; on failure nothing is
|
|
67
|
+
* allocated. compact selects the no-space separators. */
|
|
68
|
+
YEPTRIS_API YeptrisStatus yeptris_json_from_marshal(const char* m, size_t m_len, int compact,
|
|
69
|
+
char** out, size_t* out_len);
|
|
70
|
+
|
|
59
71
|
#ifdef __cplusplus
|
|
60
72
|
}
|
|
61
73
|
#endif
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
#include "dom.h"
|
|
11
11
|
|
|
12
|
+
#include "../scan/scan.h" /* the dq-forced SSOT (the fact bit) */
|
|
13
|
+
|
|
12
14
|
#include <yeptris/resolve.h> /* the tag-id SSOT (the strict literal arm) */
|
|
13
15
|
|
|
14
16
|
/* The bounded-parse caps: each node consumes at least one input byte
|
|
@@ -140,6 +142,16 @@ static yep_sview dom_str_in(yep_dom* d, const yep_view* v, int borrowed) {
|
|
|
140
142
|
* given metadata; tag/anchor views encode through dom_str_in (tags and
|
|
141
143
|
* event-path anchors copy to the arena; input-backed views borrow).
|
|
142
144
|
* tag_id stays 0 — the caller resolves (scalars) or leaves 0. */
|
|
145
|
+
/* Value assign + the emit-side fact bit (issue #352 lane 2): the bit
|
|
146
|
+
* is "verified clean" of the dq-forced byte set — the serializer's
|
|
147
|
+
* sc_route then skips its per-derive rescan. The scan is the SSOT
|
|
148
|
+
* predicate (yep_scan_dq_forced); every value assignment routes
|
|
149
|
+
* through here so no path can leave a dirty scalar marked clean. */
|
|
150
|
+
static void dom_set_value(yep_dom* d, uint32_t id, const yep_view* v, int borrowed) {
|
|
151
|
+
d->nodes[id].value = dom_str_in(d, v, borrowed);
|
|
152
|
+
d->nodes[id].no_exotic = !yep_scan_dq_forced(v->p, v->len);
|
|
153
|
+
}
|
|
154
|
+
|
|
143
155
|
uint32_t dom_open_node(yep_dom* d, uint8_t kind, const yep_view* tag, const yep_view* anchor,
|
|
144
156
|
int anchor_borrowed, uint8_t style, uint8_t implicit, uint8_t flow) {
|
|
145
157
|
if (!dom_grow_nodes(d, 1)) {
|
|
@@ -421,7 +433,7 @@ int yep_dom_on_event(void* ctx, const yep_event* ev) {
|
|
|
421
433
|
if (id == UINT32_MAX) {
|
|
422
434
|
return -1;
|
|
423
435
|
}
|
|
424
|
-
|
|
436
|
+
dom_set_value(d, id, &ev->value, ev->borrowed);
|
|
425
437
|
if (ev->anchor_id != 0 && dom_anchor_set(d, ev->anchor_id, id) != 0) {
|
|
426
438
|
return -1;
|
|
427
439
|
}
|
|
@@ -437,7 +449,7 @@ int yep_dom_on_event(void* ctx, const yep_event* ev) {
|
|
|
437
449
|
if (id == UINT32_MAX) {
|
|
438
450
|
return -1;
|
|
439
451
|
}
|
|
440
|
-
|
|
452
|
+
dom_set_value(d, id, &ev->value, ev->borrowed);
|
|
441
453
|
d->nodes[id].target = target;
|
|
442
454
|
return dom_place(d, id);
|
|
443
455
|
}
|
|
@@ -902,9 +914,11 @@ int dom_on_flow_build(void* ctx, const char* p, size_t open, size_t len, uint32_
|
|
|
902
914
|
d->nodes[sid].value =
|
|
903
915
|
yep_dom_str_commit(d, yep_finish_double_into(p, (uint32_t)(t.at + 1),
|
|
904
916
|
(uint32_t)(t.end - 1), dst, span));
|
|
917
|
+
d->nodes[sid].no_exotic = 1; /* JSON-class strings: the
|
|
918
|
+
walker rejected every control byte */
|
|
905
919
|
} else {
|
|
906
920
|
yep_view v = {p + t.at + 1, (uint32_t)(t.end - t.at - 2)};
|
|
907
|
-
|
|
921
|
+
dom_set_value(d, sid, &v, 1);
|
|
908
922
|
}
|
|
909
923
|
if (dom_place(d, sid) != 0) {
|
|
910
924
|
goto fail;
|
|
@@ -918,6 +932,8 @@ int dom_on_flow_build(void* ctx, const char* p, size_t open, size_t len, uint32_
|
|
|
918
932
|
}
|
|
919
933
|
yep_view v = {p + t.at, (uint32_t)(t.end - t.at)};
|
|
920
934
|
d->nodes[sid].value = dom_str_in(d, &v, 1);
|
|
935
|
+
d->nodes[sid].no_exotic = 1; /* the walker validated the
|
|
936
|
+
span as a JSON number or literal */
|
|
921
937
|
if (t.cls == '#') {
|
|
922
938
|
d->nodes[sid].tag_id = yep_resolve_number(r, t.is_float);
|
|
923
939
|
} else if (d->flow_strict) {
|
|
@@ -1087,13 +1103,15 @@ int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
|
1087
1103
|
}
|
|
1088
1104
|
d->nodes[id].value = yep_dom_str_commit(
|
|
1089
1105
|
d, yep_finish_double_into(src, off, off + len, dst, len));
|
|
1106
|
+
d->nodes[id].no_exotic = 1; /* JSON-class: escapes
|
|
1107
|
+
only, the walker's law */
|
|
1090
1108
|
} else {
|
|
1091
1109
|
yep_view v = {src + off, len};
|
|
1092
|
-
|
|
1110
|
+
dom_set_value(d, id, &v, 1);
|
|
1093
1111
|
}
|
|
1094
1112
|
} else {
|
|
1095
1113
|
yep_view v = {src + off, len};
|
|
1096
|
-
|
|
1114
|
+
dom_set_value(d, id, &v, 1);
|
|
1097
1115
|
}
|
|
1098
1116
|
d->nodes[id].tag_id = tag;
|
|
1099
1117
|
if (dom_place(d, id) != 0) {
|
|
@@ -1141,7 +1159,7 @@ int dom_on_block_open(void* ctx, const yep_view* key, uint32_t line, uint16_t ke
|
|
|
1141
1159
|
d->depth--;
|
|
1142
1160
|
return -1;
|
|
1143
1161
|
}
|
|
1144
|
-
|
|
1162
|
+
dom_set_value(d, kid, key, 1);
|
|
1145
1163
|
d->nodes[kid].tag_id = r->resolve(NULL, key->p, key->len);
|
|
1146
1164
|
return dom_place(d, kid) == 0 ? 1 : -1;
|
|
1147
1165
|
}
|
|
@@ -1156,7 +1174,7 @@ int dom_on_block_item(void* ctx, const yep_block_value* v) {
|
|
|
1156
1174
|
if (vid == UINT32_MAX) {
|
|
1157
1175
|
return -1;
|
|
1158
1176
|
}
|
|
1159
|
-
|
|
1177
|
+
dom_set_value(d, vid, &v->value, v->borrowed);
|
|
1160
1178
|
d->nodes[vid].tag_id = r->resolve(NULL, v->value.p, v->value.len);
|
|
1161
1179
|
return dom_place(d, vid) == 0 ? 1 : -1;
|
|
1162
1180
|
}
|
|
@@ -1183,7 +1201,7 @@ int dom_on_block_pair(void* ctx, const yep_view* key, const yep_block_value* v,
|
|
|
1183
1201
|
if (kid == UINT32_MAX) {
|
|
1184
1202
|
return -1;
|
|
1185
1203
|
}
|
|
1186
|
-
|
|
1204
|
+
dom_set_value(d, kid, key, 1);
|
|
1187
1205
|
d->nodes[kid].tag_id = r->resolve(NULL, key->p, key->len);
|
|
1188
1206
|
if (dom_place(d, kid) != 0) {
|
|
1189
1207
|
return -1;
|
|
@@ -1193,7 +1211,7 @@ int dom_on_block_pair(void* ctx, const yep_view* key, const yep_block_value* v,
|
|
|
1193
1211
|
if (vid == UINT32_MAX) {
|
|
1194
1212
|
return -1;
|
|
1195
1213
|
}
|
|
1196
|
-
|
|
1214
|
+
dom_set_value(d, vid, &v->value, 1); /* borrowed — the
|
|
1197
1215
|
event path stamps it too now (73); the gates compare decoded
|
|
1198
1216
|
views, not arena placement */
|
|
1199
1217
|
d->nodes[vid].target = target;
|
|
@@ -1205,7 +1223,7 @@ int dom_on_block_pair(void* ctx, const yep_view* key, const yep_block_value* v,
|
|
|
1205
1223
|
if (vid == UINT32_MAX) {
|
|
1206
1224
|
return -1;
|
|
1207
1225
|
}
|
|
1208
|
-
|
|
1226
|
+
dom_set_value(d, vid, &v->value, v->borrowed);
|
|
1209
1227
|
d->nodes[vid].tag_id = r->resolve(NULL, v->value.p, v->value.len);
|
|
1210
1228
|
if (anchored && v->anchor_id != 0 && dom_anchor_set(d, v->anchor_id, vid) != 0) {
|
|
1211
1229
|
return -1;
|
|
@@ -1224,7 +1242,7 @@ int dom_on_scalar(void* ctx, const yep_view* value, const yep_view* tag, const y
|
|
|
1224
1242
|
if (id == UINT32_MAX) {
|
|
1225
1243
|
return -1;
|
|
1226
1244
|
}
|
|
1227
|
-
|
|
1245
|
+
dom_set_value(d, id, value, borrowed);
|
|
1228
1246
|
d->nodes[id].tag_id = tag_id;
|
|
1229
1247
|
if (anchor_id != 0 && dom_anchor_set(d, anchor_id, id) != 0) {
|
|
1230
1248
|
return -1;
|
|
@@ -60,12 +60,16 @@ typedef struct yep_sview {
|
|
|
60
60
|
* fields left the record — they live in the DOM's lazily-built side
|
|
61
61
|
* tables (parse pays nothing; dom.c owns them). */
|
|
62
62
|
typedef struct yep_dnode {
|
|
63
|
-
uint8_t kind : 2;
|
|
64
|
-
uint8_t style : 3;
|
|
65
|
-
uint8_t flow : 1;
|
|
66
|
-
uint8_t implicit : 1;
|
|
67
|
-
uint8_t
|
|
68
|
-
|
|
63
|
+
uint8_t kind : 2; /* yep_dom_kind (0-3) */
|
|
64
|
+
uint8_t style : 3; /* yep_scalar_style (0-5) */
|
|
65
|
+
uint8_t flow : 1; /* collection opened in flow style */
|
|
66
|
+
uint8_t implicit : 1; /* scalar written without quotes/tag */
|
|
67
|
+
uint8_t no_exotic : 1; /* value verified clean of the dq-forced
|
|
68
|
+
* byte set (#352 lane 2): the serializer's
|
|
69
|
+
* sc_route skips its rescan. Set at every
|
|
70
|
+
* value assignment; 0 = unknown = scan. */
|
|
71
|
+
uint8_t tag_id; /* resolved tag (resolve/resolver.h) */
|
|
72
|
+
uint32_t first_child; /* child link (mappings: key,value,key,value…) */
|
|
69
73
|
uint32_t last_child;
|
|
70
74
|
uint32_t next_sibling;
|
|
71
75
|
uint32_t count; /* children (mappings: 2 × pairs) */
|
|
@@ -486,31 +486,12 @@ enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL, Y
|
|
|
486
486
|
* The block map's explicit-key promotion consults the same predicate:
|
|
487
487
|
* a key whose content demotes to DQ re-parses as a QUOTED key and must
|
|
488
488
|
* ride simple, or round two's render differs (the nightly's round 118). */
|
|
489
|
-
static int sc_dq_forced(const char* p, uint32_t len) {
|
|
490
|
-
for (uint32_t k = 0; k + 1 < len; k++) {
|
|
491
|
-
if ((unsigned char)p[k] == 0xc2 && (unsigned char)p[k + 1] == 0x85) {
|
|
492
|
-
return 1;
|
|
493
|
-
}
|
|
494
|
-
/* LS/PS are breaks like NEL: only their escapes are stable */
|
|
495
|
-
if ((unsigned char)p[k] == 0xe2 && k + 2 < len && (unsigned char)p[k + 1] == 0x80 &&
|
|
496
|
-
((unsigned char)p[k + 2] == 0xa8 || (unsigned char)p[k + 2] == 0xa9)) {
|
|
497
|
-
return 1;
|
|
498
|
-
}
|
|
499
|
-
if ((unsigned char)p[k] == 0xef && k + 2 < len && (unsigned char)p[k + 1] == 0xbb &&
|
|
500
|
-
(unsigned char)p[k + 2] == 0xbf) {
|
|
501
|
-
return 1;
|
|
502
|
-
}
|
|
503
|
-
if ((unsigned char)p[k] == 0xef && k + 2 < len && (unsigned char)p[k + 1] == 0xbf &&
|
|
504
|
-
((unsigned char)p[k + 2] == 0xbe || (unsigned char)p[k + 2] == 0xbf)) {
|
|
505
|
-
return 1; /* noncharacter: only its escape re-parses */
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return 0;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
489
|
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in,
|
|
512
|
-
int has_tag) {
|
|
513
|
-
|
|
490
|
+
int has_tag, int no_exotic) {
|
|
491
|
+
/* the parse-time fact bit (#352 lane 2): a dnode verified clean of
|
|
492
|
+
* the dq-forced set skips the rescan — 0 (mutation-built or JSON
|
|
493
|
+
* classes) keeps the scan */
|
|
494
|
+
if (!no_exotic && yep_scan_dq_forced(p, len)) {
|
|
514
495
|
return YEP_SC_DQ;
|
|
515
496
|
}
|
|
516
497
|
if (w->in_flow && len > 0) {
|
|
@@ -641,14 +622,14 @@ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int a
|
|
|
641
622
|
* memoized long scalars, in visit order either way. */
|
|
642
623
|
if (w->sc_dec != NULL && len >= YEP_SC_MEMO_MIN) {
|
|
643
624
|
if (w->dry) {
|
|
644
|
-
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0);
|
|
625
|
+
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0, n->no_exotic);
|
|
645
626
|
w->sc_dec[w->sc_i] = route;
|
|
646
627
|
} else {
|
|
647
628
|
route = w->sc_dec[w->sc_i];
|
|
648
629
|
}
|
|
649
630
|
w->sc_i++;
|
|
650
631
|
} else {
|
|
651
|
-
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0);
|
|
632
|
+
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0, n->no_exotic);
|
|
652
633
|
}
|
|
653
634
|
switch (route) {
|
|
654
635
|
case YEP_SC_NOTHING:
|
|
@@ -1190,7 +1171,7 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1190
1171
|
* anyway (the nightly's run 38177622733, ":\n! |-\n r\n:") */
|
|
1191
1172
|
if (!complex_key && kn->style >= 4 && kn->style <= 5 && kn->value.len > 0 &&
|
|
1192
1173
|
kn->tag.len > 0 && memchr(wv(w, kn->value).p, '\n', wv(w, kn->value).len) == NULL &&
|
|
1193
|
-
!
|
|
1174
|
+
(kn->no_exotic || !yep_scan_dq_forced(wv(w, kn->value).p, wv(w, kn->value).len))) {
|
|
1194
1175
|
/* a TAGGED SINGLE-LINE literal/folded key keeps the block
|
|
1195
1176
|
* form in sc_route (the #290 law), so the value separator
|
|
1196
1177
|
* would glue onto the block's CONTENT line (colons are
|
|
@@ -1403,6 +1384,560 @@ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key)
|
|
|
1403
1384
|
emit_scalar(w, n, parent_col, as_key);
|
|
1404
1385
|
}
|
|
1405
1386
|
|
|
1387
|
+
/* ============================================================
|
|
1388
|
+
* yeptris-ruby#322: the marshal READER — Ruby Marshal.dump bytes
|
|
1389
|
+
* to JSON text in one C call (no per-scalar crossings, no object
|
|
1390
|
+
* reconstruction). The format facts mirror marshal.c's emission
|
|
1391
|
+
* contract (empirically pinned against Ruby 3.4): w_long small form
|
|
1392
|
+
* x==0 -> 0x00, |x| <= 122 -> sign*(|x|+5), size form tag +-k with
|
|
1393
|
+
* LE payload (2^8k - m for negatives); strings carry 'I' ivar
|
|
1394
|
+
* wrappers; arrays/hashes/strings/floats register for '@' links.
|
|
1395
|
+
* A JSON dump duplicates linked re-references (cycled or shared
|
|
1396
|
+
* subtrees re-walk from the registered position, depth-guarded).
|
|
1397
|
+
* The JSON-irrelevant constructs ('o' objects, 'S' structs, 'u'
|
|
1398
|
+
* user marshal, 'l' bignums beyond i64, non-finite floats) return
|
|
1399
|
+
* UNSUPPORTED — the binding falls back to its per-node walk.
|
|
1400
|
+
* ============================================================ */
|
|
1401
|
+
|
|
1402
|
+
#include <inttypes.h>
|
|
1403
|
+
|
|
1404
|
+
typedef struct {
|
|
1405
|
+
const char* p;
|
|
1406
|
+
size_t len;
|
|
1407
|
+
size_t at;
|
|
1408
|
+
} mj_stream;
|
|
1409
|
+
|
|
1410
|
+
typedef struct {
|
|
1411
|
+
uint32_t kind; /* 1 seq, 2 map */
|
|
1412
|
+
uint32_t items;
|
|
1413
|
+
} mj_frame;
|
|
1414
|
+
|
|
1415
|
+
typedef struct {
|
|
1416
|
+
yep_writer* w;
|
|
1417
|
+
mj_stream s;
|
|
1418
|
+
mj_frame* frames;
|
|
1419
|
+
int depth;
|
|
1420
|
+
int compact;
|
|
1421
|
+
/* the symbol table: ';' links resolve to these names */
|
|
1422
|
+
struct {
|
|
1423
|
+
const char* p;
|
|
1424
|
+
uint32_t len;
|
|
1425
|
+
}* syms;
|
|
1426
|
+
size_t syms_n, syms_cap;
|
|
1427
|
+
/* the object table: '@' links re-walk from these positions */
|
|
1428
|
+
struct {
|
|
1429
|
+
size_t pos;
|
|
1430
|
+
uint32_t chain_pos;
|
|
1431
|
+
}* objs;
|
|
1432
|
+
size_t objs_n, objs_cap;
|
|
1433
|
+
size_t* chain; /* the active positions (cycle guard) */
|
|
1434
|
+
size_t chain_n, chain_cap;
|
|
1435
|
+
int failed; /* 0 ok, YEPTRIS_ERROR_UNSUPPORTED */
|
|
1436
|
+
} mj_ctx;
|
|
1437
|
+
|
|
1438
|
+
static int mj_fail(mj_ctx* c) {
|
|
1439
|
+
c->failed = YEPTRIS_ERROR_UNSUPPORTED;
|
|
1440
|
+
return -1;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
static int mj_byte(mj_ctx* c, unsigned char* out) {
|
|
1444
|
+
if (c->s.at >= c->s.len)
|
|
1445
|
+
return mj_fail(c);
|
|
1446
|
+
*out = (unsigned char)c->s.p[c->s.at++];
|
|
1447
|
+
return 0;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/* the w_long inverse — mirrors m_w_long's encode exactly:
|
|
1451
|
+
* 0x00 -> 0; 1..4 -> positive size form k=b; 5..127 -> small +(
|
|
1452
|
+
* b-5); 129..250 -> small -(251-b); 251..255 -> negative size form
|
|
1453
|
+
* k=256-b (payload = LE(2^(8k) - m)); 128/248..250 unreachable (the
|
|
1454
|
+
* writer takes the small form first). */
|
|
1455
|
+
static int mj_long(mj_ctx* c, int64_t* out) {
|
|
1456
|
+
unsigned char b;
|
|
1457
|
+
if (mj_byte(c, &b) != 0)
|
|
1458
|
+
return -1;
|
|
1459
|
+
if (b == 0) {
|
|
1460
|
+
*out = 0;
|
|
1461
|
+
return 0;
|
|
1462
|
+
}
|
|
1463
|
+
if (b <= 4) { /* positive size form, k = b */
|
|
1464
|
+
int64_t v = 0;
|
|
1465
|
+
for (int i = 0; i < (int)b; i++) {
|
|
1466
|
+
unsigned char d;
|
|
1467
|
+
if (mj_byte(c, &d) != 0)
|
|
1468
|
+
return -1;
|
|
1469
|
+
v |= (int64_t)d << (8 * i);
|
|
1470
|
+
}
|
|
1471
|
+
*out = v;
|
|
1472
|
+
return 0;
|
|
1473
|
+
}
|
|
1474
|
+
if (b <= 127) {
|
|
1475
|
+
*out = (int64_t)b - 5;
|
|
1476
|
+
return 0;
|
|
1477
|
+
}
|
|
1478
|
+
if (b >= 129 && b <= 250) {
|
|
1479
|
+
*out = (int64_t)b - 251;
|
|
1480
|
+
return 0;
|
|
1481
|
+
}
|
|
1482
|
+
{ /* 251..255: negative size form */
|
|
1483
|
+
int bytes = 256 - (int)b;
|
|
1484
|
+
int64_t v = 0;
|
|
1485
|
+
for (int i = 0; i < bytes; i++) {
|
|
1486
|
+
unsigned char d;
|
|
1487
|
+
if (mj_byte(c, &d) != 0)
|
|
1488
|
+
return -1;
|
|
1489
|
+
v |= (int64_t)d << (8 * i);
|
|
1490
|
+
}
|
|
1491
|
+
*out = v - ((int64_t)1 << (8 * bytes));
|
|
1492
|
+
return 0;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
static const char* mj_sym_name(mj_ctx* c, int64_t idx, uint32_t* len_out) {
|
|
1497
|
+
if (idx < 0 || (size_t)idx >= c->syms_n) {
|
|
1498
|
+
mj_fail(c);
|
|
1499
|
+
return NULL;
|
|
1500
|
+
}
|
|
1501
|
+
*len_out = c->syms[idx].len;
|
|
1502
|
+
return c->syms[idx].p;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
static int mj_ivar_skip(mj_ctx* c);
|
|
1506
|
+
static int mj_link_key(mj_ctx* c, int64_t idx);
|
|
1507
|
+
|
|
1508
|
+
static int mj_value(mj_ctx* c);
|
|
1509
|
+
|
|
1510
|
+
static int mj_string_body(mj_ctx* c, int64_t len) {
|
|
1511
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1512
|
+
return mj_fail(c);
|
|
1513
|
+
emit_dq_json(c->w, c->s.p + c->s.at, (uint32_t)len);
|
|
1514
|
+
c->s.at += (size_t)len;
|
|
1515
|
+
return 0;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/* a symbol: ':' + long(len) + bytes — registers in the table; the
|
|
1519
|
+
* name is emitted as a JSON string when emit is set */
|
|
1520
|
+
static int mj_symbol(mj_ctx* c, int emit) {
|
|
1521
|
+
int64_t len;
|
|
1522
|
+
if (mj_long(c, &len) != 0)
|
|
1523
|
+
return -1;
|
|
1524
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1525
|
+
return mj_fail(c);
|
|
1526
|
+
const char* name = c->s.p + c->s.at;
|
|
1527
|
+
uint32_t nlen = (uint32_t)len;
|
|
1528
|
+
c->s.at += (size_t)len;
|
|
1529
|
+
if (c->syms_n == c->syms_cap) {
|
|
1530
|
+
c->syms_cap = c->syms_cap ? c->syms_cap * 2 : 16;
|
|
1531
|
+
c->syms = (void*)realloc(c->syms, c->syms_cap * sizeof(*c->syms));
|
|
1532
|
+
if (!c->syms)
|
|
1533
|
+
return mj_fail(c);
|
|
1534
|
+
}
|
|
1535
|
+
c->syms[c->syms_n].p = name;
|
|
1536
|
+
c->syms[c->syms_n].len = nlen;
|
|
1537
|
+
c->syms_n++;
|
|
1538
|
+
if (emit) {
|
|
1539
|
+
emit_dq_json(c->w, name, nlen);
|
|
1540
|
+
}
|
|
1541
|
+
return 0;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/* an ivar wrapper 'I': the value then long(count) then count pairs of
|
|
1545
|
+
* (symbol, value) — read-and-discard (encodings and such) */
|
|
1546
|
+
static int mj_ivar_skip(mj_ctx* c);
|
|
1547
|
+
|
|
1548
|
+
static int mj_discard_value(mj_ctx* c) {
|
|
1549
|
+
unsigned char t;
|
|
1550
|
+
if (mj_byte(c, &t) != 0)
|
|
1551
|
+
return -1;
|
|
1552
|
+
switch (t) {
|
|
1553
|
+
case '0':
|
|
1554
|
+
case 'T':
|
|
1555
|
+
case 'F':
|
|
1556
|
+
return 0;
|
|
1557
|
+
case 'i': {
|
|
1558
|
+
int64_t v;
|
|
1559
|
+
return mj_long(c, &v);
|
|
1560
|
+
}
|
|
1561
|
+
case '"': {
|
|
1562
|
+
int64_t len;
|
|
1563
|
+
if (mj_long(c, &len) != 0)
|
|
1564
|
+
return -1;
|
|
1565
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1566
|
+
return mj_fail(c);
|
|
1567
|
+
c->s.at += (size_t)len;
|
|
1568
|
+
return 0;
|
|
1569
|
+
}
|
|
1570
|
+
case ':':
|
|
1571
|
+
return mj_symbol(c, 0);
|
|
1572
|
+
case ';': {
|
|
1573
|
+
int64_t idx;
|
|
1574
|
+
if (mj_long(c, &idx) != 0)
|
|
1575
|
+
return -1;
|
|
1576
|
+
return mj_sym_name(c, idx, &(uint32_t){0}) ? 0 : -1;
|
|
1577
|
+
}
|
|
1578
|
+
case 'I':
|
|
1579
|
+
return mj_ivar_skip(c);
|
|
1580
|
+
default:
|
|
1581
|
+
return mj_fail(c); /* ivars on JSON trees carry symbol/E
|
|
1582
|
+
pairs only in practice */
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/* the count + (symbol, value) pairs that follow an 'I' wrapper's
|
|
1587
|
+
* wrapped value */
|
|
1588
|
+
static int mj_ivar_pairs(mj_ctx* c) {
|
|
1589
|
+
int64_t count;
|
|
1590
|
+
if (mj_long(c, &count) != 0)
|
|
1591
|
+
return -1;
|
|
1592
|
+
for (int64_t i = 0; i < count; i++) {
|
|
1593
|
+
if (mj_discard_value(c) != 0)
|
|
1594
|
+
return -1; /* the name symbol */
|
|
1595
|
+
if (mj_discard_value(c) != 0)
|
|
1596
|
+
return -1; /* the ivar value */
|
|
1597
|
+
}
|
|
1598
|
+
return 0;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/* a standalone 'I' wrapper: the wrapped value, then the pairs */
|
|
1602
|
+
static int mj_ivar_skip(mj_ctx* c) {
|
|
1603
|
+
if (mj_discard_value(c) != 0)
|
|
1604
|
+
return -1; /* the wrapped value */
|
|
1605
|
+
return mj_ivar_pairs(c);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
static int mj_value(mj_ctx* c) {
|
|
1609
|
+
unsigned char t;
|
|
1610
|
+
size_t tpos = c->s.at;
|
|
1611
|
+
if (mj_byte(c, &t) != 0)
|
|
1612
|
+
return -1;
|
|
1613
|
+
/* arrays, hashes, strings and floats register for '@' links (the
|
|
1614
|
+
* marshal.c emission contract, 0-based stream order); integers,
|
|
1615
|
+
* bignums, symbols and immediates never register */
|
|
1616
|
+
if (t == '[' || t == '{' || t == '"' || t == 'f') {
|
|
1617
|
+
if (c->objs_n == c->objs_cap) {
|
|
1618
|
+
c->objs_cap = c->objs_cap ? c->objs_cap * 2 : 16;
|
|
1619
|
+
c->objs = (void*)realloc(c->objs, c->objs_cap * sizeof(*c->objs));
|
|
1620
|
+
if (!c->objs)
|
|
1621
|
+
return mj_fail(c);
|
|
1622
|
+
}
|
|
1623
|
+
c->objs[c->objs_n].pos = tpos;
|
|
1624
|
+
c->objs[c->objs_n].chain_pos = 0;
|
|
1625
|
+
c->objs_n++;
|
|
1626
|
+
}
|
|
1627
|
+
switch (t) {
|
|
1628
|
+
case '0':
|
|
1629
|
+
wr_put(c->w, "null", 4);
|
|
1630
|
+
return 0;
|
|
1631
|
+
case 'T':
|
|
1632
|
+
wr_put(c->w, "true", 4);
|
|
1633
|
+
return 0;
|
|
1634
|
+
case 'F':
|
|
1635
|
+
wr_put(c->w, "false", 5);
|
|
1636
|
+
return 0;
|
|
1637
|
+
case 'i': {
|
|
1638
|
+
int64_t v;
|
|
1639
|
+
if (mj_long(c, &v) != 0)
|
|
1640
|
+
return -1;
|
|
1641
|
+
char buf[24];
|
|
1642
|
+
int n = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
1643
|
+
wr_put(c->w, buf, (uint32_t)n);
|
|
1644
|
+
return 0;
|
|
1645
|
+
}
|
|
1646
|
+
case '"': {
|
|
1647
|
+
int64_t len;
|
|
1648
|
+
if (mj_long(c, &len) != 0)
|
|
1649
|
+
return -1;
|
|
1650
|
+
int rc = mj_string_body(c, len);
|
|
1651
|
+
if (rc != 0)
|
|
1652
|
+
return -1;
|
|
1653
|
+
/* the 'I' wrapper follows when the string carries ivars: the
|
|
1654
|
+
* value is already parsed — pairs only */
|
|
1655
|
+
if (c->s.at < c->s.len && c->s.p[c->s.at] == 'I') {
|
|
1656
|
+
c->s.at++;
|
|
1657
|
+
if (mj_ivar_pairs(c) != 0)
|
|
1658
|
+
return -1;
|
|
1659
|
+
}
|
|
1660
|
+
return 0;
|
|
1661
|
+
}
|
|
1662
|
+
case 'I': {
|
|
1663
|
+
/* the wrapped value IS the payload: re-dispatch into it, then
|
|
1664
|
+
* the ivar pairs ride */
|
|
1665
|
+
if (mj_value(c) != 0)
|
|
1666
|
+
return -1;
|
|
1667
|
+
return mj_ivar_pairs(c);
|
|
1668
|
+
}
|
|
1669
|
+
case 'l': {
|
|
1670
|
+
/* the bignum: sign byte + w_long(limb count) + LE 16-bit
|
|
1671
|
+
* limbs; the i64-representable range emits, wider falls back */
|
|
1672
|
+
unsigned char sign;
|
|
1673
|
+
if (mj_byte(c, &sign) != 0)
|
|
1674
|
+
return -1;
|
|
1675
|
+
if (sign != 0x2b && sign != 0x2d)
|
|
1676
|
+
return mj_fail(c); /* + / - */
|
|
1677
|
+
int64_t words;
|
|
1678
|
+
if (mj_long(c, &words) != 0)
|
|
1679
|
+
return -1;
|
|
1680
|
+
if (words < 0 || words > 4)
|
|
1681
|
+
return mj_fail(c);
|
|
1682
|
+
int64_t v = 0;
|
|
1683
|
+
for (int64_t i = 0; i < words; i++) {
|
|
1684
|
+
for (int b = 0; b < 2; b++) {
|
|
1685
|
+
unsigned char d;
|
|
1686
|
+
if (mj_byte(c, &d) != 0)
|
|
1687
|
+
return -1;
|
|
1688
|
+
v |= (int64_t)d << (16 * i + 8 * b);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
if (sign == 0x2d) {
|
|
1692
|
+
v = -v;
|
|
1693
|
+
}
|
|
1694
|
+
char buf[24];
|
|
1695
|
+
int n = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
1696
|
+
wr_put(c->w, buf, (uint32_t)n);
|
|
1697
|
+
return 0;
|
|
1698
|
+
}
|
|
1699
|
+
case 'f': {
|
|
1700
|
+
int64_t len;
|
|
1701
|
+
if (mj_long(c, &len) != 0)
|
|
1702
|
+
return -1;
|
|
1703
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1704
|
+
return mj_fail(c);
|
|
1705
|
+
char tmp[64];
|
|
1706
|
+
uint32_t tl = (uint32_t)len < sizeof tmp - 1 ? (uint32_t)len : (uint32_t)sizeof tmp - 1;
|
|
1707
|
+
memcpy(tmp, c->s.p + c->s.at, tl);
|
|
1708
|
+
tmp[tl] = '\0';
|
|
1709
|
+
c->s.at += (size_t)len;
|
|
1710
|
+
if (strcmp(tmp, "inf") == 0 || strcmp(tmp, "-inf") == 0 || strcmp(tmp, "nan") == 0) {
|
|
1711
|
+
return mj_fail(c); /* JSON has no inf/nan spelling */
|
|
1712
|
+
}
|
|
1713
|
+
double d = strtod(tmp, NULL);
|
|
1714
|
+
char buf[40];
|
|
1715
|
+
int wl = yep_d2s_json(d, buf);
|
|
1716
|
+
wr_put(c->w, buf, (uint32_t)wl);
|
|
1717
|
+
return 0;
|
|
1718
|
+
}
|
|
1719
|
+
case '[':
|
|
1720
|
+
case '{': {
|
|
1721
|
+
int64_t count;
|
|
1722
|
+
if (mj_long(c, &count) != 0)
|
|
1723
|
+
return -1;
|
|
1724
|
+
if (count < 0)
|
|
1725
|
+
return mj_fail(c);
|
|
1726
|
+
if (c->depth >= 512)
|
|
1727
|
+
return mj_fail(c);
|
|
1728
|
+
c->depth++;
|
|
1729
|
+
wr_byte(c->w, t == '[' ? '[' : '{');
|
|
1730
|
+
for (int64_t i = 0; i < count; i++) {
|
|
1731
|
+
if (i > 0) {
|
|
1732
|
+
wr_put(c->w, c->compact ? "," : ", ", c->compact ? 1 : 2);
|
|
1733
|
+
}
|
|
1734
|
+
if (t == '{') {
|
|
1735
|
+
/* the key: a string or a symbol — JSON keys are strings */
|
|
1736
|
+
unsigned char kt;
|
|
1737
|
+
if (mj_byte(c, &kt) != 0)
|
|
1738
|
+
return -1;
|
|
1739
|
+
if (kt == 'I') {
|
|
1740
|
+
/* an ivar-wrapped key: the inner value emits, then
|
|
1741
|
+
* the pairs (the 'I' is consumed) */
|
|
1742
|
+
if (mj_value(c) != 0)
|
|
1743
|
+
return -1;
|
|
1744
|
+
if (mj_ivar_pairs(c) != 0)
|
|
1745
|
+
return -1;
|
|
1746
|
+
} else if (kt == '@') {
|
|
1747
|
+
/* a linked key: re-emit the target (#322's corpus
|
|
1748
|
+
* links the users' repeated key strings) */
|
|
1749
|
+
int64_t idx;
|
|
1750
|
+
if (mj_long(c, &idx) != 0)
|
|
1751
|
+
return -1;
|
|
1752
|
+
if (mj_link_key(c, idx) != 0)
|
|
1753
|
+
return -1;
|
|
1754
|
+
} else if (kt == '"') {
|
|
1755
|
+
int64_t len;
|
|
1756
|
+
if (mj_long(c, &len) != 0)
|
|
1757
|
+
return -1;
|
|
1758
|
+
int rc = mj_string_body(c, len);
|
|
1759
|
+
if (rc != 0)
|
|
1760
|
+
return -1;
|
|
1761
|
+
if (c->s.at < c->s.len && c->s.p[c->s.at] == 'I') {
|
|
1762
|
+
c->s.at++;
|
|
1763
|
+
if (mj_ivar_pairs(c) != 0)
|
|
1764
|
+
return -1;
|
|
1765
|
+
}
|
|
1766
|
+
} else if (kt == ':') {
|
|
1767
|
+
if (mj_symbol(c, 1) != 0)
|
|
1768
|
+
return -1;
|
|
1769
|
+
} else if (kt == ';') {
|
|
1770
|
+
int64_t idx;
|
|
1771
|
+
if (mj_long(c, &idx) != 0)
|
|
1772
|
+
return -1;
|
|
1773
|
+
uint32_t nlen;
|
|
1774
|
+
const char* nm = mj_sym_name(c, idx, &nlen);
|
|
1775
|
+
if (!nm)
|
|
1776
|
+
return -1;
|
|
1777
|
+
emit_dq_json(c->w, nm, nlen);
|
|
1778
|
+
} else {
|
|
1779
|
+
return mj_fail(c);
|
|
1780
|
+
}
|
|
1781
|
+
wr_put(c->w, c->compact ? ":" : ": ", c->compact ? 1 : 2);
|
|
1782
|
+
if (mj_value(c) != 0)
|
|
1783
|
+
return -1;
|
|
1784
|
+
} else {
|
|
1785
|
+
if (mj_value(c) != 0)
|
|
1786
|
+
return -1;
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
c->depth--;
|
|
1790
|
+
wr_byte(c->w, t == '[' ? ']' : '}');
|
|
1791
|
+
return 0;
|
|
1792
|
+
}
|
|
1793
|
+
case ';': {
|
|
1794
|
+
int64_t idx;
|
|
1795
|
+
if (mj_long(c, &idx) != 0)
|
|
1796
|
+
return -1;
|
|
1797
|
+
uint32_t nlen;
|
|
1798
|
+
const char* nm = mj_sym_name(c, idx, &nlen);
|
|
1799
|
+
if (!nm)
|
|
1800
|
+
return -1;
|
|
1801
|
+
emit_dq_json(c->w, nm, nlen);
|
|
1802
|
+
return 0;
|
|
1803
|
+
}
|
|
1804
|
+
case ':': {
|
|
1805
|
+
/* a bare symbol VALUE: emitted as its name string */
|
|
1806
|
+
if (mj_symbol(c, 1) != 0)
|
|
1807
|
+
return -1;
|
|
1808
|
+
return 0;
|
|
1809
|
+
}
|
|
1810
|
+
case '@': {
|
|
1811
|
+
/* an object link: re-walk from the registered position */
|
|
1812
|
+
int64_t idx;
|
|
1813
|
+
if (mj_long(c, &idx) != 0)
|
|
1814
|
+
return -1;
|
|
1815
|
+
if (idx < 0 || (size_t)idx >= c->objs_n)
|
|
1816
|
+
return mj_fail(c);
|
|
1817
|
+
size_t save = c->s.at;
|
|
1818
|
+
c->s.at = c->objs[idx].pos;
|
|
1819
|
+
/* cycle guard: the link target is in the active chain */
|
|
1820
|
+
for (size_t k = 0; k < c->chain_n; k++) {
|
|
1821
|
+
if (c->chain[k] == c->objs[idx].pos) {
|
|
1822
|
+
c->s.at = save;
|
|
1823
|
+
return mj_fail(c);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
if (c->chain_n == c->chain_cap) {
|
|
1827
|
+
c->chain_cap = c->chain_cap ? c->chain_cap * 2 : 16;
|
|
1828
|
+
c->chain = (size_t*)realloc(c->chain, c->chain_cap * sizeof(*c->chain));
|
|
1829
|
+
if (!c->chain) {
|
|
1830
|
+
c->s.at = save;
|
|
1831
|
+
return mj_fail(c);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
c->chain[c->chain_n++] = c->objs[idx].pos;
|
|
1835
|
+
int rc = mj_value(c);
|
|
1836
|
+
c->chain_n--;
|
|
1837
|
+
c->s.at = save;
|
|
1838
|
+
if (rc != 0)
|
|
1839
|
+
return -1;
|
|
1840
|
+
return 0;
|
|
1841
|
+
}
|
|
1842
|
+
default:
|
|
1843
|
+
return mj_fail(c);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
/* an '@' object link in KEY position: the link's target re-emitted as
|
|
1848
|
+
* the key string (Ruby links repeated key objects — the users' "id"
|
|
1849
|
+
* keys, yeptris-ruby#322's corpus). The link consumes no stream
|
|
1850
|
+
* bytes; the key walk resumes at the caller's cursor. */
|
|
1851
|
+
static int mj_link_key(mj_ctx* c, int64_t idx) {
|
|
1852
|
+
if (idx < 0 || (size_t)idx >= c->objs_n)
|
|
1853
|
+
return mj_fail(c);
|
|
1854
|
+
size_t save = c->s.at;
|
|
1855
|
+
for (size_t k = 0; k < c->chain_n; k++) {
|
|
1856
|
+
if (c->chain[k] == c->objs[idx].pos) {
|
|
1857
|
+
c->s.at = save;
|
|
1858
|
+
return mj_fail(c);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
c->s.at = c->objs[idx].pos;
|
|
1862
|
+
if (c->chain_n == c->chain_cap) {
|
|
1863
|
+
c->chain_cap = c->chain_cap ? c->chain_cap * 2 : 16;
|
|
1864
|
+
c->chain = (size_t*)realloc(c->chain, c->chain_cap * sizeof(*c->chain));
|
|
1865
|
+
if (!c->chain) {
|
|
1866
|
+
c->s.at = save;
|
|
1867
|
+
return mj_fail(c);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
c->chain[c->chain_n++] = c->objs[idx].pos;
|
|
1871
|
+
int rc = mj_value(c);
|
|
1872
|
+
c->chain_n--;
|
|
1873
|
+
c->s.at = save;
|
|
1874
|
+
return rc;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
YEPTRIS_API YeptrisStatus yeptris_json_from_marshal(const char* m, size_t m_len, int compact,
|
|
1878
|
+
char** out, size_t* out_len) {
|
|
1879
|
+
if (out == NULL || out_len == NULL)
|
|
1880
|
+
return YEPTRIS_ERROR_ARG;
|
|
1881
|
+
*out = NULL;
|
|
1882
|
+
*out_len = 0;
|
|
1883
|
+
if (m == NULL || m_len < 2 || (unsigned char)m[0] != 0x04 || (unsigned char)m[1] != 0x08) {
|
|
1884
|
+
return YEPTRIS_ERROR_UNSUPPORTED;
|
|
1885
|
+
}
|
|
1886
|
+
yep_emitter em;
|
|
1887
|
+
memset(&em, 0, sizeof(em));
|
|
1888
|
+
em.doc = NULL;
|
|
1889
|
+
yep_writer* w = &em.w;
|
|
1890
|
+
w->sc_dec = NULL;
|
|
1891
|
+
w->sc_i = 0;
|
|
1892
|
+
w->json = 1;
|
|
1893
|
+
w->json_compact = compact ? 1 : 0;
|
|
1894
|
+
w->json_pretty = 0;
|
|
1895
|
+
w->canonical = 0;
|
|
1896
|
+
w->explicit_doc_start = 0;
|
|
1897
|
+
w->best_width = 0;
|
|
1898
|
+
w->sv_input = NULL;
|
|
1899
|
+
w->sv_arena = NULL;
|
|
1900
|
+
w->sink = NULL;
|
|
1901
|
+
w->force_flow = 0;
|
|
1902
|
+
w->in_flow = 0;
|
|
1903
|
+
w->flow_item = 0;
|
|
1904
|
+
w->unserializable = 0;
|
|
1905
|
+
|
|
1906
|
+
mj_ctx c;
|
|
1907
|
+
memset(&c, 0, sizeof(c));
|
|
1908
|
+
c.w = w;
|
|
1909
|
+
c.s.p = m;
|
|
1910
|
+
c.s.len = m_len;
|
|
1911
|
+
c.s.at = 2; /* the 4.8 header */
|
|
1912
|
+
c.compact = compact ? 1 : 0;
|
|
1913
|
+
|
|
1914
|
+
/* one wet pass into a growable buffer (the #352 slice-2 shape) */
|
|
1915
|
+
size_t est = m_len * 2 + 64;
|
|
1916
|
+
char* buf = (char*)malloc(est);
|
|
1917
|
+
if (!buf)
|
|
1918
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
1919
|
+
w->p = buf;
|
|
1920
|
+
w->cap = est;
|
|
1921
|
+
w->grow = 1;
|
|
1922
|
+
|
|
1923
|
+
mj_value(&c);
|
|
1924
|
+
|
|
1925
|
+
int failed = c.failed;
|
|
1926
|
+
size_t wrote = w->len;
|
|
1927
|
+
buf = w->p;
|
|
1928
|
+
free(c.syms);
|
|
1929
|
+
free(c.objs);
|
|
1930
|
+
free(c.chain);
|
|
1931
|
+
if (failed || w->oom) {
|
|
1932
|
+
free(buf);
|
|
1933
|
+
return failed ? (YeptrisStatus)failed : YEPTRIS_ERROR_MEMORY;
|
|
1934
|
+
}
|
|
1935
|
+
buf[wrote] = '\0';
|
|
1936
|
+
*out = buf;
|
|
1937
|
+
*out_len = wrote;
|
|
1938
|
+
return YEPTRIS_OK;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1406
1941
|
size_t yep_emit_run(yep_emitter* em, int dry) {
|
|
1407
1942
|
yep_writer* w = &em->w;
|
|
1408
1943
|
const yep_dom* d = em->doc->dom;
|
|
@@ -1895,7 +1895,12 @@ static int e_flow_key_after(const char* p, size_t len, size_t close) {
|
|
|
1895
1895
|
static int e_flow_floor_ok(const char* p, size_t open, size_t close, uint16_t floor) {
|
|
1896
1896
|
size_t j = open;
|
|
1897
1897
|
while (j < close) {
|
|
1898
|
-
|
|
1898
|
+
/* the break rule is the scan's: \n, or a \r the next byte does
|
|
1899
|
+
* not join into CRLF — a \n-only walk let a CR-broken content
|
|
1900
|
+
* line escape the indent check (issue #771: the whole parse
|
|
1901
|
+
* took the fast path past a floor violation the windowed
|
|
1902
|
+
* parse's general kernel rejected) */
|
|
1903
|
+
if (p[j] == '\n' || (p[j] == '\r' && (j + 1 >= close || p[j + 1] != '\n'))) {
|
|
1899
1904
|
size_t k = j + 1;
|
|
1900
1905
|
uint32_t ind = 0;
|
|
1901
1906
|
while (k < close && p[k] == ' ') {
|
|
@@ -1908,12 +1913,17 @@ static int e_flow_floor_ok(const char* p, size_t open, size_t close, uint16_t fl
|
|
|
1908
1913
|
}
|
|
1909
1914
|
j++;
|
|
1910
1915
|
}
|
|
1911
|
-
if (close
|
|
1912
|
-
size_t
|
|
1913
|
-
uint32_t
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
+
if (close > open) {
|
|
1917
|
+
size_t from = open;
|
|
1918
|
+
uint32_t ln = 0;
|
|
1919
|
+
size_t ls = open;
|
|
1920
|
+
yep_scan_advance_line(p, &from, close, &ln, &ls);
|
|
1921
|
+
/* advance_line cannot peek past its `to`: a lone \r at
|
|
1922
|
+
* close - 1 ends the closer's line AT close */
|
|
1923
|
+
if (p[close - 1] == '\r' && p[close] != '\n') {
|
|
1924
|
+
ls = close;
|
|
1916
1925
|
}
|
|
1926
|
+
uint32_t ind = 0;
|
|
1917
1927
|
while (ls < close && p[ls] == ' ') {
|
|
1918
1928
|
ind++;
|
|
1919
1929
|
ls++;
|
|
@@ -1950,7 +1960,8 @@ static int e_flow_json(yep_engine* e, yep_view anchor, yep_view tag, uint32_t an
|
|
|
1950
1960
|
return 0; /* not JSON-class: the general kernel */
|
|
1951
1961
|
}
|
|
1952
1962
|
if (rc == 1) {
|
|
1953
|
-
int single = (memchr(p + open_pos, '\n', bclose - open_pos) == NULL)
|
|
1963
|
+
int single = (memchr(p + open_pos, '\n', bclose - open_pos) == NULL) &&
|
|
1964
|
+
(memchr(p + open_pos, '\r', bclose - open_pos) == NULL);
|
|
1954
1965
|
if (e_flow_key_after(p, len, bclose) ||
|
|
1955
1966
|
(e->flow_enforce && !single &&
|
|
1956
1967
|
!e_flow_floor_ok(p, open_pos, bclose, e->flow_floor))) {
|
|
@@ -153,8 +153,46 @@ int yep_scan_prop_char(unsigned char c) {
|
|
|
153
153
|
return !yep_ct_any(c, YEP_CT_BLANK | YEP_CT_LBREAK | YEP_CT_FLOW_IND) && c != ',' && c != '#';
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
int yep_scan_dq_forced(const char* p, uint32_t len) {
|
|
157
|
+
for (uint32_t k = 0; k + 1 < len; k++) {
|
|
158
|
+
if ((unsigned char)p[k] == 0xc2 && (unsigned char)p[k + 1] == 0x85) {
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
/* LS/PS are breaks like NEL: only their escapes are stable */
|
|
162
|
+
if ((unsigned char)p[k] == 0xe2 && k + 2 < len && (unsigned char)p[k + 1] == 0x80 &&
|
|
163
|
+
((unsigned char)p[k + 2] == 0xa8 || (unsigned char)p[k + 2] == 0xa9)) {
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
if ((unsigned char)p[k] == 0xef && k + 2 < len && (unsigned char)p[k + 1] == 0xbb &&
|
|
167
|
+
(unsigned char)p[k + 2] == 0xbf) {
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
if ((unsigned char)p[k] == 0xef && k + 2 < len && (unsigned char)p[k + 1] == 0xbf &&
|
|
171
|
+
((unsigned char)p[k + 2] == 0xbe || (unsigned char)p[k + 2] == 0xbf)) {
|
|
172
|
+
return 1; /* noncharacter: only its escape re-parses */
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
156
178
|
size_t yep_scan_prop_end(const char* p, size_t len, size_t pos) {
|
|
157
|
-
while (pos < len
|
|
179
|
+
while (pos < len) {
|
|
180
|
+
unsigned char c = (unsigned char)p[pos];
|
|
181
|
+
if (!yep_scan_prop_char(c)) {
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
/* NEL/LS/PS end the name (libyaml's IS_BLANKZ anchor law: the
|
|
185
|
+
* suite's ns-anchor-char excludes breaks): the byte-level
|
|
186
|
+
* table cannot classify a multibyte break, and the name run
|
|
187
|
+
* then swallowed it — the r69/r70 nightly divergence class
|
|
188
|
+
* ("&k:":(<PS>..."), shape-dependent clip downstream */
|
|
189
|
+
if (c == 0xC2 && pos + 1 < len && (unsigned char)p[pos + 1] == 0x85) {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
if (c == 0xE2 && pos + 2 < len && (unsigned char)p[pos + 1] == 0x80 &&
|
|
193
|
+
((unsigned char)p[pos + 2] & 0xFE) == 0xA8) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
158
196
|
pos++;
|
|
159
197
|
}
|
|
160
198
|
return pos;
|
|
@@ -98,6 +98,12 @@ int yep_scan_prop_char(unsigned char c);
|
|
|
98
98
|
/* First offset at/after pos that cannot continue an anchor/alias name. */
|
|
99
99
|
size_t yep_scan_prop_end(const char* p, size_t len, size_t pos);
|
|
100
100
|
|
|
101
|
+
/* The dq-forced byte set (issue #352 lane 2): NEL, LS/PS, the BOM,
|
|
102
|
+
* and the noncharacters — the bytes whose only stable spelling is
|
|
103
|
+
* the double-quoted escape. One home for the emitter's route check
|
|
104
|
+
* AND the DOM's parse-time fact bit, so the two cannot drift. */
|
|
105
|
+
int yep_scan_dq_forced(const char* p, uint32_t len);
|
|
106
|
+
|
|
101
107
|
/* Tag-shorthand suffix byte: libyaml's URI set (";/?:@&=+$,_.!~*'()%"
|
|
102
108
|
* plus alphanumerics; flow indicators and ',' terminate instead).
|
|
103
109
|
* Stray non-set bytes stay visible as the props follower, which
|