yeptris 0.6.12.1 → 0.6.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffeaeb76cbc3a0a575cf5fce09e5ceff42ae233fa38125c4fb575eb15c2aafab
4
- data.tar.gz: d6667ffc31e29fd13f97d7bd5378b6a3611c39f849f7a69075d9e90c6e262df6
3
+ metadata.gz: c5dd96eb5583028d0da7d38ee4addaf108b8a2495696aacc6f68c43117773da3
4
+ data.tar.gz: b489c5bfc4b41d9cb3d8b836ac4b88f62aaeab5856788412301cde79ee0dfd2f
5
5
  SHA512:
6
- metadata.gz: 52298af429a38b41b55adf972d89156e5c214367debc1ccf618fd8e1a438414004861c8b6a7e9a1cc1e5235c68ba07a734ae580da668b3b7751b708102c9d46c
7
- data.tar.gz: c40301fda3424c9ef0d3d314a71dbf05b9a50af488c12b470f14cb8523912416ceb762de6e73ca6b44a97ac04b549d565113b688333320dabe9f3a2861895e50
6
+ metadata.gz: 4d91f1cdb572d444d4f7a43aec756400375581b3fa7bb1f6bbf62e9ad134ffa392fb1a763f11daffb07ae865e4046b6379a15b7f57eec5fb72c842bb580aee52
7
+ data.tar.gz: 7ac70b81ca8cf423226b48f8e6422686dc6edd9593a94bc82ebc7d0470ee40a9de9f28b4bd0f4ddf1c85ee1e132156fa1d889db8a1af9a788a6f5384f60c08c6
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
@@ -368,5 +386,24 @@ module Yeptris
368
386
  ERROR_ARG = 6
369
387
  ERROR_UNSUPPORTED = 7
370
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
+
371
408
  end
372
409
  end
@@ -9,13 +9,37 @@ module Yeptris
9
9
  module_function
10
10
 
11
11
  KIND = { scalar: 0, sequence: 1, mapping: 2, callback: 3 }.freeze
12
- TYPE = { str: 0, int: 1, float: 2, bool: 3, null: 4 }.freeze
12
+ TYPE = { str: 0, int: 1, float: 2, bool: 3, null: 4, any: 5 }.freeze
13
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
14
23
 
15
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
16
40
  class RequiredMissing < Error; end
17
41
 
18
- ELEMENT_SIZE = { 0 => 16, 1 => 8, 2 => 8, 3 => 1, 4 => 1 }.freeze # by TYPE value
42
+ ELEMENT_SIZE = { 0 => 16, 1 => 8, 2 => 8, 3 => 1, 4 => 1, 5 => ANY_SIZE }.freeze # by TYPE value
19
43
 
20
44
  # A materialized span: zero-copy off/len into the source plus the
21
45
  # node's byte offset (the CALLBACK escape hatch).
@@ -47,7 +71,7 @@ module Yeptris
47
71
  { kind: KIND.fetch(n.fetch(:kind)),
48
72
  type: TYPE.fetch(n[:type] || :str),
49
73
  wire: n[:wire_name],
50
- flags: n[:required] ? REQUIRED : 0,
74
+ flags: (n[:required] ? REQUIRED : 0) | (n[:first_wins] ? FIRST_WINS : 0),
51
75
  child_index: n[:child_index] || 0,
52
76
  child_count: n[:child_count] || 0 }
53
77
  end
@@ -120,6 +144,16 @@ module Yeptris
120
144
  when TYPE[:float] then data.get_double(k * 8)
121
145
  when TYPE[:bool] then data.get_uchar(k) == 1
122
146
  when TYPE[:null] then nil
147
+ when TYPE[:any]
148
+ rec = k * ANY_SIZE
149
+ case ANY_KIND[data.get_uint8(rec)]
150
+ when :int then data.get_int64(rec + 16)
151
+ when :float then data.get_double(rec + 16)
152
+ when :bool then data.get_uint8(rec + 3) == 1
153
+ when :null then nil
154
+ else # str/timestamp: the zero-copy span
155
+ source.byteslice(data.get_uint32(rec + 4), data.get_uint32(rec + 8))
156
+ end
123
157
  else
124
158
  off = data.get_uint32(k * 16)
125
159
  len = data.get_uint32(k * 16 + 4)
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.12.1".freeze
6
+ VERSION = "0.6.13.1".freeze
7
7
  # The error hierarchy lives in THIS file (the parent namespace's
8
8
  # own file): nested constants do not trigger a parent-constant
