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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fab04bdd5f0236d59b5d139d410c095d9c99c8aacccaa18c464324c58b0eef33
4
- data.tar.gz: 9ba0ca96dd534843e8238921f3de634c7f18b051f90b3bde901be74b950a6403
3
+ metadata.gz: 5900164f14038b2ef4e6c7b3e7764c335faf037559f9743099787e2ffcadb2f7
4
+ data.tar.gz: b539d2090254785c57c6dfd5a08e46200b668b0061619691e779a5c186a3978a
5
5
  SHA512:
6
- metadata.gz: 2281939709069aaf555ed5df307cc613b304ad236d7c3fc13cca8a4fc28a0ee8ca35f41d7dd3b46c9db71a8a088d90565e4ccfd7aa25c4512a83d46b41bddd79
7
- data.tar.gz: 9731da1240594c945e1eba4f7eef86e3ec10556c23ab0c84617b2109c17c2c08e08d2c64c5cd558103c07abe2ef93da00735ca6c42703e61fbae4eac7a0ad64c
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
- MARSHAL = begin
198
- MARSHAL_ALL_DOCS = 0
199
- MARSHAL_FIRST_DOC = 1
200
- attach_function :yeptris_marshal,
201
- %i[pointer size_t int int pointer pointer], :int
202
- attach_function :yeptris_marshal_node,
203
- %i[yeptris_node pointer pointer], :int
204
- attach_function :yeptris_marshal_free, [:pointer], :void
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
- CBOR_EX = begin
242
- attach_function :yeptris_cbor_decode, %i[pointer size_t uint32 yeptris_status_out],
243
- :yeptris_document
244
- callback :cbor_item_cb, %i[pointer yeptris_document size_t], :int
245
- attach_function :yeptris_cbor_decode_sequence,
246
- %i[pointer size_t uint32 cbor_item_cb pointer yeptris_status_out], :size_t
247
- attach_function :yeptris_cbor_encode, %i[yeptris_document uint32 pointer], :pointer
248
- attach_function :yeptris_cbor_encode_sequence,
249
- %i[pointer size_t uint32 pointer], :pointer
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
@@ -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
- obj.force_encoding(::Encoding::UTF_8) if obj.encoding == ::Encoding::ASCII_8BIT && !obj.ascii_only?
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
- name = tag.split(":").last
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 then scalar(obj, STYLE_BY_NAME[string_style(obj)], emit, blob, off)
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.11.1".freeze
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
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(yeptris
6
- VERSION 0.6.11
6
+ VERSION 0.6.12
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -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
- if (root_closed) {
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
- d->handles = yep_hpool_create(sys);
484
- if (d->handles == NULL) {
485
- yep_pool_destroy(pool);
486
- yep_free(sys, d);
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
- p->head = yep_block_new(sys, block_size);
92
- p->blocks = 1;
93
- if (p->head == NULL) {
94
- yep_free(sys, p);
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
- ((yep_event*)ev)->tag_id = yep_tag_from_uri((const char*)ev->tag.p, ev->tag.len);
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->handles, sizeof(yeptris_node), 16);
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->handles, sizeof(yeptris_node), 16);
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
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yeptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.11.1
4
+ version: 0.6.12.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.