yeptris 0.6.60.1-arm-linux-eabihf → 0.6.61.1-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e078deaa97a27d09dc305c57e5d879ec727b506c0edfd6c1ef27a3fa7bb8d98
4
- data.tar.gz: 9d3a325cba29a0c5c51f33be9c99ccca3ce4febdb46dd4595d2e7d3dbb73988e
3
+ metadata.gz: 62af06f8d7d9384dde77c3cca415da00be7d70f65505119fd43a82fa9e0c9e1f
4
+ data.tar.gz: afc2489139cdc52bb5d7f2a96382150c9cb78eca767741a806cbdd2c231b66bd
5
5
  SHA512:
6
- metadata.gz: fa97a7e1b0332bcd8106b1c244d5a1b11641ff28c5be0fba465a84095cfc120a87e469a23e32acd7e19c6f1dbddabb2ce0c6b7eb372e3848a0866140d0771670
7
- data.tar.gz: a54e2f72f58ef07db3ef50755aa03bfbc41381770f252387bc331e7c71ead5818bb8b352197960336f69056ebe04e25a3dc272ee43032645facddd7658239a30
6
+ metadata.gz: f7d3ecc7415faae434ffb16d3bf6578d5aa9130c98dc6057a5dca4ceecf2a5d6737a213f70c8fd2538a9a8e5a4015b80b526e557d8382ca0800ca61b1a235f2b
7
+ data.tar.gz: 114d8c80fb7ada38b45b9bba05b44c6b7087c3b6dde852bcdc0004fabaaa13211742122f557adc6f21a1c2b61e0e7e0eda135bb5f1beafebdacf7f07f7585823
@@ -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 60
15
- #define YEPTRIS_VERSION_STRING "0.6.60"
14
+ #define YEPTRIS_VERSION_PATCH 61
15
+ #define YEPTRIS_VERSION_STRING "0.6.61"
16
16
 
17
17
  #ifdef __cplusplus
18
18
  extern "C" {
data/lib/yeptris/yaml.rb CHANGED
@@ -350,6 +350,16 @@ module Yeptris
350
350
  Yeptris::FFI::BUILD_END => END_ENTRY }.freeze
351
351
 
352
352
  def dump(obj, canonical: false, header: false, width: nil)
353
+ # The nil root rides the bare form (psych's dump(nil) == "\n",
354
+ # #290): the engine's r201 quoted-form law ("a document-root
355
+ # empty scalar rides the quoted form" -- the plain form does not
356
+ # re-parse, the roundtrip invariant) renders the built empty
357
+ # scalar as "" -- correct for the engine's generic serializer,
358
+ # wrong for the psych surface. The binding maps an empty stream
359
+ # back to nil on load (load\#docs.empty?\#nil), so the bare form
360
+ # round-trips THROUGH THE BINDING while keeping the engine's
361
+ # invariant untouched.
362
+ return header ? "--- \n" : "\n" if obj.nil? && !canonical
353
363
  parts = []
354
364
  blob = String.new(encoding: Encoding::BINARY)
