yeptris 0.6.48.0-aarch64-linux → 0.6.50.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: 35fb61a844f3c216af5b507b1734b3b35ec903925bcbb17a8d2ea650fa501eaf
4
- data.tar.gz: 4120fa28eef23c4d25e474030efd19c21a88d663a4db944d2e12f762673b475e
3
+ metadata.gz: '0782e25e15d541b7a7e5448f7c63e79e627a130946d131860133021262d15c28'
4
+ data.tar.gz: daf5279398e226c9ab663ab1482356938a675a7cdd84b0e872134e2f6651322f
5
5
  SHA512:
6
- metadata.gz: 651d7bd9d2b054ebe3af1d2047f304e9a6aeb7e8f3c6dd0a04efb93143f2634b3ae6764d6392a783538589b45e9d6a9f4a7c979647005e565a0c3529ffc7ec7e
7
- data.tar.gz: 2ea6d6b6c9c51c9cfcac877a097f4c86affd0d1af7b51b489891f1766b62880d0011c8c7054d00850315e88c919934c66faebf57dc301d9382ce806ec43ae6ef
6
+ metadata.gz: 423942744e7c5fbaa2c7dc4cde3f088abaf4aaacb88e4095c9e0fd0af1e3bf9774024522fcaa34d97e511ddba3b62e30a53753ee127a50ee3727f2a08fa08ca2
7
+ data.tar.gz: 102f3c0f8f3baa9d78ea65a3124c24c632d8f2e8940835e63c28061c81cb5b16039be3ac8c58e1751943ba3d10ef8287ee245260eb81b8a0a5b3d3a3ee63527e
@@ -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 48
15
- #define YEPTRIS_VERSION_STRING "0.6.48"
14
+ #define YEPTRIS_VERSION_PATCH 50
15
+ #define YEPTRIS_VERSION_STRING "0.6.50"
16
16
 
17
17
  #ifdef __cplusplus
