yeptris 0.6.5.4 → 0.6.7.2
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/libyeptris/extconf.rb +57 -0
- data/lib/yeptris/ffi.rb +12 -15
- data/lib/yeptris/json.rb +5 -2
- data/lib/yeptris/psych.rb +76 -3
- data/lib/yeptris.rb +36 -14
- 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 +181 -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 +119 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +99 -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 +371 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +846 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +824 -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 +37 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +973 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +290 -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 +429 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +349 -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 +203 -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 +746 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +61 -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 +744 -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 +115 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3689 -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 +650 -0
- data/vendor/libyeptris/src/yeptris/plan.c +817 -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 +238 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1292 -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 +112 -2
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
/* parse.c — public API glue: encoding front-end → engine → DOM. */
|
|
2
|
+
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
5
|
+
#include "common/simd_text.h"
|
|
6
|
+
#include "doc.h"
|
|
7
|
+
#include "dom/dom.h"
|
|
8
|
+
#include "encoding/encoding.h"
|
|
9
|
+
#include "memory/allocator.h"
|
|
10
|
+
#include "memory/pool.h"
|
|
11
|
+
#include "parse/engine.h"
|
|
12
|
+
#include "parse/numbers.h"
|
|
13
|
+
#include "resolve/resolver.h"
|
|
14
|
+
#include "scan/json.h"
|
|
15
|
+
|
|
16
|
+
#include "parse/events.h"
|
|
17
|
+
#include <errno.h>
|
|
18
|
+
#include <math.h>
|
|
19
|
+
#include <stdlib.h>
|
|
20
|
+
|
|
21
|
+
#include <yeptris.h>
|
|
22
|
+
|
|
23
|
+
YEPTRIS_API const char* yeptris_last_error(uint32_t* line, uint32_t* col) {
|
|
24
|
+
const yep_error* tls = yep_error_tls();
|
|
25
|
+
if (line != NULL) {
|
|
26
|
+
*line = tls->line;
|
|
27
|
+
}
|
|
28
|
+
if (col != NULL) {
|
|
29
|
+
*col = tls->col;
|
|
30
|
+
}
|
|
31
|
+
return tls->msg;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisParseOptions* opts,
|
|
35
|
+
int json_mode, YeptrisStatus* status);
|
|
36
|
+
|
|
37
|
+
YEPTRIS_API YeptrisDocument yeptris_parse(const char* buf, size_t len, YeptrisStatus* status) {
|
|
38
|
+
return yeptris_parse_ex(buf, len, NULL, status);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* The strict-JSON document wrapper (both routes share it). */
|
|
42
|
+
static YeptrisDocument yep_json_doc_wrap(yep_dom* dom, const char* buf, size_t len,
|
|
43
|
+
const yep_allocator* sys, YeptrisStatus* status) {
|
|
44
|
+
(void)len;
|
|
45
|
+
yeptris_document* doc = yep_alloc(sys, sizeof(yeptris_document));
|
|
46
|
+
if (doc == NULL) {
|
|
47
|
+
yep_dom_destroy(dom);
|
|
48
|
+
if (status != NULL) {
|
|
49
|
+
*status = YEPTRIS_ERROR_MEMORY;
|
|
50
|
+
}
|
|
51
|
+
return NULL;
|
|
52
|
+
}
|
|
53
|
+
doc->dom = dom;
|
|
54
|
+
doc->sys = sys;
|
|
55
|
+
doc->schema = YEPTRIS_SCHEMA_12_CORE; /* strict JSON is core by construction */
|
|
56
|
+
doc->transcoded = NULL;
|
|
57
|
+
doc->transcoded_len = 0;
|
|
58
|
+
doc->input = buf;
|
|
59
|
+
doc->finish_pool = NULL;
|
|
60
|
+
if (status != NULL) {
|
|
61
|
+
*status = YEPTRIS_OK;
|
|
62
|
+
}
|
|
63
|
+
return (YeptrisDocument)doc;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
YEPTRIS_API YeptrisDocument yeptris_parse_json(const char* buf, size_t len, YeptrisStatus* status) {
|
|
67
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
68
|
+
if (buf == NULL && len != 0) {
|
|
69
|
+
st = YEPTRIS_ERROR_ARG;
|
|
70
|
+
goto jfail;
|
|
71
|
+
}
|
|
72
|
+
/* Clean-gate fast route (TODO.restructure/81 stage 2): a gate-clean
|
|
73
|
+
* buffer is printable ASCII, and the FUSED builder's walk runs
|
|
74
|
+
* STRICT here (dom->flow_strict) — one pass validates RFC 8259 and
|
|
75
|
+
* builds. Rejects, non-opener roots, and surprises fall to the
|
|
76
|
+
* original sequence below, whose error precedence is byte-for-byte
|
|
77
|
+
* the pinned behavior (json-suite-strict gates this). */
|
|
78
|
+
if (len > 0 && !yep_text_active()->gate_scan(buf, len)) {
|
|
79
|
+
size_t off = 0;
|
|
80
|
+
while (off < len &&
|
|
81
|
+
(buf[off] == ' ' || buf[off] == '\t' || buf[off] == '\n' || buf[off] == '\r')) {
|
|
82
|
+
off++;
|
|
83
|
+
}
|
|
84
|
+
if (off < len && (buf[off] == '[' || buf[off] == '{')) {
|
|
85
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
86
|
+
yep_dom* dom = yep_dom_create(sys);
|
|
87
|
+
if (dom == NULL) {
|
|
88
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
89
|
+
goto jfail;
|
|
90
|
+
}
|
|
91
|
+
dom->input_base = buf;
|
|
92
|
+
dom->input_len = len;
|
|
93
|
+
dom->flow_strict = 1;
|
|
94
|
+
size_t close = 0;
|
|
95
|
+
int rc = dom_on_flow_build(dom, buf, off, len, 1, 0, (yep_view){0}, (yep_view){0}, 0,
|
|
96
|
+
YEP_DOM_MAX_DEPTH, &close);
|
|
97
|
+
dom->flow_strict = 0;
|
|
98
|
+
int tail_ok = 0;
|
|
99
|
+
if (rc == 1) {
|
|
100
|
+
/* trailing garbage after the closer is a reject the
|
|
101
|
+
* fallback reports exactly as before */
|
|
102
|
+
size_t t = close + 1;
|
|
103
|
+
while (t < len &&
|
|
104
|
+
(buf[t] == ' ' || buf[t] == '\t' || buf[t] == '\n' || buf[t] == '\r')) {
|
|
105
|
+
t++;
|
|
106
|
+
}
|
|
107
|
+
tail_ok = (t == len);
|
|
108
|
+
}
|
|
109
|
+
if (rc == 1 && tail_ok && dom_on_flow_commit(dom) > 0) {
|
|
110
|
+
return yep_json_doc_wrap(dom, buf, len, sys, status);
|
|
111
|
+
}
|
|
112
|
+
yep_dom_destroy(dom); /* reject/rollback: the sequence below */
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
size_t verr = 0;
|
|
116
|
+
if (!yep_json_document(buf, len, &verr)) {
|
|
117
|
+
yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, verr,
|
|
118
|
+
"not strict JSON at byte %zu", verr);
|
|
119
|
+
st = YEPTRIS_ERROR_PARSE;
|
|
120
|
+
goto jfail;
|
|
121
|
+
}
|
|
122
|
+
size_t uerr = 0;
|
|
123
|
+
if (yep_text_active()->gate_scan(buf, len) &&
|
|
124
|
+
!yep_utf8_validate((const unsigned char*)buf, len, &uerr)) {
|
|
125
|
+
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, uerr, "ill-formed UTF-8 at byte %zu",
|
|
126
|
+
uerr);
|
|
127
|
+
st = YEPTRIS_ERROR_ENCODING;
|
|
128
|
+
goto jfail;
|
|
129
|
+
}
|
|
130
|
+
/* Direct DOM construction (TODO.impl/27): the validated buffer
|
|
131
|
+
* needs no engine — one walk builds the tree, skipping the event
|
|
132
|
+
* pipeline. A builder/validator disagreement defers to the engine
|
|
133
|
+
* (never happens in-tree; belt and braces). */
|
|
134
|
+
{
|
|
135
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
136
|
+
yep_dom* dom = yep_dom_create(sys);
|
|
137
|
+
if (dom == NULL) {
|
|
138
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
139
|
+
goto jfail;
|
|
140
|
+
}
|
|
141
|
+
dom->input_base = buf; /* strict JSON is UTF-8 by definition */
|
|
142
|
+
dom->input_len = len;
|
|
143
|
+
yep_dom_prepare_len(dom, len);
|
|
144
|
+
int brc = yep_dom_build_json(dom, buf, len);
|
|
145
|
+
if (brc == -1) {
|
|
146
|
+
yep_dom_destroy(dom);
|
|
147
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
148
|
+
goto jfail;
|
|
149
|
+
}
|
|
150
|
+
if (brc == -2) {
|
|
151
|
+
yep_dom_destroy(dom);
|
|
152
|
+
return parse_impl(buf, len, NULL, 1, status);
|
|
153
|
+
}
|
|
154
|
+
return yep_json_doc_wrap(dom, buf, len, sys, status);
|
|
155
|
+
}
|
|
156
|
+
jfail:
|
|
157
|
+
if (status != NULL) {
|
|
158
|
+
*status = st;
|
|
159
|
+
}
|
|
160
|
+
return NULL;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisParseOptions* opts,
|
|
164
|
+
int json_mode, YeptrisStatus* status) {
|
|
165
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
166
|
+
if (buf == NULL && len != 0) {
|
|
167
|
+
st = YEPTRIS_ERROR_ARG;
|
|
168
|
+
goto fail;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
172
|
+
|
|
173
|
+
/* Encoding front-end: BOM sniff; borrow UTF-8, transcode the rest. */
|
|
174
|
+
const char* data = buf;
|
|
175
|
+
size_t data_len = len;
|
|
176
|
+
unsigned char* transcoded = NULL;
|
|
177
|
+
size_t transcoded_len = 0;
|
|
178
|
+
yep_encoding enc = YEP_ENC_UNKNOWN;
|
|
179
|
+
|
|
180
|
+
if (json_mode) {
|
|
181
|
+
/* JSON mode validated grammar and UTF-8 well-formedness at the
|
|
182
|
+
* entry: no BOM, no transcoding, and never the printable set —
|
|
183
|
+
* RFC 8259 allows DEL and noncharacters that YAML c-printable
|
|
184
|
+
* excludes. The engine takes the bytes directly. */
|
|
185
|
+
goto engine_enter;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
size_t bom = yep_bom_sniff((const unsigned char*)buf, len, &enc);
|
|
189
|
+
data = buf + bom;
|
|
190
|
+
data_len = len - bom;
|
|
191
|
+
|
|
192
|
+
if (enc == YEP_ENC_UTF16LE || enc == YEP_ENC_UTF16BE || enc == YEP_ENC_UTF32LE ||
|
|
193
|
+
enc == YEP_ENC_UTF32BE) {
|
|
194
|
+
size_t terr = 0;
|
|
195
|
+
int rc = yep_transcode_to_utf8(sys, enc, (const unsigned char*)data, data_len, &transcoded,
|
|
196
|
+
&transcoded_len, &terr);
|
|
197
|
+
if (rc != 0) {
|
|
198
|
+
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, terr,
|
|
199
|
+
"ill-formed input encoding");
|
|
200
|
+
yep_free(sys, transcoded);
|
|
201
|
+
st = YEPTRIS_ERROR_ENCODING;
|
|
202
|
+
goto fail;
|
|
203
|
+
}
|
|
204
|
+
data = (const char*)transcoded;
|
|
205
|
+
data_len = transcoded_len;
|
|
206
|
+
size_t pverr = 0;
|
|
207
|
+
if (!yep_printable_validate((const unsigned char*)data, data_len, &pverr)) {
|
|
208
|
+
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, pverr,
|
|
209
|
+
"non-printable character at byte %zu", pverr);
|
|
210
|
+
yep_free(sys, transcoded);
|
|
211
|
+
st = YEPTRIS_ERROR_ENCODING;
|
|
212
|
+
goto fail;
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
/* The encoding gate rides the cheap gate_scan kernel; sizing
|
|
216
|
+
* rides length heuristics (TODO.restructure/66: the fused
|
|
217
|
+
* multi-class stats sweep cost ~12% on the ubuntu asymmetry
|
|
218
|
+
* shapes — ryml pays no pre-pass). Pure gate-safe input skips
|
|
219
|
+
* the SWAR validator entirely; anything else falls to it for
|
|
220
|
+
* the authoritative answer + error position. */
|
|
221
|
+
if (yep_text_active()->gate_scan(data, data_len)) {
|
|
222
|
+
size_t verr = 0;
|
|
223
|
+
if (!yep_printable_validate((const unsigned char*)data, data_len, &verr)) {
|
|
224
|
+
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, verr,
|
|
225
|
+
"ill-formed or non-printable UTF-8 at byte %zu", verr);
|
|
226
|
+
st = YEPTRIS_ERROR_ENCODING;
|
|
227
|
+
goto fail;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Engine → DOM. */
|
|
233
|
+
yep_engine* eng = NULL;
|
|
234
|
+
engine_enter:
|
|
235
|
+
eng = yep_engine_create(sys);
|
|
236
|
+
if (opts != NULL) {
|
|
237
|
+
yep_engine_set_resolver(eng, opts->schema == YEPTRIS_SCHEMA_11_COMPAT
|
|
238
|
+
? yep_resolver_compat11()
|
|
239
|
+
: yep_resolver_core12());
|
|
240
|
+
if (opts->max_depth > 0) {
|
|
241
|
+
yep_engine_set_max_depth(eng, opts->max_depth);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (eng == NULL) {
|
|
245
|
+
yep_free(sys, transcoded);
|
|
246
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
247
|
+
goto fail;
|
|
248
|
+
}
|
|
249
|
+
yep_dom* dom = yep_dom_create(sys);
|
|
250
|
+
if (dom == NULL) {
|
|
251
|
+
yep_engine_destroy(eng);
|
|
252
|
+
yep_free(sys, transcoded);
|
|
253
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
254
|
+
goto fail;
|
|
255
|
+
}
|
|
256
|
+
/* the direct builders resolve with the engine's schema (the typing
|
|
257
|
+
* SSOT — one resolver decision per scalar, whichever path builds) */
|
|
258
|
+
dom->resolver = (opts != NULL && opts->schema == YEPTRIS_SCHEMA_11_COMPAT)
|
|
259
|
+
? yep_resolver_compat11()
|
|
260
|
+
: yep_resolver_core12();
|
|
261
|
+
/* BEFORE the run: input-slice strings borrow as views (zero copy)
|
|
262
|
+
* — the document keeps `transcoded` alive for exactly this. Set
|
|
263
|
+
* after, every scalar was arena-copied (found 2026-09-10). */
|
|
264
|
+
dom->input_len = data_len;
|
|
265
|
+
dom->input_base = transcoded ? (const char*)transcoded : buf;
|
|
266
|
+
yep_dom_prepare_len(dom, data_len);
|
|
267
|
+
{
|
|
268
|
+
/* the nametab reserve survives: a memchr chain for '&' is
|
|
269
|
+
* far cheaper than the class sweep it replaced */
|
|
270
|
+
yep_text_stats amp_only = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
271
|
+
const char* q = data;
|
|
272
|
+
const char* end = data + data_len;
|
|
273
|
+
if (q != NULL) {
|
|
274
|
+
while ((q = memchr(q, '&', (size_t)(end - q))) != NULL) {
|
|
275
|
+
amp_only.amp++;
|
|
276
|
+
q++;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
yep_engine_prepare(eng, &_only);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
yep_sink sink = {.on_event = yep_dom_on_event,
|
|
283
|
+
.ctx = dom,
|
|
284
|
+
.on_flow_build = dom_on_flow_build,
|
|
285
|
+
.on_flow_commit = dom_on_flow_commit,
|
|
286
|
+
.on_flow_rollback = dom_on_flow_rollback,
|
|
287
|
+
.on_block_pair = dom_on_block_pair,
|
|
288
|
+
.on_scalar = dom_on_scalar,
|
|
289
|
+
.on_block_open = dom_on_block_open,
|
|
290
|
+
.on_block_item = dom_on_block_item};
|
|
291
|
+
int rc = yep_engine_run(eng, data, data_len, &sink);
|
|
292
|
+
if (rc != 0) {
|
|
293
|
+
const yep_error* ee = yep_engine_error(eng);
|
|
294
|
+
yep_err_code code = ee ? ee->code : YEP_ERR_UNEXPECTED;
|
|
295
|
+
if (ee) {
|
|
296
|
+
yep_error* tls = yep_error_tls();
|
|
297
|
+
*tls = *ee;
|
|
298
|
+
}
|
|
299
|
+
yep_dom_destroy(dom);
|
|
300
|
+
yep_engine_destroy(eng);
|
|
301
|
+
yep_free(sys, transcoded);
|
|
302
|
+
st = (code == YEP_ERR_MEMORY) ? YEPTRIS_ERROR_MEMORY
|
|
303
|
+
: (code == YEP_ERR_DEPTH) ? YEPTRIS_ERROR_DEPTH
|
|
304
|
+
: YEPTRIS_ERROR_PARSE;
|
|
305
|
+
goto fail;
|
|
306
|
+
}
|
|
307
|
+
/* Resolved tags, folded and escaped scalars live in the engine's
|
|
308
|
+
* finish pool; the document must own it or every such string would
|
|
309
|
+
* dangle at engine teardown (found by ASAN). */
|
|
310
|
+
yep_pool* finish = yep_engine_detach_pool(eng);
|
|
311
|
+
yep_engine_destroy(eng);
|
|
312
|
+
/* DOM strings are input-offsets or arena copies; nothing in the
|
|
313
|
+
* tree references the finish pool anymore — release it now */
|
|
314
|
+
|
|
315
|
+
/* Empty stream (no documents): NULL document with YEPTRIS_OK. */
|
|
316
|
+
if (dom->dcount == 0) {
|
|
317
|
+
yep_dom_destroy(dom);
|
|
318
|
+
yep_pool_destroy(finish);
|
|
319
|
+
yep_free(sys, transcoded);
|
|
320
|
+
if (status != NULL) {
|
|
321
|
+
*status = YEPTRIS_OK;
|
|
322
|
+
}
|
|
323
|
+
return NULL;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
yeptris_document* doc = yep_alloc(sys, sizeof(yeptris_document));
|
|
327
|
+
if (doc == NULL) {
|
|
328
|
+
yep_dom_destroy(dom);
|
|
329
|
+
yep_pool_destroy(finish);
|
|
330
|
+
yep_free(sys, transcoded);
|
|
331
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
332
|
+
goto fail;
|
|
333
|
+
}
|
|
334
|
+
doc->dom = dom;
|
|
335
|
+
doc->sys = sys;
|
|
336
|
+
doc->schema = (opts != NULL && opts->schema == YEPTRIS_SCHEMA_11_COMPAT)
|
|
337
|
+
? YEPTRIS_SCHEMA_11_COMPAT
|
|
338
|
+
: YEPTRIS_SCHEMA_12_CORE;
|
|
339
|
+
doc->transcoded = transcoded;
|
|
340
|
+
doc->transcoded_len = transcoded_len;
|
|
341
|
+
doc->input = buf;
|
|
342
|
+
/* the finish pool dies here: every DOM string is an input offset
|
|
343
|
+
* or an arena copy (dom_ev_str) — nothing references it */
|
|
344
|
+
yep_pool_destroy(finish);
|
|
345
|
+
doc->finish_pool = NULL;
|
|
346
|
+
return (YeptrisDocument)doc;
|
|
347
|
+
|
|
348
|
+
fail:
|
|
349
|
+
if (status != NULL) {
|
|
350
|
+
*status = st;
|
|
351
|
+
}
|
|
352
|
+
return NULL;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
YEPTRIS_API YeptrisDocument yeptris_parse_ex(const char* buf, size_t len,
|
|
356
|
+
const YeptrisParseOptions* opts,
|
|
357
|
+
YeptrisStatus* status) {
|
|
358
|
+
return parse_impl(buf, len, opts, 0, status);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
YEPTRIS_API void yeptris_document_free(YeptrisDocument handle) {
|
|
362
|
+
yeptris_document* doc = (yeptris_document*)handle;
|
|
363
|
+
if (doc == NULL) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
yep_dom_destroy(doc->dom);
|
|
367
|
+
yep_pool_destroy((yep_pool*)doc->finish_pool);
|
|
368
|
+
yep_free(doc->sys, doc->transcoded);
|
|
369
|
+
yep_free(doc->sys, doc);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
YEPTRIS_API size_t yeptris_document_count(YeptrisDocument handle) {
|
|
373
|
+
yeptris_document* doc = (yeptris_document*)handle;
|
|
374
|
+
return doc ? doc->dom->dcount : 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
yeptris_node* yep_handle_new(yeptris_document* doc, uint32_t id) {
|
|
378
|
+
yeptris_node* n = yep_hpool_alloc(doc->dom->handles, sizeof(yeptris_node), 16);
|
|
379
|
+
if (n == NULL) {
|
|
380
|
+
return NULL;
|
|
381
|
+
}
|
|
382
|
+
n->doc = doc;
|
|
383
|
+
n->id = id;
|
|
384
|
+
return n;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
static YeptrisNode node_new_handle(yeptris_document* doc, uint32_t id) {
|
|
388
|
+
return (YeptrisNode)yep_handle_new(doc, id);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
YEPTRIS_API YeptrisNode yeptris_document_root(YeptrisDocument handle, size_t index) {
|
|
392
|
+
yeptris_document* doc = (yeptris_document*)handle;
|
|
393
|
+
if (doc == NULL || index >= doc->dom->dcount) {
|
|
394
|
+
return NULL;
|
|
395
|
+
}
|
|
396
|
+
return node_new_handle(doc, doc->dom->docs[index]);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
static const yep_dnode* node_of(YeptrisNode handle) {
|
|
400
|
+
yeptris_node* n = (yeptris_node*)handle;
|
|
401
|
+
if (n == NULL || n->doc == NULL) {
|
|
402
|
+
return NULL;
|
|
403
|
+
}
|
|
404
|
+
return yep_dom_node(n->doc->dom, n->id);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
YEPTRIS_API uint32_t yeptris_node_id(YeptrisNode handle) {
|
|
408
|
+
yeptris_node* n = (yeptris_node*)handle;
|
|
409
|
+
return n == NULL ? UINT32_MAX : n->id;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
YEPTRIS_API YeptrisNodeKind yeptris_node_kind(YeptrisNode handle) {
|
|
413
|
+
const yep_dnode* n = node_of(handle);
|
|
414
|
+
if (n == NULL) {
|
|
415
|
+
return YEPTRIS_NODE_SCALAR;
|
|
416
|
+
}
|
|
417
|
+
switch (n->kind) {
|
|
418
|
+
case YEP_DOM_SEQUENCE:
|
|
419
|
+
return YEPTRIS_NODE_SEQUENCE;
|
|
420
|
+
case YEP_DOM_MAPPING:
|
|
421
|
+
return YEPTRIS_NODE_MAPPING;
|
|
422
|
+
case YEP_DOM_ALIAS:
|
|
423
|
+
return YEPTRIS_NODE_ALIAS;
|
|
424
|
+
default:
|
|
425
|
+
return YEPTRIS_NODE_SCALAR;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* Decodes a node's compact string through its document's regions. */
|
|
430
|
+
static yep_view node_view(const yeptris_node* h, yep_sview sv) {
|
|
431
|
+
return yep_dom_view(h->doc->dom, sv);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static const char* view_out(yep_view v, size_t* len) {
|
|
435
|
+
if (len != NULL) {
|
|
436
|
+
*len = v.len;
|
|
437
|
+
}
|
|
438
|
+
return v.len ? v.p : (v.p ? v.p : "");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
YEPTRIS_API const char* yeptris_node_value(YeptrisNode handle, size_t* len) {
|
|
442
|
+
const yep_dnode* n = node_of(handle);
|
|
443
|
+
if (n == NULL || (n->kind != YEP_DOM_SCALAR && n->kind != YEP_DOM_ALIAS)) {
|
|
444
|
+
if (len != NULL) {
|
|
445
|
+
*len = 0;
|
|
446
|
+
}
|
|
447
|
+
return NULL;
|
|
448
|
+
}
|
|
449
|
+
return view_out(node_view((yeptris_node*)handle, n->value), len);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
YEPTRIS_API YeptrisScalarStyle yeptris_node_style(YeptrisNode handle) {
|
|
453
|
+
const yep_dnode* n = node_of(handle);
|
|
454
|
+
return n ? (YeptrisScalarStyle)n->style : YEPTRIS_STYLE_ANY;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
YEPTRIS_API const char* yeptris_tag_uri(YeptrisTagId id) {
|
|
458
|
+
return yep_tag_uri((yep_tag_id)id);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
YEPTRIS_API YeptrisTagId yeptris_node_tag_id(YeptrisNode handle) {
|
|
462
|
+
const yep_dnode* n = node_of(handle);
|
|
463
|
+
if (n == NULL) {
|
|
464
|
+
return YEPTRIS_TAG_CUSTOM;
|
|
465
|
+
}
|
|
466
|
+
if (n->kind == YEPTRIS_NODE_SEQUENCE) {
|
|
467
|
+
return YEPTRIS_TAG_SEQ;
|
|
468
|
+
}
|
|
469
|
+
if (n->kind == YEPTRIS_NODE_MAPPING) {
|
|
470
|
+
return YEPTRIS_TAG_MAP;
|
|
471
|
+
}
|
|
472
|
+
return (YeptrisTagId)n->tag_id;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* strips '_' and ',' into dst (nul-terminated); returns length.
|
|
476
|
+
* The COMMON case (no separators) returns the borrowed view — no
|
|
477
|
+
* copy, no nul-termination needed by the in-place parsers. */
|
|
478
|
+
YEPTRIS_API YeptrisStatus yeptris_node_int(YeptrisNode handle, int64_t* out) {
|
|
479
|
+
/* conversion is the number kernel's alone (MECE: one fold, one
|
|
480
|
+
* fast path, one Psych-sexagesimal weight table) */
|
|
481
|
+
const yep_dnode* n = node_of(handle);
|
|
482
|
+
if (n == NULL || out == NULL) {
|
|
483
|
+
return YEPTRIS_ERROR_ARG;
|
|
484
|
+
}
|
|
485
|
+
if (n->tag_id != YEPTRIS_TAG_INT) {
|
|
486
|
+
return YEPTRIS_ERROR_PARSE;
|
|
487
|
+
}
|
|
488
|
+
yep_view v = node_view((yeptris_node*)handle, n->value);
|
|
489
|
+
return yep_num_i64((const char*)v.p, v.len, out) == 0 ? YEPTRIS_OK : YEPTRIS_ERROR_PARSE;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
YEPTRIS_API YeptrisStatus yeptris_node_float(YeptrisNode handle, double* out) {
|
|
493
|
+
const yep_dnode* n = node_of(handle);
|
|
494
|
+
if (n == NULL || out == NULL) {
|
|
495
|
+
return YEPTRIS_ERROR_ARG;
|
|
496
|
+
}
|
|
497
|
+
if (n->tag_id != YEPTRIS_TAG_FLOAT) {
|
|
498
|
+
return YEPTRIS_ERROR_PARSE;
|
|
499
|
+
}
|
|
500
|
+
yep_view v = node_view((yeptris_node*)handle, n->value);
|
|
501
|
+
return yep_num_f64((const char*)v.p, v.len, out) == 0 ? YEPTRIS_OK : YEPTRIS_ERROR_PARSE;
|
|
502
|
+
}
|
|
503
|
+
YEPTRIS_API YeptrisStatus yeptris_node_bool(YeptrisNode handle, int* out) {
|
|
504
|
+
const yep_dnode* n = node_of(handle);
|
|
505
|
+
if (n == NULL || out == NULL) {
|
|
506
|
+
return YEPTRIS_ERROR_ARG;
|
|
507
|
+
}
|
|
508
|
+
if (n->tag_id != YEPTRIS_TAG_BOOL) {
|
|
509
|
+
return YEPTRIS_ERROR_PARSE;
|
|
510
|
+
}
|
|
511
|
+
/* the resolver accepted it; true-set membership decides the value */
|
|
512
|
+
static const char* k_true[] = {"true", "True", "TRUE", "y", "Y", "yes",
|
|
513
|
+
"Yes", "YES", "on", "On", "ON"};
|
|
514
|
+
for (size_t i = 0; i < sizeof(k_true) / sizeof(k_true[0]); i++) {
|
|
515
|
+
size_t m = strlen(k_true[i]);
|
|
516
|
+
yep_view v = node_view((yeptris_node*)handle, n->value);
|
|
517
|
+
if (v.len == m && memcmp(v.p, k_true[i], m) == 0) {
|
|
518
|
+
*out = 1;
|
|
519
|
+
return YEPTRIS_OK;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
*out = 0;
|
|
523
|
+
return YEPTRIS_OK;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
YEPTRIS_API const char* yeptris_node_tag(YeptrisNode handle, size_t* len) {
|
|
527
|
+
const yep_dnode* n = node_of(handle);
|
|
528
|
+
if (n == NULL || n->tag.len == 0) {
|
|
529
|
+
if (len != NULL) {
|
|
530
|
+
*len = 0;
|
|
531
|
+
}
|
|
532
|
+
return n ? NULL : NULL;
|
|
533
|
+
}
|
|
534
|
+
return view_out(node_view((yeptris_node*)handle, n->tag), len);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
YEPTRIS_API const char* yeptris_node_anchor(YeptrisNode handle, size_t* len) {
|
|
538
|
+
const yep_dnode* n = node_of(handle);
|
|
539
|
+
if (n == NULL || n->anchor.len == 0) {
|
|
540
|
+
if (len != NULL) {
|
|
541
|
+
*len = 0;
|
|
542
|
+
}
|
|
543
|
+
return NULL;
|
|
544
|
+
}
|
|
545
|
+
return view_out(node_view((yeptris_node*)handle, n->anchor), len);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
static YeptrisNode wrap(yeptris_node* base, uint32_t id) {
|
|
549
|
+
if (base == NULL || base->doc == NULL) {
|
|
550
|
+
return NULL;
|
|
551
|
+
}
|
|
552
|
+
/* Handles live in the document's thread-safe arena: one
|
|
553
|
+
* document_free reclaims every handle ever handed out (zero-leak
|
|
554
|
+
* contract) while read-only sharing stays safe (TODO.impl/19) */
|
|
555
|
+
yeptris_node* n = yep_hpool_alloc(base->doc->dom->handles, sizeof(yeptris_node), 16);
|
|
556
|
+
if (n == NULL) {
|
|
557
|
+
return NULL;
|
|
558
|
+
}
|
|
559
|
+
n->doc = base->doc;
|
|
560
|
+
n->id = id;
|
|
561
|
+
return (YeptrisNode)n;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
YEPTRIS_API YeptrisNode yeptris_node_alias_target(YeptrisNode handle) {
|
|
565
|
+
const yep_dnode* n = node_of(handle);
|
|
566
|
+
if (n == NULL || n->kind != YEP_DOM_ALIAS || n->target == UINT32_MAX) {
|
|
567
|
+
return NULL;
|
|
568
|
+
}
|
|
569
|
+
return wrap((yeptris_node*)handle, n->target);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
YEPTRIS_API size_t yeptris_node_seq_count(YeptrisNode handle) {
|
|
573
|
+
const yep_dnode* n = node_of(handle);
|
|
574
|
+
return (n && n->kind == YEP_DOM_SEQUENCE) ? n->count : 0;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
YEPTRIS_API YeptrisNode yeptris_node_seq_at(YeptrisNode handle, size_t index) {
|
|
578
|
+
const yep_dnode* n = node_of(handle);
|
|
579
|
+
if (n == NULL || n->kind != YEP_DOM_SEQUENCE || index >= n->count) {
|
|
580
|
+
return NULL;
|
|
581
|
+
}
|
|
582
|
+
const yep_dom* dom = ((yeptris_node*)handle)->doc->dom;
|
|
583
|
+
uint32_t id = n->first_child;
|
|
584
|
+
for (size_t i = 0; i < index && id != UINT32_MAX; i++) {
|
|
585
|
+
const yep_dnode* cur = yep_dom_node(dom, id);
|
|
586
|
+
id = cur ? cur->next_sibling : UINT32_MAX;
|
|
587
|
+
}
|
|
588
|
+
return wrap((yeptris_node*)handle, id);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
YEPTRIS_API size_t yeptris_node_map_count(YeptrisNode handle) {
|
|
592
|
+
const yep_dnode* n = node_of(handle);
|
|
593
|
+
return (n && n->kind == YEP_DOM_MAPPING) ? n->count / 2 : 0;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
YEPTRIS_API int yeptris_node_map_at(YeptrisNode handle, size_t index, YeptrisNode* key,
|
|
597
|
+
YeptrisNode* value) {
|
|
598
|
+
const yep_dnode* n = node_of(handle);
|
|
599
|
+
if (n == NULL || n->kind != YEPTRIS_NODE_MAPPING || index >= n->count / 2) {
|
|
600
|
+
return -1;
|
|
601
|
+
}
|
|
602
|
+
yeptris_node* h = (yeptris_node*)handle;
|
|
603
|
+
const yep_dom* d = h->doc->dom;
|
|
604
|
+
uint32_t child = n->first_child;
|
|
605
|
+
for (size_t i = 0; i < index * 2; i++) {
|
|
606
|
+
child = d->nodes[child].next_sibling; /* pairs are key,value,… */
|
|
607
|
+
}
|
|
608
|
+
if (key != NULL) {
|
|
609
|
+
*key = wrap(h, child);
|
|
610
|
+
}
|
|
611
|
+
if (value != NULL) {
|
|
612
|
+
*value = wrap(h, d->nodes[child].next_sibling);
|
|
613
|
+
}
|
|
614
|
+
return 0;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
YEPTRIS_API YeptrisNode yeptris_node_map_get(YeptrisNode handle, const char* key, size_t key_len) {
|
|
618
|
+
const yep_dnode* n = node_of(handle);
|
|
619
|
+
if (n == NULL || n->kind != YEP_DOM_MAPPING || key == NULL) {
|
|
620
|
+
return NULL;
|
|
621
|
+
}
|
|
622
|
+
yep_dom* dom = ((yeptris_node*)handle)->doc->dom;
|
|
623
|
+
/* O(1) through the lazy index (first use builds it); the scan
|
|
624
|
+
* below is the build-failure fallback */
|
|
625
|
+
uint32_t hit =
|
|
626
|
+
yep_midx_lookup(dom, ((yeptris_node*)handle)->id, (yep_view){key, (uint32_t)key_len});
|
|
627
|
+
if (hit != UINT32_MAX) {
|
|
628
|
+
const yep_dnode* kn = yep_dom_node(dom, hit);
|
|
629
|
+
if (kn != NULL) {
|
|
630
|
+
return wrap((yeptris_node*)handle, kn->next_sibling);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
uint32_t id = n->first_child;
|
|
634
|
+
int want_key = 1;
|
|
635
|
+
while (id != UINT32_MAX) {
|
|
636
|
+
const yep_dnode* cur = yep_dom_node(dom, id);
|
|
637
|
+
if (cur == NULL) {
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
if (want_key && cur->kind == YEP_DOM_SCALAR) {
|
|
641
|
+
yep_view kv = {key, (uint32_t)key_len};
|
|
642
|
+
if (yep_view_eq(yep_dom_view(dom, cur->value), kv)) {
|
|
643
|
+
return wrap((yeptris_node*)handle, cur->next_sibling);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
want_key = !want_key;
|
|
647
|
+
id = cur->next_sibling;
|
|
648
|
+
}
|
|
649
|
+
return NULL;
|
|
650
|
+
}
|