355
365
  off = [0]
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.60.1".freeze
6
+ VERSION = "0.6.61.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.60
6
+ VERSION 0.6.61
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -91,6 +91,27 @@ YEPTRIS_API YeptrisStatus yeptris_stream_iter_next(yeptris_stream_iter* it, Yept
91
91
  YEPTRIS_API YeptrisStatus yeptris_stream_iter_status(const yeptris_stream_iter* it, uint32_t* line,
92
92
  uint32_t* col);
93
93
 
94
+ /* The last successful step's document SLICE inside the iterator's
95
+ * buffer space (post-gate: BOM/transcode/normalization may have moved
96
+ * the buffer — the offsets are relative to the CURRENT buf the
97
+ * iterator holds, which is the space `yeptris_stream_iter_next`'s
98
+ * documents live in). Hosts that materialize through their own value
99
+ * route (the Python/Rust/TS drains) re-parse exactly these bytes and
100
+ * inherit the engine's framing without a DOM walk: one typing SSOT —
101
+ * the drain — and scanner-accurate frames. ARG on NULL; ARG when the
102
+ * last step was not a successful document. */
103
+ YEPTRIS_API YeptrisStatus yeptris_stream_iter_slice(const yeptris_stream_iter* it, size_t* off,
104
+ size_t* len);
105
+
106
+ /* The iterator's ACTIVE buffer (the caller's bytes, or the post-gate
107
+ * owned copy after a BOM strip / UTF-16-32 transcode / NEL-LS-PS
108
+ * normalization) and its length. `yeptris_stream_iter_slice`'s offsets
109
+ * index THIS buffer — hosts copy their frame bytes out of here
110
+ * (ctypes.string_at / Rust slice / TS Buffer) and re-parse them through
111
+ * their own drain. Borrowed: valid until yeptris_stream_iter_free. */
112
+ YEPTRIS_API YeptrisStatus yeptris_stream_iter_buffer(const yeptris_stream_iter* it,
113
+ const char** buf, size_t* len);
114
+
94
115
  YEPTRIS_API void yeptris_stream_iter_free(yeptris_stream_iter* it);
95
116
 
96
117
  #ifdef __cplusplus
@@ -158,7 +158,18 @@ static yep_sview dom_str_in(yep_dom* d, const yep_view* v, int borrowed) {
158
158
  yep_sview sv = {0, 0};
159
159
  return sv;
160
160
  }
161
- if (borrowed && v->p != NULL && d->input_base != NULL && (const char*)v->p >= d->input_base) {
161
+ /* The zero-copy law: borrow ONLY a view demonstrably inside the
162
+ * recorded input window. The old heap-order probe (v->p >=
163
+ * input_base) misfiled every engine-owned buffer that happened to
164
+ * sit above the caller's input — the r171 normalizer copy path
165
+ * (norm_buf) hands exactly such views to the pair/event builders —
166
+ * and the resulting input-relative offset was then layout luck per
167
+ * platform/build (the ubuntu-gcc / MSVC-arm / TSan / UBSan CI
168
+ * divergences of PR #1042). The upper bound makes the decision a
169
+ * pure function of the view's location class: both engines of a
170
+ * differential run now store identically on every platform. */
171
+ if (borrowed && v->p != NULL && d->input_base != NULL &&
172
+ (size_t)((const char*)v->p - d->input_base) + v->len <= d->input_len) {
162
173
  return yep_sv_input(d, *v);
163
174
  }
164
175
  return yep_dom_str_put(d, (const char*)v->p, v->len);
@@ -1640,15 +1640,64 @@ static int yt_f_run(yt_fused* F) {
1640
1640
  col + 1 != st[depth].content_col && st[depth].phantom) {
1641
1641
  yt_f_close(F, st[depth].kind);
1642
1642
  st[depth].phantom = 0;
1643
+ st[depth].content_col =
1644
+ 0; /* the content map is gone:
1645
+ * its column must not greet later pairs (the
1646
+ * stale-frame law, run 38179552341) — after the
1647
+ * chained-pending arm resumes here, a stale
1648
+ * column rode yt_f_open_form's sibling test and
1649
+ * misfiled the next pending's container as a
1650
+ * key-SCALAR ("b:\n c: d\nq:\n b:\n c: d\n"
1651
+ * lost q's map; the nightly's run 42488986434) */
1643
1652
  }
1644
1653
  if (depth > 0 && st[depth].pending) {
1645
- /* chained same-column pending ("a:\na:"): resolving
1646
- * the first via yt_f_open corrupts the frame mapping
1647
- * when the second's container carries valued pairs —
1648
- * the OPEN'd map swallows the second key as the
1649
- * first pair's value (the nightly's run 38110714729
1650
- * tree corruption). The engine owns the shape */
1651
- return 1;
1654
+ /* a chained pending ("a:\na:", the deep-nesting
1655
+ * "k:\n k:\n k:" ladder): resolve the outer
1656
+ * one before the sibling's frame takes the slot.
1657
+ * A same-column sibling key (col == the pending's
1658
+ * own column) gives the pending a NULL value —
1659
+ * the null pair MUST land now, or the OPEN'd
1660
+ * container swallows the sibling key as its
1661
+ * first pair's value (the nightly's run
1662
+ * 38110714729 tree corruption: {a: a, ?{b: c}:
1663
+ * h} instead of {a: null, a: {b: c}, h: v}).
1664
+ * When the OPEN form fired, the null pairs
1665
+ * INSIDE the opened container (parent → {k:
1666
+ * null}); under the key-SCALAR form it completes
1667
+ * the key's pair in the parent's map. A deeper
1668
+ * line (col > indent) opens the pending's
1669
+ * container and the chain nests. Anchored
1670
+ * pendings keep the engine: their anchor rides
1671
+ * the event path (yt_f_open drops the name) */
1672
+ if (st[depth].aid != 0) {
1673
+ return 1;
1674
+ }
1675
+ uint8_t of = yt_f_open_form(&st[depth - 1], depth == 1, st[depth].indent);
1676
+ if (aid != 0 && of == 0) {
1677
+ /* the outer pending resolved as a SIBLING
1678
+ * key-SCALAR: its value container is "the pair's",
1679
+ * materialized by the next OPEN-class record — but
1680
+ * the anchored pending resolves through the event
1681
+ * path, which emits its KEY SCALAR *before* the
1682
+ * anchored MAP_START. That key pairs with the
1683
+ * stranded outer key (the pair's value slot) and
1684
+ * the anchored map strands as a sibling key of the
1685
+ * parent: "2: e\n4:\n 5: &5\n 5: e\n7: s"
1686
+ * built {2:e, 4:"5", {5:e}&5:"7", s:null} instead
1687
+ * of {2:e, 4:{5:{5:e}&5}, 7:s} (the nightly's
1688
+ * crash-d8e3b3f392c37a62257576ce20d5a19b351f66ad,
1689
+ * round 213 — the first shape where a completed
1690
+ * pair arms root_pair_col BEFORE the chain). The
1691
+ * engine resolves the composition. */
1692
+ return 1;
1693
+ }
1694
+ yt_f_open(F, &st[depth], of);
1695
+ root_open = 1;
1696
+ if (col == st[depth].indent) {
1697
+ (void)yt_put(t, yt_props(YTP_SCALAR, 0, 0x0904u));
1698
+ yt_f_empty_span(F);
1699
+ }
1700
+ st[depth].pending = 0;
1652
1701
  }
1653
1702
  /* a spent key-SCALAR frame (resolved, phantom closed,
1654
1703
  * no map of its own) gives its slot to the sibling —
@@ -112,6 +112,7 @@ YEPTRIS_API size_t yeptris_serialize_into_ex(YeptrisDocument handle,
112
112
  em.w.col = 0;
113
113
  em.w.sv_input = handle ? ((const yeptris_document*)handle)->dom->input_base : NULL;
114
114
  em.w.sv_arena = handle ? ((const yeptris_document*)handle)->dom->str : NULL;
115
+ em.w.resolver = handle ? ((const yeptris_document*)handle)->dom->resolver : NULL;
115
116
  em.w.sink = NULL;
116
117
  em.w.watermark = 0;
117
118
  em.w.sink_aborted = 0;
@@ -181,6 +182,7 @@ YEPTRIS_API size_t yeptris_serialize_into_trusted(YeptrisDocument handle,
181
182
  em.w.col = 0;
182
183
  em.w.sv_input = ((const yeptris_document*)handle)->dom->input_base;
183
184
  em.w.sv_arena = ((const yeptris_document*)handle)->dom->str;
185
+ em.w.resolver = ((const yeptris_document*)handle)->dom->resolver;
184
186
  em.w.sink = NULL;
185
187
  em.w.watermark = 0;
186
188
  em.w.sink_aborted = 0;
@@ -249,6 +251,7 @@ YEPTRIS_API char* yeptris_serialize_ex(YeptrisDocument handle, const yeptris_emi
249
251
  em.w.col = 0;
250
252
  em.w.sv_input = handle ? ((const yeptris_document*)handle)->dom->input_base : NULL;
251
253
  em.w.sv_arena = handle ? ((const yeptris_document*)handle)->dom->str : NULL;
254
+ em.w.resolver = handle ? ((const yeptris_document*)handle)->dom->resolver : NULL;
252
255
  em.w.sink = NULL;
253
256
  em.w.watermark = 0;
254
257
  em.w.sink_aborted = 0;
@@ -390,6 +393,7 @@ YEPTRIS_API char* yeptris_serialize_json(YeptrisDocument handle, size_t* len) {
390
393
  em.w.col = 0;
391
394
  em.w.sv_input = handle ? ((const yeptris_document*)handle)->dom->input_base : NULL;
392
395
  em.w.sv_arena = handle ? ((const yeptris_document*)handle)->dom->str : NULL;
396
+ em.w.resolver = handle ? ((const yeptris_document*)handle)->dom->resolver : NULL;
393
397
  em.w.sink = NULL;
394
398
  em.w.watermark = 0;
395
399
  em.w.sink_aborted = 0;
@@ -434,6 +438,7 @@ YEPTRIS_API char* yeptris_serialize_json_ex(YeptrisDocument handle, size_t* len,
434
438
  em.w.col = 0;
435
439
  em.w.sv_input = handle ? ((const yeptris_document*)handle)->dom->input_base : NULL;
436
440
  em.w.sv_arena = handle ? ((const yeptris_document*)handle)->dom->str : NULL;
441
+ em.w.resolver = handle ? ((const yeptris_document*)handle)->dom->resolver : NULL;
437
442
  em.w.sink = NULL;
438
443
  em.w.watermark = 0;
439
444
  em.w.sink_aborted = 0;
@@ -480,6 +485,7 @@ char* yep_serialize_json_compact(const yeptris_document* doc, size_t* len) {
480
485
  em.w.col = 0;
481
486
  em.w.sv_input = doc->dom->input_base;
482
487
  em.w.sv_arena = doc->dom->str;
488
+ em.w.resolver = doc->dom->resolver;
483
489
  em.w.sink = NULL;
484
490
  em.w.watermark = 0;
485
491
  em.w.sink_aborted = 0;
@@ -528,6 +534,7 @@ char* yep_serialize_json_pretty_ex(const yeptris_document* doc, size_t* len, con
528
534
  em.w.col = 0;
529
535
  em.w.sv_input = doc->dom->input_base;
530
536
  em.w.sv_arena = doc->dom->str;
537
+ em.w.resolver = doc->dom->resolver;
531
538
  em.w.sink = NULL;
532
539
  em.w.watermark = 0;
533
540
  em.w.sink_aborted = 0;
@@ -604,6 +611,7 @@ YEPTRIS_API size_t yeptris_serialize_stream(YeptrisDocument handle,
604
611
  em.w.col = 0;
605
612
  em.w.sv_input = handle ? ((const yeptris_document*)handle)->dom->input_base : NULL;
606
613
  em.w.sv_arena = handle ? ((const yeptris_document*)handle)->dom->str : NULL;
614
+ em.w.resolver = handle ? ((const yeptris_document*)handle)->dom->resolver : NULL;
607
615
  em.w.sink = sink;
608
616
  em.w.sink_ctx = ctx;
609
617
  em.w.watermark = YEP_EMIT_WATERMARK;
@@ -539,7 +539,33 @@ static void emit_literal(yep_writer* w, const yep_dnode* n, int parent_col) {
539
539
  /* Canonical scalar (13B): typed words and numbers plain, everything
540
540
  * else double-quoted; floats re-print through the shortest printer so
541
541
  * canonical output is a fixed point (parse of canonical re-prints
542
- * identically — the byte-stability gate). */
542
+ * identically — the byte-stability gate). The DOM stores only the tag
543
+ * + source text, so the typed arms re-derive the VALUE from the
544
+ * resolver's own tables (#937): the bool table decides true/false
545
+ * (the old length guess spelled every non-4-char true-word false), the
546
+ * int decode spells JSON-unspellable forms (0x1F/0o7) in decimal, and
547
+ * the inf/nan words ride quoted in JSON mode (no JSON infinity). */
548
+ static int json_int_token(const char* p, uint32_t n) {
549
+ /* the strict JSON int token: -?(0|[1-9][0-9]*) — leading zeros
550
+ * and a '+' are YAML-spellable but not JSON (those decode) */
551
+ uint32_t i = 0;
552
+ if (i < n && p[0] == '-') {
553
+ i = 1;
554
+ }
555
+ if (i >= n) {
556
+ return 0;
557
+ }
558
+ if (p[i] == '0') {
559
+ return i + 1 == n;
560
+ }
561
+ for (uint32_t k = i; k < n; k++) {
562
+ if (p[k] < '0' || p[k] > '9') {
563
+ return 0;
564
+ }
565
+ }
566
+ return 1;
567
+ }
568
+
543
569
  static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
544
570
  yep_view vdec = wv(w, n->value);
545
571
  const char* p = (const char*)vdec.p;
@@ -553,17 +579,24 @@ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
553
579
  wr_put(w, "~", 1);
554
580
  }
