yeptris 0.6.32.1-aarch64-linux → 0.6.33.1-aarch64-linux
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/lib/yeptris/3.3/native.so +0 -0
- data/lib/yeptris/3.4/native.so +0 -0
- data/lib/yeptris/4.0/native.so +0 -0
- data/lib/yeptris/node.rb +12 -3
- data/lib/yeptris/psych/visitors.rb +1 -1
- data/lib/yeptris/yaml.rb +1 -1
- data/lib/yeptris.rb +1 -1
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +17 -5
- data/vendor/libyeptris/src/yeptris/dom/dom.c +52 -2
- data/vendor/libyeptris/src/yeptris/dom/ytape.c +238 -16
- data/vendor/libyeptris/src/yeptris/emit/emit.c +22 -1
- data/vendor/libyeptris/src/yeptris/emit/style.c +13 -1
- data/vendor/libyeptris/src/yeptris/emit/writer.c +395 -54
- data/vendor/libyeptris/src/yeptris/emit/writer.h +7 -0
- data/vendor/libyeptris/src/yeptris/events/values.c +1 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +1178 -127
- data/vendor/libyeptris/src/yeptris/parse/engine.h +3 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.c +28 -3
- data/vendor/libyeptris/src/yeptris/parse/scalars.h +12 -0
- data/vendor/libyeptris/src/yeptris/parse.c +12 -4
- data/vendor/libyeptris/src/yeptris/scan/scan.c +42 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.h +7 -0
- data/vendor/libyeptris/src/yeptris/tape.c +88 -1
- data/vendor/libyeptris/src/yeptris/tape_in.h +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aa8d3b78ec99584df719ea52b83de134ccec4b1c5c00dabbfd54e2850c2789c5
|
|
4
|
+
data.tar.gz: f259f9122e64be66be515aa9e41a8bf8d74bec2aeaf4f6a6f9855ed416c899b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5bf5c78083f143525812c7d41ec69b515a9836f0f061e6a621c1b4f7a63a6d89e54eccf11cdda6c9cbc35943637d946cf39a2f06126d19e97acbe6495a1fe6fc
|
|
7
|
+
data.tar.gz: 2ff20e717f4351f3b74305464ceb7deab18442090748d5d79504574639df0ef9ee42149b426e813ca683daf2bec620472f3791b194820e8888ad004209da0709
|
data/lib/yeptris/3.3/native.so
CHANGED
|
Binary file
|
data/lib/yeptris/3.4/native.so
CHANGED
|
Binary file
|
data/lib/yeptris/4.0/native.so
CHANGED
|
Binary file
|
data/lib/yeptris/node.rb
CHANGED
|
@@ -50,7 +50,7 @@ class Yeptris::Node
|
|
|
50
50
|
|
|
51
51
|
# Scalar content / alias name (UTF-8 String), nil for collections.
|
|
52
52
|
def value
|
|
53
|
-
len =
|
|
53
|
+
len = len_buffer
|
|
54
54
|
ptr = alive { Yeptris::FFI.yeptris_node_value(@c_ptr, len) }
|
|
55
55
|
return nil if ptr.null?
|
|
56
56
|
|
|
@@ -69,9 +69,18 @@ class Yeptris::Node
|
|
|
69
69
|
STYLES[alive { Yeptris::FFI.yeptris_node_style(@c_ptr) }]
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# The out-length receiver: ONE buffer per thread, reused. A fresh
|
|
73
|
+
# FFI::MemoryPointer per accessor call was an allocation storm that
|
|
74
|
+
# stalled the Windows heap under the #182 churn loop (the #504
|
|
75
|
+
# family — the hang's stack sits in FFI::MemoryPointer#initialize,
|
|
76
|
+
# node.rb anchor/tag reads inside Integer#times with GC.start).
|
|
77
|
+
def len_buffer
|
|
78
|
+
Thread.current[:yeptris_len_buf] ||= ::FFI::MemoryPointer.new(:size_t)
|
|
79
|
+
end
|
|
80
|
+
|
|
72
81
|
# Explicit tag URI when present, else nil.
|
|
73
82
|
def tag
|
|
74
|
-
len =
|
|
83
|
+
len = len_buffer
|
|
75
84
|
ptr = alive { Yeptris::FFI.yeptris_node_tag(@c_ptr, len) }
|
|
76
85
|
return nil if ptr.null?
|
|
77
86
|
|
|
@@ -79,7 +88,7 @@ class Yeptris::Node
|
|
|
79
88
|
end
|
|
80
89
|
|
|
81
90
|
def anchor
|
|
82
|
-
len =
|
|
91
|
+
len = len_buffer
|
|
83
92
|
ptr = alive { Yeptris::FFI.yeptris_node_anchor(@c_ptr, len) }
|
|
84
93
|
return nil if ptr.null?
|
|
85
94
|
|
|
@@ -216,7 +216,7 @@ module Yeptris
|
|
|
216
216
|
# explicit !!str tag (single-quoted) so it does not
|
|
217
217
|
# re-load as a merge
|
|
218
218
|
key_node = @tree.new_scalar("<<", :single_quoted)
|
|
219
|
-
key_node.set_tag("
|
|
219
|
+
key_node.set_tag("tag:yaml.org,2002:str")
|
|
220
220
|
m.map_add_node(key_node, visit(v))
|
|
221
221
|
else
|
|
222
222
|
m.map_add(key_text(k), visit(v))
|
data/lib/yeptris/yaml.rb
CHANGED
|
@@ -364,7 +364,7 @@ module Yeptris
|
|
|
364
364
|
# stdlib yaml_tree: a literal chevron key carries the
|
|
365
365
|
# explicit !!str tag so it does not re-load as a merge
|
|
366
366
|
key_node = doc.new_scalar("<<", :single_quoted)
|
|
367
|
-
key_node.set_tag("
|
|
367
|
+
key_node.set_tag("tag:yaml.org,2002:str")
|
|
368
368
|
m.map_add_node(key_node, build(doc, v, seen))
|
|
369
369
|
else
|
|
370
370
|
m.map_add(key_text(k), build(doc, v, seen))
|
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.33.1".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
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
cmake_minimum_required(VERSION 3.20)
|
|
4
4
|
|
|
5
5
|
project(yeptris
|
|
6
|
-
VERSION 0.6.
|
|
6
|
+
VERSION 0.6.33
|
|
7
7
|
DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
|
|
8
8
|
LANGUAGES C CXX
|
|
9
9
|
)
|
|
@@ -98,15 +98,27 @@ if(YEPTRIS_ENABLE_UBSAN)
|
|
|
98
98
|
message(STATUS "UndefinedBehaviorSanitizer: ENABLED (fatal)")
|
|
99
99
|
endif()
|
|
100
100
|
|
|
101
|
-
# libFuzzer implies ASAN (
|
|
101
|
+
# libFuzzer implies ASAN+UBSan (the teptris fuzz.yml pattern: the
|
|
102
|
+
# memory-safety contracts ride the SAME binary as the fuzzer, so every
|
|
103
|
+
# nightly round reports UB at the crash site instead of libFuzzer's
|
|
104
|
+
# "combine with AddressSanitizer" note).
|
|
102
105
|
if(YEPTRIS_ENABLE_FUZZING)
|
|
103
106
|
# instrument everything, but link the fuzzer MAIN only into the
|
|
104
107
|
# harnesses: a global -fsanitize=fuzzer link option demands
|
|
105
108
|
# LLVMFuzzerTestOneInput from every executable (the CLI and the
|
|
106
109
|
# test binaries died on it once CXX also pinned to clang++)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
if(APPLE)
|
|
111
|
+
# Apple ld64 cannot link UBSan'd static archives
|
|
112
|
+
# ("invalid r_symbolnum"): ASan-only locally
|
|
113
|
+
add_compile_options(-fsanitize=fuzzer-no-link,address -fno-omit-frame-pointer -g)
|
|
114
|
+
add_link_options(-fsanitize=address)
|
|
115
|
+
message(STATUS "libFuzzer: ENABLED (implies ASAN; UBSan needs linux ld)")
|
|
116
|
+
else()
|
|
117
|
+
add_compile_options(-fsanitize=fuzzer-no-link,address,undefined
|
|
118
|
+
-fno-sanitize-recover=undefined -fno-omit-frame-pointer -g)
|
|
119
|
+
add_link_options(-fsanitize=address,undefined)
|
|
120
|
+
message(STATUS "libFuzzer: ENABLED (implies ASAN+UBSan)")
|
|
121
|
+
endif()
|
|
110
122
|
endif()
|
|
111
123
|
|
|
112
124
|
# -------------------------------------------------------------------- LTO --
|
|
@@ -392,6 +392,27 @@ int yep_dom_on_event(void* ctx, const yep_event* ev) {
|
|
|
392
392
|
if (d->depth == 0) {
|
|
393
393
|
return -1;
|
|
394
394
|
}
|
|
395
|
+
if (ev->type == YEP_EV_MAP_END && d->map_pending_key[d->depth - 1]) {
|
|
396
|
+
/* an odd child reached the mapping: the unpaired node —
|
|
397
|
+
* possibly an ANCHORED one — used to be dropped here, its
|
|
398
|
+
* anchor lost, and a later alias then emitted a dangling
|
|
399
|
+
* reference the re-parse rejected (the nightly's round
|
|
400
|
+
* 102). Pair it with a null: "k:" with nothing is a null
|
|
401
|
+
* value in every other position. */
|
|
402
|
+
yep_event nv;
|
|
403
|
+
memset(&nv, 0, sizeof(nv));
|
|
404
|
+
nv.type = YEP_EV_SCALAR;
|
|
405
|
+
nv.style = YEP_STYLE_PLAIN;
|
|
406
|
+
nv.implicit = 1;
|
|
407
|
+
uint32_t nid = dom_new_node(d, &nv, YEP_DOM_SCALAR);
|
|
408
|
+
if (nid == UINT32_MAX) {
|
|
409
|
+
return -1;
|
|
410
|
+
}
|
|
411
|
+
uint32_t top = d->stack[d->depth - 1];
|
|
412
|
+
dom_link(d, top, d->pending_key_id[d->depth - 1]);
|
|
413
|
+
dom_link(d, top, nid);
|
|
414
|
+
d->map_pending_key[d->depth - 1] = 0;
|
|
415
|
+
}
|
|
395
416
|
d->depth--;
|
|
396
417
|
return 0;
|
|
397
418
|
|
|
@@ -729,6 +750,7 @@ static int jb_value(jbuilder* b) {
|
|
|
729
750
|
return 0;
|
|
730
751
|
}
|
|
731
752
|
for (;;) {
|
|
753
|
+
size_t member_start = b->i;
|
|
732
754
|
int rc = jb_value(b);
|
|
733
755
|
if (rc != 0) {
|
|
734
756
|
return rc;
|
|
@@ -738,6 +760,14 @@ static int jb_value(jbuilder* b) {
|
|
|
738
760
|
/* a value must be followed by , or } — the map's key
|
|
739
761
|
* slot is pending until the ':' value completes */
|
|
740
762
|
if (b->i < b->len && b->p[b->i] == ':' && d->map_pending_key[d->depth - 1] != 0) {
|
|
763
|
+
/* libyaml's simple-key law rides the JSON-class
|
|
764
|
+
* route too: a key whose colon distance crosses
|
|
765
|
+
* 1024 characters defers to the engine's clean
|
|
766
|
+
* error (run 38143486175) */
|
|
767
|
+
if (b->i - member_start > 1024 &&
|
|
768
|
+
yep_simple_key_chars(b->p + member_start, b->i - member_start) > 1024) {
|
|
769
|
+
return -2;
|
|
770
|
+
}
|
|
741
771
|
b->i++;
|
|
742
772
|
rc = jb_value(b);
|
|
743
773
|
if (rc != 0) {
|
|
@@ -820,8 +850,11 @@ int dom_on_flow_build(void* ctx, const char* p, size_t open, size_t len, uint32_
|
|
|
820
850
|
yep_json_walk w;
|
|
821
851
|
yep_json_tok t;
|
|
822
852
|
yep_json_walk_init(&w, p, len, open, max_depth);
|
|
823
|
-
w.strict =
|
|
824
|
-
|
|
853
|
+
w.strict = 1; /* the same byte-for-byte JSON gate as the engine's
|
|
854
|
+
* pass 1: a lenient walk split number/literal keys
|
|
855
|
+
* across the fast/slow paths (whole {9->1} vs chunked
|
|
856
|
+
* {"9:1"->null}); flow_strict keeps owning the TAG
|
|
857
|
+
* route only (TODO.restructure/81) */
|
|
825
858
|
for (;;) {
|
|
826
859
|
yep_jw_status st = yep_json_walk_next(&w, &t);
|
|
827
860
|
if (st == YEP_JW_REJECT) {
|
|
@@ -976,6 +1009,23 @@ int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
|
976
1009
|
if (d->depth == 0) {
|
|
977
1010
|
return -1;
|
|
978
1011
|
}
|
|
1012
|
+
if (d->nodes[d->stack[d->depth - 1]].kind == YEP_DOM_MAPPING &&
|
|
1013
|
+
d->map_pending_key[d->depth - 1]) {
|
|
1014
|
+
/* an odd child reached the mapping (see the event-path
|
|
1015
|
+
* MAP_END): pair it with a null instead of dropping it */
|
|
1016
|
+
yep_event nv;
|
|
1017
|
+
memset(&nv, 0, sizeof(nv));
|
|
1018
|
+
nv.type = YEP_EV_SCALAR;
|
|
1019
|
+
nv.style = YEP_STYLE_PLAIN;
|
|
1020
|
+
nv.implicit = 1;
|
|
1021
|
+
uint32_t nid = dom_new_node(d, &nv, YEP_DOM_SCALAR);
|
|
1022
|
+
if (nid == UINT32_MAX) {
|
|
1023
|
+
return -1;
|
|
1024
|
+
}
|
|
1025
|
+
dom_link(d, d->stack[d->depth - 1], d->pending_key_id[d->depth - 1]);
|
|
1026
|
+
dom_link(d, d->stack[d->depth - 1], nid);
|
|
1027
|
+
d->map_pending_key[d->depth - 1] = 0;
|
|
1028
|
+
}
|
|
979
1029
|
d->depth--;
|
|
980
1030
|
break;
|
|
981
1031
|
case YEP_T_NULL:
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
#include "dom/dom.h"
|
|
21
21
|
#include "memory/allocator.h"
|
|
22
22
|
#include "memory/pool.h"
|
|
23
|
+
#include "parse/scalars.h"
|
|
23
24
|
#include "resolve/resolver.h"
|
|
24
25
|
#include "scan/json.h"
|
|
25
26
|
#include "scan/scan.h"
|
|
@@ -355,6 +356,14 @@ static uint32_t yt_flow_scan(const yeptris_json_tape* s, int* long_key) {
|
|
|
355
356
|
return 0xFFFFu; /* deeper than the scratch stack: the
|
|
356
357
|
* general kernel's depth path decides */
|
|
357
358
|
}
|
|
359
|
+
/* the container consumes the parent frame's pending slot
|
|
360
|
+
* (key or value); after it closes the OTHER slot is next —
|
|
361
|
+
* a container value left key_next pinned at 1, and every
|
|
362
|
+
* key after a container value skipped the law (the
|
|
363
|
+
* nightly's round-108 shape: {"": [...], "giant": x}) */
|
|
364
|
+
if (d > 0 && st[d].map) {
|
|
365
|
+
st[d].key_next = (uint8_t)!st[d].key_next;
|
|
366
|
+
}
|
|
358
367
|
d++;
|
|
359
368
|
if ((uint32_t)d > maxd) {
|
|
360
369
|
maxd = (uint32_t)d;
|
|
@@ -371,7 +380,12 @@ static uint32_t yt_flow_scan(const yeptris_json_tape* s, int* long_key) {
|
|
|
371
380
|
}
|
|
372
381
|
if (st[d].map && st[d].key_next == 0) {
|
|
373
382
|
uint32_t mlen = (uint32_t)(r >> 8) & 0xFFFFFFu;
|
|
374
|
-
if (kind == YEP_T_STR && mlen + 2u > 1024u
|
|
383
|
+
if (kind == YEP_T_STR && mlen + 2u > 1024u &&
|
|
384
|
+
yep_simple_key_chars((const char*)s->_src + (size_t)((uint32_t)(r >> 32)), mlen) +
|
|
385
|
+
2 >
|
|
386
|
+
1024) {
|
|
387
|
+
/* the budget counts characters (the round-108 shrink:
|
|
388
|
+
* a 2-byte code point at byte delta 1025 is legal) */
|
|
375
389
|
*long_key = 1;
|
|
376
390
|
}
|
|
377
391
|
st[d].key_next = 1;
|
|
@@ -429,7 +443,9 @@ static int yt_flow_classify_record(yep_ytape* t, const char* p, size_t open, siz
|
|
|
429
443
|
yep_json_walk w;
|
|
430
444
|
yep_json_tok tok;
|
|
431
445
|
yep_json_walk_init(&w, p, len, open, max_depth);
|
|
432
|
-
w.strict =
|
|
446
|
+
w.strict = 1; /* mirrors the engine pass-1 / dom builder gates:
|
|
447
|
+
* reject anything non-JSON so the span falls to
|
|
448
|
+
* the general kernel (whole/chunked agreement) */
|
|
433
449
|
int exhausted = 0;
|
|
434
450
|
for (;;) {
|
|
435
451
|
if (w.i - open > 256) {
|
|
@@ -1128,8 +1144,11 @@ static size_t yt_flat_run(yep_ytape* t, const char* p, size_t pos, size_t len, u
|
|
|
1128
1144
|
}
|
|
1129
1145
|
unsigned char c = (unsigned char)p[li];
|
|
1130
1146
|
if (c == '\t' || c == '-' || c == '&' || c == '!' || c == '*' || c == '\'' || c == '"' ||
|
|
1131
|
-
c == '[' || c == '{' || c == '|' || c == '>' || c == '%' || c == '?'
|
|
1132
|
-
|
|
1147
|
+
c == '[' || c == '{' || c == '|' || c == '>' || c == '%' || c == '?' ||
|
|
1148
|
+
!yep_plain_first_ok(c)) {
|
|
1149
|
+
return pos; /* indicators, tab-led, and the plain-first law
|
|
1150
|
+
(a ']' key rode the flat-run — the nightly's
|
|
1151
|
+
marshal crasher "<: .ba\n]ge: 21"): full arms */
|
|
1133
1152
|
}
|
|
1134
1153
|
if (!stop_set || p[ls] != ':' || (ls + 1 < le && p[ls + 1] != ' ' && p[ls + 1] != '\t')) {
|
|
1135
1154
|
return pos; /* not a plain key: value line */
|
|
@@ -1192,6 +1211,11 @@ static size_t yt_flat_run(yep_ytape* t, const char* p, size_t pos, size_t len, u
|
|
|
1192
1211
|
(vt + 1 >= le || p[vt + 1] == ' ' || p[vt + 1] == '\t' || p[vt + 1] == '#'))) {
|
|
1193
1212
|
return pos; /* block/flow/anchor values: the full arms */
|
|
1194
1213
|
}
|
|
1214
|
+
if (!yep_plain_first_ok(vc)) {
|
|
1215
|
+
return pos; /* ']' '}' ',' '@' '`': no plain value can start
|
|
1216
|
+
* there (the nightly's "k1: --a &p{\n0: ]" marshal
|
|
1217
|
+
* crasher) — the engine errors cleanly */
|
|
1218
|
+
}
|
|
1195
1219
|
/* a terminating ':' inside the value is the mapping-values error */
|
|
1196
1220
|
{
|
|
1197
1221
|
const char* h = (const char*)memchr(p + vt, ':', le - vt);
|
|
@@ -1233,6 +1257,15 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1233
1257
|
st[0].started = 1; /* the root's start event defers to its first pair */
|
|
1234
1258
|
int root_open = 0;
|
|
1235
1259
|
int root_evented = 0;
|
|
1260
|
+
uint32_t root_pair_col = 0; /* the root map's first pair line's column
|
|
1261
|
+
* (0 = none seen): the second-root dedent
|
|
1262
|
+
* law reads this, NOT content_col — the
|
|
1263
|
+
* pending arm never set content_col (" k:
|
|
1264
|
+
* &a", the nightly's run 39064966578,
|
|
1265
|
+
* issue #630) and arming content_col here
|
|
1266
|
+
* instead broke the " k: \n j: w" siblings
|
|
1267
|
+
* (the next nightly's crash-753ce4c1: the
|
|
1268
|
+
* map dissolved into four root scalars) */
|
|
1236
1269
|
(void)yt_put(t, ((uint64_t)YTP_EV_COMPACT << 56) | 1u);
|
|
1237
1270
|
(void)yt_put(t, ((uint64_t)YTP_EV_COMPACT << 56) | 3u);
|
|
1238
1271
|
size_t pos = 0;
|
|
@@ -1362,6 +1395,28 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1362
1395
|
} else {
|
|
1363
1396
|
uint8_t of = yt_f_open_form(&st[depth - 1], depth == 1, st[depth].indent);
|
|
1364
1397
|
yt_f_open(F, &st[depth], of);
|
|
1398
|
+
if (depth == 1 && st[0].content_col == 0) {
|
|
1399
|
+
/* the root bookkeeping the anchored branch carries:
|
|
1400
|
+
* without it a dedent past the root map's column
|
|
1401
|
+
* pops to a content_col-0 st[0] and the pair arm
|
|
1402
|
+
* read "root pairs at column 0" — the nightly's
|
|
1403
|
+
* run 38128905644, " american:\n - Boston...\n
|
|
1404
|
+
* national:" split into PHANTOM DOCUMENTS
|
|
1405
|
+
* ("---\nb\n---\nv") while the engine rejected */
|
|
1406
|
+
st[0].content_col = st[depth].indent + 1;
|
|
1407
|
+
}
|
|
1408
|
+
if (st[depth].content_col == 0) {
|
|
1409
|
+
/* the opened map's pairs live at the KEY's
|
|
1410
|
+
* column (the anchored branch tracks this;
|
|
1411
|
+
* without it the off-column guard never fires
|
|
1412
|
+
* and a key line BETWEEN the resolved seq's
|
|
1413
|
+
* column and the parent's fell through to a
|
|
1414
|
+
* SILENTLY TRUNCATED document — the nightly's
|
|
1415
|
+
* run 38118641722, "ue:\n - one\n woc:\n
|
|
1416
|
+
* - one\n wt o- : thrt" dropped lines 3-5
|
|
1417
|
+
* while the engine rejected at 3:2) */
|
|
1418
|
+
st[depth].content_col = st[depth].indent + 1;
|
|
1419
|
+
}
|
|
1365
1420
|
root_open = 1;
|
|
1366
1421
|
st[depth].pending = 0;
|
|
1367
1422
|
}
|
|
@@ -1380,6 +1435,22 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1380
1435
|
while (depth > 0 && (st[depth].kind == 1 ? (col < st[depth].indent ||
|
|
1381
1436
|
(col <= st[depth].indent && !dash))
|
|
1382
1437
|
: col < st[depth].indent)) {
|
|
1438
|
+
if (st[depth].kind == 1 && col == st[depth].indent && !dash &&
|
|
1439
|
+
!(depth > 1 && st[depth - 1].kind == 0 &&
|
|
1440
|
+
st[depth - 1].indent == st[depth].indent)) {
|
|
1441
|
+
return 1; /* a non-dash line at an INDENTED seq's
|
|
1442
|
+
* column: the seq owns its column, the
|
|
1443
|
+
* pair cannot join it and cannot close
|
|
1444
|
+
* it (libyaml: "did not find expected
|
|
1445
|
+
* '-' indicator"; the nightly's run
|
|
1446
|
+
* 38117287882, "amer:\n - j\n n:\n
|
|
1447
|
+
* - Bksl,} -- }" — the runner popped
|
|
1448
|
+
* the seq and SILENTLY DROPPED the
|
|
1449
|
+
* lines while the engine rejected).
|
|
1450
|
+
* Only the INDENTLESS seq — parent map
|
|
1451
|
+
* keys at the same column — closes on
|
|
1452
|
+
* a key line there ("k:\n- j\nn: v") */
|
|
1453
|
+
}
|
|
1383
1454
|
if (st[depth].pending) {
|
|
1384
1455
|
return 1; /* empty "key:" mid-doc: the engine owns it */
|
|
1385
1456
|
}
|
|
@@ -1457,6 +1528,40 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1457
1528
|
!(c == '&' || c == '!' || c == '*' || c == '\'' || c == '"' || c == '[' ||
|
|
1458
1529
|
c == '{' || c == '|' || c == '>' || c == '%' || c == '?' || c == '-') &&
|
|
1459
1530
|
yep_plain_first_ok((unsigned char)c)) {
|
|
1531
|
+
if (depth == 0) {
|
|
1532
|
+
if (root_pair_col != 0 && col < root_pair_col) {
|
|
1533
|
+
/* a dedent below the root map's own pair column:
|
|
1534
|
+
* a SECOND root without "---" (issue #630, " k:
|
|
1535
|
+
* &a\n - r\na: b" — the pending-value root arm
|
|
1536
|
+
* never arms content_col, so the off-column
|
|
1537
|
+
* guard below cannot see this; the engine and
|
|
1538
|
+
* libyaml reject with "did not find expected
|
|
1539
|
+
* <document start>") */
|
|
1540
|
+
return 1;
|
|
1541
|
+
}
|
|
1542
|
+
if (root_pair_col == 0) {
|
|
1543
|
+
root_pair_col = col; /* the first root pair owns its column */
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
if (st[depth].kind == 0 &&
|
|
1547
|
+
((st[depth].content_col != 0 && col + 1 != st[depth].content_col &&
|
|
1548
|
+
(col != st[depth].indent || (depth == 0 && st[0].content_col > 1))) ||
|
|
1549
|
+
(st[depth].content_col == 0 && depth > 0 && !st[depth].pending &&
|
|
1550
|
+
col != st[depth].indent))) {
|
|
1551
|
+
/* an OFF-COLUMN pair. No pending key awaits it, so a
|
|
1552
|
+
* more-indented line cannot open a child and a
|
|
1553
|
+
* dedented one is a second root (the catch-39 law;
|
|
1554
|
+
* libyaml: "mapping values are not allowed in this
|
|
1555
|
+
* context"). Two states: a frame with its own content
|
|
1556
|
+
* column rejects every other column; a frame whose
|
|
1557
|
+
* content map already closed (the phantom close above)
|
|
1558
|
+
* only accepts pairs at its own key column. The pop
|
|
1559
|
+
* loop pops on col < indent alone — without these
|
|
1560
|
+
* checks "a:\n b: 1\nc: 2\n d: 3" rode the stale
|
|
1561
|
+
* frame as a phantom sibling (the nightly's run
|
|
1562
|
+
* 38179552341) */
|
|
1563
|
+
return 1;
|
|
1564
|
+
}
|
|
1460
1565
|
uint32_t kend = line_stop;
|
|
1461
1566
|
while (kend > t0 && (p[kend - 1] == ' ' || p[kend - 1] == '\t')) {
|
|
1462
1567
|
kend--;
|
|
@@ -1488,6 +1593,12 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1488
1593
|
return 1; /* an anchor needs a name (libyaml):
|
|
1489
1594
|
the engine owns the error */
|
|
1490
1595
|
}
|
|
1596
|
+
if (ne < line_end && p[ne] == '#') {
|
|
1597
|
+
return 1; /* a glued '#' is junk on the name
|
|
1598
|
+
(libyaml: "did not find expected
|
|
1599
|
+
alphabetic or numeric character") —
|
|
1600
|
+
the engine owns it */
|
|
1601
|
+
}
|
|
1491
1602
|
F->anchor_seq++;
|
|
1492
1603
|
aid = F->anchor_seq;
|
|
1493
1604
|
if (!yt_anchor_table(F)) {
|
|
@@ -1504,8 +1615,24 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1504
1615
|
* (aid rides it: an anchored container resolves through
|
|
1505
1616
|
* the S2900 + anchored-start form, not on_block_open) */
|
|
1506
1617
|
if (st[depth].kind == 0 && st[depth].content_col != 0 &&
|
|
1507
|
-
col + 1 != st[depth].content_col &&
|
|
1508
|
-
|
|
1618
|
+
col + 1 != st[depth].content_col &&
|
|
1619
|
+
(col != st[depth].indent || (depth == 0 && st[0].content_col > 1))) {
|
|
1620
|
+
return 1; /* off-column: a continuation line — the multiline arm;
|
|
1621
|
+
* at the ROOT the indent exemption dies once pairs
|
|
1622
|
+
* live above column 0: a col-0 line under a col-1
|
|
1623
|
+
* root is a DEDENT — a second root without "---",
|
|
1624
|
+
* the engine's error (libyaml: "did not find
|
|
1625
|
+
* expected <document start>") */
|
|
1626
|
+
}
|
|
1627
|
+
if (st[depth].kind != 0) {
|
|
1628
|
+
return 1; /* a pending "key:" over a SEQ frame: the
|
|
1629
|
+
* key cannot join the sequence (libyaml
|
|
1630
|
+
* "mapping values are not allowed in
|
|
1631
|
+
* this context"; the nightly's run
|
|
1632
|
+
* 38127584324, "k:\n- 42\n bar:\n
|
|
1633
|
+
* - 45 e." — the pending pushed UNDER
|
|
1634
|
+
* the seq and materialized as an ITEM
|
|
1635
|
+
* while the engine rejected) */
|
|
1509
1636
|
}
|
|
1510
1637
|
if (st[depth].kind == 0 && st[depth].content_col != 0 &&
|
|
1511
1638
|
col + 1 != st[depth].content_col && st[depth].phantom) {
|
|
@@ -1513,10 +1640,13 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1513
1640
|
st[depth].phantom = 0;
|
|
1514
1641
|
}
|
|
1515
1642
|
if (depth > 0 && st[depth].pending) {
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1643
|
+
/* chained same-column pending ("a:\na:"): resolving
|
|
1644
|
+
* the first via yt_f_open corrupts the frame mapping
|
|
1645
|
+
* when the second's container carries valued pairs —
|
|
1646
|
+
* the OPEN'd map swallows the second key as the
|
|
1647
|
+
* first pair's value (the nightly's run 38110714729
|
|
1648
|
+
* tree corruption). The engine owns the shape */
|
|
1649
|
+
return 1;
|
|
1520
1650
|
}
|
|
1521
1651
|
/* a spent key-SCALAR frame (resolved, phantom closed,
|
|
1522
1652
|
* no map of its own) gives its slot to the sibling —
|
|
@@ -1540,10 +1670,22 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1540
1670
|
st[depth].anchored_first = (uint8_t)(aid != 0);
|
|
1541
1671
|
} else {
|
|
1542
1672
|
if (st[depth].kind == 0 && st[depth].content_col != 0 &&
|
|
1543
|
-
col + 1 != st[depth].content_col &&
|
|
1544
|
-
|
|
1673
|
+
col + 1 != st[depth].content_col &&
|
|
1674
|
+
(col != st[depth].indent || (depth == 0 && st[0].content_col > 1))) {
|
|
1675
|
+
return 1; /* off-column: a continuation line — the multiline arm;
|
|
1676
|
+
* the root's indent exemption dies once pairs
|
|
1677
|
+
* live above column 0 (dedent = second root) */
|
|
1545
1678
|
}
|
|
1546
1679
|
if (depth > 0 && st[depth].pending && st[depth].aid != 0) {
|
|
1680
|
+
if (depth > 1) {
|
|
1681
|
+
/* an anchored pending BELOW the root frame: the
|
|
1682
|
+
* deferral's records only flush at depth 1 — a
|
|
1683
|
+
* deeper one drops its pairs at EOF ("4:\n4: r\n
|
|
1684
|
+
* 5:\n &5\n 5: e" lost the anchored map; the
|
|
1685
|
+
* nightly's run 38202382251) — the engine
|
|
1686
|
+
* resolves it */
|
|
1687
|
+
return 1;
|
|
1688
|
+
}
|
|
1547
1689
|
if (col <= st[depth].indent) {
|
|
1548
1690
|
return 1; /* same-column "content": the anchored value is
|
|
1549
1691
|
* null and this line is a sibling — the engine */
|
|
@@ -1588,6 +1730,14 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1588
1730
|
} else if (depth > 0 && st[depth].pending) {
|
|
1589
1731
|
uint8_t of = yt_f_open_form(&st[depth - 1], depth == 1, st[depth].indent);
|
|
1590
1732
|
yt_f_open(F, &st[depth], of);
|
|
1733
|
+
if (depth == 1 && st[0].content_col == 0) {
|
|
1734
|
+
/* the pending->MAP resolution needs the same
|
|
1735
|
+
* root bookkeeping the seq arm gained (run
|
|
1736
|
+
* 38134498534, " e:\n f: g\nh: i" — the
|
|
1737
|
+
* dedent popped to a content_col-0 root and
|
|
1738
|
+
* the doc accepted what the engine rejects) */
|
|
1739
|
+
st[0].content_col = st[depth].indent + 1;
|
|
1740
|
+
}
|
|
1591
1741
|
root_open = 1;
|
|
1592
1742
|
st[depth].pending = 0;
|
|
1593
1743
|
if (col == st[depth].indent) {
|
|
@@ -1611,6 +1761,10 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1611
1761
|
if (st[depth].phantom) {
|
|
1612
1762
|
yt_f_close(F, st[depth].kind);
|
|
1613
1763
|
st[depth].phantom = 0;
|
|
1764
|
+
st[depth].content_col = 0; /* the content map is
|
|
1765
|
+
* gone: its column must not
|
|
1766
|
+
* greet later pairs (the
|
|
1767
|
+
* stale frame below) */
|
|
1614
1768
|
}
|
|
1615
1769
|
} else {
|
|
1616
1770
|
yt_f_start(F, &st[depth]);
|
|
@@ -1633,6 +1787,29 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1633
1787
|
}
|
|
1634
1788
|
ne++;
|
|
1635
1789
|
}
|
|
1790
|
+
if (ne < line_end && p[ne] == '#') {
|
|
1791
|
+
return 1; /* a glued '#' is junk on the
|
|
1792
|
+
* name (the engine errors:
|
|
1793
|
+
* "did not find expected
|
|
1794
|
+
* alphabetic or numeric
|
|
1795
|
+
* character") */
|
|
1796
|
+
}
|
|
1797
|
+
{
|
|
1798
|
+
size_t ae = ne;
|
|
1799
|
+
while (ae < line_end && (p[ae] == ' ' || p[ae] == '\t')) {
|
|
1800
|
+
ae++;
|
|
1801
|
+
}
|
|
1802
|
+
if (ae < line_end && p[ae] != '#') {
|
|
1803
|
+
return 1; /* an alias node is COMPLETE —
|
|
1804
|
+
* on the line only a blank
|
|
1805
|
+
* then a comment may follow
|
|
1806
|
+
* ("<<: *x &y": the engine
|
|
1807
|
+
* errors, libyaml "did not
|
|
1808
|
+
* find expected key"; this
|
|
1809
|
+
* branch silently dropped the
|
|
1810
|
+
* bytes) */
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1636
1813
|
uint32_t taid = 0;
|
|
1637
1814
|
if (!yt_anchor_get(F, (uint32_t)(vt + 1), (uint32_t)(ne - vt - 1), &taid)) {
|
|
1638
1815
|
return 1; /* undefined alias: the engine errors */
|
|
@@ -1695,10 +1872,25 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1695
1872
|
}
|
|
1696
1873
|
if (vc == '|' && aid == 0 && !st[depth].anchored_first &&
|
|
1697
1874
|
(vt + 1 >= line_end || p[vt + 1] == ' ' || p[vt + 1] == '\t')) {
|
|
1875
|
+
/* the header tail: only blanks and an optional
|
|
1876
|
+
* comment may follow the indicator (libyaml:
|
|
1877
|
+
* "did not find expected comment or line break";
|
|
1878
|
+
* the nightly's run 38119775008, "ext: | c* " —
|
|
1879
|
+
* this arm took the literal and SILENTLY DROPPED
|
|
1880
|
+
* the trailing header bytes while the engine
|
|
1881
|
+
* rejected) */
|
|
1882
|
+
size_t ht = vt + 1;
|
|
1883
|
+
while (ht < line_end && (p[ht] == ' ' || p[ht] == '\t')) {
|
|
1884
|
+
ht++;
|
|
1885
|
+
}
|
|
1886
|
+
if (ht < line_end && p[ht] != '#') {
|
|
1887
|
+
return 1;
|
|
1888
|
+
}
|
|
1698
1889
|
/* bare literal block, clip chomp: measure the block,
|
|
1699
1890
|
* copy it into the finish pool, emit the pool form */
|
|
1700
1891
|
size_t scan = next;
|
|
1701
1892
|
uint32_t bindent = 0;
|
|
1893
|
+
uint32_t lead_blank_max = 0;
|
|
1702
1894
|
int have_bi = 0, have_content = 0;
|
|
1703
1895
|
size_t clen = 0, tb = 0;
|
|
1704
1896
|
size_t end_scan = scan;
|
|
@@ -1730,6 +1922,9 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1730
1922
|
/* a blank line: spaces past the block indent are
|
|
1731
1923
|
* CONTENT; only stripped-empty lines clip away */
|
|
1732
1924
|
size_t ind = ie - ls;
|
|
1925
|
+
if (!have_content && ind > lead_blank_max) {
|
|
1926
|
+
lead_blank_max = (uint32_t)ind;
|
|
1927
|
+
}
|
|
1733
1928
|
size_t res = have_bi && ind > bindent ? ind - bindent : 0;
|
|
1734
1929
|
clen += res + 1;
|
|
1735
1930
|
if (res == 0) {
|
|
@@ -1745,6 +1940,22 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1745
1940
|
if (ind <= col) {
|
|
1746
1941
|
break; /* dedented past the block */
|
|
1747
1942
|
}
|
|
1943
|
+
if (ind < lead_blank_max) {
|
|
1944
|
+
return 1; /* a LEADING blank line
|
|
1945
|
+
* with MORE indent
|
|
1946
|
+
* floors the block
|
|
1947
|
+
* (libyaml's
|
|
1948
|
+
* auto-detect takes
|
|
1949
|
+
* the blanks' max;
|
|
1950
|
+
* content below the
|
|
1951
|
+
* floor breaks the
|
|
1952
|
+
* map — Psych: "did
|
|
1953
|
+
* not find expected
|
|
1954
|
+
* key"; the nightly's
|
|
1955
|
+
* run 38163867815,
|
|
1956
|
+
* "a: |\n \n b"):
|
|
1957
|
+
* the engine's error */
|
|
1958
|
+
}
|
|
1748
1959
|
bindent = ind;
|
|
1749
1960
|
have_bi = 1;
|
|
1750
1961
|
} else if (ind < bindent) {
|
|
@@ -1768,7 +1979,9 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1768
1979
|
spilled = 1;
|
|
1769
1980
|
}
|
|
1770
1981
|
end_scan =
|
|
1771
|
-
(le < len && p[le] == '\r' && le + 1 < len
|
|
1982
|
+
(le < len && p[le] == '\r' && le + 1 < len && p[le + 1] == '\n')
|
|
1983
|
+
? le + 2
|
|
1984
|
+
: le + 1; /* lone CR is a 1-byte break */
|
|
1772
1985
|
scan = end_scan;
|
|
1773
1986
|
}
|
|
1774
1987
|
if (!have_content) {
|
|
@@ -1781,7 +1994,14 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1781
1994
|
return 1;
|
|
1782
1995
|
}
|
|
1783
1996
|
}
|
|
1784
|
-
|
|
1997
|
+
/* the BLOCK is clen-sized: both copy paths write
|
|
1998
|
+
* every measured line (the clip lives in the
|
|
1999
|
+
* SPAN, not the copy) — allocating flen wrote
|
|
2000
|
+
* tb bytes past the block whenever a literal
|
|
2001
|
+
* ended with stripped-empty lines (the nightly's
|
|
2002
|
+
* run 38137087116 ASan heap-buffer-overflow,
|
|
2003
|
+
* WRITE at ytape.c's re-walk memcpy) */
|
|
2004
|
+
char* dst = (char*)yep_pool_alloc(F->pool, clen, 16);
|
|
1785
2005
|
if (dst == NULL) {
|
|
1786
2006
|
return 1;
|
|
1787
2007
|
}
|
|
@@ -1828,8 +2048,10 @@ static int yt_f_run(yt_fused* F) {
|
|
|
1828
2048
|
q += le - (ls + bindent);
|
|
1829
2049
|
*q++ = '\n';
|
|
1830
2050
|
}
|
|
1831
|
-
s2 = (le < len && p[le] == '\r' && le + 1 < len
|
|
1832
|
-
|
|
2051
|
+
s2 = (le < len && p[le] == '\r' && le + 1 < len &&
|
|
2052
|
+
p[le + 1] == '\n')
|
|
2053
|
+
? le + 2
|
|
2054
|
+
: le + 1; /* lone CR is a 1-byte break */
|
|
1833
2055
|
}
|
|
1834
2056
|
}
|
|
1835
2057
|
if ((size_t)(q - dst) != clen) {
|