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.
@@ -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*, minLength/maxLength
8
- * (code points), minItems/maxItems, uniqueItems, properties, required,
9
- * additionalProperties (bool or schema), items (+ prefixItems; the
10
- * draft-07 array form rides as prefixItems), allOf/anyOf/oneOf/not,
11
- * $ref (LOCAL "#/..." pointers — $defs and definitions both resolve),
12
- * boolean schemas. Unknown keywords, and known keywords whose values
13
- * cannot be read, are IGNORED (2020-12's posture) — an unreadable
14
- * constraint never guesses.
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 = yep_re_search(pat, pl, v.p != NULL ? v.p : "", v.len);
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
- uint32_t hit = UINT32_MAX;
852
- uint32_t k = n->first_child;
853
- while (k != UINT32_MAX) {
854
- uint32_t v = yep_dom_node(j->v.dom, k)
855
- ? yep_dom_node(j->v.dom, k)->next_sibling
856
- : UINT32_MAX;
857
- const yep_dnode* kn = yep_dom_node(j->v.dom, k);
858
- if (kn != NULL && kn->kind == YEP_DOM_SCALAR) {
859
- yep_view kv = yep_dom_view(j->v.dom, kn->value);
860
- if (kv.len == nl && (nl == 0 || memcmp(kv.p, name, nl) == 0)) {
861
- hit = v;
862
- break;
863
- }
864
- }
865
- k = v != UINT32_MAX && yep_dom_node(j->v.dom, v)
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 entry lives in validate.c (yeptris_json_schema_validate):
995
- * it parses both sides, builds the report and drives this seam per
996
- * document (the declaration is internal.h's — the shared SSOT). */
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
- yeptris_validate_report* rep) {
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;