555
581
  return;
556
- case YEPTRIS_TAG_BOOL:
557
- if (len == 4) { /* true/TRUE/True/null-ish variants */
558
- wr_put(w, (p[0] == 't' || p[0] == 'T') ? "true" : "null", 4);
559
- } else { /* false family (5 chars) */
560
- wr_put(w, "false", 5);
582
+ case YEPTRIS_TAG_BOOL: {
583
+ int bv = yep_tag_bool_value(p, len);
584
+ if (bv < 0) {
585
+ break; /* not a bool word: quoted (the resolver only
586
+ tags the table's words; defense) */
561
587
  }
588
+ wr_put(w, bv ? "true" : "false", bv ? 4u : 5u);
562
589
  return;
590
+ }
563
591
  case YEPTRIS_TAG_FLOAT: {
564
592
  const char* word = yep_tag_float_word(p, len);
565
593
  if (word != NULL) {
566
- wr_put(w, word, (uint32_t)strlen(word));
594
+ uint32_t wl = (uint32_t)strlen(word);
595
+ if (w->json) {
596
+ emit_dq_json(w, word, wl);
597
+ } else {
598
+ wr_put(w, word, wl);
599
+ }
567
600
  return;
568
601
  }
569
602
  char buf[40];
@@ -584,8 +617,23 @@ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
584
617
  break; /* unparseable view: fall through to quoting */
585
618
  }
586
619
  case YEPTRIS_TAG_INT:
587
- wr_put(w, p, len);
588
- return;
620
+ if (!w->json || json_int_token(p, len)) {
621
+ /* YAML keeps the source text (stable, lossless); JSON
622
+ * keeps JSON-number-shaped text (big ints stay exact) */
623
+ wr_put(w, p, len);
624
+ return;
625
+ }
626
+ {
627
+ int compat = w->resolver == yep_resolver_compat11();
628
+ int64_t v;
629
+ if (yep_tag_int64(p, len, compat, &v) == 0) {
630
+ char buf[24];
631
+ int nl = snprintf(buf, sizeof buf, "%lld", (long long)v);
632
+ wr_put(w, buf, (uint32_t)nl);
633
+ return;
634
+ }
635
+ }
636
+ break; /* undecodable int text: quoted keeps JSON valid */
589
637
  default:
590
638
  break;
591
639
  }
