yeptris 0.6.16.2-powerpc64le-linux → 0.6.17.1-powerpc64le-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: 8affd114cd9f63e7079742015b2b34872af58c930cb5ba37dc7dc915fe4780b1
4
- data.tar.gz: 9528ac190d2a6bb9f0182858a650bb31f5315767e8220cf59fa936f04d1d62c3
3
+ metadata.gz: 36cb17a206c740954fafe1ce7b1fcbb6f94a47179e22beeee390498d20b1d07f
4
+ data.tar.gz: 8db951649c7030a30d94fdbba0778fefef872d222ed479a83d459a1c0a36cfd9
5
5
  SHA512:
6
- metadata.gz: c21f7c752192b87e5aa6a3a1089359f72cf577a876f3b5cd1653a7c1c7e9a216b4c5bf0d964c64f32c29815d851d616870b4a1b78990d2344f9127650c5b2000
7
- data.tar.gz: 37cf8d32c00a99dc09c9fccb59b9551e514946680e7fae0eae199c0d66a7552b957b547968e9f60f339e3f94765f1851b47212eb869b4572d0173fd4bc68ba49
6
+ metadata.gz: 19b0c83147c2c5f0882efb9bc32bedd6be9c7c8d80b50c86b985f36ddc6fe0d051b31c7a0795ee9340cf47dab64825072d0012d1dede882ad8497f24dbb45e85
7
+ data.tar.gz: 3146be4e5e094c747fa2bb0335bba6bf35a7fdee4190428d6883341f8003f472a50505e63d60c6ba59428031e7204cb70f64a1e169d2dc1932abc0604c3bc06a
@@ -25,102 +25,144 @@ static VALUE cr_key_str(const char* p, size_t len) {
25
25
  return rb_enc_interned_str(p, (long)len, cr_utf8_enc);
26
26
  }
27
27
 
28
+ #include "cbor/sink.h"
28
29
  #include "dom/dom.h"
29
30
  #include "doc.h" /* the public YeptrisDocument wrapper: ->dom */
30
31
  #include <yeptris/cbor.h>
31
32
  #include <yeptris/dom.h>
32
33
  #include <yeptris/resolve.h>
33
34
 
34
- static const char* cr_view(const yep_dom* d, yep_sview sv, uint32_t* len) {
35
- *len = sv.len;
36
- if (sv.len == 0) {
37
- return "";
38
- }
39
- return (sv.off & YEP_SV_INPUT) ? (d->str + (sv.off & YEP_SV_OFF)) : (d->input_base + sv.off);
35
+ /* ---- the VALUE sink (#157): bytes -> VALUE in ONE pass ----
36
+ *
37
+ * The decode grammar drives; these callbacks build the same VALUEs
38
+ * the old DOM + cr_walk pair produced (verified by the binding's
39
+ * differential suite): nil/true/false by tag, ints via LL2NUM (the
40
+ * INT64_MIN text parity wart kept), floats via DBL2NUM (no
41
+ * text round trip), strings copied utf8 (byte strings today ride the
42
+ * same utf8 String), map keys interned (values never are).
43
+ *
44
+ * GC: the container stack is a Ruby array (every VALUE reachable);
45
+ * the input buffer is read directly, so decode runs with GC disabled
46
+ * (the caller's String may move otherwise — the #160 bus error). */
47
+
48
+ struct rv_ctx {
49
+ VALUE stack; /* [.., container, (pending-key)?] */
50
+ VALUE root; /* Qnil until the top item closes */
51
+ };
52
+
53
+ static VALUE rv_top(const struct rv_ctx* c) {
54
+ long n = RARRAY_LEN(c->stack);
55
+ return n > 0 ? rb_ary_entry(c->stack, n - 1) : Qnil;
40
56
  }
41
57
 
42
- static VALUE cr_scalar(const yep_dom* d, const yep_dnode* n) {
43
- uint32_t len = 0;
44
- const char* p = cr_view(d, n->value, &len);
45
- switch (n->tag_id) {
46
- case YEPTRIS_TAG_NULL:
47
- return Qnil;
48
- case YEPTRIS_TAG_BOOL:
49
- if (len == 1) { /* Psych: "y"/"n" stay Strings */
50
- return rb_utf8_str_new(p, len);
51
- }
52
- return (len == 4 && memcmp(p, "true", 4) == 0) ? Qtrue : Qfalse;
53
- case YEPTRIS_TAG_INT: {
54
- char buf[32];
55
- size_t cp = len < sizeof(buf) - 1 ? len : sizeof(buf) - 1;
56
- memcpy(buf, p, cp);
57
- buf[cp] = '\0';
58
- char* end = NULL;
59
- long long v = strtoll(buf, &end, 10);
60
- if (end != buf && *end == '\0' && end == buf + len && v > INT64_MIN) {
61
- return LL2NUM(v);
62
- }
63
- return rb_utf8_str_new(p, len); /* int_or_string's fallback */
58
+ static VALUE rv_attach(struct rv_ctx* c, VALUE v) {
59
+ VALUE top = rv_top(c);
60
+ if (RB_TYPE_P(top, T_ARRAY)) {
61
+ rb_ary_push(top, v);
62
+ return v;
64
63
  }
65
- case YEPTRIS_TAG_FLOAT: {
66
- char buf[64];
67
- size_t cp = len < sizeof(buf) - 1 ? len : sizeof(buf) - 1;
68
- memcpy(buf, p, cp);
69
- buf[cp] = '\0';
70
- char* end = NULL;
71
- double v = strtod(buf, &end);
72
- if (end != buf && end != buf + len) {
73
- return rb_utf8_str_new(p, len); /* float_or_string's fallback */
74
- }
75
- return DBL2NUM(v);
64
+ if (RB_TYPE_P(top, T_HASH)) {
65
+ rb_ary_push(c->stack, v); /* becomes the pending key */
66
+ return v;
76
67
  }
77
- default:
78
- return rb_utf8_str_new(p, len);
68
+ /* a pending key: pop it, land v in the map */
69
+ VALUE k = rb_ary_pop(c->stack);
70
+ VALUE map = rv_top(c);
71
+ if (RB_TYPE_P(map, T_HASH)) {
72
+ rb_hash_aset(map, k, v);
73
+ return v;
79
74
  }
75
+ /* the stack emptied: v is the root */
76
+ c->root = v;
77
+ return v;
80
78
  }
81
79
 
82
- static VALUE cr_walk(const yep_dom* d, uint32_t id) {
83
- const yep_dnode* n = &d->nodes[id];
84
- switch (n->kind) {
85
- case YEP_DOM_SCALAR:
86
- return cr_scalar(d, n);
87
- case YEP_DOM_SEQUENCE: {
88
- long cnt = (long)n->count;
89
- VALUE a = rb_ary_new_capa(cnt);
90
- uint32_t c = n->first_child;
91
- for (long i = 0; i < cnt && c != UINT32_MAX; i++) {
92
- rb_ary_push(a, cr_walk(d, c));
93
- c = d->nodes[c].next_sibling;
94
- }
95
- return a;
80
+ static int rv_text(void* ctx, const char* s, uint32_t n, uint8_t tag_id, int is_key,
81
+ const char* tag, uint32_t tag_len) {
82
+ (void)tag;
83
+ (void)tag_len;
84
+ struct rv_ctx* c = ctx;
85
+ VALUE v;
86
+ switch (tag_id) {
87
+ case YEPTRIS_TAG_NULL:
88
+ v = Qnil;
89
+ break;
90
+ case YEPTRIS_TAG_BOOL:
91
+ v = (n == 4 && memcmp(s, "true", 4) == 0) ? Qtrue : Qfalse;
92
+ break;
93
+ default: /* rendered text (diagnostics, simple(N), key ints) */
94
+ v = rb_utf8_str_new(s, (long)n);
95
+ break;
96
96
  }
97
- case YEP_DOM_MAPPING: {
98
- long pairs = (long)(n->count / 2);
99
- VALUE h = HASH_NEW_CAPA(pairs);
100
- uint32_t c = n->first_child;
101
- for (long i = 0; i < pairs && c != UINT32_MAX; i++) {
102
- VALUE k;
103
- const yep_dnode* kn = &d->nodes[c];
104
- if (kn->kind == YEP_DOM_SCALAR && kn->tag_id == YEPTRIS_TAG_STR) {
105
- uint32_t klen = 0;
106
- const char* kp = cr_view(d, kn->value, &klen);
107
- k = cr_key_str(kp, klen);
108
- c = kn->next_sibling;
109
- } else {
110
- k = cr_walk(d, c);
111
- c = d->nodes[c].next_sibling;
112
- }
113
- VALUE v = (c != UINT32_MAX) ? cr_walk(d, c) : Qnil;
114
- if (c != UINT32_MAX) {
115
- c = d->nodes[c].next_sibling;
116
- }
117
- rb_hash_aset(h, k, v);
118
- }
119
- return h;
97
+ (void)is_key;
98
+ rv_attach(c, v);
99
+ return 1;
100
+ }
101
+
102
+ static int rv_int(void* ctx, int negative, uint64_t mag, int is_key, const char* tag,
103
+ uint32_t tag_len) {
104
+ (void)tag;
105
+ (void)tag_len;
106
+ struct rv_ctx* c = ctx;
107
+ VALUE v;
108
+ if (negative && mag == (uint64_t)1 << 63) {
109
+ /* parity: the DOM path's strtoll guard renders INT64_MIN a
110
+ * String today; the differential suite pins it */
111
+ v = rb_utf8_str_new("-9223372036854775808", 20);
112
+ } else {
113
+ v = LL2NUM(negative ? -(int64_t)mag : (int64_t)mag);
120
114
  }
121
- default: /* ALIAS: CBOR decode never produces one */
122
- return Qnil;
115
+ rv_attach(c, v);
116
+ return 1;
117
+ }
118
+
119
+ static int rv_float(void* ctx, double dv, int is_key, const char* tag, uint32_t tag_len) {
120
+ (void)is_key;
121
+ (void)tag;
122
+ (void)tag_len;
123
+ rv_attach(ctx, DBL2NUM(dv));
124
+ return 1;
125
+ }
126
+
127
+ static int rv_str(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
128
+ const char* tag, uint32_t tag_len) {
129
+ (void)borrowed;
130
+ (void)tag;
131
+ (void)tag_len;
132
+ struct rv_ctx* c = ctx;
133
+ VALUE v = is_key ? rb_enc_interned_str((const char*)p, (long)n, cr_utf8_enc)
134
+ : rb_utf8_str_new((const char*)p, (long)n);
135
+ rv_attach(c, v);
136
+ return 1;
137
+ }
138
+
139
+ static int rv_bytes(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
140
+ const char* tag, uint32_t tag_len) {
141
+ /* parity: byte strings ride the same utf8 String today */
142
+ return rv_str(ctx, p, n, borrowed, is_key, tag, tag_len);
143
+ }
144
+
145
+ static int rv_open(void* ctx, int is_map, uint64_t cap, const char* tag, uint32_t tag_len) {
146
+ (void)tag;
147
+ (void)tag_len;
148
+ struct rv_ctx* c = ctx;
149
+ VALUE v;
150
+ if (is_map) {
151
+ v = (cap != UINT64_MAX && cap / 2 <= 4096) ? HASH_NEW_CAPA((long)(cap / 2))
152
+ : rb_hash_new();
153
+ } else {
154
+ v = (cap != UINT64_MAX && cap <= 8192) ? rb_ary_new_capa((long)cap) : rb_ary_new();
123
155
  }
156
+ rb_ary_push(c->stack, v);
157
+ return 1;
158
+ }
159
+
160
+ static int rv_close(void* ctx, int is_map) {
161
+ (void)is_map;
162
+ struct rv_ctx* c = ctx;
163
+ VALUE v = rb_ary_pop(c->stack);
164
+ rv_attach(c, v);
165
+ return 1;
124
166
  }
125
167
 
126
168
  VALUE yep_rb_cbor_load(const char* p, size_t len, int strict) {
@@ -128,27 +170,24 @@ VALUE yep_rb_cbor_load(const char* p, size_t len, int strict) {
128
170
  cr_utf8_enc = rb_utf8_encoding();
129
171
  }
130
172
  YeptrisStatus st = YEPTRIS_OK;
131
- /* the DOM borrows the input buffer zero-copy; the walk ALLOCATES,
132
- * and a GC compaction mid-walk moves the caller's String — the
133
- * borrowed base dangles (the #160 CI bus error on 3.2/linux).
134
- * Same discipline as the JSON walk: GC off across decode+walk */
173
+ /* the decoder reads the caller's buffer directly; GC off across
174
+ * the pass (a compaction would move the String under p — the
175
+ * #160 bus error) */
135
176
  VALUE gc_on = rb_gc_disable();
136
- /* YeptrisDocument is a WRAPPER (doc.h) — ->dom is the tree; a raw
137
- * yep_dom* cast read the wrapper's fields as the dom (the CI
138
- * segfaults: docs at the wrong offset) */
139
- yeptris_document* doc = (yeptris_document*)yeptris_cbor_decode(
140
- p, len, strict ? YEPTRIS_CBOR_STRICT : 0, &st);
141
- yep_dom* d = (doc != NULL) ? doc->dom : NULL;
142
- if (d == NULL) {
143
- if (RTEST(gc_on)) {
144
- rb_gc_enable();
145
- }
146
- return Qundef;
147
- }
148
- VALUE v = (d->dcount > 0 && d->docs[0] != UINT32_MAX) ? cr_walk(d, d->docs[0]) : Qnil;
149
- yeptris_document_free((YeptrisDocument)doc);
177
+ struct rv_ctx ctx;
178
+ ctx.stack = rb_ary_new();
179
+ ctx.root = Qnil;
180
+ static const yep_cbor_sink RV_SINK = {
181
+ NULL, rv_text, rv_int, rv_float, rv_str, rv_bytes, rv_open, rv_close,
182
+ };
183
+ yep_cbor_sink sink = RV_SINK;
184
+ sink.ctx = &ctx;
185
+ st = yep_cbor_decode_gen((const unsigned char*)p, len, strict, NULL, &sink);
150
186
  if (RTEST(gc_on)) {
151
187
  rb_gc_enable();
152
188
  }
153
- return v;
189
+ if (st != YEPTRIS_OK) {
190
+ return Qundef;
191
+ }
192
+ return ctx.root;
154
193
  }
Binary file
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.16.2".freeze
6
+ VERSION = "0.6.17.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
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.16
6
+ VERSION 0.6.17
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -65,7 +65,9 @@ enum {
65
65
  (default: last, as at parse) */
66
66
  };
67
67
 
68
- /* One plan node. 32 bytes, ABI-pinned. */
68
+ /* One plan node. ABI-pinned: a pointer plus 16 fixed-width bytes —
69
+ * 24 on LP64, 20 on 32-bit (the hosts' FFI mirrors size :pointer
70
+ * per-process, so the layout contract is the POINTER plus the tail). */
69
71
  typedef struct yeptris_desc_node {
70
72
  const char* wire_name; /* mapping key to match (NULL: the root) */
71
73
  uint8_t kind; /* YEP_SK_* */
@@ -77,7 +79,7 @@ typedef struct yeptris_desc_node {
77
79
  uint32_t reserved; /* must be 0 (ABI headroom) */
78
80
  } yeptris_desc_node;
79
81
 
80
- YEPTRIS_CT_ASSERT(sizeof(yeptris_desc_node) == 24);
82
+ YEPTRIS_CT_ASSERT(sizeof(yeptris_desc_node) == sizeof(const char*) + 16);
81
83
 
82
84
  /* One column's output. Caller allocates the payload array (sized by
83
85
  * expectation); the call fills it and sets count. No per-value FFI:
@@ -27,6 +27,8 @@
27
27
  #include <math.h>
28
28
  #include <string.h>
29
29
 
30
+ #include <stdlib.h>
31
+
30
32
  #include <yeptris/cbor.h>
31
33
  #include <yeptris/resolve.h>
32
34
 
@@ -37,29 +39,41 @@
37
39
  #include "../memory/allocator.h"
38
40
  #include "../parse/events.h"
39
41
  #include "doc.h"
42
+ #include "sink.h"
40
43
 
41
44
  #define YEP_CBOR_INDEF UINT64_MAX
42
45
  #define YEP_CBOR_MAX_ARG_BYTES 8
43
46
 
44
47
  typedef struct {
45
- uint64_t rem; /* items left; maps count 2*pairs; INDEF = until break */
48
+ uint64_t rem; /* items left; maps count 2*pairs; INDEF = until break */
49
+ uint64_t consumed; /* items landed (the key/value parity) */
46
50
  uint8_t is_map;
47
51
  } yep_cframe;
48
52
 
53
+ #define YEP_CBOR_TAGBUF \
54
+ 512 /* a semantic-tag chain over ~23 links is \
55
+ * pathological; the arena form had no cap, \
56
+ * the stack form does (documented) */
57
+
49
58
  typedef struct {
50
- yep_dom* d;
59
+ yep_dom* d; /* the DOM sink's ctx (NULL for pure sinks) */
51
60
  const unsigned char* p;
52
61
  size_t len;
53
62
  size_t i;
54
63
  int strict;
55
64
  YeptrisStatus status;
56
- /* pending tag chain: tag numbers arrive before their content and
57
- * render into the arena as "N N N" (outermost first — the encoder
58
- * splits it back); the span attaches to the next data item */
59
- yep_sview pending_tag;
60
- int have_pending;
65
+ /* the pending semantic-tag chain, text form ("N N N", outermost
66
+ * first — the next item's writer consumes it) */
67
+ char tagbuf[YEP_CBOR_TAGBUF];
68
+ uint32_t taglen;
69
+ /* indefinite-string accumulation (chunk framing is the grammar's;
70
+ * the finished span goes to one str/bytes callback) */
71
+ unsigned char* ibuf;
72
+ size_t icap;
73
+ uint32_t ilen;
74
+ const yep_cbor_sink* s;
61
75
  yep_cframe frame[YEP_DOM_MAX_DEPTH];
62
- int depth; /* == d->depth by construction */
76
+ int depth;
63
77
  } yep_cdec;
64
78
 
65
79
  static void cbor_fail(yep_cdec* c, yep_err_code code, const char* msg) {
@@ -146,116 +160,74 @@ static double cbor_half(uint16_t h) {
146
160
  return (h & 0x8000u) != 0 ? -val : val;
147
161
  }
148
162
 
149
- /* Scalar from arena text (numbers, diagnostics, literals). */
150
- static uint32_t cbor_text_node(yep_cdec* c, const char* text, uint32_t n, uint8_t tag_id) {
151
- yep_view tag_v;
152
- const yep_view* tag = NULL;
153
- if (c->have_pending) {
154
- /* the pending chain lives in the DOM arena (offsets, not
155
- * pointers — the arena reallocs as it grows) */
156
- tag_v.p = c->d->str + (c->pending_tag.off & YEP_SV_OFF);
157
- tag_v.len = c->pending_tag.len;
158
- tag = &tag_v;
159
- c->have_pending = 0;
160
- }
161
- uint32_t id = dom_open_node(c->d, YEP_DOM_SCALAR, tag, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
162
- if (id == UINT32_MAX) {
163
- c->status = YEPTRIS_ERROR_MEMORY;
164
- return UINT32_MAX;
165
- }
166
- yep_dnode* node = &c->d->nodes[id];
167
- char* dst = yep_dom_str_tail(c->d, n);
168
- if (dst == NULL) {
169
- c->status = YEPTRIS_ERROR_MEMORY;
170
- return UINT32_MAX;
171
- }
172
- memcpy(dst, text, n);
173
- node->value = yep_dom_str_commit(c->d, n);
174
- node->tag_id = tag_id;
175
- if (dom_place(c->d, id) != 0) {
176
- c->status = YEPTRIS_ERROR_MEMORY;
177
- return UINT32_MAX;
163
+ /* The pending semantic-tag chain, consumed by the next writer. */
164
+ static const char* cbor_take_tag(yep_cdec* c, uint32_t* len) {
165
+ if (c->taglen == 0) {
166
+ *len = 0;
167
+ return NULL;
178
168
  }
179
- return id;
169
+ *len = c->taglen;
170
+ c->taglen = 0;
171
+ return c->tagbuf;
180
172
  }
181
173
 
182
- /* Scalar borrowing the input (definite strings, bignum byte strings). */
183
- static uint32_t cbor_span_node(yep_cdec* c, const unsigned char* p, uint32_t n) {
184
- yep_view tag_v;
185
- const yep_view* tag = NULL;
186
- if (c->have_pending) {
187
- tag_v.p = c->d->str + (c->pending_tag.off & YEP_SV_OFF);
188
- tag_v.len = c->pending_tag.len;
189
- tag = &tag_v;
190
- c->have_pending = 0;
191
- }
192
- uint32_t id = dom_open_node(c->d, YEP_DOM_SCALAR, tag, NULL, 0, YEP_STYLE_DOUBLE_QUOTED, 0, 0);
193
- if (id == UINT32_MAX) {
174
+ /* Rendered text (numbers-as-text, literals, diagnostics). */
175
+ static uint32_t cbor_text_node(yep_cdec* c, const char* text, uint32_t n, uint8_t tag_id,
176
+ int is_key) {
177
+ uint32_t tag_len;
178
+ const char* tag = cbor_take_tag(c, &tag_len);
179
+ if (!c->s->text(c->s->ctx, text, n, tag_id, is_key, tag, tag_len)) {
194
180
  c->status = YEPTRIS_ERROR_MEMORY;
195
181
  return UINT32_MAX;
196
182
  }
197
- yep_dnode* node = &c->d->nodes[id];
198
- node->value.off = (uint32_t)((const char*)p - c->d->input_base);
199
- node->value.len = n;
200
- if (dom_place(c->d, id) != 0) {
183
+ return 0; /* item identity is the sink's business */
184
+ }
185
+
186
+ /* Definite string (mt2 -> bytes_val, mt3 -> str_val), borrowing the input. */
187
+ static uint32_t cbor_span_node(yep_cdec* c, const unsigned char* p, uint32_t n, uint8_t mt,
188
+ int is_key) {
189
+ uint32_t tag_len;
190
+ const char* tag = cbor_take_tag(c, &tag_len);
191
+ int ok = (mt == 2) ? c->s->bytes_val(c->s->ctx, p, n, 1, is_key, tag, tag_len)
192
+ : c->s->str_val(c->s->ctx, p, n, 1, is_key, tag, tag_len);
193
+ if (!ok) {
201
194
  c->status = YEPTRIS_ERROR_MEMORY;
202
195
  return UINT32_MAX;
203
196
  }
204
- return id;
197
+ return 0;
205
198
  }
206
199
 
207
- static int cbor_open(yep_cdec* c, int is_map) {
200
+ static int cbor_open(yep_cdec* c, int is_map, uint64_t cap) {
208
201
  if (c->depth >= YEP_DOM_MAX_DEPTH) {
209
202
  yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, c->i,
210
203
  "CBOR nesting exceeds the depth guard at byte %zu", c->i);
211
204
  c->status = YEPTRIS_ERROR_DEPTH;
212
205
  return 0;
213
206
  }
214
- yep_view tag_v;
215
- const yep_view* tag = NULL;
216
- if (c->have_pending) {
217
- tag_v.p = c->d->str + (c->pending_tag.off & YEP_SV_OFF);
218
- tag_v.len = c->pending_tag.len;
219
- tag = &tag_v;
220
- c->have_pending = 0;
221
- }
222
- uint32_t id =
223
- dom_open_node(c->d, is_map ? YEP_DOM_MAPPING : YEP_DOM_SEQUENCE, tag, NULL, 0, 0, 0, 1);
224
- if (id == UINT32_MAX) {
207
+ uint32_t tag_len;
208
+ const char* tag = cbor_take_tag(c, &tag_len);
209
+ if (!c->s->open(c->s->ctx, is_map, cap, tag, tag_len)) {
225
210
  c->status = YEPTRIS_ERROR_MEMORY;
226
211
  return 0;
227
212
  }
228
- if (dom_place(c->d, id) != 0) {
229
- c->status = YEPTRIS_ERROR_MEMORY;
230
- return 0;
231
- }
232
- c->d->map_pending_key[c->d->depth] = 0;
233
- c->d->stack[c->d->depth++] = id;
234
- c->depth++; /* the frame mirrors the DOM's stack one-to-one */
213
+ /* a sibling at this depth may have left state behind */
214
+ c->frame[c->depth].consumed = 0;
215
+ c->depth++;
235
216
  return 1;
236
217
  }
237
218
 
238
219
  static int cbor_note_tag(yep_cdec* c, uint64_t tag) {
239
- char buf[24];
240
- uint32_t n = 0;
241
- if (c->have_pending) {
242
- buf[n++] = ' '; /* separator between chain links */
243
- }
244
- n += cbor_u64dec(tag, buf + n);
245
- char* dst = yep_dom_str_tail(c->d, n);
246
- if (dst == NULL) {
247
- c->status = YEPTRIS_ERROR_MEMORY;
220
+ if (c->taglen + 1 + 20 > sizeof c->tagbuf) {
221
+ yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, c->i,
222
+ "semantic-tag chain exceeds %u bytes at byte %zu", (unsigned)sizeof c->tagbuf,
223
+ c->i);
224
+ c->status = YEPTRIS_ERROR_PARSE;
248
225
  return 0;
249
226
  }
250
- yep_sview piece = yep_dom_str_commit(c->d, n);
251
- if (!c->have_pending) {
252
- c->pending_tag = piece;
253
- c->pending_tag.len = 0;
254
- c->have_pending = 1;
227
+ if (c->taglen > 0) {
228
+ c->tagbuf[c->taglen++] = ' '; /* separator between chain links */
255
229
  }
256
- memcpy(dst, buf, n);
257
- /* piece-by-piece commit keeps the chain contiguous in the arena */
258
- c->pending_tag.len += n;
230
+ c->taglen += cbor_u64dec(tag, c->tagbuf + c->taglen);
259
231
  return 1;
260
232
  }
261
233
 
@@ -263,7 +235,6 @@ static int cbor_note_tag(yep_cdec* c, uint64_t tag) {
263
235
  * arena; text chunks validate per chunk — RFC 8949 s3.2.3's code-point
264
236
  * boundary rule makes per-chunk validity imply whole-string validity). */
265
237
  static int cbor_indef_string(yep_cdec* c, int is_text) {
266
- uint32_t first = 0;
267
238
  uint32_t total = 0;
268
239
  for (;;) {
269
240
  if (c->i >= c->len) {
@@ -291,75 +262,55 @@ static int cbor_indef_string(yep_cdec* c, int is_text) {
291
262
  return 0;
292
263
  }
293
264
  if (n > 0) { /* zero-length chunks are legal, just empty */
294
- char* dst = yep_dom_str_tail(c->d, (uint32_t)n);
295
- if (dst == NULL) {
296
- c->status = YEPTRIS_ERROR_MEMORY;
297
- return 0;
298
- }
299
- yep_sview piece = yep_dom_str_commit(c->d, (uint32_t)n);
300
- memcpy(dst, c->p + c->i, (size_t)n);
301
- if (total == 0) {
302
- first = piece.off;
265
+ if (total + n > c->icap) {
266
+ size_t ncap = c->icap ? c->icap * 2 : 256;
267
+ while (ncap < total + n) {
268
+ ncap *= 2;
269
+ }
270
+ unsigned char* nib = realloc(c->ibuf, ncap);
271
+ if (nib == NULL) {
272
+ c->status = YEPTRIS_ERROR_MEMORY;
273
+ return 0;
274
+ }
275
+ c->ibuf = nib;
276
+ c->icap = ncap;
303
277
  }
278
+ memcpy(c->ibuf + total, c->p + c->i, (size_t)n);
304
279
  total += (uint32_t)n;
305
280
  }
306
281
  c->i += (size_t)n;
307
282
  }
308
- /* node over the concatenated arena span */
309
- yep_view tag_v;
310
- const yep_view* tag = NULL;
311
- if (c->have_pending) {
312
- tag_v.p = c->d->str + (c->pending_tag.off & YEP_SV_OFF);
313
- tag_v.len = c->pending_tag.len;
314
- tag = &tag_v;
315
- c->have_pending = 0;
316
- }
317
- uint32_t id = dom_open_node(c->d, YEP_DOM_SCALAR, tag, NULL, 0, YEP_STYLE_DOUBLE_QUOTED, 0, 0);
318
- if (id == UINT32_MAX) {
319
- c->status = YEPTRIS_ERROR_MEMORY;
320
- return 0;
321
- }
322
- yep_dnode* node = &c->d->nodes[id];
323
- node->value.off = first;
324
- node->value.len = total;
325
- if (dom_place(c->d, id) != 0) {
283
+ /* one value over the accumulated span */
284
+ uint32_t tag_len;
285
+ const char* tag = cbor_take_tag(c, &tag_len);
286
+ int ok =
287
+ (is_text ? c->s->str_val : c->s->bytes_val)(c->s->ctx, c->ibuf, total, 0, 0, tag, tag_len);
288
+ if (!ok) {
326
289
  c->status = YEPTRIS_ERROR_MEMORY;
327
290
  return 0;
328
291
  }
329
292
  return 1;
330
293
  }
331
294
 
332
- /* yep_json_number-style inline render: one node for a known integer. */
333
- static int cbor_int_node(yep_cdec* c, int negative, uint64_t mag) {
334
- char buf[24];
335
- uint32_t n = 0;
336
- uint8_t tag_id;
337
- if (negative) {
338
- buf[n++] = '-';
295
+ /* Known integer: value = negative ? -mag : mag, mag <= 2^63. */
296
+ static int cbor_int_node(yep_cdec* c, int negative, uint64_t mag, int is_key) {
297
+ uint32_t tag_len;
298
+ const char* tag = cbor_take_tag(c, &tag_len);
299
+ if (!c->s->int_val(c->s->ctx, negative, mag, is_key, tag, tag_len)) {
300
+ c->status = YEPTRIS_ERROR_MEMORY;
301
+ return 0;
339
302
  }
340
- n += cbor_u64dec(mag, buf + n);
341
- tag_id = YEPTRIS_TAG_INT;
342
- return cbor_text_node(c, buf, n, tag_id) != UINT32_MAX;
303
+ return 1;
343
304
  }
344
305
 
345
- static int cbor_float_node(yep_cdec* c, double dv) {
346
- char buf[48];
347
- uint32_t n;
348
- uint8_t tag_id = YEPTRIS_TAG_FLOAT;
349
- if (dv != dv) {
350
- memcpy(buf, "NaN", 3);
351
- n = 3;
352
- } else if (dv > 1.7976931348623157e308) {
353
- memcpy(buf, "Infinity", 8);
354
- n = 8;
355
- } else if (dv < -1.7976931348623157e308) {
356
- memcpy(buf, "-Infinity", 9);
357
- n = 9;
358
- } else {
359
- n = (uint32_t)yep_d2s_shortest(dv, buf);
306
+ static int cbor_float_node(yep_cdec* c, double dv, int is_key) {
307
+ uint32_t tag_len;
308
+ const char* tag = cbor_take_tag(c, &tag_len);
309
+ if (!c->s->float_val(c->s->ctx, dv, is_key, tag, tag_len)) {
310
+ c->status = YEPTRIS_ERROR_MEMORY;
311
+ return 0;
360
312
  }
361
- (void)tag_id;
362
- return cbor_text_node(c, buf, n, YEPTRIS_TAG_FLOAT) != UINT32_MAX;
313
+ return 1;
363
314
  }
364
315
 
365
316
  /* The diagnostic renderer for lenient-mode non-string SCALAR map keys
@@ -408,9 +359,9 @@ static int cbor_key_diag(yep_cdec* c, uint8_t mt, uint64_t val, double dv) {
408
359
  }
409
360
  break;
410
361
  default:
411
- return cbor_float_node(c, dv); /* floats: the shortest text */
362
+ return cbor_float_node(c, dv, 1); /* floats: the shortest text */
412
363
  }
413
- return cbor_text_node(c, buf, n, 0) != UINT32_MAX;
364
+ return cbor_text_node(c, buf, n, 0, 1) != UINT32_MAX;
414
365
  }
415
366
 
416
367
  /* Decodes ONE data item (scalar: creates+places the node; container:
@@ -425,9 +376,10 @@ static int cbor_item(yep_cdec* c) {
425
376
  uint8_t ai = ib & 0x1F;
426
377
 
427
378
  /* the map-key position decides representation before the item is
428
- * built (the DOM pairs whatever node lands there) */
379
+ * built: even items consumed = key, odd = value (the frame's own
380
+ * parity — the DOM's placement machine tracks its own copy) */
429
381
  int key_slot = 0;
430
- if (c->depth > 0 && c->frame[c->depth - 1].is_map && !c->d->map_pending_key[c->d->depth - 1]) {
382
+ if (c->depth > 0 && c->frame[c->depth - 1].is_map && c->frame[c->depth - 1].consumed % 2 == 0) {
431
383
  key_slot = 1;
432
384
  }
433
385
 
@@ -444,7 +396,7 @@ static int cbor_item(yep_cdec* c) {
444
396
  if (key_slot) {
445
397
  CBOR_REJECT("structure as map key");
446
398
  }
447
- if (!cbor_open(c, 0)) {
399
+ if (!cbor_open(c, 0, YEP_CBOR_INDEF)) {
448
400
  return 0;
449
401
  }
450
402
  c->frame[c->depth - 1].rem = YEP_CBOR_INDEF;
@@ -454,7 +406,7 @@ static int cbor_item(yep_cdec* c) {
454
406
  if (key_slot) {
455
407
  CBOR_REJECT("structure as map key");
456
408
  }
457
- if (!cbor_open(c, 1)) {
409
+ if (!cbor_open(c, 1, YEP_CBOR_INDEF)) {
458
410
  return 0;
459
411
  }
460
412
  c->frame[c->depth - 1].rem = YEP_CBOR_INDEF;
@@ -482,9 +434,9 @@ static int cbor_item(yep_cdec* c) {
482
434
  return cbor_key_diag(c, 0, arg, 0) ? 1 : 0;
483
435
  }
484
436
  if (arg <= (uint64_t)INT64_MAX) {
485
- return cbor_int_node(c, 0, arg) ? 1 : 0;
437
+ return cbor_int_node(c, 0, arg, key_slot) ? 1 : 0;
486
438
  }
487
- return cbor_float_node(c, (double)arg) ? 1 : 0;
439
+ return cbor_float_node(c, (double)arg, key_slot) ? 1 : 0;
488
440
  }
489
441
  case 1: {
490
442
  if (key_slot && c->strict) {
@@ -495,9 +447,9 @@ static int cbor_item(yep_cdec* c) {
495
447
  }
496
448
  if (arg <= (uint64_t)INT64_MAX + 1) { /* -1-2^63 == INT64_MIN */
497
449
  uint64_t mag = arg + 1;
498
- return cbor_int_node(c, 1, mag) ? 1 : 0;
450
+ return cbor_int_node(c, 1, mag, key_slot) ? 1 : 0;
499
451
  }
500
- return cbor_float_node(c, -((double)arg + 1.0)) ? 1 : 0;
452
+ return cbor_float_node(c, -((double)arg + 1.0), key_slot) ? 1 : 0;
501
453
  }
502
454
  case 2:
503
455
  case 3: {
@@ -518,7 +470,7 @@ static int cbor_item(yep_cdec* c) {
518
470
  c->i += (size_t)arg;
519
471
  return ok ? 1 : 0;
520
472
  }
521
- uint32_t id = cbor_span_node(c, c->p + c->i, (uint32_t)arg);
473
+ uint32_t id = cbor_span_node(c, c->p + c->i, (uint32_t)arg, (uint8_t)mt, key_slot);
522
474
  c->i += (size_t)arg;
523
475
  return id != UINT32_MAX ? 1 : 0;
524
476
  }
@@ -529,7 +481,7 @@ static int cbor_item(yep_cdec* c) {
529
481
  if (arg > c->len - c->i) { /* every item consumes >= 1 byte */
530
482
  CBOR_REJECT("array overruns the input");
531
483
  }
532
- if (!cbor_open(c, 0)) {
484
+ if (!cbor_open(c, 0, arg)) {
533
485
  return 0;
534
486
  }
535
487
  c->frame[c->depth - 1].rem = arg;
@@ -543,7 +495,7 @@ static int cbor_item(yep_cdec* c) {
543
495
  if (arg > (c->len - c->i) / 2) { /* pairs need 2 items each */
544
496
  CBOR_REJECT("map overruns the input");
545
497
  }
546
- if (!cbor_open(c, 1)) {
498
+ if (!cbor_open(c, 1, arg * 2)) {
547
499
  return 0;
548
500
  }
549
501
  c->frame[c->depth - 1].rem = arg * 2;
@@ -566,22 +518,22 @@ static int cbor_item(yep_cdec* c) {
566
518
  if (key_slot) {
567
519
  return cbor_key_diag(c, 7, 20, 0) ? 1 : 0;
568
520
  }
569
- return cbor_text_node(c, "false", 5, YEPTRIS_TAG_BOOL) != UINT32_MAX ? 1 : 0;
521
+ return cbor_text_node(c, "false", 5, YEPTRIS_TAG_BOOL, key_slot) != UINT32_MAX ? 1 : 0;
570
522
  case 21:
571
523
  if (key_slot) {
572
524
  return cbor_key_diag(c, 7, 21, 0) ? 1 : 0;
573
525
  }
574
- return cbor_text_node(c, "true", 4, YEPTRIS_TAG_BOOL) != UINT32_MAX ? 1 : 0;
526
+ return cbor_text_node(c, "true", 4, YEPTRIS_TAG_BOOL, key_slot) != UINT32_MAX ? 1 : 0;
575
527
  case 22:
576
528
  if (key_slot) {
577
529
  return cbor_key_diag(c, 7, 22, 0) ? 1 : 0;
578
530
  }
579
- return cbor_text_node(c, "null", 4, YEPTRIS_TAG_NULL) != UINT32_MAX ? 1 : 0;
531
+ return cbor_text_node(c, "null", 4, YEPTRIS_TAG_NULL, key_slot) != UINT32_MAX ? 1 : 0;
580
532
  case 23:
581
533
  if (key_slot) {
582
534
  return cbor_key_diag(c, 7, 23, 0) ? 1 : 0;
583
535
  }
584
- return cbor_text_node(c, "undefined", 9, 0) != UINT32_MAX ? 1 : 0;
536
+ return cbor_text_node(c, "undefined", 9, 0, key_slot) != UINT32_MAX ? 1 : 0;
585
537
  case 24: {
586
538
  if (c->len - c->i < 1) {
587
539
  CBOR_REJECT("truncated simple value");
@@ -597,7 +549,7 @@ static int cbor_item(yep_cdec* c) {
597
549
  memcpy(buf, "simple(", 7);
598
550
  uint32_t n = 7 + cbor_u64dec(v, buf + 7);
599
551
  buf[n++] = ')';
600
- return cbor_text_node(c, buf, n, 0) != UINT32_MAX ? 1 : 0;
552
+ return cbor_text_node(c, buf, n, 0, key_slot) != UINT32_MAX ? 1 : 0;
601
553
  }
602
554
  case 25: {
603
555
  if (c->len - c->i < 2) {
@@ -641,7 +593,7 @@ static int cbor_item(yep_cdec* c) {
641
593
  memcpy(buf, "simple(", 7);
642
594
  uint32_t n = 7 + cbor_u64dec(ai, buf + 7);
643
595
  buf[n++] = ')';
644
- return cbor_text_node(c, buf, n, 0) != UINT32_MAX ? 1 : 0;
596
+ return cbor_text_node(c, buf, n, 0, key_slot) != UINT32_MAX ? 1 : 0;
645
597
  }
646
598
  CBOR_REJECT("reserved additional information");
647
599
  }
@@ -652,34 +604,191 @@ static int cbor_item(yep_cdec* c) {
652
604
  if (key_slot) {
653
605
  return cbor_key_diag(c, 8, 0, dv) ? 1 : 0;
654
606
  }
655
- return cbor_float_node(c, dv) ? 1 : 0;
607
+ return cbor_float_node(c, dv, key_slot) ? 1 : 0;
656
608
  }
657
609
 
658
- int yep_cbor_decode_dom(yep_dom* d, const unsigned char* p, size_t len, int strict,
659
- size_t* consumed) {
610
+ /* The DOM sink: the yeptris_cbor_decode public path. Each impl is the
611
+ * pre-sink writer, byte for byte (the differential suite pins it). */
612
+
613
+ static int dom_sink_text(void* dp, const char* s, uint32_t n, uint8_t tag_id, int is_key,
614
+ const char* tag, uint32_t tag_len) {
615
+ yep_dom* d = dp;
616
+ (void)is_key;
617
+ yep_view tag_v;
618
+ const yep_view* tagp = NULL;
619
+ if (tag != NULL) {
620
+ char* dst = yep_dom_str_tail(d, tag_len);
621
+ if (dst == NULL) {
622
+ return 0;
623
+ }
624
+ memcpy(dst, tag, tag_len);
625
+ yep_sview piece = yep_dom_str_commit(d, tag_len);
626
+ tag_v.p = d->str + (piece.off & YEP_SV_OFF);
627
+ tag_v.len = tag_len;
628
+ tagp = &tag_v;
629
+ }
630
+ uint32_t id = dom_open_node(d, YEP_DOM_SCALAR, tagp, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
631
+ if (id == UINT32_MAX) {
632
+ return 0;
633
+ }
634
+ yep_dnode* node = &d->nodes[id];
635
+ char* dst = yep_dom_str_tail(d, n);
636
+ if (dst == NULL) {
637
+ return 0;
638
+ }
639
+ memcpy(dst, s, n);
640
+ node->value = yep_dom_str_commit(d, n);
641
+ node->tag_id = tag_id;
642
+ return dom_place(d, id) == 0;
643
+ }
644
+
645
+ static int dom_sink_span(void* dp, const unsigned char* p, uint32_t n, int borrowed, int is_key,
646
+ const char* tag, uint32_t tag_len) {
647
+ yep_dom* d = dp;
648
+ (void)is_key;
649
+ (void)borrowed; /* 0 rides the scratch buffer: still copied to the arena */
650
+ yep_view tag_v;
651
+ const yep_view* tagp = NULL;
652
+ if (tag != NULL) {
653
+ char* dst = yep_dom_str_tail(d, tag_len);
654
+ if (dst == NULL) {
655
+ return 0;
656
+ }
657
+ memcpy(dst, tag, tag_len);
658
+ yep_sview piece = yep_dom_str_commit(d, tag_len);
659
+ tag_v.p = d->str + (piece.off & YEP_SV_OFF);
660
+ tag_v.len = tag_len;
661
+ tagp = &tag_v;
662
+ }
663
+ uint32_t id = dom_open_node(d, YEP_DOM_SCALAR, tagp, NULL, 0, YEP_STYLE_DOUBLE_QUOTED, 0, 0);
664
+ if (id == UINT32_MAX) {
665
+ return 0;
666
+ }
667
+ yep_dnode* node = &d->nodes[id];
668
+ if (borrowed) {
669
+ node->value.off = (uint32_t)((const char*)p - d->input_base);
670
+ node->value.len = n;
671
+ } else if (n > 0) { /* the empty indefinite string needs no arena */
672
+ char* dst = yep_dom_str_tail(d, n);
673
+ if (dst == NULL) {
674
+ return 0;
675
+ }
676
+ memcpy(dst, p, n);
677
+ node->value = yep_dom_str_commit(d, n);
678
+ }
679
+ return dom_place(d, id) == 0;
680
+ }
681
+
682
+ static int dom_sink_str(void* dp, const unsigned char* p, uint32_t n, int borrowed, int is_key,
683
+ const char* tag, uint32_t tag_len) {
684
+ return dom_sink_span(dp, p, n, borrowed, is_key, tag, tag_len);
685
+ }
686
+
687
+ static int dom_sink_bytes(void* dp, const unsigned char* p, uint32_t n, int borrowed, int is_key,
688
+ const char* tag, uint32_t tag_len) {
689
+ return dom_sink_span(dp, p, n, borrowed, is_key, tag, tag_len);
690
+ }
691
+
692
+ static int dom_sink_open(void* dp, int is_map, uint64_t cap, const char* tag, uint32_t tag_len) {
693
+ yep_dom* d = dp;
694
+ (void)cap;
695
+ yep_view tag_v;
696
+ const yep_view* tagp = NULL;
697
+ if (tag != NULL) {
698
+ char* dst = yep_dom_str_tail(d, tag_len);
699
+ if (dst == NULL) {
700
+ return 0;
701
+ }
702
+ memcpy(dst, tag, tag_len);
703
+ yep_sview piece = yep_dom_str_commit(d, tag_len);
704
+ tag_v.p = d->str + (piece.off & YEP_SV_OFF);
705
+ tag_v.len = tag_len;
706
+ tagp = &tag_v;
707
+ }
708
+ uint32_t id =
709
+ dom_open_node(d, is_map ? YEP_DOM_MAPPING : YEP_DOM_SEQUENCE, tagp, NULL, 0, 0, 0, 1);
710
+ if (id == UINT32_MAX) {
711
+ return 0;
712
+ }
713
+ if (dom_place(d, id) != 0) {
714
+ return 0;
715
+ }
716
+ d->map_pending_key[d->depth] = 0;
717
+ d->stack[d->depth++] = id;
718
+ return 1;
719
+ }
720
+
721
+ static int dom_sink_close(void* dp, int is_map) {
722
+ yep_dom* d = dp;
723
+ (void)is_map;
724
+ d->depth--;
725
+ return 1;
726
+ }
727
+
728
+ static int dom_sink_int(void* dp, int negative, uint64_t mag, int is_key, const char* tag,
729
+ uint32_t tag_len) {
730
+ char buf[24];
731
+ uint32_t n = 0;
732
+ if (negative) {
733
+ buf[n++] = '-';
734
+ }
735
+ n += cbor_u64dec(mag, buf + n);
736
+ return dom_sink_text(dp, buf, n, YEPTRIS_TAG_INT, is_key, tag, tag_len);
737
+ }
738
+
739
+ static int dom_sink_float(void* dp, double dv, int is_key, const char* tag, uint32_t tag_len) {
740
+ char buf[48];
741
+ (void)is_key;
742
+ uint32_t n;
743
+ if (dv != dv) {
744
+ memcpy(buf, "NaN", 3);
745
+ n = 3;
746
+ } else if (dv > 1.7976931348623157e308) {
747
+ memcpy(buf, "Infinity", 8);
748
+ n = 8;
749
+ } else if (dv < -1.7976931348623157e308) {
750
+ memcpy(buf, "-Infinity", 9);
751
+ n = 9;
752
+ } else {
753
+ n = (uint32_t)yep_d2s_shortest(dv, buf);
754
+ }
755
+ return dom_sink_text(dp, buf, n, YEPTRIS_TAG_FLOAT, is_key, tag, tag_len);
756
+ }
757
+
758
+ static const yep_cbor_sink yep_cbor_dom_sink_impl = {
759
+ NULL, dom_sink_text, dom_sink_int, dom_sink_float,
760
+ dom_sink_str, dom_sink_bytes, dom_sink_open, dom_sink_close,
761
+ };
762
+
763
+ int yep_cbor_decode_gen(const unsigned char* p, size_t len, int strict, size_t* consumed,
764
+ const yep_cbor_sink* sink) {
660
765
  yep_cdec c;
661
766
  memset(&c, 0, sizeof(c));
662
- c.d = d;
767
+ c.s = sink;
663
768
  c.p = p;
664
769
  c.len = len;
665
770
  c.strict = strict;
666
771
  c.status = YEPTRIS_OK;
667
772
  int done_top = 0; /* the ONE top-level item is done (tags never count) */
773
+ int rc;
668
774
  for (;;) {
669
775
  if (c.depth == 0) {
670
776
  if (done_top) { /* the root closed; one data item per call */
671
777
  if (c.i != len && consumed == NULL) {
672
778
  cbor_fail(&c, YEP_ERR_UNEXPECTED, "trailing bytes after the data item");
673
- return YEPTRIS_ERROR_PARSE;
779
+ rc = YEPTRIS_ERROR_PARSE;
780
+ goto done;
674
781
  }
675
782
  if (consumed != NULL) {
676
783
  *consumed = c.i;
677
784
  }
678
- return YEPTRIS_OK;
785
+ rc = YEPTRIS_OK;
786
+ goto done;
679
787
  }
680
788
  int st = cbor_item(&c);
681
789
  if (st == 0) {
682
- return (int)c.status;
790
+ rc = (int)c.status;
791
+ goto done;
683
792
  }
684
793
  if (st == 2) {
685
794
  continue; /* tag chain: the content item follows */
@@ -689,19 +798,26 @@ int yep_cbor_decode_dom(yep_dom* d, const unsigned char* p, size_t len, int stri
689
798
  }
690
799
  yep_cframe* fr = &c.frame[c.depth - 1];
691
800
  if (fr->rem == 0) { /* definite container complete */
692
- c.d->depth--;
801
+ if (!c.s->close(c.s->ctx, fr->is_map)) {
802
+ rc = YEPTRIS_ERROR_MEMORY;
803
+ goto done;
804
+ }
693
805
  c.depth--;
694
806
  continue;
695
807
  }
696
808
  if (fr->rem == YEP_CBOR_INDEF) {
697
809
  if (c.i < c.len && c.p[c.i] == 0xFF) {
698
- if (fr->is_map && c.d->map_pending_key[c.d->depth - 1]) {
810
+ if (fr->is_map && fr->consumed % 2 == 1) {
699
811
  cbor_fail(&c, YEP_ERR_UNEXPECTED,
700
812
  "break in a map value position (odd item count)");
701
- return YEPTRIS_ERROR_PARSE;
813
+ rc = YEPTRIS_ERROR_PARSE;
814
+ goto done;
702
815
  }
703
816
  c.i++;
704
- c.d->depth--;
817
+ if (!c.s->close(c.s->ctx, fr->is_map)) {
818
+ rc = YEPTRIS_ERROR_MEMORY;
819
+ goto done;
820
+ }
705
821
  c.depth--;
706
822
  continue;
707
823
  }
@@ -709,16 +825,33 @@ int yep_cbor_decode_dom(yep_dom* d, const unsigned char* p, size_t len, int stri
709
825
  size_t parent = (size_t)(c.depth - 1);
710
826
  int st = cbor_item(&c);
711
827
  if (st == 0) {
712
- return (int)c.status;
828
+ rc = (int)c.status;
829
+ goto done;
713
830
  }
714
831
  if (st == 2) {
715
832
  continue; /* tags never consume a container slot */
716
833
  }
834
+ c.frame[parent].consumed++;
717
835
  if (c.frame[parent].rem != YEP_CBOR_INDEF) {
718
836
  c.frame[parent].rem--;
719
837
  /* one item (scalar, or a container now open) consumed a parent slot */
720
838
  }
721
839
  }
840
+ done:
841
+ free(c.ibuf);
842
+ return rc;
843
+ }
844
+
845
+ int yep_cbor_decode_dom_sink(void* dp, const unsigned char* p, size_t len, int strict,
846
+ size_t* consumed) {
847
+ yep_cbor_sink s = yep_cbor_dom_sink_impl;
848
+ s.ctx = dp;
849
+ return yep_cbor_decode_gen(p, len, strict, consumed, &s);
850
+ }
851
+
852
+ int yep_cbor_decode_dom(yep_dom* d, const unsigned char* p, size_t len, int strict,
853
+ size_t* consumed) {
854
+ return yep_cbor_decode_dom_sink(d, p, len, strict, consumed);
722
855
  }
723
856
 
724
857
  static YeptrisDocument cbor_wrap(yep_dom* dom, const void* buf, const yep_allocator* sys) {
@@ -0,0 +1,58 @@
1
+ /* sink.h — the CBOR decode seam (#157).
2
+ *
3
+ * The grammar loop (decode.c's cbor_item machinery) is the single
4
+ * source of CBOR semantics; a sink implements the item constructors
5
+ * on top of it. Two sinks exist: the DOM builder (the default, the
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
8
+ * and the pending semantic-tag chain in its text form ("N N N",
9
+ * outermost first — NULL when the item carries no chain).
10
+ */
11
+ #ifndef YEP_CBOR_SINK_H
12
+ #define YEP_CBOR_SINK_H
13
+
14
+ #include <stddef.h>
15
+ #include <stdint.h>
16
+
17
+ #include <yeptris/api.h> /* YEPTRIS_API (the ext links these) */
18
+
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;
47
+
48
+ /* The generic engine: the grammar, driving `sink`. Same reject/
49
+ * encoding/depth semantics as the DOM entry. */
50
+ YEPTRIS_API int yep_cbor_decode_gen(const unsigned char* p, size_t len, int strict,
51
+ size_t* consumed, const yep_cbor_sink* sink);
52
+
53
+ /* The DOM sink: pairs yep_cbor_decode_gen with a yep_dom* (the
54
+ * yep_cbor_decode_dom public path's driver; d is a yep_dom*). */
55
+ YEPTRIS_API int yep_cbor_decode_dom_sink(void* d, const unsigned char* p, size_t len, int strict,
56
+ size_t* consumed);
57
+
58
+ #endif /* YEP_CBOR_SINK_H */
@@ -134,8 +134,17 @@ static void wr_indent(yep_writer* w, int depth) {
134
134
 
135
135
  static void emit_dq(yep_writer* w, const char* p, uint32_t n) {
136
136
  wr_byte(w, '"');
137
+ uint32_t run = 0;
137
138
  for (uint32_t i = 0; i < n; i++) {
138
139
  unsigned char c = (unsigned char)p[i];
140
+ if (c >= 0x20 && c != '"' && c != '\\' && c != 0x7f) {
141
+ run++;
142
+ continue;
143
+ }
144
+ if (run) {
145
+ wr_put(w, p + i - run, run);
146
+ run = 0;
147
+ }
139
148
  if (c == '"' || c == '\\') {
140
149
  wr_byte(w, '\\');
141
150
  wr_byte(w, (char)c);
@@ -161,10 +170,11 @@ static void emit_dq(yep_writer* w, const char* p, uint32_t n) {
161
170
  char hex[4] = {'\\', 'x', hd[(c >> 4) & 0xf], hd[c & 0xf]};
162
171
  wr_put(w, hex, 4);
163
172
  }
164
- } else {
165
- wr_byte(w, (char)c);
166
173
  }
167
174
  }
175
+ if (run) {
176
+ wr_put(w, p + n - run, run);
177
+ }
168
178
  wr_byte(w, '"');
169
179
  }
170
180
 
@@ -315,6 +325,10 @@ enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL };
315
325
 
316
326
  static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in,
317
327
  int has_tag) {
328
+ if ((sty_in == 1) && len > 0 &&
329
+ (as_key ? yep_style_plain_key_safe(p, len) : yep_style_plain_safe(p, len))) {
330
+ return YEP_SC_PLAIN; /* plain-safe implies no breaks: memchr skipped */
331
+ }
318
332
  int multiline = (len > 0 && memchr(p, '\n', len) != NULL);
319
333
  int blockable = 0;
320
334
  if (multiline && !as_key) {
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.16.2
4
+ version: 0.6.17.1
5
5
  platform: powerpc64le-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -91,6 +91,7 @@ files:
91
91
  - vendor/libyeptris/src/yeptris/cbor/cbor.h
92
92
  - vendor/libyeptris/src/yeptris/cbor/decode.c
93
93
  - vendor/libyeptris/src/yeptris/cbor/encode.c
94
+ - vendor/libyeptris/src/yeptris/cbor/sink.h
94
95
  - vendor/libyeptris/src/yeptris/common/chartype.c
95
96
  - vendor/libyeptris/src/yeptris/common/chartype.h
96
97
  - vendor/libyeptris/src/yeptris/common/cpu.c