18
18
  extern "C" {
data/lib/yeptris/cbor.rb CHANGED
@@ -77,6 +77,11 @@ module Yeptris
77
77
  end
78
78
 
79
79
  def dump(obj, canonical: true)
80
+ # yeptris-ruby#322: the marshal face answers in one C call when
81
+ # the host has an exact spelling; nil routes to the tree walk
82
+ data = marshal_cbor_dump(obj, canonical)
83
+ return data if data
84
+
80
85
  docs = build_documents([obj])
81
86
  begin
82
87
  encode_one(docs[0], canonical)
@@ -123,6 +128,33 @@ module Yeptris
123
128
  take_buffer(::Yeptris::FFI.yeptris_cbor_encode(doc.c_ptr, opts, len_ptr), len_ptr)
124
129
  end
125
130
 
131
+ # yeptris-ruby#322: Marshal.dump bytes -> one CBOR item in ONE C
132
+ # call (yeptris_cbor_from_marshal, engine 0.6.49+). Returns nil
133
+ # when the route is unavailable or the host's bytes have no exact
134
+ # fast-path spelling (UNSUPPORTED), and the caller keeps the
135
+ # YAMLTree walk, which is exact by construction. Marshal-undumpable
136
+ # hosts raise TypeError; nil lets that walk own the DumpError
137
+ # contract too.
138
+ def marshal_cbor_dump(obj, canonical)
139
+ return nil unless defined?(::Yeptris::FFI::MARSHAL_CBOR) &&
140
+ ::Yeptris::FFI::MARSHAL_CBOR
141
+ m = Marshal.dump(obj)
142
+ out_p = ::FFI::MemoryPointer.new(:pointer)
143
+ olen_p = ::FFI::MemoryPointer.new(:size_t)
144
+ st = ::Yeptris::FFI.yeptris_cbor_from_marshal(
145
+ m, m.bytesize, canonical ? CANONICAL : 0, out_p, olen_p
146
+ )
147
+ return nil unless st == ::Yeptris::FFI::OK
148
+ buf = out_p.read_pointer
149
+ return nil if buf.null?
150
+
151
+ data = buf.read_bytes(::Yeptris::FFI.read_c_size_t(olen_p))
152
+ ::Yeptris::FFI.yeptris_marshal_free(buf)
153
+ data.force_encoding(Encoding::BINARY)
154
+ rescue ::TypeError
155
+ nil
156
+ end
157
+
126
158
  # The encode convenience contract: a malloc'd buffer plus its
127
159
  # byte length — binary data, freed through the library's own free.
128
160
  def take_buffer(ptr, len_ptr)
data/lib/yeptris/ffi.rb CHANGED
@@ -303,6 +303,12 @@ module Yeptris
303
303
  attach_function :yeptris_cbor_encode_sequence,
304
304
  %i[pointer size_t uint32 pointer], :pointer
305
305
  CBOR_EX = !(@missing ||= []).include?(:yeptris_cbor_decode)
306
+ # yeptris-ruby#322: Marshal.dump bytes -> one CBOR item in one C
307
+ # call (engine 0.6.49+); MARSHAL_CBOR goes false on older libraries
308
+ # and CBOR.dump keeps the YAMLTree walk
309
+ attach_function :yeptris_cbor_from_marshal,
310
+ %i[pointer size_t uint32 pointer pointer], :int
311
+ MARSHAL_CBOR = !(@missing ||= []).include?(:yeptris_cbor_from_marshal)
306
312
 
307
313
  # Owned char* results (serialize*): one reader, freed exactly once.
308
314
  # The release goes through yeptris_free (libyeptris's own
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.48.0".freeze
6
+ VERSION = "0.6.50.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.48
6
+ VERSION 0.6.50
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -58,7 +58,8 @@ set(YEPTRIS_SOURCES
58
58
  )
59
59
 
60
60
  if(YEPTRIS_WITH_CBOR)
61
- list(APPEND YEPTRIS_SOURCES yeptris/cbor/decode.c yeptris/cbor/encode.c)
61
+ list(APPEND YEPTRIS_SOURCES yeptris/cbor/decode.c yeptris/cbor/encode.c
62
+ yeptris/cbor/from_marshal.c)
62
63
  endif()
63
64
 
64
65
  # AOT SIMD TUs (TODO.impl/04): per-ISA compile flags; the TUs and the
@@ -94,6 +94,30 @@ YEPTRIS_API size_t yeptris_cbor_encode_into(YeptrisDocument doc, uint32_t opts,
94
94
  * byte count. */
95
95
  YEPTRIS_API void* yeptris_cbor_encode(YeptrisDocument doc, uint32_t opts, size_t* len);
96
96
 
97
+ /* The binding's dump face (yeptris-ruby#322): Ruby Marshal 4.8 bytes
98
+ * IN, ONE CBOR data item OUT, one C call — the CBOR twin of
99
+ * yeptris_json_from_marshal (marshal.h), walked in C with no Document
100
+ * and no per-node host crossings. opts: YEPTRIS_CBOR_CANONICAL sorts
101
+ * each map's pairs by encoded-key bytes; 0 keeps insertion order.
102
+ *
103
+ * Covers the host subset the per-node route spells: nil/bool/int/
104
+ * float/string/array/hash, symbols (as ":name" text), 'I' wrappers,
105
+ * shared-object links (duplicated bytes), bignums up to four 16-bit
106
+ * words (inside int64 as integers, past either end as preferred
107
+ * floats), and the per-node route's map-key spellings (strings,
108
+ * symbols, integers, floats, booleans). Anything else — objects,
109
+ * structs, user marshal, invalid-UTF-8 strings, non-finite float text,
110
+ * wider bignums, nil/array/hash map keys, cycles — returns
111
+ * YEPTRIS_ERROR_UNSUPPORTED and the binding keeps its per-node walk.
112
+ *
113
+ * The bytes are byte-identical to yeptris_cbor_encode over the
114
+ * document the per-node walk builds (pinned by test_cbor_marshal.cpp).
115
+ * *out is malloc'd, free with yeptris_marshal_free (or free) exactly
116
+ * once; on failure nothing is allocated. Trailing bytes after the
117
+ * root are UNSUPPORTED (the stream is exactly one value). */
118
+ YEPTRIS_API YeptrisStatus yeptris_cbor_from_marshal(const char* m, size_t m_len, uint32_t opts,
119
+ char** out, size_t* out_len);
120
+
97
121
  #ifdef __cplusplus
98
122
  }
99
123
  #endif
@@ -31,11 +31,13 @@ YEPTRIS_API YeptrisDocument yeptris_parse_json(const char* data, size_t len, Yep
31
31
  * collections, JSON escapes, shortest floats, null/true/false words).
32
32
  * Returns a malloc'd NUL-terminated buffer (caller frees) with *len
33
33
  * (may be NULL) set, or NULL on a NULL document / allocation
34
- * failure / zero documents. */
34
+ * failure / zero documents / a document with no JSON spelling
35
+ * (unserializable). */
35
36
  YEPTRIS_API char* yeptris_serialize_json(YeptrisDocument doc, size_t* len);
36
37
 
37
38
  /* The compact variant: no padding spaces (JSON.generate's shape);
38
- * compact == 0 is byte-identical to yeptris_serialize_json. */
39
+ * compact == 0 is byte-identical to yeptris_serialize_json. Same
40
+ * NULL contract, the unserializable law included. */
39
41
  YEPTRIS_API char* yeptris_serialize_json_ex(YeptrisDocument doc, size_t* len, int compact);
40
42
 
41
43
  /* The pretty variant: JSON.pretty_generate's shape — one entry per
@@ -74,6 +74,11 @@ typedef struct yeptris_json_tape {
74
74
  outlive the tape — same contract as the spans) */
75
75
  size_t _srclen;
76
76
  void* _block; /* the carved allocation base */
77
+ /* the >1024 simple-key verdict, settled DURING the lenient walk
78
+ * (one compare per map key) — the post-scan re-walk of the whole
79
+ * tape cost 16% of json-users parse (#739 profile, 2026-10-28) */
80
+ unsigned char _long_key_known;
81
+ unsigned char _long_key;
77
82
  } yeptris_json_tape;
