yeptris 0.6.32.1-arm-linux-eabihf → 0.6.33.2-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.
@@ -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 = t->flow_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
- return pos; /* indicators, tab-led: the full arms */
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 && col != st[depth].indent) {
1508
- return 1; /* off-column: a continuation line — the multiline arm */
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
- uint8_t of = yt_f_open_form(&st[depth - 1], depth == 1, st[depth].indent);
1517
- yt_f_open(F, &st[depth], of);
1518
- root_open = 1;
1519
- st[depth].pending = 0;
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 && col != st[depth].indent) {
1544
- return 1; /* off-column: a continuation line — the multiline arm */
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) ? le + 2 : le + 1;
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
- char* dst = (char*)yep_pool_alloc(F->pool, flen, 16);
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) ? le + 2
1832
- : le + 1;
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) {
@@ -51,6 +51,9 @@ YEPTRIS_API size_t yeptris_serialize_into_ex(YeptrisDocument handle,
51
51
  em.w.p = NULL;
52
52
  em.w.last = 0;
53
53
  em.w.force_flow = 0;
54
+ em.w.in_flow = 0;
55
+ em.w.flow_item = 0;
56
+ em.w.unserializable = 0;
54
57
  em.w.canonical = opts_canonical(opts);
55
58
  em.w.explicit_doc_start = opts_doc_marker(opts);
56
59
  em.w.json = 0;
@@ -116,6 +119,9 @@ YEPTRIS_API char* yeptris_serialize_ex(YeptrisDocument handle, const yeptris_emi
116
119
  em.w.p = NULL;
117
120
  em.w.last = 0;
118
121
  em.w.force_flow = 0;
122
+ em.w.in_flow = 0;
123
+ em.w.flow_item = 0;
124
+ em.w.unserializable = 0;
119
125
  em.w.canonical = opts_canonical(opts);
120
126
  em.w.explicit_doc_start = opts_doc_marker(opts);
121
127
  em.w.json = 0;
@@ -160,7 +166,7 @@ YEPTRIS_API char* yeptris_serialize_ex(YeptrisDocument handle, const yeptris_emi
160
166
  * any growth; every post-run touch goes through the writer's
161
167
  * pointer (the first round's abort was exactly this aliasing) */
162
168
  out = em.w.p;
163
- if (em.w.oom) {
169
+ if (em.w.oom || em.w.unserializable) {
164
170
  free(out);
165
171
  if (em.canon_names.sys != NULL) {
166
172
  yep_nametab_free(&em.canon_names);
@@ -200,6 +206,9 @@ YEPTRIS_API char* yeptris_serialize_json(YeptrisDocument handle, size_t* len) {
200
206
  em.w.p = NULL;
201
207
  em.w.last = 0;
202
208
  em.w.force_flow = 0;
209
+ em.w.in_flow = 0;
210
+ em.w.flow_item = 0;
211
+ em.w.unserializable = 0;
203
212
  em.w.canonical = 0;
204
213
  em.w.explicit_doc_start = 0; /* JSON output carries no --- */
205
214
  em.w.json = 1;
@@ -258,6 +267,9 @@ YEPTRIS_API char* yeptris_serialize_json_ex(YeptrisDocument handle, size_t* len,
258
267
  em.w.p = NULL;
259
268
  em.w.last = 0;
260
269
  em.w.force_flow = 0;
270
+ em.w.in_flow = 0;
271
+ em.w.flow_item = 0;
272
+ em.w.unserializable = 0;
261
273
  em.w.canonical = 0;
262
274
  em.w.explicit_doc_start = 0; /* JSON output carries no --- */
263
275
  em.w.json = 1;
@@ -310,6 +322,9 @@ char* yep_serialize_json_compact(const yeptris_document* doc, size_t* len) {
310
322
  em.w.p = NULL;
311
323
  em.w.last = 0;
312
324
  em.w.force_flow = 0;
325
+ em.w.in_flow = 0;
326
+ em.w.flow_item = 0;
327
+ em.w.unserializable = 0;
313
328
  em.w.canonical = 0;
314
329
  em.w.explicit_doc_start = 0; /* JSON output carries no --- */
315
330
  em.w.json = 1;
@@ -362,6 +377,9 @@ char* yep_serialize_json_pretty(const yeptris_document* doc, size_t* len) {
362
377
  em.w.p = NULL;
363
378
  em.w.last = 0;
364
379
  em.w.force_flow = 0;
380
+ em.w.in_flow = 0;
381
+ em.w.flow_item = 0;
382
+ em.w.unserializable = 0;
365
383
  em.w.canonical = 0;
366
384
  em.w.explicit_doc_start = 0; /* JSON output carries no --- */
367
385
  em.w.json = 1;
@@ -430,6 +448,9 @@ YEPTRIS_API size_t yeptris_serialize_stream(YeptrisDocument handle,
430
448
  em.w.p = NULL;
431
449
  em.w.last = 0;
432
450
  em.w.force_flow = 0;
451
+ em.w.in_flow = 0;
452
+ em.w.flow_item = 0;
453
+ em.w.unserializable = 0;
433
454
  em.w.canonical = opts_canonical(opts);
434
455
  em.w.explicit_doc_start = opts_doc_marker(opts);
435
456
  em.w.json = 0;
@@ -19,6 +19,13 @@ int yep_style_plain_safe(const char* p, uint32_t len) {
19
19
  if (is_blank(p[0]) || is_blank(p[len - 1])) {
20
20
  return 0;
21
21
  }
22
+ if (len >= 3 && (unsigned char)p[0] == 0xef && (unsigned char)p[1] == 0xbb &&
23
+ (unsigned char)p[2] == 0xbf) {
24
+ /* a LEADING U+FEFF re-parses as a document BOM and vanishes —
25
+ * the emitted form must carry its escape ("\uFEFF", the
26
+ * nightly's run 38258674337); mid-token BOMs ride plain */
27
+ return 0;
28
+ }
22
29
  switch (p[0]) {
23
30
  case ':':
24
31
  /* libyaml (Psych symbol dumps ":name"): a leading colon only
@@ -119,7 +126,12 @@ int yep_style_plain_safe(const char* p, uint32_t len) {
119
126
  }
120
127
  }
121
128
  /* document markers */
122
- if ((len == 3 && (memcmp(p, "---", 3) == 0 || memcmp(p, "...", 3) == 0))) {
129
+ if (len >= 3 && (memcmp(p, "---", 3) == 0 || memcmp(p, "...", 3) == 0) &&
130
+ (len == 3 || p[3] == ' ' || p[3] == '\t' || p[3] == '\n' || p[3] == '\r')) {
131
+ /* a PREFIX marker is worse than the bare one: emitted plain at
132
+ * line start, "--- ..." re-reads as a document marker (the
133
+ * nightly's "--- --- >" folded-plain crasher) — libyaml quotes
134
+ * both */
123
135
  return 0;
124
136
  }
125
137
  return 1;