@@ -1602,6 +1650,24 @@ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key)
1602
1650
  }
1603
1651
  emit_tag(w, n);
1604
1652
  emit_anchor_ex(em, n);
1653
+ if (parent_col < 0 && !w->canonical && !w->json && n->kind == 0 && n->style <= 1) {
1654
+ /* A DOCUMENT-ROOT empty scalar rides the quoted form: libyaml
1655
+ * renders "--- ''" and never a bare marker with nothing after
1656
+ * it. A plain-empty root emitted as NOTHING (the value law of
1657
+ * issue #290, "k:" — kept below for non-root contexts) sheds
1658
+ * the root node entirely; the re-parse of that stream produced
1659
+ * an ANY-styled empty root whose render is "" — and the bytes
1660
+ * diverged on round two (the nightly's r201 fuzz_roundtrip
1661
+ * byte-instability catch, ">\n! \n !<aaa: bbb\n...\n<NEL><NEL>",
1662
+ * whose second document's break-only content parses to a
1663
+ * plain-empty root). Quoting the root's emptiness makes every
1664
+ * empty-root spelling converge on the same fixpoint bytes. */
1665
+ yep_view vdec = wv(w, n->value);
1666
+ if (vdec.len == 0) {
1667
+ emit_dq(w, (const char*)vdec.p, vdec.len);
1668
+ return;
1669
+ }
1670
+ }
1605
1671
  if (parent_col < 0) {
1606
1672
  parent_col = 0; /* root scalar: block body at column 2 */
1607
1673
  }
