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.
@@ -53,6 +53,14 @@ typedef struct {
53
53
  uint8_t kind;
54
54
  } yep_frame;
55
55
 
56
+ /* One repeat-alias memo slot: the name is an input-relative span
57
+ * (alias names are always raw input views), so at+len re-derive it. */
58
+ typedef struct e_alias_slot {
59
+ uint32_t at; /* offset of the name inside e->p */
60
+ uint32_t len; /* 0 = slot empty */
61
+ uint32_t ord; /* the ordinal the nametab answered */
62
+ } e_alias_slot;
63
+
56
64
  struct yep_engine {
57
65
  const yep_allocator* sys;
58
66
  yep_pool* pool; /* finish pool: folded/escaped scalar content */
@@ -89,10 +97,13 @@ struct yep_engine {
89
97
  * 38541356075) */
90
98
  uint32_t anchor_placed_cap;
91
99
  /* repeat-alias memo (TODO.restructure/71): offset/len/ordinal of
92
- * the last alias resolved — hit by memcmp, not by the table */
93
- uint32_t alias_memo_at;
94
- uint32_t alias_memo_len;
95
- uint32_t alias_memo_ord;
100
+ * the last alias resolved — a hit answers by an input-span word
101
+ * compare (yep_view_eq; no libc call), not by the table.
102
+ * anchor_define drops the slot when the name being (re)defined is
103
+ * the memo's: the table's last-definition-wins overwrite would
104
+ * otherwise leave the memo serving a STALE ordinal (and skip the
105
+ * new ordinal's unplaced check). */
106
+ e_alias_slot alias_memo;
96
107
  /* the line-facts memo (TODO.restructure/76): one kernel pass per
97
108
  * line feeds both the line info and the shape */
98
109
  yep_line_facts facts_cache;
@@ -149,6 +160,7 @@ struct yep_engine {
149
160
  uint32_t ev_buf_n, ev_buf_cap;
150
161
  uint32_t ev_scopes; /* active entry-buffer scopes */
151
162
  char* norm_buf;
163
+ size_t norm_buf_len; /* norm_buf's byte length (NULL when no copy) */
152
164
  unsigned char* enc_buf;
153
165
  /* owned UTF-16/32 transcode scratch (run-owned) */ /* owned input copy with exotic breaks
154
166
  normalized */
@@ -425,17 +437,17 @@ static uint32_t anchor_id_of(yep_engine* e, yep_view name) {
425
437
  * the same alias thousands of times in a row; the memo answers
426
438
  * those with one input compare, skipping the hash + a likely
427
439
  * cache-missing probe into the (multi-MB, reserved) slots table. */
428
- if (e->alias_memo_len == name.len && e->alias_memo_len != 0 &&
429
- memcmp(e->p + e->alias_memo_at, name.p, name.len) == 0) {
430
- return e->alias_memo_ord;
440
+ if (e->alias_memo.len == name.len && name.len != 0 &&
441
+ yep_view_eq((yep_view){e->p + e->alias_memo.at, name.len}, name)) {
442
+ return e->alias_memo.ord;
431
443
  }
432
444
  uint32_t id = yep_nametab_get(&e->anchors, name);
433
445
  if (id == YEP_NAMETAB_NIL) {
434
446
  return 0;
435
447
  }
436
- e->alias_memo_at = (uint32_t)(name.p - e->p);
437
- e->alias_memo_len = name.len;
438
- e->alias_memo_ord = id;
448
+ e->alias_memo.at = (uint32_t)(name.p - e->p);
449
+ e->alias_memo.len = name.len;
450
+ e->alias_memo.ord = id;
439
451
  return id;
440
452
  }
441
453
 
@@ -447,30 +459,48 @@ static void e_anchor_mark(yep_engine* e, uint32_t id) {
447
459
  }
448
460
  }
449
461
 
462
+ /* Grows the per-ordinal placed flags so flags[id-1] is writable.
463
+ * Returns 0 only on allocation failure (table unchanged). */
464
+ static int anchor_placed_fit(yep_engine* e, uint32_t id) {
465
+ if (id <= e->anchor_placed_cap) {
466
+ return 1;
467
+ }
468
+ uint32_t cap = e->anchor_placed_cap ? e->anchor_placed_cap : 64;
469
+ while (cap < id) {
470
+ cap *= 2;
471
+ }
472
+ uint8_t* nb = yep_alloc(e->sys, cap);
473
+ if (nb == NULL) {
474
+ return 0;
475
+ }
476
+ if (e->anchor_placed != NULL) {
477
+ memcpy(nb, e->anchor_placed, e->anchor_placed_cap);
478
+ yep_free(e->sys, e->anchor_placed);
479
+ }
480
+ memset(nb + e->anchor_placed_cap, 0, cap - e->anchor_placed_cap);
481
+ e->anchor_placed = nb;
482
+ e->anchor_placed_cap = cap;
483
+ return 1;
484
+ }
485
+
450
486
  static uint32_t anchor_define(yep_engine* e, yep_view name) {
451
487
  /* Last definition wins (libyaml parity); OOM leaves the anchor
452
488
  * undefined so its alias errors. The VALUE is the anchor's 1-based
453
489
  * ordinal — sinks key on it and skip the name hash entirely. */
454
490
  uint32_t id = ++e->anchor_serial;
455
- if (id > e->anchor_placed_cap) {
456
- uint32_t cap = e->anchor_placed_cap ? e->anchor_placed_cap : 64;
457
- while (cap < id) {
458
- cap *= 2;
459
- }
460
- uint8_t* nb = yep_alloc(e->sys, cap);
461
- if (nb == NULL) {
462
- e->anchor_serial--;
463
- return 0; /* OOM: the anchor stays undefined, its alias errors */
464
- }
465
- if (e->anchor_placed != NULL) {
466
- memcpy(nb, e->anchor_placed, e->anchor_placed_cap);
467
- yep_free(e->sys, e->anchor_placed);
468
- }
469
- memset(nb + e->anchor_placed_cap, 0, cap - e->anchor_placed_cap);
470
- e->anchor_placed = nb;
471
- e->anchor_placed_cap = cap;
491
+ if (!anchor_placed_fit(e, id)) {
492
+ e->anchor_serial--;
493
+ return 0; /* OOM: the anchor stays undefined, its alias errors */
472
494
  }
473
495
  yep_nametab_set(&e->anchors, name, id);
496
+ /* A (re)definition changes the ordinal the table answers for THIS
497
+ * name: a memo still naming it would serve the OLD ordinal
498
+ * (violating last-definition-wins) and skip the new ordinal's
499
+ * unplaced check. */
500
+ if (e->alias_memo.len == name.len && name.len != 0 &&
501
+ yep_view_eq((yep_view){e->p + e->alias_memo.at, name.len}, name)) {
502
+ e->alias_memo.len = 0;
503
+ }
474
504
  return id;
475
505
  }
476
506
 
@@ -1341,9 +1371,12 @@ static yep_view e_resolve_tag(yep_engine* e, yep_view tag) {
1341
1371
  /* Parses &anchor / !tag runs at the cursor into *anchor / *tag. A node
1342
1372
  * carries at most one of each (c-ns-properties): a second property of
1343
1373
  * the same kind is the suite's 4JVG error. Keeping the last one let a
1344
- * mutated anchor run read differently from libyaml (catch 156). */
1345
- static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1346
- int have_anchor = 0, have_tag = 0;
1374
+ * mutated anchor run read differently from libyaml (catch 156).
1375
+ * *have_anchor / *have_tag carry that across CALLS — the flow node
1376
+ * parse re-runs the scan when a property rides the next line, and the
1377
+ * duplicate law must hold across the gap. */
1378
+ static void e_props_ex(yep_engine* e, yep_view* anchor, yep_view* tag, int* have_anchor,
1379
+ int* have_tag) {
1347
1380
  for (;;) {
1348
1381
  e_skip_inline_space(e);
1349
1382
  if (e->pos >= e->len) {
@@ -1351,11 +1384,11 @@ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1351
1384
  }
1352
1385
  unsigned char c = (unsigned char)e->p[e->pos];
1353
1386
  if (c == '&') {
1354
- if (have_anchor) {
1387
+ if (*have_anchor) {
1355
1388
  e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
1356
1389
  return;
1357
1390
  }
1358
- have_anchor = 1;
1391
+ *have_anchor = 1;
1359
1392
  size_t start = e->pos + 1;
1360
1393
  e->pos = yep_scan_prop_end(e->p, e->len, start);
1361
1394
  anchor->p = e->p + start;
@@ -1380,7 +1413,24 @@ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1380
1413
  * keep it (libyaml rejects those: "&a: key: v", W5VH's
1381
1414
  * value position — suite-VALID, ledgered). The colon
1382
1415
  * re-lexes as the key indicator and the existing colon
1383
- * arm builds the anchored null key. */
1416
+ * arm builds the anchored null key.
1417
+ *
1418
+ * The r228 nightly (run 42645063770, crash-8100227a)
1419
+ * caught the PROPS-LED follower: "&k2: ! {\n}" — the run
1420
+ * between the separator and the collection is the VALUE's
1421
+ * own properties, so libyaml still reads the anchored null
1422
+ * key (tag "<!>" on the flow map); yeptris instead rode
1423
+ * the colon into the name and stamped "k2:" + tag on the
1424
+ * map itself, and the anchor-led twin "&k2: &v {...}"
1425
+ * outright REJECTED (duplicate anchor) where libyaml
1426
+ * accepts. The follower scan now crosses ONE props run
1427
+ * ("!tag" / "&anch", verbatim "!<!..>" included —
1428
+ * scan_prop_end ends at a blank/break like the name scan)
1429
+ * before the indicator check, and a crossed run
1430
+ * un-consumes regardless of what follows: the tag/plain
1431
+ * member "&k2: ! t" reads the same pair in libyaml. The
1432
+ * plain-led twins (no props run — "&a: key", W5VH) never
1433
+ * cross one and keep riding. */
1384
1434
  if (e->p[e->pos - 1] == ':') {
1385
1435
  /* the follower scan crosses blanks, breaks AND
1386
1436
  * comments: the pair separator at line end still opens
@@ -1393,6 +1443,7 @@ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1393
1443
  * blank-equivalent here: skip it to its break and keep
1394
1444
  * scanning. */
1395
1445
  size_t t = e->pos;
1446
+ int rode_props = 0;
1396
1447
  for (;;) {
1397
1448
  while (t < e->len && (e->p[t] == ' ' || e->p[t] == '\t' || e->p[t] == '\n' ||
1398
1449
  e->p[t] == '\r')) {
@@ -1404,12 +1455,28 @@ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1404
1455
  }
1405
1456
  continue;
1406
1457
  }
1458
+ /* the r228 props-led law: one props run between
1459
+ * the separator and the follower belongs to the
1460
+ * VALUE node — a second keeps the first's verdict
1461
+ * ("&k2: !t &a {" still un-consumes on the "!") */
1462
+ if (!rode_props && t < e->len && (e->p[t] == '!' || e->p[t] == '&')) {
1463
+ t = yep_scan_prop_end(e->p, e->len, t + 1);
1464
+ rode_props = 1;
1465
+ continue;
1466
+ }
1407
1467
  break;
1408
1468
  }
1409
1469
  if (t < e->len &&
1410
1470
  (e->p[t] == '[' || e->p[t] == '{' || e->p[t] == '|' || e->p[t] == '>')) {
1411
1471
  anchor->len -= 1;
1412
1472
  e->pos -= 1;
1473
+ } else if (rode_props) {
1474
+ /* a crossed props run is the value's own: libyaml
1475
+ * reads the pair whatever follows the run — the
1476
+ * tag-led plain ("&k2: ! t") and the bare-tag/EOF
1477
+ * tail ("&k2: !") included */
1478
+ anchor->len -= 1;
1479
+ e->pos -= 1;
1413
1480
  }
1414
1481
  }
1415
1482
  if (e->pos < e->len && e->p[e->pos] == '#') {
@@ -1424,11 +1491,11 @@ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1424
1491
  return;
1425
1492
  }
1426
1493
  } else if (c == '!') {
1427
- if (have_tag) {
1494
+ if (*have_tag) {
1428
1495
  e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
1429
1496
  return;
1430
1497
  }
1431
- have_tag = 1;
1498
+ *have_tag = 1;
1432
1499
  size_t start = e->pos;
1433
1500
  e->pos++;
1434
1501
  if (e->pos < e->len && e->p[e->pos] == '<') {
@@ -1476,6 +1543,13 @@ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1476
1543
  }
1477
1544
  }
1478
1545
 
1546
+ /* The one-call form: fresh duplicate state (the block node parse
1547
+ * consumes the whole run in a single scan). */
1548
+ static void e_props(yep_engine* e, yep_view* anchor, yep_view* tag) {
1549
+ int have_anchor = 0, have_tag = 0;
1550
+ e_props_ex(e, anchor, tag, &have_anchor, &have_tag);
1551
+ }
1552
+
1479
1553
  /* ------------------------------------------------------------- aliases */
1480
1554
 
1481
1555
  static int e_alias(yep_engine* e, yep_event* ev) {
@@ -1737,7 +1811,8 @@ static int e_flow_node(yep_engine* e, yep_event* ev, int keyish) {
1737
1811
  ev->line = e->line;
1738
1812
  ev->col = e_col(e, e->pos) + 1;
1739
1813
  yep_view anchor = {0}, tag = {0};
1740
- e_props(e, &anchor, &tag);
1814
+ int have_anchor = 0, have_tag = 0;
1815
+ e_props_ex(e, &anchor, &tag, &have_anchor, &have_tag);
1741
1816
  if (e->err.code != YEP_ERR_NONE) {
1742
1817
  return -1; /* e_props failed (empty anchor name, unresolved tag) */
1743
1818
  }
@@ -1764,6 +1839,38 @@ static int e_flow_node(yep_engine* e, yep_event* ev, int keyish) {
1764
1839
  return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
1765
1840
  }
1766
1841
  c = (unsigned char)e->p[e->pos];
1842
+ /* Properties may span line breaks in flow (libyaml scans the
1843
+ * anchor/tag run across breaks: "&a\n!!t v" carries the tag with
1844
+ * value v; "!t\n&a x" carries the anchor). The run above skips
1845
+ * INLINE space only, so the cursor stopped at the break and the
1846
+ * property byte led the plain branch instead — a plain scalar
1847
+ * glued from tag/anchor bytes (the nightly's round 211,
1848
+ * crash-925daa54, "&anchor\n !!312 3123]"). Re-lex the riding
1849
+ * run with the have_* state threaded so a second property of one
1850
+ * kind still errors (the 4JVG law). Each iteration consumes at
1851
+ * least one byte or fails, so the loop is bounded. */
1852
+ while (c == '&' || c == '!') {
1853
+ e_props_ex(e, &anchor, &tag, &have_anchor, &have_tag);
1854
+ if (e->err.code != YEP_ERR_NONE) {
1855
+ return -1;
1856
+ }
1857
+ if (!yep_view_is_empty(anchor) && (memchr(anchor.p, '"', anchor.len) != NULL ||
1858
+ memchr(anchor.p, '\'', anchor.len) != NULL)) {
1859
+ return (int)e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
1860
+ }
1861
+ ev->anchor = anchor;
1862
+ ev->tag = tag;
1863
+ if (!yep_view_is_empty(anchor)) {
1864
+ ev->anchor_id = anchor_define(e, anchor);
1865
+ }
1866
+ if (e_flow_ws(e) != 0) {
1867
+ return -1;
1868
+ }
1869
+ if (e->pos >= e->len) {
1870
+ return e_fail(e, YEP_ERR_UNEXPECTED, e->pos);
1871
+ }
1872
+ c = (unsigned char)e->p[e->pos];
1873
+ }
1767
1874
  if ((c == ',' || c == ']' || c == '}' || c == ':') &&
1768
1875
  (!yep_view_is_empty(anchor) || !yep_view_is_empty(tag))) {
1769
1876
  ev->implicit = 1; /* properties with no node: a tagged null */
@@ -4235,12 +4342,35 @@ yep_pool* yep_engine_detach_pool(yep_engine* e) {
4235
4342
  return p;
4236
4343
  }
4237
4344
 
4345
+ char* yep_engine_detach_norm_buf(yep_engine* e, size_t* len_out) {
4346
+ if (e == NULL) {
4347
+ if (len_out != NULL) {
4348
+ *len_out = 0;
4349
+ }
4350
+ return NULL;
4351
+ }
4352
+ char* b = e->norm_buf;
4353
+ if (len_out != NULL) {
4354
+ *len_out = e->norm_buf_len;
4355
+ }
4356
+ e->norm_buf = NULL;
4357
+ e->norm_buf_len = 0;
4358
+ return b;
4359
+ }
4360
+
4238
4361
  void yep_engine_prepare(yep_engine* e, const yep_text_stats* st) {
4239
4362
  if (e == NULL || st == NULL) {
4240
4363
  return;
4241
4364
  }
4242
4365
  if (st->amp > 0) {
4243
4366
  yep_nametab_reserve(&e->anchors, (uint32_t)st->amp);
4367
+ /* every definition bumps the serial exactly once and '&' marks
4368
+ * every definition, so amp is the serial's exact ceiling: the
4369
+ * placed flags pre-fit here and the parse never pays the
4370
+ * doubling chain (alloc + copy + memset per growth). Best
4371
+ * effort — anchor_define's growth stays as the fallback for
4372
+ * engines driven without prepare. */
4373
+ anchor_placed_fit(e, (uint32_t)st->amp + 1);
4244
4374
  }
4245
4375
  }
4246
4376
 
@@ -4315,6 +4445,7 @@ const yep_stopset yep_norm_break_leads = {
4315
4445
  * headers). Verified against libyaml by the NormLsPs pins and the
4316
4446
  * fuzz-diff LS/PS comparator. */
4317
4447
  static int step_bs_header(const char* p, size_t len, size_t i);
4448
+ static size_t norm_next_fold_break(const char* p, size_t len, size_t i, size_t* br_len);
4318
4449
 
4319
4450
  typedef struct yep_norm_ctx {
4320
4451
  int quote; /* 0 none, else the opening quote char */
@@ -4326,15 +4457,44 @@ typedef struct yep_norm_ctx {
4326
4457
  int at_break;
4327
4458
  int bs_open;
4328
4459
  int bs_content;
4329
- uint32_t bs_indent; /* the header line's indent; (uint32_t)-1 = the
4330
- * doc-inline root rule (markers only) */
4331
- uint8_t started; /* at least one byte consumed: the walk's i == 0
4332
- * clauses are stream-start, not buffer-start —
4333
- * the chunked route resumes mid-stream at a ---
4334
- * cut, and the carried context must say so */
4335
- unsigned char prev; /* the last consumed byte (valid when started):
4336
- * the '#' and line-start tests peek p[i - 1],
4337
- * which at a resumed buffer is carried state */
4460
+ uint32_t bs_indent; /* the header line's indent; (uint32_t)-1 = the
4461
+ * doc-inline root rule (markers only) */
4462
+ uint8_t started; /* at least one byte consumed: the walk's i == 0
4463
+ * clauses are stream-start, not buffer-start —
4464
+ * the chunked route resumes mid-stream at a ---
4465
+ * cut, and the carried context must say so */
4466
+ unsigned char prev; /* the last consumed byte (valid when started):
4467
+ * the '#' and line-start tests peek p[i - 1],
4468
+ * which at a resumed buffer is carried state */
4469
+ unsigned char bb; /* the byte immediately before the last FOLDED
4470
+ * break (0 = not armed): the r133 law — a
4471
+ * tok_start armed by a fold whose before-break
4472
+ * byte was plain content is a continuation
4473
+ * line, and its "!<" must not open a verbatim
4474
+ * tag (cleared wherever an indicator arms the
4475
+ * slot instead) */
4476
+ unsigned char plain_open; /* a plain scalar VALUE word is in
4477
+ * progress (set by every non-indicator content
4478
+ * byte, preserved across breaks): the r147
4479
+ * law — the bb gate's single-byte slice misses
4480
+ * the run-tail shapes ("- \n<NEL>!" arms
4481
+ * bb='\n', in the indicator set), so the open
4482
+ * keys on the value-in-progress fact: a
4483
+ * fold-armed tok_start over an open plain is a
4484
+ * continuation line whose "!<" is content,
4485
+ * never props */
4486
+ int phantom_flow; /* openers armed by the glued ':'-twin
4487
+ * ("a:{b]" — the #836-accepted prev==':'
4488
+ * clause) alone: libyaml keeps those bytes
4489
+ * INTERIOR to the plain (no flow collection
4490
+ * exists), so their depth must never arm the
4491
+ * r155 real-flow clear on ']' / '}' — the
4492
+ * walk's in_flow is monotone and the phantom
4493
+ * opens inflate it, which defeated the
4494
+ * "block context keeps the fact" law (the
4495
+ * r169 nightly: the ']' cleared plain_open
4496
+ * through the phantom and the next fold's
4497
+ * "!<" opened a never-closed verbatim) */
4338
4498
  } yep_norm_ctx;
4339
4499
 
4340
4500
  typedef struct yep_norm_tok {
@@ -4482,6 +4642,7 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4482
4642
  t.n = 2;
4483
4643
  t.fold_off = 0;
4484
4644
  t.fold_len = 2;
4645
+ c->bb = (i > 0) ? (unsigned char)p[i - 1] : '\n';
4485
4646
  norm_break_arms(c);
4486
4647
  return t;
4487
4648
  }
@@ -4489,6 +4650,7 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4489
4650
  t.n = 3;
4490
4651
  t.fold_off = 0;
4491
4652
  t.fold_len = 3;
4653
+ c->bb = (i > 0) ? (unsigned char)p[i - 1] : '\n';
4492
4654
  norm_break_arms(c);
4493
4655
  return t;
4494
4656
  }
@@ -4502,19 +4664,22 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4502
4664
  c->at_break = 0;
4503
4665
  return t;
4504
4666
  }
4505
- if (ch == ' ' || ch == '\t') {
4506
- /* a value position survives spaces in block context ("k: 'x'"
4507
- * opens; the plain continuation "a 'b'" does not — its word
4508
- * did not begin with an indicator) */
4509
- c->tok_start = (c->in_flow > 0) ? 1 : (c->word_led || c->tok_start);
4510
- return t;
4511
- }
4512
-
4513
4667
  int line_start = (!c->started || c->at_break || c->prev == '\n' || c->prev == '\r');
4514
4668
 
4515
4669
  /* block scalars: inside the fold no quote opens and every break
4516
4670
  * keeps its fold; the region ends at a col-0 marker, a %-line
4517
- * after content, or a dedent to the header line's indent */
4671
+ * after content, or a dedent to the header line's indent. It runs
4672
+ * BEFORE the token-space arm: a fold line's leading spaces are its
4673
+ * INDENT, and the space arm consumed them one byte at a time (the
4674
+ * at_break stays armed) so the indent walk below counted from the
4675
+ * FIRST NON-SPACE byte — a one-space-indented content line read
4676
+ * ind=0, the dedent rule (ind <= bs_indent) ended the fold
4677
+ * mid-content, and the byte lexed as a token: "!<" armed a phantom
4678
+ * verbatim whose breaks-ride-raw law left the whole tail
4679
+ * unnormalized, the raw NEL run classified as one content line,
4680
+ * and the framing walk spawned a phantom empty plain second
4681
+ * document libyaml never emits (the r194 nightly,
4682
+ * ">\n ! \n !<aaa: bbb\n...\n<NEL><NEL>"). */
4518
4683
  if (line_start && c->in_flow == 0 && c->bs_open) {
4519
4684
  size_t k = i;
4520
4685
  uint32_t ind = 0;
@@ -4528,12 +4693,52 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4528
4693
  return t; /* a final line without its break */
4529
4694
  }
4530
4695
  if (p[k] == '\n' || p[k] == '\r' || p[k] == '#') {
4696
+ /* the line ends at ANY break spelling: a multibyte break
4697
+ * (NEL/LS/PS) FOLDS at the token tail, exactly like the
4698
+ * '#' comment arm ends its line (the pre-r194 path: the
4699
+ * token-space arm consumed the leading spaces one byte at
4700
+ * a time and the line's '#' hit the comment arm, whose
4701
+ * nel/lps case folds). Riding the break raw inside the
4702
+ * wholesale token leaked it to the engine's verbatim
4703
+ * block-scalar reader, and the r19-family indent
4704
+ * transform's verify (sub ">\n #ML<NEL>\n") stopped
4705
+ * flipping to parity — the r195 nightly ">\n#ML<NEL>". */
4531
4706
  size_t e = k;
4532
- while (e < len && p[e] != '\n') {
4707
+ size_t bs = 0;
4708
+ size_t br = 0;
4709
+ while (e < len) {
4710
+ if (p[e] == '\n') {
4711
+ bs = e;
4712
+ br = 1;
4713
+ e++;
4714
+ break;
4715
+ }
4716
+ if (p[e] == '\r') {
4717
+ bs = e;
4718
+ br = (e + 1 < len && p[e + 1] == '\n') ? 2 : 1;
4719
+ e += br;
4720
+ break;
4721
+ }
4722
+ size_t mb = yep_scan_mb_break_len(p, len, e);
4723
+ if (mb > 0) {
4724
+ bs = e;
4725
+ br = mb;
4726
+ e += mb;
4727
+ break;
4728
+ }
4533
4729
  e++;
4534
4730
  }
4535
- t.n = (e < len) ? (e - i + 1) : (len - i);
4536
- c->at_break = (e < len);
4731
+ if (br == 0) {
4732
+ t.n = len - i;
4733
+ c->at_break = 0;
4734
+ return t; /* the tail rides the line to EOF */
4735
+ }
4736
+ t.n = e - i;
4737
+ if (p[bs] != '\n' && p[bs] != '\r') {
4738
+ t.fold_off = (int)(bs - i);
4739
+ t.fold_len = (int)br;
4740
+ }
4741
+ c->at_break = 1;
4537
4742
  return t; /* blank or comment-content line */
4538
4743
  }
4539
4744
  int marker = (ind == 0 && k + 2 < len &&
@@ -4561,6 +4766,14 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4561
4766
  }
4562
4767
  }
4563
4768
 
4769
+ if (ch == ' ' || ch == '\t') {
4770
+ /* a value position survives spaces in block context ("k: 'x'"
4771
+ * opens; the plain continuation "a 'b'" does not — its word
4772
+ * did not begin with an indicator) */
4773
+ c->tok_start = (c->in_flow > 0) ? 1 : (c->word_led || c->tok_start);
4774
+ return t;
4775
+ }
4776
+
4564
4777
  /* the header: a '>' / '|' at a value position with a valid
4565
4778
  * follower opens the fold (the engine's own floor is the parent's
4566
4779
  * column, which the walk cannot know — the header LINE's indent
@@ -4585,12 +4798,29 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4585
4798
  c->bs_open = 1;
4586
4799
  c->bs_indent = ind;
4587
4800
  c->bs_content = 0;
4588
- size_t e = i;
4589
- while (e < len && p[e] != '\n') {
4590
- e++;
4801
+ /* the header line ends at ANY fold break: the tail past
4802
+ * the indicators (usually the comment) is OUTSIDE the
4803
+ * block-content area, so the #638 fold law governs it — a
4804
+ * NEL/LS/PS there ends the header line and the next line
4805
+ * rides the bs branch from its own indent (the r171
4806
+ * nightly: the '\n'-only scan swallowed " #<"+NEL+" t"
4807
+ * whole, the NEL rode unnormalized to the engine, whose
4808
+ * comment skip then ate the content line — empty where
4809
+ * libyaml's IS_BREAKZ comment law reads "t") */
4810
+ size_t br_len = 0;
4811
+ size_t e = norm_next_fold_break(p, len, i, &br_len);
4812
+ if (br_len > 0) {
4813
+ t.n = (e + br_len) - i;
4814
+ t.fold_off = (int)(e - i); /* the break FOLDS: the copy
4815
+ * splices the '\n' (a
4816
+ * plain-consumed multibyte
4817
+ * break would ride raw) */
4818
+ t.fold_len = (int)br_len;
4819
+ c->at_break = 1;
4820
+ } else {
4821
+ t.n = len - i;
4822
+ c->at_break = 0;
4591
4823
  }
4592
- t.n = (e < len) ? (e - i + 1) : (len - i);
4593
- c->at_break = (e < len);
4594
4824
  return t;
4595
4825
  }
4596
4826
  /* not a header ("| : [" is a mapping key): fall through */
@@ -4616,24 +4846,55 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4616
4846
  c->bs_open = 1;
4617
4847
  c->bs_indent = (uint32_t)-1;
4618
4848
  c->bs_content = 0;
4619
- size_t e = i;
4620
- while (e < len && p[e] != '\n') {
4621
- e++;
4849
+ c->plain_open = 0; /* the marker is a document boundary */
4850
+ /* the marker line's tail ends at any fold break (the same
4851
+ * law as the plain header above -- the comment tail is
4852
+ * outside the content area) */
4853
+ size_t br_len = 0;
4854
+ size_t e = norm_next_fold_break(p, len, i, &br_len);
4855
+ if (br_len > 0) {
4856
+ t.n = (e + br_len) - i;
4857
+ t.fold_off = (int)(e - i); /* the break folds (the header twin) */
4858
+ t.fold_len = (int)br_len;
4859
+ c->at_break = 1;
4860
+ } else {
4861
+ t.n = len - i;
4862
+ c->at_break = 0;
4622
4863
  }
4623
- t.n = (e < len) ? (e - i + 1) : (len - i);
4624
- c->at_break = (e < len);
4625
4864
  return t;
4626
4865
  }
4627
- t.n = 3; /* the marker rides; the line's tail processes normally */
4866
+ t.n = 3; /* the marker rides; the line's tail processes normally */
4867
+ c->plain_open = 0; /* a document boundary: the next node's
4868
+ * props position must not inherit the
4869
+ * previous document's open plain */
4628
4870
  return t;
4629
4871
  }
4630
4872
 
4631
4873
  if (c->tok_start) {
4632
4874
  c->at_break = 0; /* any content byte ends the line-start state */
4633
4875
  if (ch == '!' && i + 1 < len && p[i + 1] == '<') {
4634
- c->in_verbatim = 1;
4635
- t.n = 2;
4636
- return t;
4876
+ /* the r133 law: a tok_start armed by a FOLDED break whose
4877
+ * before-break byte was plain content is a continuation
4878
+ * line — its "!<" is plain content, not props; only a
4879
+ * new-node position (an indicator like ':' armed the slot)
4880
+ * opens a verbatim tag. Unconditioned, the continuation's
4881
+ * "!<" opened a phantom verbatim that swallowed the rest
4882
+ * of the input: later NELs rode unnormalized and the plain
4883
+ * fold emitted raw lead-stripped 0x85 into the value. The
4884
+ * r147 law extends the gate to the run-tail shapes the
4885
+ * single-byte bb probe misses ("- \n<NEL>!" arms bb='\n',
4886
+ * in the indicator set; "key: <NEL><NEL>!" arms bb=0x85,
4887
+ * outside it — both while a plain VALUE is in progress,
4888
+ * which plain_open carries): a plain value word open
4889
+ * across the fold means the line is a continuation, and
4890
+ * libyaml's 1.1 read folds the same NELs to newlines
4891
+ * (READ_LINE spells NEL -> LF, the fold law joins). */
4892
+ int bb_plain = c->bb != 0 && !strchr("&*!-?:,[]{}#%'\"|> \t\n\r", c->bb);
4893
+ if (!bb_plain && !c->plain_open) {
4894
+ c->in_verbatim = 1;
4895
+ t.n = 2;
4896
+ return t;
4897
+ }
4637
4898
  }
4638
4899
  c->word_led = (ch == '&' || ch == '*' || ch == '!' || ch == '-' || ch == '?' || ch == ':' ||
4639
4900
  ch == ',' || ch == '[' || ch == '{' || ch == ']' || ch == '}');
@@ -4655,10 +4916,29 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4655
4916
  * fold rendered one fold-SPACE per break instead of the n-1
4656
4917
  * newline tally (the r98 nightly — the #836 phantom's flow
4657
4918
  * half; the comma half landed in #836 itself). */
4919
+ int phantom = !c->tok_start; /* the glued ':'-twin alone (the
4920
+ * #836-accepted prev==':' state):
4921
+ * libyaml keeps the opener interior
4922
+ * to the plain, so its depth rides
4923
+ * phantom_flow, never the real count */
4658
4924
  c->tok_start = 1;
4925
+ if (phantom) {
4926
+ c->phantom_flow++;
4927
+ /* the phantom opener is INTERIOR to the open plain VALUE
4928
+ * word (libyaml's read keeps the bytes content) — the fact
4929
+ * must survive it, or the r147 gate dies two shapes later:
4930
+ * the slot the opener armed says "new node" while the plain
4931
+ * is still open, and a following "!<" opened a phantom
4932
+ * verbatim that swallowed the rest of the input (the r217
4933
+ * nightly: "a:{!<x>"+NEL — "k:{" armed by the ':' indicator
4934
+ * clears a live plain_open). The r155/r169 law's opener
4935
+ * twin: block context keeps the fact. */
4936
+ } else {
4937
+ c->plain_open = 0; /* the slot was armed by an indicator */
4938
+ }
4659
4939
  c->in_flow++;
4660
4940
  } else if (ch == ',') {
4661
- /* flow separators glue the next token — THE COMMA IN FLOW
4941
+ /* flow separators glue the next token — THE COMMA IN REAL FLOW
4662
4942
  * ONLY. The unconditioned arm let a BLOCK plain's interior
4663
4943
  * comma arm tok_start, a following "!<" opened a phantom
4664
4944
  * verbatim tag that swallowed the rest of the input (breaks
@@ -4667,22 +4947,66 @@ static yep_norm_tok norm_step(yep_norm_ctx* c, const char* p, size_t len, size_t
4667
4947
  * fold's blank-line tally lost them (the r96 nightly,
4668
4948
  * yeptris#836, "b,,,!<!<"+NEL^3). ':' stays unconditioned:
4669
4949
  * the BLOCK value position rides it ("k: 'x'" opens the
4670
- * quote, the NormLsPs pins). */
4671
- if (c->in_flow > 0) {
4950
+ * quote, the NormLsPs pins). The r217 law: "in flow" means
4951
+ * REAL flow depth (the r155/r169 gate) — the glued ':'-twin's
4952
+ * phantom opens inflate in_flow without any flow collection
4953
+ * existing in libyaml's read, so their commas are INTERIOR
4954
+ * plain content ("o&a-:{*,,,!<!<"+NEL^3: the phantom '{'
4955
+ * inflated in_flow, the interior ','s armed a props slot, and
4956
+ * the "!<" opened the phantom verbatim again). */
4957
+ if (c->in_flow > c->phantom_flow) {
4672
4958
  c->tok_start = 1;
4959
+ c->bb = 0; /* the slot was armed by an indicator, not a fold */
4960
+ c->plain_open = 0;
4673
4961
  }
4674
4962
  } else if (ch == ':') {
4675
4963
  /* the separator law (#833's own predicate): a ':' arms the
4676
4964
  * next token slot only before a blank/EOL/EOF — "k: 'x'"
4677
4965
  * opens the value quote (the NormLsPs pins) while an
4678
4966
  * interior "x:!<" of a block plain does NOT (the phantom
4679
- * verbatim again, the #836 colon-led twin) */
4967
+ * verbatim again, the #836 colon-led twin). The r217 law for
4968
+ * the DECLINED ':': a ':' before a non-blank byte is interior
4969
+ * to a plain, or the plain's own LEAD ("k:\t:{!<" — the
4970
+ * separating ':'+tab left plain_open dead, the value plain's
4971
+ * lead ':' declines, and the phantom '{' behind it armed a
4972
+ * props slot both of whose gates were dead — the "!<" opened
4973
+ * the phantom verbatim and the NEL run rode raw). The decline
4974
+ * IS the plain-context fact: arm the value word here. */
4680
4975
  if (i + 1 >= len || p[i + 1] == ' ' || p[i + 1] == '\t' || p[i + 1] == '\n' ||
4681
4976
  p[i + 1] == '\r') {
4682
4977
  c->tok_start = 1;
4978
+ c->bb = 0; /* the slot was armed by an indicator, not a fold */
4979
+ c->plain_open = 0;
4980
+ } else {
4981
+ c->plain_open = 1;
4683
4982
  }
4684
4983
  } else if (ch == ']' || ch == '}') {
4685
4984
  c->tok_start = 1;
4985
+ c->bb = 0; /* the slot was armed by an indicator, not a fold */
4986
+ if (c->in_flow > c->phantom_flow) {
4987
+ c->plain_open = 0;
4988
+ }
4989
+ /* block context keeps the fact: a plain cannot start with
4990
+ * ']' / '}' (c-indicator), so reaching this byte with the value
4991
+ * word open means it is INTERIOR content (the catch-85 law's
4992
+ * indicator twin) — the r155 law. The unconditional clear let
4993
+ * the next fold-armed "!<" open a phantom verbatim (the r147
4994
+ * gate's blind-spot twin: the ']' byte also blanks bb_plain —
4995
+ * it is in the bb indicator set — so both gate inputs said
4996
+ * "new node") and the NELs after it rode unnormalized raw into
4997
+ * the plain fold ("(999999;!<1]" +NEL+ "!<" +NEL+ "¥" +NEL^2).
4998
+ * The clear keys on REAL flow (in_flow above the phantom
4999
+ * depth): the glued ':'-twin's phantom opens inflate in_flow
5000
+ * without any flow collection existing in libyaml's read, and
5001
+ * the inflated count defeated the law on a plain's interior ']'
5002
+ * ("a:{b]" +NEL — the r169 nightly's fifth arming shape). */
5003
+ }
5004
+ /* the r147 law: a non-indicator content byte begins/continues a
5005
+ * plain scalar VALUE word — the fact survives breaks (breaks only
5006
+ * arm tok_start), so a later fold-armed "!<" reads it (indicator
5007
+ * bytes leave the fact to their own arm above) */
5008
+ if (!strchr("&*!-?:,[]{}#%'\"|>", ch)) {
5009
+ c->plain_open = 1;
4686
5010
  }
4687
5011
  return t;
4688
5012
  }
@@ -4798,6 +5122,24 @@ static char* norm_breaks_run(const yep_allocator* sys, const char* p, size_t len
4798
5122
  } else {
4799
5123
  memcpy(out + o, p + j, (size_t)t.fold_off);
4800
5124
  o += (size_t)t.fold_off;
5125
+ /* A raw CR copied just before this rewrite must not pair
5126
+ * with the replacement '\n' into a phantom CRLF: the byte
5127
+ * readers count CR LF as ONE break (SKIP_LINE's pair law)
5128
+ * while the input's CR + NEL/LS/PS were TWO — the rewrite
5129
+ * silently halved the fold's break tally (";!\r<NEL>\r;\r"
5130
+ * read ";!\\n;" where the (n-1) newline law says
5131
+ * ";!\\n\\n;", the r188 nightly). Rewrite the WHOLE
5132
+ * trailing run of raw CRs to '\n' — one CR is one break,
5133
+ * and only the run's last byte could ever touch the
5134
+ * replacement, but a shallower rewrite would just move the
5135
+ * phantom pair one byte left ("a\r\r<NEL>" -> "a\r\n\n"
5136
+ * re-pairs on the first CR; the run makes it "a\n\n\n",
5137
+ * four breaks in, four breaks out). LFs never pair with
5138
+ * each other, so the run's count survives everywhere the
5139
+ * fold law counts breaks. */
5140
+ for (size_t t2 = o; t2 > 0 && out[t2 - 1] == '\r'; t2--) {
5141
+ out[t2 - 1] = '\n';
5142
+ }
4801
5143
  out[o++] = '\n';
4802
5144
  memcpy(out + o, p + j + t.fold_off + t.fold_len,
4803
5145
  t.n - (size_t)t.fold_off - (size_t)t.fold_len);
@@ -4822,6 +5164,7 @@ static const char* e_normalize_breaks(yep_engine* e, const char* p, size_t len,
4822
5164
  return p;
4823
5165
  }
4824
5166
  e->norm_buf = out;
5167
+ e->norm_buf_len = out ? *out_len : 0;
4825
5168
  return out;
4826
5169
  }
4827
5170
 
@@ -4981,6 +5324,41 @@ void yep_engine_destroy(yep_engine* e) {
4981
5324
  * that flow to the depth cap, chunked cut and errored at 2:1).
4982
5325
  * Returns 1 valid, 0 definitely not a header, -1 truncated (the tail
4983
5326
  * may still complete a valid header). */
5327
+
5328
+ /* The next FOLD break at or after i under the #638 fold law outside
5329
+ * quotes ('\n', lone '\r' / CRLF, NEL, LS/PS): returns its start
5330
+ * offset and stores its consumed length, or returns 0 with *br_len 0
5331
+ * when the tail holds none. A truncated multibyte lead at the end
5332
+ * reads as "none": the one-shot walk runs on validated complete input
5333
+ * (a dangling lead never arrives), and the streaming scanner holds the
5334
+ * header line for the bytes that may still complete it. */
5335
+ static size_t norm_next_fold_break(const char* p, size_t len, size_t i, size_t* br_len) {
5336
+ size_t e = i;
5337
+ *br_len = 0;
5338
+ while (e < len) {
5339
+ unsigned char ec = (unsigned char)p[e];
5340
+ if (ec == '\n') {
5341
+ *br_len = 1;
5342
+ return e;
5343
+ }
5344
+ if (ec == '\r') {
5345
+ *br_len = (e + 1 < len && p[e + 1] == '\n') ? 2 : 1;
5346
+ return e;
5347
+ }
5348
+ if (ec == 0xC2 && e + 1 < len && (unsigned char)p[e + 1] == 0x85) {
5349
+ *br_len = 2;
5350
+ return e;
5351
+ }
5352
+ if (ec == 0xE2 && e + 2 < len && (unsigned char)p[e + 1] == 0x80 &&
5353
+ ((unsigned char)p[e + 2] & 0xFE) == 0xA8) {
5354
+ *br_len = 3;
5355
+ return e;
5356
+ }
5357
+ e++;
5358
+ }
5359
+ return 0;
5360
+ }
5361
+
4984
5362
  static int step_bs_header(const char* p, size_t len, size_t i) {
4985
5363
  size_t k = i + 1;
4986
5364
  int chomp = 0, indent = 0, space = 0;
@@ -5174,6 +5552,12 @@ static size_t step_scan(yep_stepstate* st) {
5174
5552
  * ate the '---' marker, chunked errored on the unterminated
5175
5553
  * prefix — the nightly's run 38534965021) */
5176
5554
  st->tok_start = (st->in_flow > 0) ? 1 : st->word_led;
5555
+ /* the one-shot walk's law: the first post-break byte
5556
+ * clears the line-start fact — spaces included, or an
5557
+ * INDENTED " ---" (a plain/seq position) read as a
5558
+ * col-0 marker through the line-start gate and the cut
5559
+ * landed inside the line (the r171 gate twin) */
5560
+ st->at_break = 0;
5177
5561
  continue;
5178
5562
  }
5179
5563
  if (st->tok_start) {
@@ -5250,7 +5634,11 @@ static size_t step_scan(yep_stepstate* st) {
5250
5634
  }
5251
5635
  if (st->in_flow == 0) {
5252
5636
  char prev = i > 0 ? p[i - 1] : '\n';
5253
- if (prev != '\n' && prev != '\r') {
5637
+ if (prev != '\n' && prev != '\r' && !st->at_break) {
5638
+ /* the one-shot walk's line_start law honors at_break
5639
+ * (-- the mb-break arms arm it); the prev-only check
5640
+ * missed every line that opens after a NEL/LS/PS, so
5641
+ * the ride-along never fired there (the r171 twin) */
5254
5642
  continue;
5255
5643
  }
5256
5644
  /* Block scalars: once a '>' / '|' header is seen, its
@@ -5326,15 +5714,26 @@ static size_t step_scan(yep_stepstate* st) {
5326
5714
  }
5327
5715
  st->bs_open = 1;
5328
5716
  st->bs_indent = ind;
5329
- size_t e = i;
5330
- while (e < st->len && p[e] != '\n') {
5331
- e++;
5332
- }
5333
- if (e >= st->len) {
5717
+ /* the header line ends at ANY fold break (the
5718
+ * one-shot walk's law, norm_next_fold_break): the
5719
+ * tail past the indicators is outside the
5720
+ * block-content area, so the #638 fold law
5721
+ * governs it. The break arms' state rides here
5722
+ * too — at_break opens the content line's
5723
+ * ride-along through the line-start gate (the
5724
+ * r171 nightly: the '\n'-only scan never
5725
+ * completed a NEL-terminated header line and
5726
+ * held the window at the '>' forever) */
5727
+ size_t br_len = 0;
5728
+ size_t e = norm_next_fold_break(p, st->len, i, &br_len);
5729
+ if (br_len == 0) {
5334
5730
  hold = i; /* an incomplete header line */
5335
5731
  break;
5336
5732
  }
5337
- i = e;
5733
+ st->tok_start = 1;
5734
+ st->word_led = 0;
5735
+ st->at_break = 1;
5736
+ i = e + br_len - 1; /* the loop's i++ lands past the break */
5338
5737
  continue;
5339
5738
  }
5340
5739
  /* Not a header ("| : [" is a mapping key): fall through
@@ -5512,6 +5911,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5512
5911
  }
5513
5912
  yep_free(e->sys, e->norm_buf);
5514
5913
  e->norm_buf = NULL;
5914
+ e->norm_buf_len = 0;
5515
5915
  /* enc_buf is NOT reset here: yep_engine_run's front-end may have
5516
5916
  * transcoded THIS call's input into it microseconds ago -- buf
5517
5917
  * points into it, and freeing it before the break-normalize scan
@@ -5536,7 +5936,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5536
5936
  if (e->anchor_placed != NULL) {
5537
5937
  memset(e->anchor_placed, 0, e->anchor_placed_cap);
5538
5938
  }
5539
- e->alias_memo_len = 0;
5939
+ e->alias_memo.len = 0;
5540
5940
  e->tagmap_n = 0;
5541
5941
  e->saw_yaml = 0;
5542
5942
  e->doc_begin = (size_t)-1;
@@ -5771,11 +6171,11 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5771
6171
  if (e->anchor_placed != NULL) {
5772
6172
  memset(e->anchor_placed, 0, e->anchor_placed_cap);
5773
6173
  }
5774
- e->alias_memo_len = 0; /* so are anchors */
5775
- e->alias_memo_len = 0; /* and the repeat-alias memo: doc 1's
5776
- * "*ORIGIN" memoized the ordinal and doc 2's
5777
- * identical name answered from it ACROSS the
5778
- * boundary (the nightly's run 38266487549) */
6174
+ /* so are anchors — and the repeat-alias memo: doc 1's
6175
+ * "*ORIGIN" memoized the ordinal and doc 2's identical
6176
+ * name answered from it ACROSS the boundary (the
6177
+ * nightly's run 38266487549) */
6178
+ e->alias_memo.len = 0;
5779
6179
  }
5780
6180
  e_event_init(&ev, YEP_EV_DOCUMENT_START);
5781
6181
  ev.style = 1; /* explicit marker */
@@ -5862,7 +6262,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
5862
6262
  if (e->anchor_placed != NULL) {
5863
6263
  memset(e->anchor_placed, 0, e->anchor_placed_cap);
5864
6264
  }
5865
- e->alias_memo_len = 0;
6265
+ e->alias_memo.len = 0;
5866
6266
  if (emit_now(e, &ev) != 0) {
5867
6267
  return -2;
5868
6268
  }
@@ -6101,7 +6501,7 @@ static int engine_run_impl(yep_engine* e, const char* buf, size_t len, const yep
6101
6501
  if (e->anchor_placed != NULL) {
6102
6502
  memset(e->anchor_placed, 0, e->anchor_placed_cap);
6103
6503
  }
6104
- e->alias_memo_len = 0;
6504
+ e->alias_memo.len = 0;
6105
6505
  }
6106
6506
  if (emit_end) {
6107
6507
  e_event_init(&ev, YEP_EV_STREAM_END);