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,14 +1,16 @@
1
1
  /* regexp.c — the bounded ECMA-regex subset behind JSON Schema's
2
- * `pattern` keyword (#882 slice 2).
2
+ * `pattern` keyword (#882 slice 2; #885 item 2).
3
3
  *
4
4
  * Supported: literals, '.', [...] classes (ranges, negation, \d\w\s
5
5
  * and their negations, \n\t\r\f\v\0, \xHH), groups (capturing ignored,
6
6
  * (?:...) fine), alternation, the quantifiers * + ? {n} {n,} {n,m}
7
7
  * (lazy markers parse and are ignored — match/no-match has no
8
8
  * preference), the ^ $ \b \B assertions. Unsupported constructs
9
- * (backreferences, lookaround, unknown letter escapes) are
10
- * SyntaxErrors: the caller treats an invalid pattern as an absent
11
- * keyword (2020-12's ignore posture), never a guess.
9
+ * (backreferences, lookaround) are SyntaxErrors, and unknown LETTER
10
+ * escapes are identity escapes (non-unicode ECMA-262 Annex B: \p
11
+ * without the u flag is the literal 'p'): the caller treats an invalid
12
+ * pattern as an absent keyword (2020-12's ignore posture), never a
13
+ * guess.
12
14
  *
13
15
  * Shape: byte-oriented — classes are ASCII, UTF-8 literals match
14
16
  * byte-wise (ASCII patterns, the JSON Schema norm, are exact).
@@ -17,6 +19,14 @@
17
19
  * Every array is a fixed stack budget; overflow is a SyntaxError (-1),
18
20
  * never a crash. Truth lives here once (MECE: a second consumer moves
19
21
  * this to its own module).
22
+ *
23
+ * Two consumption shapes over one compiler (#885 item 3):
24
+ * `yep_re_search` compiles into a stack ctx and drops it (the one-shot
25
+ * walk, zero allocation); the retained-program cache
26
+ * (`yep_re_cache_*`) compiles a schema's patterns ONCE into exact-size
27
+ * heap programs and runs them per validated string — the XSD
28
+ * content-model economy. A cache miss recompiles (identical verdicts);
29
+ * the cache itself is immutable once populated.
20
30
  */
21
31
 
22
32
  #include <string.h>
@@ -223,9 +233,13 @@ static int p_escape(re_ctx* c, int* out) {
223
233
  return 0;
224
234
  }
225
235
  default:
226
- /* letters/digits outside the set (incl. backrefs): SyntaxError;
227
- * punctuation: identity escape */
228
- if ((e >= 'a' && e <= 'z') || (e >= 'A' && e <= 'Z') || (e >= '0' && e <= '9')) {
236
+ /* non-unicode ECMA-262 (Annex B): unknown LETTER escapes are
237
+ * identity escapes — \p outside unicode mode is the literal
238
+ * 'p' (property escapes need the u flag this kernel does not
239
+ * carry). Digits stay SyntaxError: 1-9 are backreferences,
240
+ * out of the subset — an ignored pattern beats a misread one.
241
+ * Punctuation is identity too. */
242
+ if (e >= '0' && e <= '9') {
229
243
  c->err = 1;
230
244
  return -1;
231
245
  }
@@ -808,33 +822,43 @@ static int c_node(re_ctx* c, int id, re_frag* out, int depth) {
808
822
  }
809
823
  }
810
824
 
