yeptris 0.6.11.1 → 0.6.12.3
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 +63 -25
- data/lib/yeptris/materializer.rb +3 -0
- data/lib/yeptris/node.rb +2 -0
- data/lib/yeptris/psych/drop_in.rb +21 -0
- data/lib/yeptris/psych.rb +15 -2
- data/lib/yeptris/yaml.rb +24 -1
- data/lib/yeptris.rb +1 -1
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/yeptris/build.c +6 -1
- data/vendor/libyeptris/src/yeptris/dom/dom.c +23 -10
- data/vendor/libyeptris/src/yeptris/dom/dom.h +2 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +11 -3
- data/vendor/libyeptris/src/yeptris/marshal.c +4 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +13 -6
- data/vendor/libyeptris/src/yeptris/parse/engine.c +10 -1
- data/vendor/libyeptris/src/yeptris/parse.c +2 -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: 5900164f14038b2ef4e6c7b3e7764c335faf037559f9743099787e2ffcadb2f7
|
|
4
|
+
data.tar.gz: b539d2090254785c57c6dfd5a08e46200b668b0061619691e779a5c186a3978a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d837519cc77193279158957e4757a0b2ee6f250467d293422676135b6aee89058eac9dbb48c6b7a5f3eb79ca77cc1e076399f93935ed75d652612244b845db74
|
|
7
|
+
data.tar.gz: 5eff7ba15bd3c4314508d226ebaa4d2e3aa0eb0a7cc2887645078acbbecd193cbaa443af6877891b226224719b8955700f8567ec8a0446cc42fa713349993864
|
data/lib/yeptris/ffi.rb
CHANGED
|
@@ -28,6 +28,32 @@ module Yeptris
|
|
|
28
28
|
MSG
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
class << self
|
|
32
|
+
# #138's class, hit again by the oiml-cs report: a stale
|
|
33
|
+
# libyeptris on the machine (the Windows base-name dedupe makes
|
|
34
|
+
# a system copy win) lacks newer symbols, and a raising attach
|
|
35
|
+
# aborts this file mid-evaluation — every constant after it
|
|
36
|
+
# never defines, so the next autoload dies with
|
|
37
|
+
# 'uninitialized constant Yeptris::FFI::NODE_SCALAR'. Attaches
|
|
38
|
+
# now record the miss and define a raising stub of the same
|
|
39
|
+
# name: the load completes, optional surfaces fail with a clear
|
|
40
|
+
# message at USE, and the ESSENTIAL self-check at the bottom of
|
|
41
|
+
# this file raises the friendly load error for truly old
|
|
42
|
+
# engines.
|
|
43
|
+
def attach_function(name, args, ret, opts = {})
|
|
44
|
+
super
|
|
45
|
+
rescue ::FFI::NotFoundError, ::FFI::TypeError
|
|
46
|
+
define_singleton_method(name) do |*_a, **_kw|
|
|
47
|
+
raise ::FFI::NotFoundError,
|
|
48
|
+
"yeptris: #{name} is unavailable — the loaded " \
|
|
49
|
+
"libyeptris is older than this gem. Update the engine " \
|
|
50
|
+
"library (or clear the stale copy shadowing it)."
|
|
51
|
+
end
|
|
52
|
+
(@missing ||= []) << name
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
31
57
|
|
|
32
58
|
typedef :pointer, :yeptris_document
|
|
33
59
|
typedef :pointer, :yeptris_node
|
|
@@ -194,18 +220,14 @@ module Yeptris
|
|
|
194
220
|
# instead of walking per-value records in pure Ruby. Same feature-
|
|
195
221
|
# detect discipline as the columnar drain (older libraries fall
|
|
196
222
|
# back to the record walk).
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
true
|
|
206
|
-
rescue ::FFI::NotFoundError
|
|
207
|
-
false
|
|
208
|
-
end
|
|
223
|
+
MARSHAL_ALL_DOCS = 0
|
|
224
|
+
MARSHAL_FIRST_DOC = 1
|
|
225
|
+
attach_function :yeptris_marshal,
|
|
226
|
+
%i[pointer size_t int int pointer pointer], :int
|
|
227
|
+
attach_function :yeptris_marshal_node,
|
|
228
|
+
%i[yeptris_node pointer pointer], :int
|
|
229
|
+
attach_function :yeptris_marshal_free, [:pointer], :void
|
|
230
|
+
MARSHAL = !(@missing ||= []).include?(:yeptris_marshal_node)
|
|
209
231
|
|
|
210
232
|
# bulk build (TODO.impl/15 phase D): one call raises a document
|
|
211
233
|
BUILD_SCALAR = 1
|
|
@@ -238,19 +260,15 @@ module Yeptris
|
|
|
238
260
|
|
|
239
261
|
# CBOR (RFC 8949, TODO.cbor): absent on libraries before the
|
|
240
262
|
# codec's release — the CBOR module feature-detects
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
true
|
|
251
|
-
rescue ::FFI::NotFoundError
|
|
252
|
-
false
|
|
253
|
-
end
|
|
263
|
+
attach_function :yeptris_cbor_decode, %i[pointer size_t uint32 yeptris_status_out],
|
|
264
|
+
:yeptris_document
|
|
265
|
+
callback :cbor_item_cb, %i[pointer yeptris_document size_t], :int
|
|
266
|
+
attach_function :yeptris_cbor_decode_sequence,
|
|
267
|
+
%i[pointer size_t uint32 cbor_item_cb pointer yeptris_status_out], :size_t
|
|
268
|
+
attach_function :yeptris_cbor_encode, %i[yeptris_document uint32 pointer], :pointer
|
|
269
|
+
attach_function :yeptris_cbor_encode_sequence,
|
|
270
|
+
%i[pointer size_t uint32 pointer], :pointer
|
|
271
|
+
CBOR_EX = !(@missing ||= []).include?(:yeptris_cbor_decode)
|
|
254
272
|
|
|
255
273
|
# Owned char* results (serialize*): one reader, freed exactly once.
|
|
256
274
|
# The release goes through yeptris_free (libyeptris's own
|
|
@@ -337,6 +355,7 @@ module Yeptris
|
|
|
337
355
|
TAG_BOOL = 3
|
|
338
356
|
TAG_NULL = 4
|
|
339
357
|
TAG_TIMESTAMP = 5
|
|
358
|
+
TAG_BINARY = 8
|
|
340
359
|
|
|
341
360
|
# the schema-descriptor API (issue #238; TODO.restructure/83)
|
|
342
361
|
DESC_ABI = 1
|
|
@@ -367,5 +386,24 @@ module Yeptris
|
|
|
367
386
|
ERROR_ARG = 6
|
|
368
387
|
ERROR_UNSUPPORTED = 7
|
|
369
388
|
ERROR_INTERNAL = 8
|
|
389
|
+
# The load-time essentials (the read path + the ownership
|
|
390
|
+
# contract): absent on engines far older than the optional
|
|
391
|
+
# surfaces — raise the friendly error once every constant has
|
|
392
|
+
# fully defined, so no partial-module state leaks into autoloads
|
|
393
|
+
# (the #138 class, closed at the root).
|
|
394
|
+
ESSENTIAL = %i[
|
|
395
|
+
yeptris_version yeptris_last_error yeptris_parse yeptris_parse_ex
|
|
396
|
+
yeptris_parse_json yeptris_document_free yeptris_document_count
|
|
397
|
+
yeptris_document_root yeptris_node_kind yeptris_node_value
|
|
398
|
+
yeptris_node_tag_id yeptris_node_style yeptris_document_new
|
|
399
|
+
yeptris_document_set_root yeptris_serialize yeptris_serialize_ex
|
|
400
|
+
].freeze
|
|
401
|
+
missing_core = ESSENTIAL & (@missing ||= [])
|
|
402
|
+
raise ::FFI::NotFoundError,
|
|
403
|
+
"yeptris: the loaded libyeptris is older than this gem " \
|
|
404
|
+
"requires (missing: #{missing_core.join(', ')}). Update the " \
|
|
405
|
+
"engine library, or clear the stale copy shadowing it." \
|
|
406
|
+
unless missing_core.empty?
|
|
407
|
+
|
|
370
408
|
end
|
|
371
409
|
end
|
data/lib/yeptris/materializer.rb
CHANGED
|
@@ -37,6 +37,7 @@ module Yeptris
|
|
|
37
37
|
TAG_BOOL = Yeptris::FFI::TAG_BOOL
|
|
38
38
|
TAG_NULL = Yeptris::FFI::TAG_NULL
|
|
39
39
|
TAG_TIMESTAMP = Yeptris::FFI::TAG_TIMESTAMP
|
|
40
|
+
TAG_BINARY = Yeptris::FFI::TAG_BINARY
|
|
40
41
|
TAG_MERGE = 9 # YEPTRIS_TAG_MERGE (resolve.h): a plain '<<' key
|
|
41
42
|
|
|
42
43
|
INF_WORDS = {
|
|
@@ -152,6 +153,8 @@ module Yeptris
|
|
|
152
153
|
float_or_string(value)
|
|
153
154
|
when TAG_TIMESTAMP
|
|
154
155
|
parse_timestamp(value)
|
|
156
|
+
when TAG_BINARY
|
|
157
|
+
value.unpack1("m") # psych to_ruby: !binary -> decode64, ASCII-8BIT (#168)
|
|
155
158
|
else
|
|
156
159
|
value
|
|
157
160
|
end
|
data/lib/yeptris/node.rb
CHANGED
|
@@ -121,6 +121,7 @@ class Yeptris::Node
|
|
|
121
121
|
Yeptris::FFI::TAG_BOOL => :bool,
|
|
122
122
|
Yeptris::FFI::TAG_NULL => :null,
|
|
123
123
|
Yeptris::FFI::TAG_TIMESTAMP => :timestamp,
|
|
124
|
+
Yeptris::FFI::TAG_BINARY => :binary,
|
|
124
125
|
}.freeze
|
|
125
126
|
|
|
126
127
|
def tag_id
|
|
@@ -351,6 +352,7 @@ class Yeptris::Node
|
|
|
351
352
|
to_f
|
|
352
353
|
end
|
|
353
354
|
when :timestamp then ::Yeptris::Materializer.parse_timestamp(value)
|
|
355
|
+
when :binary then value.unpack1("m") # ASCII-8BIT bytes (#168)
|
|
354
356
|
else
|
|
355
357
|
# beyond int64 the C resolver leaves plain scalars :str; Psych
|
|
356
358
|
# materializes Integer (issue #31) — rebuild from the text
|
|
@@ -22,6 +22,27 @@ end
|
|
|
22
22
|
Object.class_eval { remove_const(:Psych) } if defined?(::Psych) && !::Psych.equal?(Yeptris::Psych)
|
|
23
23
|
::Psych = Yeptris::Psych
|
|
24
24
|
|
|
25
|
+
# #167: third-party gems feature-gate on Psych::VERSION at require
|
|
26
|
+
# time (mechanize 2.14.1's cookie-jar reads it in a class body). The
|
|
27
|
+
# face reports the STDLIB version it replaces: ORIGINAL's when psych
|
|
28
|
+
# was preloaded, else the default-gem spec's version (local lookup —
|
|
29
|
+
# requiring stdlib psych post-rebind is censored by the header).
|
|
30
|
+
unless Yeptris::Psych.const_defined?(:VERSION, false)
|
|
31
|
+
version =
|
|
32
|
+
if Yeptris::Psych.const_defined?(:ORIGINAL, false)
|
|
33
|
+
Yeptris::Psych::ORIGINAL::VERSION
|
|
34
|
+
else
|
|
35
|
+
spec = begin
|
|
36
|
+
::Gem.loaded_specs["psych"] ||
|
|
37
|
+
::Gem::Specification.find_by_name("psych", ::Gem::Requirement.default)
|
|
38
|
+
rescue ::StandardError, ::Gem::Exception, ::LoadError
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
spec&.version&.to_s
|
|
42
|
+
end
|
|
43
|
+
Yeptris::Psych.const_set(:VERSION, version) if version
|
|
44
|
+
end
|
|
45
|
+
|
|
25
46
|
# yaml-first boot order: ::YAML still references the ORIGINAL stdlib
|
|
26
47
|
# module object, whose singleton methods route through stdlib's parse
|
|
27
48
|
# stream — the UTF-8 tagging (and every other normalization) never
|
data/lib/yeptris/psych.rb
CHANGED
|
@@ -165,7 +165,18 @@ module Yeptris
|
|
|
165
165
|
def force_utf8_scalars(obj)
|
|
166
166
|
case obj
|
|
167
167
|
when ::String
|
|
168
|
-
|
|
168
|
+
# valid_encoding? on an ASCII-8BIT string is ALWAYS true
|
|
169
|
+
# (it is bytes) — the discriminator is whether the bytes
|
|
170
|
+
# READ as UTF-8: #135's case (UTF-8 scalars the C side
|
|
171
|
+
# materialized as BINARY) re-tags; DECODED !binary bytes
|
|
172
|
+
# that are not UTF-8 stay BINARY (#168). Binary content that
|
|
173
|
+
# happens to be valid UTF-8 re-tags — noted divergence from
|
|
174
|
+
# stdlib, which keeps BINARY; real payloads (zips, bodies)
|
|
175
|
+
# never are.
|
|
176
|
+
if obj.encoding == ::Encoding::ASCII_8BIT && !obj.ascii_only?
|
|
177
|
+
probe = obj.dup.force_encoding(::Encoding::UTF_8)
|
|
178
|
+
obj.force_encoding(::Encoding::UTF_8) if probe.valid_encoding?
|
|
179
|
+
end
|
|
169
180
|
obj
|
|
170
181
|
when ::Hash
|
|
171
182
|
obj.each { |k, v| force_utf8_scalars(k); force_utf8_scalars(v) }
|
|
@@ -307,7 +318,9 @@ module Yeptris
|
|
|
307
318
|
when :scalar, :mapping, :sequence
|
|
308
319
|
tag = node.tag
|
|
309
320
|
unless tag.nil?
|
|
310
|
-
|
|
321
|
+
# both tag spellings pass: the URI form's last segment and
|
|
322
|
+
# the short "!binary" shorthand (#168's cassettes carry it)
|
|
323
|
+
name = tag.split(":").last.sub(/\A!/, "")
|
|
311
324
|
unless %w[str int float bool null timestamp seq map merge value
|
|
312
325
|
binary].include?(name)
|
|
313
326
|
raise DisallowedClass, name
|
data/lib/yeptris/yaml.rb
CHANGED
|
@@ -192,7 +192,19 @@ module Yeptris
|
|
|
192
192
|
obj.each { |e| place(e, emit, blob, off, seen) }
|
|
193
193
|
emit.call(STOP, 0, 0, 0)
|
|
194
194
|
end
|
|
195
|
-
when String
|
|
195
|
+
when String
|
|
196
|
+
if obj.encoding == ::Encoding::ASCII_8BIT && !obj.ascii_only?
|
|
197
|
+
# #168: psych visit_String's binary branch — strict base64
|
|
198
|
+
# + the short !binary tag + literal style (Builder
|
|
199
|
+
# build_string carries the same branch; the tagged-literal
|
|
200
|
+
# writer keeps the block form)
|
|
201
|
+
scalar([obj].pack("m0"), STYLE_LIT, emit, blob, off)
|
|
202
|
+
emit.call(BUILD_TAG, 0, off[0], 7)
|
|
203
|
+
blob << "!binary"
|
|
204
|
+
off[0] += 7
|
|
205
|
+
else
|
|
206
|
+
scalar(obj, STYLE_BY_NAME[string_style(obj)], emit, blob, off)
|
|
207
|
+
end
|
|
196
208
|
when Symbol then scalar(":#{obj}", STYLE_PLAIN, emit, blob, off)
|
|
197
209
|
when Integer then scalar(obj.to_s, STYLE_PLAIN, emit, blob, off)
|
|
198
210
|
when Float then scalar(float_text(obj), STYLE_PLAIN, emit, blob, off)
|
|
@@ -351,6 +363,17 @@ module Yeptris
|
|
|
351
363
|
# visit_String matrix) serves both builders; this side carries
|
|
352
364
|
# the style onto the per-node DOM.
|
|
353
365
|
def build_string(doc, s)
|
|
366
|
+
# psych 5.5 visit_String's binary branch, verbatim: an
|
|
367
|
+
# ASCII-8BIT string with non-ASCII bytes emits as strict base64
|
|
368
|
+
# (pack('m0'), never wrapped) under the short !binary tag with
|
|
369
|
+
# literal style — the writer keeps the block form for tagged
|
|
370
|
+
# literals (#168). BOTH dump paths converge here (the top-level
|
|
371
|
+
# scalar fast route and the YAMLTree visitor).
|
|
372
|
+
if s.encoding == ::Encoding::ASCII_8BIT && !s.ascii_only?
|
|
373
|
+
bin = new_scalar(doc, [s].pack("m0"), :force_lit)
|
|
374
|
+
bin.set_tag("!binary")
|
|
375
|
+
return bin
|
|
376
|
+
end
|
|
354
377
|
case BulkBuilder.string_style(s)
|
|
355
378
|
when :single then new_scalar(doc, s, :force_str_sq)
|
|
356
379
|
when :double then new_scalar(doc, s, :force_str)
|
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.12.3".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
|
|
@@ -221,12 +221,16 @@ YEPTRIS_API YeptrisStatus yeptris_document_build(YeptrisDocument handle,
|
|
|
221
221
|
int depth = 0;
|
|
222
222
|
uint32_t root = UINT32_MAX;
|
|
223
223
|
uint32_t root_closed = 0;
|
|
224
|
+
uint32_t root_tagged = 0; /* one trailing TAG on the closed root (#168) */
|
|
224
225
|
uint32_t pending_key = UINT32_MAX; /* map frame's buffered key */
|
|
225
226
|
uint32_t last_id = UINT32_MAX; /* TAG applies here (#300) */
|
|
226
227
|
YeptrisStatus rc = YEPTRIS_OK;
|
|
227
228
|
for (size_t i = 0; i < count && rc == YEPTRIS_OK; i++) {
|
|
228
229
|
const YeptrisBuildEntry* e = &entries[i];
|
|
229
|
-
|
|
230
|
+
/* A closed SCALAR root admits exactly one trailing TAG —
|
|
231
|
+
* psych's binary root is `--- !binary |-` (#168); everything
|
|
232
|
+
* after (more ops, a second tag) still rejects */
|
|
233
|
+
if (root_closed && !(e->op == YEPTRIS_BUILD_TAG && !root_tagged && last_id == root)) {
|
|
230
234
|
rc = YEPTRIS_ERROR_PARSE; /* the document ended; more entries */
|
|
231
235
|
break;
|
|
232
236
|
}
|
|
@@ -283,6 +287,7 @@ YEPTRIS_API YeptrisStatus yeptris_document_build(YeptrisDocument handle,
|
|
|
283
287
|
rc = YEPTRIS_ERROR_MEMORY;
|
|
284
288
|
break;
|
|
285
289
|
}
|
|
290
|
+
root_tagged = root_closed;
|
|
286
291
|
continue;
|
|
287
292
|
default:
|
|
288
293
|
rc = YEPTRIS_ERROR_PARSE; /* unknown op */
|
|
@@ -480,17 +480,11 @@ yep_dom* yep_dom_create(const yep_allocator* sys) {
|
|
|
480
480
|
memset(d, 0, sizeof(*d));
|
|
481
481
|
d->sys = sys;
|
|
482
482
|
d->pool = pool;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return NULL;
|
|
488
|
-
}
|
|
483
|
+
/* #157: the handle pool is lazy — two allocations and a mutex per
|
|
484
|
+
* document that decode-only consumers (CBOR load, serialize, free)
|
|
485
|
+
* never touch; yep_dom_handles() materializes it on first use */
|
|
486
|
+
d->handles = NULL;
|
|
489
487
|
if (yep_mutex_init(&d->midx.mu) != 0) {
|
|
490
|
-
yep_free(d->sys, d->anchor_nodes);
|
|
491
|
-
yep_free(d->sys, d->mut_att);
|
|
492
|
-
yep_free(d->sys, d->mut_depth);
|
|
493
|
-
yep_hpool_destroy(d->handles);
|
|
494
488
|
yep_pool_destroy(pool);
|
|
495
489
|
yep_free(sys, d);
|
|
496
490
|
return NULL;
|
|
@@ -499,6 +493,25 @@ yep_dom* yep_dom_create(const yep_allocator* sys) {
|
|
|
499
493
|
return d;
|
|
500
494
|
}
|
|
501
495
|
|
|
496
|
+
/* Lazy handle pool, mirroring the map-index's discipline (mapindex.c):
|
|
497
|
+
* EVERY access rides the dom's lazy-init mutex — read-only sharing
|
|
498
|
+
* means multiple threads may race the FIRST handle creation on a
|
|
499
|
+
* shared document (Threads.ReadOnlySharing; the TSAN catch on the
|
|
500
|
+
* first cut). The uncontended lock costs less than the hpool's own
|
|
501
|
+
* per-allocation lock that follows. */
|
|
502
|
+
struct yep_hpool* yep_dom_handles(yep_dom* d) {
|
|
503
|
+
if (d == NULL || !d->midx.mu_ready) {
|
|
504
|
+
return NULL;
|
|
505
|
+
}
|
|
506
|
+
yep_mutex_lock(&d->midx.mu);
|
|
507
|
+
if (d->handles == NULL) {
|
|
508
|
+
d->handles = yep_hpool_create(d->sys);
|
|
509
|
+
}
|
|
510
|
+
struct yep_hpool* h = d->handles;
|
|
511
|
+
yep_mutex_unlock(&d->midx.mu);
|
|
512
|
+
return h;
|
|
513
|
+
}
|
|
514
|
+
|
|
502
515
|
void yep_dom_destroy(yep_dom* d) {
|
|
503
516
|
if (d == NULL) {
|
|
504
517
|
return;
|
|
@@ -197,6 +197,8 @@ void dom_mut_set_depth(yep_dom* d, uint32_t id, uint16_t depth);
|
|
|
197
197
|
|
|
198
198
|
/* thread-safe handle arena (see hpool.c) */
|
|
199
199
|
struct yep_hpool* yep_hpool_create(const yep_allocator* sys);
|
|
200
|
+
/* Lazy handle-pool acquisition (#157): NULL dom or OOM stays NULL. */
|
|
201
|
+
struct yep_hpool* yep_dom_handles(yep_dom* d);
|
|
200
202
|
void yep_hpool_destroy(struct yep_hpool* p);
|
|
201
203
|
void* yep_hpool_alloc(struct yep_hpool* p, size_t size, size_t align);
|
|
202
204
|
|
|
@@ -313,7 +313,8 @@ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
|
|
|
313
313
|
enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL };
|
|
314
314
|
#define YEP_SC_MEMO_MIN 32u /* bytes; derive-below, memoize-above */
|
|
315
315
|
|
|
316
|
-
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in
|
|
316
|
+
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in,
|
|
317
|
+
int has_tag) {
|
|
317
318
|
int multiline = (len > 0 && memchr(p, '\n', len) != NULL);
|
|
318
319
|
int blockable = 0;
|
|
319
320
|
if (multiline && !as_key) {
|
|
@@ -343,6 +344,13 @@ static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key,
|
|
|
343
344
|
if (len == 0) {
|
|
344
345
|
return YEP_SC_DQ;
|
|
345
346
|
}
|
|
347
|
+
/* A TAGGED block scalar keeps the block form even single-line
|
|
348
|
+
* (psych's !binary emits `!binary |-` + one base64 line; an
|
|
349
|
+
* untagged single-line literal re-emits plain — libyaml parity,
|
|
350
|
+
* the #290 family) */
|
|
351
|
+
if (has_tag) {
|
|
352
|
+
return YEP_SC_LITERAL;
|
|
353
|
+
}
|
|
346
354
|
sty = 1;
|
|
347
355
|
}
|
|
348
356
|
switch (sty) {
|
|
@@ -388,14 +396,14 @@ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int a
|
|
|
388
396
|
* memoized long scalars, in visit order either way. */
|
|
389
397
|
if (w->sc_dec != NULL && len >= YEP_SC_MEMO_MIN) {
|
|
390
398
|
if (w->dry) {
|
|
391
|
-
route = sc_route(w, p, len, as_key, n->style);
|
|
399
|
+
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0);
|
|
392
400
|
w->sc_dec[w->sc_i] = route;
|
|
393
401
|
} else {
|
|
394
402
|
route = w->sc_dec[w->sc_i];
|
|
395
403
|
}
|
|
396
404
|
w->sc_i++;
|
|
397
405
|
} else {
|
|
398
|
-
route = sc_route(w, p, len, as_key, n->style);
|
|
406
|
+
route = sc_route(w, p, len, as_key, n->style, n->tag.len > 0);
|
|
399
407
|
}
|
|
400
408
|
switch (route) {
|
|
401
409
|
case YEP_SC_NOTHING:
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
#include <yeptris/error.h>
|
|
42
42
|
#include <yeptris/marshal.h>
|
|
43
|
+
#include <yeptris/resolve.h> /* YEPTRIS_TAG_BINARY (#168) */
|
|
43
44
|
|
|
44
45
|
typedef struct {
|
|
45
46
|
const char* name; /* into the value arena */
|
|
@@ -487,6 +488,9 @@ static int pre_scan(const yep_value_ctx* c, uint64_t** counts_out, size_t* nopen
|
|
|
487
488
|
case YEP_V_TIMESTAMP:
|
|
488
489
|
return -2;
|
|
489
490
|
case YEP_V_STR:
|
|
491
|
+
if (vals[i].tag_id == YEPTRIS_TAG_BINARY) {
|
|
492
|
+
return -2; /* !binary: the walk base64-decodes (#168) */
|
|
493
|
+
}
|
|
490
494
|
if (vals[i].is_key == 1 && vals[i].len == 2 && c->arena[vals[i].off] == '<' &&
|
|
491
495
|
c->arena[vals[i].off + 1] == '<') {
|
|
492
496
|
return -2; /* merge key: the walk resolves these */
|
|
@@ -45,6 +45,15 @@ void* yep_pool_alloc(yep_pool* pool, size_t size, size_t align) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
yep_block* b = pool->head;
|
|
48
|
+
if (b == NULL) { /* the lazy first block (#157) */
|
|
49
|
+
b = yep_block_new(pool->sys, pool->block_size);
|
|
50
|
+
if (b == NULL) {
|
|
51
|
+
return NULL;
|
|
52
|
+
}
|
|
53
|
+
b->next = NULL;
|
|
54
|
+
pool->head = b;
|
|
55
|
+
pool->blocks = 1;
|
|
56
|
+
}
|
|
48
57
|
uintptr_t base = (uintptr_t)b + sizeof(yep_block);
|
|
49
58
|
uintptr_t start = yep_round_up(base + b->used, align);
|
|
50
59
|
|
|
@@ -88,12 +97,10 @@ yep_pool* yep_pool_create(const yep_allocator* sys, size_t block_size) {
|
|
|
88
97
|
}
|
|
89
98
|
p->sys = sys;
|
|
90
99
|
p->block_size = block_size;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return NULL;
|
|
96
|
-
}
|
|
100
|
+
/* #157: the first block is lazy — one fewer eager allocation per
|
|
101
|
+
* document; pool_alloc materializes it on first use */
|
|
102
|
+
p->head = NULL;
|
|
103
|
+
p->blocks = 0;
|
|
97
104
|
return p;
|
|
98
105
|
}
|
|
99
106
|
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
#include "resolve/resolver.h"
|
|
28
28
|
#include "scan/json.h"
|
|
29
29
|
#include "scan/scan.h"
|
|
30
|
+
#include <yeptris/resolve.h> /* YEPTRIS_TAG_* names (#168) */
|
|
30
31
|
|
|
31
32
|
#define YEP_MAX_DEPTH 1000
|
|
32
33
|
#define YEP_MAX_FOLD_LINES 8192
|
|
@@ -158,7 +159,15 @@ static int emit_now(yep_engine* e, const yep_event* ev) {
|
|
|
158
159
|
/* Resolution happens exactly here: every scalar path flows
|
|
159
160
|
* through this choke point (the resolver is the typing SSOT) */
|
|
160
161
|
if (ev->tag.len > 0) {
|
|
161
|
-
|
|
162
|
+
yep_tag_id tid = yep_tag_from_uri((const char*)ev->tag.p, ev->tag.len);
|
|
163
|
+
/* psych's own dumped form is the LOCAL !binary shorthand,
|
|
164
|
+
* and stdlib to_ruby accepts it beside the core URI (#168)
|
|
165
|
+
* — compat schemas resolve it to BINARY as well */
|
|
166
|
+
if (tid == YEPTRIS_TAG_CUSTOM && ev->tag.len == 7 &&
|
|
167
|
+
memcmp(ev->tag.p, "!binary", 7) == 0 && e->resolver == yep_resolver_compat11()) {
|
|
168
|
+
tid = YEPTRIS_TAG_BINARY;
|
|
169
|
+
}
|
|
170
|
+
((yep_event*)ev)->tag_id = tid;
|
|
162
171
|
} else if (ev->implicit && e->resolver != NULL) {
|
|
163
172
|
((yep_event*)ev)->tag_id = e->resolver->resolve(
|
|
164
173
|
e->resolver->ctx, (const char*)ev->value.p, (uint32_t)ev->value.len);
|
|
@@ -375,7 +375,7 @@ YEPTRIS_API size_t yeptris_document_count(YeptrisDocument handle) {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
yeptris_node* yep_handle_new(yeptris_document* doc, uint32_t id) {
|
|
378
|
-
yeptris_node* n = yep_hpool_alloc(doc->dom
|
|
378
|
+
yeptris_node* n = yep_hpool_alloc(yep_dom_handles(doc->dom), sizeof(yeptris_node), 16);
|
|
379
379
|
if (n == NULL) {
|
|
380
380
|
return NULL;
|
|
381
381
|
}
|
|
@@ -552,7 +552,7 @@ static YeptrisNode wrap(yeptris_node* base, uint32_t id) {
|
|
|
552
552
|
/* Handles live in the document's thread-safe arena: one
|
|
553
553
|
* document_free reclaims every handle ever handed out (zero-leak
|
|
554
554
|
* contract) while read-only sharing stays safe (TODO.impl/19) */
|
|
555
|
-
yeptris_node* n = yep_hpool_alloc(base->doc->dom
|
|
555
|
+
yeptris_node* n = yep_hpool_alloc(yep_dom_handles(base->doc->dom), sizeof(yeptris_node), 16);
|
|
556
556
|
if (n == NULL) {
|
|
557
557
|
return NULL;
|
|
558
558
|
}
|