yeptris 0.6.52.0-arm-linux-eabihf → 0.6.54.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/yeptris/cbor/from_marshal.c +7 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +3 -1
- data/vendor/libyeptris/src/yeptris/dom/dom.c +15 -8
- data/vendor/libyeptris/src/yeptris/dom/dom.h +8 -2
- data/vendor/libyeptris/src/yeptris/dom/ytape.c +2 -2
- data/vendor/libyeptris/src/yeptris/emit/emit.c +4 -0
- data/vendor/libyeptris/src/yeptris/emit/style.c +9 -191
- data/vendor/libyeptris/src/yeptris/emit/writer.c +149 -35
- data/vendor/libyeptris/src/yeptris/encoding/transcode.c +9 -0
- data/vendor/libyeptris/src/yeptris/events/capture.c +19 -7
- data/vendor/libyeptris/src/yeptris/events/iterparse.c +1 -1
- data/vendor/libyeptris/src/yeptris/events/recorder.c +6 -2
- data/vendor/libyeptris/src/yeptris/marshal.c +10 -5
- data/vendor/libyeptris/src/yeptris/memory/arena.c +8 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +7 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +185 -67
- data/vendor/libyeptris/src/yeptris/parse/events.h +2 -2
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +2 -1
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +3 -1
- data/vendor/libyeptris/src/yeptris/scan/json.c +6 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +202 -1
- data/vendor/libyeptris/src/yeptris/scan/scan.h +12 -1
- metadata +1 -1
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
#include "style.h"
|
|
26
26
|
|
|
27
|
+
#include <math.h>
|
|
27
28
|
#include <stdio.h>
|
|
28
29
|
#include <stdlib.h>
|
|
29
30
|
|
|
@@ -95,6 +96,40 @@ static void wr_put(yep_writer* w, const char* p, uint32_t n) {
|
|
|
95
96
|
w->len += n;
|
|
96
97
|
return;
|
|
97
98
|
}
|
|
99
|
+
if (w->sink != NULL && !w->grow && !w->bounded && w->cap > 0 &&
|
|
100
|
+
(size_t)n > w->cap - w->len - 1) {
|
|
101
|
+
/* the fixed-window streaming route cannot hold this put even
|
|
102
|
+
* after the watermark flush: CHUNK it — window-sized pieces
|
|
103
|
+
* with a flush between each. Append-only output makes any cut
|
|
104
|
+
* a safe cut; the sink sees more, smaller chunks. Memory stays
|
|
105
|
+
* bounded by the window (the route's whole point) — the old
|
|
106
|
+
* unconditional memcpy wrote straight past it for any scalar
|
|
107
|
+
* larger than the remaining room (the 2026-10-31 audit's
|
|
108
|
+
* streaming-overflow finding). */
|
|
109
|
+
size_t off = 0;
|
|
110
|
+
while (off < (size_t)n) {
|
|
111
|
+
if (w->sink_aborted) {
|
|
112
|
+
w->len += (size_t)n - off; /* keep counting: the caller fails */
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
size_t room = w->cap - w->len - 1;
|
|
116
|
+
if (room > (size_t)n - off) {
|
|
117
|
+
room = (size_t)n - off;
|
|
118
|
+
}
|
|
119
|
+
memcpy(w->p + w->len, p + off, room);
|
|
120
|
+
w->len += room;
|
|
121
|
+
off += room;
|
|
122
|
+
if (off < (size_t)n) {
|
|
123
|
+
if (w->sink(w->sink_ctx, w->p, w->len) != 0) {
|
|
124
|
+
w->sink_aborted = 1;
|
|
125
|
+
} else {
|
|
126
|
+
w->flushed += w->len;
|
|
127
|
+
w->len = 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
98
133
|
/* the +1 reserves the terminator slot: the serializers write the
|
|
99
134
|
* NUL at p[len] after the run, and a write landing exactly on cap
|
|
100
135
|
* (len + n == cap) put that NUL one byte past the buffer — the
|
|
@@ -487,10 +522,11 @@ enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL, Y
|
|
|
487
522
|
* a key whose content demotes to DQ re-parses as a QUOTED key and must
|
|
488
523
|
* ride simple, or round two's render differs (the nightly's round 118). */
|
|
489
524
|
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in,
|
|
490
|
-
int has_tag, int no_exotic) {
|
|
491
|
-
/* the parse-time fact
|
|
492
|
-
* the dq-forced set skips
|
|
493
|
-
*
|
|
525
|
+
int has_tag, int no_exotic, int plain_fact) {
|
|
526
|
+
/* the parse-time fact bits (#352 lanes 2-3): a dnode verified clean
|
|
527
|
+
* of the dq-forced set skips that rescan, and a verified plain-safe
|
|
528
|
+
* span skips the safety walk — 0 (mutation-built or JSON classes)
|
|
529
|
+
* keeps the derive */
|
|
494
530
|
if (!no_exotic && yep_scan_dq_forced(p, len)) {
|
|
495
531
|
return YEP_SC_DQ;
|
|
496
532
|
}
|
|
@@ -535,7 +571,10 @@ static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key,
|
|
|
535
571
|
int plain_ok = 0;
|
|
536
572
|
int plain_checked = 0;
|
|
537
573
|
if ((sty_in == 1) && len > 0) {
|
|
538
|
-
plain_ok =
|
|
574
|
+
plain_ok = plain_fact;
|
|
575
|
+
if (!plain_ok) {
|
|
576
|
+
plain_ok = as_key ? yep_style_plain_key_safe(p, len) : yep_style_plain_safe(p, len);
|
|
577
|
+
}
|
|
539
578
|
plain_checked = 1;
|
|
540
579
|
if (plain_ok) {
|
|
541
580
|
return YEP_SC_PLAIN; /* plain-safe implies no breaks: memchr skipped */
|
|
@@ -594,7 +633,10 @@ static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key,
|
|
|
594
633
|
return as_key ? YEP_SC_DQ : YEP_SC_NOTHING;
|
|
595
634
|
}
|
|
596
635
|
if (!plain_checked) {
|
|
597
|
-
plain_ok =
|
|
636
|
+
plain_ok = plain_fact;
|
|
637
|
+
if (!plain_ok) {
|
|
638
|
+
plain_ok = as_key ? yep_style_plain_key_safe(p, len) : yep_style_plain_safe(p, len);
|
|
639
|
+
}
|
|
598
640
|
plain_checked = 1;
|
|
599
641
|
}
|
|
600
642
|
if (plain_ok) {
|
|
@@ -635,14 +677,15 @@ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int a
|
|
|
635
677
|
* memoized long scalars, in visit order either way. */
|
|
636
678
|
if (w->sc_dec != NULL && len >= YEP_SC_MEMO_MIN) {
|
|
637
679
|
if (w->dry) {
|
|
638
|
-
route =
|
|
680
|
+
route =
|
|
681
|
+
sc_route(w, p, len, as_key, n->style, n->tag.len > 0, n->no_exotic, n->plain_ok);
|
|
639
682
|
w->sc_dec[w->sc_i] = route;
|
|
640
683
|
} else {
|
|
641
684
|
route = w->sc_dec[w->sc_i];
|
|
642
685
|
}
|
|
643
686
|
w->sc_i++;
|
|
644
687
|
} else {
|
|
645
|
-
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0, n->no_exotic);
|
|
688
|
+
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0, n->no_exotic, n->plain_ok);
|
|
646
689
|
}
|
|
647
690
|
switch (route) {
|
|
648
691
|
case YEP_SC_NOTHING:
|
|
@@ -762,8 +805,14 @@ static void emit_anchor_ex(yep_emitter* em, const yep_dnode* n) {
|
|
|
762
805
|
if (anchor_name_unspellable(w, n)) {
|
|
763
806
|
return;
|
|
764
807
|
}
|
|
765
|
-
wr_byte(w, '&');
|
|
766
808
|
yep_view adec = wv(w, n->anchor);
|
|
809
|
+
if (w->json) {
|
|
810
|
+
/* JSON spells no anchors — but a name the emitter could never
|
|
811
|
+
* spell still fails the entry (the "{k: &a?b v}" law) */
|
|
812
|
+
wr_anchor_check(w, &adec);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
wr_byte(w, '&');
|
|
767
816
|
if (w->canonical) {
|
|
768
817
|
uint32_t idx = yep_nametab_get(&em->canon_names, adec);
|
|
769
818
|
if (idx == YEP_NAMETAB_NIL) {
|
|
@@ -782,6 +831,12 @@ static void emit_anchor_ex(yep_emitter* em, const yep_dnode* n) {
|
|
|
782
831
|
|
|
783
832
|
static void emit_alias_ex(yep_emitter* em, const yep_dnode* n) {
|
|
784
833
|
yep_writer* w = &em->w;
|
|
834
|
+
if (w->json) {
|
|
835
|
+
/* JSON has no alias references: the projection writes null
|
|
836
|
+
* (bounded — dereferencing would loop on cycles) */
|
|
837
|
+
wr_put(w, "null", 4);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
785
840
|
yep_view name_v = wv(w, n->value);
|
|
786
841
|
if (w->in_flow && !w->canonical) {
|
|
787
842
|
/* the alias twin of wr_anchor_check: a flow alias is spelled
|
|
@@ -916,6 +971,9 @@ static void emit_tag(yep_writer* w, const yep_dnode* n) {
|
|
|
916
971
|
if (n->tag.len == 0) {
|
|
917
972
|
return;
|
|
918
973
|
}
|
|
974
|
+
if (w->json) {
|
|
975
|
+
return; /* JSON spells no tags (the ts probe: "k: !" -> {"k": ! ""}) */
|
|
976
|
+
}
|
|
919
977
|
yep_view tdec = wv(w, n->tag);
|
|
920
978
|
wr_tag_token(w, &tdec);
|
|
921
979
|
wr_byte(w, ' ');
|
|
@@ -925,15 +983,18 @@ static void emit_tag(yep_writer* w, const yep_dnode* n) {
|
|
|
925
983
|
* "key:"/"-" — properties may not precede a dash entry directly (the
|
|
926
984
|
* suite's SY6V rule). */
|
|
927
985
|
static void emit_props_tail(yep_writer* w, const yep_dnode* n) {
|
|
986
|
+
if (n->anchor.len > 0 && anchor_name_unspellable(w, n)) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (w->json) {
|
|
990
|
+
return; /* JSON spells no properties */
|
|
991
|
+
}
|
|
928
992
|
if (n->tag.len > 0) {
|
|
929
993
|
wr_byte(w, ' ');
|
|
930
994
|
yep_view tdec2 = wv(w, n->tag);
|
|
931
995
|
wr_tag_token(w, &tdec2);
|
|
932
996
|
}
|
|
933
997
|
if (n->anchor.len > 0) {
|
|
934
|
-
if (anchor_name_unspellable(w, n)) {
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
998
|
wr_byte(w, ' ');
|
|
938
999
|
yep_view adec2 = wv(w, n->anchor);
|
|
939
1000
|
wr_anchor_check(w, &adec2);
|
|
@@ -1120,42 +1181,57 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1120
1181
|
vn0->count == 0) {
|
|
1121
1182
|
yep_view kv = wv(w, kn0->value);
|
|
1122
1183
|
yep_view vv = wv(w, vn0->value);
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1184
|
+
uint32_t f_ind = 0, f_nl = 0, f_total = 0;
|
|
1185
|
+
int fused = kv.len > 0 && !key_written_over_limit(w, kn0) && kn0->plain_ok &&
|
|
1186
|
+
(vv.len == 0 || vn0->plain_ok);
|
|
1187
|
+
if (fused) {
|
|
1126
1188
|
/* fused member write: plain-safe spans carry no breaks,
|
|
1127
1189
|
* so the col scan is arithmetic and one reservation
|
|
1128
1190
|
* covers the whole member (line + key + ':' + value) */
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1191
|
+
f_ind = pair > 0 ? (uint32_t)content_col : 0u;
|
|
1192
|
+
f_nl = (pair > 0 && w->last != '\n') ? 1u : 0u;
|
|
1193
|
+
f_total = f_nl + f_ind + kv.len + 1u + (vv.len > 0 ? vv.len + 1u : 0u);
|
|
1194
|
+
/* the fixed-window streaming route: the fused write
|
|
1195
|
+
* bypasses wr_put (whose chunked path bounds every
|
|
1196
|
+
* put), and this block ends in continue — so a member
|
|
1197
|
+
* that does not fit the window must DECLINE THE WHOLE
|
|
1198
|
+
* ARM to reach the general body, which emits the
|
|
1199
|
+
* identical bytes through wr_put. cap==0 routes are the
|
|
1200
|
+
* old trusted-count forms: the writer cannot reason
|
|
1201
|
+
* about their capacity, and they keep the arm. */
|
|
1202
|
+
if (!w->dry && !w->grow && w->cap != 0 && w->len + (size_t)f_total + 1 > w->cap) {
|
|
1203
|
+
fused = 0;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
if (fused) {
|
|
1132
1207
|
w->last = vv.len > 0 ? (char)vv.p[vv.len - 1] : ':';
|
|
1133
|
-
if (
|
|
1134
|
-
w->col = (int)(
|
|
1208
|
+
if (f_nl) {
|
|
1209
|
+
w->col = (int)(f_total - 1u); /* the break resets, then the line */
|
|
1135
1210
|
} else {
|
|
1136
|
-
w->col += (int)
|
|
1211
|
+
w->col += (int)f_total;
|
|
1137
1212
|
}
|
|
1138
1213
|
if (w->dry) {
|
|
1139
|
-
w->len +=
|
|
1214
|
+
w->len += f_total;
|
|
1140
1215
|
} else {
|
|
1141
|
-
if (w->grow && w->len +
|
|
1216
|
+
if (w->grow && w->len + f_total + 1 > w->cap) {
|
|
1142
1217
|
if (w->bounded) {
|
|
1143
1218
|
w->oom = 1; /* keep counting: the trusted caller reads the size */
|
|
1144
1219
|
} else {
|
|
1145
|
-
wr_grow(w,
|
|
1220
|
+
wr_grow(w, f_total);
|
|
1146
1221
|
}
|
|
1147
1222
|
}
|
|
1148
1223
|
if (w->oom) {
|
|
1149
|
-
w->len +=
|
|
1224
|
+
w->len += f_total; /* keep counting: the caller fails on oom */
|
|
1150
1225
|
} else {
|
|
1151
1226
|
/* the fused write bypasses wr_put/wr_byte: keep
|
|
1152
1227
|
* the reservation honest (the #739 lane-1 law) */
|
|
1153
|
-
w->reserve =
|
|
1228
|
+
w->reserve =
|
|
1229
|
+
(size_t)f_total <= w->reserve ? w->reserve - (size_t)f_total : 0;
|
|
1154
1230
|
char* q = w->p + w->len;
|
|
1155
|
-
if (
|
|
1231
|
+
if (f_nl) {
|
|
1156
1232
|
*q++ = '\n';
|
|
1157
1233
|
}
|
|
1158
|
-
for (uint32_t i = 0; i <
|
|
1234
|
+
for (uint32_t i = 0; i < f_ind; i++) {
|
|
1159
1235
|
*q++ = ' ';
|
|
1160
1236
|
}
|
|
1161
1237
|
memcpy(q, kv.p, kv.len);
|
|
@@ -1165,7 +1241,7 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1165
1241
|
*q++ = ' ';
|
|
1166
1242
|
memcpy(q, vv.p, vv.len);
|
|
1167
1243
|
}
|
|
1168
|
-
w->len +=
|
|
1244
|
+
w->len += f_total;
|
|
1169
1245
|
wr_maybe_flush(w);
|
|
1170
1246
|
}
|
|
1171
1247
|
}
|
|
@@ -1445,9 +1521,10 @@ static void emit_json_flow(yep_emitter* em, uint32_t id) {
|
|
|
1445
1521
|
}
|
|
1446
1522
|
if (map) {
|
|
1447
1523
|
/* the one generic-path rule JSON keeps: a non-scalar key
|
|
1448
|
-
*
|
|
1449
|
-
*
|
|
1450
|
-
|
|
1524
|
+
* rides the explicit "? key : " form — same bytes emit_flow
|
|
1525
|
+
* writes. (Propertied scalar keys used to join it; JSON
|
|
1526
|
+
* drops properties, so they ride the plain quoted form.) */
|
|
1527
|
+
if (cn->kind != 0) {
|
|
1451
1528
|
wr_put(w, "? ", 2);
|
|
1452
1529
|
emit_json_node(em, child);
|
|
1453
1530
|
wr_put(w, " : ", 3);
|
|
@@ -1546,6 +1623,10 @@ typedef struct {
|
|
|
1546
1623
|
* 'I'-wrapped construct registers ONCE (at the 'I');
|
|
1547
1624
|
* its immediate inner must not add a second entry or
|
|
1548
1625
|
* every later '@' link shifts */
|
|
1626
|
+
int depth; /* mj_value recursion guard — the reader walks hostile
|
|
1627
|
+
* bytes, not Ruby's heap: nesting is unbounded in the
|
|
1628
|
+
* input, the stack is not (the parser's own
|
|
1629
|
+
* max_depth=1000 is the same law) */
|
|
1549
1630
|
} mj_ctx;
|
|
1550
1631
|
|
|
1551
1632
|
static int mj_fail(mj_ctx* c) {
|
|
@@ -1672,7 +1753,23 @@ static int mj_ivar_pairs(mj_ctx* c) {
|
|
|
1672
1753
|
}
|
|
1673
1754
|
|
|
1674
1755
|
/* stream-position-only walk: consumes one value, emits nothing */
|
|
1756
|
+
|
|
1757
|
+
#define MJ_DEPTH_MAX 1000
|
|
1758
|
+
|
|
1759
|
+
/* the discard twin rides the same budget (its 'I'/'['/'{' recursion is
|
|
1760
|
+
* just as hostile-byte reachable) */
|
|
1761
|
+
static int mj_discard_value_inner(mj_ctx* c);
|
|
1762
|
+
|
|
1675
1763
|
static int mj_discard_value(mj_ctx* c) {
|
|
1764
|
+
if (c->depth >= MJ_DEPTH_MAX)
|
|
1765
|
+
return mj_fail(c);
|
|
1766
|
+
c->depth++;
|
|
1767
|
+
int rc = mj_discard_value_inner(c);
|
|
1768
|
+
c->depth--;
|
|
1769
|
+
return rc;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
static int mj_discard_value_inner(mj_ctx* c) {
|
|
1676
1773
|
unsigned char t;
|
|
1677
1774
|
int rc = mj_byte(c, &t);
|
|
1678
1775
|
if (rc != 0)
|
|
@@ -1771,7 +1868,20 @@ static int mj_emit_memo(mj_ctx* c, const mj_obj* o) {
|
|
|
1771
1868
|
return 0;
|
|
1772
1869
|
}
|
|
1773
1870
|
|
|
1871
|
+
static int mj_value_inner(mj_ctx* c);
|
|
1872
|
+
|
|
1873
|
+
/* the wrapper owns the recursion budget: hostile bytes can nest arrays
|
|
1874
|
+
* without bound, the stack cannot (the parser's max_depth=1000 law) */
|
|
1774
1875
|
static int mj_value(mj_ctx* c) {
|
|
1876
|
+
if (c->depth >= MJ_DEPTH_MAX)
|
|
1877
|
+
return mj_fail(c);
|
|
1878
|
+
c->depth++;
|
|
1879
|
+
int rc = mj_value_inner(c);
|
|
1880
|
+
c->depth--;
|
|
1881
|
+
return rc;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
static int mj_value_inner(mj_ctx* c) {
|
|
1775
1885
|
unsigned char t;
|
|
1776
1886
|
size_t tpos = c->s.at;
|
|
1777
1887
|
int reg_idx = -1;
|
|
@@ -1838,7 +1948,9 @@ static int mj_value(mj_ctx* c) {
|
|
|
1838
1948
|
unsigned char lo, hi;
|
|
1839
1949
|
if (mj_byte(c, &lo) != 0 || mj_byte(c, &hi) != 0)
|
|
1840
1950
|
return -1;
|
|
1841
|
-
|
|
1951
|
+
/* shift in the unsigned domain: the signed form put
|
|
1952
|
+
* 0xFFFF into the sign bit at limb 3 (UB, UBSan-flagged) */
|
|
1953
|
+
v |= (int64_t)((uint64_t)(lo | ((uint64_t)hi << 8)) << (16 * i));
|
|
1842
1954
|
}
|
|
1843
1955
|
if (sign == 0x2d)
|
|
1844
1956
|
v = -v;
|
|
@@ -1858,9 +1970,11 @@ static int mj_value(mj_ctx* c) {
|
|
|
1858
1970
|
memcpy(tmp, c->s.p + c->s.at, tl);
|
|
1859
1971
|
tmp[tl] = '\0';
|
|
1860
1972
|
c->s.at += (size_t)len;
|
|
1861
|
-
if (strcmp(tmp, "inf") == 0 || strcmp(tmp, "-inf") == 0 || strcmp(tmp, "nan") == 0)
|
|
1862
|
-
return mj_fail(c); /* JSON has no inf/nan spelling */
|
|
1863
1973
|
double d = strtod(tmp, NULL);
|
|
1974
|
+
if (!isfinite(d))
|
|
1975
|
+
return mj_fail(c); /* JSON has no inf/nan spelling — and a
|
|
1976
|
+
* strtod OVERFLOW ("1e999") produces
|
|
1977
|
+
* one from text that never spells it */
|
|
1864
1978
|
char buf[40];
|
|
1865
1979
|
int wl = yep_d2s_json(d, buf);
|
|
1866
1980
|
wr_put(c->w, buf, (uint32_t)wl);
|
|
@@ -95,6 +95,15 @@ static int yep_transcode_utf16(const yep_allocator* sys, int big_endian, const u
|
|
|
95
95
|
static int yep_transcode_utf32(const yep_allocator* sys, int big_endian, const unsigned char* src,
|
|
96
96
|
size_t len, unsigned char** out, size_t* out_len, size_t* err_pos) {
|
|
97
97
|
size_t units = len / 4;
|
|
98
|
+
if (len % 4 != 0) {
|
|
99
|
+
/* a dangling final unit is an error, not a silent truncation
|
|
100
|
+
* (the UTF-16 twin rejects its own; the header contract says
|
|
101
|
+
* truncated units error at the offending byte) */
|
|
102
|
+
if (err_pos != NULL) {
|
|
103
|
+
*err_pos = units * 4;
|
|
104
|
+
}
|
|
105
|
+
return -2;
|
|
106
|
+
}
|
|
98
107
|
unsigned char* dst = yep_alloc(sys, len ? len : 1);
|
|
99
108
|
if (dst == NULL) {
|
|
100
109
|
return -1;
|
|
@@ -29,8 +29,14 @@ void yep_rec_free(yep_rec_store* s) {
|
|
|
29
29
|
yep_rec_init(s);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/* Returns 0 ok / -1 OOM. On OOM the record is NOT committed (the
|
|
33
|
+
* caller unwinds before s->n++): the old drop-the-string form left
|
|
34
|
+
* value_len set with offset 0, so materialize read whatever string
|
|
35
|
+
* lived at the arena head - or past a NULL arena on first-alloc
|
|
36
|
+
* failure. */
|
|
37
|
+
static int arena_put(yep_rec_store* s, const char* p, uint32_t len, uint32_t* off) {
|
|
33
38
|
if (len == 0 || p == NULL) {
|
|
39
|
+
*off = 0;
|
|
34
40
|
return 0;
|
|
35
41
|
}
|
|
36
42
|
if (s->arena_len + len > s->arena_cap) {
|
|
@@ -40,15 +46,15 @@ static uint32_t arena_put(yep_rec_store* s, const char* p, uint32_t len) {
|
|
|
40
46
|
}
|
|
41
47
|
char* na = realloc(s->arena, cap);
|
|
42
48
|
if (na == NULL) {
|
|
43
|
-
return
|
|
49
|
+
return -1;
|
|
44
50
|
}
|
|
45
51
|
s->arena = na;
|
|
46
52
|
s->arena_cap = cap;
|
|
47
53
|
}
|
|
48
54
|
memcpy(s->arena + s->arena_len, p, len);
|
|
49
|
-
|
|
55
|
+
*off = (uint32_t)s->arena_len;
|
|
50
56
|
s->arena_len += len;
|
|
51
|
-
return
|
|
57
|
+
return 0;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
int yep_rec_on_event(void* ctx, const yep_event* ev) {
|
|
@@ -116,11 +122,17 @@ int yep_rec_on_event(void* ctx, const yep_event* ev) {
|
|
|
116
122
|
r->col = ev->col;
|
|
117
123
|
r->end_line = ev->end_line;
|
|
118
124
|
r->end_col = ev->end_col;
|
|
119
|
-
|
|
125
|
+
if (arena_put(s, (const char*)ev->value.p, (uint32_t)ev->value.len, &r->value_off) != 0) {
|
|
126
|
+
return -1;
|
|
127
|
+
}
|
|
120
128
|
r->value_len = (uint32_t)ev->value.len;
|
|
121
|
-
|
|
129
|
+
if (arena_put(s, (const char*)ev->anchor.p, (uint32_t)ev->anchor.len, &r->anchor_off) != 0) {
|
|
130
|
+
return -1;
|
|
131
|
+
}
|
|
122
132
|
r->anchor_len = (uint32_t)ev->anchor.len;
|
|
123
|
-
|
|
133
|
+
if (arena_put(s, (const char*)ev->tag.p, (uint32_t)ev->tag.len, &r->tag_off) != 0) {
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
124
136
|
r->tag_len = (uint32_t)ev->tag.len;
|
|
125
137
|
s->n++;
|
|
126
138
|
return 0;
|
|
@@ -161,7 +161,7 @@ YEPTRIS_API const YeptrisEvent* yeptris_iterparse_next(YeptrisIterparse it, size
|
|
|
161
161
|
yep_engine_destroy(eng);
|
|
162
162
|
if (rc == -1) {
|
|
163
163
|
it->done = 1;
|
|
164
|
-
it->status = YEPTRIS_ERROR_PARSE;
|
|
164
|
+
it->status = err != NULL ? YEPTRIS_ERROR_PARSE : YEPTRIS_ERROR_MEMORY;
|
|
165
165
|
if (it->store.n == 0) {
|
|
166
166
|
if (!it->emitted_stream_start) {
|
|
167
167
|
/* the engine emitted +STR before failing: match it */
|
|
@@ -74,9 +74,13 @@ YEPTRIS_API YeptrisStatus yeptris_recorder_feed(YeptrisRecorder rec, const char*
|
|
|
74
74
|
if (err != NULL) {
|
|
75
75
|
rec->err_line = err->line;
|
|
76
76
|
rec->err_col = err->col;
|
|
77
|
+
rec->status = YEPTRIS_ERROR_PARSE;
|
|
78
|
+
} else {
|
|
79
|
+
/* a clean error channel means the SINK failed: the capture
|
|
80
|
+
* ran out of memory, the bytes never were a parse error */
|
|
81
|
+
rec->status = YEPTRIS_ERROR_MEMORY;
|
|
77
82
|
}
|
|
78
|
-
rec->final_fed = 1; /* a
|
|
79
|
-
rec->status = YEPTRIS_ERROR_PARSE;
|
|
83
|
+
rec->final_fed = 1; /* a failed feed kills the stream */
|
|
80
84
|
return rec->status;
|
|
81
85
|
}
|
|
82
86
|
rec->status = YEPTRIS_OK;
|
|
@@ -291,8 +291,13 @@ static int psych_int_text(const char* p, uint32_t len) {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
/* decimal text -> bignum limbs (base 2^16 LE), then Marshal's 'l':
|
|
294
|
-
* sign + w_long(limb count) + limbs.
|
|
295
|
-
*
|
|
294
|
+
* sign + w_long(limb count) + limbs. The gate (marshal_node: text
|
|
295
|
+
* under 1300 chars) bounds the digits at 1299 -> ceil(1299*log2(10)/16)
|
|
296
|
+
* = 270 limbs; the arrays are sized from that bound so no carry is
|
|
297
|
+
* ever dropped (the old 128-limb cap silently truncated 617..1299-digit
|
|
298
|
+
* values into wrong numbers). */
|
|
299
|
+
#define E_BIGNUM_LIMBS_MAX 272
|
|
300
|
+
|
|
296
301
|
static void e_bignum_text(E* e, const char* p, uint32_t len) {
|
|
297
302
|
int neg = 0;
|
|
298
303
|
if (p[0] == '-' || p[0] == '+') {
|
|
@@ -300,7 +305,7 @@ static void e_bignum_text(E* e, const char* p, uint32_t len) {
|
|
|
300
305
|
p++;
|
|
301
306
|
len--;
|
|
302
307
|
}
|
|
303
|
-
uint16_t limbs[
|
|
308
|
+
uint16_t limbs[E_BIGNUM_LIMBS_MAX];
|
|
304
309
|
memset(limbs, 0, sizeof(limbs));
|
|
305
310
|
int nl = 1;
|
|
306
311
|
for (uint32_t i = 0; i < len; i++) {
|
|
@@ -313,7 +318,7 @@ static void e_bignum_text(E* e, const char* p, uint32_t len) {
|
|
|
313
318
|
limbs[j] = (uint16_t)(t & 0xffffu);
|
|
314
319
|
carry = t >> 16;
|
|
315
320
|
}
|
|
316
|
-
while (carry != 0 && nl <
|
|
321
|
+
while (carry != 0 && nl < E_BIGNUM_LIMBS_MAX) {
|
|
317
322
|
limbs[nl++] = (uint16_t)(carry & 0xffffu);
|
|
318
323
|
carry >>= 16;
|
|
319
324
|
}
|
|
@@ -324,7 +329,7 @@ static void e_bignum_text(E* e, const char* p, uint32_t len) {
|
|
|
324
329
|
e_byte(e, 'l');
|
|
325
330
|
e_byte(e, neg ? '-' : '+');
|
|
326
331
|
e_long(e, nl);
|
|
327
|
-
unsigned char b[
|
|
332
|
+
unsigned char b[E_BIGNUM_LIMBS_MAX * 2];
|
|
328
333
|
for (int i = 0; i < nl; i++) {
|
|
329
334
|
b[2 * i] = (unsigned char)(limbs[i] & 0xff);
|
|
330
335
|
b[2 * i + 1] = (unsigned char)(limbs[i] >> 8);
|
|
@@ -85,6 +85,14 @@ void* yep_arena_alloc(yep_arena* arena, size_t size, size_t align) {
|
|
|
85
85
|
if (arena == NULL || size == 0 || !yep_align_ok(align)) {
|
|
86
86
|
return NULL;
|
|
87
87
|
}
|
|
88
|
+
/* a hostile size near SIZE_MAX must FAIL the fit test, not wrap
|
|
89
|
+
* start+size past the pointer space and pass it (the sizing sums
|
|
90
|
+
* below stay inside the guard's slack) */
|
|
91
|
+
if (size > (SIZE_MAX - align) / 2) {
|
|
92
|
+
/* half the pointer space: both the fit test's start+size and
|
|
93
|
+
* the new-block payload sums stay wrap-free below this */
|
|
94
|
+
return NULL;
|
|
95
|
+
}
|
|
88
96
|
|
|
89
97
|
yep_arena_block* b = arena->head;
|
|
90
98
|
uintptr_t base = (uintptr_t)b + sizeof(yep_arena_block);
|
|
@@ -43,6 +43,13 @@ void* yep_pool_alloc(yep_pool* pool, size_t size, size_t align) {
|
|
|
43
43
|
if (pool == NULL || size == 0 || !yep_align_ok(align)) {
|
|
44
44
|
return NULL;
|
|
45
45
|
}
|
|
46
|
+
/* mirror of the arena guard: the fit test's start+size and the
|
|
47
|
+
* payload sums must not wrap on a hostile size */
|
|
48
|
+
if (size > (SIZE_MAX - align) / 2) {
|
|
49
|
+
/* half the pointer space: both the fit test's start+size and
|
|
50
|
+
* the new-block payload sums stay wrap-free below this */
|
|
51
|
+
return NULL;
|
|
52
|
+
}
|
|
46
53
|
|
|
47
54
|
yep_block* b = pool->head;
|
|
48
55
|
if (b == NULL) { /* the lazy first block (#157) */
|