811
- /* ---- the public search: Pike VM over the subject ----------------------- */
812
-
813
- int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_len) {
814
- if (pat == NULL || pat_len == 0 || pat_len > RE_MAX_PAT) {
815
- return -1;
816
- }
817
- re_ctx c;
818
- memset(&c, 0, sizeof(c));
819
- c.pat = pat;
820
- c.pn = pat_len;
825
+ /* ---- compile: pattern -> program --------------------------------------- */
821
826
 
822
- int root = p_alt(&c, 0);
823
- if (root < 0 || c.pi != c.pn) {
827
+ /* parse + compile into the caller's ctx: MATCH emits last, every
828
+ * pending arrow resolves. 0 ok (the fragment entry pc in *entry_out),
829
+ * -1 SyntaxError (or a budget trip — the caller treats both as
830
+ * "pattern outside the subset", never a guess). */
831
+ static int re_build(re_ctx* c, const char* pat, uint32_t pat_len, int* entry_out) {
832
+ memset(c, 0, sizeof(*c));
833
+ c->pat = pat;
834
+ c->pn = pat_len;
835
+ int root = p_alt(c, 0);
836
+ if (root < 0 || c->pi != c->pn) {
824
837
  return -1; /* trailing garbage is a SyntaxError */
825
838
  }
826
839
  re_frag f;
827
- if (c_node(&c, root, &f, 0) < 0) {
840
+ if (c_node(c, root, &f, 0) < 0) {
828
841
  return -1;
829
842
  }
830
- if (emit_inst(&c, RE_OP_MATCH, 0, 0, 0) < 0) {
843
+ if (emit_inst(c, RE_OP_MATCH, 0, 0, 0) < 0) {
831
844
  return -1;
832
845
  }
833
- frag_connect(&f, c.inst, c.inst_n - 1); /* MATCH emits last */
834
- if (c.err) {
846
+ frag_connect(&f, c->inst, c->inst_n - 1); /* MATCH emits last */
847
+ if (c->err) {
835
848
  return -1;
836
849
  }
850
+ *entry_out = f.in;
851
+ return 0;
852
+ }
853
+
854
+ /* ---- the Pike VM (shared by the one-shot and the retained programs) ----- */
837
855
 
856
+ /* Runs one compiled program over the subject: 1 match, 0 no match.
857
+ * `entry` is the fragment entry pc (MATCH is emitted last, so the graph
858
+ * is self-contained). Never fails: a program exists only when its
859
+ * pattern compiled. */
860
+ static int re_vm(const re_inst* inst, const uint8_t (*cls)[32], int entry, const char* s,
861
+ uint32_t s_len) {
838
862
  uint8_t mark[RE_MAX_INST];
839
863
  uint16_t cur[RE_MAX_INST], nxt[RE_MAX_INST];
840
864
  /* closure depth: the seeds (m+1: live threads + the unanchored
@@ -845,7 +869,7 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
845
869
  uint32_t gen = 1;
846
870
  memset(mark, 0, sizeof(mark));
847
871
 
848
- if (f.in < 0) {
872
+ if (entry < 0) {
849
873
  return 1; /* an empty pattern matches everywhere */
850
874
  }
851
875
  /* initial closure at subject offset 0 (from the fragment entry;
@@ -853,19 +877,19 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
853
877
  int cur_n = 0;
854
878
  {
855
879
  int st_n = 0;
856
- stack[st_n++] = (uint16_t)f.in;
880
+ stack[st_n++] = (uint16_t)entry;
857
881
  while (st_n > 0) {
858
882
  uint16_t pc = stack[--st_n];
859
883
  if (mark[pc] == (uint8_t)gen) {
860
884
  continue;
861
885
  }
862
886
  mark[pc] = (uint8_t)gen;
863
- uint8_t op = c.inst[pc].op;
887
+ uint8_t op = inst[pc].op;
864
888
  if (op == RE_OP_JMP) {
865
- stack[st_n++] = c.inst[pc].x;
889
+ stack[st_n++] = inst[pc].x;
866
890
  } else if (op == RE_OP_SPLIT) {
867
- stack[st_n++] = c.inst[pc].x;
868
- stack[st_n++] = c.inst[pc].y;
891
+ stack[st_n++] = inst[pc].x;
892
+ stack[st_n++] = inst[pc].y;
869
893
  } else if (op == RE_OP_MATCH) {
870
894
  return 1; /* the empty match at offset 0 */
871
895
  } else if (op == RE_OP_ASSERT) {
@@ -873,7 +897,7 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
873
897
  * subject; a word boundary holds exactly when the
874
898
  * subject starts inside a word (no previous char) */
875
899
  int ok = 0;
876
- switch (c.inst[pc].cls) {
900
+ switch (inst[pc].cls) {
877
901
  case RE_AS_BOL:
878
902
  ok = 1;
879
903
  break;
@@ -882,12 +906,12 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
882
906
  break;
883
907
  default: {
884
908
  int wn = s_len > 0 && re_word((unsigned char)s[0]);
885
- ok = c.inst[pc].cls == RE_AS_WBOUND ? (int)wn : !(int)wn;
909
+ ok = inst[pc].cls == RE_AS_WBOUND ? (int)wn : !(int)wn;
886
910
  break;
887
911
  }
888
912
  }
889
913
  if (ok) {
890
- stack[st_n++] = c.inst[pc].x; /* the connected exit */
914
+ stack[st_n++] = inst[pc].x; /* the connected exit */
891
915
  }
892
916
  } else {
893
917
  cur[cur_n++] = pc; /* CHAR/BYTE: a live thread */
@@ -904,17 +928,17 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
904
928
  int nxt_n = 0;
905
929
  for (int i = 0; i < cur_n; i++) {
906
930
  uint16_t pc = cur[i];
907
- int hit = c.inst[pc].op == RE_OP_BYTE
908
- ? (c.inst[pc].cls == byte)
909
- : (c.cls[c.inst[pc].cls][(byte >> 3) & 31] & (1u << (byte & 7))) != 0;
931
+ int hit = inst[pc].op == RE_OP_BYTE
932
+ ? (inst[pc].cls == byte)
933
+ : (cls[inst[pc].cls][(byte >> 3) & 31] & (1u << (byte & 7))) != 0;
910
934
  if (hit) {
911
935
  /* the exit is the CONNECTED target (frag_connect), not
912
936
  * a fall-through: fragments link arbitrary pcs */
913
- nxt[nxt_n++] = c.inst[pc].x;
937
+ nxt[nxt_n++] = inst[pc].x;
914
938
  }
915
939
  }
916
- nxt[nxt_n++] = (uint16_t)f.in; /* unanchored: the search restarts
917
- * at every offset */
940
+ nxt[nxt_n++] = (uint16_t)entry; /* unanchored: the search restarts
941
+ * at every offset */
918
942
  gen++;
919
943
  if ((gen & 0xFF) == 0) {
920
944
  memset(mark, 0, sizeof(mark));
@@ -931,17 +955,17 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
931
955
  continue;
932
956
  }
933
957
  mark[pc] = (uint8_t)gen;
934
- uint8_t op = c.inst[pc].op;
958
+ uint8_t op = inst[pc].op;
935
959
  if (op == RE_OP_JMP) {
936
- stack[st_n++] = c.inst[pc].x;
960
+ stack[st_n++] = inst[pc].x;
937
961
  } else if (op == RE_OP_SPLIT) {
938
- stack[st_n++] = c.inst[pc].x;
939
- stack[st_n++] = c.inst[pc].y;
962
+ stack[st_n++] = inst[pc].x;
963
+ stack[st_n++] = inst[pc].y;
940
964
  } else if (op == RE_OP_MATCH) {
941
965
  matched = 1;
942
966
  } else if (op == RE_OP_ASSERT) {
943
967
  int ok = 0;
944
- switch (c.inst[pc].cls) {
968
+ switch (inst[pc].cls) {
945
969
  case RE_AS_BOL:
946
970
  ok = (sp + 1) == 0;
947
971
  break;
@@ -951,12 +975,12 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
951
975
  default: {
952
976
  int wp = re_word((unsigned char)s[sp]);
953
977
  int wn = (sp + 1) < (uint32_t)s_len && re_word((unsigned char)s[sp + 1]);
954
- ok = c.inst[pc].cls == RE_AS_WBOUND ? (wp != wn) : (wp == wn);
978
+ ok = inst[pc].cls == RE_AS_WBOUND ? (wp != wn) : (wp == wn);
955
979
  break;
956
980
  }
957
981
  }
958
982
  if (ok) {
959
- stack[st_n++] = c.inst[pc].x; /* the connected exit */
983
+ stack[st_n++] = inst[pc].x; /* the connected exit */
960
984
  }
961
985
  } else {
962
986
  nxt[live_n++] = pc; /* a live thread for the next byte */
@@ -970,3 +994,141 @@ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_l
970
994
  }
971
995
  return 0;
972
996
  }
997
+
998
+ /* ---- the one-shot search (compile fresh, run, drop) --------------------- */
999
+
1000
+ int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_len) {
1001
+ if (pat == NULL || pat_len == 0 || pat_len > RE_MAX_PAT) {
1002
+ return -1;
1003
+ }
1004
+ re_ctx c;
1005
+ int entry = -1;
1006
+ if (re_build(&c, pat, pat_len, &entry) != 0) {
1007
+ return -1;
1008
+ }
1009
+ return re_vm(c.inst, c.cls, entry, s, s_len);
1010
+ }
1011
+
1012
+ /* ---- the retained programs (the compiled schema's pattern cache) -------- */
1013
+
1014
+ /* A compiled program: exact-size copies of the instruction stream and
1015
+ * the class table (the ctx's bounded budgets, retained past the
1016
+ * compile call). */
1017
+ typedef struct {
1018
+ re_inst* inst;
1019
+ uint8_t (*cls)[32];
1020
+ int inst_n, cls_n;
1021
+ int entry;
1022
+ } re_prog;
1023
+
1024
+ static void re_prog_free(re_prog* p) {
1025
+ const yep_allocator* sys = yep_system_allocator();
1026
+ yep_free(sys, p->inst);
1027
+ yep_free(sys, p->cls);
1028
+ p->inst = NULL;
1029
+ p->cls = NULL;
1030
+ }
1031
+
1032
+ /* Compiles into a heap program. 0 ok; -1 SyntaxError or allocation
1033
+ * failure (nothing retained; both read as "the pattern is unusable",
1034
+ * the kernel's ignore posture). */
1035
+ static int re_compile(const char* pat, uint32_t pat_len, re_prog* out) {
1036
+ if (pat == NULL || pat_len == 0 || pat_len > RE_MAX_PAT) {
1037
+ return -1;
1038
+ }
1039
+ re_ctx c;
1040
+ int entry = -1;
1041
+ if (re_build(&c, pat, pat_len, &entry) != 0) {
1042
+ return -1;
1043
+ }
1044
+ const yep_allocator* sys = yep_system_allocator();
1045
+ re_prog p;
1046
+ memset(&p, 0, sizeof(p));
1047
+ p.inst_n = c.inst_n;
1048
+ p.cls_n = c.cls_n;
1049
+ p.entry = entry;
1050
+ p.inst = yep_alloc(sys, (size_t)c.inst_n * sizeof(re_inst));
1051
+ p.cls = yep_alloc(sys, (size_t)(c.cls_n > 0 ? c.cls_n : 1) * 32);
1052
+ if (p.inst == NULL || p.cls == NULL) {
1053
+ re_prog_free(&p);
1054
+ return -1;
1055
+ }
1056
+ memcpy(p.inst, c.inst, (size_t)c.inst_n * sizeof(re_inst));
1057
+ memcpy(p.cls, c.cls, (size_t)c.cls_n * 32);
1058
+ *out = p;
1059
+ return 0;
1060
+ }
1061
+
1062
+ /* The table: pattern source -> compiled program. Keys are the schema's
1063
+ * own byte views (stable for the handle's lifetime); lookups compare
1064
+ * content so equal patterns share one program. Bounded: over
1065
+ * RE_CACHE_MAX entries a miss recompiles — the one-shot behavior. */
1066
+ #define RE_CACHE_MAX 256
1067
+ typedef struct {
1068
+ const char* pat;
1069
+ uint32_t plen;
1070
+ re_prog prog;
1071
+ } re_cache_slot;
1072
+
1073
+ struct yep_re_cache {
1074
+ re_cache_slot slot[RE_CACHE_MAX];
1075
+ int n;
1076
+ };
1077
+
1078
+ yep_re_cache* yep_re_cache_new(void) {
1079
+ yep_re_cache* t = yep_alloc(yep_system_allocator(), sizeof(yep_re_cache));
1080
+ if (t != NULL) {
1081
+ memset(t, 0, sizeof(*t)); /* the allocator does not zero: n starts empty */
1082
+ }
1083
+ return t;
1084
+ }
1085
+
1086
+ void yep_re_cache_free(yep_re_cache* t) {
1087
+ if (t == NULL) {
1088
+ return;
1089
+ }
1090
+ for (int i = 0; i < t->n; i++) {
1091
+ re_prog_free(&t->slot[i].prog);
1092
+ }
1093
+ yep_free(yep_system_allocator(), t);
1094
+ }
1095
+
1096
+ int yep_re_cache_add(yep_re_cache* t, const char* pat, uint32_t plen) {
1097
+ if (t == NULL || pat == NULL || plen == 0) {
1098
+ return 0; /* nothing to retain */
1099
+ }
1100
+ for (int i = 0; i < t->n; i++) {
1101
+ if (t->slot[i].plen == plen && memcmp(t->slot[i].pat, pat, plen) == 0) {
1102
+ return 1; /* already retained (equal patterns share one program) */
1103
+ }
1104
+ }
1105
+ if (t->n >= RE_CACHE_MAX) {
1106
+ return 0; /* the table is full: misses recompile per use, as the
1107
+ * one-shot face always has */
1108
+ }
1109
+ re_prog p;
1110
+ if (re_compile(pat, plen, &p) != 0) {
1111
+ return 0; /* invalid pattern: not retained; every use reads -1,
1112
+ * exactly the uncached verdict */
1113
+ }
1114
+ t->slot[t->n].pat = pat;
1115
+ t->slot[t->n].plen = plen;
1116
+ t->slot[t->n].prog = p;
1117
+ t->n++;
1118
+ return 1;
1119
+ }
1120
+
1121
+ int yep_re_cache_search(const yep_re_cache* t, const char* pat, uint32_t plen, const char* s,
1122
+ uint32_t s_len) {
1123
+ if (t != NULL && pat != NULL) {
1124
+ for (int i = 0; i < t->n; i++) {
1125
+ if (t->slot[i].plen == plen && memcmp(t->slot[i].pat, pat, plen) == 0) {
1126
+ return re_vm(t->slot[i].prog.inst, t->slot[i].prog.cls, t->slot[i].prog.entry, s,
1127
+ s_len);
1128
+ }
1129
+ }
1130
+ }
1131
+ /* a miss (or no cache: the one-shot face) compiles fresh — identical
1132
+ * verdicts either way, and the handle stays untouched */
1133
+ return yep_re_search(pat, plen, s, s_len);
1134
+ }
@@ -269,12 +269,16 @@ uint64_t yep_validate_node_hash(const yep_dom* d, uint32_t id, int depth) {
269
269
  return h;
270
270
  }
271
271
  if (n->kind == YEP_DOM_MAPPING) {
272
+ /* mappings are UNORDERED (spec 3.2.1.3 canonical form): the
273
+ * fold is commutative (XOR of per-pair mixes) so key order
274
+ * cannot change the fingerprint */
272
275
  uint64_t h = 0x5EED4A9ULL;
273
276
  uint32_t k = n->first_child;
274
277
  while (k != UINT32_MAX) {
275
278
  uint32_t v = yep_dom_node(d, k) ? yep_dom_node(d, k)->next_sibling : UINT32_MAX;
276
- h = hash_mix(h, yep_validate_node_hash(d, k, depth + 1));
277
- h = hash_mix(h, yep_validate_node_hash(d, v, depth + 1));
279
+ uint64_t pair = hash_mix(0x51D1, yep_validate_node_hash(d, k, depth + 1));
280
+ pair = hash_mix(pair, yep_validate_node_hash(d, v, depth + 1));
281
+ h ^= pair;
278
282
  k = v != UINT32_MAX && yep_dom_node(d, v) ? yep_dom_node(d, v)->next_sibling
279
283
  : UINT32_MAX;
280
284
  }
@@ -285,21 +289,21 @@ uint64_t yep_validate_node_hash(const yep_dom* d, uint32_t id, int depth) {
285
289
  yep_view v = yep_dom_view(d, n->value);
286
290
  uint64_t h = 0x5CA1A + tag;
287
291
  if (tag == YEPTRIS_TAG_INT || tag == YEPTRIS_TAG_FLOAT) {
288
- /* canonical: the numeric value (0o13 == 0xB == 11; 1 == 1.0
289
- * rides the same number bucket only when the tags match) */
290
- int64_t iv = 0;
292
+ /* canonical: the numeric VALUE, one bucket for both tags (1 and
293
+ * 1.0 are the same canonical number) — the double bits; exact
294
+ * for i64-representable magnitudes up to 2^53, and beyond that
295
+ * two distinct ints may collide (equality is the authority, a
296
+ * collision only costs a probe) */
291
297
  double dv = 0;
298
+ int64_t iv = 0;
292
299
  if (yep_num_i64(v.p, v.len, &iv) == 0) {
293
- int is_neg = iv < 0;
294
- uint64_t mag = is_neg ? (uint64_t)(-(iv + 1)) + 1 : (uint64_t)iv;
295
- h = hash_mix(h, mag ^ (is_neg ? 0x8000000000000000ULL : 0));
296
- } else if (yep_num_f64(v.p, v.len, &dv) == 0) {
297
- uint64_t bits;
298
- memcpy(&bits, &dv, sizeof(bits));
299
- h = hash_mix(h, bits);
300
+ dv = (double)iv;
300
301
  } else {
301
- h = hash_mix(h, v.len);
302
+ yep_num_f64(v.p, v.len, &dv);
302
303
  }
304
+ uint64_t bits;
305
+ memcpy(&bits, &dv, sizeof(bits));
306
+ h = hash_mix(h, bits);
303
307
  return h;
304
308
  }
305
309
  for (uint32_t i = 0; i < v.len; i++) {
@@ -380,24 +384,31 @@ int yep_validate_node_equal(const yep_dom* da, uint32_t a, const yep_dom* db, ui
380
384
  }
381
385
  return 1;
382
386
  }
383
- /* scalars */
387
+ /* scalars: numbers (int or float, either tag) compare by numeric
388
+ * value (2020-12: 0 and 0.0 are one constant); everything else
389
+ * requires the same tag */
384
390
  uint8_t ta = yep_validate_scalar_tag(na), tb = yep_validate_scalar_tag(nb);
385
- if (ta != tb) {
391
+ int numa = ta == YEPTRIS_TAG_INT || ta == YEPTRIS_TAG_FLOAT;
392
+ int numb = tb == YEPTRIS_TAG_INT || tb == YEPTRIS_TAG_FLOAT;
393
+ if (numa != numb) {
386
394
  return 0;
387
395
  }
388
396
  yep_view va = yep_dom_view(da, na->value);
389
397
  yep_view vb = yep_dom_view(db, nb->value);
390
- if (ta == YEPTRIS_TAG_INT || ta == YEPTRIS_TAG_FLOAT) {
391
- double fa, fb;
398
+ if (numa) {
392
399
  int64_t ia, ib;
393
400
  if (yep_num_i64(va.p, va.len, &ia) == 0 && yep_num_i64(vb.p, vb.len, &ib) == 0) {
394
401
  return ia == ib;
395
402
  }
403
+ double fa, fb;
396
404
  if (yep_num_f64(va.p, va.len, &fa) == 0 && yep_num_f64(vb.p, vb.len, &fb) == 0) {
397
405
  return fa == fb;
398
406
  }
399
407
  return 0;
400
408
  }
409
+ if (ta != tb) {
410
+ return 0;
411
+ }
401
412
  return va.len == vb.len && (va.len == 0 || memcmp(va.p, vb.p, va.len) == 0);
402
413
  }
403
414
 
@@ -739,21 +750,31 @@ YEPTRIS_API void yeptris_validate_report_free(yeptris_validate_report* report) {
739
750
  yep_free(sys, report);
740
751
  }
741
752
 
742
- /* The shared tail: walk every document of the DOM into a fresh report. */
743
- static YeptrisStatus validate_dom(yep_dom* dom, const yep_resolver* fam, int json_family,
744
- yeptris_validate_report** out) {
753
+ /* The report constructor (internal.h's seam — one home for the shape). */
754
+ yeptris_validate_report* yep_validate_report_new(void) {
745
755
  const yep_allocator* sys = yep_system_allocator();
746
756
  yeptris_validate_report* r = yep_alloc(sys, sizeof(*r));
747
757
  if (r == NULL) {
748
- return YEPTRIS_ERROR_MEMORY;
758
+ return NULL;
749
759
  }
750
760
  r->diags = yep_alloc(sys, YEP_VALIDATE_MAX_DIAGS * sizeof(yeptris_diagnostic));
751
761
  if (r->diags == NULL) {
752
762
  yep_free(sys, r);
753
- return YEPTRIS_ERROR_MEMORY;
763
+ return NULL;
754
764
  }
755
765
  r->count = 0;
756
766
  r->truncated = 0;
767
+ return r;
768
+ }
769
+
770
+ /* The shared tail: walk every document of the DOM into a fresh report. */
771
+ static YeptrisStatus validate_dom(yep_dom* dom, const yep_resolver* fam, int json_family,
772
+ yeptris_validate_report** out) {
773
+ const yep_allocator* sys = yep_system_allocator();
774
+ yeptris_validate_report* r = yep_validate_report_new();
775
+ if (r == NULL) {
776
+ return YEPTRIS_ERROR_MEMORY;
777
+ }
757
778
 
758
779
  yep_vctx c;
759
780
  memset(&c, 0, sizeof(c));
@@ -816,18 +837,10 @@ YEPTRIS_API YeptrisStatus yeptris_validate_document(YeptrisDocument doc, Yeptris
816
837
  }
817
838
  if (dom == NULL) {
818
839
  /* an empty stream (or NULL doc): valid, empty report */
819
- const yep_allocator* sys = yep_system_allocator();
820
- yeptris_validate_report* r = yep_alloc(sys, sizeof(*r));
840
+ yeptris_validate_report* r = yep_validate_report_new();
821
841
  if (r == NULL) {
822
842
  return YEPTRIS_ERROR_MEMORY;
823
843
  }
824
- r->diags = yep_alloc(sys, YEP_VALIDATE_MAX_DIAGS * sizeof(yeptris_diagnostic));
825
- if (r->diags == NULL) {
826
- yep_free(sys, r);
827
- return YEPTRIS_ERROR_MEMORY;
828
- }
829
- r->count = 0;
830
- r->truncated = 0;
831
844
  *out = r;
832
845
  return YEPTRIS_OK;
833
846
  }
@@ -871,69 +884,17 @@ YEPTRIS_API YeptrisStatus yeptris_json_schema_validate(const char* schema, size_
871
884
  return YEPTRIS_ERROR_ARG;
872
885
  }
873
886
  *out = NULL;
874
- const yep_allocator* sys = yep_system_allocator();
875
- YeptrisParseOptions opts;
876
- memset(&opts, 0, sizeof(opts)); /* core12 both sides: the YAML->JSON data model */
877
-
878
- /* the schema document (document 0 of its stream) */
887
+ /* the compiled-schema economy, one call deep (#885 item 3): the
888
+ * schema parses ONCE here exactly as it always did, rides the SAME
889
+ * walk and report paths as the retained handle, and dies at return
890
+ * — the observable statuses and byte-for-byte verdicts are the
891
+ * suite port's 1298 cases, unchanged */
879
892
  YeptrisStatus st = YEPTRIS_OK;
880
- YeptrisDocument sdoc = yeptris_parse_eager_ex(schema, schema_len, &opts, &st);
881
- if (sdoc == NULL) {
882
- if (st == YEPTRIS_OK) {
883
- yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
884
- "the schema bytes hold no document");
885
- return YEPTRIS_ERROR_ARG;
886
- }
887
- return st; /* the schema did not parse */
888
- }
889
- yep_dom* sdom = yep_doc_dom((yeptris_document*)sdoc);
890
- if (sdom == NULL || sdom->dcount == 0) {
891
- yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
892
- "the schema bytes hold no document");
893
- yeptris_document_free(sdoc);
894
- return YEPTRIS_ERROR_ARG;
893
+ yeptris_schema* s = yep_schema_compile_st(schema, schema_len, &st);
894
+ if (s == NULL) {
895
+ return st; /* the schema's parse/arg failure, verbatim */
895
896
  }
896
-
897
- /* the instance stream */
898
- YeptrisDocument idoc = yeptris_parse_eager_ex(buf, len, &opts, &st);
899
- if (idoc == NULL) {
900
- yeptris_document_free(sdoc);
901
- if (st == YEPTRIS_OK) {
902
- /* an empty instance stream: nothing to invalidate */
903
- return yeptris_validate_document(NULL, YEPTRIS_SCHEMA_12_CORE, out);
904
- }
905
- return st;
906
- }
907
- yep_dom* idom = yep_doc_dom((yeptris_document*)idoc);
908
- if (idom == NULL) {
909
- yeptris_document_free(idoc);
910
- yeptris_document_free(sdoc);
911
- return YEPTRIS_ERROR_MEMORY;
912
- }
913
-
914
- yeptris_validate_report* r = yep_alloc(sys, sizeof(*r));
915
- if (r == NULL) {
916
- yeptris_document_free(idoc);
917
- yeptris_document_free(sdoc);
918
- return YEPTRIS_ERROR_MEMORY;
919
- }
920
- r->diags = yep_alloc(sys, YEP_VALIDATE_MAX_DIAGS * sizeof(yeptris_diagnostic));
921
- if (r->diags == NULL) {
922
- yep_free(sys, r);
923
- yeptris_document_free(idoc);
924
- yeptris_document_free(sdoc);
925
- return YEPTRIS_ERROR_MEMORY;
926
- }
927
- r->count = 0;
928
- r->truncated = 0;
929
-
930
- int rc = yep_jsonschema_run(sdom, sdom->docs[0], idom, idom->docs, idom->dcount, r);
931
- yeptris_document_free(idoc);
932
- yeptris_document_free(sdoc);
933
- if (rc != 0) {
934
- yeptris_validate_report_free(r);
935
- return YEPTRIS_ERROR_MEMORY;
936
- }
937
- *out = r;
938
- return r->count > 0 ? YEPTRIS_ERROR_SCHEMA : YEPTRIS_OK;
897
+ st = yeptris_schema_validate(s, buf, len, out);
898
+ yeptris_schema_free(s);
899
+ return st;
939
900
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yeptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.58.1
4
+ version: 0.6.59.1
5
5
  platform: arm-linux-eabihf
6
6
  authors:
7
7
  - Ribose Inc.
@@ -60,6 +60,7 @@ files:
60
60
  - lib/yeptris/psych/tree_builder.rb
61
61
  - lib/yeptris/psych/visitors.rb
62
62
  - lib/yeptris/schema.rb
63
+ - lib/yeptris/validation.rb
63
64
  - lib/yeptris/valueml.rb
64
65
  - lib/yeptris/yaml.rb
65
66
  - lib/yeptris/yaml/descriptor.rb
@@ -177,6 +178,7 @@ files:
177
178
  - vendor/libyeptris/src/yeptris/schema.c
178
179
  - vendor/libyeptris/src/yeptris/tape.c
179
180
  - vendor/libyeptris/src/yeptris/tape_in.h
181
+ - vendor/libyeptris/src/yeptris/validate/compile.c
180
182
  - vendor/libyeptris/src/yeptris/validate/internal.h
181
183
  - vendor/libyeptris/src/yeptris/validate/jsonschema.c
182
184
  - vendor/libyeptris/src/yeptris/validate/regexp.c