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,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
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
/*
|
|
227
|
-
*
|
|
228
|
-
|
|
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
|
-
/* ----
|
|
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
|
-
|
|
823
|
-
|
|
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(
|
|
840
|
+
if (c_node(c, root, &f, 0) < 0) {
|
|
828
841
|
return -1;
|
|
829
842
|
}
|
|
830
|
-
if (emit_inst(
|
|
843
|
+
if (emit_inst(c, RE_OP_MATCH, 0, 0, 0) < 0) {
|
|
831
844
|
return -1;
|
|
832
845
|
}
|
|
833
|
-
frag_connect(&f, c
|
|
834
|
-
if (c
|
|
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 (
|
|
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)
|
|
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 =
|
|
887
|
+
uint8_t op = inst[pc].op;
|
|
864
888
|
if (op == RE_OP_JMP) {
|
|
865
|
-
stack[st_n++] =
|
|
889
|
+
stack[st_n++] = inst[pc].x;
|
|
866
890
|
} else if (op == RE_OP_SPLIT) {
|
|
867
|
-
stack[st_n++] =
|
|
868
|
-
stack[st_n++] =
|
|
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 (
|
|
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 =
|
|
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++] =
|
|
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 =
|
|
908
|
-
? (
|
|
909
|
-
: (
|
|
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++] =
|
|
937
|
+
nxt[nxt_n++] = inst[pc].x;
|
|
914
938
|
}
|
|
915
939
|
}
|
|
916
|
-
nxt[nxt_n++] = (uint16_t)
|
|
917
|
-
|
|
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 =
|
|
958
|
+
uint8_t op = inst[pc].op;
|
|
935
959
|
if (op == RE_OP_JMP) {
|
|
936
|
-
stack[st_n++] =
|
|
960
|
+
stack[st_n++] = inst[pc].x;
|
|
937
961
|
} else if (op == RE_OP_SPLIT) {
|
|
938
|
-
stack[st_n++] =
|
|
939
|
-
stack[st_n++] =
|
|
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 (
|
|
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 =
|
|
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++] =
|
|
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
|
-
|
|
277
|
-
|
|
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
|
|
289
|
-
*
|
|
290
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
743
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
881
|
-
if (
|
|
882
|
-
|
|
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
|
-
|
|
898
|
-
|
|
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.
|
|
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
|