yeptris 0.6.16.2 → 0.6.18.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: f6b3f2406ea8b763cfa7bd0d0a36ea30ba7bb80c8322c6ebe5fd8300725e47d2
4
- data.tar.gz: 9a500939ce7f65be9219acc0a44c62819ddd09f4f64486177065681b8490e686
3
+ metadata.gz: 658657a4f718471797ff0c61fea6a4549a990974827b6946a1246bbd8249b4a6
4
+ data.tar.gz: 6e3b1331c96a030091ce62ddf51b7a1a001fa137c45306441d9e19bca43d3d72
5
5
  SHA512:
6
- metadata.gz: 6c4eeced42673cb9613462d239f6427bdb1190e14b0a733976e3bd9c17ee061a4fe7fb5f14ace52ea05242b186410570b8d44f1fc5bbc2ab2aacbcd65188d7ef
7
- data.tar.gz: 5b84d8229ff419ab0c04fba154088775776ed5f70f4e6be5a9939e9b495e61ac82656a1bf0d7cbddbdf55ae9c1ba1912c44fc2af5b22e16d40a432e71009b185
6
+ metadata.gz: 9636825560b2ec8aebb29a42bccc9b25ce357d7e3800cc2f349bce9f0fa68d2934bfc7e545e8050c20d200b0d7301eda04c21d03787db86e4078948c824112cd
7
+ data.tar.gz: 56b6bc6ece08fe4dd01d4929a727527fbe4894f5d7d44aab0b35e8010b797309aef0cd375db13a59532f1e745c8afcb013bc9f1cd82869c6d1d98ccf36963d8e
@@ -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
  }
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.18.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.16
6
+ VERSION 0.6.18
7
7
  DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -23,10 +23,13 @@
23
23
 
24
24
  #include "emit/float/api.h"
25
25
 
26
- #if defined(__SIZEOF_INT128__)
26
+ /* YEP_FLOAT_FORCE_LIMBS: test hook — exercises the portable lo/hi
27
+ * tier on hosts that also have __int128 (the limbs math is otherwise
28
+ * only compiled on 32-bit targets). */
29
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS) && !defined(YEP_FLOAT_FORCE_LIMBS)
27
30
  typedef unsigned __int128 yep_u128;
28
31
  #define YEP_MUL64(a, b) ((yep_u128)(uint64_t)(a) * (uint64_t)(b))
29
- #elif defined(_MSC_VER)
32
+ #elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
30
33
  #include <intrin.h>
31
34
  /* MSVC has no __int128: the lo/hi pair behind the SAME helper surface
32
35
  * (print.c's interval loop rides only these — no operator arithmetic
@@ -35,10 +38,17 @@ typedef struct {
35
38
  uint64_t lo, hi;
36
39
  } yep_u128;
37
40
  #define YEP_MUL64(a, b) yep_u128_mul64(a, b)
41
+ #else
42
+ /* Any compiler without __int128 (32-bit GCC/Clang, MSVC x86-32): the
43
+ * same lo/hi pair; multiplication via 32-bit limbs (below). */
44
+ typedef struct {
45
+ uint64_t lo, hi;
46
+ } yep_u128;
47
+ #define YEP_MUL64(a, b) yep_u128_mul64(a, b)
38
48
  #endif
39
49
 
