yeptris 0.6.18.2 → 0.6.20.1
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/materializer.rb +12 -11
- data/lib/yeptris/psych/handler.rb +5 -0
- data/lib/yeptris/psych/parser.rb +8 -3
- data/lib/yeptris/psych/tree_builder.rb +119 -0
- data/lib/yeptris/psych.rb +4 -0
- data/lib/yeptris.rb +15 -11
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/CMakeLists.txt +1 -0
- data/vendor/libyeptris/src/include/yeptris/events.h +8 -1
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +1 -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 +8 -4
- data/vendor/libyeptris/src/yeptris/dom/ytape.c +640 -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/events/capture.c +5 -1
- data/vendor/libyeptris/src/yeptris/parse/engine.c +150 -3
- data/vendor/libyeptris/src/yeptris/parse/events.h +6 -0
- data/vendor/libyeptris/src/yeptris/parse.c +144 -42
- 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 +539 -163
- data/vendor/libyeptris/src/yeptris/tape_in.h +12 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e22b16a8d0158fb8161f390d54b7dd8de6612fbfcc881741a7896f0a891b6188
|
|
4
|
+
data.tar.gz: 9fab611eff46efb8a82f434a6daf89fafbd400c3493ee80a36a01fbcba7a8983
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 66e8f18cd12134048d4a89d95f4d3c93faeb2f0700e0e31a9ce34b12269feba643c4e896fe2d5b3bc0d36958439e97901b9b2e376fc9afc1c62e455e70af2c66
|
|
7
|
+
data.tar.gz: c983b4b04736ae785e8c3b6c4380968986eaf105326b2d3e10c155f9d86fe0522cd76cb940649685ff11b4d510794828d28e56a737d4e9afda23badceb9262da
|
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/materializer.rb
CHANGED
|
@@ -12,9 +12,10 @@ module Yeptris
|
|
|
12
12
|
# materializer (Node#to_ruby) stays for node-based use; this is the
|
|
13
13
|
# Yeptris::YAML fast path.
|
|
14
14
|
class Materializer
|
|
15
|
-
RECORD_SIZE =
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
RECORD_SIZE = 44 # YeptrisEventRecord layout (events.h, ABI-pinned;
|
|
16
|
+
# #179 added end_line/end_col in the 0.6.19 window)
|
|
17
|
+
FIELDS = 14 # unpacked values per record
|
|
18
|
+
RECORD_UNPACK = "C4V10" # type/style/flags/tag_id, line..tag_len
|
|
18
19
|
|
|
19
20
|
STREAM_START = 1
|
|
20
21
|
STREAM_END = 2
|
|
@@ -256,10 +257,10 @@ module Yeptris
|
|
|
256
257
|
# not the text
|
|
257
258
|
tag_id = flat[i + 3]
|
|
258
259
|
v = Materializer.scan_by_tag(
|
|
259
|
-
arena[flat[i +
|
|
260
|
+
arena[flat[i + 8], flat[i + 9]], tag_id, (flat[i + 2] & EF_IMPLICIT) != 0
|
|
260
261
|
)
|
|
261
|
-
l = flat[i +
|
|
262
|
-
anchors[arena[flat[i +
|
|
262
|
+
l = flat[i + 11]
|
|
263
|
+
anchors[arena[flat[i + 10], l]] = v if l != 0
|
|
263
264
|
# place(): the scalar fast path inlined — the overwhelming
|
|
264
265
|
# majority of events land here
|
|
265
266
|
if stack.empty?
|
|
@@ -284,22 +285,22 @@ module Yeptris
|
|
|
284
285
|
end
|
|
285
286
|
when MAPPING_START
|
|
286
287
|
h = {}
|
|
287
|
-
l = flat[i +
|
|
288
|
-
anchors[arena[flat[i +
|
|
288
|
+
l = flat[i + 11]
|
|
289
|
+
anchors[arena[flat[i + 10], l]] = h if l != 0
|
|
289
290
|
merge_target.push(place(docs, stack, pending_key, pending_key_merge, h))
|
|
290
291
|
stack.push(h)
|
|
291
292
|
pending_key.push(nil)
|
|
292
293
|
pending_key_merge.push(nil)
|
|
293
294
|
when SEQUENCE_START
|
|
294
295
|
a = []
|
|
295
|
-
l = flat[i +
|
|
296
|
-
anchors[arena[flat[i +
|
|
296
|
+
l = flat[i + 11]
|
|
297
|
+
anchors[arena[flat[i + 10], l]] = a if l != 0
|
|
297
298
|
merge_target.push(place(docs, stack, pending_key, pending_key_merge, a))
|
|
298
299
|
stack.push(a)
|
|
299
300
|
pending_key.push(nil)
|
|
300
301
|
pending_key_merge.push(nil)
|
|
301
302
|
when ALIAS
|
|
302
|
-
name = arena[flat[i +
|
|
303
|
+
name = arena[flat[i + 8], flat[i + 9]]
|
|
303
304
|
raise Yeptris::ParseError, "unknown anchor: #{name.inspect}" unless anchors.key?(name)
|
|
304
305
|
|
|
305
306
|
place(docs, stack, pending_key, pending_key_merge, anchors[name])
|
|
@@ -49,6 +49,11 @@ module Yeptris
|
|
|
49
49
|
|
|
50
50
|
def alias(anchor); end
|
|
51
51
|
|
|
52
|
+
# #179: called before EVERY event with the event's 0-based marks
|
|
53
|
+
# (psych 5's contract — TreeBuilder stashes them for node
|
|
54
|
+
# locations). No-op by default; handlers that care override it.
|
|
55
|
+
def event_location(start_line, start_column, end_line, end_column); end
|
|
56
|
+
|
|
52
57
|
# +style+ is one of the scalar constants above; +plain+ /
|
|
53
58
|
# +quoted+ are the implicit-typing flags (plain: resolvable
|
|
54
59
|
# without a tag).
|
data/lib/yeptris/psych/parser.rb
CHANGED
|
@@ -52,12 +52,17 @@ module Yeptris
|
|
|
52
52
|
h = @handler
|
|
53
53
|
i = 0
|
|
54
54
|
while i < flat.length
|
|
55
|
+
# #179: psych's parser calls event_location before every
|
|
56
|
+
# event; TreeBuilder stashes the marks for node locations
|
|
57
|
+
# (0-based).
|
|
58
|
+
h.event_location(flat[i + 4] - 1, flat[i + 5] - 1,
|
|
59
|
+
flat[i + 6] - 1, flat[i + 7] - 1)
|
|
55
60
|
type = flat[i]
|
|
56
61
|
style = flat[i + 1]
|
|
57
62
|
flags = flat[i + 2]
|
|
58
|
-
value = field(flat, arena, i +
|
|
59
|
-
anchor = field(flat, arena, i +
|
|
60
|
-
tag = field(flat, arena, i +
|
|
63
|
+
value = field(flat, arena, i + 8)
|
|
64
|
+
anchor = field(flat, arena, i + 10)
|
|
65
|
+
tag = field(flat, arena, i + 12)
|
|
61
66
|
case type
|
|
62
67
|
when STREAM_START then h.start_stream(UTF8)
|
|
63
68
|
when STREAM_END then h.end_stream
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yeptris
|
|
4
|
+
module Psych
|
|
5
|
+
# The stdlib TreeBuilder port (#179): builds a Nodes tree carrying
|
|
6
|
+
# start/end line:column on every node. The parser calls
|
|
7
|
+
# event_location before each event (psych 5's exact contract);
|
|
8
|
+
# stdlib's class is the reference — this port follows it
|
|
9
|
+
# function-for-function over the binding's keyword-arg Nodes.
|
|
10
|
+
class TreeBuilder < Handler
|
|
11
|
+
attr_reader :root
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
super
|
|
15
|
+
@stack = []
|
|
16
|
+
@last = nil
|
|
17
|
+
@root = nil
|
|
18
|
+
@start_line = nil
|
|
19
|
+
@start_column = nil
|
|
20
|
+
@end_line = nil
|
|
21
|
+
@end_column = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def event_location(start_line, start_column, end_line, end_column)
|
|
25
|
+
@start_line = start_line
|
|
26
|
+
@start_column = start_column
|
|
27
|
+
@end_line = end_line
|
|
28
|
+
@end_column = end_column
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
%w[Sequence Mapping].each do |node|
|
|
32
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
33
|
+
def start_#{node.downcase}(anchor, tag, implicit, style)
|
|
34
|
+
n = Nodes::#{node}.new(anchor: anchor, tag: tag, style: style)
|
|
35
|
+
set_start_location(n)
|
|
36
|
+
@last.children << n
|
|
37
|
+
push n
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def end_#{node.downcase}
|
|
41
|
+
n = pop
|
|
42
|
+
set_end_location(n)
|
|
43
|
+
n
|
|
44
|
+
end
|
|
45
|
+
RUBY
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def start_document(version, tag_directives, implicit)
|
|
49
|
+
n = Nodes::Document.new(version, tag_directives)
|
|
50
|
+
set_start_location(n)
|
|
51
|
+
@last.children << n
|
|
52
|
+
push n
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def end_document(_implicit = false)
|
|
56
|
+
n = pop
|
|
57
|
+
set_end_location(n)
|
|
58
|
+
n
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def start_stream(_encoding)
|
|
62
|
+
n = Nodes::Stream.new
|
|
63
|
+
set_start_location(n)
|
|
64
|
+
@root = n
|
|
65
|
+
push n
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def end_stream
|
|
69
|
+
n = pop
|
|
70
|
+
set_end_location(n)
|
|
71
|
+
n
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def scalar(value, anchor, tag, plain, quoted, style)
|
|
75
|
+
n = Nodes::Scalar.new(value, anchor: anchor, tag: tag,
|
|
76
|
+
plain: plain, quoted: quoted, style: style)
|
|
77
|
+
set_location(n)
|
|
78
|
+
@last.children << n
|
|
79
|
+
n
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def alias(anchor)
|
|
83
|
+
n = Nodes::Alias.new(anchor)
|
|
84
|
+
set_location(n)
|
|
85
|
+
@last.children << n
|
|
86
|
+
n
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def push(value)
|
|
92
|
+
@stack.push(value)
|
|
93
|
+
@last = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def pop
|
|
97
|
+
value = @stack.pop
|
|
98
|
+
@last = @stack.last
|
|
99
|
+
value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def set_location(n)
|
|
103
|
+
n.start_line = @start_line
|
|
104
|
+
n.start_column = @start_column
|
|
105
|
+
n.end_line = @end_line
|
|
106
|
+
n.end_column = @end_column
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def set_start_location(n)
|
|
110
|
+
set_location(n)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def set_end_location(n)
|
|
114
|
+
n.end_line = @end_line
|
|
115
|
+
n.end_column = @end_column
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
data/lib/yeptris/psych.rb
CHANGED
|
@@ -86,6 +86,8 @@ module Yeptris
|
|
|
86
86
|
# own entry so referencing Psych::Handlers triggers the load
|
|
87
87
|
autoload :Handlers, "yeptris/psych/handler"
|
|
88
88
|
autoload :Parser, "yeptris/psych/parser"
|
|
89
|
+
# #179: the located tree builder (stdlib's class, ported)
|
|
90
|
+
autoload :TreeBuilder, "yeptris/psych/tree_builder"
|
|
89
91
|
autoload :CoderShim, "yeptris/psych/coder_shim"
|
|
90
92
|
autoload :Visitors, "yeptris/psych/visitors"
|
|
91
93
|
# The typed opt-in marker for arbitrary-object dump/load
|
|
@@ -425,6 +427,8 @@ module Yeptris
|
|
|
425
427
|
include Enumerable
|
|
426
428
|
|
|
427
429
|
attr_reader :children
|
|
430
|
+
# #179: the event marks (0-based), applied by TreeBuilder
|
|
431
|
+
attr_accessor :start_line, :start_column, :end_line, :end_column
|
|
428
432
|
attr_reader :handle # @api private — the Yeptris::Node
|
|
429
433
|
# @api private — the tree builder attaches handles; a writer,
|
|
430
434
|
# never instance_variable_set from outside
|
data/lib/yeptris.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
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.1".freeze
|
|
7
7
|
# The error hierarchy lives in THIS file (the parent namespace's
|
|
8
8
|
# own file): nested constants do not trigger a parent-constant
|
|
9
9
|
# autoload, and the law forbids internal requires — defining the
|
|
@@ -91,17 +91,21 @@ end
|
|
|
91
91
|
# Optional C-API materializer (TODO.restructure/22): fused visit →
|
|
92
92
|
# Ruby objects via the Ruby C API. Feature-detected — LoadError leaves
|
|
93
93
|
# the FFI ladder (Marshal → columns → records) as the sole path.
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
94
|
+
# One bundle per Ruby minor (the leptris-ruby #207/#227 lesson: a PE
|
|
95
|
+
# DLL must bind its build Ruby's runtime; a unix bundle is also
|
|
96
|
+
# minor-locked by the internal API it compiles against). The MINOR
|
|
97
|
+
# lives in the DIRECTORY (lib/yeptris/<minor>/native.<ext>) and the
|
|
98
|
+
# FILE name stays unversioned: ruby derives the init symbol from the
|
|
99
|
+
# feature basename, so native-3.4.so asked for the untypeable
|
|
100
|
+
# "Init_native-3" and the versioned-file scheme could never load
|
|
101
|
+
# (#157 — every shipped bundle was dead code). Dev checkouts keep
|
|
102
|
+
# the plain native.<ext> name (mkmf layout, built for the running
|
|
103
|
+
# Ruby). A missing cell falls back LOUDLY to the ladder.
|
|
98
104
|
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+/]}"
|
|
105
|
+
# per-minor FIRST: a single version-agnostic native.so loads under
|
|
106
|
+
# ANY Ruby and is an ABI gamble across minors — Ruby 3.0 ran a
|
|
107
|
+
# 3.3-built ext and the JSON parse failed.
|
|
108
|
+
require "yeptris/#{RUBY_VERSION[/\A\d+\.\d+/]}/native"
|
|
105
109
|
rescue LoadError
|
|
106
110
|
begin
|
|
107
111
|
require "yeptris/native"
|
|
@@ -66,6 +66,9 @@ typedef struct {
|
|
|
66
66
|
size_t tag_len;
|
|
67
67
|
uint32_t line; /* 1-based node start */
|
|
68
68
|
uint32_t col;
|
|
69
|
+
uint32_t end_line; /* #179: libyaml end_mark semantics — 1-based line,
|
|
70
|
+
* col one past the node's end token */
|
|
71
|
+
uint32_t end_col;
|
|
69
72
|
} YeptrisEvent;
|
|
70
73
|
|
|
71
74
|
/* ---- push ------------------------------------------------------------- */
|
|
@@ -106,13 +109,17 @@ typedef struct {
|
|
|
106
109
|
* struct's pad byte: sizeof stays 36 (ABI-pinned) */
|
|
107
110
|
uint32_t line;
|
|
108
111
|
uint32_t col;
|
|
112
|
+
uint32_t end_line; /* #179: libyaml end_mark semantics (see the
|
|
113
|
+
* internal event) — 1-based, col past the end */
|
|
114
|
+
uint32_t end_col;
|
|
109
115
|
uint32_t value_off;
|
|
110
116
|
uint32_t value_len;
|
|
111
117
|
uint32_t anchor_off;
|
|
112
118
|
uint32_t anchor_len;
|
|
113
119
|
uint32_t tag_off;
|
|
114
120
|
uint32_t tag_len;
|
|
115
|
-
} YeptrisEventRecord;
|
|
121
|
+
} YeptrisEventRecord; /* 44B — the bindings mirror this layout in the
|
|
122
|
+
same lockstep window (the 0.6.9 law) */
|
|
116
123
|
|
|
117
124
|
YEPTRIS_API YeptrisRecorder yeptris_recorder_new(void);
|
|
118
125
|
|
|
@@ -868,6 +868,7 @@ static YeptrisDocument cbor_wrap(yep_dom* dom, const void* buf, const yep_alloca
|
|
|
868
868
|
d->input = (const char*)buf;
|
|
869
869
|
d->finish_pool = NULL;
|
|
870
870
|
d->lazy_tape = NULL; /* field-by-field ctor: no garbage for free */
|
|
871
|
+
d->lazy_kind = 0;
|
|
871
872
|
return (YeptrisDocument)d;
|
|
872
873
|
}
|
|
873
874
|
|
|
@@ -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 */
|