yeptris 0.1.0 → 0.1.1.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/lib/yeptris/ffi.rb +18 -0
- data/lib/yeptris/valueml.rb +171 -12
- data/lib/yeptris/version.rb +1 -1
- data/lib/yeptris/yaml.rb +14 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 321301694ee81cae03622b737dce8d9603d6fffa87cacc280967c3bf7e9e397b
|
|
4
|
+
data.tar.gz: '09892fb7c54261dc8d46c5354a1c55052e6bf1333552bc7632e36cd433706c66'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d3e550bfe02ed9abff59332131fa13ae93e0e4ab7bf46b4cbcf8f57670d7592ad2494b2c9aa17c331a504ab3fdca48274bf585cc429a282a8c4d507fd3c5a04
|
|
7
|
+
data.tar.gz: b66b1001c328be6081ddbd53d790a9fbfbff4d4881da5ee74edea0d2065cb5266d876cdc1f3bb4ddcacbc6c41b2087661218f2fb11cd4763136a380073dfbbe2
|
data/lib/yeptris/ffi.rb
CHANGED
|
@@ -118,6 +118,24 @@ module Yeptris
|
|
|
118
118
|
%i[pointer size_t int pointer pointer pointer pointer], :int
|
|
119
119
|
attach_function :yeptris_value_free, %i[pointer pointer], :void
|
|
120
120
|
|
|
121
|
+
# Columnar drain (libyeptris > 0.1.1): the same stream as parallel
|
|
122
|
+
# typed buffers, one carved allocation. Feature-detected — the
|
|
123
|
+
# record API is the fallback on older libraries.
|
|
124
|
+
COLUMNS = begin
|
|
125
|
+
attach_function :yeptris_value_drain_columns, %i[pointer size_t int pointer], :int
|
|
126
|
+
attach_function :yeptris_value_free_columns, [:pointer], :void
|
|
127
|
+
true
|
|
128
|
+
rescue ::FFI::NotFoundError
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class ValueColumns < ::FFI::Struct
|
|
133
|
+
layout :count, :size_t, :arena_len, :size_t,
|
|
134
|
+
:payloads, :pointer, :offs, :pointer, :lens, :pointer,
|
|
135
|
+
:kinds, :pointer, :tags, :pointer, :is_keys, :pointer,
|
|
136
|
+
:bools, :pointer, :arena, :pointer
|
|
137
|
+
end
|
|
138
|
+
|
|
121
139
|
# bulk build (TODO.impl/15 phase D): one call raises a document
|
|
122
140
|
BUILD_SCALAR = 1
|
|
123
141
|
BUILD_SEQ = 2
|
data/lib/yeptris/valueml.rb
CHANGED
|
@@ -55,6 +55,38 @@ module Yeptris
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
# Columnar path (libyeptris > 0.1.1): whole columns unpack in one
|
|
59
|
+
# call each, and the walk indexes tight arrays (no 7-slot stride).
|
|
60
|
+
# Semantics are IDENTICAL to the record walk — the two bodies are
|
|
61
|
+
# maintained in lockstep; field-access shape is the only difference.
|
|
62
|
+
def load_all_columns(yaml, schema: :compat_11)
|
|
63
|
+
yaml = yaml.read if yaml.respond_to?(:read)
|
|
64
|
+
yaml = yaml.to_s
|
|
65
|
+
cols = FFI::ValueColumns.new
|
|
66
|
+
st = FFI.yeptris_value_drain_columns(
|
|
67
|
+
yaml, yaml.bytesize,
|
|
68
|
+
schema == :compat_11 ? FFI::SCHEMA_11_COMPAT : FFI::SCHEMA_12_CORE,
|
|
69
|
+
cols
|
|
70
|
+
)
|
|
71
|
+
raise ParseError, FFI.last_error_message if st != FFI::OK
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
n = cols[:count]
|
|
75
|
+
kinds = cols[:kinds].read_bytes(n).unpack("C*")
|
|
76
|
+
tags = cols[:tags].read_bytes(n).unpack("C*")
|
|
77
|
+
ikeys = cols[:is_keys].read_bytes(n).unpack("C*")
|
|
78
|
+
bools = cols[:bools].read_bytes(n).unpack("C*")
|
|
79
|
+
offs = cols[:offs].read_bytes(n * 4).unpack("V*")
|
|
80
|
+
lens = cols[:lens].read_bytes(n * 4).unpack("V*")
|
|
81
|
+
pays = cols[:payloads].read_bytes(n * 8).unpack("q<*")
|
|
82
|
+
arena_bytes = cols[:arena].read_bytes(cols[:arena_len])
|
|
83
|
+
arena_bytes.force_encoding(Encoding::UTF_8)
|
|
84
|
+
walk_columns(kinds, tags, ikeys, bools, offs, lens, pays, arena_bytes)
|
|
85
|
+
ensure
|
|
86
|
+
FFI.yeptris_value_free_columns(cols)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
58
90
|
def load(yaml, schema: :compat_11)
|
|
59
91
|
docs = load_all(yaml, schema: schema)
|
|
60
92
|
docs.empty? ? nil : docs.first
|
|
@@ -97,14 +129,14 @@ module Yeptris
|
|
|
97
129
|
anchors[pending_anchor] = v
|
|
98
130
|
pending_anchor = nil
|
|
99
131
|
end
|
|
100
|
-
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat, i)
|
|
132
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
101
133
|
when V_INT
|
|
102
134
|
v = flat[i + P64]
|
|
103
135
|
if pending_anchor
|
|
104
136
|
anchors[pending_anchor] = v
|
|
105
137
|
pending_anchor = nil
|
|
106
138
|
end
|
|
107
|
-
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat, i)
|
|
139
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
108
140
|
when V_FLOAT
|
|
109
141
|
text = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
110
142
|
# Psych's float grammar requires the dot (or an inf/nan
|
|
@@ -119,7 +151,7 @@ module Yeptris
|
|
|
119
151
|
anchors[pending_anchor] = v
|
|
120
152
|
pending_anchor = nil
|
|
121
153
|
end
|
|
122
|
-
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat, i)
|
|
154
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
123
155
|
when V_BOOL
|
|
124
156
|
text = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
125
157
|
# Psych quirk: single-char y/n stay Strings
|
|
@@ -128,16 +160,16 @@ module Yeptris
|
|
|
128
160
|
anchors[pending_anchor] = v
|
|
129
161
|
pending_anchor = nil
|
|
130
162
|
end
|
|
131
|
-
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat, i)
|
|
163
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
132
164
|
when V_NULL
|
|
133
|
-
slot(docs, stack, pending_key, pending_key_tag, nil, merge_target, flat, i)
|
|
165
|
+
slot(docs, stack, pending_key, pending_key_tag, nil, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
134
166
|
when V_TS
|
|
135
167
|
v = Materializer.parse_timestamp(arena.byteslice(flat[i + OFF], flat[i + LEN]))
|
|
136
168
|
if pending_anchor
|
|
137
169
|
anchors[pending_anchor] = v
|
|
138
170
|
pending_anchor = nil
|
|
139
171
|
end
|
|
140
|
-
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat, i)
|
|
172
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
141
173
|
when MAP_OPEN
|
|
142
174
|
h = {}
|
|
143
175
|
if pending_anchor
|
|
@@ -145,7 +177,7 @@ module Yeptris
|
|
|
145
177
|
pending_anchor = nil
|
|
146
178
|
end
|
|
147
179
|
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, h, merge_target,
|
|
148
|
-
flat, i))
|
|
180
|
+
flat[i + IS_KEY], flat[i + TAG]))
|
|
149
181
|
stack.push(h)
|
|
150
182
|
pending_key.push(nil)
|
|
151
183
|
pending_key_tag.push(nil)
|
|
@@ -156,7 +188,7 @@ module Yeptris
|
|
|
156
188
|
pending_anchor = nil
|
|
157
189
|
end
|
|
158
190
|
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, a, merge_target,
|
|
159
|
-
flat, i))
|
|
191
|
+
flat[i + IS_KEY], flat[i + TAG]))
|
|
160
192
|
stack.push(a)
|
|
161
193
|
pending_key.push(nil)
|
|
162
194
|
pending_key_tag.push(nil)
|
|
@@ -170,7 +202,7 @@ module Yeptris
|
|
|
170
202
|
docs.push(nil)
|
|
171
203
|
when ALIAS
|
|
172
204
|
v = anchors[arena.byteslice(flat[i + OFF], flat[i + LEN])]
|
|
173
|
-
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat, i)
|
|
205
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, flat[i + IS_KEY], flat[i + TAG])
|
|
174
206
|
when ANCHOR
|
|
175
207
|
pending_anchor = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
176
208
|
end
|
|
@@ -179,13 +211,140 @@ module Yeptris
|
|
|
179
211
|
docs
|
|
180
212
|
end
|
|
181
213
|
|
|
214
|
+
# The columnar twin of walk above (lockstep: same semantics, same
|
|
215
|
+
# order; fields come from tight per-kind arrays, stride 1).
|
|
216
|
+
def walk_columns(kinds, tags, ikeys, bools, offs, lens, pays, arena)
|
|
217
|
+
docs = []
|
|
218
|
+
stack = []
|
|
219
|
+
anchors = {}
|
|
220
|
+
pending_key = []
|
|
221
|
+
pending_key_tag = []
|
|
222
|
+
pending_anchor = nil
|
|
223
|
+
merge_target = []
|
|
224
|
+
|
|
225
|
+
i = 0
|
|
226
|
+
n = kinds.length
|
|
227
|
+
while i < n
|
|
228
|
+
case kinds[i]
|
|
229
|
+
when V_STR
|
|
230
|
+
text = arena.byteslice(offs[i], lens[i])
|
|
231
|
+
v = if bools[i] == 1 && text.length > 1 &&
|
|
232
|
+
text.start_with?(":") && !text.start_with?("::")
|
|
233
|
+
text[1..].to_sym
|
|
234
|
+
else
|
|
235
|
+
text
|
|
236
|
+
end
|
|
237
|
+
if pending_anchor
|
|
238
|
+
anchors[pending_anchor] = v
|
|
239
|
+
pending_anchor = nil
|
|
240
|
+
end
|
|
241
|
+
# inline the dominant placement (Hash parent, value slot,
|
|
242
|
+
# non-merge key): a slot() call per pair was measurable
|
|
243
|
+
if !stack.empty?
|
|
244
|
+
parent = stack.last
|
|
245
|
+
if parent.is_a?(Hash) && ikeys[i] == 0 && (k = pending_key[-1]) && k != "<<"
|
|
246
|
+
parent[k] = v
|
|
247
|
+
pending_key[-1] = nil
|
|
248
|
+
else
|
|
249
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
250
|
+
end
|
|
251
|
+
else
|
|
252
|
+
docs[-1] = v
|
|
253
|
+
end
|
|
254
|
+
when V_INT
|
|
255
|
+
v = pays[i]
|
|
256
|
+
if pending_anchor
|
|
257
|
+
anchors[pending_anchor] = v
|
|
258
|
+
pending_anchor = nil
|
|
259
|
+
end
|
|
260
|
+
if !stack.empty?
|
|
261
|
+
parent = stack.last
|
|
262
|
+
if parent.is_a?(Hash) && ikeys[i] == 0 && (k = pending_key[-1]) && k != "<<"
|
|
263
|
+
parent[k] = v
|
|
264
|
+
pending_key[-1] = nil
|
|
265
|
+
else
|
|
266
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
267
|
+
end
|
|
268
|
+
else
|
|
269
|
+
docs[-1] = v
|
|
270
|
+
end
|
|
271
|
+
when V_FLOAT
|
|
272
|
+
text = arena.byteslice(offs[i], lens[i])
|
|
273
|
+
v = if text.include?(".") || text.include?(":") || text.start_with?(".")
|
|
274
|
+
[pays[i]].pack("q<").unpack1("E")
|
|
275
|
+
else
|
|
276
|
+
text
|
|
277
|
+
end
|
|
278
|
+
if pending_anchor
|
|
279
|
+
anchors[pending_anchor] = v
|
|
280
|
+
pending_anchor = nil
|
|
281
|
+
end
|
|
282
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
283
|
+
when V_BOOL
|
|
284
|
+
text = arena.byteslice(offs[i], lens[i])
|
|
285
|
+
v = text.length == 1 ? text : bools[i] == 1
|
|
286
|
+
if pending_anchor
|
|
287
|
+
anchors[pending_anchor] = v
|
|
288
|
+
pending_anchor = nil
|
|
289
|
+
end
|
|
290
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
291
|
+
when V_NULL
|
|
292
|
+
slot(docs, stack, pending_key, pending_key_tag, nil, merge_target, ikeys[i], tags[i])
|
|
293
|
+
when V_TS
|
|
294
|
+
v = Materializer.parse_timestamp(arena.byteslice(offs[i], lens[i]))
|
|
295
|
+
if pending_anchor
|
|
296
|
+
anchors[pending_anchor] = v
|
|
297
|
+
pending_anchor = nil
|
|
298
|
+
end
|
|
299
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
300
|
+
when MAP_OPEN
|
|
301
|
+
h = {}
|
|
302
|
+
if pending_anchor
|
|
303
|
+
anchors[pending_anchor] = h
|
|
304
|
+
pending_anchor = nil
|
|
305
|
+
end
|
|
306
|
+
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, h, merge_target,
|
|
307
|
+
ikeys[i], tags[i]))
|
|
308
|
+
stack.push(h)
|
|
309
|
+
pending_key.push(nil)
|
|
310
|
+
pending_key_tag.push(nil)
|
|
311
|
+
when SEQ_OPEN
|
|
312
|
+
a = []
|
|
313
|
+
if pending_anchor
|
|
314
|
+
anchors[pending_anchor] = a
|
|
315
|
+
pending_anchor = nil
|
|
316
|
+
end
|
|
317
|
+
merge_target.push(slot(docs, stack, pending_key, pending_key_tag, a, merge_target,
|
|
318
|
+
ikeys[i], tags[i]))
|
|
319
|
+
stack.push(a)
|
|
320
|
+
pending_key.push(nil)
|
|
321
|
+
pending_key_tag.push(nil)
|
|
322
|
+
when CLOSE
|
|
323
|
+
closed = stack.pop
|
|
324
|
+
pending_key.pop
|
|
325
|
+
pending_key_tag.pop
|
|
326
|
+
target = merge_target.pop
|
|
327
|
+
Materializer.merge_into(target, closed) if target
|
|
328
|
+
when DOC
|
|
329
|
+
docs.push(nil)
|
|
330
|
+
when ALIAS
|
|
331
|
+
v = anchors[arena.byteslice(offs[i], lens[i])]
|
|
332
|
+
slot(docs, stack, pending_key, pending_key_tag, v, merge_target, ikeys[i], tags[i])
|
|
333
|
+
when ANCHOR
|
|
334
|
+
pending_anchor = arena.byteslice(offs[i], lens[i])
|
|
335
|
+
end
|
|
336
|
+
i += 1
|
|
337
|
+
end
|
|
338
|
+
docs
|
|
339
|
+
end
|
|
340
|
+
|
|
182
341
|
# Places a completed value: document root, sequence entry, or a
|
|
183
342
|
# map's key (is_key) / value (completing the pending pair).
|
|
184
343
|
# Returns the merge TARGET when the value is a still-empty
|
|
185
344
|
# container placed under a '<<' key — the open-site caller pushes
|
|
186
345
|
# it so the matching CLOSE merges into it (an inline map's
|
|
187
346
|
# contents arrive after its open); scalar merges apply now.
|
|
188
|
-
def slot(docs, stack, pending_key, pending_key_tag, v, _merge_target,
|
|
347
|
+
def slot(docs, stack, pending_key, pending_key_tag, v, _merge_target, is_key, tag)
|
|
189
348
|
if stack.empty?
|
|
190
349
|
docs[-1] = v
|
|
191
350
|
return nil
|
|
@@ -195,9 +354,9 @@ module Yeptris
|
|
|
195
354
|
parent.push(v)
|
|
196
355
|
return nil
|
|
197
356
|
end
|
|
198
|
-
if
|
|
357
|
+
if is_key == 1
|
|
199
358
|
pending_key[-1] = v
|
|
200
|
-
pending_key_tag[-1] =
|
|
359
|
+
pending_key_tag[-1] = tag
|
|
201
360
|
return nil
|
|
202
361
|
end
|
|
203
362
|
key = pending_key[-1]
|
data/lib/yeptris/version.rb
CHANGED
data/lib/yeptris/yaml.rb
CHANGED
|
@@ -11,12 +11,24 @@ module Yeptris
|
|
|
11
11
|
# schema: :compat_11 selects Psych/libyaml implicit typing
|
|
12
12
|
# (yes/no, 0o/octal, sexagesimal); :core_12 (default) is YAML 1.2.
|
|
13
13
|
def load(yaml, schema: :compat_11)
|
|
14
|
-
|
|
14
|
+
docs = _drain_all(yaml, schema)
|
|
15
|
+
docs.empty? ? nil : docs.first
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
# Every document in the stream, in order.
|
|
18
19
|
def load_stream(yaml, schema: :compat_11)
|
|
19
|
-
|
|
20
|
+
_drain_all(yaml, schema)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The columnar drain when the loaded libyeptris has it (>= 0.1.2
|
|
24
|
+
# era builds), the record drain otherwise — one code path, the
|
|
25
|
+
# fastest the library offers.
|
|
26
|
+
def _drain_all(yaml, schema)
|
|
27
|
+
if FFI::COLUMNS
|
|
28
|
+
ValueML.load_all_columns(yaml, schema: schema)
|
|
29
|
+
else
|
|
30
|
+
ValueML.load_all(yaml, schema: schema)
|
|
31
|
+
end
|
|
20
32
|
end
|
|
21
33
|
|
|
22
34
|
# The Psych-suite port's spelling (spec/psych/): compat typing.
|