9
9
  # autoload, and the law forbids internal requires — defining the
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(yeptris
6
- VERSION 0.6.12
6
+ VERSION 0.6.13
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -1,5 +1,6 @@
1
1
  /* dom.c — event-built DOM (builder sink + storage). */
2
2
 
3
+ #include <stdio.h>
3
4
  #include <string.h>
4
5
 
5
6
  #include "common/simd_text.h"
@@ -864,6 +865,118 @@ int dom_on_flow_commit(void* ctx) {
864
865
  return 1;
865
866
  }
866
867
 
868
+ int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
869
+ if (d == NULL || t == NULL || t->_src == NULL) {}
870
+ /* the columns are the one representation valid on every route
871
+ * (strict: native; lenient fused: lazily materialized from the
872
+ * records; scalar-root lenient: native column writes) */
873
+ if (yeptris_tape_columns((yeptris_json_tape*)t) != 0) {}
874
+ if (t->kinds == NULL) {}
875
+ d->input_base = (const char*)t->_src;
876
+ d->input_len = t->_srclen;
877
+ const char* src = (const char*)t->_src;
878
+ d->depth = 0;
879
+ for (uint32_t i = 0; i < t->count; i++) {
880
+ const uint8_t kind = t->kinds[i];
881
+ const uint32_t len = t->lens[i];
882
+ const uint32_t off = t->offs[i];
883
+ switch (kind) {
884
+ case YEP_T_DOC:
885
+ break; /* the stream boundary record */
886
+ case YEP_T_SEQ_OPEN:
887
+ case YEP_T_MAP_OPEN: {
888
+ if (d->depth >= YEP_DOM_MAX_DEPTH) {}
889
+ uint32_t id =
890
+ dom_open_node(d, kind == YEP_T_SEQ_OPEN ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING, NULL,
891
+ NULL, 0, 0, 0, 1);
892
+ if (id == UINT32_MAX || dom_place(d, id) != 0) {}
893
+ d->map_pending_key[d->depth] = 0;
894
+ d->stack[d->depth++] = id;
895
+ break;
896
+ }
897
+ case YEP_T_CLOSE:
898
+ if (d->depth == 0) {}
899
+ d->depth--;
900
+ break;
901
+ case YEP_T_NULL:
902
+ case YEP_T_TRUE:
903
+ case YEP_T_FALSE:
904
+ case YEP_T_INT:
905
+ case YEP_T_FLOAT:
906
+ case YEP_T_NUM:
907
+ case YEP_T_STR: {
908
+ yep_tag_id tag = YEPTRIS_TAG_STR;
909
+ uint8_t style = YEP_STYLE_PLAIN;
910
+ int implicit = 0;
911
+ switch (kind) {
912
+ case YEP_T_NULL:
913
+ tag = YEPTRIS_TAG_NULL;
914
+ implicit = 1;
915
+ break;
916
+ case YEP_T_TRUE:
917
+ case YEP_T_FALSE:
918
+ tag = YEPTRIS_TAG_BOOL;
919
+ implicit = 1;
920
+ break;
921
+ case YEP_T_INT:
922
+ case YEP_T_FLOAT:
923
+ case YEP_T_NUM:
924
+ tag = YEPTRIS_TAG_INT;
925
+ implicit = 1;
926
+ if (kind == YEP_T_NUM) {
927
+ /* the lenient route's deferred contract: the span
928
+ * carried NO number grammar at parse — classify
929
+ * now (the tape convert's own authority) */
930
+ size_t adv = 0;
931
+ int flt = 0;
932
+ if (yep_json_number_shape(src + off, len, &adv, &flt) == 0 || adv != len) {
933
+ return -1; /* malformed: the strict route rejected at parse */
934
+ }
935
+ tag = flt ? YEPTRIS_TAG_FLOAT : YEPTRIS_TAG_INT;
936
+ } else if (kind == YEP_T_FLOAT) {
937
+ tag = YEPTRIS_TAG_FLOAT;
938
+ }
939
+ break;
940
+ default:
941
+ style = YEP_STYLE_DOUBLE_QUOTED;
942
+ break;
943
+ }
944
+ uint32_t id =
945
+ dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, style, (uint8_t)implicit, 0);
946
+ if (id == UINT32_MAX) {
947
+ return -1;
948
+ }
949
+ if (kind == YEP_T_STR) {
950
+ /* the record's span is the INNER bytes (quotes off) */
951
+ if (memchr(src + off, '\\', len) != NULL) {
952
+ /* escaped: unescape into the arena (the fused
953
+ * builder's arm, byte for byte) */
954
+ char* dst = yep_dom_str_tail(d, len);
955
+ if (dst == NULL) {}
956
+ d->nodes[id].value = yep_dom_str_commit(
957
+ d, yep_finish_double_into(src, off, off + len, dst, len));
958
+ } else {
959
+ yep_view v = {src + off, len};
960
+ d->nodes[id].value = dom_str_in(d, &v, 1);
961
+ }
962
+ } else {
963
+ yep_view v = {src + off, len};
964
+ d->nodes[id].value = dom_str_in(d, &v, 1);
965
+ }
966
+ d->nodes[id].tag_id = tag;
967
+ if (dom_place(d, id) != 0) {
968
+ return -1;
969
+ }
970
+ break;
971
+ }
972
+ default:
973
+ return -1; /* a CONT run or an unknown kind: neither reaches a
974
+ strict-legal tape (CONT only past 16 MiB tokens) */
975
+ }
976
+ }
977
+ return d->depth == 0 ? 0 : -1;
978
+ }
979
+
867
980
  void dom_on_flow_rollback(void* ctx) {
868
981
  yep_dom* d = (yep_dom*)ctx;
869
982
  if (d->flow_staged) {
@@ -18,6 +18,7 @@
18
18
  #include <stdint.h>
19
19
 
20
20
  #include <yeptris/api.h> /* YEPTRIS_CT_ASSERT (the layout gates) */
21
+ #include <yeptris/tape.h> /* yeptris_json_tape (dom_from_tape) */ /* YEPTRIS_CT_ASSERT (the layout gates) */
21
22
 
22
23
  #include "../common/simd_text.h"
23
24
 
@@ -246,6 +247,15 @@ void yep_mut_set_depths(yep_dom* d, uint32_t id, uint16_t depth);
246
247
  * strictly-validated span [open, close] directly through the DOM's own
247
248
  * placement/link/anchor laws — node-for-node identical to the event
248
249
  * path (the flow-direct-diff ctest pins it). 1 = built, <0 = abort. */
250
+ /* dom_from_tape — the lazy-DOM seam (#342 slice 1): builds the node
251
+ * tree from the tape's records, mirroring dom_on_flow_build's arms
252
+ * (borrowed spans for clean strings, an arena unescape for escaped
253
+ * ones, typed literals by kind). The records are pre-validated, so
254
+ * the walk is placement only. Returns 0, or -1 (allocation/depth/an
255
+ * unexpected record). STRICT tapes only — YEP_T_NUM (the lenient
256
+ * route's deferred numbers) is rejected. */
257
+ int dom_from_tape(yep_dom* d, const yeptris_json_tape* t);
258
+
249
259
  int dom_on_flow_build(void* ctx, const char* p, size_t open, size_t len, uint32_t line,
250
260
  size_t line_start, yep_view anchor, yep_view tag, uint32_t anchor_id,
251
261
  int max_depth, size_t* close);
@@ -13,6 +13,7 @@
13
13
  #include <string.h>
14
14
 
15
15
  #include <yeptris/schema.h>
16
+ #include <yeptris/values.h> /* YeptrisValue: ST_ANY records (#238) */
16
17
 
17
18
  #include "dom/dom.h"
18
19
  #include "memory/allocator.h"
@@ -27,7 +28,9 @@ typedef struct {
27
28
  const yep_dom* dom;
28
29
  int oom;
29
30
  int overflow;
30
- int type_mismatch; /* node index */
31
+ int type_mismatch; /* node index */
32
+ uint32_t* seen_map; /* per-node: the mapping instance that last
33
+ * emitted — YEP_SF_FIRST_WINS skips a repeat */
31
34
  } yep_schema_ctx;
32
35
 
33
36
  static int col_put(yep_schema_ctx* c, uint32_t node, const void* rec, size_t size) {
@@ -84,6 +87,56 @@ static int emit_scalar(yep_schema_ctx* c, uint32_t node, uint32_t at) {
84
87
  case YEP_ST_NULL:
85
88
  col_put(c, node, &(uint8_t){0}, 0);
86
89
  return 1;
90
+ case YEP_ST_ANY: {
91
+ /* the resolver's verdict, verbatim: a YeptrisValue record
92
+ * (#238's headroom) — kind/tag_id from the node, the span
93
+ * borrowed, and the numeric payload converted when the
94
+ * verdict says so (the values-drain's conversion, DOM-side) */
95
+ YeptrisValue r = {0, 0, 0, 0, 0, 0, 0};
96
+ r.tag_id = n->tag_id;
97
+ r.off = (uint32_t)((const char*)v.p - c->dom->input_base);
98
+ r.len = v.len;
99
+ switch (n->tag_id) {
100
+ case YEPTRIS_TAG_NULL:
101
+ r.kind = YEP_V_NULL;
102
+ break;
103
+ case YEPTRIS_TAG_BOOL: {
104
+ r.kind = YEP_V_BOOL;
105
+ r.b = (uint8_t)(v.len > 0 && (v.p[0] == 't' || v.p[0] == 'T' || v.p[0] == 'y' ||
106
+ v.p[0] == 'Y' || v.p[0] == 'o' || v.p[0] == '1'));
107
+ break;
108
+ }
109
+ case YEPTRIS_TAG_INT:
110
+ case YEPTRIS_TAG_FLOAT: {
111
+ int64_t iv = 0;
112
+ double dv = 0;
113
+ int shape = 0;
114
+ size_t end = 0;
115
+ if (v.len != 0 && v.p != NULL &&
116
+ yep_json_number_scan(v.p, v.len, &end, &shape, &iv, &dv) != 0 && end == v.len) {
117
+ if (n->tag_id == YEPTRIS_TAG_INT) {
118
+ r.kind = YEP_V_INT;
119
+ r.p = (uint64_t)iv;
120
+ } else {
121
+ r.kind = YEP_V_FLOAT;
122
+ double fd = shape == 0 ? (double)iv : dv;
123
+ memcpy(&r.p, &fd, sizeof(fd));
124
+ }
125
+ } else {
126
+ r.kind = YEP_V_STR; /* the resolver said number, the
127
+ bytes disagree: keep the span */
128
+ }
129
+ break;
130
+ }
131
+ case YEPTRIS_TAG_TIMESTAMP:
132
+ r.kind = YEP_V_TIMESTAMP;
133
+ break;
134
+ default:
135
+ r.kind = YEP_V_STR;
136
+ break;
137
+ }
138
+ return col_put(c, node, &r, sizeof(r));
139
+ }
87
140
  default:
88
141
  return -1;
89
142
  }
@@ -140,10 +193,17 @@ static int emit(yep_schema_ctx* c, uint32_t node, uint32_t at) {
140
193
  size_t wl = strlen(cd->wire_name ? cd->wire_name : "");
141
194
  if (cd->wire_name != NULL && key.len == wl &&
142
195
  memcmp(key.p, cd->wire_name, wl) == 0) {
196
+ if ((cd->flags & YEP_SF_FIRST_WINS) && c->seen_map != NULL &&
197
+ c->seen_map[ci] == at + 1) {
198
+ break; /* a repeat within THIS mapping: first wins */
199
+ }
143
200
  int rc = emit(c, ci, val);
144
201
  if (rc != 1) {
145
202
  return rc;
146
203
  }
204
+ if ((cd->flags & YEP_SF_FIRST_WINS) && c->seen_map != NULL) {
205
+ c->seen_map[ci] = at + 1;
206
+ }
147
207
  break;
148
208
  }
149
209
  }
@@ -178,6 +238,7 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
178
238
  const yep_allocator* sys = yep_system_allocator();
179
239
  yep_engine* eng = yep_engine_create(sys);
180
240
  yep_dom* dom = yep_dom_create(sys);
241
+ uint32_t* seen_map_ptr = NULL;
181
242
  YeptrisStatus st = YEPTRIS_OK;
182
243
  yep_sink sink = {.on_event = yep_dom_on_event,
183
244
  .ctx = dom,
@@ -194,6 +255,9 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
194
255
  }
195
256
  if (schema == YEPTRIS_SCHEMA_11_COMPAT) {
196
257
  yep_engine_set_resolver(eng, yep_resolver_compat11());
258
+ dom->resolver = yep_resolver_compat11(); /* the DOM's direct
259
+ builders resolve their own tag_ids —
260
+ ST_ANY reads the NODE's verdict */
197
261
  }
198
262
  dom->input_base = source;
199
263
  dom->input_len = len;
@@ -204,7 +268,12 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
204
268
  }
205
269
 
206
270
  {
207
- yep_schema_ctx c = {desc, desc_len, cols, dom, 0, 0, -1};
271
+ uint32_t* seen = seen_map_ptr = calloc(desc_len, sizeof(uint32_t));
272
+ if (seen == NULL) {
273
+ st = YEPTRIS_ERROR_MEMORY;
274
+ goto out;
275
+ }
276
+ yep_schema_ctx c = {desc, desc_len, cols, dom, 0, 0, -1, seen};
208
277
  int rc = emit(&c, 0, dom->docs[0]);
209
278
  if (rc < 0 && c.type_mismatch >= 0) {
210
279
  yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, (uint32_t)c.type_mismatch,
@@ -232,6 +301,7 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
232
301
  }
233
302
 
234
303
  out:
304
+ free(seen_map_ptr);
235
305
  yep_dom_destroy(dom);
236
306
  yep_engine_destroy(eng);
237
307
  return st;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yeptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.12.1
4
+ version: 0.6.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-20 00:00:00.000000000 Z
11
+ date: 2026-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi