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.
- checksums.yaml +7 -0
- data/README.adoc +141 -0
- data/ext/build_windows_native.rb +41 -0
- data/ext/libyeptris/extconf.rb +57 -0
- data/ext/yeptris_native/cbor_ruby.c +193 -0
- data/ext/yeptris_native/extconf.rb +47 -0
- data/ext/yeptris_native/json_ruby.c +354 -0
- data/ext/yeptris_native/yeptris_native.c +497 -0
- data/lib/yeptris/cbor.rb +130 -0
- data/lib/yeptris/document.rb +255 -0
- data/lib/yeptris/ffi.rb +426 -0
- data/lib/yeptris/json/descriptor.rb +266 -0
- data/lib/yeptris/json.rb +394 -0
- data/lib/yeptris/materializer.rb +354 -0
- data/lib/yeptris/native-3.4.so +0 -0
- data/lib/yeptris/node.rb +453 -0
- data/lib/yeptris/psych/class_loader.rb +24 -0
- data/lib/yeptris/psych/coder_shim.rb +55 -0
- data/lib/yeptris/psych/drop_in.rb +63 -0
- data/lib/yeptris/psych/encodable.rb +14 -0
- data/lib/yeptris/psych/handler.rb +90 -0
- data/lib/yeptris/psych/parser.rb +105 -0
- data/lib/yeptris/psych/scalar_scanner.rb +149 -0
- data/lib/yeptris/psych/visitors.rb +545 -0
- data/lib/yeptris/psych.rb +622 -0
- data/lib/yeptris/schema.rb +253 -0
- data/lib/yeptris/valueml.rb +455 -0
- data/lib/yeptris/yaml/descriptor.rb +56 -0
- data/lib/yeptris/yaml.rb +422 -0
- data/lib/yeptris.rb +113 -0
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +254 -0
- data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
- data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
- data/vendor/libyeptris/src/CMakeLists.txt +167 -0
- data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
- data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
- data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
- data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
- data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
- data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
- data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
- data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
- data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
- data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
- data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
- data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
- data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
- data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
- data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
- data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
- data/vendor/libyeptris/src/include/yeptris.h +23 -0
- data/vendor/libyeptris/src/yeptris/build.c +376 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
- data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
- data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
- data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
- data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
- data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
- data/vendor/libyeptris/src/yeptris/doc.h +56 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
- data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
- data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
- data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
- data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
- data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
- data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
- data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
- data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
- data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
- data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
- data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
- data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
- data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
- data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
- data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
- data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
- data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
- data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
- data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
- data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
- data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
- data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
- data/vendor/libyeptris/src/yeptris/parse.c +737 -0
- data/vendor/libyeptris/src/yeptris/plan.c +824 -0
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
- data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
- data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
- data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
- data/vendor/libyeptris/src/yeptris/schema.c +308 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
- data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
- data/vendor/libyeptris/src/yeptris/version.c +7 -0
- data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
- data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
- data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
- metadata +199 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
|
|
2
|
+
/* json_ruby.c — fused RFC 8259 → Ruby VALUE (TODO.restructure/22). */
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
/* rb_hash_new_capa is Ruby 3.2+; older Rubies grow dynamically (the
|
|
5
|
+
* FFI fallback stays correct on every minor either way). */
|
|
6
|
+
#if RUBY_API_VERSION_MAJOR > 3 || (RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 2)
|
|
7
|
+
#define HASH_NEW_CAPA(n) rb_hash_new_capa(n)
|
|
8
|
+
#else
|
|
9
|
+
#define HASH_NEW_CAPA(n) rb_hash_new()
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
#include <ruby/encoding.h>
|
|
13
|
+
#include <ruby/intern.h>
|
|
14
|
+
#include <stdlib.h>
|
|
15
|
+
#include <string.h>
|
|
16
|
+
#include "parse/scalars.h"
|
|
17
|
+
#include <yeptris/visit.h>
|
|
18
|
+
#include "scan/json.h"
|
|
19
|
+
|
|
20
|
+
#define YEP_JR_MAX 1000
|
|
21
|
+
#define YEP_KC 1024
|
|
22
|
+
|
|
23
|
+
typedef struct {
|
|
24
|
+
uint64_t h;
|
|
25
|
+
uint32_t len;
|
|
26
|
+
VALUE v;
|
|
27
|
+
} kcent;
|
|
28
|
+
|
|
29
|
+
typedef struct {
|
|
30
|
+
const char* p;
|
|
31
|
+
size_t len;
|
|
32
|
+
size_t i;
|
|
33
|
+
char* scratch;
|
|
34
|
+
size_t scratch_cap;
|
|
35
|
+
int depth;
|
|
36
|
+
int err;
|
|
37
|
+
rb_encoding* enc;
|
|
38
|
+
int strict_dup; /* json gem >= 3: duplicate keys raise (issue #37) */
|
|
39
|
+
VALUE dup_key;
|
|
40
|
+
kcent kc[YEP_KC];
|
|
41
|
+
} jr;
|
|
42
|
+
|
|
43
|
+
static VALUE jr_value(jr* j);
|
|
44
|
+
static VALUE jr_object_body(jr* j, VALUE h_pre);
|
|
45
|
+
|
|
46
|
+
static void jr_ws(jr* j) {
|
|
47
|
+
while (j->i < j->len) {
|
|
48
|
+
unsigned char c = (unsigned char)j->p[j->i];
|
|
49
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') j->i++;
|
|
50
|
+
else break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Token-cache knob (TODO.restructure/37): off = fresh strings
|
|
55
|
+
* everywhere (JSON.parse's shape: every key pays its own aset
|
|
56
|
+
* hashing); on = interned keys/tokens (shared frozen VALUEs
|
|
57
|
+
* memoize their hash). The CI referee A/Bs the net sign per arch. */
|
|
58
|
+
enum { YEP_CACHE_ON = 0, YEP_CACHE_OFF = 1 };
|
|
59
|
+
static int yep_cache_mode = YEP_CACHE_ON;
|
|
60
|
+
|
|
61
|
+
/* Allocation-shape knob (TODO.restructure/39): pre = capa(8) up
|
|
62
|
+
* front (forces heap buffers even for tiny arrays); natural =
|
|
63
|
+
* rb_ary_new() so 3-element arrays stay EMBEDDED in the RVALUE, and
|
|
64
|
+
* bulk-path hashes get exact capacity from the counted pairs. */
|
|
65
|
+
enum { YEP_SHAPE_PRE = 0, YEP_SHAPE_NATURAL = 1 };
|
|
66
|
+
static int yep_shape_mode = YEP_SHAPE_PRE;
|
|
67
|
+
|
|
68
|
+
static uint64_t jr_hash(const char* sp, long sl) {
|
|
69
|
+
/* 8-byte-prefix key (the leptris nametab trick): one safe load
|
|
70
|
+
* of min(8, len) bytes + a multiply mix. Replaced byte-wise FNV
|
|
71
|
+
* (~15-20ns per token on the cached path, ~15k tokens per
|
|
72
|
+
* reference-corpus parse - the x86 materialization cost the
|
|
73
|
+
* decomposition isolated, TODO.restructure/37/38). */
|
|
74
|
+
uint64_t k = 0;
|
|
75
|
+
uint64_t take = (uint64_t)sl < 8 ? (uint64_t)sl : 8;
|
|
76
|
+
memcpy(&k, sp, (size_t)take);
|
|
77
|
+
k ^= (uint64_t)sl * 0x9E3779B97F4A7C15ull;
|
|
78
|
+
k *= 0xC2B2AE3D27D4EB4Full;
|
|
79
|
+
k ^= k >> 29;
|
|
80
|
+
return k;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static VALUE jr_cached(jr* j, const char* sp, long sl) {
|
|
84
|
+
uint64_t h = jr_hash(sp, sl);
|
|
85
|
+
uint32_t slot = (uint32_t)(h & (YEP_KC - 1));
|
|
86
|
+
kcent* e = &j->kc[slot];
|
|
87
|
+
if (e->v != 0 && e->h == h && e->len == (uint32_t)sl &&
|
|
88
|
+
(long)RSTRING_LEN(e->v) == sl &&
|
|
89
|
+
memcmp(RSTRING_PTR(e->v), sp, (size_t)sl) == 0) {
|
|
90
|
+
return e->v;
|
|
91
|
+
}
|
|
92
|
+
VALUE s = rb_enc_str_new(sp, sl, j->enc);
|
|
93
|
+
rb_str_freeze(s);
|
|
94
|
+
e->h = h;
|
|
95
|
+
e->len = (uint32_t)sl;
|
|
96
|
+
e->v = s;
|
|
97
|
+
return s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static VALUE jr_str(jr* j, int as_key) {
|
|
101
|
+
size_t start = j->i, close = 0;
|
|
102
|
+
int has_esc = 0;
|
|
103
|
+
if (!yep_json_string(j->p, j->len, &j->i, &close, &has_esc)) { j->err = -2; return Qnil; }
|
|
104
|
+
const char* sp; long sl;
|
|
105
|
+
if (has_esc) {
|
|
106
|
+
uint32_t span = (uint32_t)(close - start - 1);
|
|
107
|
+
if (span + 1 > j->scratch_cap) {
|
|
108
|
+
size_t cap = j->scratch_cap ? j->scratch_cap : 64;
|
|
109
|
+
while (cap < span + 1) cap *= 2;
|
|
110
|
+
char* ns = realloc(j->scratch, cap);
|
|
111
|
+
if (!ns) { j->err = -1; return Qnil; }
|
|
112
|
+
j->scratch = ns; j->scratch_cap = cap;
|
|
113
|
+
}
|
|
114
|
+
sl = (long)yep_finish_double_into(j->p, (uint32_t)(start + 1), (uint32_t)close, j->scratch, span);
|
|
115
|
+
sp = j->scratch;
|
|
116
|
+
} else {
|
|
117
|
+
sp = j->p + start + 1;
|
|
118
|
+
sl = (long)(close - start - 1);
|
|
119
|
+
}
|
|
120
|
+
if (yep_cache_mode == YEP_CACHE_ON && (as_key || sl <= 24)) return jr_cached(j, sp, sl);
|
|
121
|
+
return rb_enc_str_new(sp, sl, j->enc);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static VALUE jr_num(jr* j) {
|
|
125
|
+
size_t start = j->i;
|
|
126
|
+
int shape = 0;
|
|
127
|
+
int64_t iv = 0;
|
|
128
|
+
double dv = 0.0;
|
|
129
|
+
/* the fused kernel (scan/json.h): ONE grammar walk, values out */
|
|
130
|
+
if (!yep_json_number_scan(j->p, j->len, &j->i, &shape, &iv, &dv)) {
|
|
131
|
+
j->err = -2;
|
|
132
|
+
return Qnil;
|
|
133
|
+
}
|
|
134
|
+
if (shape == 0) {
|
|
135
|
+
return LL2NUM(iv);
|
|
136
|
+
}
|
|
137
|
+
if (shape == 1) {
|
|
138
|
+
return DBL2NUM(dv);
|
|
139
|
+
}
|
|
140
|
+
/* integer text beyond int64: exact Bignum from the validated
|
|
141
|
+
* span (JSON.parse's behavior). Absurd lengths degrade to the
|
|
142
|
+
* approximate double. */
|
|
143
|
+
size_t n = j->i - start;
|
|
144
|
+
if (n < 512) {
|
|
145
|
+
char buf[512];
|
|
146
|
+
memcpy(buf, j->p + start, n);
|
|
147
|
+
buf[n] = '\0';
|
|
148
|
+
return rb_cstr_to_inum(buf, 10, TRUE);
|
|
149
|
+
}
|
|
150
|
+
return DBL2NUM(dv);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Insert strategy (TODO.restructure/34): bulk lands all pairs in one
|
|
154
|
+
* rb_hash_bulk_insert (skips per-pair dispatch) but costs +1.4k
|
|
155
|
+
* intermediate allocations on the reference corpus — the CI referee
|
|
156
|
+
* rules per platform. aset = the per-pair rb_hash_aset loop. */
|
|
157
|
+
enum { YEP_INS_BULK = 0, YEP_INS_ASET = 1 };
|
|
158
|
+
static int yep_ins_mode = YEP_INS_BULK;
|
|
159
|
+
|
|
160
|
+
static VALUE jr_object(jr* j) {
|
|
161
|
+
j->i++; j->depth++;
|
|
162
|
+
if (yep_shape_mode == YEP_SHAPE_PRE) {
|
|
163
|
+
VALUE h = HASH_NEW_CAPA(8);
|
|
164
|
+
jr_ws(j);
|
|
165
|
+
if (j->i < j->len && j->p[j->i] == '}') { j->i++; j->depth--; return h; }
|
|
166
|
+
return jr_object_body(j, h);
|
|
167
|
+
}
|
|
168
|
+
jr_ws(j);
|
|
169
|
+
if (j->i < j->len && j->p[j->i] == '}') { j->i++; j->depth--; return rb_hash_new(); }
|
|
170
|
+
return jr_object_body(j, Qundef); /* created post-loop with exact capa */
|
|
171
|
+
|
|
172
|
+
/* NOTREACHED */
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static VALUE jr_object_body(jr* j, VALUE h_pre) {
|
|
176
|
+
VALUE pairs[64];
|
|
177
|
+
VALUE* pv = pairs;
|
|
178
|
+
size_t pcap = 64, pn = 0, heap_cap = 0;
|
|
179
|
+
for (;;) {
|
|
180
|
+
jr_ws(j);
|
|
181
|
+
if (j->i >= j->len || j->p[j->i] != '"') { j->err = -2; goto out; }
|
|
182
|
+
VALUE key = jr_str(j, 1);
|
|
183
|
+
if (j->err) goto out;
|
|
184
|
+
jr_ws(j);
|
|
185
|
+
if (j->i >= j->len || j->p[j->i] != ':') { j->err = -2; goto out; }
|
|
186
|
+
j->i++;
|
|
187
|
+
VALUE val = jr_value(j);
|
|
188
|
+
if (j->err) goto out;
|
|
189
|
+
if (yep_ins_mode == YEP_INS_ASET || j->strict_dup) {
|
|
190
|
+
VALUE h = h_pre == Qundef ? (h_pre = HASH_NEW_CAPA(8)) : h_pre;
|
|
191
|
+
if (j->strict_dup && !NIL_P(rb_hash_aref(h, key))) {
|
|
192
|
+
j->err = -3;
|
|
193
|
+
j->dup_key = key;
|
|
194
|
+
goto out;
|
|
195
|
+
}
|
|
196
|
+
rb_hash_aset(h, key, val);
|
|
197
|
+
} else {
|
|
198
|
+
if (pn + 2 > pcap) {
|
|
199
|
+
size_t ncap = pcap * 2;
|
|
200
|
+
VALUE* nv = malloc(ncap * sizeof(VALUE));
|
|
201
|
+
if (!nv) { j->err = -1; goto out; }
|
|
202
|
+
memcpy(nv, pv, pn * sizeof(VALUE));
|
|
203
|
+
if (pv != pairs) { free(pv); heap_cap = 1; }
|
|
204
|
+
pv = nv; pcap = ncap;
|
|
205
|
+
}
|
|
206
|
+
pv[pn++] = key;
|
|
207
|
+
pv[pn++] = val;
|
|
208
|
+
}
|
|
209
|
+
jr_ws(j);
|
|
210
|
+
if (j->i >= j->len) { j->err = -2; goto out; }
|
|
211
|
+
if (j->p[j->i] == ',') { j->i++; continue; }
|
|
212
|
+
if (j->p[j->i] == '}') { j->i++; break; }
|
|
213
|
+
j->err = -2; goto out;
|
|
214
|
+
}
|
|
215
|
+
if (yep_ins_mode == YEP_INS_BULK) {
|
|
216
|
+
VALUE h = (h_pre == Qundef) ? HASH_NEW_CAPA((long)(pn / 2)) : h_pre;
|
|
217
|
+
rb_hash_bulk_insert((long)pn, (const VALUE*)pv, h);
|
|
218
|
+
if (pv != pairs) { free(pv); }
|
|
219
|
+
j->depth--;
|
|
220
|
+
return h;
|
|
221
|
+
}
|
|
222
|
+
if (h_pre == Qundef) { h_pre = HASH_NEW_CAPA(8); }
|
|
223
|
+
out:
|
|
224
|
+
if (pv != pairs) { free(pv); (void)heap_cap; }
|
|
225
|
+
if (j->err) return Qnil;
|
|
226
|
+
j->depth--;
|
|
227
|
+
return h_pre;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static VALUE jr_array(jr* j) {
|
|
231
|
+
j->i++; j->depth++;
|
|
232
|
+
VALUE a = (yep_shape_mode == YEP_SHAPE_NATURAL) ? rb_ary_new() : rb_ary_new_capa(8);
|
|
233
|
+
jr_ws(j);
|
|
234
|
+
if (j->i < j->len && j->p[j->i] == ']') { j->i++; j->depth--; return a; }
|
|
235
|
+
for (;;) {
|
|
236
|
+
VALUE v = jr_value(j);
|
|
237
|
+
if (j->err) return Qnil;
|
|
238
|
+
rb_ary_push(a, v);
|
|
239
|
+
jr_ws(j);
|
|
240
|
+
if (j->i >= j->len) { j->err = -2; return Qnil; }
|
|
241
|
+
if (j->p[j->i] == ',') { j->i++; continue; }
|
|
242
|
+
if (j->p[j->i] == ']') { j->i++; j->depth--; return a; }
|
|
243
|
+
j->err = -2; return Qnil;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
static VALUE jr_value(jr* j) {
|
|
248
|
+
if (j->depth >= YEP_JR_MAX) { j->err = -2; return Qnil; }
|
|
249
|
+
jr_ws(j);
|
|
250
|
+
if (j->i >= j->len) { j->err = -2; return Qnil; }
|
|
251
|
+
char c = j->p[j->i];
|
|
252
|
+
if (c == '{') return jr_object(j);
|
|
253
|
+
if (c == '[') return jr_array(j);
|
|
254
|
+
if (c == '"') return jr_str(j, 0);
|
|
255
|
+
if (c == 't') {
|
|
256
|
+
if (!yep_json_literal(j->p, j->len, &j->i, "true")) { j->err = -2; return Qnil; }
|
|
257
|
+
return Qtrue;
|
|
258
|
+
}
|
|
259
|
+
if (c == 'f') {
|
|
260
|
+
if (!yep_json_literal(j->p, j->len, &j->i, "false")) { j->err = -2; return Qnil; }
|
|
261
|
+
return Qfalse;
|
|
262
|
+
}
|
|
263
|
+
if (c == 'n') {
|
|
264
|
+
if (!yep_json_literal(j->p, j->len, &j->i, "null")) { j->err = -2; return Qnil; }
|
|
265
|
+
return Qnil;
|
|
266
|
+
}
|
|
267
|
+
if (c == '-' || (c >= '0' && c <= '9')) return jr_num(j);
|
|
268
|
+
j->err = -2; return Qnil;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* GC strategy for the parse window (TODO.restructure/34): JSON.parse
|
|
272
|
+
* pays minor GCs mid-parse and recycles slots continuously; a blanket
|
|
273
|
+
* disable defers every collection — fresh pages each iteration, which
|
|
274
|
+
* is cheap idle and expensive exactly under memory contention (the
|
|
275
|
+
* loaded-box regression). The strategy is a runtime choice so the CI
|
|
276
|
+
* referee can A/B without rebuilds:
|
|
277
|
+
* disable (default) — pause GC for the window
|
|
278
|
+
* none — never pause
|
|
279
|
+
* start — pause, then one gc_start before returning
|
|
280
|
+
* (pay the minor GC in-window, like JSON.parse)
|
|
281
|
+
*/
|
|
282
|
+
enum { YEP_GC_DISABLE = 0, YEP_GC_NONE = 1, YEP_GC_START = 2 };
|
|
283
|
+
/* Default per arch (TODO.restructure/35, two CI rounds of evidence):
|
|
284
|
+
* - aarch64/darwin: NONE — +0 heap pages, the stdlib's own GC cadence
|
|
285
|
+
* (0.745x mean, h2h 91% on mac runners; disable was 0.899x/48%).
|
|
286
|
+
* - x86_64: DISABLE — fresh CI VMs have free pages and cheaper
|
|
287
|
+
* page faults than minor GCs (0.911-0.922x vs none's 1.06-1.20x).
|
|
288
|
+
* The mechanism cuts both ways under load: disable's +478 pages/50
|
|
289
|
+
* iters is exactly what a LOADED x86 box punishes — set
|
|
290
|
+
* YEPTRIS_NATIVE_GC=none there (documented in README). */
|
|
291
|
+
#if defined(__aarch64__) || defined(__arm__) || defined(__ARMEL__) || defined(_M_ARM64)
|
|
292
|
+
#define YEP_GC_DEFAULT YEP_GC_NONE
|
|
293
|
+
#else
|
|
294
|
+
#define YEP_GC_DEFAULT YEP_GC_DISABLE
|
|
295
|
+
#endif
|
|
296
|
+
static int yep_gc_mode = YEP_GC_DEFAULT;
|
|
297
|
+
|
|
298
|
+
int yep_rb_gc_mode(void) { return yep_gc_mode; }
|
|
299
|
+
int yep_rb_ins_mode(void) { return yep_ins_mode; }
|
|
300
|
+
int yep_rb_cache_mode(void) { return yep_cache_mode; }
|
|
301
|
+
int yep_rb_shape_mode(void) { return yep_shape_mode; }
|
|
302
|
+
void yep_rb_set_shape_mode(int mode) {
|
|
303
|
+
if (mode == YEP_SHAPE_PRE || mode == YEP_SHAPE_NATURAL) yep_shape_mode = mode;
|
|
304
|
+
}
|
|
305
|
+
void yep_rb_set_cache_mode(int mode) {
|
|
306
|
+
if (mode == YEP_CACHE_ON || mode == YEP_CACHE_OFF) yep_cache_mode = mode;
|
|
307
|
+
}
|
|
308
|
+
void yep_rb_set_ins_mode(int mode) {
|
|
309
|
+
if (mode == YEP_INS_BULK || mode == YEP_INS_ASET) yep_ins_mode = mode;
|
|
310
|
+
}
|
|
311
|
+
void yep_rb_set_gc_mode(int mode) {
|
|
312
|
+
if (mode >= YEP_GC_DISABLE && mode <= YEP_GC_START) {
|
|
313
|
+
yep_gc_mode = mode;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Pure grammar walk, no materialization (TODO.restructure/37): the
|
|
318
|
+
* same scan kernels through the null vtable. Decomposes scan vs
|
|
319
|
+
* materialize cost. Returns seconds for n iterations. */
|
|
320
|
+
#include <time.h>
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
double yep_rb_scan_time(const char* p, size_t len, int n) {
|
|
324
|
+
static const YeptrisVisitVTable none = {0};
|
|
325
|
+
struct timespec t0, t1;
|
|
326
|
+
clock_gettime(CLOCK_MONOTONIC, &t0);
|
|
327
|
+
for (int i = 0; i < n; i++) {
|
|
328
|
+
(void)yeptris_visit_json(p, len, &none, NULL);
|
|
329
|
+
}
|
|
330
|
+
clock_gettime(CLOCK_MONOTONIC, &t1);
|
|
331
|
+
return (double)(t1.tv_sec - t0.tv_sec) + (double)(t1.tv_nsec - t0.tv_nsec) / 1e9;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
VALUE yep_rb_parse_json(const char* p, size_t len, int strict_dup) {
|
|
335
|
+
jr j;
|
|
336
|
+
memset(&j, 0, sizeof(j));
|
|
337
|
+
j.p = p; j.len = len; j.enc = rb_utf8_encoding();
|
|
338
|
+
j.strict_dup = strict_dup;
|
|
339
|
+
VALUE already = Qtrue;
|
|
340
|
+
if (yep_gc_mode != YEP_GC_NONE) already = rb_gc_disable();
|
|
341
|
+
VALUE v = jr_value(&j);
|
|
342
|
+
jr_ws(&j);
|
|
343
|
+
if (j.i != len && j.err == 0) j.err = -2;
|
|
344
|
+
free(j.scratch);
|
|
345
|
+
if (already == Qfalse) rb_gc_enable();
|
|
346
|
+
if (yep_gc_mode == YEP_GC_START && j.err == 0) rb_gc_start();
|
|
347
|
+
if (j.err == -1) rb_raise(rb_eNoMemError, "yeptris native json");
|
|
348
|
+
if (j.err == -3) {
|
|
349
|
+
rb_raise(rb_path2class("Yeptris::ParseError"), "duplicate key \"%s\" in JSON object",
|
|
350
|
+
RSTRING_PTR(j.dup_key));
|
|
351
|
+
}
|
|
352
|
+
if (j.err != 0) rb_raise(rb_path2class("Yeptris::ParseError"), "native json parse failed");
|
|
353
|
+
return v;
|
|
354
|
+
}
|