corvus_json_schema 0.1.3 → 0.1.4
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/Cargo.lock +2 -12
- data/README.md +10 -1
- data/THIRD-PARTY-NOTICES.txt +95 -0
- data/VERSIONHISTORY.md +24 -0
- data/ext/corvus_json_schema/Cargo.toml +2 -2
- data/ext/corvus_json_schema/clippy.toml +3 -4
- data/ext/corvus_json_schema/src/lib.rs +42 -19
- data/ext/corvus_json_schema/vendor/corvus-json-schema/Cargo.toml +9 -4
- data/ext/corvus_json_schema/vendor/corvus-json-schema/README.md +87 -4
- data/ext/corvus_json_schema/vendor/corvus-json-schema/THIRD-PARTY-NOTICES.txt +93 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/compiler.rs +88 -96
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/document.rs +2 -2
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/charset.rs +271 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/compile.rs +941 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/dfa.rs +366 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/fold.rs +138 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/mod.rs +329 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/parse.rs +1128 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/re2.rs +129 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/tables_gen.rs +6685 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/tests.rs +1672 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/ucd.rs +309 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/units.rs +165 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/ecmaregex/vm.rs +981 -0
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval/plan/fused.rs +13 -9
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval/plan.rs +12 -11
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/eval.rs +105 -73
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/formats.rs +95 -1
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/lib.rs +165 -30
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/loader.rs +19 -21
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/numbers.rs +1 -1
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/options.rs +199 -4
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/pattern.rs +758 -79
- data/ext/corvus_json_schema/vendor/corvus-json-schema/src/uri.rs +4 -4
- data/lib/corvus_json_schema/version.rb +1 -1
- data/lib/corvus_json_schema.rb +13 -3
- metadata +16 -2
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//! The size of the program a linear matcher compiles for a tree (`re2Cost` of `re2.go`).
|
|
2
|
+
//!
|
|
3
|
+
//! The Go engine writes a tree with no lookaround and no backreference in the syntax of the standard library's
|
|
4
|
+
//! regexp package, and bounds the program that package is asked to compile. This crate keeps its own translation
|
|
5
|
+
//! for the `regex` crate (see `pattern.rs`), and takes the same bound from here, for the `regex` crate and for the
|
|
6
|
+
//! automaton of `dfa.rs`.
|
|
7
|
+
|
|
8
|
+
use super::parse::{Node, NodeKind, UNBOUNDED};
|
|
9
|
+
|
|
10
|
+
/// Estimates the number of instructions of the program a linear matcher runs for a tree, which is what a match on
|
|
11
|
+
/// it can cost for each byte of a text when every instruction has a thread on it. It is [`re2_cost`] with one thing
|
|
12
|
+
/// a linear matcher does to a tree before it compiles it: alternatives that are each one character or one class are
|
|
13
|
+
/// one class, so `(?:a|b|c)*` is the two instructions of `[abc]*`. (The Go engine asks the regexp package what it
|
|
14
|
+
/// compiled. The `regex` crate does not say, so this is worked out from the tree.)
|
|
15
|
+
pub(crate) fn linear_cost(n: &Node) -> i64 {
|
|
16
|
+
match n.kind {
|
|
17
|
+
NodeKind::Empty => 0,
|
|
18
|
+
NodeKind::Alt if n.subs.iter().all(|sub| matches!(sub.kind, NodeKind::Char | NodeKind::Set)) => 1,
|
|
19
|
+
NodeKind::Cat | NodeKind::Alt | NodeKind::Group => {
|
|
20
|
+
let mut cost = n.subs.len() as i64 - 1;
|
|
21
|
+
for sub in &n.subs {
|
|
22
|
+
cost += linear_cost(sub);
|
|
23
|
+
if cost > MAX_RE2_COST {
|
|
24
|
+
return MAX_RE2_COST + 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
cost
|
|
28
|
+
}
|
|
29
|
+
NodeKind::Repeat => {
|
|
30
|
+
let copies = (if n.max == UNBOUNDED { n.min } else { n.max }).max(1) as i64;
|
|
31
|
+
let body = linear_cost(&n.subs[0]) + 1;
|
|
32
|
+
if copies > MAX_RE2_COST || body * copies > MAX_RE2_COST {
|
|
33
|
+
return MAX_RE2_COST + 1;
|
|
34
|
+
}
|
|
35
|
+
body * copies
|
|
36
|
+
}
|
|
37
|
+
_ => 1,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// The cap on [`linear_size`]: a program of that size is never compiled.
|
|
42
|
+
const MAX_LINEAR_SIZE: i64 = 1 << 30;
|
|
43
|
+
|
|
44
|
+
/// Estimates the memory the `regex` crate holds for the program it compiles for a tree, in bytes. That crate does
|
|
45
|
+
/// not say what a compiled pattern holds, so this is what the cache of compiled patterns counts for one. A
|
|
46
|
+
/// character is a state or a few, a class of ASCII characters is one, and a class with characters beyond ASCII is
|
|
47
|
+
/// the automaton of their UTF-8 forms, which grows with its ranges. A counted repetition is written out. Measured
|
|
48
|
+
/// against what the matcher of that crate reports (`regex-automata`'s `memory_usage`), the estimate was above it
|
|
49
|
+
/// for every pattern of the benchmark corpora that the crate is given and for classes of Unicode properties.
|
|
50
|
+
pub(crate) fn linear_size(n: &Node) -> i64 {
|
|
51
|
+
/// The bytes of one state, with its share of what the matcher holds beside the program.
|
|
52
|
+
const STATE: i64 = 96;
|
|
53
|
+
/// The states of a class with characters beyond ASCII, before its ranges are counted.
|
|
54
|
+
const WIDE_CLASS: i64 = 64;
|
|
55
|
+
/// What the matcher holds for any pattern, in states.
|
|
56
|
+
const FIXED: i64 = 56;
|
|
57
|
+
fn states(n: &Node) -> i64 {
|
|
58
|
+
match n.kind {
|
|
59
|
+
NodeKind::Empty => 0,
|
|
60
|
+
NodeKind::Char => {
|
|
61
|
+
if n.r < 0x80 {
|
|
62
|
+
1
|
|
63
|
+
} else {
|
|
64
|
+
4
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
NodeKind::Set => {
|
|
68
|
+
let ranges = &n.class().ranges;
|
|
69
|
+
if ranges.last().is_none_or(|&last| last < 0x80) { 1 } else { WIDE_CLASS + ranges.len() as i64 / 2 }
|
|
70
|
+
}
|
|
71
|
+
NodeKind::Cat | NodeKind::Alt | NodeKind::Group => {
|
|
72
|
+
let mut total = n.subs.len() as i64;
|
|
73
|
+
for sub in &n.subs {
|
|
74
|
+
total += states(sub);
|
|
75
|
+
if total > MAX_LINEAR_SIZE {
|
|
76
|
+
return MAX_LINEAR_SIZE;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
total
|
|
80
|
+
}
|
|
81
|
+
NodeKind::Repeat => {
|
|
82
|
+
let copies = (if n.max == UNBOUNDED { n.min } else { n.max }).max(1) as i64;
|
|
83
|
+
(copies * (states(&n.subs[0]) + 1)).min(MAX_LINEAR_SIZE)
|
|
84
|
+
}
|
|
85
|
+
_ => 1,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
(STATE * (states(n) + FIXED)).min(MAX_LINEAR_SIZE)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// The largest program a linear matcher is asked to compile, in the instructions estimated for it before it is
|
|
92
|
+
/// written (see [`re2_cost`]). A counted repetition is written out, so `a{1000}` is a thousand instructions, and a
|
|
93
|
+
/// linear matcher takes time proportional to its program to compile and, for every character of a text, to match.
|
|
94
|
+
pub(crate) const MAX_RE2_COST: i64 = 10_000;
|
|
95
|
+
|
|
96
|
+
/// Estimates the number of instructions of the program a linear matcher compiles for a tree. A counted repetition
|
|
97
|
+
/// is written out: `x{n,m}` is m copies of x, and `x{n,}` is n copies and a loop. The estimate stops counting above
|
|
98
|
+
/// `MAX_RE2_COST`.
|
|
99
|
+
pub(crate) fn re2_cost(n: &Node) -> i64 {
|
|
100
|
+
match n.kind {
|
|
101
|
+
NodeKind::Empty => 0,
|
|
102
|
+
NodeKind::Cat | NodeKind::Alt | NodeKind::Group => {
|
|
103
|
+
let mut cost = n.subs.len() as i64 - 1;
|
|
104
|
+
for sub in &n.subs {
|
|
105
|
+
cost += re2_cost(sub);
|
|
106
|
+
if cost > MAX_RE2_COST {
|
|
107
|
+
return MAX_RE2_COST + 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
cost
|
|
111
|
+
}
|
|
112
|
+
NodeKind::Repeat => {
|
|
113
|
+
let mut copies = n.max as i64;
|
|
114
|
+
if n.max == UNBOUNDED {
|
|
115
|
+
copies = n.min as i64;
|
|
116
|
+
}
|
|
117
|
+
if copies < 1 {
|
|
118
|
+
copies = 1;
|
|
119
|
+
}
|
|
120
|
+
// Each copy has the body and one instruction for the choice to enter it.
|
|
121
|
+
let body = re2_cost(&n.subs[0]) + 1;
|
|
122
|
+
if copies > MAX_RE2_COST || body * copies > MAX_RE2_COST {
|
|
123
|
+
return MAX_RE2_COST + 1;
|
|
124
|
+
}
|
|
125
|
+
body * copies
|
|
126
|
+
}
|
|
127
|
+
_ => 1,
|
|
128
|
+
}
|
|
129
|
+
}
|