yeptris 0.6.59.1-arm-linux-eabihf → 0.6.61.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 +71 -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 +73 -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/dom/dom.c +12 -1
- data/vendor/libyeptris/src/yeptris/dom/ytape.c +56 -7
- data/vendor/libyeptris/src/yeptris/emit/emit.c +93 -13
- data/vendor/libyeptris/src/yeptris/emit/writer.c +243 -652
- data/vendor/libyeptris/src/yeptris/emit/writer.h +25 -8
- data/vendor/libyeptris/src/yeptris/events/push.c +28 -3
- data/vendor/libyeptris/src/yeptris/events/values.c +46 -0
- data/vendor/libyeptris/src/yeptris/marshal/to_json.c +650 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +611 -130
- data/vendor/libyeptris/src/yeptris/parse/engine.h +5 -0
- data/vendor/libyeptris/src/yeptris/parse/stream_iter.c +358 -0
- data/vendor/libyeptris/src/yeptris/parse.c +216 -44
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +3 -6
- 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 +38 -0
- data/vendor/libyeptris/src/yeptris/resolve/tags.c +192 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +22 -7
- data/vendor/libyeptris/src/yeptris/tape.c +24 -5
- 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]);
|
|
@@ -448,7 +539,33 @@ static void emit_literal(yep_writer* w, const yep_dnode* n, int parent_col) {
|
|
|
448
539
|
/* Canonical scalar (13B): typed words and numbers plain, everything
|
|
449
540
|
* else double-quoted; floats re-print through the shortest printer so
|
|
450
541
|
* canonical output is a fixed point (parse of canonical re-prints
|
|
451
|
-
* identically — the byte-stability gate).
|
|
542
|
+
* identically — the byte-stability gate). The DOM stores only the tag
|
|
543
|
+
* + source text, so the typed arms re-derive the VALUE from the
|
|
544
|
+
* resolver's own tables (#937): the bool table decides true/false
|
|
545
|
+
* (the old length guess spelled every non-4-char true-word false), the
|
|
546
|
+
* int decode spells JSON-unspellable forms (0x1F/0o7) in decimal, and
|
|
547
|
+
* the inf/nan words ride quoted in JSON mode (no JSON infinity). */
|
|
548
|
+
static int json_int_token(const char* p, uint32_t n) {
|
|
549
|
+
/* the strict JSON int token: -?(0|[1-9][0-9]*) — leading zeros
|
|
550
|
+
* and a '+' are YAML-spellable but not JSON (those decode) */
|
|
551
|
+
uint32_t i = 0;
|
|
552
|
+
if (i < n && p[0] == '-') {
|
|
553
|
+
i = 1;
|
|
554
|
+
}
|
|
555
|
+
if (i >= n) {
|
|
556
|
+
return 0;
|
|
557
|
+
}
|
|
558
|
+
if (p[i] == '0') {
|
|
559
|
+
return i + 1 == n;
|
|
560
|
+
}
|
|
561
|
+
for (uint32_t k = i; k < n; k++) {
|
|
562
|
+
if (p[k] < '0' || p[k] > '9') {
|
|
563
|
+
return 0;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return 1;
|
|
567
|
+
}
|
|
568
|
+
|
|
452
569
|
static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
|
|
453
570
|
yep_view vdec = wv(w, n->value);
|
|
454
571
|
const char* p = (const char*)vdec.p;
|
|
@@ -462,17 +579,24 @@ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
|
|
|
462
579
|
wr_put(w, "~", 1);
|
|
463
580
|
}
|
|
464
581
|
return;
|
|
465
|
-
case YEPTRIS_TAG_BOOL:
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
582
|
+
case YEPTRIS_TAG_BOOL: {
|
|
583
|
+
int bv = yep_tag_bool_value(p, len);
|
|
584
|
+
if (bv < 0) {
|
|
585
|
+
break; /* not a bool word: quoted (the resolver only
|
|
586
|
+
tags the table's words; defense) */
|
|
470
587
|
}
|
|
588
|
+
wr_put(w, bv ? "true" : "false", bv ? 4u : 5u);
|
|
471
589
|
return;
|
|
590
|
+
}
|
|
472
591
|
case YEPTRIS_TAG_FLOAT: {
|
|
473
592
|
const char* word = yep_tag_float_word(p, len);
|
|
474
593
|
if (word != NULL) {
|
|
475
|
-
|
|
594
|
+
uint32_t wl = (uint32_t)strlen(word);
|
|
595
|
+
if (w->json) {
|
|
596
|
+
emit_dq_json(w, word, wl);
|
|
597
|
+
} else {
|
|
598
|
+
wr_put(w, word, wl);
|
|
599
|
+
}
|
|
476
600
|
return;
|
|
477
601
|
}
|
|
478
602
|
char buf[40];
|
|
@@ -493,8 +617,23 @@ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
|
|
|
493
617
|
break; /* unparseable view: fall through to quoting */
|
|
494
618
|
}
|
|
495
619
|
case YEPTRIS_TAG_INT:
|
|
496
|
-
|
|
497
|
-
|
|
620
|
+
if (!w->json || json_int_token(p, len)) {
|
|
621
|
+
/* YAML keeps the source text (stable, lossless); JSON
|
|
622
|
+
* keeps JSON-number-shaped text (big ints stay exact) */
|
|
623
|
+
wr_put(w, p, len);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
{
|
|
627
|
+
int compat = w->resolver == yep_resolver_compat11();
|
|
628
|
+
int64_t v;
|
|
629
|
+
if (yep_tag_int64(p, len, compat, &v) == 0) {
|
|
630
|
+
char buf[24];
|
|
631
|
+
int nl = snprintf(buf, sizeof buf, "%lld", (long long)v);
|
|
632
|
+
wr_put(w, buf, (uint32_t)nl);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
break; /* undecodable int text: quoted keeps JSON valid */
|
|
498
637
|
default:
|
|
499
638
|
break;
|
|
500
639
|
}
|
|
@@ -521,6 +660,13 @@ enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL, Y
|
|
|
521
660
|
* The block map's explicit-key promotion consults the same predicate:
|
|
522
661
|
* a key whose content demotes to DQ re-parses as a QUOTED key and must
|
|
523
662
|
* ride simple, or round two's render differs (the nightly's round 118). */
|
|
663
|
+
|
|
664
|
+
/* The flow arm below is a per-byte switch ON PURPOSE (#904 item 4's
|
|
665
|
+
* measured skip): the stopset SIMD kernel variant was measured at
|
|
666
|
+
* ~+3% median on flow-json emit (the only shape that enters the arm;
|
|
667
|
+
* 4-sample A/B, build-bench harness) — under the ledger's ~5% bar for
|
|
668
|
+
* touching the hot derive path, and within run noise under a loaded
|
|
669
|
+
* machine. PERF-LEDGER carries the numbers. */
|
|
524
670
|
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in,
|
|
525
671
|
int has_tag, int no_exotic, int plain_fact) {
|
|
526
672
|
/* the parse-time fact bits (#352 lanes 2-3): a dnode verified clean
|
|
@@ -533,7 +679,9 @@ static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key,
|
|
|
533
679
|
if (w->in_flow && len > 0) {
|
|
534
680
|
/* flow context: the indicators TERMINATE a plain scalar — a
|
|
535
681
|
* raw ']' inside "{}" re-parses as a stray close (the
|
|
536
|
-
* nightly's "555555ta]" explicit-key crasher)
|
|
682
|
+
* nightly's "555555ta]" explicit-key crasher). Per-byte switch
|
|
683
|
+
* ON PURPOSE: the stopset kernel variant measured under the
|
|
684
|
+
* skip bar (the comment above; PERF-LEDGER has the numbers). */
|
|
537
685
|
for (uint32_t k = 0; k < len; k++) {
|
|
538
686
|
switch (p[k]) {
|
|
539
687
|
case ',':
|
|
@@ -699,10 +847,21 @@ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int a
|
|
|
699
847
|
break; /* an empty plain value emits as nothing (libyaml's null
|
|
700
848
|
rendering, issue #290) */
|
|
701
849
|
case YEP_SC_PLAIN:
|
|
702
|
-
|
|
850
|
+
if (w->block_fold && !as_key && !w->in_flow && len > 0) {
|
|
851
|
+
/* #903: block-context width folding; keys never fold
|
|
852
|
+
* (libyaml: allow_breaks = !simple_key_context) and flow
|
|
853
|
+
* members keep the flow walk */
|
|
854
|
+
emit_plain_folded(w, p, len, parent_col + w->indent);
|
|
855
|
+
} else {
|
|
856
|
+
wr_put(w, p, len);
|
|
857
|
+
}
|
|
703
858
|
break;
|
|
704
859
|
case YEP_SC_SQ:
|
|
705
|
-
|
|
860
|
+
if (w->block_fold && !as_key && !w->in_flow && len > 0) {
|
|
861
|
+
emit_sq_folded(w, p, len, parent_col + w->indent);
|
|
862
|
+
} else {
|
|
863
|
+
emit_sq(w, p, len);
|
|
864
|
+
}
|
|
706
865
|
break;
|
|
707
866
|
case YEP_SC_LITERAL:
|
|
708
867
|
emit_literal(w, n, parent_col);
|
|
@@ -1172,13 +1331,16 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1172
1331
|
* key, plain unpropertied scalar value, both safety-proven by
|
|
1173
1332
|
* the exact predicates sc_route consults (no memo touch, so
|
|
1174
1333
|
* the dry/wet sc_i streams stay aligned). Anything else falls
|
|
1175
|
-
* through untouched — parity by construction.
|
|
1334
|
+
* through untouched — parity by construction. block_fold
|
|
1335
|
+
* (#903) declines the arm wholesale: the fused write's
|
|
1336
|
+
* arithmetic column model has no fold walk, and the general
|
|
1337
|
+
* body emits the identical bytes with folding. */
|
|
1176
1338
|
const yep_dnode* kn0 = yep_dom_node(d, child);
|
|
1177
1339
|
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) {
|
|
1340
|
+
if (!w->canonical && !w->json && !w->force_flow && !w->block_fold && vn0 != NULL &&
|
|
1341
|
+
kn0->kind == 0 && kn0->style == 1 && kn0->anchor.len == 0 && kn0->tag.len == 0 &&
|
|
1342
|
+
kn0->count == 0 && vn0->kind == 0 && vn0->style == 1 && vn0->anchor.len == 0 &&
|
|
1343
|
+
vn0->tag.len == 0 && vn0->count == 0) {
|
|
1182
1344
|
yep_view kv = wv(w, kn0->value);
|
|
1183
1345
|
yep_view vv = wv(w, vn0->value);
|
|
1184
1346
|
uint32_t f_ind = 0, f_nl = 0, f_total = 0;
|
|
@@ -1337,11 +1499,11 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1337
1499
|
* with a complex key forcing the explicit form) */
|
|
1338
1500
|
if (!(vn->kind == 0 || vn->kind == 3 || vn->flow || vn->count == 0)) {
|
|
1339
1501
|
int seq_val = (vn->kind == 1 && !vn->flow);
|
|
1340
|
-
int val_col = seq_val ? content_col : content_col +
|
|
1502
|
+
int val_col = seq_val ? content_col : content_col + w->indent;
|
|
1341
1503
|
emit_props_tail(w, vn);
|
|
1342
1504
|
wr_byte(w, '\n');
|
|
1343
1505
|
wr_indent(w, val_col);
|
|
1344
|
-
emit_node(em, kn->next_sibling, val_col -
|
|
1506
|
+
emit_node(em, kn->next_sibling, val_col - w->indent, 0);
|
|
1345
1507
|
} else {
|
|
1346
1508
|
emit_node(em, kn->next_sibling, content_col, 0);
|
|
1347
1509
|
}
|
|
@@ -1368,15 +1530,16 @@ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1368
1530
|
} else {
|
|
1369
1531
|
/* nested block collection: next line. libyaml parity:
|
|
1370
1532
|
* a SEQUENCE value sits at the key's column (zero
|
|
1371
|
-
* extra indent); mappings indent
|
|
1533
|
+
* extra indent); mappings indent one level (issue #95;
|
|
1534
|
+
* the level width is the #903 indent option) */
|
|
1372
1535
|
int seq_val = (vn->kind == 1 && !vn->flow);
|
|
1373
|
-
int val_col = seq_val ? content_col : content_col +
|
|
1536
|
+
int val_col = seq_val ? content_col : content_col + w->indent;
|
|
1374
1537
|
emit_props_tail(w, vn);
|
|
1375
1538
|
wr_byte(w, '\n');
|
|
1376
1539
|
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 -
|
|
1540
|
+
/* emit_block_seq dashes at parent_col+indent: pass
|
|
1541
|
+
* val_col-indent so the dashes land at val_col exactly */
|
|
1542
|
+
emit_node(em, kn->next_sibling, val_col - w->indent, 0);
|
|
1380
1543
|
}
|
|
1381
1544
|
}
|
|
1382
1545
|
pair++;
|
|
@@ -1419,11 +1582,19 @@ static void emit_block_seq(yep_emitter* em, uint32_t id, int content_col) {
|
|
|
1419
1582
|
wr_byte(w, '-');
|
|
1420
1583
|
emit_props_tail(w, cn);
|
|
1421
1584
|
wr_byte(w, '\n');
|
|
1422
|
-
wr_indent(w, content_col +
|
|
1585
|
+
wr_indent(w, content_col + w->indent);
|
|
1423
1586
|
emit_node(em, child, content_col, 0);
|
|
1424
1587
|
} else {
|
|
1425
1588
|
/* compact: a block collection starts right after "- " */
|
|
1426
1589
|
wr_put(w, "- ", 2);
|
|
1590
|
+
if (w->indent > 2 && cn->kind != 0 && cn->kind != 3 && !cn->flow && cn->count > 0) {
|
|
1591
|
+
/* libyaml/PyYAML's indent shape (#903): a nested
|
|
1592
|
+
* block collection's first line rides at the LEVEL
|
|
1593
|
+
* column (dash + indent), not the dash+2 token width
|
|
1594
|
+
* — indent=4 spells "- a: 1" / "- - x"; scalars,
|
|
1595
|
+
* aliases and empty/flow collections keep "- x" */
|
|
1596
|
+
wr_indent(w, w->indent - 2);
|
|
1597
|
+
}
|
|
1427
1598
|
emit_node(em, child, content_col, 0);
|
|
1428
1599
|
}
|
|
1429
1600
|
idx++;
|
|
@@ -1459,7 +1630,10 @@ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key)
|
|
|
1459
1630
|
emit_anchor_ex(em, n);
|
|
1460
1631
|
wr_put(w, "{}", 2);
|
|
1461
1632
|
} else {
|
|
1462
|
-
|
|
1633
|
+
/* the root (parent_col < 0, the caller's -2) rides column
|
|
1634
|
+
* 0; every nesting level adds the indent option (#903) */
|
|
1635
|
+
int base = (parent_col < 0) ? parent_col + 2 : parent_col + w->indent;
|
|
1636
|
+
emit_block_map(em, id, base);
|
|
1463
1637
|
}
|
|
1464
1638
|
return;
|
|
1465
1639
|
}
|
|
@@ -1469,12 +1643,31 @@ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key)
|
|
|
1469
1643
|
emit_anchor_ex(em, n);
|
|
1470
1644
|
wr_put(w, "[]", 2);
|
|
1471
1645
|
} else {
|
|
1472
|
-
|
|
1646
|
+
int base = (parent_col < 0) ? parent_col + 2 : parent_col + w->indent;
|
|
1647
|
+
emit_block_seq(em, id, base);
|
|
1473
1648
|
}
|
|
1474
1649
|
return;
|
|
1475
1650
|
}
|
|
1476
1651
|
emit_tag(w, n);
|
|
1477
1652
|
emit_anchor_ex(em, n);
|
|
1653
|
+
if (parent_col < 0 && !w->canonical && !w->json && n->kind == 0 && n->style <= 1) {
|
|
1654
|
+
/* A DOCUMENT-ROOT empty scalar rides the quoted form: libyaml
|
|
1655
|
+
* renders "--- ''" and never a bare marker with nothing after
|
|
1656
|
+
* it. A plain-empty root emitted as NOTHING (the value law of
|
|
1657
|
+
* issue #290, "k:" — kept below for non-root contexts) sheds
|
|
1658
|
+
* the root node entirely; the re-parse of that stream produced
|
|
1659
|
+
* an ANY-styled empty root whose render is "" — and the bytes
|
|
1660
|
+
* diverged on round two (the nightly's r201 fuzz_roundtrip
|
|
1661
|
+
* byte-instability catch, ">\n! \n !<aaa: bbb\n...\n<NEL><NEL>",
|
|
1662
|
+
* whose second document's break-only content parses to a
|
|
1663
|
+
* plain-empty root). Quoting the root's emptiness makes every
|
|
1664
|
+
* empty-root spelling converge on the same fixpoint bytes. */
|
|
1665
|
+
yep_view vdec = wv(w, n->value);
|
|
1666
|
+
if (vdec.len == 0) {
|
|
1667
|
+
emit_dq(w, (const char*)vdec.p, vdec.len);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1478
1671
|
if (parent_col < 0) {
|
|
1479
1672
|
parent_col = 0; /* root scalar: block body at column 2 */
|
|
1480
1673
|
}
|
|
@@ -1574,617 +1767,6 @@ static void emit_json_node(yep_emitter* em, uint32_t id) {
|
|
|
1574
1767
|
emit_canonical_scalar(w, n);
|
|
1575
1768
|
}
|
|
1576
1769
|
|
|
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
1770
|
size_t yep_emit_run(yep_emitter* em, int dry) {
|
|
2189
1771
|
yep_writer* w = &em->w;
|
|
2190
1772
|
const yep_dom* d = em->doc->dom;
|
|
@@ -2239,7 +1821,16 @@ size_t yep_emit_run(yep_emitter* em, int dry) {
|
|
|
2239
1821
|
yep_view tdec3 = wv(w, root->tag);
|
|
2240
1822
|
wr_tag_token(w, &tdec3);
|
|
2241
1823
|
}
|
|
2242
|
-
if (root->anchor.len > 0) {
|
|
1824
|
+
if (root->anchor.len > 0 && !anchor_name_unspellable(w, root)) {
|
|
1825
|
+
/* the r235 law: the lead line writes the name UNQUOTED
|
|
1826
|
+
* and anchor names have no quoting -- a name the spell
|
|
1827
|
+
* law rejects has no safe lead-line spelling. The r235
|
|
1828
|
+
* nightly's riding-colon names ("&:" and the artifact's
|
|
1829
|
+
* "&k:\"" — run 42739927952, crash-5de6f9ff) re-parsed
|
|
1830
|
+
* as an anchored empty KEY of the follow-on collection
|
|
1831
|
+
* (the anchor lost, s1 != s2, byte growth); the
|
|
1832
|
+
* colon-tail law fails the serialize like every other
|
|
1833
|
+
* anchor write path (the r228 "&: |" law) */
|
|
2243
1834
|
if (root->tag.len > 0) {
|
|
2244
1835
|
wr_byte(w, ' '); /* props need the separator */
|
|
2245
1836
|
}
|