40
50
  static inline yep_u128 yep_u128_of(uint64_t v) {
41
- #if defined(__SIZEOF_INT128__)
51
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
42
52
  return (yep_u128)v;
43
53
  #else
44
54
  yep_u128 r = {v, 0};
@@ -47,7 +57,7 @@ static inline yep_u128 yep_u128_of(uint64_t v) {
47
57
  }
48
58
 
49
59
  static inline yep_u128 yep_u128_max(void) {
50
- #if defined(__SIZEOF_INT128__)
60
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
51
61
  return ~(yep_u128)0;
52
62
  #else
53
63
  yep_u128 r = {~(uint64_t)0, ~(uint64_t)0};
@@ -56,7 +66,7 @@ static inline yep_u128 yep_u128_max(void) {
56
66
  }
57
67
 
58
68
  static inline yep_u128 yep_u128_add(yep_u128 a, yep_u128 b) {
59
- #if defined(__SIZEOF_INT128__)
69
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
60
70
  return a + b;
61
71
  #else
62
72
  yep_u128 r;
@@ -67,7 +77,7 @@ static inline yep_u128 yep_u128_add(yep_u128 a, yep_u128 b) {
67
77
  }
68
78
 
69
79
  static inline yep_u128 yep_u128_sub(yep_u128 a, yep_u128 b) {
70
- #if defined(__SIZEOF_INT128__)
80
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
71
81
  return a - b;
72
82
  #else
73
83
  yep_u128 r;
@@ -79,7 +89,7 @@ static inline yep_u128 yep_u128_sub(yep_u128 a, yep_u128 b) {
79
89
 
80
90
  /* n < 128 */
81
91
  static inline yep_u128 yep_u128_shl(yep_u128 a, unsigned n) {
82
- #if defined(__SIZEOF_INT128__)
92
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
83
93
  return a << n;
84
94
  #else
85
95
  yep_u128 r = {0, 0};
@@ -98,7 +108,7 @@ static inline yep_u128 yep_u128_shl(yep_u128 a, unsigned n) {
98
108
 
99
109
  /* n < 128 */
100
110
  static inline yep_u128 yep_u128_shr(yep_u128 a, unsigned n) {
101
- #if defined(__SIZEOF_INT128__)
111
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
102
112
  return a >> n;
103
113
  #else
104
114
  yep_u128 r = {0, 0};
@@ -116,7 +126,7 @@ static inline yep_u128 yep_u128_shr(yep_u128 a, unsigned n) {
116
126
  }
117
127
 
118
128
  static inline int yep_u128_cmp(yep_u128 a, yep_u128 b) {
119
- #if defined(__SIZEOF_INT128__)
129
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
120
130
  return a < b ? -1 : (a > b ? 1 : 0);
121
131
  #else
122
132
  if (a.hi != b.hi) {
@@ -142,29 +152,47 @@ static inline yep_u128 yep_u128_mul64(uint64_t a, uint64_t b) {
142
152
  r.lo = a * b;
143
153
  return r;
144
154
  }
155
+ #elif !defined(__SIZEOF_INT128__) || defined(YEP_FLOAT_FORCE_LIMBS)
156
+ /* portable: 32-bit limbs, exact (the struct tier only) */
157
+ static inline yep_u128 yep_u128_mul64(uint64_t a, uint64_t b) {
158
+ uint64_t a0 = (uint32_t)a, a1 = a >> 32;
159
+ uint64_t b0 = (uint32_t)b, b1 = b >> 32;
160
+ uint64_t ll = a0 * b0;
161
+ uint64_t lh = a0 * b1;
162
+ uint64_t hl = a1 * b0;
163
+ uint64_t mid = (ll >> 32) + (uint32_t)lh + (uint32_t)hl;
164
+ yep_u128 r;
165
+ r.lo = (mid << 32) | (uint32_t)ll;
166
+ r.hi = (a1 * b1) + (lh >> 32) + (hl >> 32) + (mid >> 32);
167
+ return r;
168
+ }
145
169
  #endif
146
170
 
147
171
  /* x * 10 stays in range */
148
172
  static inline int yep_u128_fits10(yep_u128 x) {
149
- #if defined(__SIZEOF_INT128__)
173
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
150
174
  return x <= (~(yep_u128)0) / 10;
151
175
  #else
152
- /* exact: hi*10 must not overflow 64 bits (with the lo carry) */
153
- uint64_t hi_prod_hi = __umulh(x.hi, 10u);
154
- uint64_t hi_prod_lo = x.hi * 10u;
155
- uint64_t lo_carry = __umulh(x.lo, 10u);
156
- return hi_prod_hi == 0 && hi_prod_lo + lo_carry >= hi_prod_lo;
176
+ /* exact: x*10 = (hi*10 << 64) + lo*10 — overflow iff hi*10
177
+ * escapes 64 bits or the lo-carry wraps the sum */
178
+ yep_u128 hp = YEP_MUL64(x.hi, 10u);
179
+ if (hp.hi != 0) {
180
+ return 0;
181
+ }
182
+ yep_u128 lp = YEP_MUL64(x.lo, 10u);
183
+ return hp.lo + lp.hi >= hp.lo;
157
184
  #endif
158
185
  }
159
186
 
160
187
  /* m small (< 2^32); caller guarantees no overflow (fits10 guarded) */
161
188
  static inline yep_u128 yep_u128_mul_small(yep_u128 a, uint32_t m) {
162
- #if defined(__SIZEOF_INT128__)
189
+ #if defined(__SIZEOF_INT128__) && !defined(YEP_FLOAT_FORCE_LIMBS)
163
190
  return a * m;
164
191
  #else
165
192
  yep_u128 r;
166
- r.lo = a.lo * m;
167
- r.hi = a.hi * m + __umulh(a.lo, m);
193
+ yep_u128 lp = YEP_MUL64(a.lo, m);
194
+ r.lo = lp.lo;
195
+ r.hi = a.hi * m + lp.hi;
168
196
  return r;
169
197
  #endif
170
198
  }
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.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.