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
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
/* jsonschema.c — the JSON Schema draft 2020-12 keyword subset
|
|
2
|
-
* (#882 slice 2).
|
|
2
|
+
* (#882 slice 2; #885 item 2 sweep).
|
|
3
3
|
*
|
|
4
4
|
* The schema and the target are both parsed with the same engine; the
|
|
5
5
|
* walk applies the schema tree to the instance tree and emits
|
|
6
6
|
* diagnostics at instance JSON-pointer paths. Subset: type, enum,
|
|
7
|
-
* const, pattern, minimum/maximum/exclusive*,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* const, pattern, minimum/maximum/exclusive*, multipleOf (libm-free
|
|
8
|
+
* quotient integrality), minLength/maxLength (code points),
|
|
9
|
+
* minItems/maxItems, uniqueItems, contains (+ minContains/maxContains
|
|
10
|
+
* via suppressed probes), properties, required, patternProperties
|
|
11
|
+
* (the pattern kernel over KEY strings), propertyNames (the schema
|
|
12
|
+
* applies to the key string itself), minProperties/maxProperties,
|
|
13
|
+
* additionalProperties (bool or schema; excludes properties AND
|
|
14
|
+
* patternProperties matches), dependentRequired, dependentSchemas,
|
|
15
|
+
* if/then/else (the if-schema rides a suppressed probe), items
|
|
16
|
+
* (+ prefixItems; the draft-07 array form rides as prefixItems),
|
|
17
|
+
* allOf/anyOf/oneOf/not, $ref (LOCAL "#/..." pointers — $defs and
|
|
18
|
+
* definitions both resolve), boolean schemas. Unknown keywords, and
|
|
19
|
+
* known keywords whose values cannot be read, are IGNORED (2020-12's
|
|
20
|
+
* posture) — an unreadable constraint never guesses. Carried out of
|
|
21
|
+
* the subset (documented in test/conformance/jsonschema/MATRIX.md):
|
|
22
|
+
* unevaluatedItems/unevaluatedProperties (annotation plumbing),
|
|
23
|
+
* $anchor/$id/$dynamicRef (pointer/base-URI machinery), remote $ref,
|
|
24
|
+
* contentMediaType/contentSchema (annotation-only in 2020-12).
|
|
15
25
|
*
|
|
16
26
|
* Instance types ride the core-schema resolution (the YAML->JSON data
|
|
17
27
|
* model): "y" is boolean, 1.0 is integer (2020-12: zero fractional
|
|
@@ -41,6 +51,9 @@ typedef struct {
|
|
|
41
51
|
yep_vctx v; /* instance-side context (path, report, guards) */
|
|
42
52
|
const yep_dom* sd; /* the schema document's tree */
|
|
43
53
|
uint32_t sroot; /* the schema's document root */
|
|
54
|
+
/* the compiled schema's retained pattern programs (NULL: the
|
|
55
|
+
* one-shot face — every search compiles fresh, identical verdicts) */
|
|
56
|
+
const yep_re_cache* cache;
|
|
44
57
|
} js_ctx;
|
|
45
58
|
|
|
46
59
|
/* ---- schema-side facts -------------------------------------------------- */
|
|
@@ -220,10 +233,43 @@ static int inst_seq_count(const yep_dom* d, uint32_t id, uint32_t* count) {
|
|
|
220
233
|
return 1;
|
|
221
234
|
}
|
|
222
235
|
|
|
236
|
+
/* Finds an instance pair by key bytes (scalar keys only): the VALUE
|
|
237
|
+
* node id, UINT32_MAX absent. *key_out receives the KEY node id when
|
|
238
|
+
* non-NULL (patternProperties/propertyNames walk the key itself). */
|
|
239
|
+
static uint32_t inst_find_key(const yep_dom* d, uint32_t map_id, const char* name, size_t nl,
|
|
240
|
+
uint32_t* key_out) {
|
|
241
|
+
const yep_dnode* n = yep_dom_node(d, map_id);
|
|
242
|
+
if (n == NULL || n->kind != YEP_DOM_MAPPING) {
|
|
243
|
+
return UINT32_MAX;
|
|
244
|
+
}
|
|
245
|
+
uint32_t k = n->first_child;
|
|
246
|
+
while (k != UINT32_MAX) {
|
|
247
|
+
uint32_t v = yep_dom_node(d, k) ? yep_dom_node(d, k)->next_sibling : UINT32_MAX;
|
|
248
|
+
const yep_dnode* kn = yep_dom_node(d, k);
|
|
249
|
+
if (kn != NULL && kn->kind == YEP_DOM_SCALAR) {
|
|
250
|
+
yep_view kv = yep_dom_view(d, kn->value);
|
|
251
|
+
if (kv.len == nl && (nl == 0 || memcmp(kv.p, name, nl) == 0)) {
|
|
252
|
+
if (key_out != NULL) {
|
|
253
|
+
*key_out = k;
|
|
254
|
+
}
|
|
255
|
+
return v;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
k = v != UINT32_MAX && yep_dom_node(d, v) ? yep_dom_node(d, v)->next_sibling : UINT32_MAX;
|
|
259
|
+
}
|
|
260
|
+
return UINT32_MAX;
|
|
261
|
+
}
|
|
262
|
+
|
|
223
263
|
/* ---- the walk ----------------------------------------------------------- */
|
|
224
264
|
|
|
225
265
|
static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth);
|
|
226
266
|
|
|
267
|
+
/* the object-keyword walkers (defined after js_apply) */
|
|
268
|
+
static void check_pattern_properties(js_ctx* j, uint32_t pp_id, uint32_t inst_id, int depth);
|
|
269
|
+
static void check_property_names(js_ctx* j, uint32_t pn_id, uint32_t inst_id, int depth);
|
|
270
|
+
static void check_dependent_required(js_ctx* j, uint32_t dr_id, uint32_t inst_id, int depth);
|
|
271
|
+
static void check_dependent_schemas(js_ctx* j, uint32_t ds_id, uint32_t inst_id, int depth);
|
|
272
|
+
|
|
227
273
|
/* A suppressed probe: does `schema_id` hold at `inst_id`? Diagnostics
|
|
228
274
|
* during the trial are swallowed. */
|
|
229
275
|
static int js_holds(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth) {
|
|
@@ -621,6 +667,28 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
621
667
|
"%g is not less than %g", iv, b);
|
|
622
668
|
}
|
|
623
669
|
}
|
|
670
|
+
if (kw_get(j, schema_id, "multipleOf", &mv) && mv != UINT32_MAX) {
|
|
671
|
+
double m;
|
|
672
|
+
if (schema_number(j, mv, &m)) {
|
|
673
|
+
/* integrality of iv/m, libm-free: NaN fails (q==q),
|
|
674
|
+
* +-inf fails (q-q is NaN); every finite |q| >= 2^53
|
|
675
|
+
* is already an integer at double precision, and
|
|
676
|
+
* below that the int64 cast round-trips iff q is
|
|
677
|
+
* integral. A 0 divisor (never a legal schema) lands
|
|
678
|
+
* in inf/NaN and fails every instance. */
|
|
679
|
+
double q = iv / m;
|
|
680
|
+
int ok = (q == q) && (q - q == 0);
|
|
681
|
+
if (ok) {
|
|
682
|
+
double aq = q < 0 ? -q : q;
|
|
683
|
+
ok = aq >= 9007199254740992.0 /* 2^53 */ || q == (double)(int64_t)q;
|
|
684
|
+
}
|
|
685
|
+
if (!ok) {
|
|
686
|
+
yep_validate_emit(&j->v, YEPTRIS_DIAG_MULTIPLE_OF,
|
|
687
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
688
|
+
"%g is not a multiple of %g", iv, m);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
624
692
|
}
|
|
625
693
|
}
|
|
626
694
|
|
|
@@ -653,7 +721,7 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
653
721
|
if (schema_word(j, pv, &pat, &pl)) {
|
|
654
722
|
const yep_dnode* n = yep_dom_node(j->v.dom, inst_id);
|
|
655
723
|
yep_view v = yep_dom_view(j->v.dom, n->value);
|
|
656
|
-
int rc =
|
|
724
|
+
int rc = yep_re_cache_search(j->cache, pat, pl, v.p != NULL ? v.p : "", v.len);
|
|
657
725
|
if (rc == 0) {
|
|
658
726
|
yep_validate_emit(&j->v, YEPTRIS_DIAG_PATTERN,
|
|
659
727
|
yep_validate_node_off(j->v.dom, inst_id),
|
|
@@ -691,6 +759,57 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
691
759
|
if (kw_schema(j, schema_id, "uniqueItems", &uv, &uf) && uf == 0) {
|
|
692
760
|
check_unique(j, inst_id, yep_validate_node_off(j->v.dom, inst_id));
|
|
693
761
|
}
|
|
762
|
+
/* contains: count the items the schema holds on (suppressed
|
|
763
|
+
* probes), then the minContains/maxContains qualifiers
|
|
764
|
+
* (defaults: at least one; no maximum). min/maxContains
|
|
765
|
+
* without contains are annotations — ignored. */
|
|
766
|
+
uint32_t ctv;
|
|
767
|
+
if (kw_get(j, schema_id, "contains", &ctv) && ctv != UINT32_MAX) {
|
|
768
|
+
int ctf = 0;
|
|
769
|
+
if (snode(j, ctv) != NULL && schema_truth(j, ctv, &ctf)) {
|
|
770
|
+
uint32_t matches = 0;
|
|
771
|
+
uint32_t ich = yep_dom_node(j->v.dom, inst_id)
|
|
772
|
+
? yep_dom_node(j->v.dom, inst_id)->first_child
|
|
773
|
+
: UINT32_MAX;
|
|
774
|
+
for (uint32_t i = 0; i < cnt && ich != UINT32_MAX; i++) {
|
|
775
|
+
uint32_t ni = yep_dom_node(j->v.dom, ich)
|
|
776
|
+
? yep_dom_node(j->v.dom, ich)->next_sibling
|
|
777
|
+
: UINT32_MAX;
|
|
778
|
+
if (js_holds(j, ctv, ich, depth)) {
|
|
779
|
+
matches++;
|
|
780
|
+
}
|
|
781
|
+
ich = ni;
|
|
782
|
+
}
|
|
783
|
+
double minc = 1.0;
|
|
784
|
+
double maxc = -1.0; /* unset */
|
|
785
|
+
uint32_t mc;
|
|
786
|
+
int have_minc = 0;
|
|
787
|
+
if (kw_get(j, schema_id, "minContains", &mc) && mc != UINT32_MAX) {
|
|
788
|
+
if (schema_number(j, mc, &minc)) {
|
|
789
|
+
have_minc = 1;
|
|
790
|
+
} else {
|
|
791
|
+
minc = 1.0; /* an unreadable qualifier: default */
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (kw_get(j, schema_id, "maxContains", &mc) && mc != UINT32_MAX) {
|
|
795
|
+
if (!schema_number(j, mc, &maxc)) {
|
|
796
|
+
maxc = -1.0;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if ((double)matches < minc) {
|
|
800
|
+
yep_validate_emit(
|
|
801
|
+
&j->v, have_minc ? YEPTRIS_DIAG_MIN_CONTAINS : YEPTRIS_DIAG_CONTAINS,
|
|
802
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
803
|
+
"%u of %u items match the contains schema (need %g)", matches, cnt,
|
|
804
|
+
minc);
|
|
805
|
+
} else if (maxc >= 0.0 && (double)matches > maxc) {
|
|
806
|
+
yep_validate_emit(&j->v, YEPTRIS_DIAG_MAX_CONTAINS,
|
|
807
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
808
|
+
"%u of %u items match the contains schema (max %g)",
|
|
809
|
+
matches, cnt, maxc);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
694
813
|
/* prefixItems: per-index schemas; the draft-07 array form
|
|
695
814
|
* of items rides the same semantics */
|
|
696
815
|
uint32_t prefix = 0;
|
|
@@ -830,14 +949,50 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
830
949
|
}
|
|
831
950
|
}
|
|
832
951
|
|
|
952
|
+
/* if/then/else: the if-schema rides a suppressed probe; exactly one
|
|
953
|
+
* of then/else applies. An unreadable if (or then/else) is ignored. */
|
|
954
|
+
{
|
|
955
|
+
uint32_t ifv;
|
|
956
|
+
if (kw_get(j, schema_id, "if", &ifv) && ifv != UINT32_MAX) {
|
|
957
|
+
int iff = 0;
|
|
958
|
+
if (schema_truth(j, ifv, &iff)) {
|
|
959
|
+
uint32_t tv = UINT32_MAX, ev = UINT32_MAX;
|
|
960
|
+
int tf = 0, ef = 0;
|
|
961
|
+
int have_t = kw_schema(j, schema_id, "then", &tv, &tf);
|
|
962
|
+
int have_e = kw_schema(j, schema_id, "else", &ev, &ef);
|
|
963
|
+
if (js_holds(j, ifv, inst_id, depth)) {
|
|
964
|
+
if (have_t) {
|
|
965
|
+
js_apply(j, tv, inst_id, depth);
|
|
966
|
+
}
|
|
967
|
+
} else if (have_e) {
|
|
968
|
+
js_apply(j, ev, inst_id, depth);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
833
974
|
/* the object keywords (ignored on non-objects) */
|
|
834
975
|
{
|
|
835
976
|
const yep_dnode* n = yep_dom_node(j->v.dom, inst_id);
|
|
836
977
|
if (n != NULL && n->kind == YEP_DOM_MAPPING) {
|
|
978
|
+
const uint32_t pairs = n->count / 2;
|
|
837
979
|
/* properties: known keys */
|
|
838
980
|
uint32_t props = UINT32_MAX;
|
|
839
981
|
int have_props = kw_get(j, schema_id, "properties", &props) && props != UINT32_MAX &&
|
|
840
982
|
snode(j, props) != NULL && snode(j, props)->kind == YEP_DOM_MAPPING;
|
|
983
|
+
/* patternProperties: pattern -> schema over the KEY strings
|
|
984
|
+
* (readable once; additionalProperties must exclude its
|
|
985
|
+
* matches too) */
|
|
986
|
+
uint32_t pp_id = UINT32_MAX;
|
|
987
|
+
{
|
|
988
|
+
uint32_t ppid;
|
|
989
|
+
if (kw_get(j, schema_id, "patternProperties", &ppid) && ppid != UINT32_MAX) {
|
|
990
|
+
const yep_dnode* pnm = snode(j, ppid);
|
|
991
|
+
if (pnm != NULL && pnm->kind == YEP_DOM_MAPPING) {
|
|
992
|
+
pp_id = ppid;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
841
996
|
uint32_t req;
|
|
842
997
|
if (kw_get(j, schema_id, "required", &req) && req != UINT32_MAX) {
|
|
843
998
|
const yep_dnode* rn = snode(j, req);
|
|
@@ -847,41 +1002,22 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
847
1002
|
const char* name = NULL;
|
|
848
1003
|
uint32_t nl = 0;
|
|
849
1004
|
uint32_t nc = snode(j, ch) ? snode(j, ch)->next_sibling : UINT32_MAX;
|
|
850
|
-
if (schema_word(j, ch, &name, &nl)
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
? yep_dom_node(j->v.dom, v)->next_sibling
|
|
867
|
-
: UINT32_MAX;
|
|
868
|
-
}
|
|
869
|
-
if (hit == UINT32_MAX) {
|
|
870
|
-
char namebuf[64];
|
|
871
|
-
size_t cl = nl < sizeof(namebuf) - 1 ? nl : sizeof(namebuf) - 1;
|
|
872
|
-
memcpy(namebuf, name, cl);
|
|
873
|
-
namebuf[cl] = '\0';
|
|
874
|
-
/* the diagnostic points at the missing
|
|
875
|
-
* leaf: object path + /key (the ajv
|
|
876
|
-
* instancePath convention) */
|
|
877
|
-
size_t mark = j->v.path_len;
|
|
878
|
-
int pushed = yep_validate_path_push(&j->v, namebuf, cl);
|
|
879
|
-
yep_validate_emit(&j->v, YEPTRIS_DIAG_REQUIRED,
|
|
880
|
-
yep_validate_node_off(j->v.dom, inst_id),
|
|
881
|
-
"missing required property \"%s\"", namebuf);
|
|
882
|
-
if (pushed == 0) {
|
|
883
|
-
yep_validate_path_pop(&j->v, mark);
|
|
884
|
-
}
|
|
1005
|
+
if (schema_word(j, ch, &name, &nl) &&
|
|
1006
|
+
inst_find_key(j->v.dom, inst_id, name, nl, NULL) == UINT32_MAX) {
|
|
1007
|
+
char namebuf[64];
|
|
1008
|
+
size_t cl = nl < sizeof(namebuf) - 1 ? nl : sizeof(namebuf) - 1;
|
|
1009
|
+
memcpy(namebuf, name, cl);
|
|
1010
|
+
namebuf[cl] = '\0';
|
|
1011
|
+
/* the diagnostic points at the missing
|
|
1012
|
+
* leaf: object path + /key (the ajv
|
|
1013
|
+
* instancePath convention) */
|
|
1014
|
+
size_t mark = j->v.path_len;
|
|
1015
|
+
int pushed = yep_validate_path_push(&j->v, namebuf, cl);
|
|
1016
|
+
yep_validate_emit(&j->v, YEPTRIS_DIAG_REQUIRED,
|
|
1017
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
1018
|
+
"missing required property \"%s\"", namebuf);
|
|
1019
|
+
if (pushed == 0) {
|
|
1020
|
+
yep_validate_path_pop(&j->v, mark);
|
|
885
1021
|
}
|
|
886
1022
|
}
|
|
887
1023
|
ch = nc;
|
|
@@ -925,6 +1061,57 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
925
1061
|
k = v != UINT32_MAX && snode(j, v) ? snode(j, v)->next_sibling : UINT32_MAX;
|
|
926
1062
|
}
|
|
927
1063
|
}
|
|
1064
|
+
/* patternProperties: every pattern that matches a key
|
|
1065
|
+
* applies its schema to that key's value */
|
|
1066
|
+
if (pp_id != UINT32_MAX) {
|
|
1067
|
+
check_pattern_properties(j, pp_id, inst_id, depth);
|
|
1068
|
+
}
|
|
1069
|
+
/* propertyNames: the schema applies to each KEY string */
|
|
1070
|
+
{
|
|
1071
|
+
uint32_t pnv;
|
|
1072
|
+
if (kw_get(j, schema_id, "propertyNames", &pnv) && pnv != UINT32_MAX) {
|
|
1073
|
+
int pnf = 0;
|
|
1074
|
+
if (snode(j, pnv) != NULL && schema_truth(j, pnv, &pnf)) {
|
|
1075
|
+
check_property_names(j, pnv, inst_id, depth);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
/* min/maxProperties */
|
|
1080
|
+
{
|
|
1081
|
+
uint32_t mv;
|
|
1082
|
+
if (kw_get(j, schema_id, "minProperties", &mv) && mv != UINT32_MAX) {
|
|
1083
|
+
double b;
|
|
1084
|
+
if (schema_number(j, mv, &b) && (double)pairs < b) {
|
|
1085
|
+
yep_validate_emit(&j->v, YEPTRIS_DIAG_MIN_PROPERTIES,
|
|
1086
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
1087
|
+
"%u properties is fewer than %g", pairs, b);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (kw_get(j, schema_id, "maxProperties", &mv) && mv != UINT32_MAX) {
|
|
1091
|
+
double b;
|
|
1092
|
+
if (schema_number(j, mv, &b) && (double)pairs > b) {
|
|
1093
|
+
yep_validate_emit(&j->v, YEPTRIS_DIAG_MAX_PROPERTIES,
|
|
1094
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
1095
|
+
"%u properties is more than %g", pairs, b);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
/* dependentRequired: name -> the names that must ride along */
|
|
1100
|
+
{
|
|
1101
|
+
uint32_t drv;
|
|
1102
|
+
if (kw_get(j, schema_id, "dependentRequired", &drv) && drv != UINT32_MAX) {
|
|
1103
|
+
check_dependent_required(j, drv, inst_id, depth);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
/* dependentSchemas: when the named key is present, its
|
|
1107
|
+
* schema applies to the whole object (walked directly, like
|
|
1108
|
+
* allOf: the inner diagnostics carry the failure) */
|
|
1109
|
+
{
|
|
1110
|
+
uint32_t dsv;
|
|
1111
|
+
if (kw_get(j, schema_id, "dependentSchemas", &dsv) && dsv != UINT32_MAX) {
|
|
1112
|
+
check_dependent_schemas(j, dsv, inst_id, depth);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
928
1115
|
/* additionalProperties: the keys properties does not name */
|
|
929
1116
|
uint32_t ap;
|
|
930
1117
|
int af = 0;
|
|
@@ -953,6 +1140,27 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
953
1140
|
: UINT32_MAX;
|
|
954
1141
|
}
|
|
955
1142
|
}
|
|
1143
|
+
/* patternProperties matches take the key out of
|
|
1144
|
+
* additionalProperties' scope too (2020-12) */
|
|
1145
|
+
if (!known && pp_id != UINT32_MAX) {
|
|
1146
|
+
const yep_dnode* pnm = snode(j, pp_id);
|
|
1147
|
+
uint32_t pk = pnm->first_child;
|
|
1148
|
+
while (pk != UINT32_MAX && !known) {
|
|
1149
|
+
const yep_dnode* pkn = snode(j, pk);
|
|
1150
|
+
uint32_t pv =
|
|
1151
|
+
snode(j, pk) ? snode(j, pk)->next_sibling : UINT32_MAX;
|
|
1152
|
+
const char* pat = NULL;
|
|
1153
|
+
uint32_t pl = 0;
|
|
1154
|
+
if (pkn != NULL && pkn->kind == YEP_DOM_SCALAR &&
|
|
1155
|
+
schema_word(j, pk, &pat, &pl) &&
|
|
1156
|
+
yep_re_cache_search(j->cache, pat, pl, kv.p != NULL ? kv.p : "",
|
|
1157
|
+
kv.len) == 1) {
|
|
1158
|
+
known = 1;
|
|
1159
|
+
}
|
|
1160
|
+
pk = pv != UINT32_MAX && snode(j, pv) ? snode(j, pv)->next_sibling
|
|
1161
|
+
: UINT32_MAX;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
956
1164
|
if (!known) {
|
|
957
1165
|
char namebuf[64];
|
|
958
1166
|
size_t cl = kv.len < sizeof(namebuf) - 1 ? kv.len : sizeof(namebuf) - 1;
|
|
@@ -982,6 +1190,165 @@ static void js_apply(js_ctx* j, uint32_t schema_id, uint32_t inst_id, int depth)
|
|
|
982
1190
|
}
|
|
983
1191
|
}
|
|
984
1192
|
|
|
1193
|
+
/* ---- the object-keyword walkers ------------------------------------------ */
|
|
1194
|
+
|
|
1195
|
+
/* Copies a bounded name for diagnostics/detail text: the copied
|
|
1196
|
+
* length (the caller pushes paths with it — keys may hold NULs). */
|
|
1197
|
+
static size_t name_copy(char* buf, size_t cap, const char* p, size_t len) {
|
|
1198
|
+
size_t cl = len < cap - 1 ? len : cap - 1;
|
|
1199
|
+
if (cl > 0 && p != NULL) {
|
|
1200
|
+
memcpy(buf, p, cl);
|
|
1201
|
+
}
|
|
1202
|
+
buf[cl] = '\0';
|
|
1203
|
+
return cl;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/* patternProperties: pattern -> schema over the KEY strings; every
|
|
1207
|
+
* matching pattern applies its schema to the key's value (a false
|
|
1208
|
+
* schema rides js_apply's FALSE_SCHEMA diagnostic). */
|
|
1209
|
+
static void check_pattern_properties(js_ctx* j, uint32_t pp_id, uint32_t inst_id, int depth) {
|
|
1210
|
+
const yep_dom* d = j->v.dom;
|
|
1211
|
+
const yep_dnode* pnm = snode(j, pp_id);
|
|
1212
|
+
const yep_dnode* inst = yep_dom_node(d, inst_id);
|
|
1213
|
+
if (pnm == NULL || inst == NULL) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
uint32_t k = inst->first_child;
|
|
1217
|
+
while (k != UINT32_MAX && !j->v.oom) {
|
|
1218
|
+
uint32_t v = yep_dom_node(d, k) ? yep_dom_node(d, k)->next_sibling : UINT32_MAX;
|
|
1219
|
+
const yep_dnode* kn = yep_dom_node(d, k);
|
|
1220
|
+
uint32_t nk =
|
|
1221
|
+
v != UINT32_MAX && yep_dom_node(d, v) ? yep_dom_node(d, v)->next_sibling : UINT32_MAX;
|
|
1222
|
+
if (kn != NULL && kn->kind == YEP_DOM_SCALAR && v != UINT32_MAX) {
|
|
1223
|
+
yep_view kv = yep_dom_view(d, kn->value);
|
|
1224
|
+
uint32_t pk = pnm->first_child;
|
|
1225
|
+
while (pk != UINT32_MAX && !j->v.oom) {
|
|
1226
|
+
const yep_dnode* pkn = snode(j, pk);
|
|
1227
|
+
uint32_t pv = snode(j, pk) ? snode(j, pk)->next_sibling : UINT32_MAX;
|
|
1228
|
+
uint32_t np =
|
|
1229
|
+
pv != UINT32_MAX && snode(j, pv) ? snode(j, pv)->next_sibling : UINT32_MAX;
|
|
1230
|
+
const char* pat = NULL;
|
|
1231
|
+
uint32_t pl = 0;
|
|
1232
|
+
if (pkn != NULL && pkn->kind == YEP_DOM_SCALAR && pv != UINT32_MAX &&
|
|
1233
|
+
schema_word(j, pk, &pat, &pl) &&
|
|
1234
|
+
yep_re_cache_search(j->cache, pat, pl, kv.p != NULL ? kv.p : "", kv.len) == 1) {
|
|
1235
|
+
char namebuf[64];
|
|
1236
|
+
size_t cl = name_copy(namebuf, sizeof(namebuf), kv.p, kv.len);
|
|
1237
|
+
size_t mark = j->v.path_len;
|
|
1238
|
+
if (yep_validate_path_push(&j->v, namebuf, cl) == 0) {
|
|
1239
|
+
js_apply(j, pv, v, depth);
|
|
1240
|
+
yep_validate_path_pop(&j->v, mark);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
pk = np;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
k = nk;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/* propertyNames: the schema applies to each KEY (the key string IS the
|
|
1251
|
+
* instance; a false propertyNames rides FALSE_SCHEMA at the key). */
|
|
1252
|
+
static void check_property_names(js_ctx* j, uint32_t pn_id, uint32_t inst_id, int depth) {
|
|
1253
|
+
const yep_dom* d = j->v.dom;
|
|
1254
|
+
const yep_dnode* inst = yep_dom_node(d, inst_id);
|
|
1255
|
+
if (inst == NULL) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
uint32_t k = inst->first_child;
|
|
1259
|
+
while (k != UINT32_MAX && !j->v.oom) {
|
|
1260
|
+
uint32_t v = yep_dom_node(d, k) ? yep_dom_node(d, k)->next_sibling : UINT32_MAX;
|
|
1261
|
+
const yep_dnode* kn = yep_dom_node(d, k);
|
|
1262
|
+
uint32_t nk =
|
|
1263
|
+
v != UINT32_MAX && yep_dom_node(d, v) ? yep_dom_node(d, v)->next_sibling : UINT32_MAX;
|
|
1264
|
+
if (kn != NULL && kn->kind == YEP_DOM_SCALAR) {
|
|
1265
|
+
yep_view kv = yep_dom_view(d, kn->value);
|
|
1266
|
+
char namebuf[64];
|
|
1267
|
+
size_t cl = name_copy(namebuf, sizeof(namebuf), kv.p, kv.len);
|
|
1268
|
+
size_t mark = j->v.path_len;
|
|
1269
|
+
if (yep_validate_path_push(&j->v, namebuf, cl) == 0) {
|
|
1270
|
+
js_apply(j, pn_id, k, depth);
|
|
1271
|
+
yep_validate_path_pop(&j->v, mark);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
k = nk;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/* dependentRequired: name -> the names required alongside it. A
|
|
1279
|
+
* present trigger with an absent dependent is DIAG_DEPENDENT_REQUIRED
|
|
1280
|
+
* at the object (the missing leaf's path, the required convention). */
|
|
1281
|
+
static void check_dependent_required(js_ctx* j, uint32_t dr_id, uint32_t inst_id, int depth) {
|
|
1282
|
+
(void)depth;
|
|
1283
|
+
const yep_dom* d = j->v.dom;
|
|
1284
|
+
const yep_dnode* dm = snode(j, dr_id);
|
|
1285
|
+
if (dm == NULL || dm->kind != YEP_DOM_MAPPING) {
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
uint32_t sk = dm->first_child;
|
|
1289
|
+
while (sk != UINT32_MAX && !j->v.oom) {
|
|
1290
|
+
uint32_t sv = snode(j, sk) ? snode(j, sk)->next_sibling : UINT32_MAX;
|
|
1291
|
+
uint32_t ns = sv != UINT32_MAX && snode(j, sv) ? snode(j, sv)->next_sibling : UINT32_MAX;
|
|
1292
|
+
const yep_dnode* skn = snode(j, sk);
|
|
1293
|
+
if (skn != NULL && skn->kind == YEP_DOM_SCALAR && sv != UINT32_MAX) {
|
|
1294
|
+
yep_view trig = yep_dom_view(j->sd, skn->value);
|
|
1295
|
+
const yep_dnode* ln = snode(j, sv);
|
|
1296
|
+
if (trig.p != NULL && ln != NULL && ln->kind == YEP_DOM_SEQUENCE &&
|
|
1297
|
+
inst_find_key(d, inst_id, trig.p, trig.len, NULL) != UINT32_MAX) {
|
|
1298
|
+
char trigbuf[64];
|
|
1299
|
+
name_copy(trigbuf, sizeof(trigbuf), trig.p, trig.len);
|
|
1300
|
+
uint32_t rk = ln->first_child;
|
|
1301
|
+
while (rk != UINT32_MAX && !j->v.oom) {
|
|
1302
|
+
uint32_t rn = snode(j, rk) ? snode(j, rk)->next_sibling : UINT32_MAX;
|
|
1303
|
+
const char* name = NULL;
|
|
1304
|
+
uint32_t nl = 0;
|
|
1305
|
+
if (schema_word(j, rk, &name, &nl) &&
|
|
1306
|
+
inst_find_key(d, inst_id, name, nl, NULL) == UINT32_MAX) {
|
|
1307
|
+
/* the detail text is printf-composed: the view
|
|
1308
|
+
* is NOT NUL-terminated — %s reads past the
|
|
1309
|
+
* DOM allocation (ASan-caught); copy first */
|
|
1310
|
+
char namebuf[64];
|
|
1311
|
+
size_t cl = name_copy(namebuf, sizeof(namebuf), name, nl);
|
|
1312
|
+
size_t mark = j->v.path_len;
|
|
1313
|
+
int pushed = yep_validate_path_push(&j->v, namebuf, cl);
|
|
1314
|
+
yep_validate_emit(&j->v, YEPTRIS_DIAG_DEPENDENT_REQUIRED,
|
|
1315
|
+
yep_validate_node_off(j->v.dom, inst_id),
|
|
1316
|
+
"\"%s\" requires \"%s\"", trigbuf, namebuf);
|
|
1317
|
+
if (pushed == 0) {
|
|
1318
|
+
yep_validate_path_pop(&j->v, mark);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
rk = rn;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
sk = ns;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/* dependentSchemas: when the named key is present, its schema applies
|
|
1330
|
+
* to the whole object. */
|
|
1331
|
+
static void check_dependent_schemas(js_ctx* j, uint32_t ds_id, uint32_t inst_id, int depth) {
|
|
1332
|
+
const yep_dom* d = j->v.dom;
|
|
1333
|
+
const yep_dnode* dm = snode(j, ds_id);
|
|
1334
|
+
if (dm == NULL || dm->kind != YEP_DOM_MAPPING) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
uint32_t sk = dm->first_child;
|
|
1338
|
+
while (sk != UINT32_MAX && !j->v.oom) {
|
|
1339
|
+
uint32_t sv = snode(j, sk) ? snode(j, sk)->next_sibling : UINT32_MAX;
|
|
1340
|
+
uint32_t ns = sv != UINT32_MAX && snode(j, sv) ? snode(j, sv)->next_sibling : UINT32_MAX;
|
|
1341
|
+
const yep_dnode* skn = snode(j, sk);
|
|
1342
|
+
if (skn != NULL && skn->kind == YEP_DOM_SCALAR && sv != UINT32_MAX) {
|
|
1343
|
+
yep_view trig = yep_dom_view(j->sd, skn->value);
|
|
1344
|
+
if (trig.p != NULL && inst_find_key(d, inst_id, trig.p, trig.len, NULL) != UINT32_MAX) {
|
|
1345
|
+
js_apply(j, sv, inst_id, depth);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
sk = ns;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
985
1352
|
/* ---- the entry ----------------------------------------------------------- */
|
|
986
1353
|
|
|
987
1354
|
/* Validates one instance document root against the schema root.
|
|
@@ -991,16 +1358,19 @@ static int js_validate_doc(js_ctx* j, uint32_t inst_root) {
|
|
|
991
1358
|
return j->v.oom ? -1 : 0;
|
|
992
1359
|
}
|
|
993
1360
|
|
|
994
|
-
/* The public
|
|
995
|
-
*
|
|
996
|
-
*
|
|
1361
|
+
/* The public entries live in validate.c/compile.c (yeptris_json_schema_
|
|
1362
|
+
* validate compiles+validates+frees through the compiled handle; the
|
|
1363
|
+
* compiled face drives this seam per instance stream): they parse the
|
|
1364
|
+
* sides, build the report and hand over the schema DOM + the retained
|
|
1365
|
+
* pattern cache (the declarations are internal.h's — the shared SSOT). */
|
|
997
1366
|
int yep_jsonschema_run(const yep_dom* schema_dom, uint32_t schema_root, const yep_dom* instance_dom,
|
|
998
|
-
const uint32_t* doc_roots, uint32_t doc_count,
|
|
999
|
-
|
|
1367
|
+
const uint32_t* doc_roots, uint32_t doc_count, yeptris_validate_report* rep,
|
|
1368
|
+
const yep_re_cache* cache) {
|
|
1000
1369
|
js_ctx j;
|
|
1001
1370
|
memset(&j, 0, sizeof(j));
|
|
1002
1371
|
j.sd = schema_dom;
|
|
1003
1372
|
j.sroot = schema_root;
|
|
1373
|
+
j.cache = cache;
|
|
1004
1374
|
j.v.dom = instance_dom;
|
|
1005
1375
|
j.v.rep = rep;
|
|
1006
1376
|
j.v.input = instance_dom->input_base;
|