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,11 @@ int yep_engine_step(yep_engine* e, const char* chunk, size_t len, int final, con
53
53
  * allocates a fresh one if reused). Returns NULL when absent. */
54
54
  yep_pool* yep_engine_detach_pool(yep_engine* e);
55
55
 
56
+ /* Detaches the run's break-normalizer copy (ownership moves to the
57
+ * caller; the caller owns the returned buffer once detached). Returns
58
+ * NULL when the run kept the input zero-copy. */
59
+ char* yep_engine_detach_norm_buf(yep_engine* e, size_t* len_out);
60
+
56
61
  /* Implicit-typing schema for the next run (NULL: 1.2 core), and the
57
62
  * runtime nesting limit. */
58
63
  struct yep_resolver;
@@ -67,6 +67,9 @@ struct yeptris_stream_iter {
67
67
  YeptrisStatus last_status; /* the LAST next()'s per-document verdict */
68
68
  uint32_t last_line;
69
69
  uint32_t last_col;
70
+ size_t last_off; /* the last SUCCESSFUL document's slice
71
+ * in the (post-gate) buffer space */
72
+ size_t last_len;
70
73
  };
71
74
 
72
75
  yeptris_stream_iter* yeptris_stream_iter_new(const char* buf, size_t len,
@@ -127,6 +130,8 @@ static void it_fail(struct yeptris_stream_iter* it, YeptrisStatus st, uint32_t l
127
130
  it->last_status = st;
128
131
  it->last_line = line;
129
132
  it->last_col = col;
133
+ it->last_len = 0; /* the slice accessor must not serve a stale frame
134
+ * (the success path sets off/len right after) */
130
135
  }
131
136
 
132
137
  static int engine_opts(yep_engine* eng, const YeptrisParseOptions* opts) {
@@ -290,12 +295,14 @@ YeptrisStatus yeptris_stream_iter_next(struct yeptris_stream_iter* it, YeptrisDo
290
295
  }
291
296
  return it->last_status;
292
297
  }
298
+ it_fail(it, YEPTRIS_OK, 0, 0);
299
+ it->last_off = it->cursor;
300
+ it->last_len = pos;
293
301
  it->cursor += pos;
294
302
  it->index++;
295
303
  if (index != NULL) {
296
304
  *index = it->index - 1;
297
305
  }
298
- it_fail(it, YEPTRIS_OK, 0, 0);
299
306
  *out = doc;
300
307
  return YEPTRIS_OK;
301
308
  }
@@ -315,6 +322,31 @@ YeptrisStatus yeptris_stream_iter_status(const struct yeptris_stream_iter* it, u
315
322
  return it->last_status;
316
323
  }
317
324
 
