yeptris 0.6.48.0-arm-linux-eabihf → 0.6.50.0-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/cbor.rb +32 -0
- data/lib/yeptris/ffi.rb +6 -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 +2 -1
- data/vendor/libyeptris/src/include/yeptris/cbor.h +24 -0
- data/vendor/libyeptris/src/include/yeptris/json.h +4 -2
- data/vendor/libyeptris/src/include/yeptris/tape.h +5 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +59 -9
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +32 -31
- data/vendor/libyeptris/src/yeptris/cbor/encode_priv.h +32 -0
- data/vendor/libyeptris/src/yeptris/cbor/from_marshal.c +975 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +49 -87
- data/vendor/libyeptris/src/yeptris/emit/style.c +43 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +123 -6
- data/vendor/libyeptris/src/yeptris/parse/engine.c +16 -2
- data/vendor/libyeptris/src/yeptris/parse/engine.h +6 -1
- data/vendor/libyeptris/src/yeptris/parse.c +6 -3
- data/vendor/libyeptris/src/yeptris/tape.c +70 -46
- metadata +3 -1
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
/* from_marshal.c — Ruby Marshal.dump bytes to ONE CBOR data item
|
|
2
|
+
* (yeptris-ruby#322's CBOR half: the binding's dump face).
|
|
3
|
+
*
|
|
4
|
+
* The twin of emit/writer.c's marshal READER (yeptris_json_from_marshal):
|
|
5
|
+
* the Marshal stream is walked once, in C, and the CBOR item is
|
|
6
|
+
* appended straight to the output — no per-node Ruby crossings, no
|
|
7
|
+
* Document, no YAMLTree. The format facts mirror marshal.c's emission
|
|
8
|
+
* contract (pinned against Ruby 3.4): w_long small form x==0 -> 0x00,
|
|
9
|
+
* |x| <= 122 -> sign*(|x|+5), size form tag +-k with LE payload; 'I'
|
|
10
|
+
* ivar wrappers; arrays/hashes/strings/floats/bignums register for
|
|
11
|
+
* '@' links (0-based stream order, wrappers transparent).
|
|
12
|
+
*
|
|
13
|
+
* '@' links DUPLICATE the registered construct's emitted bytes when the
|
|
14
|
+
* construct is a host SCALAR (strings, floats, bignums, symbols): the
|
|
15
|
+
* per-node route never anchors host scalars, so it emits the value
|
|
16
|
+
* twice. Arrays and hashes are anchored there (the "&1" / "*1" pair)
|
|
17
|
+
* and the CBOR encoder refuses anchors — a link to one is UNSUPPORTED,
|
|
18
|
+
* and so is a link to a construct still being emitted (a cycle).
|
|
19
|
+
*
|
|
20
|
+
* Parity ledger (empirically pinned against the per-node route; the
|
|
21
|
+
* route's own machinery is binding/lib/yeptris/psych/visitors.rb +
|
|
22
|
+
* Builder#build_string + cbor/encode.c):
|
|
23
|
+
* - Symbols spell ":name" (the YAMLTree's `":#{sym}"` scalar), NOT the
|
|
24
|
+
* bare name the JSON face emits; symbols with non-UTF-8 bytes ride a
|
|
25
|
+
* byte string (the YAMLTree scalar's encoder fallback)
|
|
26
|
+
* - a String VALUE is emitted only when the per-node route leaves it a
|
|
27
|
+
* text scalar: ASCII-8BIT strings must be ASCII-only (non-ASCII ones
|
|
28
|
+
* take the !binary tag the CBOR encoder refuses); 'I'-wrapped UTF-8
|
|
29
|
+
* strings must be valid UTF-8 (the Psych plain-safety regexp raises
|
|
30
|
+
* otherwise); US-ASCII strings must be ASCII-only; strings in any
|
|
31
|
+
* other named encoding are UNSUPPORTED
|
|
32
|
+
* - a String KEY loses its encoding in the per-node route (map_add
|
|
33
|
+
* stores the raw bytes), so the key law is byte-based: valid UTF-8
|
|
34
|
+
* takes the compat-11 resolver's verdict — INT/FLOAT/BOOL/NULL keys
|
|
35
|
+
* (numeric-looking text, y/yes/on/true, ~/null, the empty key) are
|
|
36
|
+
* UNSUPPORTED, everything else is text; invalid UTF-8 rides a byte
|
|
37
|
+
* string
|
|
38
|
+
* - the reserved diagnostic words ("undefined", "simple(N)") reproduce
|
|
39
|
+
* encode.c's simple ledger on text keys and values
|
|
40
|
+
* - map keys also keep the per-node route's duplicate check: two keys
|
|
41
|
+
* whose stored TEXT matches fail there. Among supported keys equal
|
|
42
|
+
* emitted bytes === equal text, so the check is a pairwise scan of
|
|
43
|
+
* the emitted key spans
|
|
44
|
+
* - bignums up to four 16-bit words ride the integer law inside int64;
|
|
45
|
+
* past either end they re-render as the preferred float of their
|
|
46
|
+
* magnitude (the fallback number-kernel's ledger, sign included).
|
|
47
|
+
* Wider bignums and non-finite float text are UNSUPPORTED
|
|
48
|
+
* - booleans in key position are UNSUPPORTED: the per-node canonical
|
|
49
|
+
* sort keys bools by their TEXT ("true") but emits f4/f5
|
|
50
|
+
*
|
|
51
|
+
* Canonical mode sorts each map's pairs by the bytewise order of
|
|
52
|
+
* their ENCODED keys (encode.c's s4.2.1 insertion sort, stable); a
|
|
53
|
+
* per-map scratch buffer holds the stream-order pairs, then the
|
|
54
|
+
* sorted spans copy into the item. Registration order stays the
|
|
55
|
+
* STREAM's — only the bytes are reordered. */
|
|
56
|
+
|
|
57
|
+
#include "encode_priv.h"
|
|
58
|
+
|
|
59
|
+
#include <math.h>
|
|
60
|
+
#include <stdio.h>
|
|
61
|
+
#include <stdlib.h>
|
|
62
|
+
#include <string.h>
|
|
63
|
+
|
|
64
|
+
#include <yeptris/cbor.h>
|
|
65
|
+
#include <yeptris/marshal.h>
|
|
66
|
+
#include <yeptris/resolve.h>
|
|
67
|
+
|
|
68
|
+
#include "../encoding/encoding.h"
|
|
69
|
+
#include "../resolve/resolver.h"
|
|
70
|
+
|
|
71
|
+
#define CM_MAX_DEPTH 512
|
|
72
|
+
|
|
73
|
+
/* a string VALUE's Ruby encoding, as the per-node route sees it */
|
|
74
|
+
#define CM_ENC_BINARY 0 /* no :E ivar — ASCII-8BIT */
|
|
75
|
+
#define CM_ENC_UTF8 1 /* :E true */
|
|
76
|
+
#define CM_ENC_USASCII 2 /* :E false */
|
|
77
|
+
#define CM_ENC_NAMED 3 /* :E names another encoding */
|
|
78
|
+
|
|
79
|
+
typedef struct {
|
|
80
|
+
const char* p;
|
|
81
|
+
size_t len;
|
|
82
|
+
size_t at;
|
|
83
|
+
} cm_stream;
|
|
84
|
+
|
|
85
|
+
typedef struct {
|
|
86
|
+
const char* p;
|
|
87
|
+
uint32_t len;
|
|
88
|
+
} cm_sym;
|
|
89
|
+
|
|
90
|
+
typedef struct {
|
|
91
|
+
uint8_t* p;
|
|
92
|
+
size_t len;
|
|
93
|
+
size_t cap;
|
|
94
|
+
} cm_buf;
|
|
95
|
+
|
|
96
|
+
typedef struct {
|
|
97
|
+
cm_buf* buf; /* where the construct's bytes were emitted (NULL: consumed) */
|
|
98
|
+
size_t off;
|
|
99
|
+
size_t len; /* 0 until the construct completes: a link is a cycle */
|
|
100
|
+
unsigned char kind; /* the wrapped value's lead byte ('I' looks through) */
|
|
101
|
+
/* a String keeps its payload here: a link lands in a position whose
|
|
102
|
+
* role law (value or map key) may re-spell it, so the span copy is
|
|
103
|
+
* not enough. senc is -1 for every non-string construct. */
|
|
104
|
+
const char* sp;
|
|
105
|
+
uint32_t slen;
|
|
106
|
+
int senc;
|
|
107
|
+
} cm_obj;
|
|
108
|
+
|
|
109
|
+
typedef struct {
|
|
110
|
+
cm_stream s;
|
|
111
|
+
cm_sym* syms;
|
|
112
|
+
size_t syms_n, syms_cap;
|
|
113
|
+
cm_obj* objs;
|
|
114
|
+
size_t objs_n, objs_cap;
|
|
115
|
+
cm_buf** keep; /* every buffer, owed until the walk ends */
|
|
116
|
+
size_t keep_n, keep_cap;
|
|
117
|
+
uint32_t opts;
|
|
118
|
+
int failed; /* 0 ok, else the YeptrisStatus to return */
|
|
119
|
+
int no_reg; /* an 'I'-wrapped construct registers ONCE (at the 'I') */
|
|
120
|
+
int in_key; /* the next walk emits a map key (the key law applies) */
|
|
121
|
+
int depth;
|
|
122
|
+
} cm_ctx;
|
|
123
|
+
|
|
124
|
+
static int cm_fail(cm_ctx* c, YeptrisStatus st) {
|
|
125
|
+
if (c->failed == 0) {
|
|
126
|
+
c->failed = (int)st;
|
|
127
|
+
}
|
|
128
|
+
return -1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ---- stream ---------------------------------------------------------- */
|
|
132
|
+
|
|
133
|
+
static int cm_byte(cm_ctx* c, unsigned char* out) {
|
|
134
|
+
if (c->s.at >= c->s.len) {
|
|
135
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
136
|
+
}
|
|
137
|
+
*out = (unsigned char)c->s.p[c->s.at++];
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* the w_long inverse (marshal.c's m_w_long) */
|
|
142
|
+
static int cm_long(cm_ctx* c, int64_t* out) {
|
|
143
|
+
unsigned char b;
|
|
144
|
+
if (cm_byte(c, &b) != 0) {
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
if (b == 0) {
|
|
148
|
+
*out = 0;
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
if (b <= 4) { /* positive size form, k = b */
|
|
152
|
+
int64_t v = 0;
|
|
153
|
+
for (int i = 0; i < (int)b; i++) {
|
|
154
|
+
unsigned char d;
|
|
155
|
+
if (cm_byte(c, &d) != 0) {
|
|
156
|
+
return -1;
|
|
157
|
+
}
|
|
158
|
+
v |= (int64_t)d << (8 * i);
|
|
159
|
+
}
|
|
160
|
+
*out = v;
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
if (b <= 127) {
|
|
164
|
+
*out = (int64_t)b - 5;
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
if (b >= 129 && b <= 250) {
|
|
168
|
+
*out = (int64_t)b - 251;
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
{ /* 251..255: negative size form */
|
|
172
|
+
int bytes = 256 - (int)b;
|
|
173
|
+
int64_t v = 0;
|
|
174
|
+
for (int i = 0; i < bytes; i++) {
|
|
175
|
+
unsigned char d;
|
|
176
|
+
if (cm_byte(c, &d) != 0) {
|
|
177
|
+
return -1;
|
|
178
|
+
}
|
|
179
|
+
v |= (int64_t)d << (8 * i);
|
|
180
|
+
}
|
|
181
|
+
*out = v - ((int64_t)1 << (8 * bytes));
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* length-prefixed bytes with a bounds check */
|
|
187
|
+
static int cm_take(cm_ctx* c, int64_t len, const char** p) {
|
|
188
|
+
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at)) {
|
|
189
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
190
|
+
}
|
|
191
|
+
*p = c->s.p + c->s.at;
|
|
192
|
+
c->s.at += (size_t)len;
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ---- output buffers -------------------------------------------------- */
|
|
197
|
+
|
|
198
|
+
static int cm_reserve(cm_ctx* c, cm_buf* b, size_t extra) {
|
|
199
|
+
if (b->cap - b->len >= extra) {
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
size_t cap = b->cap ? b->cap : 64;
|
|
203
|
+
while (cap - b->len < extra) {
|
|
204
|
+
if (cap > (size_t)-1 / 2) {
|
|
205
|
+
return cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
206
|
+
}
|
|
207
|
+
cap *= 2;
|
|
208
|
+
}
|
|
209
|
+
uint8_t* np = (uint8_t*)realloc(b->p, cap);
|
|
210
|
+
if (np == NULL) {
|
|
211
|
+
return cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
212
|
+
}
|
|
213
|
+
b->p = np;
|
|
214
|
+
b->cap = cap;
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static cm_buf* cm_buf_new(cm_ctx* c) {
|
|
219
|
+
cm_buf* b = (cm_buf*)calloc(1, sizeof(*b));
|
|
220
|
+
if (b == NULL) {
|
|
221
|
+
cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
222
|
+
return NULL;
|
|
223
|
+
}
|
|
224
|
+
if (c->keep_n == c->keep_cap) {
|
|
225
|
+
size_t cap = c->keep_cap ? c->keep_cap * 2 : 8;
|
|
226
|
+
cm_buf** nk = (cm_buf**)realloc(c->keep, cap * sizeof(*nk));
|
|
227
|
+
if (nk == NULL) {
|
|
228
|
+
free(b);
|
|
229
|
+
cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
230
|
+
return NULL;
|
|
231
|
+
}
|
|
232
|
+
c->keep = nk;
|
|
233
|
+
c->keep_cap = cap;
|
|
234
|
+
}
|
|
235
|
+
c->keep[c->keep_n++] = b;
|
|
236
|
+
return b;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static int cm_put(cm_ctx* c, cm_buf* b, const void* p, size_t n) {
|
|
240
|
+
if (n == 0) {
|
|
241
|
+
return 0;
|
|
242
|
+
}
|
|
243
|
+
if (cm_reserve(c, b, n) != 0) {
|
|
244
|
+
return -1;
|
|
245
|
+
}
|
|
246
|
+
memcpy(b->p + b->len, p, n);
|
|
247
|
+
b->len += n;
|
|
248
|
+
return 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
static int cm_byte_out(cm_ctx* c, cm_buf* b, uint8_t v) {
|
|
252
|
+
if (cm_reserve(c, b, 1) != 0) {
|
|
253
|
+
return -1;
|
|
254
|
+
}
|
|
255
|
+
b->p[b->len++] = v;
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static int cm_head(cm_ctx* c, cm_buf* b, uint8_t mt, uint64_t arg) {
|
|
260
|
+
if (cm_reserve(c, b, 9) != 0) {
|
|
261
|
+
return -1;
|
|
262
|
+
}
|
|
263
|
+
size_t pos = 0;
|
|
264
|
+
yep_cbor_put_head(b->p + b->len, &pos, mt, arg);
|
|
265
|
+
b->len += pos;
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
static int cm_ascii_only(const char* p, uint32_t n) {
|
|
270
|
+
for (uint32_t i = 0; i < n; i++) {
|
|
271
|
+
if ((unsigned char)p[i] >= 0x80) {
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
static int cm_text(cm_ctx* c, cm_buf* b, const char* p, uint32_t n) {
|
|
279
|
+
int sv = yep_cbor_simple_of(p, n);
|
|
280
|
+
if (sv >= 0) { /* the reserved diagnostic words (encode.c's ledger) */
|
|
281
|
+
if (sv < 24) {
|
|
282
|
+
return cm_byte_out(c, b, (uint8_t)(0xE0 | (uint8_t)sv));
|
|
283
|
+
}
|
|
284
|
+
return cm_byte_out(c, b, 0xF8) != 0 ? -1 : cm_byte_out(c, b, (uint8_t)sv);
|
|
285
|
+
}
|
|
286
|
+
if (n > 0 && !yep_utf8_validate((const unsigned char*)p, n, NULL)) {
|
|
287
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
288
|
+
}
|
|
289
|
+
if (cm_head(c, b, 3, n) != 0) {
|
|
290
|
+
return -1;
|
|
291
|
+
}
|
|
292
|
+
return cm_put(c, b, p, n);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* a symbol spells ":name" (the YAMLTree scalar the per-node route
|
|
296
|
+
* emits); invalid UTF-8 rides a byte string like encode.c's fallback */
|
|
297
|
+
static int cm_symbol_text(cm_ctx* c, cm_buf* b, const char* p, uint32_t n) {
|
|
298
|
+
uint8_t mt = n == 0 || yep_utf8_validate((const unsigned char*)p, n, NULL) ? 3 : 2;
|
|
299
|
+
if (cm_head(c, b, mt, (uint64_t)n + 1) != 0) {
|
|
300
|
+
return -1;
|
|
301
|
+
}
|
|
302
|
+
if (cm_byte_out(c, b, ':') != 0) {
|
|
303
|
+
return -1;
|
|
304
|
+
}
|
|
305
|
+
return cm_put(c, b, p, n);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* A map KEY carries no encoding in the per-node route: map_add stores
|
|
309
|
+
* the raw bytes as a PLANE scalar and the document's compat-11
|
|
310
|
+
* resolver retypes it. INT/FLOAT/BOOL/NULL verdicts are UNSUPPORTED
|
|
311
|
+
* (the fallback's number/bool/null key spelling is the only behavior;
|
|
312
|
+
* an empty key is the NULL verdict). Text keys keep the simple
|
|
313
|
+
* ledger; invalid UTF-8 becomes a byte string (encode.c's
|
|
314
|
+
* cbor_encode_key). */
|
|
315
|
+
static int cm_key_bytes(cm_ctx* c, cm_buf* b, const char* p, uint32_t n) {
|
|
316
|
+
if (n > 0 && !yep_utf8_validate((const unsigned char*)p, n, NULL)) {
|
|
317
|
+
if (cm_head(c, b, 2, n) != 0) {
|
|
318
|
+
return -1;
|
|
319
|
+
}
|
|
320
|
+
return cm_put(c, b, p, n);
|
|
321
|
+
}
|
|
322
|
+
yep_tag_id tag = yep_resolver_compat11()->resolve(NULL, p, n);
|
|
323
|
+
if (tag == YEPTRIS_TAG_INT || tag == YEPTRIS_TAG_FLOAT || tag == YEPTRIS_TAG_BOOL ||
|
|
324
|
+
tag == YEPTRIS_TAG_NULL) {
|
|
325
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
326
|
+
}
|
|
327
|
+
return cm_text(c, b, p, n);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* A string VALUE keeps the per-node route's Builder#build_string law:
|
|
331
|
+
* non-ASCII ASCII-8BIT strings take the !binary tag the encoder
|
|
332
|
+
* refuses; UTF-8 must validate (the plain-safety regexp raises);
|
|
333
|
+
* US-ASCII must be 7-bit; named encodings are UNSUPPORTED. */
|
|
334
|
+
static int cm_value_bytes(cm_ctx* c, cm_buf* b, const char* p, uint32_t n, int enc) {
|
|
335
|
+
int ascii = cm_ascii_only(p, n);
|
|
336
|
+
switch (enc) {
|
|
337
|
+
case CM_ENC_UTF8:
|
|
338
|
+
if (!ascii && !yep_utf8_validate((const unsigned char*)p, n, NULL)) {
|
|
339
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
case CM_ENC_BINARY:
|
|
343
|
+
case CM_ENC_USASCII:
|
|
344
|
+
if (!ascii) {
|
|
345
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
default:
|
|
349
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
350
|
+
}
|
|
351
|
+
return cm_text(c, b, p, n);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* ---- tables ---------------------------------------------------------- */
|
|
355
|
+
|
|
356
|
+
static int cm_symbol(cm_ctx* c, int emit, cm_buf* out) {
|
|
357
|
+
int64_t len;
|
|
358
|
+
if (cm_long(c, &len) != 0) {
|
|
359
|
+
return -1;
|
|
360
|
+
}
|
|
361
|
+
const char* name;
|
|
362
|
+
if (cm_take(c, len, &name) != 0) {
|
|
363
|
+
return -1;
|
|
364
|
+
}
|
|
365
|
+
if (c->syms_n == c->syms_cap) {
|
|
366
|
+
size_t cap = c->syms_cap ? c->syms_cap * 2 : 16;
|
|
367
|
+
cm_sym* ns = (cm_sym*)realloc(c->syms, cap * sizeof(*ns));
|
|
368
|
+
if (ns == NULL) {
|
|
369
|
+
return cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
370
|
+
}
|
|
371
|
+
c->syms = ns;
|
|
372
|
+
c->syms_cap = cap;
|
|
373
|
+
}
|
|
374
|
+
c->syms[c->syms_n].p = name;
|
|
375
|
+
c->syms[c->syms_n].len = (uint32_t)len;
|
|
376
|
+
c->syms_n++;
|
|
377
|
+
if (emit) {
|
|
378
|
+
return cm_symbol_text(c, out, name, (uint32_t)len);
|
|
379
|
+
}
|
|
380
|
+
return 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* the object memo entry for a registering construct. Returns the index
|
|
384
|
+
* or -1 (failed). */
|
|
385
|
+
static int cm_obj_register(cm_ctx* c, cm_buf* out, unsigned char kind) {
|
|
386
|
+
if (c->objs_n == c->objs_cap) {
|
|
387
|
+
size_t cap = c->objs_cap ? c->objs_cap * 2 : 16;
|
|
388
|
+
cm_obj* no = (cm_obj*)realloc(c->objs, cap * sizeof(*no));
|
|
389
|
+
if (no == NULL) {
|
|
390
|
+
cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
391
|
+
return -1;
|
|
392
|
+
}
|
|
393
|
+
c->objs = no;
|
|
394
|
+
c->objs_cap = cap;
|
|
395
|
+
}
|
|
396
|
+
size_t idx = c->objs_n++;
|
|
397
|
+
c->objs[idx].buf = out;
|
|
398
|
+
c->objs[idx].off = out != NULL ? out->len : 0;
|
|
399
|
+
c->objs[idx].len = 0;
|
|
400
|
+
c->objs[idx].kind = kind;
|
|
401
|
+
c->objs[idx].sp = NULL;
|
|
402
|
+
c->objs[idx].slen = 0;
|
|
403
|
+
c->objs[idx].senc = -1;
|
|
404
|
+
return (int)idx;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* '@' link. A linked STRING is re-spelled by the role law of the
|
|
408
|
+
* position it lands in (a value keeps Builder#build_string's encoding
|
|
409
|
+
* rules; a map key resolves like every other key text) — the per-node
|
|
410
|
+
* route duplicates the scalar into that position, so a copied span
|
|
411
|
+
* would carry the wrong role's spelling. Every other registering
|
|
412
|
+
* construct copies its span: floats encode identically in both roles,
|
|
413
|
+
* and containers are declined (the per-node route anchors them). */
|
|
414
|
+
static int cm_emit_memo(cm_ctx* c, const cm_obj* o, cm_buf* out) {
|
|
415
|
+
if (out == NULL) {
|
|
416
|
+
return 0; /* consumed, not emitted */
|
|
417
|
+
}
|
|
418
|
+
if (o->senc >= 0) {
|
|
419
|
+
return c->in_key ? cm_key_bytes(c, out, o->sp, o->slen)
|
|
420
|
+
: cm_value_bytes(c, out, o->sp, o->slen, o->senc);
|
|
421
|
+
}
|
|
422
|
+
if (o->buf == NULL) {
|
|
423
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED); /* consumed, not emitted */
|
|
424
|
+
}
|
|
425
|
+
if (o->len == 0) {
|
|
426
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED); /* a cycle */
|
|
427
|
+
}
|
|
428
|
+
if (o->kind == '[' || o->kind == '{') {
|
|
429
|
+
/* the per-node route anchors containers; the encoder refuses
|
|
430
|
+
* the alias — the fallback raises */
|
|
431
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
432
|
+
}
|
|
433
|
+
if (cm_reserve(c, out, o->len) != 0) {
|
|
434
|
+
return -1;
|
|
435
|
+
}
|
|
436
|
+
/* the span may live in out itself: reserve first, then memmove */
|
|
437
|
+
memmove(out->p + out->len, o->buf->p + o->off, o->len);
|
|
438
|
+
out->len += o->len;
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
static int cm_value(cm_ctx* c, cm_buf* out);
|
|
443
|
+
|
|
444
|
+
/* The count + (symbol, value) pairs that follow an 'I' wrapper. The
|
|
445
|
+
* values are consumed (out == NULL) but still REGISTER — Marshal's
|
|
446
|
+
* link slots are stream-order, context-free. When enc is non-NULL the
|
|
447
|
+
* encoding entries are captured: ":E" carries true (UTF-8) / false
|
|
448
|
+
* (US-ASCII), and a named encoding rides its own ":encoding" ivar. */
|
|
449
|
+
static int cm_ivar_pairs(cm_ctx* c, int* enc) {
|
|
450
|
+
int64_t count;
|
|
451
|
+
if (cm_long(c, &count) != 0) {
|
|
452
|
+
return -1;
|
|
453
|
+
}
|
|
454
|
+
if (count < 0) {
|
|
455
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
456
|
+
}
|
|
457
|
+
for (int64_t i = 0; i < count; i++) {
|
|
458
|
+
unsigned char nt;
|
|
459
|
+
if (cm_byte(c, &nt) != 0) {
|
|
460
|
+
return -1;
|
|
461
|
+
}
|
|
462
|
+
int is_e = 0;
|
|
463
|
+
int is_enc = 0;
|
|
464
|
+
if (nt == ':') {
|
|
465
|
+
if (cm_symbol(c, 0, NULL) != 0) {
|
|
466
|
+
return -1;
|
|
467
|
+
}
|
|
468
|
+
const cm_sym* s = &c->syms[c->syms_n - 1];
|
|
469
|
+
is_e = s->len == 1 && s->p[0] == 'E';
|
|
470
|
+
is_enc = s->len == 8 && memcmp(s->p, "encoding", 8) == 0;
|
|
471
|
+
} else if (nt == ';') {
|
|
472
|
+
int64_t sidx;
|
|
473
|
+
if (cm_long(c, &sidx) != 0) {
|
|
474
|
+
return -1;
|
|
475
|
+
}
|
|
476
|
+
if (sidx < 0 || (size_t)sidx >= c->syms_n) {
|
|
477
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
478
|
+
}
|
|
479
|
+
is_e = c->syms[sidx].len == 1 && c->syms[sidx].p[0] == 'E';
|
|
480
|
+
is_enc = c->syms[sidx].len == 8 && memcmp(c->syms[sidx].p, "encoding", 8) == 0;
|
|
481
|
+
} else {
|
|
482
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
483
|
+
}
|
|
484
|
+
if (enc != NULL && (is_e || is_enc)) {
|
|
485
|
+
if (c->s.at >= c->s.len) {
|
|
486
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
487
|
+
}
|
|
488
|
+
unsigned char vt = (unsigned char)c->s.p[c->s.at];
|
|
489
|
+
*enc = is_enc ? CM_ENC_NAMED
|
|
490
|
+
: vt == 'T' ? CM_ENC_UTF8
|
|
491
|
+
: vt == 'F' ? CM_ENC_USASCII
|
|
492
|
+
: CM_ENC_NAMED;
|
|
493
|
+
}
|
|
494
|
+
if (cm_value(c, NULL) != 0) { /* the ivar value: walked, not emitted */
|
|
495
|
+
return -1;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return 0;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* 'I' + '"': the encoding ivar follows the payload, and the value law
|
|
502
|
+
* needs it — parse both here instead of the transparent wrapper path.
|
|
503
|
+
* reg_idx is the wrapper's own memo slot; the payload recorded there
|
|
504
|
+
* lets a later '@' re-spell the string by its landing role. */
|
|
505
|
+
static int cm_wrapped_string(cm_ctx* c, cm_buf* out, int reg_idx) {
|
|
506
|
+
unsigned char inner;
|
|
507
|
+
if (cm_byte(c, &inner) != 0) {
|
|
508
|
+
return -1;
|
|
509
|
+
}
|
|
510
|
+
if (inner != '"') {
|
|
511
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
512
|
+
}
|
|
513
|
+
int64_t len;
|
|
514
|
+
if (cm_long(c, &len) != 0) {
|
|
515
|
+
return -1;
|
|
516
|
+
}
|
|
517
|
+
const char* p;
|
|
518
|
+
if (cm_take(c, len, &p) != 0) {
|
|
519
|
+
return -1;
|
|
520
|
+
}
|
|
521
|
+
int enc = CM_ENC_BINARY; /* no :E ivar: the original was ASCII-8BIT */
|
|
522
|
+
if (cm_ivar_pairs(c, &enc) != 0) {
|
|
523
|
+
return -1;
|
|
524
|
+
}
|
|
525
|
+
if (reg_idx >= 0) {
|
|
526
|
+
c->objs[reg_idx].sp = p;
|
|
527
|
+
c->objs[reg_idx].slen = (uint32_t)len;
|
|
528
|
+
c->objs[reg_idx].senc = enc;
|
|
529
|
+
}
|
|
530
|
+
if (out == NULL) {
|
|
531
|
+
return 0;
|
|
532
|
+
}
|
|
533
|
+
if (c->in_key) {
|
|
534
|
+
return cm_key_bytes(c, out, p, (uint32_t)len);
|
|
535
|
+
}
|
|
536
|
+
return cm_value_bytes(c, out, p, (uint32_t)len, enc);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* the per-node route's map-key spellings: strings (plain or 'I'
|
|
540
|
+
* wrapped), symbols and their links, integers, floats, bignums and
|
|
541
|
+
* object links. nil/array/hash/object/boolean keys stay with the
|
|
542
|
+
* fallback — its to_s/raise behavior (and the bool sort-key split) is
|
|
543
|
+
* the only behavior. */
|
|
544
|
+
static int cm_key_lead_ok(unsigned char lead) {
|
|
545
|
+
return lead == '"' || lead == 'I' || lead == ':' || lead == ';' || lead == '@' || lead == 'i' ||
|
|
546
|
+
lead == 'l' || lead == 'f';
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/* one map key (the key law applies to the construct's lead) */
|
|
550
|
+
static int cm_map_key(cm_ctx* c, cm_buf* b) {
|
|
551
|
+
int was = c->in_key;
|
|
552
|
+
c->in_key = 1;
|
|
553
|
+
int rc = cm_value(c, b);
|
|
554
|
+
c->in_key = was;
|
|
555
|
+
return rc;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/* Walks one value. out == NULL: consume only (ivar values). A
|
|
559
|
+
* registering construct ('[', '{', '"', 'f', 'l', an 'I' wrapper)
|
|
560
|
+
* closes its memo span once its bytes are out — the JSON reader's
|
|
561
|
+
* single-close-out law. A bignum takes a slot even though Ruby never
|
|
562
|
+
* emits a link to one: the slot count is what later '@' indices
|
|
563
|
+
* address. */
|
|
564
|
+
static int cm_value(cm_ctx* c, cm_buf* out) {
|
|
565
|
+
unsigned char t;
|
|
566
|
+
int rc = 0;
|
|
567
|
+
int reg_idx = -1;
|
|
568
|
+
int register_this = !c->no_reg;
|
|
569
|
+
c->no_reg = 0;
|
|
570
|
+
if (c->depth >= CM_MAX_DEPTH) {
|
|
571
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
572
|
+
}
|
|
573
|
+
if (cm_byte(c, &t) != 0) {
|
|
574
|
+
return -1;
|
|
575
|
+
}
|
|
576
|
+
if (register_this && (t == '[' || t == '{' || t == '"' || t == 'f' || t == 'l' || t == 'I')) {
|
|
577
|
+
unsigned char kind = t;
|
|
578
|
+
if (t == 'I') { /* the wrapper is transparent for links */
|
|
579
|
+
kind = c->s.at < c->s.len ? (unsigned char)c->s.p[c->s.at] : 0;
|
|
580
|
+
}
|
|
581
|
+
reg_idx = cm_obj_register(c, out, kind);
|
|
582
|
+
if (reg_idx < 0) {
|
|
583
|
+
return -1;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
switch (t) {
|
|
587
|
+
case '0':
|
|
588
|
+
rc = out != NULL ? cm_byte_out(c, out, 0xF6) : 0;
|
|
589
|
+
break;
|
|
590
|
+
case 'T':
|
|
591
|
+
rc = out != NULL ? cm_byte_out(c, out, 0xF5) : 0;
|
|
592
|
+
break;
|
|
593
|
+
case 'F':
|
|
594
|
+
rc = out != NULL ? cm_byte_out(c, out, 0xF4) : 0;
|
|
595
|
+
break;
|
|
596
|
+
case 'i': {
|
|
597
|
+
int64_t v;
|
|
598
|
+
if (cm_long(c, &v) != 0) {
|
|
599
|
+
return -1;
|
|
600
|
+
}
|
|
601
|
+
if (out != NULL) {
|
|
602
|
+
rc =
|
|
603
|
+
v >= 0 ? cm_head(c, out, 0, (uint64_t)v) : cm_head(c, out, 1, (uint64_t)(-(v + 1)));
|
|
604
|
+
}
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
case 'l': {
|
|
608
|
+
unsigned char sign;
|
|
609
|
+
if (cm_byte(c, &sign) != 0) {
|
|
610
|
+
return -1;
|
|
611
|
+
}
|
|
612
|
+
if (sign != 0x2b && sign != 0x2d) {
|
|
613
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
614
|
+
}
|
|
615
|
+
int64_t nlimbs;
|
|
616
|
+
if (cm_long(c, &nlimbs) != 0) {
|
|
617
|
+
return -1;
|
|
618
|
+
}
|
|
619
|
+
if (nlimbs < 1 || (uint64_t)(nlimbs * 2) > (uint64_t)(c->s.len - c->s.at)) {
|
|
620
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
621
|
+
}
|
|
622
|
+
if (nlimbs > 4) { /* beyond four 16-bit words: the fallback's floats */
|
|
623
|
+
c->s.at += (size_t)(nlimbs * 2);
|
|
624
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
625
|
+
}
|
|
626
|
+
uint64_t mag = 0;
|
|
627
|
+
for (int64_t i = 0; i < nlimbs; i++) {
|
|
628
|
+
unsigned char lo, hi;
|
|
629
|
+
if (cm_byte(c, &lo) != 0 || cm_byte(c, &hi) != 0) {
|
|
630
|
+
return -1;
|
|
631
|
+
}
|
|
632
|
+
mag |= (uint64_t)(lo | ((uint64_t)hi << 8)) << (16 * i);
|
|
633
|
+
}
|
|
634
|
+
if (out == NULL) {
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
if (sign == 0x2d) {
|
|
638
|
+
if (mag <= ((uint64_t)1 << 63)) {
|
|
639
|
+
rc = mag == 0 ? cm_head(c, out, 0, 0) : cm_head(c, out, 1, mag - 1);
|
|
640
|
+
} else {
|
|
641
|
+
/* past int64 the per-node route re-renders the scalar as
|
|
642
|
+
* a preferred float, sign included */
|
|
643
|
+
double d = -(double)mag;
|
|
644
|
+
uint8_t fw = yep_cbor_float_width(d);
|
|
645
|
+
if (cm_reserve(c, out, 9) != 0) {
|
|
646
|
+
return -1;
|
|
647
|
+
}
|
|
648
|
+
size_t pos = 0;
|
|
649
|
+
yep_cbor_put_float(out->p + out->len, &pos, d, fw);
|
|
650
|
+
out->len += pos;
|
|
651
|
+
}
|
|
652
|
+
} else if (mag <= (uint64_t)INT64_MAX) {
|
|
653
|
+
rc = cm_head(c, out, 0, mag);
|
|
654
|
+
} else {
|
|
655
|
+
/* the per-node route re-renders out-of-int64 integers as
|
|
656
|
+
* preferred floats (the number kernel's ledger) */
|
|
657
|
+
double d = (double)mag;
|
|
658
|
+
uint8_t fw = yep_cbor_float_width(d);
|
|
659
|
+
if (cm_reserve(c, out, 9) != 0) {
|
|
660
|
+
return -1;
|
|
661
|
+
}
|
|
662
|
+
size_t pos = 0;
|
|
663
|
+
yep_cbor_put_float(out->p + out->len, &pos, d, fw);
|
|
664
|
+
out->len += pos;
|
|
665
|
+
}
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
case 'f': {
|
|
669
|
+
int64_t len;
|
|
670
|
+
if (cm_long(c, &len) != 0) {
|
|
671
|
+
return -1;
|
|
672
|
+
}
|
|
673
|
+
const char* p;
|
|
674
|
+
if (cm_take(c, len, &p) != 0) {
|
|
675
|
+
return -1;
|
|
676
|
+
}
|
|
677
|
+
if (out == NULL) {
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
char tmp[64];
|
|
681
|
+
if (len >= (int64_t)sizeof(tmp)) {
|
|
682
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
683
|
+
}
|
|
684
|
+
memcpy(tmp, p, (size_t)len);
|
|
685
|
+
tmp[len] = '\0';
|
|
686
|
+
if (strcmp(tmp, "inf") == 0 || strcmp(tmp, "-inf") == 0 || strcmp(tmp, "nan") == 0) {
|
|
687
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED); /* the host loop raises */
|
|
688
|
+
}
|
|
689
|
+
double d = strtod(tmp, NULL);
|
|
690
|
+
uint8_t fw = yep_cbor_float_width(d);
|
|
691
|
+
if (cm_reserve(c, out, 9) != 0) {
|
|
692
|
+
return -1;
|
|
693
|
+
}
|
|
694
|
+
size_t pos = 0;
|
|
695
|
+
yep_cbor_put_float(out->p + out->len, &pos, d, fw);
|
|
696
|
+
out->len += pos;
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
case '"': {
|
|
700
|
+
int64_t len;
|
|
701
|
+
if (cm_long(c, &len) != 0) {
|
|
702
|
+
return -1;
|
|
703
|
+
}
|
|
704
|
+
const char* p;
|
|
705
|
+
if (cm_take(c, len, &p) != 0) {
|
|
706
|
+
return -1;
|
|
707
|
+
}
|
|
708
|
+
if (reg_idx >= 0) {
|
|
709
|
+
c->objs[reg_idx].sp = p;
|
|
710
|
+
c->objs[reg_idx].slen = (uint32_t)len;
|
|
711
|
+
c->objs[reg_idx].senc = CM_ENC_BINARY;
|
|
712
|
+
}
|
|
713
|
+
if (out != NULL) {
|
|
714
|
+
rc = c->in_key ? cm_key_bytes(c, out, p, (uint32_t)len)
|
|
715
|
+
: cm_value_bytes(c, out, p, (uint32_t)len, CM_ENC_BINARY);
|
|
716
|
+
}
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
case 'I': {
|
|
720
|
+
if (c->s.at < c->s.len && c->s.p[c->s.at] == '"') {
|
|
721
|
+
rc = cm_wrapped_string(c, out, reg_idx);
|
|
722
|
+
} else if (c->in_key) {
|
|
723
|
+
/* an I-wrapped array/hash key is the container's TEXT in
|
|
724
|
+
* the per-node route ("[1]") — not expressible here */
|
|
725
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
726
|
+
} else {
|
|
727
|
+
c->no_reg = 1;
|
|
728
|
+
rc = cm_value(c, out);
|
|
729
|
+
c->no_reg = 0;
|
|
730
|
+
if (rc == 0) {
|
|
731
|
+
rc = cm_ivar_pairs(c, NULL);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
case ':':
|
|
737
|
+
rc = out != NULL ? cm_symbol(c, 1, out) : cm_symbol(c, 0, NULL);
|
|
738
|
+
break;
|
|
739
|
+
case ';': {
|
|
740
|
+
int64_t idx;
|
|
741
|
+
if (cm_long(c, &idx) != 0) {
|
|
742
|
+
return -1;
|
|
743
|
+
}
|
|
744
|
+
if (idx < 0 || (size_t)idx >= c->syms_n) {
|
|
745
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
746
|
+
}
|
|
747
|
+
if (out != NULL) {
|
|
748
|
+
rc = cm_symbol_text(c, out, c->syms[idx].p, c->syms[idx].len);
|
|
749
|
+
}
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
case '@': {
|
|
753
|
+
int64_t idx;
|
|
754
|
+
if (cm_long(c, &idx) != 0) {
|
|
755
|
+
return -1;
|
|
756
|
+
}
|
|
757
|
+
if (idx < 0 || (size_t)idx >= c->objs_n) {
|
|
758
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
759
|
+
}
|
|
760
|
+
rc = cm_emit_memo(c, &c->objs[idx], out);
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
case '[': {
|
|
764
|
+
int64_t count;
|
|
765
|
+
if (cm_long(c, &count) != 0) {
|
|
766
|
+
return -1;
|
|
767
|
+
}
|
|
768
|
+
if (count < 0) {
|
|
769
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
770
|
+
}
|
|
771
|
+
if (out != NULL && cm_head(c, out, 4, (uint64_t)count) != 0) {
|
|
772
|
+
return -1;
|
|
773
|
+
}
|
|
774
|
+
c->depth++;
|
|
775
|
+
for (int64_t i = 0; i < count; i++) {
|
|
776
|
+
if (cm_value(c, out) != 0) {
|
|
777
|
+
c->depth--;
|
|
778
|
+
return -1;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
c->depth--;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
case '{': {
|
|
785
|
+
int64_t count;
|
|
786
|
+
if (cm_long(c, &count) != 0) {
|
|
787
|
+
return -1;
|
|
788
|
+
}
|
|
789
|
+
if (count < 0) {
|
|
790
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
791
|
+
}
|
|
792
|
+
if (count > 1 && (size_t)count > (size_t)-1 / (2 * sizeof(size_t))) {
|
|
793
|
+
return cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
794
|
+
}
|
|
795
|
+
if (out == NULL) { /* an ivar value: consume the pairs only */
|
|
796
|
+
c->depth++;
|
|
797
|
+
for (int64_t i = 0; i < count; i++) {
|
|
798
|
+
if (cm_map_key(c, NULL) != 0 || cm_value(c, NULL) != 0) {
|
|
799
|
+
c->depth--;
|
|
800
|
+
return -1;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
c->depth--;
|
|
804
|
+
break;
|
|
805
|
+
}
|
|
806
|
+
int canonical = (c->opts & YEPTRIS_CBOR_CANONICAL) != 0;
|
|
807
|
+
cm_buf* scratch = NULL;
|
|
808
|
+
size_t* key_off = NULL;
|
|
809
|
+
size_t* key_len = NULL;
|
|
810
|
+
size_t* pair_off = NULL;
|
|
811
|
+
size_t* pair_len = NULL;
|
|
812
|
+
uint32_t* order = NULL;
|
|
813
|
+
int mrc = -1;
|
|
814
|
+
if (count > 1) {
|
|
815
|
+
/* the key spans serve the duplicate-key check (both modes)
|
|
816
|
+
* and the canonical sort */
|
|
817
|
+
key_off = (size_t*)calloc((size_t)count, sizeof(size_t));
|
|
818
|
+
key_len = (size_t*)calloc((size_t)count, sizeof(size_t));
|
|
819
|
+
if (key_off == NULL || key_len == NULL) {
|
|
820
|
+
cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
821
|
+
goto map_out;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (canonical && count > 0) {
|
|
825
|
+
scratch = cm_buf_new(c);
|
|
826
|
+
pair_off = (size_t*)calloc((size_t)count, sizeof(size_t));
|
|
827
|
+
pair_len = (size_t*)calloc((size_t)count, sizeof(size_t));
|
|
828
|
+
order = (uint32_t*)calloc((size_t)count, sizeof(uint32_t));
|
|
829
|
+
if (scratch == NULL || pair_off == NULL || pair_len == NULL || order == NULL) {
|
|
830
|
+
cm_fail(c, YEPTRIS_ERROR_MEMORY);
|
|
831
|
+
goto map_out;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
{
|
|
835
|
+
cm_buf* dst = (canonical && count > 0) ? scratch : out;
|
|
836
|
+
if (!canonical && cm_head(c, out, 5, (uint64_t)count) != 0) {
|
|
837
|
+
goto map_out;
|
|
838
|
+
}
|
|
839
|
+
c->depth++;
|
|
840
|
+
for (int64_t i = 0; i < count; i++) {
|
|
841
|
+
if (c->s.at >= c->s.len) {
|
|
842
|
+
cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
843
|
+
goto map_out;
|
|
844
|
+
}
|
|
845
|
+
if (!cm_key_lead_ok((unsigned char)c->s.p[c->s.at])) {
|
|
846
|
+
cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
847
|
+
goto map_out;
|
|
848
|
+
}
|
|
849
|
+
if (canonical) {
|
|
850
|
+
pair_off[i] = dst->len;
|
|
851
|
+
}
|
|
852
|
+
if (count > 1) {
|
|
853
|
+
key_off[i] = dst->len;
|
|
854
|
+
}
|
|
855
|
+
if (cm_map_key(c, dst) != 0) {
|
|
856
|
+
goto map_out;
|
|
857
|
+
}
|
|
858
|
+
if (count > 1) {
|
|
859
|
+
key_len[i] = dst->len - key_off[i];
|
|
860
|
+
}
|
|
861
|
+
if (cm_value(c, dst) != 0) {
|
|
862
|
+
goto map_out;
|
|
863
|
+
}
|
|
864
|
+
if (canonical) {
|
|
865
|
+
pair_len[i] = dst->len - pair_off[i];
|
|
866
|
+
order[i] = (uint32_t)i;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
c->depth--;
|
|
870
|
+
if (count > 1) {
|
|
871
|
+
/* the per-node route's map_add duplicate check compares
|
|
872
|
+
* the stored key TEXT; among supported keys that is
|
|
873
|
+
* exactly emitted-byte equality */
|
|
874
|
+
for (int64_t i = 1; i < count; i++) {
|
|
875
|
+
for (int64_t j = 0; j < i; j++) {
|
|
876
|
+
if (key_len[i] == key_len[j] &&
|
|
877
|
+
(key_len[i] == 0 ||
|
|
878
|
+
memcmp(dst->p + key_off[i], dst->p + key_off[j], key_len[i]) == 0)) {
|
|
879
|
+
cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
880
|
+
goto map_out;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
if (canonical) {
|
|
886
|
+
for (int64_t p = 1; p < count; p++) { /* insertion sort: stable */
|
|
887
|
+
uint32_t cur = order[p];
|
|
888
|
+
int64_t j = p;
|
|
889
|
+
while (j > 0) {
|
|
890
|
+
uint32_t other = order[j - 1];
|
|
891
|
+
const uint8_t* a = dst->p + key_off[cur];
|
|
892
|
+
const uint8_t* b = dst->p + key_off[other];
|
|
893
|
+
size_t la = key_len[cur];
|
|
894
|
+
size_t lb = key_len[other];
|
|
895
|
+
size_t min = la < lb ? la : lb;
|
|
896
|
+
int cmp = memcmp(a, b, min);
|
|
897
|
+
if (cmp < 0 || (cmp == 0 && la < lb)) {
|
|
898
|
+
order[j] = order[j - 1];
|
|
899
|
+
j--;
|
|
900
|
+
} else {
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
order[j] = cur;
|
|
905
|
+
}
|
|
906
|
+
if (cm_head(c, out, 5, (uint64_t)count) != 0) {
|
|
907
|
+
goto map_out;
|
|
908
|
+
}
|
|
909
|
+
for (int64_t p = 0; p < count; p++) {
|
|
910
|
+
uint32_t cur = order[p];
|
|
911
|
+
if (cm_put(c, out, dst->p + pair_off[cur], pair_len[cur]) != 0) {
|
|
912
|
+
goto map_out;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
mrc = 0;
|
|
917
|
+
}
|
|
918
|
+
map_out:
|
|
919
|
+
free(pair_off);
|
|
920
|
+
free(pair_len);
|
|
921
|
+
free(order);
|
|
922
|
+
free(key_off);
|
|
923
|
+
free(key_len);
|
|
924
|
+
rc = mrc;
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
default:
|
|
928
|
+
return cm_fail(c, YEPTRIS_ERROR_UNSUPPORTED);
|
|
929
|
+
}
|
|
930
|
+
if (reg_idx >= 0 && out != NULL) {
|
|
931
|
+
c->objs[reg_idx].len = out->len - c->objs[reg_idx].off;
|
|
932
|
+
}
|
|
933
|
+
return rc;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
YEPTRIS_API YeptrisStatus yeptris_cbor_from_marshal(const char* m, size_t m_len, uint32_t opts,
|
|
937
|
+
char** out, size_t* out_len) {
|
|
938
|
+
if (out == NULL || out_len == NULL) {
|
|
939
|
+
return YEPTRIS_ERROR_ARG;
|
|
940
|
+
}
|
|
941
|
+
*out = NULL;
|
|
942
|
+
*out_len = 0;
|
|
943
|
+
if (m == NULL || m_len < 2 || (unsigned char)m[0] != 0x04 || (unsigned char)m[1] != 0x08) {
|
|
944
|
+
return YEPTRIS_ERROR_UNSUPPORTED;
|
|
945
|
+
}
|
|
946
|
+
cm_ctx c;
|
|
947
|
+
memset(&c, 0, sizeof(c));
|
|
948
|
+
c.s.p = m;
|
|
949
|
+
c.s.len = m_len;
|
|
950
|
+
c.s.at = 2;
|
|
951
|
+
c.opts = opts;
|
|
952
|
+
cm_buf* root = cm_buf_new(&c);
|
|
953
|
+
YeptrisStatus st;
|
|
954
|
+
if (root == NULL) {
|
|
955
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
956
|
+
} else if (cm_value(&c, root) != 0) {
|
|
957
|
+
st = c.failed ? (YeptrisStatus)c.failed : YEPTRIS_ERROR_UNSUPPORTED;
|
|
958
|
+
} else if (c.s.at != m_len) {
|
|
959
|
+
st = YEPTRIS_ERROR_UNSUPPORTED; /* trailing bytes: not one stream */
|
|
960
|
+
} else {
|
|
961
|
+
*out = (char*)root->p;
|
|
962
|
+
*out_len = root->len;
|
|
963
|
+
root->p = NULL;
|
|
964
|
+
root->len = 0;
|
|
965
|
+
st = YEPTRIS_OK;
|
|
966
|
+
}
|
|
967
|
+
for (size_t i = 0; i < c.keep_n; i++) {
|
|
968
|
+
free(c.keep[i]->p);
|
|
969
|
+
free(c.keep[i]);
|
|
970
|
+
}
|
|
971
|
+
free(c.keep);
|
|
972
|
+
free(c.syms);
|
|
973
|
+
free(c.objs);
|
|
974
|
+
return st;
|
|
975
|
+
}
|