yeptris 0.6.50.0-aarch64-linux → 0.6.51.0-aarch64-linux

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: '0782e25e15d541b7a7e5448f7c63e79e627a130946d131860133021262d15c28'
4
- data.tar.gz: daf5279398e226c9ab663ab1482356938a675a7cdd84b0e872134e2f6651322f
3
+ metadata.gz: cc1eca4788a46d1ba98920b9611fa8bde1bdb99533ac54e6ee19ac24658d8472
4
+ data.tar.gz: 8c53810f5df811d36759d3bf93de7fd66e09c5097dafa44aa29c5914986f87aa
5
5
  SHA512:
6
- metadata.gz: 423942744e7c5fbaa2c7dc4cde3f088abaf4aaacb88e4095c9e0fd0af1e3bf9774024522fcaa34d97e511ddba3b62e30a53753ee127a50ee3727f2a08fa08ca2
7
- data.tar.gz: 102f3c0f8f3baa9d78ea65a3124c24c632d8f2e8940835e63c28061c81cb5b16039be3ac8c58e1751943ba3d10ef8287ee245260eb81b8a0a5b3d3a3ee63527e
6
+ metadata.gz: f6509bbdc7bc0aedd02afd6958beb3ed1dd9b99ec332a8337478ba6c39b7b8e42ad020cb7e87184168eee519a42a62d819d57dbf02e71431b445b883c47f9bb1
7
+ data.tar.gz: b95ac44576842182cd6a28aae11d532b4b11b8c10606ae8c666e562b5749d440dd65665208e4b533444f2041914be27389649d0d33aaf734d6cb9663f5750b26
@@ -11,8 +11,8 @@
11
11
 
12
12
  #define YEPTRIS_VERSION_MAJOR 0
13
13
  #define YEPTRIS_VERSION_MINOR 6
14
- #define YEPTRIS_VERSION_PATCH 50
15
- #define YEPTRIS_VERSION_STRING "0.6.50"
14
+ #define YEPTRIS_VERSION_PATCH 51
15
+ #define YEPTRIS_VERSION_STRING "0.6.51"
16
16
 
17
17
  #ifdef __cplusplus
