yeptris 0.6.19.1 → 0.6.20.2
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 +4 -4
- data/ext/build_windows_native.rb +31 -12
- data/lib/yeptris/psych.rb +1 -1
- data/lib/yeptris.rb +30 -14
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/CMakeLists.txt +1 -0
- data/vendor/libyeptris/src/yeptris/build.c +1 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +3 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.h +47 -13
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +86 -8
- data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +82 -8
- data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +10 -6
- data/vendor/libyeptris/src/yeptris/doc.h +19 -9
- data/vendor/libyeptris/src/yeptris/dom/ytape.c +642 -0
- data/vendor/libyeptris/src/yeptris/dom/ytape.h +77 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +7 -2
- data/vendor/libyeptris/src/yeptris/marshal.c +7 -2
- data/vendor/libyeptris/src/yeptris/parse.c +193 -109
- data/vendor/libyeptris/src/yeptris/scan/json.c +43 -5
- data/vendor/libyeptris/src/yeptris/scan/json.h +9 -2
- data/vendor/libyeptris/src/yeptris/tape.c +658 -167
- data/vendor/libyeptris/src/yeptris/tape_in.h +18 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d91dc30d6dd414d0b22ce84b0e573850efa6a86e7d6fcef4425e2d4afc2fca2
|
|
4
|
+
data.tar.gz: 9efe2971ff5b4159b077fba5330a12c0c2ac91bb6849c36c86a83ddeba243a86
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b40b768c6103cc5715059d7f93350c8c2f9298f86297e2133cf339ec269529237a964bcab0ef5a6ea998a609b5370ac640ba6d195b741bd9382037e2a3114a83
|
|
7
|
+
data.tar.gz: ecfe46a3b63e70b38c11541f6926b7e59a622e8450401b0ff271a362d854317f4bc40647b9ffa2bda3233db3b1e3f09470385f8768f27b794b9655a387123ed4
|
data/ext/build_windows_native.rb
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Builds the native materializer
|
|
4
|
-
#
|
|
5
|
-
# (lib/yeptris
|
|
3
|
+
# Builds the native materializer for the CURRENT Ruby and installs it
|
|
4
|
+
# under the per-minor DIRECTORY layout
|
|
5
|
+
# (lib/yeptris/<major.minor>/native.<so|bundle|dll>).
|
|
6
6
|
#
|
|
7
7
|
# The leptris-ruby Windows lesson (#207/#227): a PE DLL cannot
|
|
8
8
|
# resolve Ruby imports lazily — it must bind the build Ruby's
|
|
9
|
-
# x64-ucrt-rubyNNN.dll. One
|
|
10
|
-
# in the
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
9
|
+
# x64-ucrt-rubyNNN.dll. One bundle per supported minor therefore ships
|
|
10
|
+
# in the platform gems, and the loader in lib/yeptris.rb picks by
|
|
11
|
+
# RUBY_VERSION at require. Ruby 3.3 has no arm64 build: that cell
|
|
12
|
+
# ships no artifact and falls back loudly to the FFI ladder.
|
|
13
|
+
#
|
|
14
|
+
# The MINOR goes in the directory and the file name stays "native":
|
|
15
|
+
# ruby derives the init symbol from the feature basename, so the old
|
|
16
|
+
# native-<minor>.so name made require ask for the untypeable
|
|
17
|
+
# "Init_native-3" and no shipped bundle could ever load (#157).
|
|
18
|
+
#
|
|
19
|
+
# Unix bundles link the same shape build-platform-gem.sh uses — no
|
|
20
|
+
# libruby DT_NEEDED (its absolute build-runner path dangles on user
|
|
21
|
+
# machines; Ruby API symbols resolve from the host process), and
|
|
22
|
+
# darwin links with -undefined dynamic_lookup.
|
|
14
23
|
#
|
|
15
24
|
# Standalone by design: the release workflow runs this under each
|
|
16
25
|
# Ruby minor (3.3/3.4/4.0) with no bundler context.
|
|
@@ -21,21 +30,31 @@ require "fileutils"
|
|
|
21
30
|
root = File.expand_path("..", __dir__)
|
|
22
31
|
ext_dir = File.join(root, "ext", "yeptris_native")
|
|
23
32
|
minor = RUBY_VERSION[/\A\d+\.\d+/]
|
|
33
|
+
windows = RbConfig::CONFIG["host_os"] =~ /mingw|mswin/
|
|
24
34
|
|
|
25
35
|
Dir.chdir(ext_dir) do
|
|
26
36
|
system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
|
|
27
37
|
# mkmf's link binds the CURRENT Ruby's runtime DLL — exactly
|
|
28
|
-
# what the
|
|
29
|
-
|
|
38
|
+
# what the per-minor scheme is for.
|
|
39
|
+
if windows
|
|
40
|
+
success = system("make")
|
|
41
|
+
else
|
|
42
|
+
darwin = RbConfig::CONFIG["host_os"] =~ /darwin/
|
|
43
|
+
dld = darwin ? "-dynamic -bundle -undefined dynamic_lookup" : ""
|
|
44
|
+
success = system("make", "LIBRUBYARG_SHARED=", "LIBRUBYARG_STATIC=",
|
|
45
|
+
"DLDFLAGS=#{dld}")
|
|
46
|
+
end
|
|
30
47
|
abort "make failed under #{RUBY_VERSION}" unless success
|
|
31
|
-
so = Dir.glob("native.{so,dll}").first
|
|
48
|
+
so = Dir.glob("native.{so,bundle,dll}").first
|
|
32
49
|
abort "native bundle not produced under #{RUBY_VERSION}" unless so
|
|
33
|
-
|
|
50
|
+
dest_dir = File.join(root, "lib", "yeptris", minor)
|
|
51
|
+
dest = File.join(dest_dir, File.basename(so))
|
|
34
52
|
# The artifact must reference only this minor's Ruby DLL.
|
|
35
53
|
imported = `strings #{so} 2>/dev/null`[/[a-z0-9-]*ruby\d{3,}\.dll/i]
|
|
36
54
|
if imported && !imported.include?("ruby#{minor.delete('.')}")
|
|
37
55
|
abort "#{so} imports #{imported} but was built under #{RUBY_VERSION} — refuse to mis-name it"
|
|
38
56
|
end
|
|
57
|
+
FileUtils.mkdir_p(dest_dir)
|
|
39
58
|
FileUtils.cp(so, dest)
|
|
40
59
|
puts "Installed native materializer for Ruby #{minor} -> #{dest} (#{imported || 'no ruby dll string found'})"
|
|
41
60
|
end
|
data/lib/yeptris/psych.rb
CHANGED
|
@@ -619,7 +619,7 @@ end
|
|
|
619
619
|
# rebind ::Psych. It does not anymore, and re-binding it here would
|
|
620
620
|
# regress #69 (any stdlib psych loaded afterwards would explode with
|
|
621
621
|
# a superclass mismatch), so the old path warns instead of acting.
|
|
622
|
-
if defined?(::Psych) && !::Psych.equal?(Yeptris::Psych)
|
|
622
|
+
if defined?(::Psych) && !::Psych.equal?(Yeptris::Psych) && Yeptris.debug?
|
|
623
623
|
warn "yeptris: \"yeptris/psych\" defines the namespace only — it no " \
|
|
624
624
|
"longer rebinds ::Psych. Require \"yeptris/psych/drop_in\" for the " \
|
|
625
625
|
"drop-in rebind, or call Yeptris::Psych explicitly."
|
data/lib/yeptris.rb
CHANGED
|
@@ -3,7 +3,17 @@
|
|
|
3
3
|
module Yeptris
|
|
4
4
|
# The gem's version lives in the parent namespace's file — the last
|
|
5
5
|
# internal require (yeptris/version) retired with it.
|
|
6
|
-
VERSION = "0.6.
|
|
6
|
+
VERSION = "0.6.20.2".freeze
|
|
7
|
+
|
|
8
|
+
# Informational notices are OPT-IN (yeptris-ruby#217): consumers
|
|
9
|
+
# embed yeptris inside CLIs that assert clean stderr, and a
|
|
10
|
+
# successful fallback is not a warning. YEPTRIS_DEBUG=1 turns the
|
|
11
|
+
# notices on.
|
|
12
|
+
def self.debug?
|
|
13
|
+
return @debug unless @debug.nil?
|
|
14
|
+
v = ENV["YEPTRIS_DEBUG"]
|
|
15
|
+
@debug = !v.nil? && !v.empty? && v != "0"
|
|
16
|
+
end
|
|
7
17
|
# The error hierarchy lives in THIS file (the parent namespace's
|
|
8
18
|
# own file): nested constants do not trigger a parent-constant
|
|
9
19
|
# autoload, and the law forbids internal requires — defining the
|
|
@@ -91,23 +101,29 @@ end
|
|
|
91
101
|
# Optional C-API materializer (TODO.restructure/22): fused visit →
|
|
92
102
|
# Ruby objects via the Ruby C API. Feature-detected — LoadError leaves
|
|
93
103
|
# the FFI ladder (Marshal → columns → records) as the sole path.
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
104
|
+
# One bundle per Ruby minor (the leptris-ruby #207/#227 lesson: a PE
|
|
105
|
+
# DLL must bind its build Ruby's runtime; a unix bundle is also
|
|
106
|
+
# minor-locked by the internal API it compiles against). The MINOR
|
|
107
|
+
# lives in the DIRECTORY (lib/yeptris/<minor>/native.<ext>) and the
|
|
108
|
+
# FILE name stays unversioned: ruby derives the init symbol from the
|
|
109
|
+
# feature basename, so native-3.4.so asked for the untypeable
|
|
110
|
+
# "Init_native-3" and the versioned-file scheme could never load
|
|
111
|
+
# (#157 — every shipped bundle was dead code). Dev checkouts keep
|
|
112
|
+
# the plain native.<ext> name (mkmf layout, built for the running
|
|
113
|
+
# Ruby). A missing cell falls back LOUDLY to the ladder.
|
|
98
114
|
begin
|
|
99
|
-
# per-minor FIRST
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
-
# as the dev-checkout fallback (built for the running Ruby).
|
|
104
|
-
require "yeptris/native-#{RUBY_VERSION[/\A\d+\.\d+/]}"
|
|
115
|
+
# per-minor FIRST: a single version-agnostic native.so loads under
|
|
116
|
+
# ANY Ruby and is an ABI gamble across minors — Ruby 3.0 ran a
|
|
117
|
+
# 3.3-built ext and the JSON parse failed.
|
|
118
|
+
require "yeptris/#{RUBY_VERSION[/\A\d+\.\d+/]}/native"
|
|
105
119
|
rescue LoadError
|
|
106
120
|
begin
|
|
107
121
|
require "yeptris/native"
|
|
108
122
|
rescue LoadError => e
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
if Yeptris.debug? # the require chain is top-level (self = main)
|
|
124
|
+
warn "yeptris: no precompiled native materializer for Ruby " \
|
|
125
|
+
"#{RUBY_VERSION[/\A\d+\.\d+/]} on this platform " \
|
|
126
|
+
"(#{e.message}); the FFI ladder carries the load"
|
|
127
|
+
end
|
|
112
128
|
end
|
|
113
129
|
end
|
|
@@ -860,6 +860,7 @@ static YeptrisDocument cbor_wrap(yep_dom* dom, const void* buf, const yep_alloca
|
|
|
860
860
|
yep_dom_destroy(dom);
|
|
861
861
|
return NULL;
|
|
862
862
|
}
|
|
863
|
+
yep_mutex_init(&d->lazy_mu);
|
|
863
864
|
d->dom = dom;
|
|
864
865
|
d->sys = sys;
|
|
865
866
|
d->schema = YEPTRIS_SCHEMA_12_CORE;
|
|
@@ -868,6 +869,7 @@ static YeptrisDocument cbor_wrap(yep_dom* dom, const void* buf, const yep_alloca
|
|
|
868
869
|
d->input = (const char*)buf;
|
|
869
870
|
d->finish_pool = NULL;
|
|
870
871
|
d->lazy_tape = NULL; /* field-by-field ctor: no garbage for free */
|
|
872
|
+
d->lazy_kind = 0;
|
|
871
873
|
return (YeptrisDocument)d;
|
|
872
874
|
}
|
|
873
875
|
|
|
@@ -966,6 +968,7 @@ YEPTRIS_API YeptrisDocument yeptris_cbor_decode(const void* buf, size_t len, uin
|
|
|
966
968
|
}
|
|
967
969
|
return NULL;
|
|
968
970
|
}
|
|
971
|
+
yep_mutex_init(&doc->lazy_mu);
|
|
969
972
|
doc->dom = dom;
|
|
970
973
|
doc->sys = sys;
|
|
971
974
|
doc->schema = YEPTRIS_SCHEMA_12_CORE; /* CBOR's model is the core schema */
|
|
@@ -99,6 +99,15 @@ typedef struct yep_chunk_masks {
|
|
|
99
99
|
uint32_t valid; /* 1-bits for the bytes that exist (tails) */
|
|
100
100
|
} yep_chunk_masks;
|
|
101
101
|
|
|
102
|
+
/* one 64-byte block's classification (json_stage1_masks) */
|
|
103
|
+
struct yep_s1_block {
|
|
104
|
+
uint64_t q; /* raw quote bytes */
|
|
105
|
+
uint64_t bs; /* backslash bytes */
|
|
106
|
+
uint64_t op; /* []{}:, */
|
|
107
|
+
uint64_t c0; /* bytes < 0x20 (ws included) */
|
|
108
|
+
uint64_t tokens; /* the resolver's verdict: ops + string-open quotes */
|
|
109
|
+
};
|
|
110
|
+
|
|
102
111
|
/* The kernel table. One struct = one dispatch point (OCP: a new ISA is a
|
|
103
112
|
* new TU exporting a new table; nothing else changes). */
|
|
104
113
|
typedef struct yep_text_kernels {
|
|
@@ -175,10 +184,25 @@ typedef struct yep_text_kernels {
|
|
|
175
184
|
* hard error is an unterminated string (odd final parity) ->
|
|
176
185
|
* return 0. idx must hold len+2 entries. Bit-identical across
|
|
177
186
|
* ISAs (the differential suite pins it); scan/json.c owns the
|
|
178
|
-
*
|
|
179
|
-
|
|
187
|
+
* grammar contract. flags (never NULL) accumulates YEP_S1_* bits:
|
|
188
|
+
* the doc-level facts stage 2 needs to skip content scanning. */
|
|
189
|
+
int (*json_stage1)(const char* p, size_t len, uint32_t* idx, size_t* nidx, unsigned* flags);
|
|
190
|
+
|
|
191
|
+
/* The structural indexer's classification half, without the
|
|
192
|
+
* emission: per 64-byte block, the four u64 masks the resolver
|
|
193
|
+
* consumes (QUOTE/BS/OP/C0; ws is dead since the token contract
|
|
194
|
+
* dropped scalar-run starts). blocks receives len/64+1 records;
|
|
195
|
+
* *nblocks returns how many were written. Same reject/flag
|
|
196
|
+
* contract as json_stage1. The single-pass tape walk consumes
|
|
197
|
+
* this — no idx array, no emission loop. */
|
|
198
|
+
int (*json_stage1_masks)(const char* p, size_t len, struct yep_s1_block* blocks,
|
|
199
|
+
size_t* nblocks, unsigned* flags);
|
|
180
200
|
} yep_text_kernels;
|
|
181
201
|
|
|
202
|
+
/* json_stage1 flag bits (accumulated across blocks; cleared by caller) */
|
|
203
|
+
#define YEP_S1_C0_IN_STRING 1u /* raw non-ws C0 inside a string: reject */
|
|
204
|
+
#define YEP_S1_HAS_ESCAPE 2u /* a backslash byte exists: validate spans */
|
|
205
|
+
|
|
182
206
|
/* The structural indexer's mask resolver — the SSOT identity set for
|
|
183
207
|
* every json_stage1 implementation (scalar in scan/json.c, the ISA
|
|
184
208
|
* twins in the SIMD TUs). Given one 64-byte block's QUOTE/BS/OP/WS
|
|
@@ -195,15 +219,13 @@ typedef struct yep_text_kernels {
|
|
|
195
219
|
* tokens = (op | starts) & ~string_tail
|
|
196
220
|
*
|
|
197
221
|
* Returns the block's contribution; carries update in place. */
|
|
198
|
-
static inline
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
size_t off, uint32_t* idx, size_t n) {
|
|
222
|
+
static inline uint64_t yep_json_stage1_tokens(uint64_t q, uint64_t bs, uint64_t op, uint64_t c0,
|
|
223
|
+
uint64_t valid, uint64_t* prev_in_string,
|
|
224
|
+
uint64_t* esc_carry, unsigned* flags) {
|
|
202
225
|
uint64_t potential = bs & ~*esc_carry;
|
|
203
226
|
uint64_t s = (((potential << 1) | 0xAAAAAAAAAAAAAAAAull) - potential) ^ 0xAAAAAAAAAAAAAAAAull;
|
|
204
|
-
uint64_t esc_bs = s & bs;
|
|
205
227
|
uint64_t escaped = s ^ (bs | *esc_carry);
|
|
206
|
-
*esc_carry =
|
|
228
|
+
*esc_carry = (s & bs) >> 63;
|
|
207
229
|
uint64_t quote_live = q & ~escaped;
|
|
208
230
|
uint64_t ps = quote_live; /* sequential prefix-xor — a flat
|
|
209
231
|
* one-expression xor of shifts misses
|
|
@@ -217,15 +239,27 @@ static inline size_t yep_json_stage1_resolve(uint64_t q, uint64_t bs, uint64_t o
|
|
|
217
239
|
uint64_t in_string = ps ^ *prev_in_string;
|
|
218
240
|
*prev_in_string = (uint64_t)((int64_t)in_string >> 63);
|
|
219
241
|
uint64_t string_tail = (in_string ^ quote_live) & valid;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
242
|
+
if (bs != 0) {
|
|
243
|
+
*flags |= YEP_S1_HAS_ESCAPE;
|
|
244
|
+
}
|
|
245
|
+
if ((c0 & in_string & valid) != 0) {
|
|
246
|
+
*flags |= YEP_S1_C0_IN_STRING;
|
|
247
|
+
}
|
|
248
|
+
return (op | (quote_live & ~string_tail)) & ~string_tail & valid;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* The full resolver: token mask derivation + the idx emission loop
|
|
252
|
+
* (the json_stage1 implementations). */
|
|
253
|
+
static inline size_t yep_json_stage1_resolve(uint64_t q, uint64_t bs, uint64_t op, uint64_t ws,
|
|
254
|
+
uint64_t c0, uint64_t valid, uint64_t* prev_in_string,
|
|
255
|
+
uint64_t* esc_carry, size_t off, uint32_t* idx,
|
|
256
|
+
size_t n, unsigned* flags) {
|
|
257
|
+
uint64_t tokens =
|
|
258
|
+
yep_json_stage1_tokens(q, bs, op, c0, valid, prev_in_string, esc_carry, flags);
|
|
224
259
|
while (tokens != 0) {
|
|
225
260
|
idx[n++] = (uint32_t)(off + (size_t)yep_ctz64(tokens));
|
|
226
261
|
tokens &= tokens - 1;
|
|
227
262
|
}
|
|
228
|
-
(void)escaped;
|
|
229
263
|
return n;
|
|
230
264
|
}
|
|
231
265
|
|
|
@@ -455,12 +455,13 @@ yep_chunk_masks yep_text_json_chunk_avx2(const char* p, size_t n) {
|
|
|
455
455
|
* (two 32-byte lane passes each, movemask-native), then the shared
|
|
456
456
|
* mask resolver (simd_text.h). The final partial block builds its
|
|
457
457
|
* masks byte-wise — no loads past len. */
|
|
458
|
-
int yep_text_json_stage1_avx2(const char* p, size_t len, uint32_t* idx, size_t* nidx
|
|
458
|
+
int yep_text_json_stage1_avx2(const char* p, size_t len, uint32_t* idx, size_t* nidx,
|
|
459
|
+
unsigned* flags) {
|
|
459
460
|
size_t n = 0;
|
|
460
|
-
uint64_t prev_in_string = 0, esc_carry = 0
|
|
461
|
+
uint64_t prev_in_string = 0, esc_carry = 0;
|
|
461
462
|
size_t off = 0;
|
|
462
463
|
for (; off + 64 <= len; off += 64) {
|
|
463
|
-
uint64_t q = 0, bs = 0, op = 0, ws = 0;
|
|
464
|
+
uint64_t q = 0, bs = 0, op = 0, ws = 0, c0 = 0;
|
|
464
465
|
for (unsigned half = 0; half < 2; half++) {
|
|
465
466
|
const __m256i v =
|
|
466
467
|
_mm256_loadu_si256((const __m256i*)(const void*)(p + off + 32 * half));
|
|
@@ -482,17 +483,21 @@ int yep_text_json_stage1_avx2(const char* p, size_t len, uint32_t* idx, size_t*
|
|
|
482
483
|
_mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8('\n')),
|
|
483
484
|
_mm256_cmpeq_epi8(v, _mm256_set1_epi8('\r'))));
|
|
484
485
|
uint64_t fw = (uint64_t)(uint32_t)_mm256_movemask_epi8(w);
|
|
486
|
+
/* unsigned min(v, 0x20) == v <=> v < 0x20 */
|
|
487
|
+
uint64_t fc = (uint64_t)(uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(
|
|
488
|
+
_mm256_max_epu8(v, _mm256_set1_epi8(0x1F)), _mm256_set1_epi8(0x1F)));
|
|
485
489
|
q |= fq << (32 * half);
|
|
486
490
|
bs |= fb << (32 * half);
|
|
487
491
|
op |= fo << (32 * half);
|
|
488
492
|
ws |= fw << (32 * half);
|
|
493
|
+
c0 |= fc << (32 * half);
|
|
489
494
|
}
|
|
490
|
-
n = yep_json_stage1_resolve(q, bs, op, ws, ~0ull, &prev_in_string, &esc_carry,
|
|
491
|
-
|
|
495
|
+
n = yep_json_stage1_resolve(q, bs, op, ws, c0, ~0ull, &prev_in_string, &esc_carry, off, idx,
|
|
496
|
+
n, flags);
|
|
492
497
|
}
|
|
493
498
|
if (off < len) {
|
|
494
499
|
size_t cn = len - off;
|
|
495
|
-
uint64_t q = 0, bs = 0, op = 0, ws = 0;
|
|
500
|
+
uint64_t q = 0, bs = 0, op = 0, ws = 0, c0 = 0;
|
|
496
501
|
for (size_t k = 0; k < cn; k++) {
|
|
497
502
|
unsigned char c = (unsigned char)p[off + k];
|
|
498
503
|
uint64_t bit = 1ull << k;
|
|
@@ -505,14 +510,86 @@ int yep_text_json_stage1_avx2(const char* p, size_t len, uint32_t* idx, size_t*
|
|
|
505
510
|
} else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
|
506
511
|
ws |= bit;
|
|
507
512
|
}
|
|
513
|
+
if (c < 0x20) {
|
|
514
|
+
c0 |= bit;
|
|
515
|
+
}
|
|
508
516
|
}
|
|
509
|
-
n = yep_json_stage1_resolve(q, bs, op, ws, (1ull << cn) - 1ull, &prev_in_string,
|
|
510
|
-
&
|
|
517
|
+
n = yep_json_stage1_resolve(q, bs, op, ws, c0, (1ull << cn) - 1ull, &prev_in_string,
|
|
518
|
+
&esc_carry, off, idx, n, flags);
|
|
511
519
|
}
|
|
512
520
|
*nidx = n;
|
|
513
521
|
return prev_in_string ? 0 : 1;
|
|
514
522
|
}
|
|
515
523
|
|
|
524
|
+
/* The classification half of the structural indexer (AVX2 twin of the
|
|
525
|
+
* NEON masks slot): two 32-byte lane passes per 64-byte block. */
|
|
526
|
+
int yep_text_json_stage1_masks_avx2(const char* p, size_t len, struct yep_s1_block* blocks,
|
|
527
|
+
size_t* nblocks, unsigned* flags) {
|
|
528
|
+
size_t nb = 0;
|
|
529
|
+
uint64_t prev_in_string = 0, esc_carry = 0;
|
|
530
|
+
size_t off = 0;
|
|
531
|
+
for (; off + 64 <= len; off += 64) {
|
|
532
|
+
uint64_t q = 0, bs = 0, op = 0, c0 = 0;
|
|
533
|
+
for (unsigned half = 0; half < 2; half++) {
|
|
534
|
+
const __m256i v =
|
|
535
|
+
_mm256_loadu_si256((const __m256i*)(const void*)(p + off + 32 * half));
|
|
536
|
+
uint64_t fq = (uint64_t)(uint32_t)_mm256_movemask_epi8(
|
|
537
|
+
_mm256_cmpeq_epi8(v, _mm256_set1_epi8('"')));
|
|
538
|
+
uint64_t fb = (uint64_t)(uint32_t)_mm256_movemask_epi8(
|
|
539
|
+
_mm256_cmpeq_epi8(v, _mm256_set1_epi8('\\')));
|
|
540
|
+
__m256i o = _mm256_or_si256(
|
|
541
|
+
_mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8('{')),
|
|
542
|
+
_mm256_cmpeq_epi8(v, _mm256_set1_epi8('}'))),
|
|
543
|
+
_mm256_or_si256(_mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8('[')),
|
|
544
|
+
_mm256_cmpeq_epi8(v, _mm256_set1_epi8(']'))),
|
|
545
|
+
_mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8(',')),
|
|
546
|
+
_mm256_cmpeq_epi8(v, _mm256_set1_epi8(':')))));
|
|
547
|
+
uint64_t fo = (uint64_t)(uint32_t)_mm256_movemask_epi8(o);
|
|
548
|
+
uint64_t fc = (uint64_t)(uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(
|
|
549
|
+
_mm256_max_epu8(v, _mm256_set1_epi8(0x1F)), _mm256_set1_epi8(0x1F)));
|
|
550
|
+
q |= fq << (32 * half);
|
|
551
|
+
bs |= fb << (32 * half);
|
|
552
|
+
op |= fo << (32 * half);
|
|
553
|
+
c0 |= fc << (32 * half);
|
|
554
|
+
}
|
|
555
|
+
blocks[nb].q = q;
|
|
556
|
+
blocks[nb].bs = bs;
|
|
557
|
+
blocks[nb].op = op;
|
|
558
|
+
blocks[nb].c0 = c0;
|
|
559
|
+
blocks[nb].tokens =
|
|
560
|
+
yep_json_stage1_tokens(q, bs, op, c0, ~0ull, &prev_in_string, &esc_carry, flags);
|
|
561
|
+
nb++;
|
|
562
|
+
}
|
|
563
|
+
if (off < len) {
|
|
564
|
+
size_t cn = len - off;
|
|
565
|
+
uint64_t q = 0, bs = 0, op = 0, c0 = 0;
|
|
566
|
+
for (size_t k = 0; k < cn; k++) {
|
|
567
|
+
unsigned char c = (unsigned char)p[off + k];
|
|
568
|
+
uint64_t bit = 1ull << k;
|
|
569
|
+
if (c == '"') {
|
|
570
|
+
q |= bit;
|
|
571
|
+
} else if (c == '\\') {
|
|
572
|
+
bs |= bit;
|
|
573
|
+
} else if (c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || c == ':') {
|
|
574
|
+
op |= bit;
|
|
575
|
+
}
|
|
576
|
+
if (c < 0x20) {
|
|
577
|
+
c0 |= bit;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
uint64_t valid = (1ull << cn) - 1ull;
|
|
581
|
+
blocks[nb].q = q;
|
|
582
|
+
blocks[nb].bs = bs;
|
|
583
|
+
blocks[nb].op = op;
|
|
584
|
+
blocks[nb].c0 = c0;
|
|
585
|
+
blocks[nb].tokens =
|
|
586
|
+
yep_json_stage1_tokens(q, bs, op, c0, valid, &prev_in_string, &esc_carry, flags);
|
|
587
|
+
nb++;
|
|
588
|
+
}
|
|
589
|
+
*nblocks = nb;
|
|
590
|
+
return prev_in_string ? 0 : 1;
|
|
591
|
+
}
|
|
592
|
+
|
|
516
593
|
const yep_text_kernels yep_text_kernels_avx2 = {
|
|
517
594
|
yep_avx2_contains,
|
|
518
595
|
yep_avx2_find,
|
|
@@ -529,6 +606,7 @@ const yep_text_kernels yep_text_kernels_avx2 = {
|
|
|
529
606
|
yep_avx2_line_facts,
|
|
530
607
|
yep_text_json_chunk_avx2,
|
|
531
608
|
yep_text_json_stage1_avx2,
|
|
609
|
+
yep_text_json_stage1_masks_avx2,
|
|
532
610
|
};
|
|
533
611
|
|
|
534
612
|
#endif /* YEP_ARCH_X86 */
|
|
@@ -588,9 +588,10 @@ yep_chunk_masks yep_text_json_chunk_neon(const char* p, size_t n) {
|
|
|
588
588
|
* (four 16-byte halves each), then the shared mask resolver (the
|
|
589
589
|
* simdjson identity set, simd_text.h). The final partial block
|
|
590
590
|
* builds its masks byte-wise — no loads past len. */
|
|
591
|
-
int yep_text_json_stage1_neon(const char* p, size_t len, uint32_t* idx, size_t* nidx
|
|
591
|
+
int yep_text_json_stage1_neon(const char* p, size_t len, uint32_t* idx, size_t* nidx,
|
|
592
|
+
unsigned* flags) {
|
|
592
593
|
size_t n = 0;
|
|
593
|
-
uint64_t prev_in_string = 0, esc_carry = 0
|
|
594
|
+
uint64_t prev_in_string = 0, esc_carry = 0;
|
|
594
595
|
size_t off = 0;
|
|
595
596
|
const uint8x16_t dq = vdupq_n_u8('"'), dbs = vdupq_n_u8('\\');
|
|
596
597
|
const uint8x16_t d1 = vdupq_n_u8('{'), d2 = vdupq_n_u8('}');
|
|
@@ -598,8 +599,9 @@ int yep_text_json_stage1_neon(const char* p, size_t len, uint32_t* idx, size_t*
|
|
|
598
599
|
const uint8x16_t d5 = vdupq_n_u8(','), d6 = vdupq_n_u8(':');
|
|
599
600
|
const uint8x16_t wsp = vdupq_n_u8(' '), wtab = vdupq_n_u8('\t');
|
|
600
601
|
const uint8x16_t wnl = vdupq_n_u8('\n'), wcr = vdupq_n_u8('\r');
|
|
602
|
+
const uint8x16_t c0t = vdupq_n_u8(0x20);
|
|
601
603
|
for (; off + 64 <= len; off += 64) {
|
|
602
|
-
uint64_t q = 0, bs = 0, op = 0, ws = 0;
|
|
604
|
+
uint64_t q = 0, bs = 0, op = 0, ws = 0, c0 = 0;
|
|
603
605
|
for (unsigned half = 0; half < 4; half++) {
|
|
604
606
|
uint8x16_t v = vld1q_u8((const uint8_t*)(const void*)(p + off + 16 * half));
|
|
605
607
|
uint64_t fq = yep_neon_bits(vceqq_u8(v, dq));
|
|
@@ -610,17 +612,19 @@ int yep_text_json_stage1_neon(const char* p, size_t len, uint32_t* idx, size_t*
|
|
|
610
612
|
vorrq_u8(vceqq_u8(v, d5), vceqq_u8(v, d6))));
|
|
611
613
|
uint64_t fw = yep_neon_bits(vorrq_u8(vorrq_u8(vceqq_u8(v, wsp), vceqq_u8(v, wtab)),
|
|
612
614
|
vorrq_u8(vceqq_u8(v, wnl), vceqq_u8(v, wcr))));
|
|
615
|
+
uint64_t fc = yep_neon_bits(vcltq_u8(v, c0t));
|
|
613
616
|
q |= fq << (16 * half);
|
|
614
617
|
bs |= fb << (16 * half);
|
|
615
618
|
op |= fo << (16 * half);
|
|
616
619
|
ws |= fw << (16 * half);
|
|
620
|
+
c0 |= fc << (16 * half);
|
|
617
621
|
}
|
|
618
|
-
n = yep_json_stage1_resolve(q, bs, op, ws, ~0ull, &prev_in_string, &esc_carry,
|
|
619
|
-
|
|
622
|
+
n = yep_json_stage1_resolve(q, bs, op, ws, c0, ~0ull, &prev_in_string, &esc_carry, off, idx,
|
|
623
|
+
n, flags);
|
|
620
624
|
}
|
|
621
625
|
if (off < len) { /* the tail: byte-wise masks, no loads past len */
|
|
622
626
|
size_t cn = len - off;
|
|
623
|
-
uint64_t q = 0, bs = 0, op = 0, ws = 0;
|
|
627
|
+
uint64_t q = 0, bs = 0, op = 0, ws = 0, c0 = 0;
|
|
624
628
|
for (size_t k = 0; k < cn; k++) {
|
|
625
629
|
unsigned char c = (unsigned char)p[off + k];
|
|
626
630
|
uint64_t bit = 1ull << k;
|
|
@@ -633,14 +637,83 @@ int yep_text_json_stage1_neon(const char* p, size_t len, uint32_t* idx, size_t*
|
|
|
633
637
|
} else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
|
634
638
|
ws |= bit;
|
|
635
639
|
}
|
|
640
|
+
if (c < 0x20) {
|
|
641
|
+
c0 |= bit;
|
|
642
|
+
}
|
|
636
643
|
}
|
|
637
|
-
n = yep_json_stage1_resolve(q, bs, op, ws, (1ull << cn) - 1ull, &prev_in_string,
|
|
638
|
-
&
|
|
644
|
+
n = yep_json_stage1_resolve(q, bs, op, ws, c0, (1ull << cn) - 1ull, &prev_in_string,
|
|
645
|
+
&esc_carry, off, idx, n, flags);
|
|
639
646
|
}
|
|
640
647
|
*nidx = n;
|
|
641
648
|
return prev_in_string ? 0 : 1;
|
|
642
649
|
}
|
|
643
650
|
|
|
651
|
+
/* The classification half of the structural indexer: the same
|
|
652
|
+
* 64-byte/4-half mask build, no emission, blocks out instead of idx. */
|
|
653
|
+
int yep_text_json_stage1_masks_neon(const char* p, size_t len, struct yep_s1_block* blocks,
|
|
654
|
+
size_t* nblocks, unsigned* flags) {
|
|
655
|
+
size_t nb = 0;
|
|
656
|
+
uint64_t prev_in_string = 0, esc_carry = 0;
|
|
657
|
+
size_t off = 0;
|
|
658
|
+
const uint8x16_t dq = vdupq_n_u8('"'), dbs = vdupq_n_u8('\\');
|
|
659
|
+
const uint8x16_t d1 = vdupq_n_u8('{'), d2 = vdupq_n_u8('}');
|
|
660
|
+
const uint8x16_t d3 = vdupq_n_u8('['), d4 = vdupq_n_u8(']');
|
|
661
|
+
const uint8x16_t d5 = vdupq_n_u8(','), d6 = vdupq_n_u8(':');
|
|
662
|
+
const uint8x16_t c0t = vdupq_n_u8(0x20);
|
|
663
|
+
for (; off + 64 <= len; off += 64) {
|
|
664
|
+
uint64_t q = 0, bs = 0, op = 0, c0 = 0;
|
|
665
|
+
for (unsigned half = 0; half < 4; half++) {
|
|
666
|
+
uint8x16_t v = vld1q_u8((const uint8_t*)(const void*)(p + off + 16 * half));
|
|
667
|
+
uint64_t fq = yep_neon_bits(vceqq_u8(v, dq));
|
|
668
|
+
uint64_t fb = yep_neon_bits(vceqq_u8(v, dbs));
|
|
669
|
+
uint64_t fo =
|
|
670
|
+
yep_neon_bits(vorrq_u8(vorrq_u8(vorrq_u8(vceqq_u8(v, d1), vceqq_u8(v, d2)),
|
|
671
|
+
vorrq_u8(vceqq_u8(v, d3), vceqq_u8(v, d4))),
|
|
672
|
+
vorrq_u8(vceqq_u8(v, d5), vceqq_u8(v, d6))));
|
|
673
|
+
uint64_t fc = yep_neon_bits(vcltq_u8(v, c0t));
|
|
674
|
+
q |= fq << (16 * half);
|
|
675
|
+
bs |= fb << (16 * half);
|
|
676
|
+
op |= fo << (16 * half);
|
|
677
|
+
c0 |= fc << (16 * half);
|
|
678
|
+
}
|
|
679
|
+
blocks[nb].q = q;
|
|
680
|
+
blocks[nb].bs = bs;
|
|
681
|
+
blocks[nb].op = op;
|
|
682
|
+
blocks[nb].c0 = c0;
|
|
683
|
+
blocks[nb].tokens =
|
|
684
|
+
yep_json_stage1_tokens(q, bs, op, c0, ~0ull, &prev_in_string, &esc_carry, flags);
|
|
685
|
+
nb++;
|
|
686
|
+
}
|
|
687
|
+
if (off < len) {
|
|
688
|
+
size_t cn = len - off;
|
|
689
|
+
uint64_t q = 0, bs = 0, op = 0, c0 = 0;
|
|
690
|
+
for (size_t k = 0; k < cn; k++) {
|
|
691
|
+
unsigned char c = (unsigned char)p[off + k];
|
|
692
|
+
uint64_t bit = 1ull << k;
|
|
693
|
+
if (c == '"') {
|
|
694
|
+
q |= bit;
|
|
695
|
+
} else if (c == '\\') {
|
|
696
|
+
bs |= bit;
|
|
697
|
+
} else if (c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || c == ':') {
|
|
698
|
+
op |= bit;
|
|
699
|
+
}
|
|
700
|
+
if (c < 0x20) {
|
|
701
|
+
c0 |= bit;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
uint64_t valid = (1ull << cn) - 1ull;
|
|
705
|
+
blocks[nb].q = q;
|
|
706
|
+
blocks[nb].bs = bs;
|
|
707
|
+
blocks[nb].op = op;
|
|
708
|
+
blocks[nb].c0 = c0;
|
|
709
|
+
blocks[nb].tokens =
|
|
710
|
+
yep_json_stage1_tokens(q, bs, op, c0, valid, &prev_in_string, &esc_carry, flags);
|
|
711
|
+
nb++;
|
|
712
|
+
}
|
|
713
|
+
*nblocks = nb;
|
|
714
|
+
return prev_in_string ? 0 : 1;
|
|
715
|
+
}
|
|
716
|
+
|
|
644
717
|
const yep_text_kernels yep_text_kernels_neon = {
|
|
645
718
|
yep_neon_contains,
|
|
646
719
|
yep_neon_find,
|
|
@@ -657,6 +730,7 @@ const yep_text_kernels yep_text_kernels_neon = {
|
|
|
657
730
|
yep_neon_line_facts,
|
|
658
731
|
yep_text_json_chunk_neon,
|
|
659
732
|
yep_text_json_stage1_neon,
|
|
733
|
+
yep_text_json_stage1_masks_neon,
|
|
660
734
|
};
|
|
661
735
|
|
|
662
736
|
#endif /* YEP_ARCH_AARCH64 */
|
|
@@ -287,12 +287,16 @@ yep_chunk_masks yep_text_json_chunk_scalar(const char* p, size_t n) {
|
|
|
287
287
|
|
|
288
288
|
/* the stage-1 scalar reference lives in scan/json.c (the grammar
|
|
289
289
|
* home); the table carries it so dispatch stays one point */
|
|
290
|
-
extern int yep_json_stage1_scalar(const char* p, size_t len, uint32_t* idx, size_t* nidx
|
|
290
|
+
extern int yep_json_stage1_scalar(const char* p, size_t len, uint32_t* idx, size_t* nidx,
|
|
291
|
+
unsigned* flags);
|
|
292
|
+
extern int yep_json_stage1_masks_scalar(const char* p, size_t len, struct yep_s1_block* blocks,
|
|
293
|
+
size_t* nblocks, unsigned* flags);
|
|
291
294
|
|
|
292
295
|
const yep_text_kernels yep_text_kernels_scalar = {
|
|
293
|
-
yep_text_contains_scalar,
|
|
294
|
-
yep_text_count_char_scalar,
|
|
295
|
-
yep_text_find_not_scalar,
|
|
296
|
-
yep_text_scan_stats_scalar,
|
|
297
|
-
yep_text_line_facts_scalar,
|
|
296
|
+
yep_text_contains_scalar, yep_text_find_scalar, yep_text_find3_scalar,
|
|
297
|
+
yep_text_count_char_scalar, yep_text_count3_scalar, yep_text_copy_count3_scalar,
|
|
298
|
+
yep_text_find_not_scalar, yep_text_stopset_find_scalar, yep_text_quote_scan_scalar,
|
|
299
|
+
yep_text_scan_stats_scalar, yep_text_qbc_find_scalar, yep_text_gate_scan_scalar,
|
|
300
|
+
yep_text_line_facts_scalar, yep_text_json_chunk_scalar, yep_json_stage1_scalar,
|
|
301
|
+
yep_json_stage1_masks_scalar,
|
|
298
302
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#include <stddef.h>
|
|
9
9
|
|
|
10
|
+
#include "common/mutex.h"
|
|
10
11
|
#include "dom/dom.h"
|
|
11
12
|
#include "memory/allocator.h"
|
|
12
13
|
|
|
@@ -18,15 +19,24 @@ typedef struct yeptris_document {
|
|
|
18
19
|
* conditioned on it (TODO.restructure/32) */
|
|
19
20
|
unsigned char* transcoded; /* owned when non-NULL */
|
|
20
21
|
size_t transcoded_len;
|
|
21
|
-
const char* input;
|
|
22
|
-
void* finish_pool;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
void* lazy_tape;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
const char* input; /* borrowed input (lifetime documentation) */
|
|
23
|
+
void* finish_pool; /* engine finish pool: resolved tags, folded and
|
|
24
|
+
escaped scalars outlive the engine through the
|
|
25
|
+
document */
|
|
26
|
+
void* lazy_tape; /* #342 slice 2: the parsed tape when the tree is
|
|
27
|
+
* deferred (gate-clean strict JSON; #378: the YAML
|
|
28
|
+
* record tape). dom==NULL until the first tree
|
|
29
|
+
* access materializes; freed with the document.
|
|
30
|
+
* void* to avoid a header cycle — parse.c casts
|
|
31
|
+
* per lazy_kind */
|
|
32
|
+
int lazy_kind; /* 0 = none/json (yeptris_json_tape), 1 = yaml
|
|
33
|
+
* (yep_ytape) — every constructor must set it
|
|
34
|
+
* (the field-by-field ctor trap) */
|
|
35
|
+
yep_mutex_raw lazy_mu; /* materialization lock: the first tree
|
|
36
|
+
* access builds; concurrent first accesses
|
|
37
|
+
* wait (read-only sharing stays lock-free
|
|
38
|
+
* once dom is set — every constructor must
|
|
39
|
+
* yep_mutex_init it, the ctor trap) */
|
|
30
40
|
} yeptris_document;
|
|
31
41
|
|
|
32
42
|
/* Node handle: a (document, node-id) pair so nodes stay usable even if
|