78
83
 
79
84
  /* Parses strict JSON into the tape. Accepts scalar roots (a single
@@ -119,14 +119,36 @@ static int cbor_arg(yep_cdec* c, uint8_t ai, uint64_t* val) {
119
119
  }
120
120
 
121
121
  static uint32_t cbor_u64dec(uint64_t v, char* buf) {
122
+ /* two digits per divide: the per-digit %10//10 loop was the decode
123
+ * lane's hottest formatter (the #742 profile) — one /100 plus a
124
+ * table load halves the divides; the reverse pass shrinks to the
125
+ * pair loop */
126
+ static const char PAIRS[] = "00010203040506070809"
127
+ "10111213141516171819"
128
+ "20212223242526272829"
129
+ "30313233343536373839"
130
+ "40414243444546474849"
131
+ "50515253545556575859"
132
+ "60616263646566676869"
133
+ "70717273747576777879"
134
+ "80818283848586878889"
135
+ "90919293949596979899";
122
136
  char tmp[20];
123
137
  uint32_t n = 0;
124
- do {
125
- tmp[n++] = (char)('0' + (v % 10));
126
- v /= 10;
127
- } while (v != 0);
138
+ while (v >= 100) {
139
+ uint32_t r = (uint32_t)(v % 100u);
140
+ v /= 100u;
141
+ tmp[n++] = PAIRS[r * 2u + 1u];
142
+ tmp[n++] = PAIRS[r * 2u];
143
+ }
144
+ if (v >= 10u) {
145
+ tmp[n++] = PAIRS[v * 2u + 1u];
146
+ tmp[n++] = PAIRS[v * 2u];
147
+ } else {
148
+ tmp[n++] = (char)('0' + (int)v);
149
+ }
128
150
  for (uint32_t k = 0; k < n; k++) {
129
- buf[k] = tmp[n - 1 - k];
151
+ buf[k] = tmp[n - 1u - k];
130
152
  }
131
153
  return n;
132
154
  }