18
18
  extern "C" {
Binary file
Binary file
Binary file
data/lib/yeptris/json.rb CHANGED
@@ -34,12 +34,21 @@ module Yeptris
34
34
 
35
35
  module_function
36
36
 
37
- def load(source)
37
+ # allow_duplicate_key: opts out of the resolved json gem's strict
38
+ # duplicate-key contract (json >= 3 rejects by default) — last value
39
+ # wins, exactly the historical JSON.parse contract. System-boundary
40
+ # parsers need it: third-party feeds (Crossref book records keyed by
41
+ # ISBN, NIST's pubs export keyed by "&nbsp", the UN search API's blank
42
+ # keys) genuinely carry duplicates, and rejecting them breaks the fetch
43
+ # rather than flagging the data (relaton/relaton#413, the ask in
44
+ # yeptris-ruby#335). Default stays the json-gem-parity strictness.
45
+ def load(source, allow_duplicate_key: false)
46
+ strict = STRICT_DUPLICATE_KEYS && !allow_duplicate_key
38
47
  source = ::Yeptris.read_input(source)
39
48
  source = source.to_s
40
49
  if defined?(::Yeptris::Native)
41
50
  begin
42
- return ::Yeptris::Native.load_json(source, STRICT_DUPLICATE_KEYS)
51
+ return ::Yeptris::Native.load_json(source, strict)
43
52
  rescue ::Yeptris::ParseError => e
44
53
  raise ParseError, e.message
45
54
  end
@@ -53,9 +62,9 @@ module Yeptris
53
62
  # none) answered last-wins under json >= 3 — found by canon's
54
63
  # dependent CI, whose parity pin now names the lane.
55
64
  return marshal_engine(
56
- source, STRICT_DUPLICATE_KEYS ? ::Yeptris::FFI::MARSHAL_STRICT_DUP_KEYS : 0
65
+ source, strict ? ::Yeptris::FFI::MARSHAL_STRICT_DUP_KEYS : 0
57
66
  ) if ::Yeptris::FFI::MARSHAL_STRICT_DUP
58
- tape_engine(source)
67
+ tape_engine(source, strict)
59
68
  end
60
69
 
61
70
  # The marshal engine (#278): yeptris_parse_json gates the strict
@@ -156,19 +165,19 @@ module Yeptris
156
165
  out.force_encoding(Encoding::UTF_8)
157
166
  end
158
167
 
159
- def tape_engine(source)
168
+ def tape_engine(source, strict_dup = STRICT_DUPLICATE_KEYS)
160
169
  tape = ::Yeptris::FFI::JsonTape.new
161
170
  rc = ::Yeptris::FFI.yeptris_parse_json_tape(source, source.bytesize, tape)
162
171
  raise ParseError, ::Yeptris::FFI.last_error_message if rc != ::Yeptris::FFI::OK
163
172
 
164
173
  begin
165
- walk_tape(source, tape)
174
+ walk_tape(source, tape, strict_dup)
166
175
  ensure
167
176
  ::Yeptris::FFI.yeptris_tape_free(tape)
168
177
  end
169
178
  end
170
179
 
171
- def walk_tape(src, tape, strict_dup = STRICT_DUPLICATE_KEYS)
180
+ def walk_tape(src, tape, strict_dup)
172
181
  n = tape[:count]
173
182
  kinds = tape[:kinds].read_bytes(n).unpack("C*")
174
183
  offs = tape[:offs].read_bytes(n * 4).unpack("V*")
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.50.0".freeze
6
+ VERSION = "0.6.51.0".freeze
7
7
 
8
8
  # Informational notices are OPT-IN (yeptris-ruby#217): consumers
9
9
  # embed yeptris inside CLIs that assert clean stderr, and a
data/libyeptris.so CHANGED
Binary file
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(yeptris
6
- VERSION 0.6.50
6
+ VERSION 0.6.51
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -53,6 +53,55 @@ extern "C" {
53
53
  YEPTRIS_API YeptrisDocument yeptris_cbor_decode(const void* buf, size_t len, uint32_t opts,
54
54
  YeptrisStatus* status);
55
55
 
56
+ /* The SAX decode face (#742): yeptris_cbor_decode's grammar driving
57
+ * consumer callbacks — items land as they parse, no document, no
58
+ * nodes. Measured (perf ledger 2026-10-29): the scan-only route reads
59
+ * ~2.1x the DOM entry on the same corpus (DOM construction is over
60
+ * half the decode lane), so validation, counting, and streaming
61
+ * transforms belong here. Items arrive in encoding order; map items
62
+ * set is_key on the key half. Strings with borrowed=1 point INTO buf —
63
+ * keep it valid while you hold them; borrowed=0 is decoder scratch.
64
+ * tag ("N N N", outermost first; NULL when none) and tag_len are
65
+ * callback-call-local. open's cap is the expected item count (maps
66
+ * count pairs*2; UINT64_MAX = indefinite); close fires for both the
67
+ * definite-complete and the break path, after the last child. The
68
+ * rendered text forms match the DOM ledger exactly (numbers-as-text,
69
+ * false/true/null/undefined, simple(N)).
70
+ *
71
+ * Every callback returns 0 on sink failure — the decode then reports
72
+ * YEPTRIS_ERROR_MEMORY — and may raise through host languages; the
73
+ * decoder owns no host state across them except the indefinite buffer,
74
+ * which its own exit paths free. All seven callbacks and sax itself
75
+ * must be non-NULL (YEPTRIS_ERROR_ARG otherwise): the loop dispatches
76
+ * unconditionally.
77
+ *
78
+ * consumed == NULL: trailing bytes after the one data item fail
79
+ * YEPTRIS_ERROR_PARSE. consumed != NULL: on success it receives the
80
+ * byte count of the items decoded — walk a CBOR Sequence (RFC 8742)
81
+ * by advancing buf by it. On failure *status (may be NULL) carries the
82
+ * code and yeptris_last_error() the message with the byte offset.
83
+ *
84
+ * Memory: none — the decoder allocates only its indefinite-chunk
85
+ * scratch and frees it on every exit. */
86
+ typedef struct yeptris_cbor_sax {
87
+ void* ctx;
88
+ int (*text)(void* ctx, const char* s, uint32_t n, uint8_t tag_id, int is_key, const char* tag,
89
+ uint32_t tag_len);
90
+ int (*int_val)(void* ctx, int negative, uint64_t mag, int is_key, const char* tag,
91
+ uint32_t tag_len);
92
+ int (*float_val)(void* ctx, double dv, int is_key, const char* tag, uint32_t tag_len);
93
+ int (*str_val)(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
94
+ const char* tag, uint32_t tag_len);
95
+ int (*bytes_val)(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
96
+ const char* tag, uint32_t tag_len);
97
+ int (*open)(void* ctx, int is_map, uint64_t cap, const char* tag, uint32_t tag_len);
98
+ int (*close)(void* ctx, int is_map);
99
+ } yeptris_cbor_sax;
100
+
101
+ YEPTRIS_API YeptrisStatus yeptris_cbor_decode_sax(const void* buf, size_t len, uint32_t opts,
102
+ const yeptris_cbor_sax* sax, size_t* consumed,
103
+ YeptrisStatus* status);
104
+
56
105
  /* CBOR Sequences (RFC 8742): top-level data items, concatenated —
57
106
  * no framing bytes. Iterates the buffer, one document per item; the
58
107
  * callback OWNS each item (free it with yeptris_document_free) and
@@ -1032,3 +1032,25 @@ YEPTRIS_API YeptrisDocument yeptris_cbor_decode(const void* buf, size_t len, uin
1032
1032
  }
1033
1033
  return (YeptrisDocument)doc;
1034
1034
  }
1035
+
1036
+ YEPTRIS_API YeptrisStatus yeptris_cbor_decode_sax(const void* buf, size_t len, uint32_t opts,
1037
+ const yeptris_cbor_sax* sax, size_t* consumed,
1038
+ YeptrisStatus* status) {
1039
+ if ((buf == NULL && len != 0) || sax == NULL || (opts & ~YEPTRIS_CBOR_STRICT) != 0 ||
1040
+ sax->text == NULL || sax->int_val == NULL || sax->float_val == NULL ||
1041
+ sax->str_val == NULL || sax->bytes_val == NULL || sax->open == NULL || sax->close == NULL) {
1042
+ if (status != NULL) {
1043
+ *status = YEPTRIS_ERROR_ARG;
1044
+ }
1045
+ return YEPTRIS_ERROR_ARG;
1046
+ }
1047
+ /* yeptris_cbor_sax IS yep_cbor_sink (sink.h aliases the public
1048
+ * face): the decoder drives the caller's vtable directly. */
1049
+ YeptrisStatus st = (YeptrisStatus)yep_cbor_decode_gen((const unsigned char*)buf, len,
1050
+ (opts & YEPTRIS_CBOR_STRICT) != 0,
1051
+ consumed, (const yep_cbor_sink*)sax);
1052
+ if (status != NULL) {
1053
+ *status = st;
1054
+ }
1055
+ return st;
1056
+ }
@@ -4,7 +4,11 @@
4
4
  * source of CBOR semantics; a sink implements the item constructors
5
5
  * on top of it. Two sinks exist: the DOM builder (the default, the
6
6
  * yeptris_cbor_decode public path) and the native ext's VALUE builder
7
- * (one pass, no intermediate). Sinks receive the resolver's tag_id
7
+ * (one pass, no intermediate). The sink vtable IS the public SAX face
8
+ * (yeptris_cbor_sax, <yeptris/cbor.h> — #742): the alias keeps the
9
+ * decoder driving the same layout the public entry hands over, so
10
+ * yeptris_cbor_decode_sax is a straight passthrough. Callback/field
11
+ * contracts are documented there; sinks receive the resolver's tag_id
8
12
  * and the pending semantic-tag chain in its text form ("N N N",
9
13
  * outermost first — NULL when the item carries no chain).
10
14
  */
@@ -15,35 +19,9 @@
15
19
  #include <stdint.h>
16
20
 
17
21
  #include <yeptris/api.h> /* YEPTRIS_API (the ext links these) */
22
+ #include <yeptris/cbor.h>
18
23
 
19
- typedef struct yep_cbor_sink {
20
- void* ctx;
21
- /* All callbacks return 0 on sink failure (the decoder reports
22
- * MEMORY). They may raise through host languages (longjmp): the
23
- * decoder owns no host-managed state across them except the
24
- * indefinite buffer, which its own exit paths free. */
25
- /* is_key: 1 = the item lands in the enclosing map's key slot
26
- * (sinks key-intern strings; the DOM ignores it). Rendered text:
27
- * numbers-as-text, false/true/null/undefined, simple(N) — the
28
- * exact bytes the DOM path stores. */
29
- int (*text)(void* ctx, const char* s, uint32_t n, uint8_t tag_id, int is_key, const char* tag,
30
- uint32_t tag_len);
31
- /* Known integers: value = negative ? -(mag) : mag, mag <= 2^63. */
32
- int (*int_val)(void* ctx, int negative, uint64_t mag, int is_key, const char* tag,
33
- uint32_t tag_len);
34
- int (*float_val)(void* ctx, double dv, int is_key, const char* tag, uint32_t tag_len);
35
- /* Strings. borrowed: 1 = p is the input (a sink may record the
36
- * offset), 0 = the decoder's scratch buffer (copy or take it). */
37
- int (*str_val)(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
38
- const char* tag, uint32_t tag_len);
39
- int (*bytes_val)(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
40
- const char* tag, uint32_t tag_len);
41
- /* Containers. cap: expected item count (maps: pairs*2), UINT64_MAX
42
- * for indefinite. close fires for BOTH the definite-complete and
43
- * the break paths, after the last child. */
44
- int (*open)(void* ctx, int is_map, uint64_t cap, const char* tag, uint32_t tag_len);
45
- int (*close)(void* ctx, int is_map);
46
- } yep_cbor_sink;
24
+ typedef yeptris_cbor_sax yep_cbor_sink;
47
25
 
48
26
  /* The generic engine: the grammar, driving `sink`. Same reject/
49
27
  * encoding/depth semantics as the DOM entry. */
@@ -72,6 +72,16 @@ void yep_val_put(yep_value_ctx* c, const YeptrisValue* v) {
72
72
  void yep_val_slot(yep_value_ctx* c, YeptrisValue* v) {
73
73
  if (c->depth > 0 && c->depth <= YEP_V_MAX_DEPTH) {
74
74
  int di = c->depth - 1;
75
+ if (c->is_seq[di]) {
76
+ /* a sequence entry never takes a key slot — the toggle
77
+ * flagged every ODD-position entry is_key=1, and the
78
+ * marshal strict-dup scan then false-flagged arrays with a
79
+ * repeated value at two odd positions ("[\"a\",\"b\",\"a\"]"
80
+ * = "duplicate key in object"; yeptris-ruby#333, the
81
+ * yeptris#680 class resurfacing through the value
82
+ * stream's own parity) */
83
+ return;
84
+ }
75
85
  if (c->key_pend[di] == 0) {
76
86
  v->is_key = 1;
77
87
  c->key_pend[di] = 1;
@@ -169,6 +179,7 @@ static int transform(yep_value_ctx* c, const yep_rec_store* store) {
169
179
  yep_val_put(c, &v);
170
180
  if (c->depth < YEP_V_MAX_DEPTH) {
171
181
  c->key_pend[c->depth] = 0;
182
+ c->is_seq[c->depth] = 1;
172
183
  }
173
184
  c->depth++;
174
185
  continue;
@@ -179,6 +190,7 @@ static int transform(yep_value_ctx* c, const yep_rec_store* store) {
179
190
  yep_val_put(c, &v);
180
191
  if (c->depth < YEP_V_MAX_DEPTH) {
181
192
  c->key_pend[c->depth] = 0;
193
+ c->is_seq[c->depth] = 0;
182
194
  }
183
195
  c->depth++;
184
196
  continue;
@@ -285,6 +297,7 @@ static int lin_node(yep_value_ctx* c, const yep_dom* d, uint32_t id) {
285
297
  yep_val_put(c, &v);
286
298
  if (c->depth < YEP_V_MAX_DEPTH) {
287
299
  c->key_pend[c->depth] = 0;
300
+ c->is_seq[c->depth] = (n->kind == YEP_DOM_SEQUENCE);
288
301
  }
289
302
  c->depth++;
290
303
  if (lin_children(c, d, n) != 0) {
@@ -24,6 +24,8 @@ typedef struct yep_value_ctx {
24
24
  int oom;
25
25
  int unsupported; /* linearize: construct the emitter cannot express */
26
26
  uint8_t key_pend[YEP_V_MAX_DEPTH]; /* per open map: key slot taken */
27
+ uint8_t is_seq[YEP_V_MAX_DEPTH]; /* per open frame: 1 = sequence —
28
+ * entries never take key slots */
27
29
  int depth;
28
30
  } yep_value_ctx;
29
31
 
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.50.0
4
+ version: 0.6.51.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-29 00:00:00.000000000 Z
11
+ date: 2026-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi