yeptris 0.6.18.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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/README.adoc +141 -0
  3. data/ext/build_windows_native.rb +41 -0
  4. data/ext/libyeptris/extconf.rb +57 -0
  5. data/ext/yeptris_native/cbor_ruby.c +193 -0
  6. data/ext/yeptris_native/extconf.rb +47 -0
  7. data/ext/yeptris_native/json_ruby.c +354 -0
  8. data/ext/yeptris_native/yeptris_native.c +497 -0
  9. data/lib/yeptris/cbor.rb +130 -0
  10. data/lib/yeptris/document.rb +255 -0
  11. data/lib/yeptris/ffi.rb +426 -0
  12. data/lib/yeptris/json/descriptor.rb +266 -0
  13. data/lib/yeptris/json.rb +394 -0
  14. data/lib/yeptris/materializer.rb +354 -0
  15. data/lib/yeptris/native-3.4.so +0 -0
  16. data/lib/yeptris/node.rb +453 -0
  17. data/lib/yeptris/psych/class_loader.rb +24 -0
  18. data/lib/yeptris/psych/coder_shim.rb +55 -0
  19. data/lib/yeptris/psych/drop_in.rb +63 -0
  20. data/lib/yeptris/psych/encodable.rb +14 -0
  21. data/lib/yeptris/psych/handler.rb +90 -0
  22. data/lib/yeptris/psych/parser.rb +105 -0
  23. data/lib/yeptris/psych/scalar_scanner.rb +149 -0
  24. data/lib/yeptris/psych/visitors.rb +545 -0
  25. data/lib/yeptris/psych.rb +622 -0
  26. data/lib/yeptris/schema.rb +253 -0
  27. data/lib/yeptris/valueml.rb +455 -0
  28. data/lib/yeptris/yaml/descriptor.rb +56 -0
  29. data/lib/yeptris/yaml.rb +422 -0
  30. data/lib/yeptris.rb +113 -0
  31. data/libyeptris.so +0 -0
  32. data/vendor/libyeptris/CMakeLists.txt +254 -0
  33. data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
  34. data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
  35. data/vendor/libyeptris/src/CMakeLists.txt +167 -0
  36. data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
  37. data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
  38. data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
  39. data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
  40. data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
  41. data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
  42. data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
  43. data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
  44. data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
  45. data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
  46. data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
  47. data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
  48. data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
  49. data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
  50. data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
  51. data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
  52. data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
  53. data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
  54. data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
  55. data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
  56. data/vendor/libyeptris/src/include/yeptris.h +23 -0
  57. data/vendor/libyeptris/src/yeptris/build.c +376 -0
  58. data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
  59. data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
  60. data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
  61. data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
  62. data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
  63. data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
  64. data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
  65. data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
  66. data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
  67. data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
  68. data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
  69. data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
  70. data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
  71. data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
  72. data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
  73. data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
  74. data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
  75. data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
  76. data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
  77. data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
  78. data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
  79. data/vendor/libyeptris/src/yeptris/doc.h +56 -0
  80. data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
  81. data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
  82. data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
  83. data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
  84. data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
  85. data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
  86. data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
  87. data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
  88. data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
  89. data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
  90. data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
  91. data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
  92. data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
  93. data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
  94. data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
  95. data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
  96. data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
  97. data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
  98. data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
  99. data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
  100. data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
  101. data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
  102. data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
  103. data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
  104. data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
  105. data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
  106. data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
  107. data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
  108. data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
  109. data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
  110. data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
  111. data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
  112. data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
  113. data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
  114. data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
  115. data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
  116. data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
  117. data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
  118. data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
  119. data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
  120. data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
  121. data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
  122. data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
  123. data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
  124. data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
  125. data/vendor/libyeptris/src/yeptris/parse.c +737 -0
  126. data/vendor/libyeptris/src/yeptris/plan.c +824 -0
  127. data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
  128. data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
  129. data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
  130. data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
  131. data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
  132. data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
  133. data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
  134. data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
  135. data/vendor/libyeptris/src/yeptris/schema.c +308 -0
  136. data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
  137. data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
  138. data/vendor/libyeptris/src/yeptris/version.c +7 -0
  139. data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
  140. data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
  141. data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
  142. metadata +199 -0
