yeptris 0.6.52.0-arm-linux-eabihf → 0.6.53.0-arm-linux-eabihf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -97,7 +97,7 @@ void yep_scan_line_f(const char* p, size_t len, size_t pos, const yep_line_facts
97
97
  li.first = 0;
98
98
  li.end = f->end;
99
99
  size_t j = f->indent;
100
- li.indent = (uint16_t)(j - pos);
100
+ li.indent = (uint32_t)(j - pos);
101
101
  if (j < li.end && p[j] == '\t') {
102
102
  size_t k = j;
103
103
  while (k < li.end && (p[k] == ' ' || p[k] == '\t')) {
@@ -162,6 +162,12 @@ static int yep_colon_terminates(const char* p, size_t len, size_t colon, int flo
162
162
  if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
163
163
  return 1;
164
164
  }
165
+ /* a raw NEL/LS/PS is a break like LF (libyaml's IS_BLANKZ): the
166
+ * value-position separator arm the #833 dig pinned — the LF twin
167
+ * terminated while the PS spelling kept the colon in the plain */
168
+ if (yep_scan_mb_break_len(p, len, next) > 0) {
169
+ return 1;
170
+ }
165
171
  if (flow && yep_ct_is(c, YEP_CT_FLOW_IND)) {
166
172
  return 1;
167
173
  }
@@ -198,6 +204,201 @@ int yep_scan_dq_forced(const char* p, uint32_t len) {
198
204
  return 0;
199
205
  }
200
206
 
207
+ /* ---- the plain-safety truth table (moved from emit/style.c,
208
+ * #352 lane 3: scan owns bytes→facts; the emitter and the DOM's
209
+ * parse-time fact bit both read THIS walk so they cannot drift) ---- */
210
+
211
+ static int is_blank(char c) {
212
+ return c == ' ' || c == '\t';
213
+ }
214
+
215
+ static int is_break(char c) {
216
+ return c == '\n' || c == '\r';
217
+ }
218
+
219
+ int yep_scan_plain_safe(const char* p, uint32_t len) {
220
+ if (len == 0) {
221
+ return 0; /* empty needs quotes or an empty-value slot */
222
+ }
223
+ if (is_blank(p[0]) || is_blank(p[len - 1])) {
224
+ return 0;
225
+ }
226
+ if (len >= 3 && (unsigned char)p[0] == 0xef && (unsigned char)p[1] == 0xbb &&
227
+ (unsigned char)p[2] == 0xbf) {
228
+ /* a LEADING U+FEFF re-parses as a document BOM and vanishes —
229
+ * the emitted form must carry its escape ("\uFEFF", the
230
+ * nightly's run 38258674337); mid-token BOMs ride plain */
231
+ return 0;
232
+ }
233
+ switch (p[0]) {
234
+ case ':':
235
+ /* libyaml (Psych symbol dumps ":name"): a leading colon only
236
+ * indicates when followed by a blank/EOL — the interior rule
237
+ * below covers it; ":name" stays plain */
238
+ break;
239
+ case '-':
240
+ case '?':
241
+ /* indicators only before a blank or EOL: "-5" and "?x" are
242
+ * plain (libyaml emits negative numbers plain; Psych, too) —
243
+ * but fall through: the document-marker check below owns
244
+ * the bare "---"/"..." (libyaml quotes those) */
245
+ if (len == 1 || is_blank(p[1])) {
246
+ return 0;
247
+ }
248
+ break;
249
+ case ',':
250
+ case '[':
251
+ case ']':
252
+ case '{':
253
+ case '}':
254
+ case '#':
255
+ case '&':
256
+ case '*':
257
+ case '!':
258
+ case '|':
259
+ case '>':
260
+ case '\'':
261
+ case '"':
262
+ case '%':
263
+ case '@':
264
+ case '`':
265
+ return 0;
266
+ default:
267
+ break;
268
+ }
269
+ /* The interior scan in SWAR words: every reject condition is a
270
+ * byte-class adjacency, so one masked word test is exact —
271
+ * break/tab bytes reject directly; ':' rejects with a blank at its
272
+ * NEXT lane (or the span's EOL); '#' with a blank at its PREVIOUS
273
+ * lane (the span's first byte is exempt — the head switch already
274
+ * handled a leading '#'). Cross-word adjacency rides one carried
275
+ * byte per boundary; the tail runs the byte loop, whose rules read
276
+ * real neighbors and need no carries. */
277
+ {
278
+ const uint64_t ONES = 0x0101010101010101ull;
279
+ const uint64_t HIGH = 0x8080808080808080ull;
280
+ #define YST_ZDET(w, ch) \
281
+ ((((w) ^ (uint64_t)(ch) * ONES) - ONES) & ~((w) ^ (uint64_t)(ch) * ONES) & HIGH)
282
+ /* the all-boring accept (#742): a span of pure [0-9A-Za-z_]
283
+ * makes EVERY rule in this function vacuous — no indicators
284
+ * (the head switch), no blanks at the ends, no BOM, no
285
+ * ': '/' #' adjacency, no breaks, and no ---/... marker — so
286
+ * one range test per word accepts without the six-mask walk.
287
+ * Anything else falls through to the exact scan below. */
288
+ {
289
+ uint32_t bi = 0;
290
+ int boring = 1;
291
+ while (bi + 8 <= len) {
292
+ uint64_t bw;
293
+ memcpy(&bw, p + bi, 8);
294
+ if (bw & HIGH) {
295
+ boring = 0;
296
+ break;
297
+ }
298
+ /* per-lane unsigned range checks via borrow bits: alpha
299
+ * is (t - 'a') and ('z' - t) both borrow-free with
300
+ * t = w|0x20 (A-Z fold); digits bound 0x30..0x39 */
301
+ uint64_t t = bw | (ONES * 0x20);
302
+ uint64_t bad = ((t - ONES * 0x61) | (ONES * 0x7A - t)) & HIGH; /* alpha */
303
+ bad |= ((bw - ONES * 0x30) | (ONES * 0x39 - bw)) & HIGH; /* digit */
304
+ bad &= ~YST_ZDET(bw, '_'); /* the one extra boring byte */
305
+ if (bad != 0) {
306
+ boring = 0;
307
+ break;
308
+ }
309
+ bi += 8;
310
+ }
311
+ if (boring) {
312
+ for (; bi < len; bi++) {
313
+ char c = p[bi];
314
+ int a = (c | 0x20);
315
+ if (!((c >= '0' && c <= '9') || (a >= 'a' && a <= 'z') || c == '_')) {
316
+ boring = 0;
317
+ break;
318
+ }
319
+ }
320
+ if (boring) {
321
+ return 1;
322
+ }
323
+ }
324
+ }
325
+ uint32_t i = 0;
326
+ int prev_blank7 = 0;
327
+ while (i + 8 <= len) {
328
+ uint64_t w;
329
+ memcpy(&w, p + i, 8);
330
+ if (w & HIGH) {
331
+ /* multibyte content: the byte loop owns the rest — a raw
332
+ * LS/PS/NEL rides in a scalar value since the quote-aware
333
+ * normalizer (#638) and is a line break on re-parse, so
334
+ * the value must be quoted, not plain (the nightly's
335
+ * round 18, crash-1e79b2e3) */
336
+ break;
337
+ }
338
+ uint64_t space = YST_ZDET(w, ' ');
339
+ uint64_t tab = YST_ZDET(w, '\t');
340
+ uint64_t blank = space | tab;
341
+ uint64_t colon = YST_ZDET(w, ':');
342
+ uint64_t hash = YST_ZDET(w, '#');
343
+ /* tab and the breaks reject outright */
344
+ if (tab | YST_ZDET(w, '\n') | YST_ZDET(w, '\r')) {
345
+ return 0;
346
+ }
347
+ /* ':' + blank next: (blank >> 8) pulls lane i+1's flag down
348
+ * into lane i; the word's last lane takes the peeked byte
349
+ * (EOL at the span end counts as blank — the rule rejects
350
+ * there) */
351
+ if (colon) {
352
+ int next_blank = i + 8 < len ? is_blank(p[i + 8]) : 1;
353
+ if (colon & ((blank >> 8) | (next_blank ? (uint64_t)0x80 << 56 : 0))) {
354
+ return 0;
355
+ }
356
+ }
357
+ /* '#' + blank prev: (blank << 8) lifts lane i-1's flag up
358
+ * into lane i; lane 0 takes the carried last-lane flag */
359
+ if (hash && (hash & ((blank << 8) | (prev_blank7 ? 0x80 : 0)))) {
360
+ return 0;
361
+ }
362
+ prev_blank7 = (int)((blank >> 63) & 1); /* lane 7's flag is its top bit */
363
+ i += 8;
364
+ }
365
+ #undef YST_ZDET
366
+ for (; i < len; i++) {
367
+ char c = p[i];
368
+ if (is_break(c) || c == '\t') {
369
+ return 0;
370
+ }
371
+ if ((unsigned char)c == 0xE2 && i + 2 < len && (unsigned char)p[i + 1] == 0x80 &&
372
+ ((unsigned char)p[i + 2] & 0xFE) == 0xA8) {
373
+ return 0; /* a raw LS/PS re-parses as a line break */
374
+ }
375
+ if ((unsigned char)c == 0xC2 && i + 1 < len && (unsigned char)p[i + 1] == 0x85) {
376
+ return 0; /* a raw NEL ditto */
377
+ }
378
+ if (c == ':') {
379
+ if (i + 1 >= len || is_blank(p[i + 1])) {
380
+ return 0;
381
+ }
382
+ }
383
+ if (c == '#') {
384
+ if (i > 0 && is_blank(p[i - 1])) {
385
+ return 0;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ /* document markers */
391
+ if (len >= 3 && (memcmp(p, "---", 3) == 0 || memcmp(p, "...", 3) == 0) &&
392
+ (len == 3 || p[3] == ' ' || p[3] == '\t' || p[3] == '\n' || p[3] == '\r')) {
393
+ /* a PREFIX marker is worse than the bare one: emitted plain at
394
+ * line start, "--- ..." re-reads as a document marker (the
395
+ * nightly's "--- --- >" folded-plain crasher) — libyaml quotes
396
+ * both */
397
+ return 0;
398
+ }
399
+ return 1;
400
+ }
401
+
201
402
  size_t yep_scan_prop_end(const char* p, size_t len, size_t pos) {
202
403
  while (pos < len) {
203
404
  unsigned char c = (unsigned char)p[pos];
@@ -20,7 +20,9 @@ extern "C" {
20
20
  typedef struct yep_line_info {
21
21
  uint32_t offset; /* byte offset of the line start */
22
22
  uint32_t end; /* byte offset of the line break (exclusive content end) */
23
- uint16_t indent; /* column of the first non-space (== end - offset when blank) */
23
+ uint32_t indent; /* column of the first non-space (== end - offset when blank);
24
+ * 32-bit: a 65536-space line wrapped to 0 and read as a
25
+ * dedent (the 2026-10-31 audit) */
24
26
  uint16_t flags;
25
27
  unsigned char first; /* first content byte ('\0' for blank lines) */
26
28
  } yep_line_info;
@@ -104,6 +106,15 @@ size_t yep_scan_prop_end(const char* p, size_t len, size_t pos);
104
106
  * AND the DOM's parse-time fact bit, so the two cannot drift. */
105
107
  int yep_scan_dq_forced(const char* p, uint32_t len);
106
108
 
109
+ /* The plain-safety truth table (issue #352 lane 3): 1 when the span
110
+ * re-parses as a plain scalar in BLOCK context (value or key — the
111
+ * key wrapper is the same predicate; the colon rule covers keys).
112
+ * The bytes→facts home: the emitter's style chooser AND the DOM's
113
+ * parse-time fact bit read this one walk, so the verdicts cannot
114
+ * drift. Multibyte-aware (raw NEL/LS/PS reject; a leading U+FEFF
115
+ * rejects; mid-token BOMs ride). */
116
+ int yep_scan_plain_safe(const char* p, uint32_t len);
117
+
107
118
  /* Tag-shorthand suffix byte: libyaml's URI set (";/?:@&=+$,_.!~*'()%"
108
119
  * plus alphanumerics; flow indicators and ',' terminate instead).
109
120
  * Stray non-set bytes stay visible as the props follower, which
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yeptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.52.0
4
+ version: 0.6.53.0
5
5
  platform: arm-linux-eabihf
6
6
  authors:
7
7
  - Ribose Inc.