yeptris 0.6.18.2-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 +7 -0
- data/README.adoc +141 -0
- data/ext/build_windows_native.rb +41 -0
- data/ext/libyeptris/extconf.rb +57 -0
- data/ext/yeptris_native/cbor_ruby.c +193 -0
- data/ext/yeptris_native/extconf.rb +47 -0
- data/ext/yeptris_native/json_ruby.c +354 -0
- data/ext/yeptris_native/yeptris_native.c +497 -0
- data/lib/yeptris/cbor.rb +130 -0
- data/lib/yeptris/document.rb +255 -0
- data/lib/yeptris/ffi.rb +426 -0
- data/lib/yeptris/json/descriptor.rb +266 -0
- data/lib/yeptris/json.rb +394 -0
- data/lib/yeptris/materializer.rb +354 -0
- data/lib/yeptris/native-3.4.so +0 -0
- data/lib/yeptris/node.rb +453 -0
- data/lib/yeptris/psych/class_loader.rb +24 -0
- data/lib/yeptris/psych/coder_shim.rb +55 -0
- data/lib/yeptris/psych/drop_in.rb +63 -0
- data/lib/yeptris/psych/encodable.rb +14 -0
- data/lib/yeptris/psych/handler.rb +90 -0
- data/lib/yeptris/psych/parser.rb +105 -0
- data/lib/yeptris/psych/scalar_scanner.rb +149 -0
- data/lib/yeptris/psych/visitors.rb +545 -0
- data/lib/yeptris/psych.rb +622 -0
- data/lib/yeptris/schema.rb +253 -0
- data/lib/yeptris/valueml.rb +455 -0
- data/lib/yeptris/yaml/descriptor.rb +56 -0
- data/lib/yeptris/yaml.rb +422 -0
- data/lib/yeptris.rb +113 -0
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +254 -0
- data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
- data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
- data/vendor/libyeptris/src/CMakeLists.txt +167 -0
- data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
- data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
- data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
- data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
- data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
- data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
- data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
- data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
- data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
- data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
- data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
- data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
- data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
- data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
- data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
- data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
- data/vendor/libyeptris/src/include/yeptris.h +23 -0
- data/vendor/libyeptris/src/yeptris/build.c +376 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
- data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
- data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
- data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
- data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
- data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
- data/vendor/libyeptris/src/yeptris/doc.h +56 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
- data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
- data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
- data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
- data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
- data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
- data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
- data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
- data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
- data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
- data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
- data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
- data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
- data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
- data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
- data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
- data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
- data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
- data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
- data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
- data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
- data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
- data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
- data/vendor/libyeptris/src/yeptris/parse.c +737 -0
- data/vendor/libyeptris/src/yeptris/plan.c +824 -0
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
- data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
- data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
- data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
- data/vendor/libyeptris/src/yeptris/schema.c +308 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
- data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
- data/vendor/libyeptris/src/yeptris/version.c +7 -0
- data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
- data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
- data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
- metadata +199 -0
|
@@ -0,0 +1,1192 @@
|
|
|
1
|
+
/* dom.c — event-built DOM (builder sink + storage). */
|
|
2
|
+
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
|
|
6
|
+
#include "common/simd_text.h"
|
|
7
|
+
|
|
8
|
+
#include "common/mutex.h"
|
|
9
|
+
|
|
10
|
+
#include "dom.h"
|
|
11
|
+
|
|
12
|
+
#include <yeptris/resolve.h> /* the tag-id SSOT (the strict literal arm) */
|
|
13
|
+
|
|
14
|
+
/* The bounded-parse caps: each node consumes at least one input byte
|
|
15
|
+
* (the grammar guarantees it), so len + 1024 is airtight for valid
|
|
16
|
+
* documents; docs and anchors are sparser still. The arena copies
|
|
17
|
+
* content the input already paid for (folds, tags, anchors) — generous
|
|
18
|
+
* 128x covers pathological tag expansion while still bounding any
|
|
19
|
+
* loop bug linearly in the input. */
|
|
20
|
+
static int cap_nodes(const yep_dom* d) {
|
|
21
|
+
return d->input_len == 0 || (size_t)d->ncount <= d->input_len + 1024;
|
|
22
|
+
}
|
|
23
|
+
static int cap_arena(const yep_dom* d, uint32_t need) {
|
|
24
|
+
return d->input_len == 0 || (size_t)(d->str_len + need) <= d->input_len * 128 + (1u << 20);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
int dom_grow_nodes(yep_dom* d, uint32_t need) {
|
|
28
|
+
if (d->ncount + need <= d->ncap) {
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
if (!cap_nodes(d)) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
uint32_t ncap = d->ncap ? d->ncap * 2 : 64;
|
|
35
|
+
while (ncap < d->ncount + need) {
|
|
36
|
+
ncap *= 2;
|
|
37
|
+
}
|
|
38
|
+
yep_dnode* nd = yep_pool_alloc(d->pool, (size_t)ncap * sizeof(yep_dnode), 16);
|
|
39
|
+
if (nd == NULL) {
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
if (d->ncount > 0) {
|
|
43
|
+
memcpy(nd, d->nodes, (size_t)d->ncount * sizeof(yep_dnode));
|
|
44
|
+
}
|
|
45
|
+
d->nodes = nd;
|
|
46
|
+
d->ncap = ncap;
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
int dom_grow_docs(yep_dom* d, uint32_t need) {
|
|
51
|
+
if (d->dcount + need <= d->dcap) {
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
if (d->input_len != 0 && (size_t)d->dcount > d->input_len / 2 + 16) {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
uint32_t ncap = d->dcap ? d->dcap * 2 : 16;
|
|
58
|
+
while (ncap < d->dcount + need) {
|
|
59
|
+
ncap *= 2;
|
|
60
|
+
}
|
|
61
|
+
uint32_t* nd = yep_pool_alloc(d->pool, (size_t)ncap * sizeof(uint32_t), 16);
|
|
62
|
+
if (nd == NULL) {
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
if (d->dcount > 0) {
|
|
66
|
+
memcpy(nd, d->docs, (size_t)d->dcount * sizeof(uint32_t));
|
|
67
|
+
}
|
|
68
|
+
d->docs = nd;
|
|
69
|
+
d->dcap = ncap;
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Doubles the arena until cap bytes fit; 0 on OOM. Offsets stored in
|
|
74
|
+
* nodes stay valid across the move. */
|
|
75
|
+
static int str_grow(yep_dom* d, uint32_t need) {
|
|
76
|
+
if (d->str_len + need <= d->str_cap) {
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
if (!cap_arena(d, need)) {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
uint32_t cap = d->str_cap ? d->str_cap : 256;
|
|
83
|
+
while (cap < d->str_len + need) {
|
|
84
|
+
cap *= 2;
|
|
85
|
+
}
|
|
86
|
+
char* ns = yep_alloc(d->sys, cap);
|
|
87
|
+
if (ns == NULL) {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
if (d->str_len > 0) {
|
|
91
|
+
memcpy(ns, d->str, d->str_len);
|
|
92
|
+
}
|
|
93
|
+
yep_free(d->sys, d->str);
|
|
94
|
+
d->str = ns;
|
|
95
|
+
d->str_cap = cap;
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
yep_sview yep_dom_str_put(yep_dom* d, const char* p, uint32_t len) {
|
|
100
|
+
yep_sview sv = {YEP_SV_INPUT, 0}; /* empty: arena offset 0 */
|
|
101
|
+
if (len == 0 || !str_grow(d, len)) {
|
|
102
|
+
return sv; /* len 0 or OOM: empty view */
|
|
103
|
+
}
|
|
104
|
+
memcpy(d->str + d->str_len, p, len);
|
|
105
|
+
sv.off = YEP_SV_INPUT | d->str_len;
|
|
106
|
+
sv.len = len;
|
|
107
|
+
d->str_len += len;
|
|
108
|
+
return sv;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
char* yep_dom_str_tail(yep_dom* d, uint32_t cap) {
|
|
112
|
+
if (!str_grow(d, cap)) {
|
|
113
|
+
return NULL;
|
|
114
|
+
}
|
|
115
|
+
return d->str + d->str_len;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
yep_sview yep_dom_str_commit(yep_dom* d, uint32_t written) {
|
|
119
|
+
yep_sview sv = {YEP_SV_INPUT | d->str_len, written};
|
|
120
|
+
d->str_len += written;
|
|
121
|
+
return sv;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Node string: borrowed views stay INPUT offsets (zero copy);
|
|
125
|
+
* everything the engine produced (folded, escaped, resolved tags,
|
|
126
|
+
* anchor names) is copied into the arena — the finish pool is then
|
|
127
|
+
* free to die with the engine. */
|
|
128
|
+
static yep_sview dom_str_in(yep_dom* d, const yep_view* v, int borrowed) {
|
|
129
|
+
if (v == NULL || v->len == 0) {
|
|
130
|
+
yep_sview sv = {0, 0};
|
|
131
|
+
return sv;
|
|
132
|
+
}
|
|
133
|
+
if (borrowed && v->p != NULL && d->input_base != NULL && (const char*)v->p >= d->input_base) {
|
|
134
|
+
return yep_sv_input(d, *v);
|
|
135
|
+
}
|
|
136
|
+
return yep_dom_str_put(d, (const char*)v->p, v->len);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* The node-init law (every builder's SSOT): creates one node with the
|
|
140
|
+
* given metadata; tag/anchor views encode through dom_str_in (tags and
|
|
141
|
+
* event-path anchors copy to the arena; input-backed views borrow).
|
|
142
|
+
* tag_id stays 0 — the caller resolves (scalars) or leaves 0. */
|
|
143
|
+
uint32_t dom_open_node(yep_dom* d, uint8_t kind, const yep_view* tag, const yep_view* anchor,
|
|
144
|
+
int anchor_borrowed, uint8_t style, uint8_t implicit, uint8_t flow) {
|
|
145
|
+
if (!dom_grow_nodes(d, 1)) {
|
|
146
|
+
return UINT32_MAX;
|
|
147
|
+
}
|
|
148
|
+
yep_dnode* n = &d->nodes[d->ncount];
|
|
149
|
+
/* template copy: one 48 B move replaces memset + four sentinel
|
|
150
|
+
* patches (TODO.restructure/73) — the hot per-node init */
|
|
151
|
+
static const yep_dnode k_init = {
|
|
152
|
+
.first_child = UINT32_MAX,
|
|
153
|
+
.last_child = UINT32_MAX,
|
|
154
|
+
.next_sibling = UINT32_MAX,
|
|
155
|
+
.target = UINT32_MAX,
|
|
156
|
+
};
|
|
157
|
+
*n = k_init;
|
|
158
|
+
n->kind = kind;
|
|
159
|
+
n->tag = dom_str_in(d, tag, 0);
|
|
160
|
+
n->anchor = dom_str_in(d, anchor, anchor_borrowed);
|
|
161
|
+
n->style = style;
|
|
162
|
+
n->implicit = implicit;
|
|
163
|
+
n->flow = flow;
|
|
164
|
+
return d->ncount++;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
uint32_t dom_new_node(yep_dom* d, const yep_event* ev, uint8_t kind) {
|
|
168
|
+
if (ev == NULL) {
|
|
169
|
+
return dom_open_node(d, kind, NULL, NULL, 0, 0, 0, 0);
|
|
170
|
+
}
|
|
171
|
+
/* event-path anchors ride the VALUE's borrowedness (historical
|
|
172
|
+
* law, kept byte-identical) */
|
|
173
|
+
uint32_t id = dom_open_node(d, kind, &ev->tag, &ev->anchor, ev->borrowed, ev->style,
|
|
174
|
+
ev->implicit, ev->flow);
|
|
175
|
+
if (id == UINT32_MAX) {
|
|
176
|
+
return UINT32_MAX;
|
|
177
|
+
}
|
|
178
|
+
d->nodes[id].tag_id = ev->tag_id;
|
|
179
|
+
return id;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static int dom_anchor_set(yep_dom* d, uint32_t ordinal, uint32_t node) {
|
|
183
|
+
if (ordinal == 0) {
|
|
184
|
+
return -1;
|
|
185
|
+
}
|
|
186
|
+
if (d->input_len != 0 && ordinal > d->input_len + 16) {
|
|
187
|
+
return -1; /* more anchors than input bytes: a bug, not a doc */
|
|
188
|
+
}
|
|
189
|
+
if (ordinal > d->anchor_nodes_cap) {
|
|
190
|
+
/* First allocation sizes from the input (TODO.restructure/71):
|
|
191
|
+
* growth from a bare 64 re-copied the table ~18 times on
|
|
192
|
+
* anchor-heavy (one entry per '&x…' line). len/32 overshoots
|
|
193
|
+
* only pathological one-anchor-per-2-bytes documents, which
|
|
194
|
+
* then double as before. */
|
|
195
|
+
uint32_t cap = d->anchor_nodes_cap ? d->anchor_nodes_cap * 2 : 64;
|
|
196
|
+
if (d->anchor_nodes_cap == 0 && d->input_len > 2048) {
|
|
197
|
+
uint32_t sized = (uint32_t)(d->input_len / 32) + 64;
|
|
198
|
+
if (sized > cap) {
|
|
199
|
+
cap = sized;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
while (cap < ordinal) {
|
|
203
|
+
cap *= 2;
|
|
204
|
+
}
|
|
205
|
+
uint32_t* na = yep_alloc(d->sys, cap * sizeof(*na));
|
|
206
|
+
if (na == NULL) {
|
|
207
|
+
return -1;
|
|
208
|
+
}
|
|
209
|
+
if (d->anchor_nodes != NULL) {
|
|
210
|
+
memcpy(na, d->anchor_nodes, d->anchor_nodes_cap * sizeof(*na));
|
|
211
|
+
yep_free(d->sys, d->anchor_nodes);
|
|
212
|
+
yep_free(d->sys, d->mut_att);
|
|
213
|
+
yep_free(d->sys, d->mut_depth);
|
|
214
|
+
}
|
|
215
|
+
d->anchor_nodes = na;
|
|
216
|
+
d->anchor_nodes_cap = cap;
|
|
217
|
+
}
|
|
218
|
+
d->anchor_nodes[ordinal - 1] = node;
|
|
219
|
+
if (ordinal > d->anchor_max) {
|
|
220
|
+
d->anchor_max = ordinal;
|
|
221
|
+
}
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static uint32_t dom_anchor_get(const yep_dom* d, uint32_t ordinal) {
|
|
226
|
+
if (ordinal == 0 || ordinal > d->anchor_max) {
|
|
227
|
+
return UINT32_MAX;
|
|
228
|
+
}
|
|
229
|
+
return d->anchor_nodes[ordinal - 1];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* The one place links form (builder and mutation both): records
|
|
233
|
+
* attachment and depth so mutation can reject double-parents and cap
|
|
234
|
+
* nesting without parent pointers. */
|
|
235
|
+
void dom_link(yep_dom* d, uint32_t parent, uint32_t child) {
|
|
236
|
+
yep_dnode* p = &d->nodes[parent];
|
|
237
|
+
if (p->count == 0) {
|
|
238
|
+
p->first_child = child;
|
|
239
|
+
} else {
|
|
240
|
+
d->nodes[p->last_child].next_sibling = child;
|
|
241
|
+
}
|
|
242
|
+
p->last_child = child;
|
|
243
|
+
p->count++;
|
|
244
|
+
/* every link changes SOME container's child list (#377's cache) */
|
|
245
|
+
d->child_cache_id = UINT32_MAX;
|
|
246
|
+
d->child_cache_len = 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* ---- mutation side tables (64-2a) ---- */
|
|
250
|
+
|
|
251
|
+
static int dom_mut_grow(yep_dom* d, uint32_t need) {
|
|
252
|
+
if (need <= d->mut_tbl_cap) {
|
|
253
|
+
return 1;
|
|
254
|
+
}
|
|
255
|
+
uint32_t cap = d->mut_tbl_cap ? d->mut_tbl_cap : 64;
|
|
256
|
+
while (cap < need) {
|
|
257
|
+
cap *= 2;
|
|
258
|
+
}
|
|
259
|
+
uint8_t* na = yep_alloc(d->sys, (size_t)cap / 8 + 1);
|
|
260
|
+
uint16_t* nd = yep_alloc(d->sys, (size_t)cap * sizeof(uint16_t));
|
|
261
|
+
if (na == NULL || nd == NULL) {
|
|
262
|
+
yep_free(d->sys, na);
|
|
263
|
+
yep_free(d->sys, nd);
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
size_t old_bytes = ((size_t)d->mut_tbl_cap + 7) / 8;
|
|
267
|
+
if (d->mut_att != NULL) {
|
|
268
|
+
memcpy(na, d->mut_att, old_bytes);
|
|
269
|
+
memcpy(nd, d->mut_depth, (size_t)d->mut_tbl_cap * sizeof(uint16_t));
|
|
270
|
+
}
|
|
271
|
+
/* the grown region is FRESH facts: unattached, depth 0 (a CI
|
|
272
|
+
* failure traced to garbage bits here — local mallocs were zero) */
|
|
273
|
+
memset(na + old_bytes, 0, (size_t)cap / 8 + 1 - old_bytes);
|
|
274
|
+
memset(nd + d->mut_tbl_cap, 0, (size_t)(cap - d->mut_tbl_cap) * sizeof(uint16_t));
|
|
275
|
+
yep_free(d->sys, d->mut_att);
|
|
276
|
+
yep_free(d->sys, d->mut_depth);
|
|
277
|
+
d->mut_att = na;
|
|
278
|
+
d->mut_depth = nd;
|
|
279
|
+
d->mut_tbl_cap = cap;
|
|
280
|
+
return 1;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
int dom_mut_att(const yep_dom* d, uint32_t id) {
|
|
284
|
+
yep_dom* m = (yep_dom*)d; /* lazy growth is benign bookkeeping */
|
|
285
|
+
if (id >= m->mut_tbl_cap && !dom_mut_grow(m, id + 1)) {
|
|
286
|
+
return 0;
|
|
287
|
+
}
|
|
288
|
+
return (m->mut_att[id >> 3] >> (id & 7)) & 1;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
void dom_mut_set_att(yep_dom* d, uint32_t id, int v) {
|
|
292
|
+
if (id >= d->mut_tbl_cap && !dom_mut_grow(d, id + 1)) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
uint8_t bit = (uint8_t)(1u << (id & 7));
|
|
296
|
+
if (v) {
|
|
297
|
+
d->mut_att[id >> 3] |= bit;
|
|
298
|
+
} else {
|
|
299
|
+
d->mut_att[id >> 3] &= (uint8_t)~bit;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
uint16_t dom_mut_depth(const yep_dom* d, uint32_t id) {
|
|
304
|
+
yep_dom* m = (yep_dom*)d;
|
|
305
|
+
if (id >= m->mut_tbl_cap && !dom_mut_grow(m, id + 1)) {
|
|
306
|
+
return 0;
|
|
307
|
+
}
|
|
308
|
+
return m->mut_depth[id];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
void dom_mut_set_depth(yep_dom* d, uint32_t id, uint16_t depth) {
|
|
312
|
+
if (id >= d->mut_tbl_cap && !dom_mut_grow(d, id + 1)) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
d->mut_depth[id] = depth;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Places a completed node: value for a pending key, child of the top
|
|
319
|
+
* collection, or document root. */
|
|
320
|
+
int dom_place(yep_dom* d, uint32_t id) {
|
|
321
|
+
if (d->depth == 0) {
|
|
322
|
+
if (!dom_grow_docs(d, 1)) {
|
|
323
|
+
return -1;
|
|
324
|
+
}
|
|
325
|
+
d->docs[d->dcount++] = id;
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
uint32_t top = d->stack[d->depth - 1];
|
|
329
|
+
yep_dnode* p = &d->nodes[top];
|
|
330
|
+
if (p->kind == YEP_DOM_MAPPING) {
|
|
331
|
+
/* per-frame pairing: a map nested as a KEY consumes this frame's
|
|
332
|
+
* slot without disturbing the parent's (global state corrupted
|
|
333
|
+
* complex keys) */
|
|
334
|
+
if (!d->map_pending_key[d->depth - 1]) {
|
|
335
|
+
d->pending_key_id[d->depth - 1] = id;
|
|
336
|
+
d->map_pending_key[d->depth - 1] = 1;
|
|
337
|
+
return 0;
|
|
338
|
+
}
|
|
339
|
+
d->map_pending_key[d->depth - 1] = 0;
|
|
340
|
+
dom_link(d, top, d->pending_key_id[d->depth - 1]); /* key */
|
|
341
|
+
dom_link(d, top, id); /* value */
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
dom_link(d, top, id);
|
|
345
|
+
return 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
int yep_dom_on_event(void* ctx, const yep_event* ev) {
|
|
349
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
350
|
+
switch (ev->type) {
|
|
351
|
+
case YEP_EV_STREAM_START:
|
|
352
|
+
case YEP_EV_DOCUMENT_START:
|
|
353
|
+
case YEP_EV_DOCUMENT_END:
|
|
354
|
+
/* bindings are document-scoped, like the engine's names */
|
|
355
|
+
if (d->anchor_max != 0) {
|
|
356
|
+
memset(d->anchor_nodes, 0, d->anchor_max * sizeof(*d->anchor_nodes));
|
|
357
|
+
d->anchor_max = 0;
|
|
358
|
+
}
|
|
359
|
+
return 0;
|
|
360
|
+
case YEP_EV_STREAM_END:
|
|
361
|
+
/* no depth walk: the parse links TOP-DOWN (every child is
|
|
362
|
+
* linked the moment it starts, under a parent whose depth is
|
|
363
|
+
* final), so dom_link's O(1) assignment already globalizes
|
|
364
|
+
* them. The walk remains the MUTATION builder's job — it
|
|
365
|
+
* attaches pre-built subtrees whose descendants keep local
|
|
366
|
+
* depths (yep_mut_set_depths after attach). */
|
|
367
|
+
return 0;
|
|
368
|
+
|
|
369
|
+
case YEP_EV_SEQ_START:
|
|
370
|
+
case YEP_EV_MAP_START: {
|
|
371
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
372
|
+
return -1;
|
|
373
|
+
}
|
|
374
|
+
uint8_t kind = (ev->type == YEP_EV_SEQ_START) ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING;
|
|
375
|
+
uint32_t id = dom_new_node(d, ev, kind);
|
|
376
|
+
if (id == UINT32_MAX) {
|
|
377
|
+
return -1;
|
|
378
|
+
}
|
|
379
|
+
if (ev->anchor_id != 0 && dom_anchor_set(d, ev->anchor_id, id) != 0) {
|
|
380
|
+
return -1;
|
|
381
|
+
}
|
|
382
|
+
if (dom_place(d, id) != 0) {
|
|
383
|
+
return -1;
|
|
384
|
+
}
|
|
385
|
+
d->map_pending_key[d->depth] = 0;
|
|
386
|
+
d->stack[d->depth++] = id;
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
case YEP_EV_SEQ_END:
|
|
391
|
+
case YEP_EV_MAP_END:
|
|
392
|
+
if (d->depth == 0) {
|
|
393
|
+
return -1;
|
|
394
|
+
}
|
|
395
|
+
d->depth--;
|
|
396
|
+
return 0;
|
|
397
|
+
|
|
398
|
+
case YEP_EV_SCALAR: {
|
|
399
|
+
uint32_t id = dom_new_node(d, ev, YEP_DOM_SCALAR);
|
|
400
|
+
if (id == UINT32_MAX) {
|
|
401
|
+
return -1;
|
|
402
|
+
}
|
|
403
|
+
d->nodes[id].value = dom_str_in(d, &ev->value, ev->borrowed);
|
|
404
|
+
if (ev->anchor_id != 0 && dom_anchor_set(d, ev->anchor_id, id) != 0) {
|
|
405
|
+
return -1;
|
|
406
|
+
}
|
|
407
|
+
return dom_place(d, id);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
case YEP_EV_ALIAS: {
|
|
411
|
+
uint32_t target = dom_anchor_get(d, ev->anchor_id);
|
|
412
|
+
if (target == UINT32_MAX) {
|
|
413
|
+
return -1;
|
|
414
|
+
}
|
|
415
|
+
uint32_t id = dom_new_node(d, ev, YEP_DOM_ALIAS);
|
|
416
|
+
if (id == UINT32_MAX) {
|
|
417
|
+
return -1;
|
|
418
|
+
}
|
|
419
|
+
d->nodes[id].value = dom_str_in(d, &ev->value, ev->borrowed);
|
|
420
|
+
d->nodes[id].target = target;
|
|
421
|
+
return dom_place(d, id);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
default:
|
|
425
|
+
return -1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
void yep_dom_reserve(yep_dom* d, uint32_t node_hint, uint32_t str_hint) {
|
|
430
|
+
if (d == NULL) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (node_hint > d->ncount) {
|
|
434
|
+
/* capacity for node_hint TOTAL nodes; growth remains on OOM */
|
|
435
|
+
(void)dom_grow_nodes(d, node_hint - d->ncount);
|
|
436
|
+
}
|
|
437
|
+
if (str_hint > d->str_cap) {
|
|
438
|
+
(void)str_grow(d, str_hint);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* Content-derived sizing (TODO.impl/06): three SIMD count passes,
|
|
443
|
+
* then reserves. Every structural byte bounds a node boundary; the
|
|
444
|
+
* arena reserve only pays when the content copies (quoted scalars
|
|
445
|
+
* escape, block scalars fold) — borrowed-only documents must not
|
|
446
|
+
* allocate an arena at all. Hints are advisory. */
|
|
447
|
+
void yep_dom_prepare(yep_dom* d, const yep_text_stats* st) {
|
|
448
|
+
if (d == NULL || st == NULL) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
/* a block line carries at least TWO nodes (a key and its value,
|
|
452
|
+
* or a dash and its entry): nl-colons cancels to ~zero on plain
|
|
453
|
+
* scalar maps, so the old hint undershot by 200k nodes on
|
|
454
|
+
* anchor-heavy (the growth memmove profiled at 5% of parse) */
|
|
455
|
+
size_t structural = st->comma + st->dash + st->bracket + st->brace + 8;
|
|
456
|
+
size_t by_line = st->nl * 2 + 8;
|
|
457
|
+
uint32_t node_hint = (uint32_t)(structural > by_line ? structural : by_line);
|
|
458
|
+
uint32_t str_hint = (st->dq + st->sq + st->pipe) > 0
|
|
459
|
+
? (uint32_t)(st->dq * 16 + st->sq * 8 + st->pipe * 128 + 64)
|
|
460
|
+
: 0;
|
|
461
|
+
yep_dom_reserve(d, node_hint, str_hint);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
void yep_dom_prepare_len(yep_dom* d, size_t len) {
|
|
465
|
+
if (d == NULL) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
yep_dom_reserve(d, (uint32_t)(len / 2 + 64), (uint32_t)(len / 16 + 256));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
yep_dom* yep_dom_create(const yep_allocator* sys) {
|
|
472
|
+
if (sys == NULL) {
|
|
473
|
+
return NULL;
|
|
474
|
+
}
|
|
475
|
+
yep_pool* pool = yep_pool_create(sys, 8192);
|
|
476
|
+
if (pool == NULL) {
|
|
477
|
+
return NULL;
|
|
478
|
+
}
|
|
479
|
+
yep_dom* d = yep_alloc(sys, sizeof(yep_dom));
|
|
480
|
+
if (d == NULL) {
|
|
481
|
+
yep_pool_destroy(pool);
|
|
482
|
+
return NULL;
|
|
483
|
+
}
|
|
484
|
+
memset(d, 0, sizeof(*d));
|
|
485
|
+
d->sys = sys;
|
|
486
|
+
d->pool = pool;
|
|
487
|
+
/* #157: the handle pool is lazy — two allocations and a mutex per
|
|
488
|
+
* document that decode-only consumers (CBOR load, serialize, free)
|
|
489
|
+
* never touch; yep_dom_handles() materializes it on first use */
|
|
490
|
+
d->handles = NULL;
|
|
491
|
+
d->child_cache = NULL;
|
|
492
|
+
d->child_cache_id = UINT32_MAX;
|
|
493
|
+
d->child_cache_len = 0;
|
|
494
|
+
if (yep_mutex_init(&d->midx.mu) != 0) {
|
|
495
|
+
yep_pool_destroy(pool);
|
|
496
|
+
yep_free(sys, d);
|
|
497
|
+
return NULL;
|
|
498
|
+
}
|
|
499
|
+
d->midx.mu_ready = 1;
|
|
500
|
+
return d;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* Lazy handle pool, mirroring the map-index's discipline (mapindex.c):
|
|
504
|
+
* EVERY access rides the dom's lazy-init mutex — read-only sharing
|
|
505
|
+
* means multiple threads may race the FIRST handle creation on a
|
|
506
|
+
* shared document (Threads.ReadOnlySharing; the TSAN catch on the
|
|
507
|
+
* first cut). The uncontended lock costs less than the hpool's own
|
|
508
|
+
* per-allocation lock that follows. */
|
|
509
|
+
struct yep_hpool* yep_dom_handles(yep_dom* d) {
|
|
510
|
+
if (d == NULL || !d->midx.mu_ready) {
|
|
511
|
+
return NULL;
|
|
512
|
+
}
|
|
513
|
+
yep_mutex_lock(&d->midx.mu);
|
|
514
|
+
if (d->handles == NULL) {
|
|
515
|
+
d->handles = yep_hpool_create(d->sys);
|
|
516
|
+
}
|
|
517
|
+
struct yep_hpool* h = d->handles;
|
|
518
|
+
yep_mutex_unlock(&d->midx.mu);
|
|
519
|
+
return h;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* #377 (ruby #168): seq_at/map_at were O(i) sibling walks — the
|
|
523
|
+
* bindings' per-element loops made an 80k-row sequence quadratic
|
|
524
|
+
* (the relaton index: 238-328s where stdlib takes ~3s). The bulk
|
|
525
|
+
* drain (yeptris_node_children) fixed the bindings; THIS fixes the
|
|
526
|
+
* accessor: the last indexed container's child ids cache under the
|
|
527
|
+
* lazy-init mutex, O(1) after the first call. Invalidated by every
|
|
528
|
+
* child-list mutation (dom_invalidate_child_cache). */
|
|
529
|
+
void dom_invalidate_child_cache(yep_dom* d) {
|
|
530
|
+
if (d == NULL) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
d->child_cache_id = UINT32_MAX;
|
|
534
|
+
d->child_cache_len = 0;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* Returns child id at index (UINT32_MAX when out of range); the
|
|
538
|
+
* container's child count rides *count_out. */
|
|
539
|
+
uint32_t dom_indexed_child(yep_dom* d, uint32_t cid, size_t index, uint32_t* count_out) {
|
|
540
|
+
const yep_dnode* n = yep_dom_node(d, cid);
|
|
541
|
+
if (n == NULL) {
|
|
542
|
+
*count_out = 0;
|
|
543
|
+
return UINT32_MAX;
|
|
544
|
+
}
|
|
545
|
+
*count_out = n->count;
|
|
546
|
+
if (d->child_cache_id == cid) {
|
|
547
|
+
if (index < d->child_cache_len) {
|
|
548
|
+
return d->child_cache[index];
|
|
549
|
+
}
|
|
550
|
+
return UINT32_MAX;
|
|
551
|
+
}
|
|
552
|
+
/* miss: build once under the lazy-init mutex (Threads.
|
|
553
|
+
* ReadOnlySharing — concurrent first calls race here exactly like
|
|
554
|
+
* the handle pool's) */
|
|
555
|
+
yep_mutex_lock(&d->midx.mu);
|
|
556
|
+
if (d->child_cache_id != cid) {
|
|
557
|
+
yep_free(d->sys, d->child_cache);
|
|
558
|
+
d->child_cache = NULL;
|
|
559
|
+
d->child_cache_len = 0;
|
|
560
|
+
if (n->count > 0) {
|
|
561
|
+
uint32_t* arr = yep_alloc(d->sys, n->count * sizeof(uint32_t));
|
|
562
|
+
if (arr != NULL) {
|
|
563
|
+
uint32_t k = 0;
|
|
564
|
+
for (uint32_t id = n->first_child; id != UINT32_MAX && k < n->count;) {
|
|
565
|
+
const yep_dnode* cn = yep_dom_node(d, id);
|
|
566
|
+
if (cn == NULL) {
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
arr[k++] = id;
|
|
570
|
+
id = cn->next_sibling;
|
|
571
|
+
}
|
|
572
|
+
d->child_cache = arr;
|
|
573
|
+
d->child_cache_len = k;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
d->child_cache_id = cid;
|
|
577
|
+
}
|
|
578
|
+
yep_mutex_unlock(&d->midx.mu);
|
|
579
|
+
if (index < d->child_cache_len) {
|
|
580
|
+
return d->child_cache[index];
|
|
581
|
+
}
|
|
582
|
+
return UINT32_MAX;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
void yep_dom_destroy(yep_dom* d) {
|
|
586
|
+
if (d == NULL) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
yep_free(d->sys, d->str);
|
|
590
|
+
yep_midx_destroy(d);
|
|
591
|
+
yep_free(d->sys, d->anchor_nodes);
|
|
592
|
+
yep_free(d->sys, d->mut_att);
|
|
593
|
+
yep_free(d->sys, d->mut_depth);
|
|
594
|
+
yep_hpool_destroy(d->handles);
|
|
595
|
+
yep_free(d->sys, d->child_cache);
|
|
596
|
+
yep_pool_destroy(d->pool);
|
|
597
|
+
yep_free(d->sys, d);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/* ------------------------------------------------- direct builders */
|
|
601
|
+
/* Two direct builders, one set of laws: yep_dom_build_json walks a
|
|
602
|
+
* whole strictly-validated JSON buffer (TODO.impl/27, the parse_json
|
|
603
|
+
* seam); dom_on_flow_json walks an engine-validated span mid-document
|
|
604
|
+
* (TODO.restructure/50, the sink fast path). Both create nodes through
|
|
605
|
+
* dom_open_node and place them through dom_place on the DOM's own
|
|
606
|
+
* stack — the event sink's pairing/link/anchor laws, shared, so the
|
|
607
|
+
* trees cannot diverge (the flow-direct-diff ctest pins it). */
|
|
608
|
+
|
|
609
|
+
#include "parse/scalars.h"
|
|
610
|
+
#include "resolve/resolver.h"
|
|
611
|
+
#include "scan/json.h"
|
|
612
|
+
#include "scan/scan.h"
|
|
613
|
+
|
|
614
|
+
static const yep_resolver* dom_resolver(const yep_dom* d) {
|
|
615
|
+
return d->resolver != NULL ? d->resolver : yep_resolver_core12();
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
typedef struct {
|
|
619
|
+
yep_dom* d;
|
|
620
|
+
const char* p;
|
|
621
|
+
size_t len;
|
|
622
|
+
size_t i;
|
|
623
|
+
} jbuilder;
|
|
624
|
+
|
|
625
|
+
/* Whole-buffer JSON: value-first recursion (the grammar is validated,
|
|
626
|
+
* the walk only advances). */
|
|
627
|
+
static int jb_value(jbuilder* b);
|
|
628
|
+
|
|
629
|
+
static int jb_string_node(jbuilder* b) {
|
|
630
|
+
uint32_t id = dom_open_node(b->d, YEP_DOM_SCALAR, NULL, NULL, 0, 0, 0, 0);
|
|
631
|
+
if (id == UINT32_MAX) {
|
|
632
|
+
return -1;
|
|
633
|
+
}
|
|
634
|
+
yep_dnode* n = &b->d->nodes[id];
|
|
635
|
+
size_t close;
|
|
636
|
+
int has_esc = 0;
|
|
637
|
+
size_t start = b->i;
|
|
638
|
+
if (!yep_json_string(b->p, b->len, &b->i, &close, &has_esc)) {
|
|
639
|
+
return -2;
|
|
640
|
+
}
|
|
641
|
+
if (has_esc) {
|
|
642
|
+
uint32_t span = (uint32_t)(close - start - 1);
|
|
643
|
+
char* dst = yep_dom_str_tail(b->d, span);
|
|
644
|
+
if (dst == NULL) {
|
|
645
|
+
return -1;
|
|
646
|
+
}
|
|
647
|
+
n->value = yep_dom_str_commit(
|
|
648
|
+
b->d, yep_finish_double_into(b->p, (uint32_t)(start + 1), (uint32_t)close, dst, span));
|
|
649
|
+
} else {
|
|
650
|
+
yep_view v = {b->p + start + 1, (uint32_t)(close - start - 1)};
|
|
651
|
+
n->value = dom_str_in(b->d, &v, 1);
|
|
652
|
+
}
|
|
653
|
+
n->style = YEP_STYLE_DOUBLE_QUOTED;
|
|
654
|
+
n->tag_id = 0; /* str */
|
|
655
|
+
return dom_place(b->d, id) == 0 ? 0 : -1;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
static int jb_scalar_node(jbuilder* b) {
|
|
659
|
+
uint32_t id = dom_open_node(b->d, YEP_DOM_SCALAR, NULL, NULL, 0, 0, 0, 0);
|
|
660
|
+
if (id == UINT32_MAX) {
|
|
661
|
+
return -1;
|
|
662
|
+
}
|
|
663
|
+
yep_dnode* n = &b->d->nodes[id];
|
|
664
|
+
size_t start = b->i;
|
|
665
|
+
size_t i = b->i;
|
|
666
|
+
const yep_resolver* r = dom_resolver(b->d);
|
|
667
|
+
if (b->p[i] == 't') {
|
|
668
|
+
if (!yep_json_literal(b->p, b->len, &i, "true")) {
|
|
669
|
+
return -2;
|
|
670
|
+
}
|
|
671
|
+
} else if (b->p[i] == 'f') {
|
|
672
|
+
if (!yep_json_literal(b->p, b->len, &i, "false")) {
|
|
673
|
+
return -2;
|
|
674
|
+
}
|
|
675
|
+
} else if (b->p[i] == 'n') {
|
|
676
|
+
if (!yep_json_literal(b->p, b->len, &i, "null")) {
|
|
677
|
+
return -2;
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
if (!yep_json_number(b->p, b->len, &i)) {
|
|
681
|
+
return -2;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
b->i = i;
|
|
685
|
+
yep_view v = {b->p + start, (uint32_t)(i - start)};
|
|
686
|
+
n->value = dom_str_in(b->d, &v, 1);
|
|
687
|
+
n->style = YEP_STYLE_PLAIN;
|
|
688
|
+
n->implicit = 1;
|
|
689
|
+
n->tag_id = r->resolve(NULL, v.p, v.len);
|
|
690
|
+
return dom_place(b->d, id) == 0 ? 0 : -1;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
static void jb_ws(jbuilder* b) {
|
|
694
|
+
while (b->i < b->len) {
|
|
695
|
+
char c = b->p[b->i];
|
|
696
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
|
697
|
+
b->i++;
|
|
698
|
+
} else {
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
static int jb_value(jbuilder* b) {
|
|
705
|
+
yep_dom* d = b->d;
|
|
706
|
+
jb_ws(b);
|
|
707
|
+
if (b->i >= b->len) {
|
|
708
|
+
return -2;
|
|
709
|
+
}
|
|
710
|
+
char c = b->p[b->i];
|
|
711
|
+
if (c == '{' || c == '[') {
|
|
712
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
713
|
+
return -2;
|
|
714
|
+
}
|
|
715
|
+
int map = (c == '{');
|
|
716
|
+
b->i++;
|
|
717
|
+
uint32_t id =
|
|
718
|
+
dom_open_node(d, map ? YEP_DOM_MAPPING : YEP_DOM_SEQUENCE, NULL, NULL, 0, 0, 0, 1);
|
|
719
|
+
if (id == UINT32_MAX) {
|
|
720
|
+
return -1;
|
|
721
|
+
}
|
|
722
|
+
if (dom_place(d, id) != 0) {
|
|
723
|
+
return -1;
|
|
724
|
+
}
|
|
725
|
+
d->map_pending_key[d->depth] = 0;
|
|
726
|
+
d->stack[d->depth++] = id;
|
|
727
|
+
jb_ws(b);
|
|
728
|
+
if (b->i < b->len && b->p[b->i] == (map ? '}' : ']')) {
|
|
729
|
+
b->i++;
|
|
730
|
+
d->depth--;
|
|
731
|
+
return 0;
|
|
732
|
+
}
|
|
733
|
+
for (;;) {
|
|
734
|
+
int rc = jb_value(b);
|
|
735
|
+
if (rc != 0) {
|
|
736
|
+
return rc;
|
|
737
|
+
}
|
|
738
|
+
jb_ws(b);
|
|
739
|
+
if (map) {
|
|
740
|
+
/* a value must be followed by , or } — the map's key
|
|
741
|
+
* slot is pending until the ':' value completes */
|
|
742
|
+
if (b->i < b->len && b->p[b->i] == ':' && d->map_pending_key[d->depth - 1] != 0) {
|
|
743
|
+
b->i++;
|
|
744
|
+
rc = jb_value(b);
|
|
745
|
+
if (rc != 0) {
|
|
746
|
+
return rc;
|
|
747
|
+
}
|
|
748
|
+
jb_ws(b);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (b->i >= b->len) {
|
|
752
|
+
return -2;
|
|
753
|
+
}
|
|
754
|
+
if (b->p[b->i] == ',') {
|
|
755
|
+
b->i++;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
if (b->p[b->i] == (map ? '}' : ']')) {
|
|
759
|
+
b->i++;
|
|
760
|
+
d->depth--;
|
|
761
|
+
return 0;
|
|
762
|
+
}
|
|
763
|
+
return -2;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (c == '"') {
|
|
767
|
+
return jb_string_node(b);
|
|
768
|
+
}
|
|
769
|
+
return jb_scalar_node(b);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
int yep_dom_build_json(yep_dom* d, const char* buf, size_t len) {
|
|
773
|
+
jbuilder b = {d, buf, len, 0};
|
|
774
|
+
int rc = jb_value(&b);
|
|
775
|
+
if (rc != 0) {
|
|
776
|
+
return rc;
|
|
777
|
+
}
|
|
778
|
+
jb_ws(&b);
|
|
779
|
+
if (b.i != len) {
|
|
780
|
+
return -2;
|
|
781
|
+
}
|
|
782
|
+
return 0;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/* --- the flow fast path (TODO.restructure/50) --- */
|
|
786
|
+
|
|
787
|
+
/* --- the fused flow build (TODO.restructure/53) --- */
|
|
788
|
+
|
|
789
|
+
/* One walk validates AND builds: the shared grammar walker drives,
|
|
790
|
+
* nodes are created through the DOM's own laws (dom_open_node,
|
|
791
|
+
* dom_place on the live stack above the entry depth). Staging: the
|
|
792
|
+
* root's placement and anchor binding defer to commit; scratch
|
|
793
|
+
* nodes live past the caller's ncount until then — ANY deviation
|
|
794
|
+
* restores both counters with zero live-state mutation, so the
|
|
795
|
+
* engine's fallback paths are exactly as before. */
|
|
796
|
+
static void dom_flow_stage_reset(yep_dom* d) {
|
|
797
|
+
d->ncount = d->flow_stage_base;
|
|
798
|
+
d->str_len = d->flow_stage_str;
|
|
799
|
+
d->flow_staged = 0;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
int dom_on_flow_build(void* ctx, const char* p, size_t open, size_t len, uint32_t line,
|
|
803
|
+
size_t line_start, yep_view anchor, yep_view tag, uint32_t anchor_id,
|
|
804
|
+
int max_depth, size_t* close) {
|
|
805
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
806
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
807
|
+
return 0; /* the event path owns the depth-limit error shape */
|
|
808
|
+
}
|
|
809
|
+
int entry_depth = d->depth;
|
|
810
|
+
d->flow_stage_base = d->ncount;
|
|
811
|
+
d->flow_stage_str = d->str_len;
|
|
812
|
+
|
|
813
|
+
uint32_t root = dom_open_node(d, p[open] == '[' ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING, &tag,
|
|
814
|
+
&anchor, 0, 0, 0, 1);
|
|
815
|
+
if (root == UINT32_MAX) {
|
|
816
|
+
goto fail;
|
|
817
|
+
}
|
|
818
|
+
d->map_pending_key[d->depth] = 0;
|
|
819
|
+
d->stack[d->depth++] = root;
|
|
820
|
+
|
|
821
|
+
const yep_resolver* r = dom_resolver(d);
|
|
822
|
+
yep_json_walk w;
|
|
823
|
+
yep_json_tok t;
|
|
824
|
+
yep_json_walk_init(&w, p, len, open, max_depth);
|
|
825
|
+
w.strict = d->flow_strict; /* the strict-JSON route rides the same
|
|
826
|
+
fused walk (TODO.restructure/81) */
|
|
827
|
+
for (;;) {
|
|
828
|
+
yep_jw_status st = yep_json_walk_next(&w, &t);
|
|
829
|
+
if (st == YEP_JW_REJECT) {
|
|
830
|
+
d->depth = entry_depth;
|
|
831
|
+
dom_flow_stage_reset(d);
|
|
832
|
+
return 0; /* not JSON-class: the general kernel */
|
|
833
|
+
}
|
|
834
|
+
if (st == YEP_JW_DONE) {
|
|
835
|
+
break;
|
|
836
|
+
}
|
|
837
|
+
switch (t.cls) {
|
|
838
|
+
case '[':
|
|
839
|
+
case '{': {
|
|
840
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
841
|
+
goto fail;
|
|
842
|
+
}
|
|
843
|
+
uint32_t cid = dom_open_node(d, t.cls == '[' ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING, NULL,
|
|
844
|
+
NULL, 0, 0, 0, 1);
|
|
845
|
+
if (cid == UINT32_MAX) {
|
|
846
|
+
goto fail;
|
|
847
|
+
}
|
|
848
|
+
if (dom_place(d, cid) != 0) {
|
|
849
|
+
goto fail;
|
|
850
|
+
}
|
|
851
|
+
d->map_pending_key[d->depth] = 0;
|
|
852
|
+
d->stack[d->depth++] = cid;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
case ']':
|
|
856
|
+
case '}':
|
|
857
|
+
d->depth--;
|
|
858
|
+
break;
|
|
859
|
+
case '"': {
|
|
860
|
+
uint32_t sid =
|
|
861
|
+
dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, YEP_STYLE_DOUBLE_QUOTED, 0, 0);
|
|
862
|
+
if (sid == UINT32_MAX) {
|
|
863
|
+
goto fail;
|
|
864
|
+
}
|
|
865
|
+
if (t.has_escape) {
|
|
866
|
+
uint32_t span = (uint32_t)(t.end - t.at - 2);
|
|
867
|
+
char* dst = yep_dom_str_tail(d, span);
|
|
868
|
+
if (dst == NULL) {
|
|
869
|
+
goto fail;
|
|
870
|
+
}
|
|
871
|
+
d->nodes[sid].value =
|
|
872
|
+
yep_dom_str_commit(d, yep_finish_double_into(p, (uint32_t)(t.at + 1),
|
|
873
|
+
(uint32_t)(t.end - 1), dst, span));
|
|
874
|
+
} else {
|
|
875
|
+
yep_view v = {p + t.at + 1, (uint32_t)(t.end - t.at - 2)};
|
|
876
|
+
d->nodes[sid].value = dom_str_in(d, &v, 1);
|
|
877
|
+
}
|
|
878
|
+
if (dom_place(d, sid) != 0) {
|
|
879
|
+
goto fail;
|
|
880
|
+
}
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
default: { /* number or literal: the walker validated the span */
|
|
884
|
+
uint32_t sid = dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
|
|
885
|
+
if (sid == UINT32_MAX) {
|
|
886
|
+
goto fail;
|
|
887
|
+
}
|
|
888
|
+
yep_view v = {p + t.at, (uint32_t)(t.end - t.at)};
|
|
889
|
+
d->nodes[sid].value = dom_str_in(d, &v, 1);
|
|
890
|
+
if (t.cls == '#') {
|
|
891
|
+
d->nodes[sid].tag_id = yep_resolve_number(r, t.is_float);
|
|
892
|
+
} else if (d->flow_strict) {
|
|
893
|
+
/* strict JSON: the walker validated the span is exactly
|
|
894
|
+
* one of the three RFC 8259 words — no resolver dispatch */
|
|
895
|
+
d->nodes[sid].tag_id = (v.p[0] == 'n') ? YEPTRIS_TAG_NULL : YEPTRIS_TAG_BOOL;
|
|
896
|
+
} else {
|
|
897
|
+
d->nodes[sid].tag_id = r->resolve(NULL, v.p, v.len);
|
|
898
|
+
}
|
|
899
|
+
if (dom_place(d, sid) != 0) {
|
|
900
|
+
goto fail;
|
|
901
|
+
}
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
d->depth = entry_depth;
|
|
907
|
+
if (w.long_key) {
|
|
908
|
+
dom_flow_stage_reset(d);
|
|
909
|
+
return 2; /* grammar-valid; pass 2 owns the simple-key error */
|
|
910
|
+
}
|
|
911
|
+
d->flow_staged = 1;
|
|
912
|
+
d->flow_stage_root = root;
|
|
913
|
+
d->flow_stage_anchor = anchor_id;
|
|
914
|
+
*close = w.close;
|
|
915
|
+
return 1;
|
|
916
|
+
fail:
|
|
917
|
+
d->depth = entry_depth;
|
|
918
|
+
dom_flow_stage_reset(d);
|
|
919
|
+
return -1;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
int dom_on_flow_commit(void* ctx) {
|
|
923
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
924
|
+
if (!d->flow_staged) {
|
|
925
|
+
return 0;
|
|
926
|
+
}
|
|
927
|
+
d->flow_staged = 0;
|
|
928
|
+
if (d->flow_stage_anchor != 0 &&
|
|
929
|
+
dom_anchor_set(d, d->flow_stage_anchor, d->flow_stage_root) != 0) {
|
|
930
|
+
return -1;
|
|
931
|
+
}
|
|
932
|
+
if (dom_place(d, d->flow_stage_root) != 0) {
|
|
933
|
+
return -1;
|
|
934
|
+
}
|
|
935
|
+
return 1;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
939
|
+
if (d == NULL || t == NULL || t->_src == NULL) {
|
|
940
|
+
return -1;
|
|
941
|
+
}
|
|
942
|
+
/* the columns are the one representation valid on every route
|
|
943
|
+
* (strict: native; lenient fused: lazily materialized from the
|
|
944
|
+
* records; scalar-root lenient: native column writes) */
|
|
945
|
+
if (yeptris_tape_columns((yeptris_json_tape*)t) != 0) {
|
|
946
|
+
return -1;
|
|
947
|
+
}
|
|
948
|
+
if (t->kinds == NULL) {
|
|
949
|
+
return -1;
|
|
950
|
+
}
|
|
951
|
+
d->input_base = (const char*)t->_src;
|
|
952
|
+
d->input_len = t->_srclen;
|
|
953
|
+
const char* src = (const char*)t->_src;
|
|
954
|
+
d->depth = 0;
|
|
955
|
+
for (uint32_t i = 0; i < t->count; i++) {
|
|
956
|
+
const uint8_t kind = t->kinds[i];
|
|
957
|
+
const uint32_t len = t->lens[i];
|
|
958
|
+
const uint32_t off = t->offs[i];
|
|
959
|
+
switch (kind) {
|
|
960
|
+
case YEP_T_DOC:
|
|
961
|
+
break; /* the stream boundary record */
|
|
962
|
+
case YEP_T_SEQ_OPEN:
|
|
963
|
+
case YEP_T_MAP_OPEN: {
|
|
964
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
965
|
+
return -1;
|
|
966
|
+
}
|
|
967
|
+
uint32_t id =
|
|
968
|
+
dom_open_node(d, kind == YEP_T_SEQ_OPEN ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING, NULL,
|
|
969
|
+
NULL, 0, 0, 0, 1);
|
|
970
|
+
if (id == UINT32_MAX || dom_place(d, id) != 0) {
|
|
971
|
+
return -1;
|
|
972
|
+
}
|
|
973
|
+
d->map_pending_key[d->depth] = 0;
|
|
974
|
+
d->stack[d->depth++] = id;
|
|
975
|
+
break;
|
|
976
|
+
}
|
|
977
|
+
case YEP_T_CLOSE:
|
|
978
|
+
if (d->depth == 0) {
|
|
979
|
+
return -1;
|
|
980
|
+
}
|
|
981
|
+
d->depth--;
|
|
982
|
+
break;
|
|
983
|
+
case YEP_T_NULL:
|
|
984
|
+
case YEP_T_TRUE:
|
|
985
|
+
case YEP_T_FALSE:
|
|
986
|
+
case YEP_T_INT:
|
|
987
|
+
case YEP_T_FLOAT:
|
|
988
|
+
case YEP_T_NUM:
|
|
989
|
+
case YEP_T_STR: {
|
|
990
|
+
yep_tag_id tag = YEPTRIS_TAG_STR;
|
|
991
|
+
uint8_t style = YEP_STYLE_PLAIN;
|
|
992
|
+
int implicit = 0;
|
|
993
|
+
switch (kind) {
|
|
994
|
+
case YEP_T_NULL:
|
|
995
|
+
tag = YEPTRIS_TAG_NULL;
|
|
996
|
+
implicit = 1;
|
|
997
|
+
break;
|
|
998
|
+
case YEP_T_TRUE:
|
|
999
|
+
case YEP_T_FALSE:
|
|
1000
|
+
tag = YEPTRIS_TAG_BOOL;
|
|
1001
|
+
implicit = 1;
|
|
1002
|
+
break;
|
|
1003
|
+
case YEP_T_INT:
|
|
1004
|
+
case YEP_T_FLOAT:
|
|
1005
|
+
case YEP_T_NUM:
|
|
1006
|
+
tag = YEPTRIS_TAG_INT;
|
|
1007
|
+
implicit = 1;
|
|
1008
|
+
if (kind == YEP_T_NUM) {
|
|
1009
|
+
/* the lenient route's deferred contract: the span
|
|
1010
|
+
* carried NO number grammar at parse — classify
|
|
1011
|
+
* now (the tape convert's own authority) */
|
|
1012
|
+
size_t adv = 0;
|
|
1013
|
+
int flt = 0;
|
|
1014
|
+
if (yep_json_number_shape(src + off, len, &adv, &flt) == 0 || adv != len) {
|
|
1015
|
+
return -1; /* malformed: the strict route rejected at parse */
|
|
1016
|
+
}
|
|
1017
|
+
tag = flt ? YEPTRIS_TAG_FLOAT : YEPTRIS_TAG_INT;
|
|
1018
|
+
} else if (kind == YEP_T_FLOAT) {
|
|
1019
|
+
tag = YEPTRIS_TAG_FLOAT;
|
|
1020
|
+
}
|
|
1021
|
+
break;
|
|
1022
|
+
default:
|
|
1023
|
+
style = YEP_STYLE_DOUBLE_QUOTED;
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
uint32_t id =
|
|
1027
|
+
dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, style, (uint8_t)implicit, 0);
|
|
1028
|
+
if (id == UINT32_MAX) {
|
|
1029
|
+
return -1;
|
|
1030
|
+
}
|
|
1031
|
+
if (kind == YEP_T_STR) {
|
|
1032
|
+
/* the record's span is the INNER bytes (quotes off) */
|
|
1033
|
+
if (memchr(src + off, '\\', len) != NULL) {
|
|
1034
|
+
/* escaped: unescape into the arena (the fused
|
|
1035
|
+
* builder's arm, byte for byte) */
|
|
1036
|
+
char* dst = yep_dom_str_tail(d, len);
|
|
1037
|
+
if (dst == NULL) {
|
|
1038
|
+
return -1;
|
|
1039
|
+
}
|
|
1040
|
+
d->nodes[id].value = yep_dom_str_commit(
|
|
1041
|
+
d, yep_finish_double_into(src, off, off + len, dst, len));
|
|
1042
|
+
} else {
|
|
1043
|
+
yep_view v = {src + off, len};
|
|
1044
|
+
d->nodes[id].value = dom_str_in(d, &v, 1);
|
|
1045
|
+
}
|
|
1046
|
+
} else {
|
|
1047
|
+
yep_view v = {src + off, len};
|
|
1048
|
+
d->nodes[id].value = dom_str_in(d, &v, 1);
|
|
1049
|
+
}
|
|
1050
|
+
d->nodes[id].tag_id = tag;
|
|
1051
|
+
if (dom_place(d, id) != 0) {
|
|
1052
|
+
return -1;
|
|
1053
|
+
}
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
default:
|
|
1057
|
+
return -1; /* a CONT run or an unknown kind: neither reaches a
|
|
1058
|
+
strict-legal tape (CONT only past 16 MiB tokens) */
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return d->depth == 0 ? 0 : -1;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
void dom_on_flow_rollback(void* ctx) {
|
|
1065
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
1066
|
+
if (d->flow_staged) {
|
|
1067
|
+
dom_flow_stage_reset(d);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/* The sink's block-open fast path (TODO.restructure/57): a `key:` line
|
|
1072
|
+
* whose value follows becomes the map node plus its pending key in one
|
|
1073
|
+
* call — the DOM stack mirrors what the MAP_START event would have
|
|
1074
|
+
* pushed. 0 only for the depth cap (the engine then takes the events,
|
|
1075
|
+
* which own the error). */
|
|
1076
|
+
int dom_on_block_open(void* ctx, const yep_view* key, uint32_t line, uint16_t key_col) {
|
|
1077
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
1078
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
1079
|
+
return 0;
|
|
1080
|
+
}
|
|
1081
|
+
const yep_resolver* r = dom_resolver(d);
|
|
1082
|
+
uint32_t mid = dom_open_node(d, YEP_DOM_MAPPING, NULL, NULL, 0, 0, 0, 0);
|
|
1083
|
+
if (mid == UINT32_MAX) {
|
|
1084
|
+
return -1;
|
|
1085
|
+
}
|
|
1086
|
+
if (dom_place(d, mid) != 0) {
|
|
1087
|
+
return -1;
|
|
1088
|
+
}
|
|
1089
|
+
d->map_pending_key[d->depth] = 0;
|
|
1090
|
+
d->stack[d->depth++] = mid;
|
|
1091
|
+
uint32_t kid = dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
|
|
1092
|
+
if (kid == UINT32_MAX) {
|
|
1093
|
+
d->depth--;
|
|
1094
|
+
return -1;
|
|
1095
|
+
}
|
|
1096
|
+
d->nodes[kid].value = dom_str_in(d, key, 1);
|
|
1097
|
+
d->nodes[kid].tag_id = r->resolve(NULL, key->p, key->len);
|
|
1098
|
+
return dom_place(d, kid) == 0 ? 1 : -1;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/* The sink's dash-item fast path (TODO.restructure/78): one node
|
|
1102
|
+
* through the node-init law, placed into the sequence the engine
|
|
1103
|
+
* opened before offering — node-for-node the event path's item. */
|
|
1104
|
+
int dom_on_block_item(void* ctx, const yep_block_value* v) {
|
|
1105
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
1106
|
+
const yep_resolver* r = dom_resolver(d);
|
|
1107
|
+
uint32_t vid = dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
|
|
1108
|
+
if (vid == UINT32_MAX) {
|
|
1109
|
+
return -1;
|
|
1110
|
+
}
|
|
1111
|
+
d->nodes[vid].value = dom_str_in(d, &v->value, v->borrowed);
|
|
1112
|
+
d->nodes[vid].tag_id = r->resolve(NULL, v->value.p, v->value.len);
|
|
1113
|
+
return dom_place(d, vid) == 0 ? 1 : -1;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/* The sink's block fast path (TODO.restructure/54): one classified
|
|
1117
|
+
* `key: value` line becomes two nodes — the key scalar resolved like
|
|
1118
|
+
* every implicit key event, the value through its class — placed with
|
|
1119
|
+
* the shared pairing law. The engine offers the line BEFORE emitting;
|
|
1120
|
+
* returning 0 here (only for an unbound alias target, which cannot
|
|
1121
|
+
* happen when the engine resolved the name) falls back to the two
|
|
1122
|
+
* events with nothing built. */
|
|
1123
|
+
int dom_on_block_pair(void* ctx, const yep_view* key, const yep_block_value* v, uint32_t line,
|
|
1124
|
+
uint16_t key_col, uint16_t val_col) {
|
|
1125
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
1126
|
+
uint32_t target = 0;
|
|
1127
|
+
if (v->cls == YEP_LVAL_ALIAS) {
|
|
1128
|
+
target = dom_anchor_get(d, v->anchor_id);
|
|
1129
|
+
if (target == UINT32_MAX) {
|
|
1130
|
+
return 0;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
const yep_resolver* r = dom_resolver(d);
|
|
1134
|
+
uint32_t kid = dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, YEP_STYLE_PLAIN, 1, 0);
|
|
1135
|
+
if (kid == UINT32_MAX) {
|
|
1136
|
+
return -1;
|
|
1137
|
+
}
|
|
1138
|
+
d->nodes[kid].value = dom_str_in(d, key, 1);
|
|
1139
|
+
d->nodes[kid].tag_id = r->resolve(NULL, key->p, key->len);
|
|
1140
|
+
if (dom_place(d, kid) != 0) {
|
|
1141
|
+
return -1;
|
|
1142
|
+
}
|
|
1143
|
+
if (v->cls == YEP_LVAL_ALIAS) {
|
|
1144
|
+
uint32_t vid = dom_open_node(d, YEP_DOM_ALIAS, NULL, NULL, 0, 0, 0, 0);
|
|
1145
|
+
if (vid == UINT32_MAX) {
|
|
1146
|
+
return -1;
|
|
1147
|
+
}
|
|
1148
|
+
d->nodes[vid].value = dom_str_in(d, &v->value, 1); /* borrowed — the
|
|
1149
|
+
event path stamps it too now (73); the gates compare decoded
|
|
1150
|
+
views, not arena placement */
|
|
1151
|
+
d->nodes[vid].target = target;
|
|
1152
|
+
return dom_place(d, vid) == 0 ? 1 : -1;
|
|
1153
|
+
}
|
|
1154
|
+
int anchored = (v->cls == YEP_LVAL_ANCHOR_PLAIN);
|
|
1155
|
+
uint32_t vid = dom_open_node(d, YEP_DOM_SCALAR, NULL, anchored ? &v->anchor : NULL, v->borrowed,
|
|
1156
|
+
YEP_STYLE_PLAIN, 1, 0);
|
|
1157
|
+
if (vid == UINT32_MAX) {
|
|
1158
|
+
return -1;
|
|
1159
|
+
}
|
|
1160
|
+
d->nodes[vid].value = dom_str_in(d, &v->value, v->borrowed);
|
|
1161
|
+
d->nodes[vid].tag_id = r->resolve(NULL, v->value.p, v->value.len);
|
|
1162
|
+
if (anchored && v->anchor_id != 0 && dom_anchor_set(d, v->anchor_id, vid) != 0) {
|
|
1163
|
+
return -1;
|
|
1164
|
+
}
|
|
1165
|
+
return dom_place(d, vid) == 0 ? 1 : -1;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/* The direct scalar path (TODO.restructure/79): the YEP_EV_SCALAR
|
|
1169
|
+
* case's node build, minus the event seam. The engine resolved
|
|
1170
|
+
* tag_id (the typing SSOT); everything else is the case body. */
|
|
1171
|
+
int dom_on_scalar(void* ctx, const yep_view* value, const yep_view* tag, const yep_view* anchor,
|
|
1172
|
+
uint32_t anchor_id, uint8_t tag_id, uint8_t style, uint8_t implicit, uint8_t flow,
|
|
1173
|
+
int borrowed) {
|
|
1174
|
+
yep_dom* d = (yep_dom*)ctx;
|
|
1175
|
+
uint32_t id = dom_open_node(d, YEP_DOM_SCALAR, tag, anchor, borrowed, style, implicit, flow);
|
|
1176
|
+
if (id == UINT32_MAX) {
|
|
1177
|
+
return -1;
|
|
1178
|
+
}
|
|
1179
|
+
d->nodes[id].value = dom_str_in(d, value, borrowed);
|
|
1180
|
+
d->nodes[id].tag_id = tag_id;
|
|
1181
|
+
if (anchor_id != 0 && dom_anchor_set(d, anchor_id, id) != 0) {
|
|
1182
|
+
return -1;
|
|
1183
|
+
}
|
|
1184
|
+
return dom_place(d, id);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const yep_dnode* yep_dom_node(const yep_dom* d, uint32_t id) {
|
|
1188
|
+
if (d == NULL || id >= d->ncount) {
|
|
1189
|
+
return NULL;
|
|
1190
|
+
}
|
|
1191
|
+
return &d->nodes[id];
|
|
1192
|
+
}
|