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
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
|
|
25
25
|
#include "style.h"
|
|
26
26
|
|
|
27
|
-
#include <math.h>
|
|
28
27
|
#include <stdio.h>
|
|
29
28
|
#include <stdlib.h>
|
|
30
29
|
|
|
@@ -57,7 +56,10 @@ static void wr_maybe_flush(yep_writer* w) {
|
|
|
57
56
|
w->len = 0;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
/* the write primitives below are writer.h-visible: the marshal reader
|
|
60
|
+
* (marshal/to_json.c) writes through the same engine (#904 item 5's
|
|
61
|
+
* move kept ONE writer). */
|
|
62
|
+
void wr_col_update(yep_writer* w, const char* p, uint32_t n) {
|
|
61
63
|
for (uint32_t i = n; i > 0; i--) { /* last newline wins */
|
|
62
64
|
if (p[i - 1] == '\n') {
|
|
63
65
|
w->col = (int)(n - i);
|
|
@@ -67,7 +69,7 @@ static void wr_col_update(yep_writer* w, const char* p, uint32_t n) {
|
|
|
67
69
|
w->col += (int)n;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
void wr_reserve(yep_writer* w, size_t need);
|
|
71
73
|
|
|
72
74
|
static void wr_grow(yep_writer* w, size_t need) {
|
|
73
75
|
if (w->oom || w->p == NULL) {
|
|
@@ -78,6 +80,10 @@ static void wr_grow(yep_writer* w, size_t need) {
|
|
|
78
80
|
if (want < w->cap * 2) {
|
|
79
81
|
want = w->cap * 2;
|
|
80
82
|
}
|
|
83
|
+
/* raw realloc ON PURPOSE (#904 item 3): w->p is either the buffer
|
|
84
|
+
* the serializers RETURN (release contract: the host's free(),
|
|
85
|
+
* emit.h) or the caller's own bytes — no document allocator may
|
|
86
|
+
* own either, or the host's free() breaks */
|
|
81
87
|
char* np = (char*)realloc(w->p, want);
|
|
82
88
|
if (np == NULL) {
|
|
83
89
|
w->oom = 1;
|
|
@@ -87,7 +93,7 @@ static void wr_grow(yep_writer* w, size_t need) {
|
|
|
87
93
|
w->cap = want;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
|
|
96
|
+
void wr_put(yep_writer* w, const char* p, uint32_t n) {
|
|
91
97
|
if (n > 0) {
|
|
92
98
|
w->last = p[n - 1];
|
|
93
99
|
}
|
|
@@ -160,7 +166,7 @@ static void wr_put(yep_writer* w, const char* p, uint32_t n) {
|
|
|
160
166
|
* trusted-count routes keep cap bookkeeping that is not the writer's
|
|
161
167
|
* to reason about (several set cap=0), so their writes stay exactly
|
|
162
168
|
* the old per-call trust. */
|
|
163
|
-
|
|
169
|
+
void wr_reserve(yep_writer* w, size_t need) {
|
|
164
170
|
/* the FLUSH runs for every route: the fixed-window streaming route
|
|
165
171
|
* (grow=0) has no reservation, so this refill path is its ONLY
|
|
166
172
|
* flush point -- skipping it let len ride to cap and the next
|
|
@@ -184,7 +190,7 @@ static void wr_reserve(yep_writer* w, size_t need) {
|
|
|
184
190
|
w->reserve = w->cap - w->len - 1;
|
|
185
191
|
}
|
|
186
192
|
|
|
187
|
-
|
|
193
|
+
void wr_byte(yep_writer* w, char c) {
|
|
188
194
|
w->last = c;
|
|
189
195
|
if (c == '\n') {
|
|
190
196
|
w->col = 0;
|
|
@@ -357,9 +363,91 @@ static void emit_sq(yep_writer* w, const char* p, uint32_t n) {
|
|
|
357
363
|
wr_byte(w, '\'');
|
|
358
364
|
}
|
|
359
365
|
|
|
366
|
+
/* Block-side width folding (#903): libyaml's plain/single-quoted fold
|
|
367
|
+
* walk. At an INTERIOR single space (not the value's first or last
|
|
368
|
+
* byte, no adjacent spaces) with the column past best_width, the break
|
|
369
|
+
* REPLACES the space and the continuation re-indents to the fold
|
|
370
|
+
* column — parent_col + indent (libyaml's emit_scalar bumps the write
|
|
371
|
+
* indent to parent_indent + best_indent for the scalar's own lines).
|
|
372
|
+
* The folded bodies carry no breaks by construction: sc_route sends
|
|
373
|
+
* multiline content to DQ/literal, so the walk only decides on ' '
|
|
374
|
+
* (IS_SPACE — a tab is content). Columns are BYTES, matching the
|
|
375
|
+
* engine's existing 13B flow fold; libyaml counts characters, so
|
|
376
|
+
* multi-byte content folds at slightly different points (documented
|
|
377
|
+
* divergence, ASCII input folds byte-identically). */
|
|
378
|
+
|
|
379
|
+
/* The fold break: libyaml's write_indent mid-scalar — indention is
|
|
380
|
+
* always clear there, so it is exactly a break plus the pad. */
|
|
381
|
+
static void wr_fold_break(yep_writer* w, int fold_col) {
|
|
382
|
+
wr_byte(w, '\n');
|
|
383
|
+
wr_indent(w, fold_col);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* The shared walk for a plain body (no doubling to apply). The fold
|
|
387
|
+
* decision prices the PENDING run in (it flushes only at a fold): the
|
|
388
|
+
* live column at the candidate space is w->col + run — exactly
|
|
389
|
+
* libyaml's emitter->column at the same byte. */
|
|
390
|
+
static void emit_plain_folded(yep_writer* w, const char* p, uint32_t n, int fold_col) {
|
|
391
|
+
uint32_t run = 0; /* pending flush span length */
|
|
392
|
+
uint32_t i = 0;
|
|
393
|
+
while (i < n) {
|
|
394
|
+
if (p[i] == ' ' && i > 0 && i + 1 < n && w->col + (int)run > w->best_width &&
|
|
395
|
+
p[i - 1] != ' ' && p[i + 1] != ' ') {
|
|
396
|
+
if (run) {
|
|
397
|
+
wr_put(w, p + i - run, run);
|
|
398
|
+
run = 0;
|
|
399
|
+
}
|
|
400
|
+
wr_fold_break(w, fold_col);
|
|
401
|
+
i++; /* the fold consumes the space (libyaml: MOVE without WRITE) */
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
i++;
|
|
405
|
+
run++;
|
|
406
|
+
}
|
|
407
|
+
if (run) {
|
|
408
|
+
wr_put(w, p + n - run, run);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* The single-quoted twin: '' doubling rides the same walk (a doubled
|
|
413
|
+
* apostrophe is two written bytes; the column tracking through wr_put
|
|
414
|
+
* prices it in automatically). */
|
|
415
|
+
static void emit_sq_folded(yep_writer* w, const char* p, uint32_t n, int fold_col) {
|
|
416
|
+
wr_byte(w, '\'');
|
|
417
|
+
uint32_t run = 0;
|
|
418
|
+
uint32_t i = 0;
|
|
419
|
+
while (i < n) {
|
|
420
|
+
if (p[i] == '\'') {
|
|
421
|
+
if (run) {
|
|
422
|
+
wr_put(w, p + i - run, run);
|
|
423
|
+
run = 0;
|
|
424
|
+
}
|
|
425
|
+
wr_put(w, "''", 2);
|
|
426
|
+
i++;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (p[i] == ' ' && i > 0 && i + 1 < n && w->col + (int)run > w->best_width &&
|
|
430
|
+
p[i - 1] != ' ' && p[i + 1] != ' ') {
|
|
431
|
+
if (run) {
|
|
432
|
+
wr_put(w, p + i - run, run);
|
|
433
|
+
run = 0;
|
|
434
|
+
}
|
|
435
|
+
wr_fold_break(w, fold_col);
|
|
436
|
+
i++;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
i++;
|
|
440
|
+
run++;
|
|
441
|
+
}
|
|
442
|
+
if (run) {
|
|
443
|
+
wr_put(w, p + n - run, run);
|
|
444
|
+
}
|
|
445
|
+
wr_byte(w, '\'');
|
|
446
|
+
}
|
|
447
|
+
|
|
360
448
|
/* JSON string escapes: \" \\ \b \f \n \r \t and \uXXXX for other
|
|
361
449
|
* controls; raw UTF-8 passes through (RFC 8259). */
|
|
362
|
-
|
|
450
|
+
void emit_dq_json(yep_writer* w, const char* p, uint32_t n) {
|
|
363
451
|
wr_byte(w, '"');
|
|
364
452
|
static const char* named[32] = {
|
|
365
453
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\b", "\\t", "\\n",
|
|
@@ -397,9 +485,10 @@ static void emit_dq_json(yep_writer* w, const char* p, uint32_t n) {
|
|
|
397
485
|
wr_byte(w, '"');
|
|
398
486
|
}
|
|
399
487
|
|
|
400
|
-
/* Literal block: header at the cursor, body lines at parent_col+
|
|
401
|
-
* The explicit indicator
|
|
402
|
-
* parent_col+
|
|
488
|
+
/* Literal block: header at the cursor, body lines at parent_col+indent.
|
|
489
|
+
* The explicit indicator spells the pad when it is ambiguous because
|
|
490
|
+
* the pad is defined as parent_col+indent everywhere (the contract
|
|
491
|
+
* that keeps it valid; indent=2 gives the historical '2'). */
|
|
403
492
|
static void emit_literal(yep_writer* w, const yep_dnode* n, int parent_col) {
|
|
404
493
|
yep_view vdec = wv(w, n->value);
|
|
405
494
|
const char* p = (const char*)vdec.p;
|
|
@@ -418,12 +507,14 @@ static void emit_literal(yep_writer* w, const yep_dnode* n, int parent_col) {
|
|
|
418
507
|
/* libyaml's indicator rules (#290 family 4): the explicit indent
|
|
419
508
|
* appears when the first body line starts with a space or is
|
|
420
509
|
* blank — the base indent is unambiguous otherwise (the pad is
|
|
421
|
-
* parent_col+
|
|
510
|
+
* parent_col+indent by contract; F6MC's leading-blank folded
|
|
511
|
+
* bodies). The digit spells the pad (#903: '2' at the default
|
|
512
|
+
* indent, the caller's level otherwise). */
|
|
422
513
|
if (body > 0 && (p[0] == ' ' || p[0] == '\n')) {
|
|
423
|
-
wr_byte(w, '
|
|
514
|
+
wr_byte(w, (char)('0' + w->indent));
|
|
424
515
|
}
|
|
425
516
|
wr_byte(w, '\n');
|
|
426
|
-
int pad = parent_col +
|
|
517
|
+
int pad = parent_col + w->indent;
|
|
427
518
|
wr_indent(w, pad);
|
|
428
519
|
for (uint32_t i = 0; i < body; i++) {
|
|
429
520
|
wr_byte(w, p[i]);
|
|
@@ -521,6 +612,13 @@ enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL, Y
|
|
|
521
612
|
* The block map's explicit-key promotion consults the same predicate:
|
|
522
613
|
* a key whose content demotes to DQ re-parses as a QUOTED key and must
|
|
523
614
|
* ride simple, or round two's render differs (the nightly's round 118). */
|
|
615
|
+
|
|
616
|
+
/* The flow arm below is a per-byte switch ON PURPOSE (#904 item 4's
|
|
617
|
+
* measured skip): the stopset SIMD kernel variant was measured at
|
|
618
|
+
* ~+3% median on flow-json emit (the only shape that enters the arm;
|
|
619
|
+
* 4-sample A/B, build-bench harness) — under the ledger's ~5% bar for
|
|
620
|
+
* touching the hot derive path, and within run noise under a loaded
|
|
621
|
+
* machine. PERF-LEDGER carries the numbers. */
|
|
524
622
|
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in,
|
|
525
623
|
int has_tag, int no_exotic, int plain_fact) {
|
|
526
624
|
/* the parse-time fact bits (#352 lanes 2-3): a dnode verified clean
|
|
@@ -533,7 +631,9 @@ static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key,
|
|
|
533
631
|
if (w->in_flow && len > 0) {
|
|
534
632
|
/* flow context: the indicators TERMINATE a plain scalar — a
|
|
535
633
|
* raw ']' inside "{}" re-parses as a stray close (the
|
|
536
|
-
* nightly's "555555ta]" explicit-key crasher)
|
|
634
|
+
* nightly's "555555ta]" explicit-key crasher). Per-byte switch
|
|
635
|
+
* ON PURPOSE: the stopset kernel variant measured under the
|
|
636
|
+
* skip bar (the comment above; PERF-LEDGER has the numbers). */
|
|
537
637
|
for (uint32_t k = 0; k < len; k++) {
|
|
538
638
|
switch (p[k]) {
|
|
539
639
|
case ',':
|
|
@@ -699,10 +799,21 @@ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int a
|
|
|
699
799
|
break; /* an empty plain value emits as nothing (libyaml's null
|
|
700
800
|
rendering, issue #290) */
|
|
701
801
|
case YEP_SC_PLAIN:
|
|
702
|
-
|
|
802
|
+
if (w->block_fold && !as_key && !w->in_flow && len > 0) {
|
|
803
|
+
/* #903: block-context width folding; keys never fold
|
|
804
|
+
* (libyaml: allow_breaks = !simple_key_context) and flow
|
|
805
|
+
* members keep the flow walk */
|
|
806
|
+
emit_plain_folded(w, p, len, parent_col + w->indent);
|
|
807
|
+
} else {
|
|
808
|
+
wr_put(w, p, len);
|
|
809
|
+
}
|
|
703
810
|
break;
|
|
704
811
|
case YEP_SC_SQ:
|
|
705
|
-
|
|
812
|
+
if (w->block_fold && !as_key && !w->in_flow && len > 0) {
|
|
813
|
+
emit_sq_folded(w, p, len, parent_col + w->indent);
|
|
814
|
+
} else {
|
|
815
|
+
emit_sq(w, p, len);
|
|
816
|
+
}
|
|
706
817
|
break;
|
|
707
818
|
case YEP_SC_LITERAL:
|
|
708
819
|
emit_literal(w, n, parent_col);
|
|
@@ -1172,13 +1283,16 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1172
1283
|
* key, plain unpropertied scalar value, both safety-proven by
|
|
1173
1284
|
* the exact predicates sc_route consults (no memo touch, so
|
|
1174
1285
|
* the dry/wet sc_i streams stay aligned). Anything else falls
|
|
1175
|
-
* through untouched — parity by construction.
|
|
1286
|
+
* through untouched — parity by construction. block_fold
|
|
1287
|
+
* (#903) declines the arm wholesale: the fused write's
|
|
1288
|
+
* arithmetic column model has no fold walk, and the general
|
|
1289
|
+
* body emits the identical bytes with folding. */
|
|
1176
1290
|
const yep_dnode* kn0 = yep_dom_node(d, child);
|
|
1177
1291
|
const yep_dnode* vn0 = yep_dom_node(d, kn0->next_sibling);
|
|
1178
|
-
if (!w->canonical && !w->json && !w->force_flow && vn0 != NULL &&
|
|
1179
|
-
kn0->
|
|
1180
|
-
|
|
1181
|
-
vn0->count == 0) {
|
|
1292
|
+
if (!w->canonical && !w->json && !w->force_flow && !w->block_fold && vn0 != NULL &&
|
|
1293
|
+
kn0->kind == 0 && kn0->style == 1 && kn0->anchor.len == 0 && kn0->tag.len == 0 &&
|
|
1294
|
+
kn0->count == 0 && vn0->kind == 0 && vn0->style == 1 && vn0->anchor.len == 0 &&
|
|
1295
|
+
vn0->tag.len == 0 && vn0->count == 0) {
|
|
1182
1296
|
yep_view kv = wv(w, kn0->value);
|
|
1183
1297
|
yep_view vv = wv(w, vn0->value);
|
|
1184
1298
|
uint32_t f_ind = 0, f_nl = 0, f_total = 0;
|
|
@@ -1337,11 +1451,11 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1337
1451
|
* with a complex key forcing the explicit form) */
|
|
1338
1452
|
if (!(vn->kind == 0 || vn->kind == 3 || vn->flow || vn->count == 0)) {
|
|
1339
1453
|
int seq_val = (vn->kind == 1 && !vn->flow);
|
|
1340
|
-
int val_col = seq_val ? content_col : content_col +
|
|
1454
|
+
int val_col = seq_val ? content_col : content_col + w->indent;
|
|
1341
1455
|
emit_props_tail(w, vn);
|
|
1342
1456
|
wr_byte(w, '\n');
|
|
1343
1457
|
wr_indent(w, val_col);
|
|
1344
|
-
emit_node(em, kn->next_sibling, val_col -
|
|
1458
|
+
emit_node(em, kn->next_sibling, val_col - w->indent, 0);
|
|
1345
1459
|
} else {
|
|
1346
1460
|
emit_node(em, kn->next_sibling, content_col, 0);
|
|
1347
1461
|
}
|
|
@@ -1368,15 +1482,16 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1368
1482
|
} else {
|
|
1369
1483
|
/* nested block collection: next line. libyaml parity:
|
|
1370
1484
|
* a SEQUENCE value sits at the key's column (zero
|
|
1371
|
-
* extra indent); mappings indent
|
|
1485
|
+
* extra indent); mappings indent one level (issue #95;
|
|
1486
|
+
* the level width is the #903 indent option) */
|
|
1372
1487
|
int seq_val = (vn->kind == 1 && !vn->flow);
|
|
1373
|
-
int val_col = seq_val ? content_col : content_col +
|
|
1488
|
+
int val_col = seq_val ? content_col : content_col + w->indent;
|
|
1374
1489
|
emit_props_tail(w, vn);
|
|
1375
1490
|
wr_byte(w, '\n');
|
|
1376
1491
|
wr_indent(w, val_col);
|
|
1377
|
-
/* emit_block_seq dashes at parent_col+
|
|
1378
|
-
* so the dashes land at val_col exactly */
|
|
1379
|
-
emit_node(em, kn->next_sibling, val_col -
|
|
1492
|
+
/* emit_block_seq dashes at parent_col+indent: pass
|
|
1493
|
+
* val_col-indent so the dashes land at val_col exactly */
|
|
1494
|
+
emit_node(em, kn->next_sibling, val_col - w->indent, 0);
|
|
1380
1495
|
}
|
|
1381
1496
|
}
|
|
1382
1497
|
pair++;
|
|
@@ -1419,11 +1534,19 @@ static void emit_block_seq(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1419
1534
|
wr_byte(w, '-');
|
|
1420
1535
|
emit_props_tail(w, cn);
|
|
1421
1536
|
wr_byte(w, '\n');
|
|
1422
|
-
wr_indent(w, content_col +
|
|
1537
|
+
wr_indent(w, content_col + w->indent);
|
|
1423
1538
|
emit_node(em, child, content_col, 0);
|
|
1424
1539
|
} else {
|
|
1425
1540
|
/* compact: a block collection starts right after "- " */
|
|
1426
1541
|
wr_put(w, "- ", 2);
|
|
1542
|
+
if (w->indent > 2 && cn->kind != 0 && cn->kind != 3 && !cn->flow && cn->count > 0) {
|
|
1543
|
+
/* libyaml/PyYAML's indent shape (#903): a nested
|
|
1544
|
+
* block collection's first line rides at the LEVEL
|
|
1545
|
+
* column (dash + indent), not the dash+2 token width
|
|
1546
|
+
* — indent=4 spells "- a: 1" / "- - x"; scalars,
|
|
1547
|
+
* aliases and empty/flow collections keep "- x" */
|
|
1548
|
+
wr_indent(w, w->indent - 2);
|
|
1549
|
+
}
|
|
1427
1550
|
emit_node(em, child, content_col, 0);
|
|
1428
1551
|
}
|
|
1429
1552
|
idx++;
|
|
@@ -1459,7 +1582,10 @@ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key)
|
|
|
1459
1582
|
emit_anchor_ex(em, n);
|
|
1460
1583
|
wr_put(w, "{}", 2);
|
|
1461
1584
|
} else {
|
|
1462
|
-
|
|
1585
|
+
/* the root (parent_col < 0, the caller's -2) rides column
|
|
1586
|
+
* 0; every nesting level adds the indent option (#903) */
|
|
1587
|
+
int base = (parent_col < 0) ? parent_col + 2 : parent_col + w->indent;
|
|
1588
|
+
emit_block_map(em, id, base);
|
|
1463
1589
|
}
|
|
1464
1590
|
return;
|
|
1465
1591
|
}
|
|
@@ -1469,7 +1595,8 @@ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key)
|
|
|
1469
1595
|
emit_anchor_ex(em, n);
|
|
1470
1596
|
wr_put(w, "[]", 2);
|
|
1471
1597
|
} else {
|
|
1472
|
-
|
|
1598
|
+
int base = (parent_col < 0) ? parent_col + 2 : parent_col + w->indent;
|
|
1599
|
+
emit_block_seq(em, id, base);
|
|
1473
1600
|
}
|
|
1474
1601
|
return;
|
|
1475
1602
|
}
|
|
@@ -1574,617 +1701,6 @@ static void emit_json_node(yep_emitter* em, uint32_t id) {
|
|
|
1574
1701
|
emit_canonical_scalar(w, n);
|
|
1575
1702
|
}
|
|
1576
1703
|
|
|
1577
|
-
/* ============================================================
|
|
1578
|
-
* yeptris-ruby#322: the marshal READER — Ruby Marshal.dump bytes
|
|
1579
|
-
* to JSON text in one C call (no per-scalar crossings, no object
|
|
1580
|
-
* reconstruction). The format facts mirror marshal.c's emission
|
|
1581
|
-
* contract (empirically pinned against Ruby 3.4): w_long small form
|
|
1582
|
-
* x==0 -> 0x00, |x| <= 122 -> sign*(|x|+5), size form tag +-k with
|
|
1583
|
-
* LE payload (2^(8k) - m for negatives); strings carry 'I' ivar
|
|
1584
|
-
* wrappers; arrays/hashes/strings/floats register for '@' links.
|
|
1585
|
-
*
|
|
1586
|
-
* '@' links MEMMOVE the registered object's emitted span — the first
|
|
1587
|
-
* walk's bytes are the cache; a lookup miss is a cycle (JSON cannot
|
|
1588
|
-
* express it, the binding falls back). No re-parse, no registration
|
|
1589
|
-
* on links: the object table stays exactly Ruby's stream order.
|
|
1590
|
-
*
|
|
1591
|
-
* The JSON-irrelevant constructs ('o' objects, 'S' structs, 'u'
|
|
1592
|
-
* user marshal, 'l' bignums beyond i64, non-finite floats) return
|
|
1593
|
-
* UNSUPPORTED — the binding falls back to its per-node walk.
|
|
1594
|
-
* ============================================================ */
|
|
1595
|
-
|
|
1596
|
-
typedef struct {
|
|
1597
|
-
const char* p;
|
|
1598
|
-
size_t len;
|
|
1599
|
-
size_t at;
|
|
1600
|
-
} mj_stream;
|
|
1601
|
-
|
|
1602
|
-
typedef struct {
|
|
1603
|
-
const char* p;
|
|
1604
|
-
uint32_t len;
|
|
1605
|
-
} mj_sym;
|
|
1606
|
-
|
|
1607
|
-
typedef struct {
|
|
1608
|
-
size_t pos; /* the stream position of the type byte */
|
|
1609
|
-
size_t out_off; /* where the emitted span starts in w's buffer */
|
|
1610
|
-
size_t out_len; /* the span's length */
|
|
1611
|
-
} mj_obj;
|
|
1612
|
-
|
|
1613
|
-
typedef struct {
|
|
1614
|
-
yep_writer* w;
|
|
1615
|
-
mj_stream s;
|
|
1616
|
-
mj_sym* syms;
|
|
1617
|
-
size_t syms_n, syms_cap;
|
|
1618
|
-
mj_obj* objs;
|
|
1619
|
-
size_t objs_n, objs_cap;
|
|
1620
|
-
int failed; /* 0 ok, YEPTRIS_ERROR_UNSUPPORTED */
|
|
1621
|
-
int compact;
|
|
1622
|
-
int no_reg; /* the next mj_value entry skips registration — an
|
|
1623
|
-
* 'I'-wrapped construct registers ONCE (at the 'I');
|
|
1624
|
-
* its immediate inner must not add a second entry or
|
|
1625
|
-
* every later '@' link shifts */
|
|
1626
|
-
int depth; /* mj_value recursion guard — the reader walks hostile
|
|
1627
|
-
* bytes, not Ruby's heap: nesting is unbounded in the
|
|
1628
|
-
* input, the stack is not (the parser's own
|
|
1629
|
-
* max_depth=1000 is the same law) */
|
|
1630
|
-
} mj_ctx;
|
|
1631
|
-
|
|
1632
|
-
static int mj_fail(mj_ctx* c) {
|
|
1633
|
-
c->failed = YEPTRIS_ERROR_UNSUPPORTED;
|
|
1634
|
-
return -1;
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
static int mj_byte(mj_ctx* c, unsigned char* out) {
|
|
1638
|
-
if (c->s.at >= c->s.len)
|
|
1639
|
-
return mj_fail(c);
|
|
1640
|
-
*out = (unsigned char)c->s.p[c->s.at++];
|
|
1641
|
-
return 0;
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
/* the w_long inverse — mirrors m_w_long's encode exactly:
|
|
1645
|
-
* 0x00 -> 0; 1..4 -> positive size form k=b; 5..127 -> small +(
|
|
1646
|
-
* b-5); 129..250 -> small -(251-b); 251..255 -> negative size form
|
|
1647
|
-
* k=256-b (payload = LE(2^(8k) - m)); 128 unreachable. */
|
|
1648
|
-
static int mj_long(mj_ctx* c, int64_t* out) {
|
|
1649
|
-
unsigned char b;
|
|
1650
|
-
if (mj_byte(c, &b) != 0)
|
|
1651
|
-
return -1;
|
|
1652
|
-
if (b == 0) {
|
|
1653
|
-
*out = 0;
|
|
1654
|
-
return 0;
|
|
1655
|
-
}
|
|
1656
|
-
if (b <= 4) { /* positive size form, k = b */
|
|
1657
|
-
int64_t v = 0;
|
|
1658
|
-
for (int i = 0; i < (int)b; i++) {
|
|
1659
|
-
unsigned char d;
|
|
1660
|
-
if (mj_byte(c, &d) != 0)
|
|
1661
|
-
return -1;
|
|
1662
|
-
v |= (int64_t)d << (8 * i);
|
|
1663
|
-
}
|
|
1664
|
-
*out = v;
|
|
1665
|
-
return 0;
|
|
1666
|
-
}
|
|
1667
|
-
if (b <= 127) {
|
|
1668
|
-
*out = (int64_t)b - 5;
|
|
1669
|
-
return 0;
|
|
1670
|
-
}
|
|
1671
|
-
if (b >= 129 && b <= 250) {
|
|
1672
|
-
*out = (int64_t)b - 251;
|
|
1673
|
-
return 0;
|
|
1674
|
-
}
|
|
1675
|
-
{ /* 251..255: negative size form */
|
|
1676
|
-
int bytes = 256 - (int)b;
|
|
1677
|
-
int64_t v = 0;
|
|
1678
|
-
for (int i = 0; i < bytes; i++) {
|
|
1679
|
-
unsigned char d;
|
|
1680
|
-
if (mj_byte(c, &d) != 0)
|
|
1681
|
-
return -1;
|
|
1682
|
-
v |= (int64_t)d << (8 * i);
|
|
1683
|
-
}
|
|
1684
|
-
*out = v - ((int64_t)1 << (8 * bytes));
|
|
1685
|
-
return 0;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
static int mj_ivar_pairs(mj_ctx* c);
|
|
1690
|
-
static int mj_value(mj_ctx* c);
|
|
1691
|
-
static int mj_discard_value(mj_ctx* c);
|
|
1692
|
-
|
|
1693
|
-
static int mj_string_body(mj_ctx* c, int64_t len) {
|
|
1694
|
-
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1695
|
-
return mj_fail(c);
|
|
1696
|
-
emit_dq_json(c->w, c->s.p + c->s.at, (uint32_t)len);
|
|
1697
|
-
c->s.at += (size_t)len;
|
|
1698
|
-
return 0;
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
/* a symbol: ':' + long(len) + bytes — registers in the table; the
|
|
1702
|
-
* name is emitted as a JSON string when emit is set */
|
|
1703
|
-
static int mj_symbol(mj_ctx* c, int emit) {
|
|
1704
|
-
int64_t len;
|
|
1705
|
-
if (mj_long(c, &len) != 0)
|
|
1706
|
-
return -1;
|
|
1707
|
-
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1708
|
-
return mj_fail(c);
|
|
1709
|
-
const char* name = c->s.p + c->s.at;
|
|
1710
|
-
uint32_t nlen = (uint32_t)len;
|
|
1711
|
-
c->s.at += (size_t)len;
|
|
1712
|
-
if (c->syms_n == c->syms_cap) {
|
|
1713
|
-
c->syms_cap = c->syms_cap ? c->syms_cap * 2 : 16;
|
|
1714
|
-
c->syms = (mj_sym*)realloc(c->syms, c->syms_cap * sizeof(*c->syms));
|
|
1715
|
-
if (!c->syms)
|
|
1716
|
-
return mj_fail(c);
|
|
1717
|
-
}
|
|
1718
|
-
c->syms[c->syms_n].p = name;
|
|
1719
|
-
c->syms[c->syms_n].len = nlen;
|
|
1720
|
-
c->syms_n++;
|
|
1721
|
-
if (emit)
|
|
1722
|
-
emit_dq_json(c->w, name, nlen);
|
|
1723
|
-
return 0;
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
/* the count + (symbol, value) pairs that follow an 'I' wrapper's
|
|
1727
|
-
* wrapped value. Parsed and DISCARDED — encoding/freeze ivars have
|
|
1728
|
-
* no JSON spelling; the symbol still enters the ';' table. */
|
|
1729
|
-
static int mj_ivar_pairs(mj_ctx* c) {
|
|
1730
|
-
int64_t count;
|
|
1731
|
-
if (mj_long(c, &count) != 0)
|
|
1732
|
-
return -1;
|
|
1733
|
-
for (int64_t i = 0; i < count; i++) {
|
|
1734
|
-
unsigned char nt;
|
|
1735
|
-
if (mj_byte(c, &nt) != 0)
|
|
1736
|
-
return -1;
|
|
1737
|
-
if (nt == ':') {
|
|
1738
|
-
if (mj_symbol(c, 0) != 0)
|
|
1739
|
-
return -1; /* the name: tabled, not emitted */
|
|
1740
|
-
} else if (nt == ';') {
|
|
1741
|
-
int64_t sidx;
|
|
1742
|
-
if (mj_long(c, &sidx) != 0)
|
|
1743
|
-
return -1;
|
|
1744
|
-
if (sidx < 0 || (size_t)sidx >= c->syms_n)
|
|
1745
|
-
return mj_fail(c);
|
|
1746
|
-
} else {
|
|
1747
|
-
return mj_fail(c); /* ivar names are symbols only */
|
|
1748
|
-
}
|
|
1749
|
-
if (mj_discard_value(c) != 0)
|
|
1750
|
-
return -1; /* the ivar value */
|
|
1751
|
-
}
|
|
1752
|
-
return 0;
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
/* stream-position-only walk: consumes one value, emits nothing */
|
|
1756
|
-
|
|
1757
|
-
#define MJ_DEPTH_MAX 1000
|
|
1758
|
-
|
|
1759
|
-
/* the discard twin rides the same budget (its 'I'/'['/'{' recursion is
|
|
1760
|
-
* just as hostile-byte reachable) */
|
|
1761
|
-
static int mj_discard_value_inner(mj_ctx* c);
|
|
1762
|
-
|
|
1763
|
-
static int mj_discard_value(mj_ctx* c) {
|
|
1764
|
-
if (c->depth >= MJ_DEPTH_MAX)
|
|
1765
|
-
return mj_fail(c);
|
|
1766
|
-
c->depth++;
|
|
1767
|
-
int rc = mj_discard_value_inner(c);
|
|
1768
|
-
c->depth--;
|
|
1769
|
-
return rc;
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
static int mj_discard_value_inner(mj_ctx* c) {
|
|
1773
|
-
unsigned char t;
|
|
1774
|
-
int rc = mj_byte(c, &t);
|
|
1775
|
-
if (rc != 0)
|
|
1776
|
-
return -1;
|
|
1777
|
-
switch (t) {
|
|
1778
|
-
case '0':
|
|
1779
|
-
case 'T':
|
|
1780
|
-
case 'F':
|
|
1781
|
-
return 0;
|
|
1782
|
-
case 'i':
|
|
1783
|
-
case ';':
|
|
1784
|
-
case '@': {
|
|
1785
|
-
int64_t v;
|
|
1786
|
-
return mj_long(c, &v);
|
|
1787
|
-
}
|
|
1788
|
-
case 'l': {
|
|
1789
|
-
unsigned char sign;
|
|
1790
|
-
int64_t nlimbs;
|
|
1791
|
-
if (mj_byte(c, &sign) != 0)
|
|
1792
|
-
return -1;
|
|
1793
|
-
if (sign != 0x2b && sign != 0x2d)
|
|
1794
|
-
return mj_fail(c);
|
|
1795
|
-
if (mj_long(c, &nlimbs) != 0)
|
|
1796
|
-
return -1;
|
|
1797
|
-
if (nlimbs < 0 || (uint64_t)(nlimbs * 2) > (uint64_t)(c->s.len - c->s.at))
|
|
1798
|
-
return mj_fail(c);
|
|
1799
|
-
c->s.at += (size_t)(nlimbs * 2);
|
|
1800
|
-
return 0;
|
|
1801
|
-
}
|
|
1802
|
-
case 'f':
|
|
1803
|
-
case '"': {
|
|
1804
|
-
int64_t len;
|
|
1805
|
-
if (mj_long(c, &len) != 0)
|
|
1806
|
-
return -1;
|
|
1807
|
-
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1808
|
-
return mj_fail(c);
|
|
1809
|
-
c->s.at += (size_t)len;
|
|
1810
|
-
return 0;
|
|
1811
|
-
}
|
|
1812
|
-
case 'I':
|
|
1813
|
-
if (mj_discard_value(c) != 0)
|
|
1814
|
-
return -1;
|
|
1815
|
-
return mj_ivar_pairs(c);
|
|
1816
|
-
case ':':
|
|
1817
|
-
return mj_symbol(c, 0);
|
|
1818
|
-
case '[': {
|
|
1819
|
-
int64_t count;
|
|
1820
|
-
if (mj_long(c, &count) != 0)
|
|
1821
|
-
return -1;
|
|
1822
|
-
for (int64_t i = 0; i < count; i++) {
|
|
1823
|
-
if (mj_discard_value(c) != 0)
|
|
1824
|
-
return -1;
|
|
1825
|
-
}
|
|
1826
|
-
return 0;
|
|
1827
|
-
}
|
|
1828
|
-
case '{': {
|
|
1829
|
-
int64_t count;
|
|
1830
|
-
if (mj_long(c, &count) != 0)
|
|
1831
|
-
return -1;
|
|
1832
|
-
for (int64_t i = 0; i < count; i++) {
|
|
1833
|
-
if (mj_discard_value(c) != 0)
|
|
1834
|
-
return -1; /* key */
|
|
1835
|
-
if (mj_discard_value(c) != 0)
|
|
1836
|
-
return -1; /* value */
|
|
1837
|
-
}
|
|
1838
|
-
return 0;
|
|
1839
|
-
}
|
|
1840
|
-
default:
|
|
1841
|
-
return mj_fail(c);
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
/* the memo'd span of a registered object, copied to the cursor. The
|
|
1846
|
-
* span lives earlier in the same buffer (append-only), so the copy
|
|
1847
|
-
* is memmove-safe. */
|
|
1848
|
-
static int mj_emit_memo(mj_ctx* c, const mj_obj* o) {
|
|
1849
|
-
if (getenv("MJ_DEBUG")) {
|
|
1850
|
-
char prev[13];
|
|
1851
|
-
uint32_t k = o->out_len < 12 ? (uint32_t)o->out_len : 12;
|
|
1852
|
-
memcpy(prev, c->w->p + o->out_off, k);
|
|
1853
|
-
prev[k] = '\0';
|
|
1854
|
-
fprintf(stderr, "memo off=%zu len=%zu: %s\n", o->out_off, o->out_len, prev);
|
|
1855
|
-
}
|
|
1856
|
-
if (o->out_len == 0)
|
|
1857
|
-
return mj_fail(c); /* not memoized yet: a cycle */
|
|
1858
|
-
wr_reserve(c->w, o->out_len);
|
|
1859
|
-
if (c->w->oom)
|
|
1860
|
-
return -1;
|
|
1861
|
-
memmove(c->w->p + c->w->len, c->w->p + o->out_off, o->out_len);
|
|
1862
|
-
wr_col_update(c->w, c->w->p + c->w->len, (uint32_t)o->out_len);
|
|
1863
|
-
c->w->len += o->out_len;
|
|
1864
|
-
/* the raw advance bypasses wr_put: hand back the reservation it
|
|
1865
|
-
* would have consumed, or the next write rides past cap */
|
|
1866
|
-
c->w->reserve -= o->out_len;
|
|
1867
|
-
c->w->last = c->w->p[c->w->len - 1];
|
|
1868
|
-
return 0;
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
static int mj_value_inner(mj_ctx* c);
|
|
1872
|
-
|
|
1873
|
-
/* the wrapper owns the recursion budget: hostile bytes can nest arrays
|
|
1874
|
-
* without bound, the stack cannot (the parser's max_depth=1000 law) */
|
|
1875
|
-
static int mj_value(mj_ctx* c) {
|
|
1876
|
-
if (c->depth >= MJ_DEPTH_MAX)
|
|
1877
|
-
return mj_fail(c);
|
|
1878
|
-
c->depth++;
|
|
1879
|
-
int rc = mj_value_inner(c);
|
|
1880
|
-
c->depth--;
|
|
1881
|
-
return rc;
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
static int mj_value_inner(mj_ctx* c) {
|
|
1885
|
-
unsigned char t;
|
|
1886
|
-
size_t tpos = c->s.at;
|
|
1887
|
-
int reg_idx = -1;
|
|
1888
|
-
size_t span_start = 0;
|
|
1889
|
-
int register_this = !c->no_reg;
|
|
1890
|
-
c->no_reg = 0;
|
|
1891
|
-
if (mj_byte(c, &t) != 0)
|
|
1892
|
-
return -1;
|
|
1893
|
-
/* arrays, hashes, strings, floats, bignums and anything
|
|
1894
|
-
* 'I'-wrapped register — ONCE per construct, at the outer byte
|
|
1895
|
-
* (the wrapper's no_reg suppresses the inner) (0-based stream
|
|
1896
|
-
* order). Bignums hold a slot but Ruby never emits a link to one
|
|
1897
|
-
* (the second occurrence re-emits in full); the entry only keeps
|
|
1898
|
-
* the slot count aligned for later links. Integers, symbols and
|
|
1899
|
-
* the other immediates never register. */
|
|
1900
|
-
if (register_this && (t == '[' || t == '{' || t == '"' || t == 'f' || t == 'I' || t == 'l')) {
|
|
1901
|
-
if (c->objs_n == c->objs_cap) {
|
|
1902
|
-
c->objs_cap = c->objs_cap ? c->objs_cap * 2 : 16;
|
|
1903
|
-
c->objs = (mj_obj*)realloc(c->objs, c->objs_cap * sizeof(*c->objs));
|
|
1904
|
-
if (!c->objs)
|
|
1905
|
-
return mj_fail(c);
|
|
1906
|
-
}
|
|
1907
|
-
c->objs[c->objs_n].pos = tpos;
|
|
1908
|
-
c->objs[c->objs_n].out_off = c->w->len;
|
|
1909
|
-
c->objs[c->objs_n].out_len = 0;
|
|
1910
|
-
reg_idx = (int)c->objs_n;
|
|
1911
|
-
c->objs_n++;
|
|
1912
|
-
span_start = c->w->len;
|
|
1913
|
-
}
|
|
1914
|
-
switch (t) {
|
|
1915
|
-
case '0':
|
|
1916
|
-
wr_put(c->w, "null", 4);
|
|
1917
|
-
break;
|
|
1918
|
-
case 'T':
|
|
1919
|
-
wr_put(c->w, "true", 4);
|
|
1920
|
-
break;
|
|
1921
|
-
case 'F':
|
|
1922
|
-
wr_put(c->w, "false", 5);
|
|
1923
|
-
break;
|
|
1924
|
-
case 'i': {
|
|
1925
|
-
int64_t v;
|
|
1926
|
-
if (mj_long(c, &v) != 0)
|
|
1927
|
-
return -1;
|
|
1928
|
-
char buf[24];
|
|
1929
|
-
int n = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
1930
|
-
wr_put(c->w, buf, (uint32_t)n);
|
|
1931
|
-
break;
|
|
1932
|
-
}
|
|
1933
|
-
case 'l': {
|
|
1934
|
-
/* the bignum: sign byte '+'/'-' + w_long(byte count) + LE
|
|
1935
|
-
* bytes; the i64-representable range emits, wider falls back */
|
|
1936
|
-
unsigned char sign;
|
|
1937
|
-
if (mj_byte(c, &sign) != 0)
|
|
1938
|
-
return -1;
|
|
1939
|
-
if (sign != 0x2b && sign != 0x2d)
|
|
1940
|
-
return mj_fail(c);
|
|
1941
|
-
int64_t nlimbs;
|
|
1942
|
-
if (mj_long(c, &nlimbs) != 0)
|
|
1943
|
-
return -1;
|
|
1944
|
-
if (nlimbs < 1 || nlimbs > 4)
|
|
1945
|
-
return mj_fail(c); /* beyond 64 bits: fall back */
|
|
1946
|
-
int64_t v = 0;
|
|
1947
|
-
for (int64_t i = 0; i < nlimbs; i++) {
|
|
1948
|
-
unsigned char lo, hi;
|
|
1949
|
-
if (mj_byte(c, &lo) != 0 || mj_byte(c, &hi) != 0)
|
|
1950
|
-
return -1;
|
|
1951
|
-
/* shift in the unsigned domain: the signed form put
|
|
1952
|
-
* 0xFFFF into the sign bit at limb 3 (UB, UBSan-flagged) */
|
|
1953
|
-
v |= (int64_t)((uint64_t)(lo | ((uint64_t)hi << 8)) << (16 * i));
|
|
1954
|
-
}
|
|
1955
|
-
if (sign == 0x2d)
|
|
1956
|
-
v = -v;
|
|
1957
|
-
char buf[24];
|
|
1958
|
-
int n = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
1959
|
-
wr_put(c->w, buf, (uint32_t)n);
|
|
1960
|
-
break;
|
|
1961
|
-
}
|
|
1962
|
-
case 'f': {
|
|
1963
|
-
int64_t len;
|
|
1964
|
-
if (mj_long(c, &len) != 0)
|
|
1965
|
-
return -1;
|
|
1966
|
-
if (len < 0 || (uint64_t)len > (uint64_t)(c->s.len - c->s.at))
|
|
1967
|
-
return mj_fail(c);
|
|
1968
|
-
char tmp[64];
|
|
1969
|
-
uint32_t tl = (uint32_t)len < sizeof tmp - 1 ? (uint32_t)len : (uint32_t)sizeof tmp - 1;
|
|
1970
|
-
memcpy(tmp, c->s.p + c->s.at, tl);
|
|
1971
|
-
tmp[tl] = '\0';
|
|
1972
|
-
c->s.at += (size_t)len;
|
|
1973
|
-
double d = strtod(tmp, NULL);
|
|
1974
|
-
if (!isfinite(d))
|
|
1975
|
-
return mj_fail(c); /* JSON has no inf/nan spelling — and a
|
|
1976
|
-
* strtod OVERFLOW ("1e999") produces
|
|
1977
|
-
* one from text that never spells it */
|
|
1978
|
-
char buf[40];
|
|
1979
|
-
int wl = yep_d2s_json(d, buf);
|
|
1980
|
-
wr_put(c->w, buf, (uint32_t)wl);
|
|
1981
|
-
break;
|
|
1982
|
-
}
|
|
1983
|
-
case '"': {
|
|
1984
|
-
int64_t len;
|
|
1985
|
-
if (mj_long(c, &len) != 0)
|
|
1986
|
-
return -1;
|
|
1987
|
-
int rc = mj_string_body(c, len);
|
|
1988
|
-
if (rc != 0)
|
|
1989
|
-
return -1;
|
|
1990
|
-
break;
|
|
1991
|
-
}
|
|
1992
|
-
case 'I': {
|
|
1993
|
-
/* the wrapped value IS the payload: re-dispatch into it, then
|
|
1994
|
-
* the ivar pairs ride. The inner dispatch must not register —
|
|
1995
|
-
* the construct registered at the 'I' (the double entry
|
|
1996
|
-
* shifted every later '@' link) */
|
|
1997
|
-
c->no_reg = 1;
|
|
1998
|
-
if (mj_value(c) != 0) {
|
|
1999
|
-
c->no_reg = 0;
|
|
2000
|
-
return -1;
|
|
2001
|
-
}
|
|
2002
|
-
c->no_reg = 0;
|
|
2003
|
-
if (mj_ivar_pairs(c) != 0)
|
|
2004
|
-
return -1;
|
|
2005
|
-
break;
|
|
2006
|
-
}
|
|
2007
|
-
case ':': {
|
|
2008
|
-
/* a bare symbol VALUE: emitted as its name string */
|
|
2009
|
-
if (mj_symbol(c, 1) != 0)
|
|
2010
|
-
return -1;
|
|
2011
|
-
break;
|
|
2012
|
-
}
|
|
2013
|
-
case ';': {
|
|
2014
|
-
int64_t idx;
|
|
2015
|
-
if (mj_long(c, &idx) != 0)
|
|
2016
|
-
return -1;
|
|
2017
|
-
if (idx < 0 || (size_t)idx >= c->syms_n)
|
|
2018
|
-
return mj_fail(c);
|
|
2019
|
-
emit_dq_json(c->w, c->syms[idx].p, c->syms[idx].len);
|
|
2020
|
-
break;
|
|
2021
|
-
}
|
|
2022
|
-
case '@': {
|
|
2023
|
-
/* an object link: the registered span memmoves to the cursor */
|
|
2024
|
-
int64_t idx;
|
|
2025
|
-
if (mj_long(c, &idx) != 0)
|
|
2026
|
-
return -1;
|
|
2027
|
-
if (idx < 0 || (size_t)idx >= c->objs_n)
|
|
2028
|
-
return mj_fail(c);
|
|
2029
|
-
if (mj_emit_memo(c, &c->objs[idx]) != 0)
|
|
2030
|
-
return -1;
|
|
2031
|
-
break;
|
|
2032
|
-
}
|
|
2033
|
-
case '[':
|
|
2034
|
-
case '{': {
|
|
2035
|
-
int64_t count;
|
|
2036
|
-
if (mj_long(c, &count) != 0)
|
|
2037
|
-
return -1;
|
|
2038
|
-
if (count < 0)
|
|
2039
|
-
return mj_fail(c);
|
|
2040
|
-
wr_byte(c->w, t == '[' ? '[' : '{');
|
|
2041
|
-
const char* sep = c->compact ? "," : ", ";
|
|
2042
|
-
uint32_t sep_len = c->compact ? 1u : 2u;
|
|
2043
|
-
for (int64_t i = 0; i < count; i++) {
|
|
2044
|
-
if (i > 0)
|
|
2045
|
-
wr_put(c->w, sep, sep_len);
|
|
2046
|
-
if (t == '[') {
|
|
2047
|
-
if (mj_value(c) != 0)
|
|
2048
|
-
return -1;
|
|
2049
|
-
} else {
|
|
2050
|
-
/* the key: a string (with its 'I' wrapper) or a
|
|
2051
|
-
* symbol — JSON keys are strings */
|
|
2052
|
-
unsigned char kt;
|
|
2053
|
-
size_t ktpos = c->s.at;
|
|
2054
|
-
if (mj_byte(c, &kt) != 0)
|
|
2055
|
-
return -1;
|
|
2056
|
-
if (kt == 'I') {
|
|
2057
|
-
if (mj_value(c) != 0)
|
|
2058
|
-
return -1;
|
|
2059
|
-
if (mj_ivar_pairs(c) != 0)
|
|
2060
|
-
return -1;
|
|
2061
|
-
} else if (kt == '"') {
|
|
2062
|
-
/* the key registers like any string object: its
|
|
2063
|
-
* span is memo'd for later '@' key links */
|
|
2064
|
-
int kreg = (int)c->objs_n;
|
|
2065
|
-
size_t kspan = c->w->len;
|
|
2066
|
-
if (kreg >= 0) {
|
|
2067
|
-
if (c->objs_n == c->objs_cap) {
|
|
2068
|
-
c->objs_cap = c->objs_cap ? c->objs_cap * 2 : 16;
|
|
2069
|
-
c->objs = (mj_obj*)realloc(c->objs, c->objs_cap * sizeof(*c->objs));
|
|
2070
|
-
if (!c->objs)
|
|
2071
|
-
return mj_fail(c);
|
|
2072
|
-
}
|
|
2073
|
-
c->objs[c->objs_n].pos = ktpos;
|
|
2074
|
-
c->objs[c->objs_n].out_off = c->w->len;
|
|
2075
|
-
c->objs[c->objs_n].out_len = 0;
|
|
2076
|
-
c->objs_n++;
|
|
2077
|
-
}
|
|
2078
|
-
int64_t len;
|
|
2079
|
-
if (mj_long(c, &len) != 0)
|
|
2080
|
-
return -1;
|
|
2081
|
-
int rc = mj_string_body(c, len);
|
|
2082
|
-
if (rc != 0)
|
|
2083
|
-
return -1;
|
|
2084
|
-
if (kreg >= 0) {
|
|
2085
|
-
c->objs[kreg].out_off = kspan;
|
|
2086
|
-
c->objs[kreg].out_len = c->w->len - kspan;
|
|
2087
|
-
}
|
|
2088
|
-
} else if (kt == ':') {
|
|
2089
|
-
if (mj_symbol(c, 1) != 0)
|
|
2090
|
-
return -1;
|
|
2091
|
-
} else if (kt == ';') {
|
|
2092
|
-
int64_t idx;
|
|
2093
|
-
if (mj_long(c, &idx) != 0)
|
|
2094
|
-
return -1;
|
|
2095
|
-
if (idx < 0 || (size_t)idx >= c->syms_n)
|
|
2096
|
-
return mj_fail(c);
|
|
2097
|
-
emit_dq_json(c->w, c->syms[idx].p, c->syms[idx].len);
|
|
2098
|
-
} else if (kt == '@') {
|
|
2099
|
-
int64_t idx;
|
|
2100
|
-
if (mj_long(c, &idx) != 0)
|
|
2101
|
-
return -1;
|
|
2102
|
-
if (idx < 0 || (size_t)idx >= c->objs_n)
|
|
2103
|
-
return mj_fail(c);
|
|
2104
|
-
if (mj_emit_memo(c, &c->objs[idx]) != 0)
|
|
2105
|
-
return -1;
|
|
2106
|
-
} else {
|
|
2107
|
-
return mj_fail(c);
|
|
2108
|
-
}
|
|
2109
|
-
wr_put(c->w, c->compact ? ":" : ": ", c->compact ? 1u : 2u);
|
|
2110
|
-
if (mj_value(c) != 0)
|
|
2111
|
-
return -1;
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
wr_byte(c->w, t == '[' ? ']' : '}');
|
|
2115
|
-
break;
|
|
2116
|
-
}
|
|
2117
|
-
default:
|
|
2118
|
-
return mj_fail(c);
|
|
2119
|
-
}
|
|
2120
|
-
if (reg_idx >= 0) {
|
|
2121
|
-
c->objs[reg_idx].out_off = span_start;
|
|
2122
|
-
c->objs[reg_idx].out_len = c->w->len - span_start;
|
|
2123
|
-
}
|
|
2124
|
-
return 0;
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
YEPTRIS_API YeptrisStatus yeptris_json_from_marshal(const char* m, size_t m_len, int compact,
|
|
2128
|
-
char** out, size_t* out_len) {
|
|
2129
|
-
if (out == NULL || out_len == NULL)
|
|
2130
|
-
return YEPTRIS_ERROR_ARG;
|
|
2131
|
-
*out = NULL;
|
|
2132
|
-
*out_len = 0;
|
|
2133
|
-
if (m == NULL || m_len < 2 || (unsigned char)m[0] != 0x04 || (unsigned char)m[1] != 0x08)
|
|
2134
|
-
return YEPTRIS_ERROR_UNSUPPORTED;
|
|
2135
|
-
yep_emitter em;
|
|
2136
|
-
memset(&em, 0, sizeof(em));
|
|
2137
|
-
yep_writer* w = &em.w;
|
|
2138
|
-
w->sc_dec = NULL;
|
|
2139
|
-
w->sc_i = 0;
|
|
2140
|
-
w->json = 1;
|
|
2141
|
-
w->json_compact = compact ? 1 : 0;
|
|
2142
|
-
w->json_pretty = 0;
|
|
2143
|
-
w->canonical = 0;
|
|
2144
|
-
w->explicit_doc_start = 0;
|
|
2145
|
-
w->best_width = 0;
|
|
2146
|
-
w->sv_input = NULL;
|
|
2147
|
-
w->sv_arena = NULL;
|
|
2148
|
-
w->sink = NULL;
|
|
2149
|
-
w->force_flow = 0;
|
|
2150
|
-
w->in_flow = 0;
|
|
2151
|
-
w->flow_item = 0;
|
|
2152
|
-
w->unserializable = 0;
|
|
2153
|
-
|
|
2154
|
-
mj_ctx c;
|
|
2155
|
-
memset(&c, 0, sizeof(c));
|
|
2156
|
-
c.w = w;
|
|
2157
|
-
c.s.p = m;
|
|
2158
|
-
c.s.len = m_len;
|
|
2159
|
-
c.s.at = 2; /* the 4.8 header */
|
|
2160
|
-
c.compact = compact ? 1 : 0;
|
|
2161
|
-
|
|
2162
|
-
/* one wet pass into a growable buffer (the #352 slice-2 shape) */
|
|
2163
|
-
size_t est = m_len * 2 + 64;
|
|
2164
|
-
char* buf = (char*)malloc(est);
|
|
2165
|
-
if (!buf)
|
|
2166
|
-
return YEPTRIS_ERROR_MEMORY;
|
|
2167
|
-
w->p = buf;
|
|
2168
|
-
w->cap = est;
|
|
2169
|
-
w->grow = 1;
|
|
2170
|
-
|
|
2171
|
-
mj_value(&c);
|
|
2172
|
-
|
|
2173
|
-
int failed = c.failed;
|
|
2174
|
-
size_t wrote = w->len;
|
|
2175
|
-
buf = w->p;
|
|
2176
|
-
free(c.syms);
|
|
2177
|
-
free(c.objs);
|
|
2178
|
-
if (failed || w->oom) {
|
|
2179
|
-
free(buf);
|
|
2180
|
-
return failed ? (YeptrisStatus)failed : YEPTRIS_ERROR_MEMORY;
|
|
2181
|
-
}
|
|
2182
|
-
buf[wrote] = '\0';
|
|
2183
|
-
*out = buf;
|
|
2184
|
-
*out_len = wrote;
|
|
2185
|
-
return YEPTRIS_OK;
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
1704
|
size_t yep_emit_run(yep_emitter* em, int dry) {
|
|
2189
1705
|
yep_writer* w = &em->w;
|
|
2190
1706
|
const yep_dom* d = em->doc->dom;
|