@@ -0,0 +1,792 @@
1
+ /* json.c — the strict-JSON grammar (RFC 8259), TODO.impl/08C/21.
2
+ *
3
+ * MECE: this is the ONLY place that decides what strict JSON is —
4
+ * the engine's flow fast path (parse/engine.c) and the whole-input
5
+ * JSON mode (yeptris_parse_json) both consume these primitives; the
6
+ * grammar never diverges between them.
7
+ */
8
+
9
+ #include <stdint.h>
10
+ #include <string.h>
11
+
12
+ #include "common/chartype.h"
13
+ #include "common/simd_text.h"
14
+
15
+ #include "parse/numbers.h"
16
+ #include "parse/scalars.h"
17
+ #include "scan/json.h"
18
+
19
+ /* ---- whole-document strict validation (JSON mode) ----
20
+ *
21
+ * optional ws, one JSON value, optional ws, EOF. err (may be NULL)
22
+ * receives the offset of the first violation. Depth is bounded by the
23
+ * shared engine limit; exceeding it fails validation like any other
24
+ * violation. */
25
+
26
+ #define JX_MAX_DEPTH 256
27
+
28
+ enum { JV_VALUE_OR_CLOSE = 0, JV_VALUE, JV_KEY_OR_CLOSE, JV_KEY, JV_COLON, JV_COMMA_OR_CLOSE };
29
+
30
+ static int jv_ws(const char* p, size_t len, size_t* i) {
31
+ while (*i < len) {
32
+ char c = p[*i];
33
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
34
+ (*i)++;
35
+ continue;
36
+ }
37
+ return 1;
38
+ }
39
+ return 0; /* EOF */
40
+ }
41
+
42
+ int yep_json_document(const char* p, size_t len, size_t* err) {
43
+ size_t i = 0;
44
+ if (err != NULL) {
45
+ *err = 0;
46
+ }
47
+ if (!jv_ws(p, len, &i)) {
48
+ if (err != NULL) {
49
+ *err = i;
50
+ }
51
+ return 0; /* empty is not a JSON document */
52
+ }
53
+ uint8_t kind[JX_MAX_DEPTH];
54
+ uint8_t expect[JX_MAX_DEPTH];
55
+ int depth = 0;
56
+ /* virtual root frame: expects exactly one value */
57
+ kind[0] = 0;
58
+ expect[0] = JV_VALUE;
59
+ depth = 1;
60
+ int root_done = 0;
61
+ for (;;) {
62
+ if (!jv_ws(p, len, &i)) {
63
+ if (err != NULL) {
64
+ *err = i;
65
+ }
66
+ return 0; /* EOF inside a value */
67
+ }
68
+ char c = p[i];
69
+ if (c == ']' || c == '}') {
70
+ int want = (c == ']') ? 0 : 1;
71
+ if (depth == 1 || kind[depth - 1] != want ||
72
+ (expect[depth - 1] != JV_VALUE_OR_CLOSE && expect[depth - 1] != JV_KEY_OR_CLOSE &&
73
+ expect[depth - 1] != JV_COMMA_OR_CLOSE)) {
74
+ if (err != NULL) {
75
+ *err = i;
76
+ }
77
+ return 0;
78
+ }
79
+ depth--;
80
+ if (depth == 1) {
81
+ /* closed the root collection */
82
+ root_done = 1;
83
+ i++;
84
+ break;
85
+ }
86
+ expect[depth - 1] = JV_COMMA_OR_CLOSE;
87
+ i++;
88
+ continue;
89
+ }
90
+ if (root_done) {
91
+ if (err != NULL) {
92
+ *err = i;
93
+ }
94
+ return 0; /* trailing content */
95
+ }
96
+ switch (expect[depth - 1]) {
97
+ case JV_COLON:
98
+ if (c != ':') {
99
+ if (err != NULL) {
100
+ *err = i;
101
+ }
102
+ return 0;
103
+ }
104
+ expect[depth - 1] = JV_VALUE;
105
+ i++;
106
+ continue;
107
+ case JV_COMMA_OR_CLOSE:
108
+ if (c != ',') {
109
+ if (err != NULL) {
110
+ *err = i;
111
+ }
112
+ return 0;
113
+ }
114
+ expect[depth - 1] = kind[depth - 1] ? JV_KEY : JV_VALUE;
115
+ i++;
116
+ continue;
117
+ default:
118
+ break;
119
+ }
120
+ if (c == '{' || c == '[') {
121
+ if (depth >= JX_MAX_DEPTH) {
122
+ if (err != NULL) {
123
+ *err = i;
124
+ }
125
+ return 0;
126
+ }
127
+ if (kind[depth - 1] == 1 &&
128
+ (expect[depth - 1] == JV_KEY_OR_CLOSE || expect[depth - 1] == JV_KEY)) {
129
+ if (err != NULL) {
130
+ *err = i;
131
+ }
132
+ return 0; /* strict JSON: a container is not a map key */
133
+ }
134
+ kind[depth] = (c == '[') ? 0 : 1;
135
+ expect[depth] = kind[depth] ? JV_KEY_OR_CLOSE : JV_VALUE_OR_CLOSE;
136
+ depth++;
137
+ i++;
138
+ continue;
139
+ }
140
+ int he = 0;
141
+ size_t close;
142
+ if (c == '"') {
143
+ if (!yep_json_string(p, len, &i, &close, &he)) {
144
+ if (err != NULL) {
145
+ *err = i;
146
+ }
147
+ return 0;
148
+ }
149
+ } else if (c == '-' || (c >= '0' && c <= '9')) {
150
+ if (!yep_json_number(p, len, &i)) {
151
+ if (err != NULL) {
152
+ *err = i;
153
+ }
154
+ return 0;
155
+ }
156
+ } else if (c == 't') {
157
+ if (!yep_json_literal(p, len, &i, "true")) {
158
+ if (err != NULL) {
159
+ *err = i;
160
+ }
161
+ return 0;
162
+ }
163
+ } else if (c == 'f') {
164
+ if (!yep_json_literal(p, len, &i, "false")) {
165
+ if (err != NULL) {
166
+ *err = i;
167
+ }
168
+ return 0;
169
+ }
170
+ } else if (c == 'n') {
171
+ if (!yep_json_literal(p, len, &i, "null")) {
172
+ if (err != NULL) {
173
+ *err = i;
174
+ }
175
+ return 0;
176
+ }
177
+ } else {
178
+ if (err != NULL) {
179
+ *err = i;
180
+ }
181
+ return 0;
182
+ }
183
+ if (depth == 1) {
184
+ root_done = 1;
185
+ break; /* scalar root */
186
+ }
187
+ if (kind[depth - 1] == 1 && expect[depth - 1] == JV_KEY_OR_CLOSE) {
188
+ if (c != '"') {
189
+ if (err != NULL) {
190
+ *err = i;
191
+ }
192
+ return 0; /* JSON map keys are strings only */
193
+ }
194
+ expect[depth - 1] = JV_COLON;
195
+ } else if (kind[depth - 1] == 1 && expect[depth - 1] == JV_KEY) {
196
+ if (c != '"') {
197
+ if (err != NULL) {
198
+ *err = i;
199
+ }
200
+ return 0;
201
+ }
202
+ expect[depth - 1] = JV_COLON;
203
+ } else {
204
+ expect[depth - 1] = JV_COMMA_OR_CLOSE;
205
+ }
206
+ }
207
+ /* trailing whitespace only, and then the input must end */
208
+ while (i < len) {
209
+ char c = p[i];
210
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
211
+ i++;
212
+ continue;
213
+ }
214
+ if (err != NULL) {
215
+ *err = i;
216
+ }
217
+ return 0;
218
+ }
219
+ return 1;
220
+ }
221
+
222
+ /* ---- token primitives (shared with the engine fast path) ---- */
223
+
224
+ int yep_json_ws(const char* p, size_t len, size_t* i, int* saw_tab) {
225
+ while (*i < len) {
226
+ char c = p[*i];
227
+ if (c == ' ' || c == '\n' || c == '\r') {
228
+ (*i)++;
229
+ continue;
230
+ }
231
+ if (c == '\t') {
232
+ *saw_tab = 1; /* tabs: let the general kernel rule on them */
233
+ return 0;
234
+ }
235
+ return 1; /* token byte */
236
+ }
237
+ return -1; /* EOF */
238
+ }
239
+
240
+ /* Strict RFC 8259 number: -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?
241
+ *
242
+ * THE grammar walk, fused with conversion (TODO.restructure/26): one
243
+ * pass validates and converts — digits accumulate into the uint64
244
+ * magnitude with overflow promotion, so integer JSON numbers (the
245
+ * hot case) never get a second scan. Any '.'/'e' marks the value a
246
+ * float; the CONVERTER for an already-validated span stays the
247
+ * number-kernel SSOT (yep_num_f64) — grammar here, conversion there,
248
+ * exactly one of each. Out params may be NULL (pure validation). */
249
+ int yep_json_number_scan(const char* p, size_t len, size_t* i, int* is_float, int64_t* iv,
250
+ double* dv) {
251
+ size_t k = *i;
252
+ int neg = 0;
253
+ if (p[k] == '-') {
254
+ neg = 1;
255
+ k++;
256
+ }
257
+ if (k >= len || p[k] < '0' || p[k] > '9') {
258
+ return 0;
259
+ }
260
+ /* Short-integer fast path (the json-doc corpus's dominant shape —
261
+ * 1-3 digit ids/vals). Three digits max so the magnitude fits a
262
+ * u16 multiply chain with no overflow check; the float/exponent
263
+ * arms and the 8-digit SWAR fall through untouched. The trailing
264
+ * delimiter rule is the same contract the slow path enforces. */
265
+ {
266
+ size_t d0 = k;
267
+ if (p[k] != '0') {
268
+ unsigned mag32 = (unsigned)(p[k] - '0');
269
+ size_t n = 1;
270
+ while (n < 3 && d0 + n < len && p[d0 + n] >= '0' && p[d0 + n] <= '9') {
271
+ mag32 = mag32 * 10u + (unsigned)(p[d0 + n] - '0');
272
+ n++;
273
+ }
274
+ size_t end = d0 + n;
275
+ if (end >= len || (p[end] != '.' && p[end] != 'e' && p[end] != 'E' && p[end] != '0' &&
276
+ (p[end] < '1' || p[end] > '9'))) {
277
+ /* no more digits and not a float/exponent — settle */
278
+ if (end < len) {
279
+ char c = p[end];
280
+ if (c != ' ' && c != '\n' && c != '\r' && c != ',' && c != ']' && c != '}' &&
281
+ c != ':') {
282
+ return 0; /* "1x" is YAML, not JSON */
283
+ }
284
+ }
285
+ if (is_float != NULL) {
286
+ *is_float = 0;
287
+ }
288
+ if (iv != NULL) {
289
+ *iv = neg ? -(int64_t)mag32 : (int64_t)mag32;
290
+ }
291
+ *i = end;
292
+ return 1;
293
+ }
294
+ /* more digits or float text: fall through to the full walk */
295
+ } else {
296
+ /* lone zero (or 0. / 0e — fall through) */
297
+ size_t end = k + 1;
298
+ if (end >= len || (p[end] != '.' && p[end] != 'e' && p[end] != 'E' &&
299
+ (p[end] < '0' || p[end] > '9'))) {
300
+ if (end < len) {
301
+ char c = p[end];
302
+ if (c != ' ' && c != '\n' && c != '\r' && c != ',' && c != ']' && c != '}' &&
303
+ c != ':') {
304
+ return 0;
305
+ }
306
+ }
307
+ /* leading-zero reject is the full walk's job: "01" falls
308
+ * through (end points at '1') and the slow path rejects */
309
+ if (is_float != NULL) {
310
+ *is_float = 0;
311
+ }
312
+ if (iv != NULL) {
313
+ *iv = 0; /* -0 is still 0 as int64 */
314
+ }
315
+ *i = end;
316
+ return 1;
317
+ }
318
+ }
319
+ }
320
+ uint64_t mag = 0;
321
+ int overflow = 0;
322
+ if (p[k] == '0') {
323
+ k++;
324
+ } else {
325
+ /* simdjson's eight-digits-at-once SWAR (their
326
+ * parse_eight_digits_unrolled, verbatim constants): eight
327
+ * branch-free ops replace eight iterations of the mul/add
328
+ * loop. Fires only while eight digits AND the magnitude can
329
+ * absorb them exactly — the per-digit tail keeps the overflow
330
+ * promotion contract. Short numbers (the corpus's 1-6 digit
331
+ * ids) stay on the original loop. */
332
+ while (k + 8 <= len) {
333
+ uint64_t w;
334
+ memcpy(&w, p + k, 8);
335
+ uint64_t t = w ^ 0x3030303030303030ull;
336
+ uint64_t hi = t & 0xF0F0F0F0F0F0F0F0ull;
337
+ uint64_t lo =
338
+ (((t & 0x0F0F0F0F0F0F0F0Full) + 0x0606060606060606ull) & 0x1010101010101010ull);
339
+ if (hi != 0 || lo != 0) {
340
+ break; /* a non-digit byte inside the window */
341
+ }
342
+ if (mag > (UINT64_MAX - 99999999ull) / 100000000ull) {
343
+ break; /* 8 more digits may not fit: the slow tail promotes */
344
+ }
345
+ uint64_t v = w - 0x3030303030303030ull;
346
+ v = (v * 10) + (v >> 8);
347
+ const uint64_t mask = 0x000000FF000000FF;
348
+ const uint64_t mul1 = 0x000F424000000064;
349
+ const uint64_t mul2 = 0x0000271000000001;
350
+ v = (((v & mask) * mul1) + (((v >> 16) & mask) * mul2)) >> 32;
351
+ mag = mag * 100000000ull + v;
352
+ k += 8;
353
+ }
354
+ while (k < len && p[k] >= '0' && p[k] <= '9') {
355
+ unsigned d = (unsigned)(p[k] - '0');
356
+ if (mag > (UINT64_MAX - d) / 10) {
357
+ overflow = 1; /* keep scanning the grammar; promote */
358
+ } else {
359
+ mag = mag * 10 + d;
360
+ }
361
+ k++;
362
+ }
363
+ }
364
+ int flt = 0;
365
+ if (k < len && p[k] == '.') {
366
+ flt = 1;
367
+ k++;
368
+ if (k >= len || p[k] < '0' || p[k] > '9') {
369
+ return 0;
370
+ }
371
+ while (k < len && p[k] >= '0' && p[k] <= '9') {
372
+ k++;
373
+ }
374
+ }
375
+ if (k < len && (p[k] == 'e' || p[k] == 'E')) {
376
+ flt = 1;
377
+ k++;
378
+ if (k < len && (p[k] == '-' || p[k] == '+')) {
379
+ k++;
380
+ }
381
+ if (k >= len || p[k] < '0' || p[k] > '9') {
382
+ return 0;
383
+ }
384
+ while (k < len && p[k] >= '0' && p[k] <= '9') {
385
+ k++;
386
+ }
387
+ }
388
+ if (k < len) {
389
+ char c = p[k];
390
+ if (c != ' ' && c != '\n' && c != '\r' && c != ',' && c != ']' && c != '}' && c != ':') {
391
+ return 0; /* "1x" is YAML, not JSON */
392
+ }
393
+ }
394
+ /* promotion first, outputs once. is_float reports TEXT shape:
395
+ * 0 = integer, iv exact (INT64_MIN included)
396
+ * 1 = float text (dot/exponent), dv converted
397
+ * 2 = integer text beyond int64 — dv carries the approximate
398
+ * double; exact-Bignum hosts rebuild from the span
399
+ * (overflow tracks uint64 saturation; the int64 bound is the
400
+ * value test below) */
401
+ uint64_t limit = neg ? (uint64_t)INT64_MAX + 1 : (uint64_t)INT64_MAX;
402
+ if (flt) {
403
+ if (is_float != NULL) {
404
+ *is_float = 1;
405
+ }
406
+ if (dv != NULL) {
407
+ yep_num_f64(p + *i, (uint32_t)(k - *i), dv);
408
+ }
409
+ } else if (overflow || mag > limit) {
410
+ if (is_float != NULL) {
411
+ *is_float = 2;
412
+ }
413
+ if (dv != NULL) {
414
+ yep_num_f64(p + *i, (uint32_t)(k - *i), dv);
415
+ }
416
+ } else {
417
+ if (is_float != NULL) {
418
+ *is_float = 0;
419
+ }
420
+ if (iv != NULL) {
421
+ *iv = (neg && mag == (uint64_t)INT64_MAX + 1) ? INT64_MIN
422
+ : (neg ? -(int64_t)mag : (int64_t)mag);
423
+ }
424
+ }
425
+ *i = k;
426
+ return 1;
427
+ }
428
+
429
+ int yep_json_number(const char* p, size_t len, size_t* i) {
430
+ return yep_json_number_scan(p, len, i, NULL, NULL, NULL);
431
+ }
432
+
433
+ /* The lazy tape's number arm: validate the grammar and report the
434
+ * TEXT shape (0 int / 1 float) with NO magnitude accumulation — ints
435
+ * skip the SWAR entirely; conversion happens at materialize through
436
+ * yep_json_number_scan. Same accepts, same rejects, same advance as
437
+ * the full scan (the 2M tape-diff oracle pins the equivalence). */
438
+ int yep_json_number_shape(const char* p, size_t len, size_t* i, int* is_float) {
439
+ size_t k = *i;
440
+ if (p[k] == '-') {
441
+ k++;
442
+ }
443
+ if (k >= len || p[k] < '0' || p[k] > '9') {
444
+ return 0;
445
+ }
446
+ if (p[k] == '0') {
447
+ k++; /* a leading zero admits no digit after it ("01" dies at
448
+ the delimiter rule, exactly like the full scan) */
449
+ } else {
450
+ while (k < len && p[k] >= '0' && p[k] <= '9') {
451
+ k++;
452
+ }
453
+ }
454
+ int flt = 0;
455
+ if (k < len && p[k] == '.') {
456
+ flt = 1;
457
+ k++;
458
+ if (k >= len || p[k] < '0' || p[k] > '9') {
459
+ return 0;
460
+ }
461
+ while (k < len && p[k] >= '0' && p[k] <= '9') {
462
+ k++;
463
+ }
464
+ }
465
+ if (k < len && (p[k] == 'e' || p[k] == 'E')) {
466
+ flt = 1;
467
+ k++;
468
+ if (k < len && (p[k] == '-' || p[k] == '+')) {
469
+ k++;
470
+ }
471
+ if (k >= len || p[k] < '0' || p[k] > '9') {
472
+ return 0;
473
+ }
474
+ while (k < len && p[k] >= '0' && p[k] <= '9') {
475
+ k++;
476
+ }
477
+ }
478
+ if (k < len) {
479
+ char c = p[k];
480
+ if (c != ' ' && c != '\n' && c != '\r' && c != ',' && c != ']' && c != '}' && c != ':') {
481
+ return 0; /* "1x" is YAML, not JSON */
482
+ }
483
+ }
484
+ *is_float = flt;
485
+ *i = k;
486
+ return 1;
487
+ }
488
+
489
+ int yep_json_literal(const char* p, size_t len, size_t* i, const char* word) {
490
+ size_t w = 0;
491
+ while (word[w] != '\0') {
492
+ if (*i + w >= len || p[*i + w] != word[w]) {
493
+ return 0;
494
+ }
495
+ w++;
496
+ }
497
+ *i += w;
498
+ if (*i < len) {
499
+ char c = p[*i];
500
+ if (c != ' ' && c != '\n' && c != '\r' && c != ',' && c != ']' && c != '}' && c != ':') {
501
+ return 0; /* "truex" is a YAML plain, not JSON */
502
+ }
503
+ }
504
+ return 1;
505
+ }
506
+
507
+ /* Strict-JSON quoted scalar: no raw breaks, only JSON escapes. The
508
+ * qbc_find kernel (first '"', '\\', or C0) decides close/escape/
509
+ * reject — one SIMD pass, no second scan. On success *i sits just
510
+ * past the close, *close_out is the close quote index, *has_esc
511
+ * reports backslashes. */
512
+ /* The escape grammar, ONE authority for the string scans (the
513
+ * TODO.restructure/47 index descent was measured dead and reverted;
514
+ * re-exporting this is a one-liner if a consumer ever returns). */
515
+ static int yep_json_escape(const char* p, size_t len, size_t at, size_t* next) {
516
+ if (at + 1 >= len) {
517
+ return 0;
518
+ }
519
+ char e2 = p[at + 1];
520
+ if (e2 == 'u') {
521
+ for (size_t h = 2; h <= 5; h++) {
522
+ if (at + h >= len || !yep_ct_is((unsigned char)p[at + h], YEP_CT_HEXDIGIT)) {
523
+ return 0;
524
+ }
525
+ }
526
+ uint32_t cp = 0;
527
+ for (size_t h = 2; h <= 5; h++) {
528
+ cp = (cp << 4) | (uint32_t)hexval((unsigned char)p[at + h]);
529
+ }
530
+ if (cp >= 0xD800 && cp <= 0xDBFF) {
531
+ /* high surrogate must pair */
532
+ if (at + 12 >= len || p[at + 6] != '\\' || p[at + 7] != 'u') {
533
+ return 0;
534
+ }
535
+ uint32_t lo = 0;
536
+ for (size_t h = 8; h <= 11; h++) {
537
+ if (!yep_ct_is((unsigned char)p[at + h], YEP_CT_HEXDIGIT)) {
538
+ return 0;
539
+ }
540
+ lo = (lo << 4) | (uint32_t)hexval((unsigned char)p[at + h]);
541
+ }
542
+ if (lo < 0xDC00 || lo > 0xDFFF) {
543
+ return 0;
544
+ }
545
+ *next = at + 12;
546
+ return 1;
547
+ }
548
+ if (cp >= 0xDC00 && cp <= 0xDFFF) {
549
+ return 0; /* lone low surrogate */
550
+ }
551
+ *next = at + 6;
552
+ return 1;
553
+ }
554
+ if (e2 != '"' && e2 != '\\' && e2 != '/' && e2 != 'b' && e2 != 'f' && e2 != 'n' && e2 != 'r' &&
555
+ e2 != 't') {
556
+ return 0; /* YAML-only escape (\a, \x…): not JSON */
557
+ }
558
+ *next = at + 2;
559
+ return 1;
560
+ }
561
+
562
+ int yep_json_string(const char* p, size_t len, size_t* i, size_t* close_out, int* has_esc) {
563
+ const yep_text_kernels* k = yep_text_active();
564
+ size_t j = *i + 1;
565
+ int esc = 0;
566
+ /* Short-string fast path: the json-doc corpus's keys/values run
567
+ * 1-8 bytes; settling them scalar skips the kernel dispatch and
568
+ * the vector prologue. A backslash falls through to the vector
569
+ * loop (the escape grammar stays there, single-sourced). */
570
+ {
571
+ size_t lim = len - j;
572
+ if (lim > 8) {
573
+ lim = 8;
574
+ }
575
+ for (size_t w = 0; w < lim; w++) { /* w: not k — MSVC C4456 */
576
+ char c = p[j + w];
577
+ if (c == '"') {
578
+ *close_out = j + w;
579
+ *has_esc = 0;
580
+ *i = j + w + 1;
581
+ return 1;
582
+ }
583
+ if (c == '\\' || (unsigned char)c < 0x20) {
584
+ break; /* escape -> vector path; C0 -> it rejects */
585
+ }
586
+ }
587
+ }
588
+ for (;;) {
589
+ /* the SIMD string-stop kernel (TODO.restructure/46): first
590
+ * '"', '\\', or C0 — one vector pass, the milestone-55 LUT
591
+ * lessons absent by construction. The byte at the hit
592
+ * classifies: quote closes, backslash escapes, else reject. */
593
+ ptrdiff_t r = k->qbc_find(p + j, len - j);
594
+ if (r < 0) {
595
+ return 0; /* unterminated */
596
+ }
597
+ size_t at = j + (size_t)r;
598
+ char c = p[at];
599
+ if (c == '"') {
600
+ *close_out = at;
601
+ *has_esc = esc;
602
+ *i = at + 1;
603
+ return 1;
604
+ }
605
+ if (c != '\\') {
606
+ return 0; /* raw C0 control (incl. breaks) inside a string */
607
+ }
608
+ /* escape: the shared grammar authority (the index-driven
609
+ * descents walk the same rules — TODO.restructure/47) */
610
+ esc = 1;
611
+ if (!yep_json_escape(p, len, at, &j)) {
612
+ return 0;
613
+ }
614
+ }
615
+ }
616
+
617
+ /* ---- the span walker (TODO.restructure/53) ----
618
+ * Ported verbatim from the engine's pass-1 state machine: the grammar
619
+ * is UNCHANGED, it just lives where both consumers reach it. States
620
+ * mirror the old JX_* set. */
621
+
622
+ #define JW_SIMPLE_KEY_MAX 1024 /* YAML 1.2 simple-key limit (libyaml parity) */
623
+
624
+ void yep_json_walk_init(yep_json_walk* w, const char* p, size_t len, size_t open, int max_depth) {
625
+ w->p = p;
626
+ w->len = len;
627
+ w->max_depth = max_depth;
628
+ w->i = open + 1;
629
+ w->close = 0;
630
+ w->long_key = 0;
631
+ w->saw_tab = 0;
632
+ w->depth = 1;
633
+ w->kind[0] = (p[open] == '[') ? 0 : 1;
634
+ w->expect[0] = w->kind[0] ? JW_KEY_OR_CLOSE : JW_VALUE_OR_CLOSE;
635
+ w->strict = 0;
636
+ }
637
+
638
+ yep_jw_status yep_json_walk_next(yep_json_walk* w, yep_json_tok* t) {
639
+ const char* p = w->p;
640
+ size_t len = w->len;
641
+ for (;;) {
642
+ if (yep_json_ws(p, len, &w->i, &w->saw_tab) != 1) {
643
+ return YEP_JW_REJECT; /* EOF or a tab the general kernel judges */
644
+ }
645
+ size_t i = w->i;
646
+ char c = p[i];
647
+ if (c == ']' || c == '}') {
648
+ int want = (c == ']') ? 0 : 1;
649
+ if (w->kind[w->depth - 1] != want || (w->expect[w->depth - 1] != JW_VALUE_OR_CLOSE &&
650
+ w->expect[w->depth - 1] != JW_KEY_OR_CLOSE &&
651
+ w->expect[w->depth - 1] != JW_COMMA_OR_CLOSE)) {
652
+ return YEP_JW_REJECT; /* mismatched or premature close */
653
+ }
654
+ t->at = i;
655
+ t->end = i + 1;
656
+ t->cls = c;
657
+ t->has_escape = 0;
658
+ w->depth--;
659
+ w->i = i + 1;
660
+ if (w->depth == 0) {
661
+ w->close = i;
662
+ return YEP_JW_DONE;
663
+ }
664
+ w->expect[w->depth - 1] = JW_COMMA_OR_CLOSE;
665
+ return YEP_JW_OK;
666
+ }
667
+ switch (w->expect[w->depth - 1]) {
668
+ case JW_COLON:
669
+ if (c != ':') {
670
+ return YEP_JW_REJECT;
671
+ }
672
+ w->expect[w->depth - 1] = JW_VALUE;
673
+ w->i = i + 1;
674
+ continue;
675
+ case JW_COMMA_OR_CLOSE:
676
+ if (c != ',') {
677
+ return YEP_JW_REJECT;
678
+ }
679
+ w->expect[w->depth - 1] = w->kind[w->depth - 1] ? JW_KEY : JW_VALUE;
680
+ w->i = i + 1;
681
+ continue;
682
+ default:
683
+ break;
684
+ }
685
+ if (c == '{' || c == '[') {
686
+ if (w->depth >= YEP_JSON_WALK_DEPTH || w->depth >= w->max_depth) {
687
+ return YEP_JW_REJECT; /* the general kernel reports depth errors */
688
+ }
689
+ if (w->kind[w->depth - 1] == 1 &&
690
+ (w->expect[w->depth - 1] == JW_KEY_OR_CLOSE || w->expect[w->depth - 1] == JW_KEY) &&
691
+ w->strict) {
692
+ return YEP_JW_REJECT; /* strict JSON: a container is not a map key */
693
+ }
694
+ t->at = i;
695
+ t->end = i + 1;
696
+ t->cls = c;
697
+ t->has_escape = 0;
698
+ w->kind[w->depth] = (c == '[') ? 0 : 1;
699
+ w->expect[w->depth] = w->kind[w->depth] ? JW_KEY_OR_CLOSE : JW_VALUE_OR_CLOSE;
700
+ w->depth++;
701
+ w->i = i + 1;
702
+ return YEP_JW_OK;
703
+ }
704
+ t->at = i;
705
+ t->has_escape = 0;
706
+ if (c == '"') {
707
+ size_t close;
708
+ if (!yep_json_string(p, len, &w->i, &close, &t->has_escape)) {
709
+ return YEP_JW_REJECT;
710
+ }
711
+ t->cls = '"';
712
+ t->end = w->i;
713
+ if (w->kind[w->depth - 1] == 1 &&
714
+ (w->expect[w->depth - 1] == JW_KEY_OR_CLOSE || w->expect[w->depth - 1] == JW_KEY) &&
715
+ w->i - i > JW_SIMPLE_KEY_MAX) {
716
+ w->long_key = 1;
717
+ }
718
+ } else if (c == '-' || (c >= '0' && c <= '9')) {
719
+ /* the ONE number pass: validate, advance, convert, and
720
+ * shape — the previous shape rode a second text walk and
721
+ * value consumers (the tape) rescanned a third time */
722
+ if (!yep_json_number_scan(p, len, &w->i, &t->nshape, &t->ival, &t->dval)) {
723
+ return YEP_JW_REJECT;
724
+ }
725
+ t->cls = '#';
726
+ t->end = w->i;
727
+ t->is_float = (t->nshape == 1);
728
+ } else if (c == 't' || c == 'f' || c == 'n') {
729
+ const char* word = (c == 't') ? "true" : (c == 'f') ? "false" : "null";
730
+ if (!yep_json_literal(p, len, &w->i, word)) {
731
+ return YEP_JW_REJECT;
732
+ }
733
+ t->cls = 'a';
734
+ t->end = w->i;
735
+ } else {
736
+ return YEP_JW_REJECT; /* YAML plain scalar / comment / indicator */
737
+ }
738
+ if (w->kind[w->depth - 1] == 1 && w->expect[w->depth - 1] == JW_KEY_OR_CLOSE) {
739
+ if (w->strict && t->cls != '"') {
740
+ return YEP_JW_REJECT; /* strict JSON: keys are strings */
741
+ }
742
+ w->expect[w->depth - 1] = JW_COLON;
743
+ } else if (w->kind[w->depth - 1] == 1 && w->expect[w->depth - 1] == JW_KEY) {
744
+ if (t->cls != '"') {
745
+ return YEP_JW_REJECT; /* JSON map keys are strings only */
746
+ }
747
+ w->expect[w->depth - 1] = JW_COLON;
748
+ } else {
749
+ w->expect[w->depth - 1] = JW_COMMA_OR_CLOSE;
750
+ }
751
+ return YEP_JW_OK;
752
+ }
753
+ }
754
+
755
+ /* ---- stage 1: the structural indexer (the token-contract front) ---
756
+ * The scalar reference for the kernels table's json_stage1 slot: the
757
+ * token positions stage 2 dispatches on — operators, string OPEN
758
+ * quotes (escape-aware), and scalar-run starts — in position order.
759
+ * Same accepts/rejects as the ISA kernels (the differential suite
760
+ * pins it); no grammar validation, only the unterminated-string
761
+ * parity error. The mask-level identity set (escape scanner via the
762
+ * ODD_BITS borrow trick, prefix-xor string parity) is simdjson's. */
763
+
764
+ YEPTRIS_API int yep_json_stage1_scalar(const char* p, size_t len, uint32_t* idx, size_t* nidx) {
765
+ size_t n = 0;
766
+ uint64_t prev_in_string = 0, esc_carry = 0, follows_carry = 0;
767
+ for (size_t off = 0; off < len; off += 64) {
768
+ size_t cn = len - off < 64 ? len - off : 64;
769
+ uint64_t q = 0, bs = 0, op = 0, ws = 0;
770
+ for (size_t k = 0; k < cn; k++) {
771
+ unsigned char c = (unsigned char)p[off + k];
772
+ uint64_t bit = 1ull << k;
773
+ if (c == '"') {
774
+ q |= bit;
775
+ } else if (c == '\\') {
776
+ bs |= bit;
777
+ } else if (c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || c == ':') {
778
+ op |= bit;
779
+ } else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
780
+ ws |= bit;
781
+ }
782
+ }
783
+ uint64_t valid = cn == 64 ? ~0ull : ((1ull << cn) - 1ull);
784
+ n = yep_json_stage1_resolve(q, bs, op, ws, valid, &prev_in_string, &esc_carry,
785
+ &follows_carry, off, idx, n);
786
+ }
787
+ *nidx = n;
788
+ return prev_in_string ? 0 : 1;
789
+ }
790
+
791
+ /* The per-chunk classifier: the kernels table's json_chunk slot
792
+ * (scalar reference in common, NEON/AVX2 TUs on their ISAs). */