@@ -727,13 +749,41 @@ static int dom_sink_close(void* dp, int is_map) {
727
749
 
728
750
  static int dom_sink_int(void* dp, int negative, uint64_t mag, int is_key, const char* tag,
729
751
  uint32_t tag_len) {
730
- char buf[24];
752
+ /* the int path formats straight into the arena (the #742
753
+ * profile's 26% lane): no stack buffer, no second copy through
754
+ * the text sink — tail, format in place, commit */
755
+ (void)is_key;
756
+ yep_dom* d = dp;
757
+ yep_view tag_v;
758
+ const yep_view* tagp = NULL;
759
+ if (tag != NULL) {
760
+ char* tdst = yep_dom_str_tail(d, tag_len);
761
+ if (tdst == NULL) {
762
+ return 0;
763
+ }
764
+ memcpy(tdst, tag, tag_len);
765
+ yep_sview piece = yep_dom_str_commit(d, tag_len);
766
+ tag_v.p = d->str + (piece.off & YEP_SV_OFF);
767
+ tag_v.len = tag_len;
768
+ tagp = &tag_v;
769
+ }
770
+ uint32_t id = dom_open_node(d, YEP_DOM_SCALAR, tagp, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
771
+ if (id == UINT32_MAX) {
772
+ return 0;
773
+ }
774
+ yep_dnode* node = &d->nodes[id];
775
+ char* dst = yep_dom_str_tail(d, 21); /* '-' plus twenty digits */
776
+ if (dst == NULL) {
777
+ return 0;
778
+ }
731
779
  uint32_t n = 0;
732
780
  if (negative) {
733
- buf[n++] = '-';
781
+ dst[n++] = '-';
734
782
  }
735
- n += cbor_u64dec(mag, buf + n);
736
- return dom_sink_text(dp, buf, n, YEPTRIS_TAG_INT, is_key, tag, tag_len);
783
+ n += cbor_u64dec(mag, dst + n);
784
+ node->value = yep_dom_str_commit(d, n);
785
+ node->tag_id = YEPTRIS_TAG_INT;
786
+ return dom_place(d, id) == 0;
737
787
  }
738
788
 
739
789
  static int dom_sink_float(void* dp, double dv, int is_key, const char* tag, uint32_t tag_len) {
@@ -25,6 +25,7 @@
25
25
  * - aliases (YAML input) are UNSUPPORTED: the CBOR model has none
26
26
  */
27
27
  #include "cbor.h"
28
+ #include "encode_priv.h"
28
29
 
29
30
  #include <math.h>
30
31
  #include <stdlib.h>
@@ -40,7 +41,7 @@
40
41
 
41
42
  /* ---- heads: minimal argument encoding (RFC 8949 s3) ----------------- */
42
43
 
43
- static size_t cbor_arg_bytes(uint64_t arg) {
44
+ size_t yep_cbor_arg_bytes(uint64_t arg) {
44
45
  if (arg < 24)
45
46
  return 1;
46
47
  if (arg <= 0xFFull)
@@ -52,10 +53,10 @@ static size_t cbor_arg_bytes(uint64_t arg) {
52
53
  return 9;
53
54
  }
54
55
 
55
- static void cbor_put_head(uint8_t* out, size_t* pos, uint8_t mt, uint64_t arg) {
56
+ void yep_cbor_put_head(uint8_t* out, size_t* pos, uint8_t mt, uint64_t arg) {
56
57
  /* payload bytes -> additional info: 1->24, 2->25, 4->26, 8->27 */
57
58
  static const uint8_t ai_of[9] = {0, 24, 25, 25, 26, 26, 26, 26, 27};
58
- size_t n = cbor_arg_bytes(arg) - 1;
59
+ size_t n = yep_cbor_arg_bytes(arg) - 1;
59
60
  out[(*pos)++] = (uint8_t)((mt << 5) | (n == 0 ? (uint8_t)arg : ai_of[n]));
60
61
  for (size_t k = 0; k < n; k++) { /* network byte order */
61
62
  out[(*pos)++] = (uint8_t)(arg >> (8 * (n - 1 - k)));
@@ -143,7 +144,7 @@ static uint16_t cbor_d2h(double d) {
143
144
  return (uint16_t)(sign | m10);
144
145
  }
145
146
 
146
- static uint8_t cbor_float_width(double d) {
147
+ uint8_t yep_cbor_float_width(double d) {
147
148
  if (d != d) {
148
149
  return 2; /* NaN: the canonical f9 7e00 (s4.2.2) */
149
150
  }
@@ -157,7 +158,7 @@ static uint8_t cbor_float_width(double d) {
157
158
  return 8;
158
159
  }
159
160
 
160
- static void cbor_put_float(uint8_t* out, size_t* pos, double d, uint8_t width) {
161
+ void yep_cbor_put_float(uint8_t* out, size_t* pos, double d, uint8_t width) {
161
162
  out[(*pos)++] = (uint8_t)((7 << 5) | (width == 2 ? 25 : width == 4 ? 26 : 27));
162
163
  uint64_t bits = 0;
163
164
  if (width == 2) {
@@ -221,7 +222,7 @@ static int cbor_scalar_number(const yep_view* v, cbor_num* out) {
221
222
  }
222
223
  out->mt = 7;
223
224
  out->dval = dv;
224
- out->fwidth = cbor_float_width(dv);
225
+ out->fwidth = yep_cbor_float_width(dv);
225
226
  return 1;
226
227
  }
227
228
 
@@ -247,17 +248,17 @@ static int cbor_bool_value(const yep_view* v) {
247
248
  /* The diagnostic round-trip (the 01 ledger): decode renders
248
249
  * undefined/simple(N) as text; encode recognizes them back. The words
249
250
  * are reserved — a literal "undefined" string encodes as the simple. */
250
- static int cbor_simple_of(const yep_view* v) {
251
- if (v->len == 9 && memcmp(v->p, "undefined", 9) == 0) {
251
+ int yep_cbor_simple_of(const char* p, size_t len) {
252
+ if (len == 9 && memcmp(p, "undefined", 9) == 0) {
252
253
  return 23;
253
254
  }
254
- if (v->len > 9 && memcmp(v->p, "simple(", 7) == 0 && v->p[v->len - 1] == ')') {
255
+ if (len > 9 && memcmp(p, "simple(", 7) == 0 && p[len - 1] == ')') {
255
256
  uint64_t val = 0;
256
- for (size_t i = 7; i + 1 < v->len; i++) {
257
- if (v->p[i] < '0' || v->p[i] > '9') {
257
+ for (size_t i = 7; i + 1 < len; i++) {
258
+ if (p[i] < '0' || p[i] > '9') {
258
259
  return -1;
259
260
  }
260
- val = val * 10 + (uint64_t)(v->p[i] - '0');
261
+ val = val * 10 + (uint64_t)(p[i] - '0');
261
262
  if (val > 255) {
262
263
  return -1;
263
264
  }
@@ -337,7 +338,7 @@ static uint8_t* cbor_encode_key(const yep_dom* d, const yep_dnode* key, size_t*
337
338
  goto bad;
338
339
  }
339
340
  for (int t = 0; t < ntags; t++) {
340
- cbor_put_head(buf, &pos, 6, tags[t]);
341
+ yep_cbor_put_head(buf, &pos, 6, tags[t]);
341
342
  }
342
343
  if (key->tag_id == YEPTRIS_TAG_INT || key->tag_id == YEPTRIS_TAG_FLOAT) {
343
344
  cbor_num num;
@@ -345,13 +346,13 @@ static uint8_t* cbor_encode_key(const yep_dom* d, const yep_dnode* key, size_t*
345
346
  goto bad;
346
347
  }
347
348
  if (num.mt != 7) {
348
- cbor_put_head(buf, &pos, num.mt, num.uval);
349
+ yep_cbor_put_head(buf, &pos, num.mt, num.uval);
349
350
  } else {
350
- cbor_put_float(buf, &pos, num.dval, num.fwidth);
351
+ yep_cbor_put_float(buf, &pos, num.dval, num.fwidth);
351
352
  }
352
353
  } else { /* strings and diagnostics: a simple, else a text item */
353
354
  if (key->tag.len == 0) {
354
- int sv = cbor_simple_of(&v);
355
+ int sv = yep_cbor_simple_of(v.p, v.len);
355
356
  if (sv >= 0) {
356
357
  if (sv < 24) {
357
358
  buf[pos++] = (uint8_t)(0xE0 | (uint8_t)sv);
@@ -363,7 +364,7 @@ static uint8_t* cbor_encode_key(const yep_dom* d, const yep_dnode* key, size_t*
363
364
  return buf;
364
365
  }
365
366
  }
366
- cbor_put_head(
367
+ yep_cbor_put_head(
367
368
  buf, &pos,
368
369
  v.len == 0 || yep_utf8_validate((const unsigned char*)v.p, v.len, NULL) ? 3 : 2, v.len);
369
370
  if (v.len > 0) {
@@ -498,7 +499,7 @@ static size_t cbor_size_scalar(cenc* e, const yep_dnode* n) {
498
499
  return 0;
499
500
  }
500
501
  for (int t = 0; t < ntags; t++) {
501
- sz += cbor_arg_bytes(tags[t]);
502
+ sz += yep_cbor_arg_bytes(tags[t]);
502
503
  }
503
504
  yep_view v = yep_dom_view(d, n->value);
504
505
  switch (n->tag_id) {
@@ -510,19 +511,19 @@ static size_t cbor_size_scalar(cenc* e, const yep_dnode* n) {
510
511
  e->status = YEPTRIS_ERROR_UNSUPPORTED;
511
512
  return 0;
512
513
  }
513
- return sz + (num.mt == 7 ? (size_t)(1 + num.fwidth) : cbor_arg_bytes(num.uval));
514
+ return sz + (num.mt == 7 ? (size_t)(1 + num.fwidth) : yep_cbor_arg_bytes(num.uval));
514
515
  }
515
516
  case YEPTRIS_TAG_BOOL:
516
517
  case YEPTRIS_TAG_NULL:
517
518
  return sz + 1;
518
519
  default: /* str: a recognized diagnostic, else a text item */
519
520
  if (n->tag.len == 0) {
520
- int sv = cbor_simple_of(&v);
521
+ int sv = yep_cbor_simple_of(v.p, v.len);
521
522
  if (sv >= 0) {
522
523
  return sz + ((uint32_t)sv < 24 ? 1 : 2);
523
524
  }
524
525
  }
525
- return sz + cbor_arg_bytes(v.len) + v.len;
526
+ return sz + yep_cbor_arg_bytes(v.len) + v.len;
526
527
  }
527
528
  }
528
529
 
@@ -541,7 +542,7 @@ static size_t cbor_size_item(cenc* e, uint32_t id) {
541
542
  return 0;
542
543
  }
543
544
  for (int t = 0; t < ntags; t++) {
544
- sz += cbor_arg_bytes(tags[t]);
545
+ sz += yep_cbor_arg_bytes(tags[t]);
545
546
  }
546
547
  if (n->kind == YEP_DOM_ALIAS) {
547
548
  e->failed = 1;
@@ -549,7 +550,7 @@ static size_t cbor_size_item(cenc* e, uint32_t id) {
549
550
  return 0;
550
551
  }
551
552
  if (n->kind == YEP_DOM_SEQUENCE) {
552
- sz += cbor_arg_bytes(n->count);
553
+ sz += yep_cbor_arg_bytes(n->count);
553
554
  uint32_t c = n->first_child;
554
555
  for (uint32_t k = 0; k < n->count && !e->failed; k++) {
555
556
  sz += cbor_size_item(e, c);
@@ -559,7 +560,7 @@ static size_t cbor_size_item(cenc* e, uint32_t id) {
559
560
  }
560
561
  /* mapping */
561
562
  uint32_t pairs = n->count / 2;
562
- sz += cbor_arg_bytes(pairs);
563
+ sz += yep_cbor_arg_bytes(pairs);
563
564
  /* hold the INDEX, not the pointer: sizing a child recurses into
564
565
  * nested maps whose preps REALLOC e->maps (#152 — a held cmap*
565
566
  * dangled and the next pair read walked freed memory; n>=11 of
@@ -610,9 +611,9 @@ static void cbor_write_scalar(cenc* e, const yep_dnode* n, uint8_t* out, size_t*
610
611
  return; /* pass 1 already failed on this; unreachable */
611
612
  }
612
613
  if (num.mt == 7) {
613
- cbor_put_float(out, pos, num.dval, num.fwidth);
614
+ yep_cbor_put_float(out, pos, num.dval, num.fwidth);
614
615
  } else {
615
- cbor_put_head(out, pos, num.mt, num.uval);
616
+ yep_cbor_put_head(out, pos, num.mt, num.uval);
616
617
  }
617
618
  return;
618
619
  }
@@ -624,7 +625,7 @@ static void cbor_write_scalar(cenc* e, const yep_dnode* n, uint8_t* out, size_t*
624
625
  return;
625
626
  default:
626
627
  if (n->tag.len == 0) {
627
- int sv = cbor_simple_of(&v);
628
+ int sv = yep_cbor_simple_of(v.p, v.len);
628
629
  if (sv >= 0) {
629
630
  if (sv < 24) {
630
631
  out[(*pos)++] = (uint8_t)(0xE0 | (uint8_t)sv);
@@ -638,7 +639,7 @@ static void cbor_write_scalar(cenc* e, const yep_dnode* n, uint8_t* out, size_t*
638
639
  /* invalid UTF-8 can only have been a byte string (the 01
639
640
  * ledger: both decode to tag-str scalars) — emit mt2 so the
640
641
  * roundtrip re-decodes to the same bytes */
641
- cbor_put_head(
642
+ yep_cbor_put_head(
642
643
  out, pos,
643
644
  v.len == 0 || yep_utf8_validate((const unsigned char*)v.p, v.len, NULL) ? 3 : 2, v.len);
644
645
  if (v.len > 0) { /* memcpy(_, NULL, 0) is UB (gcc UBSan) */
@@ -655,14 +656,14 @@ static void cbor_write_item(cenc* e, uint32_t id, uint8_t* out, size_t* pos) {
655
656
  uint64_t tags[16];
656
657
  int ntags = cbor_tag_chain(d, n, tags, 16);
657
658
  for (int t = 0; t < ntags; t++) {
658
- cbor_put_head(out, pos, 6, tags[t]);
659
+ yep_cbor_put_head(out, pos, 6, tags[t]);
659
660
  }
660
661
  if (n->kind == YEP_DOM_SCALAR) {
661
662
  cbor_write_scalar(e, n, out, pos);
662
663
  return;
663
664
  }
664
665
  if (n->kind == YEP_DOM_SEQUENCE) {
665
- cbor_put_head(out, pos, 4, n->count);
666
+ yep_cbor_put_head(out, pos, 4, n->count);
666
667
  uint32_t c = n->first_child;
667
668
  for (uint32_t k = 0; k < n->count; k++) {
668
669
  cbor_write_item(e, c, out, pos);
@@ -671,7 +672,7 @@ static void cbor_write_item(cenc* e, uint32_t id, uint8_t* out, size_t* pos) {
671
672
  return;
672
673
  }
673
674
  uint32_t pairs = n->count / 2;
674
- cbor_put_head(out, pos, 5, pairs);
675
+ yep_cbor_put_head(out, pos, 5, pairs);
675
676
  cmap* cm = NULL;
676
677
  if (e->canonical && e->next_map < e->nmaps) {
677
678
  cm = &e->maps[e->next_map++]; /* walk order == build order */
@@ -0,0 +1,32 @@
1
+ /* encode_priv.h — the CBOR encoder's shared kernels (internal).
2
+ *
3
+ * encode.c owns the DOM walk; from_marshal.c (the binding dump face)
4
+ * appends the same item bytes from a Marshal stream. The minimal-head
5
+ * table, the preferred-float width rule and the reserved diagnostic
6
+ * words are ONE law each — both writers call them from here. */
7
+
8
+ #ifndef YEPTRIS_CBOR_ENCODE_PRIV_H
9
+ #define YEPTRIS_CBOR_ENCODE_PRIV_H
10
+
11
+ #include <stddef.h>
12
+ #include <stdint.h>
13
+
14
+ /* RFC 8949 s3: the encoded size of argument arg. */
15
+ size_t yep_cbor_arg_bytes(uint64_t arg);
16
+
17
+ /* RFC 8949 s3: one head (major type + minimally encoded argument). */
18
+ void yep_cbor_put_head(uint8_t* out, size_t* pos, uint8_t mt, uint64_t arg);
19
+
20
+ /* s4.2.2: the smallest of half/single/double that round-trips. */
21
+ uint8_t yep_cbor_float_width(double d);
22
+
23
+ /* s4.2.2: one float at the requested width (NaN canonicalizes). */
24
+ void yep_cbor_put_float(uint8_t* out, size_t* pos, double d, uint8_t width);
25
+
26
+ /* The diagnostic round-trip ledger: decode renders undefined and
27
+ * simple(N) as text, so the encoder recognizes those words back — with
28
+ * the >9-byte gate that leaves single-digit simple(N) as text. Returns
29
+ * the simple value, or -1 when the text is not a reserved word. */
30
+ int yep_cbor_simple_of(const char* p, size_t len);
31
+
32
+ #endif /* YEPTRIS_CBOR_ENCODE_PRIV_H */