325
+ /* The last successful document's slice (parse.h): the host's value
326
+ * route re-parses exactly these bytes — one typing SSOT. */
327
+ YeptrisStatus yeptris_stream_iter_slice(const struct yeptris_stream_iter* it, size_t* off,
328
+ size_t* len) {
329
+ if (it == NULL || off == NULL || len == NULL) {
330
+ return YEPTRIS_ERROR_ARG;
331
+ }
332
+ if (it->last_status != YEPTRIS_OK || it->last_len == 0) {
333
+ return YEPTRIS_ERROR_ARG; /* a failure or the stream end was the last step */
334
+ }
335
+ *off = it->last_off;
336
+ *len = it->last_len;
337
+ return YEPTRIS_OK;
338
+ }
339
+
340
+ YeptrisStatus yeptris_stream_iter_buffer(const struct yeptris_stream_iter* it, const char** buf,
341
+ size_t* len) {
342
+ if (it == NULL || buf == NULL || len == NULL) {
343
+ return YEPTRIS_ERROR_ARG;
344
+ }
345
+ *buf = it->buf;
346
+ *len = it->len;
347
+ return YEPTRIS_OK;
348
+ }
349
+
318
350
  void yeptris_stream_iter_free(struct yeptris_stream_iter* it) {
319
351
  if (it == NULL) {
320
352
  return;
@@ -458,6 +458,11 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
458
458
  int norm_kept_raw = 0; /* raw NEL/LS/PS survived the quote-aware
459
459
  * walk: the engine route owns the document
460
460
  * (the tape runner predates the cut) */
461
+ char* nbuf = NULL; /* the engine's break-normalizer copy (#953):
462
+ * DOM and tape scalar slices point INTO it,
463
+ * so the document must own it like the
464
+ * finish pool and transcoded */
465
+ size_t nblen = 0;
461
466
 
462
467
  if (parse_frontend(buf, len, sys, json_mode, &data, &data_len, &transcoded, &transcoded_len,
463
468
  &norm_kept_raw, &st) != 0) {
@@ -600,8 +605,11 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
600
605
  }
601
606
  /* Resolved tags, folded and escaped scalars live in the engine's
602
607
  * finish pool; the document must own it or every such string would
603
- * dangle at engine teardown (found by ASAN). */
608
+ * dangle at engine teardown (found by ASAN). The break-normalizer
609
+ * copy is the same law (#953): scalar value slices in the DOM and
610
+ * the packed tape point INTO it. */
604
611
  yep_pool* finish = yep_engine_detach_pool(eng);
612
+ nbuf = yep_engine_detach_norm_buf(eng, &nblen);
605
613
  yep_engine_destroy(eng);
606
614
 
607
615
  /* Empty stream (no documents): NULL document with YEPTRIS_OK. */
@@ -613,6 +621,9 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
613
621
  }
614
622
  yep_pool_destroy(finish);
615
623
  yep_free(sys, transcoded);
624
+ if (nbuf != NULL) {
625
+ yep_free(sys, nbuf); /* not parked: no document survives */
626
+ }
616
627
  if (status != NULL) {
617
628
  *status = YEPTRIS_OK;
618
629
  }
@@ -631,6 +642,9 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
631
642
  yep_dom_destroy(dom);
632
643
  yep_pool_destroy(finish);
633
644
  yep_free(sys, transcoded);
645
+ if (nbuf != NULL) {
646
+ yep_free(sys, nbuf); /* not parked: no document survives */
647
+ }
634
648
  st = YEPTRIS_ERROR_MEMORY;
635
649
  goto fail;
636
650
  }
@@ -640,6 +654,31 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
640
654
  : YEPTRIS_SCHEMA_12_CORE;
641
655
  doc->transcoded = transcoded;
642
656
  doc->transcoded_len = transcoded_len;
657
+ if (nbuf != NULL) {
658
+ /* #953: the engine re-normalized THIS run (raw NEL/LS/PS rode
659
+ * quoted regions past the front-end), so every engine event
660
+ * view points INTO nbuf — and the lazy tape stored those
661
+ * pointers ABSOLUTE (they fall outside yt.input's range, so
662
+ * the span recorder kept them verbatim). The document owns
663
+ * nbuf like the finish pool, whichever copy the slot held. */
664
+ if (have_tape) {
665
+ /* rebase the tape's span base onto the doc-owned copy:
666
+ * materialization's input_base offsets against LIVE bytes.
667
+ * `transcoded` is referenced by no record (the engine
668
+ * parsed nbuf only) — release it. */
669
+ yt.input = nbuf;
670
+ yt.input_len = nblen;
671
+ yep_free(sys, transcoded);
672
+ transcoded = NULL;
673
+ doc->transcoded = (unsigned char*)nbuf;
674
+ doc->transcoded_len = nblen;
675
+ } else {
676
+ /* eager: the dom copied every scalar during the run (the
677
+ * borrowed views offset against input_base = transcoded);
678
+ * nbuf has no post-run reader — the slot keeps transcoded */
679
+ yep_free(sys, nbuf);
680
+ }
681
+ }
643
682
  doc->input = buf;
644
683
  doc->finish_pool = finish; /* NULL on the eager route */
645
684
  doc->lazy_tape = NULL;
@@ -650,6 +689,9 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
650
689
  ytap_free(&yt);
651
690
  yep_pool_destroy(finish);
652
691
  yep_free(sys, transcoded);
692
+ if (nbuf != NULL) {
693
+ yep_free(sys, nbuf); /* not parked: no document survives */
694
+ }
653
695
  yep_free(sys, doc);
654
696
  st = YEPTRIS_ERROR_MEMORY;
655
697
  goto fail;
@@ -65,11 +65,8 @@ static yep_tag_id compat11(void* ctx, const char* p, uint32_t n) {
65
65
  if (n == 0 || eq(p, n, "~") || ci_eq3(p, n, "null")) {
66
66
  return 4; /* null: psych nil for ""/"~"/null (any case) */
67
67
  }
68
- if (ci_eq3(p, n, "yes") || ci_eq3(p, n, "true") || ci_eq3(p, n, "on") || ci_eq3(p, n, "y")) {
69
- return 3;
70
- }
71
- if (ci_eq3(p, n, "no") || ci_eq3(p, n, "false") || ci_eq3(p, n, "off") || ci_eq3(p, n, "n")) {
72
- return 3;
68
+ if (yep_tag_bool_value(p, n) >= 0) {
69
+ return 3; /* the one bool table (tags.c): true/false sets ci */
73
70
  }
74
71
  if (eq(p, n, "=")) {
75
72
  return 10; /* value */
@@ -82,4 +82,22 @@ yep_tag_id yep_tag_from_uri(const char* p, uint32_t len);
82
82
  * portably). */
83
83
  const char* yep_tag_float_word(const char* p, uint32_t len);
84
84
 
85
+ /* The BOOL truth table for a resolved-bool view: 1 true, 0 false,
86
+ * -1 not a bool word. One table, two consumers: compat11's classify
87
+ * and the canonical/JSON emitter's BOOL arm — the DOM stores only the
88
+ * tag + source text, so the emitter re-asks the table (#937: guessing
89
+ * the value from the text's length spelled every non-4-char true-word
90
+ * false). The ci union of the families' sets (core's true/false forms
91
+ * are in the compat superset), so no family fact is needed. */
92
+ int yep_tag_bool_value(const char* p, uint32_t len);
93
+
94
+ /* Decode a resolved-INT view to its value through the family's own
95
+ * accepted forms: radix prefixes 0x/0o in both (0b and the leading-0
96
+ * octal + _/,/sexagesimal forms only when compat — core spells
97
+ * leading-0 decimal), digits otherwise. Overflow beyond the int64
98
+ * range returns nonzero and *out is untouched. The canonical/JSON
99
+ * emitter's INT arm (#937: source text like 0x1F is not a JSON
100
+ * token). */
101
+ int yep_tag_int64(const char* p, uint32_t len, int compat, int64_t* out);
102
+
85
103
  #endif /* YEP_RESOLVER_H */
@@ -56,3 +56,195 @@ const char* yep_tag_float_word(const char* p, uint32_t len) {
56
56
  }
57
57
  return NULL;
58
58
  }
59
+
60
+ static int ci_eq(const char* p, uint32_t n, const char* s) {
61
+ uint32_t m = 0;
62
+ while (s[m] != '\0') {
63
+ m++;
64
+ }
65
+ if (n != m) {
66
+ return 0;
67
+ }
68
+ for (uint32_t k = 0; k < n; k++) {
69
+ char a = p[k], b = s[k];
70
+ if (a >= 'A' && a <= 'Z') {
71
+ a = (char)(a - 'A' + 'a');
72
+ }
73
+ if (b >= 'A' && b <= 'Z') {
74
+ b = (char)(b - 'A' + 'a');
75
+ }
76
+ if (a != b) {
77
+ return 0;
78
+ }
79
+ }
80
+ return 1;
81
+ }
82
+
83
+ int yep_tag_bool_value(const char* p, uint32_t len) {
84
+ static const char* const words[8] = {"y", "yes", "true", "on", "n", "no", "false", "off"};
85
+ for (unsigned i = 0; i < 8; i++) {
86
+ if (ci_eq(p, len, words[i])) {
87
+ return i < 4 ? 1 : 0;
88
+ }
89
+ }
90
+ return -1;
91
+ }
92
+
93
+ /* One base-denominated digit with overflow detection (uint64 keeps
94
+ * the INT64_MIN magnitude representable). */
95
+ static int acc_digit(uint64_t* acc, int base, int dv) {
96
+ uint64_t na = *acc * (uint64_t)base;
97
+ if (na / (uint64_t)base != *acc) {
98
+ return 1;
99
+ }
100
+ na += (uint64_t)dv;
101
+ if (na < *acc) {
102
+ return 1;
103
+ }
104
+ *acc = na;
105
+ return 0;
106
+ }
107
+
108
+ int yep_tag_int64(const char* p, uint32_t n, int compat, int64_t* out) {
109
+ uint32_t i = 0;
110
+ int neg = 0;
111
+ if (n == 0) {
112
+ return 1;
113
+ }
114
+ if (p[0] == '-' || p[0] == '+') {
115
+ neg = p[0] == '-';
116
+ i = 1;
117
+ if (i >= n) {
118
+ return 1;
119
+ }
120
+ }
121
+ /* radix prefixes first (both families, lowercase only — "0X1A"
122
+ * rides a string in core AND compat; 0o is core's spelling, 0b
123
+ * compat's), then compat's leading-0 octal, then the plain
124
+ * decimal/sexagesimal walk */
125
+ uint64_t acc = 0;
126
+ int digits = 0;
127
+ int base = 10;
128
+ uint32_t d = i;
129
+ if (n - i > 2 && p[i] == '0' && p[i + 1] == 'x') {
130
+ base = 16;
131
+ d = i + 2;
132
+ if (compat && p[d] == '_') {
133
+ d++;
134
+ if (p[d] == '_') {
135
+ return 1;
136
+ }
137
+ }
138
+ } else if (n - i > 2 && p[i] == '0' && p[i + 1] == 'o') {
139
+ if (compat) {
140
+ return 1; /* 0o is not a compat form (psych) */
141
+ }
142
+ base = 8;
143
+ d = i + 2;
144
+ } else if (compat && n - i > 2 && p[i] == '0' && p[i + 1] == 'b') {
145
+ base = 2;
146
+ d = i + 2;
147
+ if (p[d] == '_') {
148
+ d++;
149
+ if (p[d] == '_') {
150
+ return 1;
151
+ }
152
+ }
153
+ } else if (compat && p[i] == '0' && n - i >= 2) {
154
+ /* leading-0 octal (psych): 0[_]*[0-7][0-7_]* — the radix
155
+ * prefixes above never reach this arm */
156
+ base = 8;
157
+ d = i + 1;
158
+ while (d < n && p[d] == '_') {
159
+ d++;
160
+ }
161
+ if (d >= n) {
162
+ *out = 0; /* plain "0" / "000" */
163
+ return 0;
164
+ }
165
+ } else if (compat && p[i] == '0' && n - i == 1) {
166
+ *out = 0;
167
+ return 0;
168
+ } else if (compat && p[i] >= '0' && p[i] <= '9') {
169
+ /* sexagesimal int (compat only): d[d_,]*(:[0-5]?d){1,2} — the
170
+ * fractional form is float-tagged and never reaches here, but
171
+ * a '.' still refuses (the decode must not guess). '_' and ','
172
+ * are the compat separators (psych legacy). */
173
+ uint32_t k = i;
174
+ while (k < n && ((p[k] >= '0' && p[k] <= '9') || p[k] == '_' || p[k] == ',')) {
175
+ if (p[k] != '_' && p[k] != ',') {
176
+ if (acc_digit(&acc, 10, p[k] - '0')) {
177
+ return 1;
178
+ }
179
+ digits = 1;
180
+ }
181
+ k++;
182
+ }
183
+ if (k < n && p[k] == ':') {
184
+ while (k < n && p[k] == ':') {
185
+ k++;
186
+ int gd = 0;
187
+ int g = 0;
188
+ while (k < n && p[k] >= '0' && p[k] <= '9' && gd < 2) {
189
+ g = g * 10 + (p[k] - '0');
190
+ gd++;
191
+ k++;
192
+ }
193
+ if (gd < 1 || g > 59) {
194
+ return 1;
195
+ }
196
+ if (acc_digit(&acc, 60, g)) {
197
+ return 1;
198
+ }
199
+ }
200
+ if (k == n) {
201
+ *out = neg ? -(int64_t)acc : (int64_t)acc;
202
+ return 0;
203
+ }
204
+ return 1;
205
+ }
206
+ /* a ':'-less digit span: the plain decimal path below would
207
+ * re-read it — answer directly */
208
+ if (k == n && digits) {
209
+ *out = neg ? -(int64_t)acc : (int64_t)acc;
210
+ return 0;
211
+ }
212
+ return 1;
213
+ }
214
+ acc = 0;
215
+ digits = 0;
216
+ for (uint32_t k = d; k < n; k++) {
217
+ char c = p[k];
218
+ int dv;
219
+ if (c >= '0' && c <= '9') {
220
+ dv = c - '0';
221
+ } else if (base == 16 && c >= 'a' && c <= 'f') {
222
+ dv = c - 'a' + 10;
223
+ } else if (base == 16 && c >= 'A' && c <= 'F') {
224
+ dv = c - 'A' + 10;
225
+ } else if (compat && (c == '_' || c == ',')) {
226
+ continue;
227
+ } else {
228
+ return 1;
229
+ }
230
+ if (base != 16 && dv >= base) {
231
+ return 1;
232
+ }
233
+ if (acc_digit(&acc, base, dv)) {
234
+ return 1;
235
+ }
236
+ digits = 1;
237
+ }
238
+ if (!digits) {
239
+ return 1;
240
+ }
241
+ if (neg && acc == (uint64_t)INT64_MAX + 1u) {
242
+ *out = INT64_MIN;
243
+ return 0;
244
+ }
245
+ if (acc > (uint64_t)INT64_MAX) {
246
+ return 1;
247
+ }
248
+ *out = neg ? -(int64_t)acc : (int64_t)acc;
249
+ return 0;
250
+ }
@@ -689,18 +689,33 @@ yep_span yep_scan_plain(const char* p, size_t len, size_t pos, int flow) {
689
689
  size_t at = (hit < 0) ? len : i + (size_t)hit;
690
690
  /* a multibyte break inside the piece ends it like an EOL: the
691
691
  * byte stopset cannot see NEL/LS/PS, and the continuation
692
- * would ride across the break into the next line's tokens */
693
- if (at > i) {
694
- const char* lead = (const char*)memchr(p + i, 0xC2, at - i);
695
- const char* lead2 = (const char*)memchr(p + i, 0xE2, at - i);
692
+ * would ride across the break into the next line's tokens.
693
+ * EVERY lead byte in the piece must be tested: the first lead
694
+ * can be an ordinary multibyte character (the r169 yen U+00A5
695
+ * leading a NEL run) while a later one is a break — testing
696
+ * the first lead only declined the check there and rode the
697
+ * break raw into the scalar (the false-lead ride, the r74
698
+ * plain-ends-at-NEL law defeated by the lead's own position). */
699
+ int mb_broken = 0;
700
+ for (size_t scan = i; at > scan;) {
701
+ const char* lead = (const char*)memchr(p + scan, 0xC2, at - scan);
702
+ const char* lead2 = (const char*)memchr(p + scan, 0xE2, at - scan);
696
703
  const char* first =
697
704
  (lead && lead2) ? (lead < lead2 ? lead : lead2) : (lead ? lead : lead2);
698
- if (first != NULL && yep_scan_mb_break_len(p, len, (size_t)(first - p)) > 0) {
705
+ if (first == NULL) {
706
+ break;
707
+ }
708
+ if (yep_scan_mb_break_len(p, len, (size_t)(first - p)) > 0) {
699
709
  at = (size_t)(first - p);
700
- i = at;
701
- s.term = YEP_TERM_EOL;
710
+ mb_broken = 1;
702
711
  break;
703
712
  }
713
+ scan = (size_t)(first - p) + 1; /* a false lead: advance past it */
714
+ }
715
+ if (mb_broken) {
716
+ i = at;
717
+ s.term = YEP_TERM_EOL;
718
+ break;
704
719
  }
705
720
  unsigned char c = (at < len) ? (unsigned char)p[at] : 0;
706
721
 
@@ -1278,13 +1278,32 @@ ldone:
1278
1278
  * Y79Y#4 error: defer to the engine, whose e_flow_ws owns the law
1279
1279
  * -- the walk's whitespace loops treat the tab as separation, which
1280
1280
  * accepted exactly the span the marshal drain's engine rejected,
1281
- * splitting parseable from marshalable (the nightly's round 120) */
1282
- for (size_t k = open + 1; k < len; k++) {
1283
- if (p[k] == '\n' && k + 1 < len && p[k + 1] == '\t') {
1284
- yeptris_tape_free(t);
1285
- return YEPTRIS_ERROR_PARSE;
1281
+ * splitting parseable from marshalable (the nightly's round 120).
1282
+ * clean_only skips the scan: its byte guarantees already rejected
1283
+ * every tab in the span (LWS stops on one, the string c0 sweeps
1284
+ * and the tail reject it), so the pair cannot exist there. The
1285
+ * lenient lane rides memchr — a '\n'-hit vector pass instead of a
1286
+ * two-compare byte loop over the whole span (13% of json-doc). */
1287
+ if (!cl) {
1288
+ size_t k = open + 1;
1289
+ while (k < len) {
1290
+ const char* nl = (const char*)memchr(p + k, '\n', len - k);
1291
+ if (nl == NULL) {
1292
+ break;
1293
+ }
1294
+ k = (size_t)(nl - p) + 1;
1295
+ if (k < len && p[k] == '\t') {
1296
+ yeptris_tape_free(t);
1297
+ return YEPTRIS_ERROR_PARSE;
1298
+ }
1286
1299
  }
1287
1300
  }
1301
+ /* the >1024 simple-key verdict settles HERE when no key crossed the
1302
+ * raw cap in lmapcolon: chars <= bytes closes the char budget too,
1303
+ * so no key can violate the law — without this, every clean
1304
+ * document paid yep_tape_long_simple_key's full-tape re-walk (16%
1305
+ * of json-doc parse, the #739 residual) */
1306
+ t->_long_key_known = 1;
1288
1307
  t->count = count;
1289
1308
  t->_src = p;
1290
1309
  t->_srclen = len;
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.60.1
4
+ version: 0.6.61.1
5
5
  platform: arm-linux-eabihf
6
6
  authors:
7
7
  - Ribose Inc.