yeptris 0.6.59.1-arm-linux-eabihf → 0.6.60.1-arm-linux-eabihf
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 +4 -4
- data/ext/yeptris_native/version.h +2 -2
- data/lib/yeptris/3.4/native.so +0 -0
- data/lib/yeptris/ffi.rb +19 -0
- data/lib/yeptris/yaml.rb +61 -0
- data/lib/yeptris.rb +1 -1
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/CMakeLists.txt +7 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +30 -2
- data/vendor/libyeptris/src/include/yeptris/parse.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +8 -0
- data/vendor/libyeptris/src/include/yeptris/validate.h +222 -21
- data/vendor/libyeptris/src/include/yeptris/values.h +8 -2
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +10 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +5 -7
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +9 -9
- data/vendor/libyeptris/src/yeptris/cbor/from_marshal.c +1 -1
- data/vendor/libyeptris/src/yeptris/common/error.h +2 -0
- data/vendor/libyeptris/src/yeptris/doc.h +20 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +85 -13
- data/vendor/libyeptris/src/yeptris/emit/writer.c +158 -642
- data/vendor/libyeptris/src/yeptris/emit/writer.h +15 -1
- data/vendor/libyeptris/src/yeptris/events/push.c +28 -3
- data/vendor/libyeptris/src/yeptris/events/values.c +24 -0
- data/vendor/libyeptris/src/yeptris/marshal/to_json.c +650 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +121 -40
- data/vendor/libyeptris/src/yeptris/parse/stream_iter.c +326 -0
- data/vendor/libyeptris/src/yeptris/parse.c +173 -43
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +1 -1
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +11 -3
- data/vendor/libyeptris/src/yeptris/resolve/full12.c +201 -0
- data/vendor/libyeptris/src/yeptris/resolve/json12.c +12 -6
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +20 -0
- data/vendor/libyeptris/src/yeptris/validate/compile.c +39 -3
- data/vendor/libyeptris/src/yeptris/validate/internal.h +223 -7
- data/vendor/libyeptris/src/yeptris/validate/jsonschema.c +645 -115
- data/vendor/libyeptris/src/yeptris/validate/output.c +246 -0
- data/vendor/libyeptris/src/yeptris/validate/regexp.c +623 -157
- data/vendor/libyeptris/src/yeptris/validate/registry.c +118 -0
- data/vendor/libyeptris/src/yeptris/validate/resources.c +601 -0
- data/vendor/libyeptris/src/yeptris/validate/stream.c +1155 -0
- data/vendor/libyeptris/src/yeptris/validate/validate.c +288 -145
- metadata +8 -1
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
/* to_json.c — the marshal READER: Ruby Marshal.dump bytes to JSON text
|
|
2
|
+
* in one C call (yeptris-ruby#322, TODO.impl/#904 item 5: the reader's
|
|
3
|
+
* MECE home — marshal-domain bytes live here, the JSON writer's
|
|
4
|
+
* primitives stay in emit/writer.c).
|
|
5
|
+
*
|
|
6
|
+
* No per-scalar crossings, no object reconstruction. The format facts
|
|
7
|
+
* mirror marshal.c's emission contract (empirically pinned against
|
|
8
|
+
* Ruby 3.4): w_long small form
|
|
9
|
+
* x==0 -> 0x00, |x| <= 122 -> sign*(|x|+5), size form tag +-k with
|
|
10
|
+
* LE payload (2^(8k) - m for negatives); strings carry 'I' ivar
|
|
11
|
+
* wrappers; arrays/hashes/strings/floats register for '@' links.
|
|
12
|
+
*
|
|
13
|
+
* '@' links MEMMOVE the registered object's emitted span — the first
|
|
14
|
+
* walk's bytes are the cache; a lookup miss is a cycle (JSON cannot
|
|
15
|
+
* express it, the binding falls back). No re-parse, no registration
|
|
16
|
+
* on links: the object table stays exactly Ruby's stream order.
|
|
17
|
+
*
|
|
18
|
+
* The JSON-irrelevant constructs ('o' objects, 'S' structs, 'u'
|
|
19
|
+
* user marshal, 'l' bignums beyond i64, non-finite floats) return
|
|
20
|
+
* UNSUPPORTED — the binding falls back to its per-node walk.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
#include "emit/writer.h"
|
|
24
|
+
|
|
25
|
+
#include <math.h>
|
|
26
|
+
#include <stdatomic.h>
|
|
27
|
+
#include <stdio.h>
|
|
28
|
+
#include <stdlib.h>
|
|
29
|
+
#include <string.h>
|
|
30
|
+
|
|
31
|
+
#include "emit/float/api.h"
|
|
32
|
+
|
|
33
|
+
#include <yeptris/marshal.h>
|
|
34
|
+
|
|
35
|
+
typedef struct {
|
|
36
|
+
const char* p;
|
|
37
|
+
size_t len;
|
|
38
|
+
size_t at;
|
|
39
|
+
} mj_stream;
|
|
40
|
+
|
|
41
|
+
typedef struct {
|
|
42
|
+
const char* p;
|
|
43
|
+
uint32_t len;
|
|
44
|
+
} mj_sym;
|
|
45
|
+
|
|
46
|
+
typedef struct {
|
|
47
|
+
size_t pos; /* the stream position of the type byte */
|
|
48
|
+
size_t out_off; /* where the emitted span starts in w's buffer */
|
|
49
|
+
size_t out_len; /* the span's length */
|
|
50
|
+
} mj_obj;
|
|
51
|
+
|
|
52
|
+
typedef struct {
|
|
53
|
+
yep_writer* w;
|
|
54
|
+
mj_stream s;
|
|
55
|
+
mj_sym* syms;
|
|
56
|
+
size_t syms_n, syms_cap;
|
|
57
|
+
mj_obj* objs;
|
|
58
|
+
size_t objs_n, objs_cap;
|
|
59
|
+
int failed; /* 0 ok, YEPTRIS_ERROR_UNSUPPORTED */
|
|
60
|
+
int compact;
|
|
61
|
+
int no_reg; /* the next mj_value entry skips registration — an
|
|
62
|
+
* 'I'-wrapped construct registers ONCE (at the 'I');
|
|
63
|
+
* its immediate inner must not add a second entry or
|
|
64
|
+
* every later '@' link shifts */
|
|
65
|
+
int depth; /* mj_value recursion guard — the reader walks hostile
|
|
66
|
+
* bytes, not Ruby's heap: nesting is unbounded in the
|
|
67
|
+
* input, the stack is not (the parser's own
|
|
68
|
+
* max_depth=1000 is the same law) */
|
|
69
|
+
} mj_ctx;
|
|
70
|
+
|
|
71
|
+
static int mj_fail(mj_ctx* c) {
|
|
72
|
+
c->failed = YEPTRIS_ERROR_UNSUPPORTED;
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static int mj_byte(mj_ctx* c, unsigned char* out) {
|
|
77
|
+
if (c->s.at >= c->s.len)
|
|
78
|
+
return mj_fail(c);
|
|
79
|
+
*out = (unsigned char)c->s.p[c->s.at++];
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* the w_long inverse — mirrors m_w_long's encode exactly:
|
|
84
|
+
* 0x00 -> 0; 1..4 -> positive size form k=b; 5..127 -> small +(
|
|
85
|
+
* b-5); 129..250 -> small -(251-b); 251..255 -> negative size form
|
|
86
|
+
* k=256-b (payload = LE(2^(8k) - m)); 128 unreachable. */
|
|
87
|
+
static int mj_long(mj_ctx* c, int64_t* out) {
|
|
88
|
+
unsigned char b;
|
|
89
|
+
if (mj_byte(c, &b) != 0)
|
|
90
|
+
return -1;
|
|
91
|
+
if (b == 0) {
|
|
92
|
+
*out = 0;
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
if (b <= 4) { /* positive size form, k = b */
|
|
96
|
+
int64_t v = 0;
|
|
97
|
+
for (int i = 0; i < (int)b; i++) {
|
|
98
|
+
unsigned char d;
|
|
99
|
+
if (mj_byte(c, &d) != 0)
|
|
100
|
+
return -1;
|
|
101
|
+
v |= (int64_t)d << (8 * i);
|
|
102
|
+
}
|
|
103
|
+
*out = v;
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
if (b <= 127) {
|
|
107
|
+
*out = (int64_t)b - 5;
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
if (b >= 129 && b <= 250) {
|
|
111
|
+
*out = (int64_t)b - 251;
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
{ /* 251..255: negative size form */
|
|
115
|
+
int bytes = 256 - (int)b;
|
|
116
|
+
int64_t v = 0;
|
|
117
|
+
for (int i = 0; i < bytes; i++) {
|
|
118
|
+
unsigned char d;
|
|
119
|
+
if (mj_byte(c, &d) != 0)
|
|
120
|
+
return -1;
|
|
121
|
+
v |= (int64_t)d << (8 * i);
|
|
122
|
+
}
|
|
123
|
+
*out = v - ((int64_t)1 << (8 * bytes));
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static int mj_ivar_pairs(mj_ctx* c);
|
|
129
|
+
static int mj_value(mj_ctx* c);
|
|
130
|
+
static int mj_discard_value(mj_ctx* c);
|
|
131
|
+
|
|
132
|
+
static int mj_string_body(mj_ctx* c, int64_t len) {
|
|
133
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
134
|
+
return mj_fail(c);
|
|
135
|
+
emit_dq_json(c->w, c->s.p + c->s.at, (uint32_t)len);
|
|
136
|
+
c->s.at += (size_t)len;
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* a symbol: ':' + long(len) + bytes — registers in the table; the
|
|
141
|
+
* name is emitted as a JSON string when emit is set */
|
|
142
|
+
static int mj_symbol(mj_ctx* c, int emit) {
|
|
143
|
+
int64_t len;
|
|
144
|
+
if (mj_long(c, &len) != 0)
|
|
145
|
+
return -1;
|
|
146
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
147
|
+
return mj_fail(c);
|
|
148
|
+
const char* name = c->s.p + c->s.at;
|
|
149
|
+
uint32_t nlen = (uint32_t)len;
|
|
150
|
+
c->s.at += (size_t)len;
|
|
151
|
+
if (c->syms_n == c->syms_cap) {
|
|
152
|
+
c->syms_cap = c->syms_cap ? c->syms_cap * 2 : 16;
|
|
153
|
+
c->syms = (mj_sym*)realloc(c->syms, c->syms_cap * sizeof(*c->syms));
|
|
154
|
+
if (!c->syms)
|
|
155
|
+
return mj_fail(c);
|
|
156
|
+
}
|
|
157
|
+
c->syms[c->syms_n].p = name;
|
|
158
|
+
c->syms[c->syms_n].len = nlen;
|
|
159
|
+
c->syms_n++;
|
|
160
|
+
if (emit)
|
|
161
|
+
emit_dq_json(c->w, name, nlen);
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* the count + (symbol, value) pairs that follow an 'I' wrapper's
|
|
166
|
+
* wrapped value. Parsed and DISCARDED — encoding/freeze ivars have
|
|
167
|
+
* no JSON spelling; the symbol still enters the ';' table. */
|
|
168
|
+
static int mj_ivar_pairs(mj_ctx* c) {
|
|
169
|
+
int64_t count;
|
|
170
|
+
if (mj_long(c, &count) != 0)
|
|
171
|
+
return -1;
|
|
172
|
+
for (int64_t i = 0; i < count; i++) {
|
|
173
|
+
unsigned char nt;
|
|
174
|
+
if (mj_byte(c, &nt) != 0)
|
|
175
|
+
return -1;
|
|
176
|
+
if (nt == ':') {
|
|
177
|
+
if (mj_symbol(c, 0) != 0)
|
|
178
|
+
return -1; /* the name: tabled, not emitted */
|
|
179
|
+
} else if (nt == ';') {
|
|
180
|
+
int64_t sidx;
|
|
181
|
+
if (mj_long(c, &sidx) != 0)
|
|
182
|
+
return -1;
|
|
183
|
+
if (sidx < 0 || (size_t)sidx >= c->syms_n)
|
|
184
|
+
return mj_fail(c);
|
|
185
|
+
} else {
|
|
186
|
+
return mj_fail(c); /* ivar names are symbols only */
|
|
187
|
+
}
|
|
188
|
+
if (mj_discard_value(c) != 0)
|
|
189
|
+
return -1; /* the ivar value */
|
|
190
|
+
}
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* stream-position-only walk: consumes one value, emits nothing */
|
|
195
|
+
|
|
196
|
+
#define MJ_DEPTH_MAX 1000
|
|
197
|
+
|
|
198
|
+
/* the discard twin rides the same budget (its 'I'/'['/'{' recursion is
|
|
199
|
+
* just as hostile-byte reachable) */
|
|
200
|
+
static int mj_discard_value_inner(mj_ctx* c);
|
|
201
|
+
|
|
202
|
+
static int mj_discard_value(mj_ctx* c) {
|
|
203
|
+
if (c->depth >= MJ_DEPTH_MAX)
|
|
204
|
+
return mj_fail(c);
|
|
205
|
+
c->depth++;
|
|
206
|
+
int rc = mj_discard_value_inner(c);
|
|
207
|
+
c->depth--;
|
|
208
|
+
return rc;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static int mj_discard_value_inner(mj_ctx* c) {
|
|
212
|
+
unsigned char t;
|
|
213
|
+
int rc = mj_byte(c, &t);
|
|
214
|
+
if (rc != 0)
|
|
215
|
+
return -1;
|
|
216
|
+
switch (t) {
|
|
217
|
+
case '0':
|
|
218
|
+
case 'T':
|
|
219
|
+
case 'F':
|
|
220
|
+
return 0;
|
|
221
|
+
case 'i':
|
|
222
|
+
case ';':
|
|
223
|
+
case '@': {
|
|
224
|
+
int64_t v;
|
|
225
|
+
return mj_long(c, &v);
|
|
226
|
+
}
|
|
227
|
+
case 'l': {
|
|
228
|
+
unsigned char sign;
|
|
229
|
+
int64_t nlimbs;
|
|
230
|
+
if (mj_byte(c, &sign) != 0)
|
|
231
|
+
return -1;
|
|
232
|
+
if (sign != 0x2b && sign != 0x2d)
|
|
233
|
+
return mj_fail(c);
|
|
234
|
+
if (mj_long(c, &nlimbs) != 0)
|
|
235
|
+
return -1;
|
|
236
|
+
if (nlimbs < 0 || (uint64_t)(nlimbs * 2) > (uint64_t)(c->s.len - c->s.at))
|
|
237
|
+
return mj_fail(c);
|
|
238
|
+
c->s.at += (size_t)(nlimbs * 2);
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
case 'f':
|
|
242
|
+
case '"': {
|
|
243
|
+
int64_t len;
|
|
244
|
+
if (mj_long(c, &len) != 0)
|
|
245
|
+
return -1;
|
|
246
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
247
|
+
return mj_fail(c);
|
|
248
|
+
c->s.at += (size_t)len;
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
case 'I':
|
|
252
|
+
if (mj_discard_value(c) != 0)
|
|
253
|
+
return -1;
|
|
254
|
+
return mj_ivar_pairs(c);
|
|
255
|
+
case ':':
|
|
256
|
+
return mj_symbol(c, 0);
|
|
257
|
+
case '[': {
|
|
258
|
+
int64_t count;
|
|
259
|
+
if (mj_long(c, &count) != 0)
|
|
260
|
+
return -1;
|
|
261
|
+
for (int64_t i = 0; i < count; i++) {
|
|
262
|
+
if (mj_discard_value(c) != 0)
|
|
263
|
+
return -1;
|
|
264
|
+
}
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
case '{': {
|
|
268
|
+
int64_t count;
|
|
269
|
+
if (mj_long(c, &count) != 0)
|
|
270
|
+
return -1;
|
|
271
|
+
for (int64_t i = 0; i < count; i++) {
|
|
272
|
+
if (mj_discard_value(c) != 0)
|
|
273
|
+
return -1; /* key */
|
|
274
|
+
if (mj_discard_value(c) != 0)
|
|
275
|
+
return -1; /* value */
|
|
276
|
+
}
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
default:
|
|
280
|
+
return mj_fail(c);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* MJ_DEBUG is read ONCE per process (the audit's finding: the memo
|
|
285
|
+
* path ran getenv per hit — an environment table walk inside the hot
|
|
286
|
+
* link copy). The flag is process-fixed for the reader's lifetime:
|
|
287
|
+
* a benign first-use race only double-reads the same name and both
|
|
288
|
+
* threads store the identical verdict through the atomic (the
|
|
289
|
+
* cpu.c one-time-detect shape). */
|
|
290
|
+
static int mj_debug_env(void) {
|
|
291
|
+
static atomic_int cached = -1;
|
|
292
|
+
int v = atomic_load_explicit(&cached, memory_order_acquire);
|
|
293
|
+
if (v < 0) {
|
|
294
|
+
v = getenv("MJ_DEBUG") != NULL;
|
|
295
|
+
atomic_store_explicit(&cached, v, memory_order_release);
|
|
296
|
+
}
|
|
297
|
+
return v;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* the memo'd span of a registered object, copied to the cursor. The
|
|
301
|
+
* span lives earlier in the same buffer (append-only), so the copy
|
|
302
|
+
* is memmove-safe. */
|
|
303
|
+
static int mj_emit_memo(mj_ctx* c, const mj_obj* o) {
|
|
304
|
+
if (mj_debug_env()) {
|
|
305
|
+
char prev[13];
|
|
306
|
+
uint32_t k = o->out_len < 12 ? (uint32_t)o->out_len : 12;
|
|
307
|
+
memcpy(prev, c->w->p + o->out_off, k);
|
|
308
|
+
prev[k] = '\0';
|
|
309
|
+
fprintf(stderr, "memo off=%zu len=%zu: %s\n", o->out_off, o->out_len, prev);
|
|
310
|
+
}
|
|
311
|
+
if (o->out_len == 0)
|
|
312
|
+
return mj_fail(c); /* not memoized yet: a cycle */
|
|
313
|
+
wr_reserve(c->w, o->out_len);
|
|
314
|
+
if (c->w->oom)
|
|
315
|
+
return -1;
|
|
316
|
+
memmove(c->w->p + c->w->len, c->w->p + o->out_off, o->out_len);
|
|
317
|
+
wr_col_update(c->w, c->w->p + c->w->len, (uint32_t)o->out_len);
|
|
318
|
+
c->w->len += o->out_len;
|
|
319
|
+
/* the raw advance bypasses wr_put: hand back the reservation it
|
|
320
|
+
* would have consumed, or the next write rides past cap */
|
|
321
|
+
c->w->reserve -= o->out_len;
|
|
322
|
+
c->w->last = c->w->p[c->w->len - 1];
|
|
323
|
+
return 0;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
static int mj_value_inner(mj_ctx* c);
|
|
327
|
+
|
|
328
|
+
/* the wrapper owns the recursion budget: hostile bytes can nest arrays
|
|
329
|
+
* without bound, the stack cannot (the parser's max_depth=1000 law) */
|
|
330
|
+
static int mj_value(mj_ctx* c) {
|
|
331
|
+
if (c->depth >= MJ_DEPTH_MAX)
|
|
332
|
+
return mj_fail(c);
|
|
333
|
+
c->depth++;
|
|
334
|
+
int rc = mj_value_inner(c);
|
|
335
|
+
c->depth--;
|
|
336
|
+
return rc;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
static int mj_value_inner(mj_ctx* c) {
|
|
340
|
+
unsigned char t;
|
|
341
|
+
size_t tpos = c->s.at;
|
|
342
|
+
int reg_idx = -1;
|
|
343
|
+
size_t span_start = 0;
|
|
344
|
+
int register_this = !c->no_reg;
|
|
345
|
+
c->no_reg = 0;
|
|
346
|
+
if (mj_byte(c, &t) != 0)
|
|
347
|
+
return -1;
|
|
348
|
+
/* arrays, hashes, strings, floats, bignums and anything
|
|
349
|
+
* 'I'-wrapped register — ONCE per construct, at the outer byte
|
|
350
|
+
* (the wrapper's no_reg suppresses the inner) (0-based stream
|
|
351
|
+
* order). Bignums hold a slot but Ruby never emits a link to one
|
|
352
|
+
* (the second occurrence re-emits in full); the entry only keeps
|
|
353
|
+
* the slot count aligned for later links. Integers, symbols and
|
|
354
|
+
* the other immediates never register. */
|
|
355
|
+
if (register_this && (t == '[' || t == '{' || t == '"' || t == 'f' || t == 'I' || t == 'l')) {
|
|
356
|
+
if (c->objs_n == c->objs_cap) {
|
|
357
|
+
c->objs_cap = c->objs_cap ? c->objs_cap * 2 : 16;
|
|
358
|
+
c->objs = (mj_obj*)realloc(c->objs, c->objs_cap * sizeof(*c->objs));
|
|
359
|
+
if (!c->objs)
|
|
360
|
+
return mj_fail(c);
|
|
361
|
+
}
|
|
362
|
+
c->objs[c->objs_n].pos = tpos;
|
|
363
|
+
c->objs[c->objs_n].out_off = c->w->len;
|
|
364
|
+
c->objs[c->objs_n].out_len = 0;
|
|
365
|
+
reg_idx = (int)c->objs_n;
|
|
366
|
+
c->objs_n++;
|
|
367
|
+
span_start = c->w->len;
|
|
368
|
+
}
|
|
369
|
+
switch (t) {
|
|
370
|
+
case '0':
|
|
371
|
+
wr_put(c->w, "null", 4);
|
|
372
|
+
break;
|
|
373
|
+
case 'T':
|
|
374
|
+
wr_put(c->w, "true", 4);
|
|
375
|
+
break;
|
|
376
|
+
case 'F':
|
|
377
|
+
wr_put(c->w, "false", 5);
|
|
378
|
+
break;
|
|
379
|
+
case 'i': {
|
|
380
|
+
int64_t v;
|
|
381
|
+
if (mj_long(c, &v) != 0)
|
|
382
|
+
return -1;
|
|
383
|
+
char buf[24];
|
|
384
|
+
int n = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
385
|
+
wr_put(c->w, buf, (uint32_t)n);
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
case 'l': {
|
|
389
|
+
/* the bignum: sign byte '+'/'-' + w_long(byte count) + LE
|
|
390
|
+
* bytes; the i64-representable range emits, wider falls back */
|
|
391
|
+
unsigned char sign;
|
|
392
|
+
if (mj_byte(c, &sign) != 0)
|
|
393
|
+
return -1;
|
|
394
|
+
if (sign != 0x2b && sign != 0x2d)
|
|
395
|
+
return mj_fail(c);
|
|
396
|
+
int64_t nlimbs;
|
|
397
|
+
if (mj_long(c, &nlimbs) != 0)
|
|
398
|
+
return -1;
|
|
399
|
+
if (nlimbs < 1 || nlimbs > 4)
|
|
400
|
+
return mj_fail(c); /* beyond 64 bits: fall back */
|
|
401
|
+
int64_t v = 0;
|
|
402
|
+
for (int64_t i = 0; i < nlimbs; i++) {
|
|
403
|
+
unsigned char lo, hi;
|
|
404
|
+
if (mj_byte(c, &lo) != 0 || mj_byte(c, &hi) != 0)
|
|
405
|
+
return -1;
|
|
406
|
+
/* shift in the unsigned domain: the signed form put
|
|
407
|
+
* 0xFFFF into the sign bit at limb 3 (UB, UBSan-flagged) */
|
|
408
|
+
v |= (int64_t)((uint64_t)(lo | ((uint64_t)hi << 8)) << (16 * i));
|
|
409
|
+
}
|
|
410
|
+
if (sign == 0x2d)
|
|
411
|
+
v = -v;
|
|
412
|
+
char buf[24];
|
|
413
|
+
int n = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
414
|
+
wr_put(c->w, buf, (uint32_t)n);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
case 'f': {
|
|
418
|
+
int64_t len;
|
|
419
|
+
if (mj_long(c, &len) != 0)
|
|
420
|
+
return -1;
|
|
421
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
422
|
+
return mj_fail(c);
|
|
423
|
+
char tmp[64];
|
|
424
|
+
uint32_t tl = (uint32_t)len < sizeof tmp - 1 ? (uint32_t)len : (uint32_t)sizeof tmp - 1;
|
|
425
|
+
memcpy(tmp, c->s.p + c->s.at, tl);
|
|
426
|
+
tmp[tl] = '\0';
|
|
427
|
+
c->s.at += (size_t)len;
|
|
428
|
+
double d = strtod(tmp, NULL);
|
|
429
|
+
if (!isfinite(d))
|
|
430
|
+
return mj_fail(c); /* JSON has no inf/nan spelling — and a
|
|
431
|
+
* strtod OVERFLOW ("1e999") produces
|
|
432
|
+
* one from text that never spells it */
|
|
433
|
+
char buf[40];
|
|
434
|
+
int wl = yep_d2s_json(d, buf);
|
|
435
|
+
wr_put(c->w, buf, (uint32_t)wl);
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case '"': {
|
|
439
|
+
int64_t len;
|
|
440
|
+
if (mj_long(c, &len) != 0)
|
|
441
|
+
return -1;
|
|
442
|
+
int rc = mj_string_body(c, len);
|
|
443
|
+
if (rc != 0)
|
|
444
|
+
return -1;
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
case 'I': {
|
|
448
|
+
/* the wrapped value IS the payload: re-dispatch into it, then
|
|
449
|
+
* the ivar pairs ride. The inner dispatch must not register —
|
|
450
|
+
* the construct registered at the 'I' (the double entry
|
|
451
|
+
* shifted every later '@' link) */
|
|
452
|
+
c->no_reg = 1;
|
|
453
|
+
if (mj_value(c) != 0) {
|
|
454
|
+
c->no_reg = 0;
|
|
455
|
+
return -1;
|
|
456
|
+
}
|
|
457
|
+
c->no_reg = 0;
|
|
458
|
+
if (mj_ivar_pairs(c) != 0)
|
|
459
|
+
return -1;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case ':': {
|
|
463
|
+
/* a bare symbol VALUE: emitted as its name string */
|
|
464
|
+
if (mj_symbol(c, 1) != 0)
|
|
465
|
+
return -1;
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case ';': {
|
|
469
|
+
int64_t idx;
|
|
470
|
+
if (mj_long(c, &idx) != 0)
|
|
471
|
+
return -1;
|
|
472
|
+
if (idx < 0 || (size_t)idx >= c->syms_n)
|
|
473
|
+
return mj_fail(c);
|
|
474
|
+
emit_dq_json(c->w, c->syms[idx].p, c->syms[idx].len);
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
case '@': {
|
|
478
|
+
/* an object link: the registered span memmoves to the cursor */
|
|
479
|
+
int64_t idx;
|
|
480
|
+
if (mj_long(c, &idx) != 0)
|
|
481
|
+
return -1;
|
|
482
|
+
if (idx < 0 || (size_t)idx >= c->objs_n)
|
|
483
|
+
return mj_fail(c);
|
|
484
|
+
if (mj_emit_memo(c, &c->objs[idx]) != 0)
|
|
485
|
+
return -1;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
case '[':
|
|
489
|
+
case '{': {
|
|
490
|
+
int64_t count;
|
|
491
|
+
if (mj_long(c, &count) != 0)
|
|
492
|
+
return -1;
|
|
493
|
+
if (count < 0)
|
|
494
|
+
return mj_fail(c);
|
|
495
|
+
wr_byte(c->w, t == '[' ? '[' : '{');
|
|
496
|
+
const char* sep = c->compact ? "," : ", ";
|
|
497
|
+
uint32_t sep_len = c->compact ? 1u : 2u;
|
|
498
|
+
for (int64_t i = 0; i < count; i++) {
|
|
499
|
+
if (i > 0)
|
|
500
|
+
wr_put(c->w, sep, sep_len);
|
|
501
|
+
if (t == '[') {
|
|
502
|
+
if (mj_value(c) != 0)
|
|
503
|
+
return -1;
|
|
504
|
+
} else {
|
|
505
|
+
/* the key: a string (with its 'I' wrapper) or a
|
|
506
|
+
* symbol — JSON keys are strings */
|
|
507
|
+
unsigned char kt;
|
|
508
|
+
size_t ktpos = c->s.at;
|
|
509
|
+
if (mj_byte(c, &kt) != 0)
|
|
510
|
+
return -1;
|
|
511
|
+
if (kt == 'I') {
|
|
512
|
+
if (mj_value(c) != 0)
|
|
513
|
+
return -1;
|
|
514
|
+
if (mj_ivar_pairs(c) != 0)
|
|
515
|
+
return -1;
|
|
516
|
+
} else if (kt == '"') {
|
|
517
|
+
/* the key registers like any string object: its
|
|
518
|
+
* span is memo'd for later '@' key links */
|
|
519
|
+
int kreg = (int)c->objs_n;
|
|
520
|
+
size_t kspan = c->w->len;
|
|
521
|
+
if (kreg >= 0) {
|
|
522
|
+
if (c->objs_n == c->objs_cap) {
|
|
523
|
+
c->objs_cap = c->objs_cap ? c->objs_cap * 2 : 16;
|
|
524
|
+
c->objs = (mj_obj*)realloc(c->objs, c->objs_cap * sizeof(*c->objs));
|
|
525
|
+
if (!c->objs)
|
|
526
|
+
return mj_fail(c);
|
|
527
|
+
}
|
|
528
|
+
c->objs[c->objs_n].pos = ktpos;
|
|
529
|
+
c->objs[c->objs_n].out_off = c->w->len;
|
|
530
|
+
c->objs[c->objs_n].out_len = 0;
|
|
531
|
+
c->objs_n++;
|
|
532
|
+
}
|
|
533
|
+
int64_t len;
|
|
534
|
+
if (mj_long(c, &len) != 0)
|
|
535
|
+
return -1;
|
|
536
|
+
int rc = mj_string_body(c, len);
|
|
537
|
+
if (rc != 0)
|
|
538
|
+
return -1;
|
|
539
|
+
if (kreg >= 0) {
|
|
540
|
+
c->objs[kreg].out_off = kspan;
|
|
541
|
+
c->objs[kreg].out_len = c->w->len - kspan;
|
|
542
|
+
}
|
|
543
|
+
} else if (kt == ':') {
|
|
544
|
+
if (mj_symbol(c, 1) != 0)
|
|
545
|
+
return -1;
|
|
546
|
+
} else if (kt == ';') {
|
|
547
|
+
int64_t idx;
|
|
548
|
+
if (mj_long(c, &idx) != 0)
|
|
549
|
+
return -1;
|
|
550
|
+
if (idx < 0 || (size_t)idx >= c->syms_n)
|
|
551
|
+
return mj_fail(c);
|
|
552
|
+
emit_dq_json(c->w, c->syms[idx].p, c->syms[idx].len);
|
|
553
|
+
} else if (kt == '@') {
|
|
554
|
+
int64_t idx;
|
|
555
|
+
if (mj_long(c, &idx) != 0)
|
|
556
|
+
return -1;
|
|
557
|
+
if (idx < 0 || (size_t)idx >= c->objs_n)
|
|
558
|
+
return mj_fail(c);
|
|
559
|
+
if (mj_emit_memo(c, &c->objs[idx]) != 0)
|
|
560
|
+
return -1;
|
|
561
|
+
} else {
|
|
562
|
+
return mj_fail(c);
|
|
563
|
+
}
|
|
564
|
+
wr_put(c->w, c->compact ? ":" : ": ", c->compact ? 1u : 2u);
|
|
565
|
+
if (mj_value(c) != 0)
|
|
566
|
+
return -1;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
wr_byte(c->w, t == '[' ? ']' : '}');
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
default:
|
|
573
|
+
return mj_fail(c);
|
|
574
|
+
}
|
|
575
|
+
if (reg_idx >= 0) {
|
|
576
|
+
c->objs[reg_idx].out_off = span_start;
|
|
577
|
+
c->objs[reg_idx].out_len = c->w->len - span_start;
|
|
578
|
+
}
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
YEPTRIS_API YeptrisStatus yeptris_json_from_marshal(const char* m, size_t m_len, int compact,
|
|
583
|
+
char** out, size_t* out_len) {
|
|
584
|
+
if (out == NULL || out_len == NULL)
|
|
585
|
+
return YEPTRIS_ERROR_ARG;
|
|
586
|
+
*out = NULL;
|
|
587
|
+
*out_len = 0;
|
|
588
|
+
if (m == NULL || m_len < 2 || (unsigned char)m[0] != 0x04 || (unsigned char)m[1] != 0x08)
|
|
589
|
+
return YEPTRIS_ERROR_UNSUPPORTED;
|
|
590
|
+
yep_emitter em;
|
|
591
|
+
memset(&em, 0, sizeof(em));
|
|
592
|
+
yep_writer* w = &em.w;
|
|
593
|
+
w->sc_dec = NULL;
|
|
594
|
+
w->sc_i = 0;
|
|
595
|
+
w->json = 1;
|
|
596
|
+
w->json_compact = compact ? 1 : 0;
|
|
597
|
+
w->json_pretty = 0;
|
|
598
|
+
w->canonical = 0;
|
|
599
|
+
w->explicit_doc_start = 0;
|
|
600
|
+
w->best_width = 0;
|
|
601
|
+
w->sv_input = NULL;
|
|
602
|
+
w->sv_arena = NULL;
|
|
603
|
+
w->sink = NULL;
|
|
604
|
+
w->force_flow = 0;
|
|
605
|
+
w->in_flow = 0;
|
|
606
|
+
w->flow_item = 0;
|
|
607
|
+
w->unserializable = 0;
|
|
608
|
+
w->indent = 2; /* #903 fields: the JSON lane never folds or
|
|
609
|
+
* block-indents, but the writer stays fully
|
|
610
|
+
* initialized */
|
|
611
|
+
w->block_fold = 0;
|
|
612
|
+
|
|
613
|
+
mj_ctx c;
|
|
614
|
+
memset(&c, 0, sizeof(c));
|
|
615
|
+
c.w = w;
|
|
616
|
+
c.s.p = m;
|
|
617
|
+
c.s.len = m_len;
|
|
618
|
+
c.s.at = 2; /* the 4.8 header */
|
|
619
|
+
c.compact = compact ? 1 : 0;
|
|
620
|
+
|
|
621
|
+
/* one wet pass into a growable buffer (the #352 slice-2 shape).
|
|
622
|
+
* Raw malloc is the CONTRACT here, not a bypass (#904 item 3): the
|
|
623
|
+
* route takes bytes, not a document — there is no document
|
|
624
|
+
* allocator to route through, and *out is "malloc'd, free with
|
|
625
|
+
* yeptris_marshal_free" (marshal.h), so the growth in wr_grow must
|
|
626
|
+
* stay realloc-on-system too or the caller's free() breaks. */
|
|
627
|
+
size_t est = m_len * 2 + 64;
|
|
628
|
+
char* buf = (char*)malloc(est);
|
|
629
|
+
if (!buf)
|
|
630
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
631
|
+
w->p = buf;
|
|
632
|
+
w->cap = est;
|
|
633
|
+
w->grow = 1;
|
|
634
|
+
|
|
635
|
+
mj_value(&c);
|
|
636
|
+
|
|
637
|
+
int failed = c.failed;
|
|
638
|
+
size_t wrote = w->len;
|
|
639
|
+
buf = w->p;
|
|
640
|
+
free(c.syms);
|
|
641
|
+
free(c.objs);
|
|
642
|
+
if (failed || w->oom) {
|
|
643
|
+
free(buf);
|
|
644
|
+
return failed ? (YeptrisStatus)failed : YEPTRIS_ERROR_MEMORY;
|
|
645
|
+
}
|
|
646
|
+
buf[wrote] = '\0';
|
|
647
|
+
*out = buf;
|
|
648
|
+
*out_len = wrote;
|
|
649
|
+
return YEPTRIS_OK;
|
|
650
|
+
}
|