yeptris 0.6.58.1-arm-linux-eabihf → 0.6.59.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/document.rb +15 -0
- data/lib/yeptris/ffi.rb +93 -0
- data/lib/yeptris/psych/visitors.rb +16 -6
- data/lib/yeptris/psych.rb +11 -5
- data/lib/yeptris/validation.rb +320 -0
- data/lib/yeptris/yaml.rb +67 -6
- data/lib/yeptris.rb +2 -1
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/CMakeLists.txt +1 -0
- data/vendor/libyeptris/src/include/yeptris/validate.h +50 -7
- data/vendor/libyeptris/src/yeptris/parse.c +8 -3
- data/vendor/libyeptris/src/yeptris/validate/compile.c +270 -0
- data/vendor/libyeptris/src/yeptris/validate/internal.h +34 -5
- data/vendor/libyeptris/src/yeptris/validate/jsonschema.c +420 -50
- data/vendor/libyeptris/src/yeptris/validate/regexp.c +207 -45
- data/vendor/libyeptris/src/yeptris/validate/validate.c +55 -94
- metadata +3 -1
data/lib/yeptris.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Yeptris
|
|
4
4
|
# The gem's version lives in the parent namespace's file — the last
|
|
5
5
|
# internal require (yeptris/version) retired with it.
|
|
6
|
-
VERSION = "0.6.
|
|
6
|
+
VERSION = "0.6.59.1".freeze
|
|
7
7
|
|
|
8
8
|
# Informational notices are OPT-IN (yeptris-ruby#217): consumers
|
|
9
9
|
# embed yeptris inside CLIs that assert clean stderr, and a
|
|
@@ -54,6 +54,7 @@ module Yeptris
|
|
|
54
54
|
autoload :ValueML, "yeptris/valueml"
|
|
55
55
|
autoload :Psych, "yeptris/psych"
|
|
56
56
|
autoload :Schema, "yeptris/schema"
|
|
57
|
+
autoload :Validation, "yeptris/validation"
|
|
57
58
|
autoload :CBOR, "yeptris/cbor"
|
|
58
59
|
end
|
|
59
60
|
|
data/libyeptris.so
CHANGED
|
Binary file
|
|
@@ -76,6 +76,14 @@ typedef enum {
|
|
|
76
76
|
YEPTRIS_DIAG_LIMIT = 26, /* report truncated (the sentinel
|
|
77
77
|
* rides the LAST slot; more
|
|
78
78
|
* diagnostics were dropped) */
|
|
79
|
+
/* the 2020-12 sweep (#885 item 2) */
|
|
80
|
+
YEPTRIS_DIAG_MULTIPLE_OF = 27,
|
|
81
|
+
YEPTRIS_DIAG_MIN_PROPERTIES = 28,
|
|
82
|
+
YEPTRIS_DIAG_MAX_PROPERTIES = 29,
|
|
83
|
+
YEPTRIS_DIAG_CONTAINS = 30,
|
|
84
|
+
YEPTRIS_DIAG_MIN_CONTAINS = 31,
|
|
85
|
+
YEPTRIS_DIAG_MAX_CONTAINS = 32,
|
|
86
|
+
YEPTRIS_DIAG_DEPENDENT_REQUIRED = 33,
|
|
79
87
|
} YeptrisDiagCode;
|
|
80
88
|
|
|
81
89
|
/* Bounded diagnostic text; always NUL-terminated (over-long paths and
|
|
@@ -127,13 +135,17 @@ YEPTRIS_API YeptrisStatus yeptris_validate(const char* buf, size_t len, YeptrisS
|
|
|
127
135
|
YEPTRIS_API YeptrisStatus yeptris_validate_document(YeptrisDocument doc, YeptrisSchema schema,
|
|
128
136
|
yeptris_validate_report** out);
|
|
129
137
|
|
|
130
|
-
/* JSON Schema draft 2020-12 subset (#882 slice 2). `schema`
|
|
131
|
-
* with the same engine (JSON is YAML flow); document 0 of the
|
|
132
|
-
* is the schema. Keyword subset: type, enum, const, pattern,
|
|
133
|
-
* maximum/exclusiveMinimum/exclusiveMaximum (numeric, 2020-12
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
138
|
+
/* JSON Schema draft 2020-12 subset (#882 slice 2; #885 item 2). `schema`
|
|
139
|
+
* is parsed with the same engine (JSON is YAML flow); document 0 of the
|
|
140
|
+
* stream is the schema. Keyword subset: type, enum, const, pattern,
|
|
141
|
+
* minimum/maximum/exclusiveMinimum/exclusiveMaximum (numeric, 2020-12
|
|
142
|
+
* form), multipleOf (libm-free quotient test), minLength/maxLength
|
|
143
|
+
* (code points), minItems/maxItems, uniqueItems, contains (+ the
|
|
144
|
+
* minContains/maxContains qualifiers), items (+ prefixItems; the
|
|
145
|
+
* draft-07 array form rides as prefixItems), properties, required,
|
|
146
|
+
* patternProperties, propertyNames, minProperties/maxProperties,
|
|
147
|
+
* additionalProperties (bool or schema; excludes patternProperties
|
|
148
|
+
* matches), dependentRequired, dependentSchemas, if/then/else,
|
|
137
149
|
* allOf/anyOf/oneOf/not, $ref (LOCAL "#/..." pointers only — $defs and
|
|
138
150
|
* definitions both resolve). Boolean schemas (`true`/`false`) apply.
|
|
139
151
|
* Unknown keywords and known keywords with unreadable values are
|
|
@@ -150,6 +162,37 @@ YEPTRIS_API YeptrisStatus yeptris_json_schema_validate(const char* schema, size_
|
|
|
150
162
|
const char* buf, size_t len,
|
|
151
163
|
yeptris_validate_report** out);
|
|
152
164
|
|
|
165
|
+
/* The COMPILED SCHEMA — the leptris-XSD economy (#885 item 3): parse
|
|
166
|
+
* the schema ONCE, validate MANY instance streams against the retained
|
|
167
|
+
* model. The handle owns everything it reads: the schema bytes (copied
|
|
168
|
+
* — the retained DOM's views borrow them), the parsed document, and
|
|
169
|
+
* the pattern kernel's compiled programs (the XSD content-model
|
|
170
|
+
* analog: `(a+)+$` compiles once here, not once per validated string
|
|
171
|
+
* the way the one-shot face pays). yeptris_schema_compile returns NULL
|
|
172
|
+
* on failure; yeptris_last_error() carries the why (bad arguments,
|
|
173
|
+
* bytes that hold no document, a schema that does not parse,
|
|
174
|
+
* allocation failure).
|
|
175
|
+
*
|
|
176
|
+
* yeptris_schema_validate keeps yeptris_json_schema_validate's exact
|
|
177
|
+
* status/report contract; diagnostics enumerate every miss across
|
|
178
|
+
* every document of the instance stream. yeptris_schema_free releases
|
|
179
|
+
* the handle and everything it owns in ONE call (no refcounting) —
|
|
180
|
+
* afterwards the handle is DEAD: passing it anywhere is undefined,
|
|
181
|
+
* exactly like a freed document. NULL is accepted (a no-op).
|
|
182
|
+
*
|
|
183
|
+
* Thread law: one schema handle per thread, like documents — the
|
|
184
|
+
* handle is immutable after compile (the pattern programs are built at
|
|
185
|
+
* compile time), so read-only sharing across threads is safe; reports
|
|
186
|
+
* are per-call and caller-owned. */
|
|
187
|
+
typedef struct yeptris_schema yeptris_schema;
|
|
188
|
+
|
|
189
|
+
YEPTRIS_API yeptris_schema* yeptris_schema_compile(const char* bytes, size_t n);
|
|
190
|
+
|
|
191
|
+
YEPTRIS_API YeptrisStatus yeptris_schema_validate(yeptris_schema* s, const char* buf, size_t len,
|
|
192
|
+
yeptris_validate_report** out);
|
|
193
|
+
|
|
194
|
+
YEPTRIS_API void yeptris_schema_free(yeptris_schema* s);
|
|
195
|
+
|
|
153
196
|
#ifdef __cplusplus
|
|
154
197
|
}
|
|
155
198
|
#endif
|
|
@@ -236,10 +236,15 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
236
236
|
goto fail;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/* a NULL/0-byte stream is a valid empty input: parse it through a
|
|
240
|
+
* non-null base so the BOM arithmetic never computes NULL+0
|
|
241
|
+
* (clang's UBSan flags the zero-offset-null form; gcc's does not —
|
|
242
|
+
* the schema-econ BadInputs pin surfaced it on the macOS arm64 leg) */
|
|
243
|
+
const char* src = buf != NULL ? buf : "";
|
|
239
244
|
const yep_allocator* sys = yep_system_allocator();
|
|
240
245
|
|
|
241
246
|
/* Encoding front-end: BOM sniff; borrow UTF-8, transcode the rest. */
|
|
242
|
-
const char* data =
|
|
247
|
+
const char* data = src;
|
|
243
248
|
size_t data_len = len;
|
|
244
249
|
unsigned char* transcoded = NULL;
|
|
245
250
|
size_t transcoded_len = 0;
|
|
@@ -256,8 +261,8 @@ static YeptrisDocument parse_impl(const char* buf, size_t len, const YeptrisPars
|
|
|
256
261
|
goto engine_enter;
|
|
257
262
|
}
|
|
258
263
|
|
|
259
|
-
size_t bom = yep_bom_sniff((const unsigned char*)
|
|
260
|
-
data =
|
|
264
|
+
size_t bom = yep_bom_sniff((const unsigned char*)src, len, &enc);
|
|
265
|
+
data = src + bom;
|
|
261
266
|
data_len = len - bom;
|
|
262
267
|
|
|
263
268
|
if (enc == YEP_ENC_UTF16LE || enc == YEP_ENC_UTF16BE || enc == YEP_ENC_UTF32LE ||
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/* compile.c — the compiled-schema economy (#885 item 3).
|
|
2
|
+
*
|
|
3
|
+
* The leptris-XSD shape (~/src/leptris/leptris/src/leptris/xsd/):
|
|
4
|
+
* parse the schema ONCE, retain the DOM and the derived state, validate
|
|
5
|
+
* many instance streams against the retained model. The handle owns
|
|
6
|
+
* everything it reads — the schema bytes (copied: the DOM's scalar
|
|
7
|
+
* views borrow them), the parsed document (its DOM, pools and finish
|
|
8
|
+
* pool die with yeptris_document_free), and the compiled pattern
|
|
9
|
+
* programs (the XSD content-model analog: `(a+)+$` compiles once at
|
|
10
|
+
* compile time, not once per validated string). One free call releases
|
|
11
|
+
* it all — no refcounting, the document-ownership law.
|
|
12
|
+
*
|
|
13
|
+
* The pattern programs are populated EAGERLY by a DFS over the schema
|
|
14
|
+
* tree (`pattern` values and `patternProperties` keys — the kernel's
|
|
15
|
+
* only two consumers), so the handle is immutable after compile and
|
|
16
|
+
* `yeptris_schema_validate` is a pure reader: one schema handle per
|
|
17
|
+
* thread like documents; read-only sharing across threads is safe.
|
|
18
|
+
* An invalid pattern is simply not retained — every use then recompiles
|
|
19
|
+
* to the one-shot's -1 (ignored keyword), never a guess.
|
|
20
|
+
*
|
|
21
|
+
* The one-shot face (yeptris_json_schema_validate) routes through the
|
|
22
|
+
* same compile+validate machinery internally: identical parse, walk
|
|
23
|
+
* and report code paths, so its observable statuses and byte-for-byte
|
|
24
|
+
* verdicts are preserved (the 1298-case suite port is the referee).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
#include <string.h>
|
|
28
|
+
|
|
29
|
+
#include <yeptris/dom.h>
|
|
30
|
+
#include <yeptris/validate.h>
|
|
31
|
+
|
|
32
|
+
#include "../common/error.h"
|
|
33
|
+
#include "../doc.h"
|
|
34
|
+
#include "../memory/allocator.h"
|
|
35
|
+
#include "../tape_in.h"
|
|
36
|
+
#include "internal.h"
|
|
37
|
+
|
|
38
|
+
/* The compiled schema. Opaque at the face (validate.h); everything it
|
|
39
|
+
* reads dies with it. */
|
|
40
|
+
struct yeptris_schema {
|
|
41
|
+
char* bytes; /* owned: the schema bytes (the DOM's input views borrow them) */
|
|
42
|
+
yeptris_document* doc; /* owned: the parsed schema document */
|
|
43
|
+
const yep_dom* dom; /* borrowed from doc (yep_doc_dom) */
|
|
44
|
+
uint32_t sroot; /* the schema document's root (docs[0]) */
|
|
45
|
+
yep_re_cache* cache; /* owned: pattern source -> compiled NFA; immutable after compile */
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/* ---- the pattern collection (compile-time, eager) ----------------------- */
|
|
49
|
+
|
|
50
|
+
static int collect_patterns(yep_re_cache* t, const yep_dom* d, uint32_t id, int depth);
|
|
51
|
+
|
|
52
|
+
/* Collects every pattern source of one mapping pair set. 0 ok, -1 OOM. */
|
|
53
|
+
static int collect_map(yep_re_cache* t, const yep_dom* d, uint32_t map_id, int depth) {
|
|
54
|
+
const yep_dnode* m = yep_dom_node(d, map_id);
|
|
55
|
+
uint32_t k = m->first_child;
|
|
56
|
+
while (k != UINT32_MAX) {
|
|
57
|
+
uint32_t v = yep_dom_node(d, k) ? yep_dom_node(d, k)->next_sibling : UINT32_MAX;
|
|
58
|
+
const yep_dnode* kn = yep_dom_node(d, k);
|
|
59
|
+
const yep_dnode* vn = v != UINT32_MAX ? yep_dom_node(d, v) : NULL;
|
|
60
|
+
if (kn != NULL && kn->kind == YEP_DOM_SCALAR) {
|
|
61
|
+
yep_view kv = yep_dom_view(d, kn->value);
|
|
62
|
+
/* the keyword names, matched by bytes like the walk's kw_get */
|
|
63
|
+
if (kv.len == 7 && memcmp(kv.p, "pattern", 7) == 0) {
|
|
64
|
+
/* "pattern": the value is the pattern source (a string
|
|
65
|
+
* scalar, schema_word's exact read) */
|
|
66
|
+
if (vn != NULL && vn->kind == YEP_DOM_SCALAR &&
|
|
67
|
+
yep_validate_scalar_tag(vn) == YEPTRIS_TAG_STR) {
|
|
68
|
+
yep_view vv = yep_dom_view(d, vn->value);
|
|
69
|
+
if (vv.p != NULL && yep_re_cache_add(t, vv.p, vv.len) < 0) {
|
|
70
|
+
return -1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else if (kv.len == 17 && memcmp(kv.p, "patternProperties", 17) == 0) {
|
|
74
|
+
/* "patternProperties": its KEYS are the pattern sources */
|
|
75
|
+
if (vn != NULL && vn->kind == YEP_DOM_MAPPING) {
|
|
76
|
+
uint32_t pk = vn->first_child;
|
|
77
|
+
while (pk != UINT32_MAX) {
|
|
78
|
+
uint32_t pv =
|
|
79
|
+
yep_dom_node(d, pk) ? yep_dom_node(d, pk)->next_sibling : UINT32_MAX;
|
|
80
|
+
const yep_dnode* pkn = yep_dom_node(d, pk);
|
|
81
|
+
if (pkn != NULL && pkn->kind == YEP_DOM_SCALAR &&
|
|
82
|
+
yep_validate_scalar_tag(pkn) == YEPTRIS_TAG_STR) {
|
|
83
|
+
yep_view pw = yep_dom_view(d, pkn->value);
|
|
84
|
+
if (pw.p != NULL && yep_re_cache_add(t, pw.p, pw.len) < 0) {
|
|
85
|
+
return -1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
pk = pv;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/* the rest of the schema rides the values (keys are scalars —
|
|
94
|
+
* nothing under them; the walk reaches nodes the same way) */
|
|
95
|
+
if (v != UINT32_MAX && collect_patterns(t, d, v, depth + 1) != 0) {
|
|
96
|
+
return -1;
|
|
97
|
+
}
|
|
98
|
+
k = v != UINT32_MAX && yep_dom_node(d, v) ? yep_dom_node(d, v)->next_sibling : UINT32_MAX;
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Depth-bounded DFS gathering every pattern the walk can reach. 0 ok,
|
|
104
|
+
* -1 OOM. Aliases are skipped: the walk sees an alias node as opaque
|
|
105
|
+
* (never a mapping), so no reachable pattern hides behind one. */
|
|
106
|
+
static int collect_patterns(yep_re_cache* t, const yep_dom* d, uint32_t id, int depth) {
|
|
107
|
+
if (depth > YEP_DOM_MAX_DEPTH) {
|
|
108
|
+
return 0; /* the walk's own guard: deeper nodes are unreachable to it too */
|
|
109
|
+
}
|
|
110
|
+
const yep_dnode* n = yep_dom_node(d, id);
|
|
111
|
+
if (n == NULL) {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
if (n->kind == YEP_DOM_MAPPING) {
|
|
115
|
+
return collect_map(t, d, id, depth);
|
|
116
|
+
}
|
|
117
|
+
if (n->kind == YEP_DOM_SEQUENCE) {
|
|
118
|
+
uint32_t ch = n->first_child;
|
|
119
|
+
while (ch != UINT32_MAX) {
|
|
120
|
+
uint32_t next = yep_dom_node(d, ch) ? yep_dom_node(d, ch)->next_sibling : UINT32_MAX;
|
|
121
|
+
if (collect_patterns(t, d, ch, depth + 1) != 0) {
|
|
122
|
+
return -1;
|
|
123
|
+
}
|
|
124
|
+
ch = next;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* ---- the seam with the explicit status (internal.h) --------------------- */
|
|
131
|
+
|
|
132
|
+
yeptris_schema* yep_schema_compile_st(const char* bytes, size_t n, YeptrisStatus* st) {
|
|
133
|
+
*st = YEPTRIS_OK;
|
|
134
|
+
if (bytes == NULL && n != 0) {
|
|
135
|
+
*st = YEPTRIS_ERROR_ARG;
|
|
136
|
+
return NULL;
|
|
137
|
+
}
|
|
138
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
139
|
+
|
|
140
|
+
/* the schema bytes are COPIED: the DOM's scalar views borrow its
|
|
141
|
+
* input, and the handle outlives this call */
|
|
142
|
+
char* copy = NULL;
|
|
143
|
+
if (n > 0) {
|
|
144
|
+
copy = yep_alloc(sys, n);
|
|
145
|
+
if (copy == NULL) {
|
|
146
|
+
*st = YEPTRIS_ERROR_MEMORY;
|
|
147
|
+
return NULL;
|
|
148
|
+
}
|
|
149
|
+
memcpy(copy, bytes, n);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* document 0 of the stream is the schema (the one-shot's exact rule) */
|
|
153
|
+
YeptrisParseOptions opts;
|
|
154
|
+
memset(&opts, 0, sizeof(opts)); /* core12: the YAML->JSON data model */
|
|
155
|
+
YeptrisStatus ps = YEPTRIS_OK;
|
|
156
|
+
yeptris_document* doc = (yeptris_document*)yeptris_parse_eager_ex(copy, n, &opts, &ps);
|
|
157
|
+
if (doc == NULL) {
|
|
158
|
+
yep_free(sys, copy);
|
|
159
|
+
if (ps == YEPTRIS_OK) { /* empty bytes: the stream holds no document */
|
|
160
|
+
yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
|
|
161
|
+
"the schema bytes hold no document");
|
|
162
|
+
*st = YEPTRIS_ERROR_ARG;
|
|
163
|
+
} else {
|
|
164
|
+
*st = ps; /* the schema did not parse — the engine's own status */
|
|
165
|
+
}
|
|
166
|
+
return NULL;
|
|
167
|
+
}
|
|
168
|
+
const yep_dom* dom = yep_doc_dom(doc);
|
|
169
|
+
if (dom == NULL || dom->dcount == 0) {
|
|
170
|
+
yeptris_document_free(doc);
|
|
171
|
+
yep_free(sys, copy);
|
|
172
|
+
yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
|
|
173
|
+
"the schema bytes hold no document");
|
|
174
|
+
*st = YEPTRIS_ERROR_ARG;
|
|
175
|
+
return NULL;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* the pattern programs: compiled once, here (a failure to retain is
|
|
179
|
+
* an allocation failure — loud, like every constructor's OOM) */
|
|
180
|
+
yep_re_cache* cache = yep_re_cache_new();
|
|
181
|
+
if (cache == NULL || collect_patterns(cache, dom, dom->docs[0], 0) != 0) {
|
|
182
|
+
yep_re_cache_free(cache);
|
|
183
|
+
yeptris_document_free(doc);
|
|
184
|
+
yep_free(sys, copy);
|
|
185
|
+
yep_error_set(yep_error_tls(), YEP_ERR_MEMORY, 0, 0, 0,
|
|
186
|
+
"the schema's pattern programs failed to compile");
|
|
187
|
+
*st = YEPTRIS_ERROR_MEMORY;
|
|
188
|
+
return NULL;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
yeptris_schema* s = yep_alloc(sys, sizeof(*s));
|
|
192
|
+
if (s == NULL) {
|
|
193
|
+
yep_re_cache_free(cache);
|
|
194
|
+
yeptris_document_free(doc);
|
|
195
|
+
yep_free(sys, copy);
|
|
196
|
+
*st = YEPTRIS_ERROR_MEMORY;
|
|
197
|
+
return NULL;
|
|
198
|
+
}
|
|
199
|
+
s->bytes = copy;
|
|
200
|
+
s->doc = doc;
|
|
201
|
+
s->dom = dom;
|
|
202
|
+
s->sroot = dom->docs[0];
|
|
203
|
+
s->cache = cache;
|
|
204
|
+
return s;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* ---- the public face (validate.h) --------------------------------------- */
|
|
208
|
+
|
|
209
|
+
YEPTRIS_API yeptris_schema* yeptris_schema_compile(const char* bytes, size_t n) {
|
|
210
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
211
|
+
return yep_schema_compile_st(bytes, n, &st); /* the error channel carries the why */
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
YEPTRIS_API void yeptris_schema_free(yeptris_schema* s) {
|
|
215
|
+
if (s == NULL) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const yep_allocator* sys = yep_system_allocator();
|
|
219
|
+
yep_re_cache_free(s->cache);
|
|
220
|
+
yeptris_document_free(s->doc); /* the DOM and every pool under it */
|
|
221
|
+
yep_free(sys, s->bytes); /* the views' home: freed after its readers */
|
|
222
|
+
yep_free(sys, s);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
YEPTRIS_API YeptrisStatus yeptris_schema_validate(yeptris_schema* s, const char* buf, size_t len,
|
|
226
|
+
yeptris_validate_report** out) {
|
|
227
|
+
if (out == NULL || s == NULL || (buf == NULL && len != 0)) {
|
|
228
|
+
return YEPTRIS_ERROR_ARG;
|
|
229
|
+
}
|
|
230
|
+
*out = NULL;
|
|
231
|
+
|
|
232
|
+
/* the instance stream (core12, the one-shot's exact parse) */
|
|
233
|
+
YeptrisParseOptions opts;
|
|
234
|
+
memset(&opts, 0, sizeof(opts));
|
|
235
|
+
YeptrisStatus st = YEPTRIS_OK;
|
|
236
|
+
YeptrisDocument idoc = yeptris_parse_eager_ex(buf, len, &opts, &st);
|
|
237
|
+
if (idoc == NULL) {
|
|
238
|
+
if (st == YEPTRIS_OK) {
|
|
239
|
+
/* an empty instance stream: nothing to invalidate */
|
|
240
|
+
yeptris_validate_report* r = yep_validate_report_new();
|
|
241
|
+
if (r == NULL) {
|
|
242
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
243
|
+
}
|
|
244
|
+
*out = r;
|
|
245
|
+
return YEPTRIS_OK;
|
|
246
|
+
}
|
|
247
|
+
return st; /* the parse's own error */
|
|
248
|
+
}
|
|
249
|
+
yep_dom* idom = yep_doc_dom((yeptris_document*)idoc);
|
|
250
|
+
if (idom == NULL) {
|
|
251
|
+
yeptris_document_free(idoc);
|
|
252
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
yeptris_validate_report* r = yep_validate_report_new();
|
|
256
|
+
if (r == NULL) {
|
|
257
|
+
yeptris_document_free(idoc);
|
|
258
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
259
|
+
}
|
|
260
|
+
/* the handle is a pure reader here (its cache is immutable): the
|
|
261
|
+
* same walk, the same report shape, the one-shot's verdicts */
|
|
262
|
+
int rc = yep_jsonschema_run(s->dom, s->sroot, idom, idom->docs, idom->dcount, r, s->cache);
|
|
263
|
+
yeptris_document_free(idoc);
|
|
264
|
+
if (rc != 0) {
|
|
265
|
+
yeptris_validate_report_free(r);
|
|
266
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
267
|
+
}
|
|
268
|
+
*out = r;
|
|
269
|
+
return r->count > 0 ? YEPTRIS_ERROR_SCHEMA : YEPTRIS_OK;
|
|
270
|
+
}
|
|
@@ -72,10 +72,24 @@ void yep_validate_path_pop(yep_vctx* c, size_t mark);
|
|
|
72
72
|
* literals, '.', classes, groups, alternation, the greedy/lazy
|
|
73
73
|
* quantifiers, anchors, \d\w\s\D\W\S\b\B and \xHH. Byte-oriented
|
|
74
74
|
* (ASCII classes; UTF-8 literals match byte-wise). A Thompson NFA —
|
|
75
|
-
* O(len * m), no backtracking blowup
|
|
76
|
-
* budget are invalid. 1 match, 0 no match,
|
|
75
|
+
* O(len * m), no backtracking blowup, no recursion over the subject.
|
|
76
|
+
* Patterns over the program budget are invalid. 1 match, 0 no match,
|
|
77
|
+
* -1 invalid pattern. */
|
|
77
78
|
int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_len);
|
|
78
79
|
|
|
80
|
+
/* The retained-program cache behind the compiled schema (#885 item 3):
|
|
81
|
+
* pattern source -> compiled NFA, populated at compile time, immutable
|
|
82
|
+
* afterwards (read-only sharing, the document law). A miss — a NULL
|
|
83
|
+
* cache included — compiles fresh with yep_re_search's exact verdicts;
|
|
84
|
+
* cache_add returns 1 stored, 0 not retained (invalid pattern, empty,
|
|
85
|
+
* table full — misses recompile), -1 allocation failure (loud). */
|
|
86
|
+
typedef struct yep_re_cache yep_re_cache;
|
|
87
|
+
yep_re_cache* yep_re_cache_new(void);
|
|
88
|
+
void yep_re_cache_free(yep_re_cache* t);
|
|
89
|
+
int yep_re_cache_add(yep_re_cache* t, const char* pat, uint32_t plen);
|
|
90
|
+
int yep_re_cache_search(const yep_re_cache* t, const char* pat, uint32_t plen, const char* s,
|
|
91
|
+
uint32_t s_len);
|
|
92
|
+
|
|
79
93
|
/* Canonical scalar tag of a DOM node (explicit tag, then the parse
|
|
80
94
|
* resolver's plain verdict, then str): the value model both walks
|
|
81
95
|
* compare under. */
|
|
@@ -90,10 +104,25 @@ int yep_validate_node_equal(const yep_dom* da, uint32_t a, const yep_dom* db, ui
|
|
|
90
104
|
int depth);
|
|
91
105
|
|
|
92
106
|
/* jsonschema.c's driver: applies the schema tree (its own DOM) to
|
|
93
|
-
* every instance document root, emitting into `rep`.
|
|
94
|
-
*
|
|
107
|
+
* every instance document root, emitting into `rep`. `cache` is the
|
|
108
|
+
* compiled schema's retained pattern programs (NULL: none — the
|
|
109
|
+
* one-shot face). Returns 0, -1 on OOM. */
|
|
95
110
|
int yep_jsonschema_run(const yep_dom* schema_dom, uint32_t schema_root, const yep_dom* instance_dom,
|
|
96
|
-
const uint32_t* doc_roots, uint32_t doc_count, yeptris_validate_report* rep
|
|
111
|
+
const uint32_t* doc_roots, uint32_t doc_count, yeptris_validate_report* rep,
|
|
112
|
+
const yep_re_cache* cache);
|
|
113
|
+
|
|
114
|
+
/* The report constructor (validate.c owns the report machinery): a
|
|
115
|
+
* fresh empty report, NULL on allocation failure. */
|
|
116
|
+
yeptris_validate_report* yep_validate_report_new(void);
|
|
117
|
+
|
|
118
|
+
/* compile.c's seam (#885 item 3): the compiled-schema constructor with
|
|
119
|
+
* an explicit status — the one-shot face preserves its exact observable
|
|
120
|
+
* statuses through it. NULL on failure with *st = YEPTRIS_ERROR_ARG
|
|
121
|
+
* (NULL bytes with length, or bytes that hold no document; the thread
|
|
122
|
+
* error channel carries the message), the failed parse's status, or
|
|
123
|
+
* YEPTRIS_ERROR_MEMORY. */
|
|
124
|
+
struct yeptris_schema;
|
|
125
|
+
yeptris_schema* yep_schema_compile_st(const char* bytes, size_t n, YeptrisStatus* st);
|
|
97
126
|
|
|
98
127
|
#ifdef __cplusplus
|
|
99
128
|
}
|