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,253 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yeptris
|
|
4
|
+
# The schema-descriptor materialization (issue #238; the C ABI lives
|
|
5
|
+
# in yeptris/schema.h and docs/schema-abi.md). One native pass fills
|
|
6
|
+
# typed columns against a caller-compiled descriptor; the
|
|
7
|
+
# intermediate generic document never exists.
|
|
8
|
+
module Schema
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
KIND = { scalar: 0, sequence: 1, mapping: 2, callback: 3 }.freeze
|
|
12
|
+
TYPE = { str: 0, int: 1, float: 2, bool: 3, null: 4, any: 5 }.freeze
|
|
13
|
+
REQUIRED = 1 << 0
|
|
14
|
+
FIRST_WINS = 1 << 1 # duplicates within one mapping: first kept
|
|
15
|
+
|
|
16
|
+
# The :any verdict (#238's headroom): the resolver's own typing,
|
|
17
|
+
# one 24-byte record per value — Integer/Float/true/false/nil, a
|
|
18
|
+
# zero-copy String for str/timestamp spans
|
|
19
|
+
ANY_SIZE = 24 # yeptris_value.h's YeptrisValue: kind@0 tag_id@1
|
|
20
|
+
# is_key@2 b@3 off@4 len@8 p@16 — the C layout, ABI-pinned
|
|
21
|
+
ANY_KIND = { 1 => :null, 2 => :bool, 3 => :int, 4 => :float,
|
|
22
|
+
5 => :str, 6 => :timestamp }.freeze
|
|
23
|
+
|
|
24
|
+
class Error < Yeptris::Error; end
|
|
25
|
+
|
|
26
|
+
# #238's executed headroom (ST_ANY/FIRST_WINS) rides the C tag
|
|
27
|
+
# AFTER v0.6.12; probe the engine rather than trusting versions
|
|
28
|
+
class << self
|
|
29
|
+
def headroom_supported?
|
|
30
|
+
return @headroom_supported unless @headroom_supported.nil?
|
|
31
|
+
@headroom_supported = begin
|
|
32
|
+
load("x: 1\n",
|
|
33
|
+
desc: [{ kind: :mapping, child_index: 1, child_count: 1 },
|
|
34
|
+
{ wire_name: "x", kind: :scalar, type: :any }]) && true
|
|
35
|
+
rescue Error, Yeptris::ParseError, Yeptris::Error
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
class RequiredMissing < Error; end
|
|
41
|
+
|
|
42
|
+
ELEMENT_SIZE = { 0 => 16, 1 => 8, 2 => 8, 3 => 1, 4 => 1, 5 => ANY_SIZE }.freeze # by TYPE value
|
|
43
|
+
|
|
44
|
+
# A materialized span: zero-copy off/len into the source plus the
|
|
45
|
+
# node's byte offset (the CALLBACK escape hatch).
|
|
46
|
+
Span = Struct.new(:offset, :length, :node_offset) do
|
|
47
|
+
def bytes_from(source)
|
|
48
|
+
source.byteslice(offset, length)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# desc: a flat array of node hashes, the C ABI 1:1 —
|
|
53
|
+
# { wire_name:, kind:, type:, required:, child_index:, child_count: }
|
|
54
|
+
# (kind/type symbolic; wire_name nil = the root). Returns one typed
|
|
55
|
+
# Ruby array per node: Integer/Float/true/false/nil/String for
|
|
56
|
+
# scalars and sequence elements (document order), Span for
|
|
57
|
+
# callbacks.
|
|
58
|
+
def load(source, schema: :core_12, desc:, capacity: 64)
|
|
59
|
+
source = Yeptris.read_input(source).to_s
|
|
60
|
+
schema_id = schema == :compat_11 ? FFI::SCHEMA_11_COMPAT : FFI::SCHEMA_12_CORE
|
|
61
|
+
plans = compile(desc)
|
|
62
|
+
loop do
|
|
63
|
+
result = run(source, schema_id, plans, capacity)
|
|
64
|
+
return result unless result == :grow
|
|
65
|
+
capacity *= 2
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# #184 (lutaml-model KV path): zip Schema columns into record
|
|
70
|
+
# hashes ready for Serializable.instantiate. Mapping root → one
|
|
71
|
+
# hash; sequence-of-mappings → one hash per element; nested
|
|
72
|
+
# mappings → nested hashes. Returns Array<Hash> always. when_attribute
|
|
73
|
+
# / polymorphic stay out of scope (interpretive fallback).
|
|
74
|
+
def load_records(source, schema: :core_12, desc:, capacity: 64)
|
|
75
|
+
cols = load(source, schema: schema, desc: desc, capacity: capacity)
|
|
76
|
+
zip_records(desc, cols)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def zip_records(desc, cols)
|
|
80
|
+
root = desc[0] || {}
|
|
81
|
+
case root[:kind]
|
|
82
|
+
when :sequence
|
|
83
|
+
child_start = root[:child_index] || 1
|
|
84
|
+
child_count = root[:child_count] || 0
|
|
85
|
+
return [] if child_count.zero?
|
|
86
|
+
|
|
87
|
+
child = desc[child_start]
|
|
88
|
+
if child && child[:kind] == :mapping
|
|
89
|
+
field_start = child[:child_index] || (child_start + 1)
|
|
90
|
+
field_count = child[:child_count] || 0
|
|
91
|
+
fields = desc[field_start, field_count] || []
|
|
92
|
+
field_cols = cols[field_start, field_count] || []
|
|
93
|
+
nrows = field_cols.map(&:length).max || 0
|
|
94
|
+
Array.new(nrows) do |r|
|
|
95
|
+
h = {}
|
|
96
|
+
fields.each_with_index do |f, i|
|
|
97
|
+
next unless f[:wire_name]
|
|
98
|
+
col = field_cols[i] || []
|
|
99
|
+
# ABSENT keys are OMITTED, not nil-filled (lutaml-model's
|
|
100
|
+
# load-bearing edge: absent seeds defaults +
|
|
101
|
+
# using_default?; explicit nil is a present value with
|
|
102
|
+
# different render semantics). The C columns fill in
|
|
103
|
+
# document order — col.length > r means present.
|
|
104
|
+
next if col.length <= r
|
|
105
|
+
|
|
106
|
+
h[f[:wire_name].to_sym] = materialize_field(f, desc, cols, col[r])
|
|
107
|
+
end
|
|
108
|
+
h
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
(cols[child_start] || []).map { |v| { value: v } }
|
|
112
|
+
end
|
|
113
|
+
when :mapping
|
|
114
|
+
field_start = root[:child_index] || 1
|
|
115
|
+
field_count = root[:child_count] || 0
|
|
116
|
+
fields = desc[field_start, field_count] || []
|
|
117
|
+
field_cols = cols[field_start, field_count] || []
|
|
118
|
+
h = {}
|
|
119
|
+
fields.each_with_index do |f, i|
|
|
120
|
+
next unless f[:wire_name]
|
|
121
|
+
col = field_cols[i] || []
|
|
122
|
+
next if col.empty? # absent: omitted, not nil-filled (#184)
|
|
123
|
+
|
|
124
|
+
h[f[:wire_name].to_sym] = materialize_field(f, desc, cols, col[0])
|
|
125
|
+
end
|
|
126
|
+
[h]
|
|
127
|
+
else
|
|
128
|
+
[{ value: cols[0]&.first }]
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
private_class_method :zip_records
|
|
132
|
+
|
|
133
|
+
def materialize_field(field, desc, cols, cell)
|
|
134
|
+
case field[:kind]
|
|
135
|
+
when :mapping
|
|
136
|
+
start = field[:child_index] || 0
|
|
137
|
+
count = field[:child_count] || 0
|
|
138
|
+
return nil if count.zero? || cell.nil?
|
|
139
|
+
|
|
140
|
+
nested = {}
|
|
141
|
+
desc[start, count]&.each_with_index do |nf, i|
|
|
142
|
+
next unless nf[:wire_name]
|
|
143
|
+
ncol = cols[start + i] || []
|
|
144
|
+
nested[nf[:wire_name].to_sym] = ncol.is_a?(Array) ? (ncol[0] rescue cell) : cell
|
|
145
|
+
end
|
|
146
|
+
nested
|
|
147
|
+
when :sequence
|
|
148
|
+
cell.is_a?(Array) ? cell : Array(cell)
|
|
149
|
+
else
|
|
150
|
+
cell
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
private_class_method :materialize_field
|
|
154
|
+
|
|
155
|
+
def compile(desc)
|
|
156
|
+
desc.map do |n|
|
|
157
|
+
{ kind: KIND.fetch(n.fetch(:kind)),
|
|
158
|
+
type: TYPE.fetch(n[:type] || :str),
|
|
159
|
+
wire: n[:wire_name],
|
|
160
|
+
flags: (n[:required] ? REQUIRED : 0) | (n[:first_wins] ? FIRST_WINS : 0),
|
|
161
|
+
child_index: n[:child_index] || 0,
|
|
162
|
+
child_count: n[:child_count] || 0 }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
private_class_method :compile
|
|
166
|
+
|
|
167
|
+
def run(source, schema_id, plans, capacity) # rubocop:disable Metrics/MethodLength
|
|
168
|
+
n = plans.length
|
|
169
|
+
desc_buf = ::FFI::MemoryPointer.new(FFI::DescNode, n, true)
|
|
170
|
+
wires = []
|
|
171
|
+
plans.each_with_index do |p, i|
|
|
172
|
+
node = FFI::DescNode.new(desc_buf + i * FFI::DescNode.size)
|
|
173
|
+
if p[:wire]
|
|
174
|
+
wires << (wire = ::FFI::MemoryPointer.from_string(p[:wire]))
|
|
175
|
+
node[:wire_name] = wire
|
|
176
|
+
end
|
|
177
|
+
node[:kind] = p[:kind]
|
|
178
|
+
node[:type_tag] = p[:type]
|
|
179
|
+
node[:flags] = p[:flags]
|
|
180
|
+
node[:child_index] = p[:child_index]
|
|
181
|
+
node[:child_count] = p[:child_count]
|
|
182
|
+
node[:reserved] = 0
|
|
183
|
+
end
|
|
184
|
+
cols_buf = ::FFI::MemoryPointer.new(FFI::SchemaColumn, n, true)
|
|
185
|
+
data_ptrs = []
|
|
186
|
+
plans.each_with_index do |p, i|
|
|
187
|
+
col = FFI::SchemaColumn.new(cols_buf + i * FFI::SchemaColumn.size)
|
|
188
|
+
elem = ELEMENT_SIZE.fetch(p[:type], 16)
|
|
189
|
+
data = ::FFI::MemoryPointer.new(elem, capacity, true)
|
|
190
|
+
data_ptrs << data
|
|
191
|
+
col[:data] = data
|
|
192
|
+
col[:capacity] = capacity
|
|
193
|
+
col[:count] = 0
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
src = ::FFI::MemoryPointer.from_string(source) # NUL-safe copy for the C side
|
|
197
|
+
st = FFI.yeptris_schema_load(src, source.bytesize, schema_id, desc_buf, n,
|
|
198
|
+
FFI::DESC_ABI, cols_buf)
|
|
199
|
+
case st
|
|
200
|
+
when FFI::OK
|
|
201
|
+
plans.each_with_index.map { |p, i| drain(cols_buf, i, p, source) }
|
|
202
|
+
when FFI::ERROR_MEMORY
|
|
203
|
+
:grow
|
|
204
|
+
when FFI::SCHEMA_ERROR
|
|
205
|
+
msg = FFI.last_error_message
|
|
206
|
+
raise RequiredMissing, msg
|
|
207
|
+
else
|
|
208
|
+
raise Yeptris::ParseError, FFI.last_error_message
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
private_class_method :run
|
|
212
|
+
|
|
213
|
+
def drain(cols_buf, i, plan, source)
|
|
214
|
+
col = FFI::SchemaColumn.new(cols_buf + i * FFI::SchemaColumn.size)
|
|
215
|
+
count = col[:count]
|
|
216
|
+
data = col[:data]
|
|
217
|
+
case plan[:kind]
|
|
218
|
+
when KIND[:callback]
|
|
219
|
+
Array.new(count) do |k|
|
|
220
|
+
off = data.get_uint32(k * 16)
|
|
221
|
+
len = data.get_uint32(k * 16 + 4)
|
|
222
|
+
node_off = data.get_uint32(k * 16 + 8)
|
|
223
|
+
Span.new(off, len, node_off)
|
|
224
|
+
end
|
|
225
|
+
else
|
|
226
|
+
type = plan[:type]
|
|
227
|
+
Array.new(count) do |k|
|
|
228
|
+
case type
|
|
229
|
+
when TYPE[:int] then data.get_int64(k * 8)
|
|
230
|
+
when TYPE[:float] then data.get_double(k * 8)
|
|
231
|
+
when TYPE[:bool] then data.get_uchar(k) == 1
|
|
232
|
+
when TYPE[:null] then nil
|
|
233
|
+
when TYPE[:any]
|
|
234
|
+
rec = k * ANY_SIZE
|
|
235
|
+
case ANY_KIND[data.get_uint8(rec)]
|
|
236
|
+
when :int then data.get_int64(rec + 16)
|
|
237
|
+
when :float then data.get_double(rec + 16)
|
|
238
|
+
when :bool then data.get_uint8(rec + 3) == 1
|
|
239
|
+
when :null then nil
|
|
240
|
+
else # str/timestamp: the zero-copy span
|
|
241
|
+
source.byteslice(data.get_uint32(rec + 4), data.get_uint32(rec + 8))
|
|
242
|
+
end
|
|
243
|
+
else
|
|
244
|
+
off = data.get_uint32(k * 16)
|
|
245
|
+
len = data.get_uint32(k * 16 + 4)
|
|
246
|
+
source.byteslice(off, len)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
private_class_method :drain
|
|
252
|
+
end
|
|
253
|
+
end
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yeptris
|
|
4
|
+
# Value-stream materialization (TODO.impl/15 phase F): one drain of
|
|
5
|
+
# PRE-CONVERTED typed values from the C side, then a minimal Ruby
|
|
6
|
+
# walk — no per-scalar parsing (the number kernels already ran),
|
|
7
|
+
# key/value pairing rides the entries' is_key bit, anchors arrive
|
|
8
|
+
# as uniform entries decorating the value they bind. The Psych
|
|
9
|
+
# quirks re-decide from tag_id + the raw bytes every entry carries.
|
|
10
|
+
module ValueML
|
|
11
|
+
DOC = 0
|
|
12
|
+
V_NULL = 1
|
|
13
|
+
V_BOOL = 2
|
|
14
|
+
V_INT = 3
|
|
15
|
+
V_FLOAT = 4
|
|
16
|
+
V_STR = 5
|
|
17
|
+
|
|
18
|
+
# Psych's integer shape (scalar_scanner): sign optional, no
|
|
19
|
+
# leading zero, '_' / ',' separators only between digits. Beyond
|
|
20
|
+
# int64 the C resolver leaves the scalar a string; Psych
|
|
21
|
+
# materializes Integer (issue #31) — every walk rebuilds it.
|
|
22
|
+
PSYCH_INT_SHAPE = /\A[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*)\z/.freeze
|
|
23
|
+
V_TS = 6
|
|
24
|
+
SEQ_OPEN = 7
|
|
25
|
+
MAP_OPEN = 8
|
|
26
|
+
CLOSE = 9
|
|
27
|
+
ALIAS = 10
|
|
28
|
+
ANCHOR = 11
|
|
29
|
+
|
|
30
|
+
FIELDS = 7
|
|
31
|
+
VALUE_SIZE = 24
|
|
32
|
+
# kind, tag, is_key, b | off, len | pad4 | payload (INT/FLOAT bits)
|
|
33
|
+
UNPACK = "C4V2x4q<"
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
def load_all(yaml, schema: :compat_11)
|
|
38
|
+
yaml = Yeptris.read_input(yaml)
|
|
39
|
+
yaml = yaml.to_s
|
|
40
|
+
vals_p = ::FFI::MemoryPointer.new(:pointer)
|
|
41
|
+
count_p = ::FFI::MemoryPointer.new(:size_t)
|
|
42
|
+
arena_p = ::FFI::MemoryPointer.new(:pointer)
|
|
43
|
+
alen_p = ::FFI::MemoryPointer.new(:size_t)
|
|
44
|
+
st = FFI.yeptris_value_drain(
|
|
45
|
+
yaml, yaml.bytesize,
|
|
46
|
+
schema == :compat_11 ? FFI::SCHEMA_11_COMPAT : FFI::SCHEMA_12_CORE,
|
|
47
|
+
vals_p, count_p, arena_p, alen_p
|
|
48
|
+
)
|
|
49
|
+
raise ParseError, FFI.last_error_message if st != FFI::OK
|
|
50
|
+
|
|
51
|
+
vals = vals_p.read_pointer
|
|
52
|
+
arena = arena_p.read_pointer
|
|
53
|
+
begin
|
|
54
|
+
count = Yeptris::FFI.read_c_size_t(count_p)
|
|
55
|
+
flat = vals.read_bytes(count * VALUE_SIZE).unpack(UNPACK * count)
|
|
56
|
+
arena_bytes = arena.read_bytes(Yeptris::FFI.read_c_size_t(alen_p))
|
|
57
|
+
arena_bytes.force_encoding(Encoding::UTF_8)
|
|
58
|
+
walk(flat, arena_bytes, schema == :compat_11)
|
|
59
|
+
ensure
|
|
60
|
+
FFI.yeptris_value_free(vals, arena)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Columnar path (libyeptris > 0.1.1): whole columns unpack in one
|
|
65
|
+
# call each, and the walk indexes tight arrays (no 7-slot stride).
|
|
66
|
+
# Semantics are IDENTICAL to the record walk — the two bodies are
|
|
67
|
+
# maintained in lockstep; field-access shape is the only difference.
|
|
68
|
+
def load_all_columns(yaml, schema: :compat_11)
|
|
69
|
+
yaml = Yeptris.read_input(yaml)
|
|
70
|
+
yaml = yaml.to_s
|
|
71
|
+
cols = FFI::ValueColumns.new
|
|
72
|
+
st = FFI.yeptris_value_drain_columns(
|
|
73
|
+
yaml, yaml.bytesize,
|
|
74
|
+
schema == :compat_11 ? FFI::SCHEMA_11_COMPAT : FFI::SCHEMA_12_CORE,
|
|
75
|
+
cols
|
|
76
|
+
)
|
|
77
|
+
raise ParseError, FFI.last_error_message if st != FFI::OK
|
|
78
|
+
|
|
79
|
+
begin
|
|
80
|
+
n = cols[:count]
|
|
81
|
+
kinds = cols[:kinds].read_bytes(n).unpack("C*")
|
|
82
|
+
tags = cols[:tags].read_bytes(n).unpack("C*")
|
|
83
|
+
ikeys = cols[:is_keys].read_bytes(n).unpack("C*")
|
|
84
|
+
bools = cols[:bools].read_bytes(n).unpack("C*")
|
|
85
|
+
offs = cols[:offs].read_bytes(n * 4).unpack("V*")
|
|
86
|
+
lens = cols[:lens].read_bytes(n * 4).unpack("V*")
|
|
87
|
+
pays = cols[:payloads].read_bytes(n * 8).unpack("q<*")
|
|
88
|
+
arena_bytes = cols[:arena].read_bytes(cols[:arena_len])
|
|
89
|
+
arena_bytes.force_encoding(Encoding::UTF_8)
|
|
90
|
+
walk_columns(kinds, tags, ikeys, bools, offs, lens, pays, arena_bytes,
|
|
91
|
+
schema == :compat_11)
|
|
92
|
+
ensure
|
|
93
|
+
FFI.yeptris_value_free_columns(cols)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The Marshal fast path (TODO.restructure/21): the C side emits
|
|
98
|
+
# Ruby Marshal 4.8 bytes; one Marshal.load call (a core-C routine)
|
|
99
|
+
# materializes the whole object graph. ~10x faster than walking
|
|
100
|
+
# the columns in pure Ruby on JSON-shaped input; ~5x on YAML. Falls
|
|
101
|
+
# back to load_all_columns on the constructs the format cannot
|
|
102
|
+
# express (merge keys, timestamps — rare; the record walk handles
|
|
103
|
+
# them). Feature-detected: older libyeptris returns the walk.
|
|
104
|
+
def load_all_marshal(yaml, schema: :compat_11, mode: :first)
|
|
105
|
+
yaml = Yeptris.read_input(yaml)
|
|
106
|
+
yaml = yaml.to_s
|
|
107
|
+
out_p = ::FFI::MemoryPointer.new(:pointer)
|
|
108
|
+
olen_p = ::FFI::MemoryPointer.new(:size_t)
|
|
109
|
+
mode_id = mode == :all ? FFI::MARSHAL_ALL_DOCS : FFI::MARSHAL_FIRST_DOC
|
|
110
|
+
st = FFI.yeptris_marshal(
|
|
111
|
+
yaml, yaml.bytesize,
|
|
112
|
+
schema == :compat_11 ? FFI::SCHEMA_11_COMPAT : FFI::SCHEMA_12_CORE,
|
|
113
|
+
mode_id, out_p, olen_p
|
|
114
|
+
)
|
|
115
|
+
return nil if st == FFI::ERROR_UNSUPPORTED
|
|
116
|
+
raise ParseError, FFI.last_error_message if st != FFI::OK
|
|
117
|
+
buf = out_p.read_pointer
|
|
118
|
+
len = Yeptris::FFI.read_c_size_t(olen_p)
|
|
119
|
+
bytes = buf.read_bytes(len)
|
|
120
|
+
bytes.force_encoding(Encoding::ASCII_8BIT)
|
|
121
|
+
::Marshal.load(bytes)
|
|
122
|
+
ensure
|
|
123
|
+
FFI.yeptris_marshal_free(buf) if buf && !buf.null?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def load(yaml, schema: :compat_11)
|
|
127
|
+
docs = load_all(yaml, schema: schema)
|
|
128
|
+
docs.empty? ? nil : docs.first
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# field offsets in the unpacked 7-tuple
|
|
132
|
+
KIND = 0
|
|
133
|
+
TAG = 1
|
|
134
|
+
IS_KEY = 2
|
|
135
|
+
B = 3
|
|
136
|
+
OFF = 4
|
|
137
|
+
LEN = 5
|
|
138
|
+
P64 = 6
|
|
139
|
+
|
|
140
|
+
def walk(flat, arena, compat = true)
|
|
141
|
+
docs = []
|
|
142
|
+
stack = []
|
|
143
|
+
anchors = {}
|
|
144
|
+
pending_key = []
|
|
145
|
+
pending_key_tag = []
|
|
146
|
+
pending_anchor = nil
|
|
147
|
+
merge_target = []
|
|
148
|
+
|
|
149
|
+
i = 0
|
|
150
|
+
n = flat.length
|
|
151
|
+
while i < n
|
|
152
|
+
kind = flat[i + KIND]
|
|
153
|
+
case kind
|
|
154
|
+
when V_STR
|
|
155
|
+
text = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
156
|
+
# implicit-plain ':name' scans to a Symbol (Psych's
|
|
157
|
+
# ScalarScanner); quoted ':x' stays a String
|
|
158
|
+
v = if flat[i + B] == 1 && text.length > 18 &&
|
|
159
|
+
PSYCH_INT_SHAPE.match?(text)
|
|
160
|
+
text.delete(",_").to_i
|
|
161
|
+
elsif flat[i + B] == 1 && text.length > 1 &&
|
|
162
|
+
text.start_with?(":") && !text.start_with?("::")
|
|
163
|
+
text[1..].to_sym
|
|
164
|
+
else
|
|
165
|
+
text
|
|
166
|
+
end
|
|
167
|
+
if pending_anchor
|
|
168
|
+
anchors[pending_anchor] = v
|
|
169
|
+
pending_anchor = nil
|
|
170
|
+
end
|
|
171
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
172
|
+
when V_INT
|
|
173
|
+
v = flat[i + P64]
|
|
174
|
+
if pending_anchor
|
|
175
|
+
anchors[pending_anchor] = v
|
|
176
|
+
pending_anchor = nil
|
|
177
|
+
end
|
|
178
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
179
|
+
when V_FLOAT
|
|
180
|
+
text = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
181
|
+
# Psych's float grammar requires the dot (or an inf/nan
|
|
182
|
+
# word, or sexagesimal ':') — exponent-only forms are
|
|
183
|
+
# Strings even when the compat tag says FLOAT
|
|
184
|
+
v = if !compat || text.include?(".") || text.include?(":") || text.start_with?(".")
|
|
185
|
+
[flat[i + P64]].pack("q<").unpack1("E")
|
|
186
|
+
else
|
|
187
|
+
text
|
|
188
|
+
end
|
|
189
|
+
if pending_anchor
|
|
190
|
+
anchors[pending_anchor] = v
|
|
191
|
+
pending_anchor = nil
|
|
192
|
+
end
|
|
193
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
194
|
+
when V_BOOL
|
|
195
|
+
text = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
196
|
+
# Psych quirk: single-char y/n stay Strings
|
|
197
|
+
v = text.length == 1 ? text : flat[i + B] == 1
|
|
198
|
+
if pending_anchor
|
|
199
|
+
anchors[pending_anchor] = v
|
|
200
|
+
pending_anchor = nil
|
|
201
|
+
end
|
|
202
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
203
|
+
when V_NULL
|
|
204
|
+
# the pending anchor binds to the null (an anchored empty
|
|
205
|
+
# scalar) — without this it would leak onto the next value
|
|
206
|
+
if pending_anchor
|
|
207
|
+
anchors[pending_anchor] = nil
|
|
208
|
+
pending_anchor = nil
|
|
209
|
+
end
|
|
210
|
+
slot(docs, stack, pending_key, pending_key_tag, nil, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
211
|
+
when V_TS
|
|
212
|
+
v = Materializer.parse_timestamp(arena.byteslice(flat[i + OFF], flat[i + LEN]))
|
|
213
|
+
if pending_anchor
|
|
214
|
+
anchors[pending_anchor] = v
|
|
215
|
+
pending_anchor = nil
|
|
216
|
+
end
|
|
217
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
218
|
+
when MAP_OPEN
|
|
219
|
+
h = {}
|
|
220
|
+
if pending_anchor
|
|
221
|
+
anchors[pending_anchor] = h
|
|
222
|
+
pending_anchor = nil
|
|
223
|
+
end
|
|
224
|
+
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, h, merge_target,
|
|
225
|
+
flat[i + IS_KEY], flat[i + TAG]))
|
|
226
|
+
stack.push(h)
|
|
227
|
+
pending_key.push(nil)
|
|
228
|
+
pending_key_tag.push(nil)
|
|
229
|
+
when SEQ_OPEN
|
|
230
|
+
a = []
|
|
231
|
+
if pending_anchor
|
|
232
|
+
anchors[pending_anchor] = a
|
|
233
|
+
pending_anchor = nil
|
|
234
|
+
end
|
|
235
|
+
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, a, merge_target,
|
|
236
|
+
flat[i + IS_KEY], flat[i + TAG]))
|
|
237
|
+
stack.push(a)
|
|
238
|
+
pending_key.push(nil)
|
|
239
|
+
pending_key_tag.push(nil)
|
|
240
|
+
when CLOSE
|
|
241
|
+
closed = stack.pop
|
|
242
|
+
pending_key.pop
|
|
243
|
+
pending_key_tag.pop
|
|
244
|
+
target = merge_target.pop
|
|
245
|
+
Materializer.merge_into(target, closed) if target
|
|
246
|
+
when DOC
|
|
247
|
+
docs.push(nil)
|
|
248
|
+
when ALIAS
|
|
249
|
+
v = anchors[arena.byteslice(flat[i + OFF], flat[i + LEN])]
|
|
250
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
251
|
+
when ANCHOR
|
|
252
|
+
pending_anchor = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
253
|
+
end
|
|
254
|
+
i += FIELDS
|
|
255
|
+
end
|
|
256
|
+
docs
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# The columnar twin of walk above (lockstep: same semantics, same
|
|
260
|
+
# order; fields come from tight per-kind arrays, stride 1).
|
|
261
|
+
def walk_columns(kinds, tags, ikeys, bools, offs, lens, pays, arena, compat = true)
|
|
262
|
+
docs = []
|
|
263
|
+
stack = []
|
|
264
|
+
anchors = {}
|
|
265
|
+
pending_key = []
|
|
266
|
+
pending_key_tag = []
|
|
267
|
+
pending_anchor = nil
|
|
268
|
+
merge_target = []
|
|
269
|
+
|
|
270
|
+
i = 0
|
|
271
|
+
n = kinds.length
|
|
272
|
+
while i < n
|
|
273
|
+
# the dominant shape: a str:str pair inside one map — place
|
|
274
|
+
# both directly, same conversions as the STR arm (the ':sym'
|
|
275
|
+
# scan rides is_key's text; anchors/merges fall through)
|
|
276
|
+
if kinds[i] == V_STR && ikeys[i] == 1 && i + 1 < n &&
|
|
277
|
+
kinds[i + 1] == V_STR && ikeys[i + 1] == 0 &&
|
|
278
|
+
pending_anchor.nil? && !stack.empty? && stack.last.is_a?(Hash)
|
|
279
|
+
kt = arena.byteslice(offs[i], lens[i])
|
|
280
|
+
if kt != "<<"
|
|
281
|
+
key = if bools[i] == 1 && kt.length > 1 && kt.start_with?(":") &&
|
|
282
|
+
!kt.start_with?("::")
|
|
283
|
+
kt[1..].to_sym
|
|
284
|
+
else
|
|
285
|
+
kt
|
|
286
|
+
end
|
|
287
|
+
vt = arena.byteslice(offs[i + 1], lens[i + 1])
|
|
288
|
+
stack.last[key] =
|
|
289
|
+
if bools[i + 1] == 1 && vt.length > 1 && vt.start_with?(":") &&
|
|
290
|
+
!vt.start_with?("::")
|
|
291
|
+
vt[1..].to_sym
|
|
292
|
+
else
|
|
293
|
+
vt
|
|
294
|
+
end
|
|
295
|
+
i += 2
|
|
296
|
+
next
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
case kinds[i]
|
|
300
|
+
when V_STR
|
|
301
|
+
text = arena.byteslice(offs[i], lens[i])
|
|
302
|
+
v = if bools[i] == 1 && text.length > 18 &&
|
|
303
|
+
PSYCH_INT_SHAPE.match?(text)
|
|
304
|
+
text.delete(",_").to_i
|
|
305
|
+
elsif bools[i] == 1 && text.length > 1 &&
|
|
306
|
+
text.start_with?(":") && !text.start_with?("::")
|
|
307
|
+
text[1..].to_sym
|
|
308
|
+
else
|
|
309
|
+
text
|
|
310
|
+
end
|
|
311
|
+
if pending_anchor
|
|
312
|
+
anchors[pending_anchor] = v
|
|
313
|
+
pending_anchor = nil
|
|
314
|
+
end
|
|
315
|
+
# inline the dominant placement (Hash parent, value slot,
|
|
316
|
+
# non-merge key): a slot() call per pair was measurable
|
|
317
|
+
if !stack.empty?
|
|
318
|
+
parent = stack.last
|
|
319
|
+
if parent.is_a?(Hash) && ikeys[i] == 0 && (k = pending_key[-1]) && k != "<<"
|
|
320
|
+
parent[k] = v
|
|
321
|
+
pending_key[-1] = nil
|
|
322
|
+
else
|
|
323
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
324
|
+
end
|
|
325
|
+
else
|
|
326
|
+
docs[-1] = v
|
|
327
|
+
end
|
|
328
|
+
when V_INT
|
|
329
|
+
v = pays[i]
|
|
330
|
+
if pending_anchor
|
|
331
|
+
anchors[pending_anchor] = v
|
|
332
|
+
pending_anchor = nil
|
|
333
|
+
end
|
|
334
|
+
if !stack.empty?
|
|
335
|
+
parent = stack.last
|
|
336
|
+
if parent.is_a?(Hash) && ikeys[i] == 0 && (k = pending_key[-1]) && k != "<<"
|
|
337
|
+
parent[k] = v
|
|
338
|
+
pending_key[-1] = nil
|
|
339
|
+
else
|
|
340
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
341
|
+
end
|
|
342
|
+
else
|
|
343
|
+
docs[-1] = v
|
|
344
|
+
end
|
|
345
|
+
when V_FLOAT
|
|
346
|
+
text = arena.byteslice(offs[i], lens[i])
|
|
347
|
+
v = if !compat || text.include?(".") || text.include?(":") || text.start_with?(".")
|
|
348
|
+
[pays[i]].pack("q<").unpack1("E")
|
|
349
|
+
else
|
|
350
|
+
text
|
|
351
|
+
end
|
|
352
|
+
if pending_anchor
|
|
353
|
+
anchors[pending_anchor] = v
|
|
354
|
+
pending_anchor = nil
|
|
355
|
+
end
|
|
356
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
357
|
+
when V_BOOL
|
|
358
|
+
text = arena.byteslice(offs[i], lens[i])
|
|
359
|
+
v = text.length == 1 ? text : bools[i] == 1
|
|
360
|
+
if pending_anchor
|
|
361
|
+
anchors[pending_anchor] = v
|
|
362
|
+
pending_anchor = nil
|
|
363
|
+
end
|
|
364
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
365
|
+
when V_NULL
|
|
366
|
+
if pending_anchor
|
|
367
|
+
anchors[pending_anchor] = nil
|
|
368
|
+
pending_anchor = nil
|
|
369
|
+
end
|
|
370
|
+
slot(docs, stack, pending_key, pending_key_tag, nil, merge_target, ikeys[i], tags[i])
|
|
371
|
+
when V_TS
|
|
372
|
+
v = Materializer.parse_timestamp(arena.byteslice(offs[i], lens[i]))
|
|
373
|
+
if pending_anchor
|
|
374
|
+
anchors[pending_anchor] = v
|
|
375
|
+
pending_anchor = nil
|
|
376
|
+
end
|
|
377
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
378
|
+
when MAP_OPEN
|
|
379
|
+
h = {}
|
|
380
|
+
if pending_anchor
|
|
381
|
+
anchors[pending_anchor] = h
|
|
382
|
+
pending_anchor = nil
|
|
383
|
+
end
|
|
384
|
+
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, h, merge_target,
|
|
385
|
+
ikeys[i], tags[i]))
|
|
386
|
+
stack.push(h)
|
|
387
|
+
pending_key.push(nil)
|
|
388
|
+
pending_key_tag.push(nil)
|
|
389
|
+
when SEQ_OPEN
|
|
390
|
+
a = []
|
|
391
|
+
if pending_anchor
|
|
392
|
+
anchors[pending_anchor] = a
|
|
393
|
+
pending_anchor = nil
|
|
394
|
+
end
|
|
395
|
+
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, a, merge_target,
|
|
396
|
+
ikeys[i], tags[i]))
|
|
397
|
+
stack.push(a)
|
|
398
|
+
pending_key.push(nil)
|
|
399
|
+
pending_key_tag.push(nil)
|
|
400
|
+
when CLOSE
|
|
401
|
+
closed = stack.pop
|
|
402
|
+
pending_key.pop
|
|
403
|
+
pending_key_tag.pop
|
|
404
|
+
target = merge_target.pop
|
|
405
|
+
Materializer.merge_into(target, closed) if target
|
|
406
|
+
when DOC
|
|
407
|
+
docs.push(nil)
|
|
408
|
+
when ALIAS
|
|
409
|
+
v = anchors[arena.byteslice(offs[i], lens[i])]
|
|
410
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
411
|
+
when ANCHOR
|
|
412
|
+
pending_anchor = arena.byteslice(offs[i], lens[i])
|
|
413
|
+
end
|
|
414
|
+
i += 1
|
|
415
|
+
end
|
|
416
|
+
docs
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Places a completed value: document root, sequence entry, or a
|
|
420
|
+
# map's key (is_key) / value (completing the pending pair).
|
|
421
|
+
# Returns the merge TARGET when the value is a still-empty
|
|
422
|
+
# container placed under a '<<' key — the open-site caller pushes
|
|
423
|
+
# it so the matching CLOSE merges into it (an inline map's
|
|
424
|
+
# contents arrive after its open); scalar merges apply now.
|
|
425
|
+
def slot(docs, stack, pending_key, pending_key_tag, v, _merge_target, is_key, tag)
|
|
426
|
+
if stack.empty?
|
|
427
|
+
docs[-1] = v
|
|
428
|
+
return nil
|
|
429
|
+
end
|
|
430
|
+
parent = stack.last
|
|
431
|
+
if parent.is_a?(Array)
|
|
432
|
+
parent.push(v)
|
|
433
|
+
return nil
|
|
434
|
+
end
|
|
435
|
+
if is_key == 1
|
|
436
|
+
pending_key[-1] = v
|
|
437
|
+
pending_key_tag[-1] = tag
|
|
438
|
+
return nil
|
|
439
|
+
end
|
|
440
|
+
key = pending_key[-1]
|
|
441
|
+
pending_key[-1] = nil
|
|
442
|
+
if key == "<<" && pending_key_tag[-1] == 9 # TAG_MERGE
|
|
443
|
+
if (v.is_a?(Hash) || v.is_a?(Array)) && v.empty?
|
|
444
|
+
parent # deferred: contents arrive after the open
|
|
445
|
+
else
|
|
446
|
+
Materializer.merge_into(parent, v)
|
|
447
|
+
nil
|
|
448
|
+
end
|
|
449
|
+
else
|
|
450
|
+
parent[key] = v
|
|
451
|
+
nil
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
end
|