@@ -1755,7 +1821,16 @@ size_t yep_emit_run(yep_emitter* em, int dry) {
1755
1821
  yep_view tdec3 = wv(w, root->tag);
1756
1822
  wr_tag_token(w, &tdec3);
1757
1823
  }
1758
- if (root->anchor.len > 0) {
1824
+ if (root->anchor.len > 0 && !anchor_name_unspellable(w, root)) {
1825
+ /* the r235 law: the lead line writes the name UNQUOTED
1826
+ * and anchor names have no quoting -- a name the spell
1827
+ * law rejects has no safe lead-line spelling. The r235
1828
+ * nightly's riding-colon names ("&:" and the artifact's
1829
+ * "&k:\"" — run 42739927952, crash-5de6f9ff) re-parsed
1830
+ * as an anchored empty KEY of the follow-on collection
1831
+ * (the anchor lost, s1 != s2, byte growth); the
1832
+ * colon-tail law fails the serialize like every other
1833
+ * anchor write path (the r228 "&: |" law) */
1759
1834
  if (root->tag.len > 0) {
1760
1835
  wr_byte(w, ' '); /* props need the separator */
1761
1836
  }
@@ -69,13 +69,16 @@ typedef struct yep_writer {
69
69
  * grow/flush checks run once per refill instead of once per byte;
70
70
  * 0 at run entry and whenever the guarantee lapses. */
71
71
  size_t reserve;
72
- int in_flow; /* scalars inside flow collections: plain must drop
73
- ',', '[', ']', '{', '}' — flow terminators */
74
- int flow_item; /* the NEXT scalar is a flow-SEQ item: a null must
75
- emit '~' (a bare nothing reads as "[, x]") */
76
- int unserializable; /* an anchor name that cannot ride raw in flow
77
- context: the document has no safe spelling —
78
- serialize fails rather than corrupt */
72
+ int in_flow; /* scalars inside flow collections: plain must drop
73
+ ',', '[', ']', '{', '}' — flow terminators */
74
+ int flow_item; /* the NEXT scalar is a flow-SEQ item: a null must
75
+ emit '~' (a bare nothing reads as "[, x]") */
76
+ int unserializable; /* an anchor name that cannot ride raw in flow
77
+ context: the document has no safe spelling —
78
+ serialize fails rather than corrupt */
79
+ const struct yep_resolver* resolver; /* the document's implicit-typing
80
+ family (NULL = core12): the INT arm's decode needs the family's
81
+ own forms (leading-0 is octal in compat, decimal in core) */
79
82
  } yep_writer;
80
83
 
81
84
  typedef struct yep_emitter {
@@ -452,6 +452,28 @@ int yep_values_from_input(const char* yaml, size_t len, int schema_compat, yep_v
452
452
  }
453
453
  /* fall through: engine */
454
454
  }
455
+ /* Unicode breaks (NEL/LS/PS) normalize to '\n' BEFORE the
456
+ * engine, exactly as the parse entry's front-end does
457
+ * (parse_frontend): this drain feeds the SAME compat-grammar
458
+ * engine, and a raw NEL it never normalized errors where
459
+ * parse accepts -- the nightly's round 239 roundtrip catch
460
+ * ("<NEL>...<NEL> >\n <NEL>...<NEL>\nt": marshal returned
461
+ * ERROR_PARSE on a document yeptris_parse_ex accepted and
462
+ * serialized cleanly). JSON stays above this: the JSON reader
463
+ * keeps U+0085 raw inside strings, only the YAML reader
464
+ * normalizes (the parse_frontend law). Ownership rides the
465
+ * same `transcoded` plumbing the transcode above installed. */
466
+ {
467
+ size_t nlen = 0;
468
+ int kept_raw = 0;
469
+ char* norm = yep_engine_normalize_breaks(sys, data, data_len, &nlen, &kept_raw);
470
+ if (norm != NULL) {
471
+ yep_free(sys, transcoded);
472
+ transcoded = (unsigned char*)norm;
473
+ data = (const char*)norm;
474
+ data_len = nlen;
475
+ }
476
+ }
455
477
  yep_engine* eng = yep_engine_create(yep_system_allocator());
456
478
  if (eng == NULL) {
457
479
  return -1;