/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.5.6/src/compile.rs
Line | Count | Source |
1 | | use std::collections::HashMap; |
2 | | use std::fmt; |
3 | | use std::iter; |
4 | | use std::result; |
5 | | use std::sync::Arc; |
6 | | |
7 | | use regex_syntax::hir::{self, Hir}; |
8 | | use regex_syntax::is_word_byte; |
9 | | use regex_syntax::utf8::{Utf8Range, Utf8Sequence, Utf8Sequences}; |
10 | | |
11 | | use crate::prog::{ |
12 | | EmptyLook, Inst, InstBytes, InstChar, InstEmptyLook, InstPtr, InstRanges, |
13 | | InstSave, InstSplit, Program, |
14 | | }; |
15 | | |
16 | | use crate::Error; |
17 | | |
18 | | type Result = result::Result<Patch, Error>; |
19 | | type ResultOrEmpty = result::Result<Option<Patch>, Error>; |
20 | | |
21 | | #[derive(Debug)] |
22 | | struct Patch { |
23 | | hole: Hole, |
24 | | entry: InstPtr, |
25 | | } |
26 | | |
27 | | /// A compiler translates a regular expression AST to a sequence of |
28 | | /// instructions. The sequence of instructions represents an NFA. |
29 | | // `Compiler` is only public via the `internal` module, so avoid deriving |
30 | | // `Debug`. |
31 | | #[allow(missing_debug_implementations)] |
32 | | pub struct Compiler { |
33 | | insts: Vec<MaybeInst>, |
34 | | compiled: Program, |
35 | | capture_name_idx: HashMap<String, usize>, |
36 | | num_exprs: usize, |
37 | | size_limit: usize, |
38 | | suffix_cache: SuffixCache, |
39 | | utf8_seqs: Option<Utf8Sequences>, |
40 | | byte_classes: ByteClassSet, |
41 | | // This keeps track of extra bytes allocated while compiling the regex |
42 | | // program. Currently, this corresponds to two things. First is the heap |
43 | | // memory allocated by Unicode character classes ('InstRanges'). Second is |
44 | | // a "fake" amount of memory used by empty sub-expressions, so that enough |
45 | | // empty sub-expressions will ultimately trigger the compiler to bail |
46 | | // because of a size limit restriction. (That empty sub-expressions don't |
47 | | // add to heap memory usage is more-or-less an implementation detail.) In |
48 | | // the second case, if we don't bail, then an excessively large repetition |
49 | | // on an empty sub-expression can result in the compiler using a very large |
50 | | // amount of CPU time. |
51 | | extra_inst_bytes: usize, |
52 | | } |
53 | | |
54 | | impl Compiler { |
55 | | /// Create a new regular expression compiler. |
56 | | /// |
57 | | /// Various options can be set before calling `compile` on an expression. |
58 | 54 | pub fn new() -> Self { |
59 | 54 | Compiler { |
60 | 54 | insts: vec![], |
61 | 54 | compiled: Program::new(), |
62 | 54 | capture_name_idx: HashMap::new(), |
63 | 54 | num_exprs: 0, |
64 | 54 | size_limit: 10 * (1 << 20), |
65 | 54 | suffix_cache: SuffixCache::new(1000), |
66 | 54 | utf8_seqs: Some(Utf8Sequences::new('\x00', '\x00')), |
67 | 54 | byte_classes: ByteClassSet::new(), |
68 | 54 | extra_inst_bytes: 0, |
69 | 54 | } |
70 | 54 | } <regex::compile::Compiler>::new Line | Count | Source | 58 | 27 | pub fn new() -> Self { | 59 | 27 | Compiler { | 60 | 27 | insts: vec![], | 61 | 27 | compiled: Program::new(), | 62 | 27 | capture_name_idx: HashMap::new(), | 63 | 27 | num_exprs: 0, | 64 | 27 | size_limit: 10 * (1 << 20), | 65 | 27 | suffix_cache: SuffixCache::new(1000), | 66 | 27 | utf8_seqs: Some(Utf8Sequences::new('\x00', '\x00')), | 67 | 27 | byte_classes: ByteClassSet::new(), | 68 | 27 | extra_inst_bytes: 0, | 69 | 27 | } | 70 | 27 | } |
<regex::compile::Compiler>::new Line | Count | Source | 58 | 27 | pub fn new() -> Self { | 59 | 27 | Compiler { | 60 | 27 | insts: vec![], | 61 | 27 | compiled: Program::new(), | 62 | 27 | capture_name_idx: HashMap::new(), | 63 | 27 | num_exprs: 0, | 64 | 27 | size_limit: 10 * (1 << 20), | 65 | 27 | suffix_cache: SuffixCache::new(1000), | 66 | 27 | utf8_seqs: Some(Utf8Sequences::new('\x00', '\x00')), | 67 | 27 | byte_classes: ByteClassSet::new(), | 68 | 27 | extra_inst_bytes: 0, | 69 | 27 | } | 70 | 27 | } |
|
71 | | |
72 | | /// The size of the resulting program is limited by size_limit. If |
73 | | /// the program approximately exceeds the given size (in bytes), then |
74 | | /// compilation will stop and return an error. |
75 | 54 | pub fn size_limit(mut self, size_limit: usize) -> Self { |
76 | 54 | self.size_limit = size_limit; |
77 | 54 | self |
78 | 54 | } <regex::compile::Compiler>::size_limit Line | Count | Source | 75 | 27 | pub fn size_limit(mut self, size_limit: usize) -> Self { | 76 | 27 | self.size_limit = size_limit; | 77 | 27 | self | 78 | 27 | } |
<regex::compile::Compiler>::size_limit Line | Count | Source | 75 | 27 | pub fn size_limit(mut self, size_limit: usize) -> Self { | 76 | 27 | self.size_limit = size_limit; | 77 | 27 | self | 78 | 27 | } |
|
79 | | |
80 | | /// If bytes is true, then the program is compiled as a byte based |
81 | | /// automaton, which incorporates UTF-8 decoding into the machine. If it's |
82 | | /// false, then the automaton is Unicode scalar value based, e.g., an |
83 | | /// engine utilizing such an automaton is responsible for UTF-8 decoding. |
84 | | /// |
85 | | /// The specific invariant is that when returning a byte based machine, |
86 | | /// the neither the `Char` nor `Ranges` instructions are produced. |
87 | | /// Conversely, when producing a Unicode scalar value machine, the `Bytes` |
88 | | /// instruction is never produced. |
89 | | /// |
90 | | /// Note that `dfa(true)` implies `bytes(true)`. |
91 | 18 | pub fn bytes(mut self, yes: bool) -> Self { |
92 | 18 | self.compiled.is_bytes = yes; |
93 | 18 | self |
94 | 18 | } <regex::compile::Compiler>::bytes Line | Count | Source | 91 | 9 | pub fn bytes(mut self, yes: bool) -> Self { | 92 | 9 | self.compiled.is_bytes = yes; | 93 | 9 | self | 94 | 9 | } |
<regex::compile::Compiler>::bytes Line | Count | Source | 91 | 9 | pub fn bytes(mut self, yes: bool) -> Self { | 92 | 9 | self.compiled.is_bytes = yes; | 93 | 9 | self | 94 | 9 | } |
|
95 | | |
96 | | /// When disabled, the program compiled may match arbitrary bytes. |
97 | | /// |
98 | | /// When enabled (the default), all compiled programs exclusively match |
99 | | /// valid UTF-8 bytes. |
100 | 54 | pub fn only_utf8(mut self, yes: bool) -> Self { |
101 | 54 | self.compiled.only_utf8 = yes; |
102 | 54 | self |
103 | 54 | } <regex::compile::Compiler>::only_utf8 Line | Count | Source | 100 | 27 | pub fn only_utf8(mut self, yes: bool) -> Self { | 101 | 27 | self.compiled.only_utf8 = yes; | 102 | 27 | self | 103 | 27 | } |
<regex::compile::Compiler>::only_utf8 Line | Count | Source | 100 | 27 | pub fn only_utf8(mut self, yes: bool) -> Self { | 101 | 27 | self.compiled.only_utf8 = yes; | 102 | 27 | self | 103 | 27 | } |
|
104 | | |
105 | | /// When set, the machine returned is suitable for use in the DFA matching |
106 | | /// engine. |
107 | | /// |
108 | | /// In particular, this ensures that if the regex is not anchored in the |
109 | | /// beginning, then a preceding `.*?` is included in the program. (The NFA |
110 | | /// based engines handle the preceding `.*?` explicitly, which is difficult |
111 | | /// or impossible in the DFA engine.) |
112 | 36 | pub fn dfa(mut self, yes: bool) -> Self { |
113 | 36 | self.compiled.is_dfa = yes; |
114 | 36 | self |
115 | 36 | } <regex::compile::Compiler>::dfa Line | Count | Source | 112 | 18 | pub fn dfa(mut self, yes: bool) -> Self { | 113 | 18 | self.compiled.is_dfa = yes; | 114 | 18 | self | 115 | 18 | } |
<regex::compile::Compiler>::dfa Line | Count | Source | 112 | 18 | pub fn dfa(mut self, yes: bool) -> Self { | 113 | 18 | self.compiled.is_dfa = yes; | 114 | 18 | self | 115 | 18 | } |
|
116 | | |
117 | | /// When set, the machine returned is suitable for matching text in |
118 | | /// reverse. In particular, all concatenations are flipped. |
119 | 18 | pub fn reverse(mut self, yes: bool) -> Self { |
120 | 18 | self.compiled.is_reverse = yes; |
121 | 18 | self |
122 | 18 | } <regex::compile::Compiler>::reverse Line | Count | Source | 119 | 9 | pub fn reverse(mut self, yes: bool) -> Self { | 120 | 9 | self.compiled.is_reverse = yes; | 121 | 9 | self | 122 | 9 | } |
<regex::compile::Compiler>::reverse Line | Count | Source | 119 | 9 | pub fn reverse(mut self, yes: bool) -> Self { | 120 | 9 | self.compiled.is_reverse = yes; | 121 | 9 | self | 122 | 9 | } |
|
123 | | |
124 | | /// Compile a regular expression given its AST. |
125 | | /// |
126 | | /// The compiler is guaranteed to succeed unless the program exceeds the |
127 | | /// specified size limit. If the size limit is exceeded, then compilation |
128 | | /// stops and returns an error. |
129 | 54 | pub fn compile(mut self, exprs: &[Hir]) -> result::Result<Program, Error> { |
130 | 54 | debug_assert!(!exprs.is_empty()); |
131 | 54 | self.num_exprs = exprs.len(); |
132 | 54 | if exprs.len() == 1 { |
133 | 54 | self.compile_one(&exprs[0]) |
134 | | } else { |
135 | 0 | self.compile_many(exprs) |
136 | | } |
137 | 54 | } <regex::compile::Compiler>::compile Line | Count | Source | 129 | 27 | pub fn compile(mut self, exprs: &[Hir]) -> result::Result<Program, Error> { | 130 | 27 | debug_assert!(!exprs.is_empty()); | 131 | 27 | self.num_exprs = exprs.len(); | 132 | 27 | if exprs.len() == 1 { | 133 | 27 | self.compile_one(&exprs[0]) | 134 | | } else { | 135 | 0 | self.compile_many(exprs) | 136 | | } | 137 | 27 | } |
<regex::compile::Compiler>::compile Line | Count | Source | 129 | 27 | pub fn compile(mut self, exprs: &[Hir]) -> result::Result<Program, Error> { | 130 | 27 | debug_assert!(!exprs.is_empty()); | 131 | 27 | self.num_exprs = exprs.len(); | 132 | 27 | if exprs.len() == 1 { | 133 | 27 | self.compile_one(&exprs[0]) | 134 | | } else { | 135 | 0 | self.compile_many(exprs) | 136 | | } | 137 | 27 | } |
|
138 | | |
139 | 54 | fn compile_one(mut self, expr: &Hir) -> result::Result<Program, Error> { |
140 | | // If we're compiling a forward DFA and we aren't anchored, then |
141 | | // add a `.*?` before the first capture group. |
142 | | // Other matching engines handle this by baking the logic into the |
143 | | // matching engine itself. |
144 | 54 | let mut dotstar_patch = Patch { hole: Hole::None, entry: 0 }; |
145 | 54 | self.compiled.is_anchored_start = expr.is_anchored_start(); |
146 | 54 | self.compiled.is_anchored_end = expr.is_anchored_end(); |
147 | 54 | if self.compiled.needs_dotstar() { |
148 | 0 | dotstar_patch = self.c_dotstar()?; |
149 | 0 | self.compiled.start = dotstar_patch.entry; |
150 | 54 | } |
151 | 54 | self.compiled.captures = vec![None]; |
152 | 54 | let patch = self.c_capture(0, expr)?.unwrap_or(self.next_inst()); |
153 | 54 | if self.compiled.needs_dotstar() { |
154 | 0 | self.fill(dotstar_patch.hole, patch.entry); |
155 | 54 | } else { |
156 | 54 | self.compiled.start = patch.entry; |
157 | 54 | } |
158 | 54 | self.fill_to_next(patch.hole); |
159 | 54 | self.compiled.matches = vec![self.insts.len()]; |
160 | 54 | self.push_compiled(Inst::Match(0)); |
161 | 54 | self.compile_finish() |
162 | 54 | } <regex::compile::Compiler>::compile_one Line | Count | Source | 139 | 27 | fn compile_one(mut self, expr: &Hir) -> result::Result<Program, Error> { | 140 | | // If we're compiling a forward DFA and we aren't anchored, then | 141 | | // add a `.*?` before the first capture group. | 142 | | // Other matching engines handle this by baking the logic into the | 143 | | // matching engine itself. | 144 | 27 | let mut dotstar_patch = Patch { hole: Hole::None, entry: 0 }; | 145 | 27 | self.compiled.is_anchored_start = expr.is_anchored_start(); | 146 | 27 | self.compiled.is_anchored_end = expr.is_anchored_end(); | 147 | 27 | if self.compiled.needs_dotstar() { | 148 | 0 | dotstar_patch = self.c_dotstar()?; | 149 | 0 | self.compiled.start = dotstar_patch.entry; | 150 | 27 | } | 151 | 27 | self.compiled.captures = vec![None]; | 152 | 27 | let patch = self.c_capture(0, expr)?.unwrap_or(self.next_inst()); | 153 | 27 | if self.compiled.needs_dotstar() { | 154 | 0 | self.fill(dotstar_patch.hole, patch.entry); | 155 | 27 | } else { | 156 | 27 | self.compiled.start = patch.entry; | 157 | 27 | } | 158 | 27 | self.fill_to_next(patch.hole); | 159 | 27 | self.compiled.matches = vec![self.insts.len()]; | 160 | 27 | self.push_compiled(Inst::Match(0)); | 161 | 27 | self.compile_finish() | 162 | 27 | } |
<regex::compile::Compiler>::compile_one Line | Count | Source | 139 | 27 | fn compile_one(mut self, expr: &Hir) -> result::Result<Program, Error> { | 140 | | // If we're compiling a forward DFA and we aren't anchored, then | 141 | | // add a `.*?` before the first capture group. | 142 | | // Other matching engines handle this by baking the logic into the | 143 | | // matching engine itself. | 144 | 27 | let mut dotstar_patch = Patch { hole: Hole::None, entry: 0 }; | 145 | 27 | self.compiled.is_anchored_start = expr.is_anchored_start(); | 146 | 27 | self.compiled.is_anchored_end = expr.is_anchored_end(); | 147 | 27 | if self.compiled.needs_dotstar() { | 148 | 0 | dotstar_patch = self.c_dotstar()?; | 149 | 0 | self.compiled.start = dotstar_patch.entry; | 150 | 27 | } | 151 | 27 | self.compiled.captures = vec![None]; | 152 | 27 | let patch = self.c_capture(0, expr)?.unwrap_or(self.next_inst()); | 153 | 27 | if self.compiled.needs_dotstar() { | 154 | 0 | self.fill(dotstar_patch.hole, patch.entry); | 155 | 27 | } else { | 156 | 27 | self.compiled.start = patch.entry; | 157 | 27 | } | 158 | 27 | self.fill_to_next(patch.hole); | 159 | 27 | self.compiled.matches = vec![self.insts.len()]; | 160 | 27 | self.push_compiled(Inst::Match(0)); | 161 | 27 | self.compile_finish() | 162 | 27 | } |
|
163 | | |
164 | 0 | fn compile_many( |
165 | 0 | mut self, |
166 | 0 | exprs: &[Hir], |
167 | 0 | ) -> result::Result<Program, Error> { |
168 | 0 | debug_assert!(exprs.len() > 1); |
169 | | |
170 | | self.compiled.is_anchored_start = |
171 | 0 | exprs.iter().all(|e| e.is_anchored_start()); Unexecuted instantiation: <regex::compile::Compiler>::compile_many::{closure#0}Unexecuted instantiation: <regex::compile::Compiler>::compile_many::{closure#0} |
172 | | self.compiled.is_anchored_end = |
173 | 0 | exprs.iter().all(|e| e.is_anchored_end()); Unexecuted instantiation: <regex::compile::Compiler>::compile_many::{closure#1}Unexecuted instantiation: <regex::compile::Compiler>::compile_many::{closure#1} |
174 | 0 | let mut dotstar_patch = Patch { hole: Hole::None, entry: 0 }; |
175 | 0 | if self.compiled.needs_dotstar() { |
176 | 0 | dotstar_patch = self.c_dotstar()?; |
177 | 0 | self.compiled.start = dotstar_patch.entry; |
178 | 0 | } else { |
179 | 0 | self.compiled.start = 0; // first instruction is always split |
180 | 0 | } |
181 | 0 | self.fill_to_next(dotstar_patch.hole); |
182 | | |
183 | 0 | let mut prev_hole = Hole::None; |
184 | 0 | for (i, expr) in exprs[0..exprs.len() - 1].iter().enumerate() { |
185 | 0 | self.fill_to_next(prev_hole); |
186 | 0 | let split = self.push_split_hole(); |
187 | 0 | let Patch { hole, entry } = |
188 | 0 | self.c_capture(0, expr)?.unwrap_or(self.next_inst()); |
189 | 0 | self.fill_to_next(hole); |
190 | 0 | self.compiled.matches.push(self.insts.len()); |
191 | 0 | self.push_compiled(Inst::Match(i)); |
192 | 0 | prev_hole = self.fill_split(split, Some(entry), None); |
193 | | } |
194 | 0 | let i = exprs.len() - 1; |
195 | 0 | let Patch { hole, entry } = |
196 | 0 | self.c_capture(0, &exprs[i])?.unwrap_or(self.next_inst()); |
197 | 0 | self.fill(prev_hole, entry); |
198 | 0 | self.fill_to_next(hole); |
199 | 0 | self.compiled.matches.push(self.insts.len()); |
200 | 0 | self.push_compiled(Inst::Match(i)); |
201 | 0 | self.compile_finish() |
202 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::compile_many Unexecuted instantiation: <regex::compile::Compiler>::compile_many |
203 | | |
204 | 54 | fn compile_finish(mut self) -> result::Result<Program, Error> { |
205 | 54 | self.compiled.insts = |
206 | 4.62k | self.insts.into_iter().map(|inst| inst.unwrap()).collect(); <regex::compile::Compiler>::compile_finish::{closure#0}Line | Count | Source | 206 | 2.31k | self.insts.into_iter().map(|inst| inst.unwrap()).collect(); |
<regex::compile::Compiler>::compile_finish::{closure#0}Line | Count | Source | 206 | 2.31k | self.insts.into_iter().map(|inst| inst.unwrap()).collect(); |
|
207 | 54 | self.compiled.byte_classes = self.byte_classes.byte_classes(); |
208 | 54 | self.compiled.capture_name_idx = Arc::new(self.capture_name_idx); |
209 | 54 | Ok(self.compiled) |
210 | 54 | } <regex::compile::Compiler>::compile_finish Line | Count | Source | 204 | 27 | fn compile_finish(mut self) -> result::Result<Program, Error> { | 205 | 27 | self.compiled.insts = | 206 | 27 | self.insts.into_iter().map(|inst| inst.unwrap()).collect(); | 207 | 27 | self.compiled.byte_classes = self.byte_classes.byte_classes(); | 208 | 27 | self.compiled.capture_name_idx = Arc::new(self.capture_name_idx); | 209 | 27 | Ok(self.compiled) | 210 | 27 | } |
<regex::compile::Compiler>::compile_finish Line | Count | Source | 204 | 27 | fn compile_finish(mut self) -> result::Result<Program, Error> { | 205 | 27 | self.compiled.insts = | 206 | 27 | self.insts.into_iter().map(|inst| inst.unwrap()).collect(); | 207 | 27 | self.compiled.byte_classes = self.byte_classes.byte_classes(); | 208 | 27 | self.compiled.capture_name_idx = Arc::new(self.capture_name_idx); | 209 | 27 | Ok(self.compiled) | 210 | 27 | } |
|
211 | | |
212 | | /// Compile expr into self.insts, returning a patch on success, |
213 | | /// or an error if we run out of memory. |
214 | | /// |
215 | | /// All of the c_* methods of the compiler share the contract outlined |
216 | | /// here. |
217 | | /// |
218 | | /// The main thing that a c_* method does is mutate `self.insts` |
219 | | /// to add a list of mostly compiled instructions required to execute |
220 | | /// the given expression. `self.insts` contains MaybeInsts rather than |
221 | | /// Insts because there is some backpatching required. |
222 | | /// |
223 | | /// The `Patch` value returned by each c_* method provides metadata |
224 | | /// about the compiled instructions emitted to `self.insts`. The |
225 | | /// `entry` member of the patch refers to the first instruction |
226 | | /// (the entry point), while the `hole` member contains zero or |
227 | | /// more offsets to partial instructions that need to be backpatched. |
228 | | /// The c_* routine can't know where its list of instructions are going to |
229 | | /// jump to after execution, so it is up to the caller to patch |
230 | | /// these jumps to point to the right place. So compiling some |
231 | | /// expression, e, we would end up with a situation that looked like: |
232 | | /// |
233 | | /// ```text |
234 | | /// self.insts = [ ..., i1, i2, ..., iexit1, ..., iexitn, ...] |
235 | | /// ^ ^ ^ |
236 | | /// | \ / |
237 | | /// entry \ / |
238 | | /// hole |
239 | | /// ``` |
240 | | /// |
241 | | /// To compile two expressions, e1 and e2, concatenated together we |
242 | | /// would do: |
243 | | /// |
244 | | /// ```ignore |
245 | | /// let patch1 = self.c(e1); |
246 | | /// let patch2 = self.c(e2); |
247 | | /// ``` |
248 | | /// |
249 | | /// while leaves us with a situation that looks like |
250 | | /// |
251 | | /// ```text |
252 | | /// self.insts = [ ..., i1, ..., iexit1, ..., i2, ..., iexit2 ] |
253 | | /// ^ ^ ^ ^ |
254 | | /// | | | | |
255 | | /// entry1 hole1 entry2 hole2 |
256 | | /// ``` |
257 | | /// |
258 | | /// Then to merge the two patches together into one we would backpatch |
259 | | /// hole1 with entry2 and return a new patch that enters at entry1 |
260 | | /// and has hole2 for a hole. In fact, if you look at the c_concat |
261 | | /// method you will see that it does exactly this, though it handles |
262 | | /// a list of expressions rather than just the two that we use for |
263 | | /// an example. |
264 | | /// |
265 | | /// Ok(None) is returned when an expression is compiled to no |
266 | | /// instruction, and so no patch.entry value makes sense. |
267 | 4.03k | fn c(&mut self, expr: &Hir) -> ResultOrEmpty { |
268 | | use crate::prog; |
269 | | use regex_syntax::hir::HirKind::*; |
270 | | |
271 | 4.03k | self.check_size()?; |
272 | 4.03k | match *expr.kind() { |
273 | 0 | Empty => self.c_empty(), |
274 | 1.67k | Literal(hir::Literal::Unicode(c)) => self.c_char(c), |
275 | 0 | Literal(hir::Literal::Byte(b)) => { |
276 | 0 | assert!(self.compiled.uses_bytes()); |
277 | 0 | self.c_byte(b) |
278 | | } |
279 | 630 | Class(hir::Class::Unicode(ref cls)) => self.c_class(cls.ranges()), |
280 | 0 | Class(hir::Class::Bytes(ref cls)) => { |
281 | 0 | if self.compiled.uses_bytes() { |
282 | 0 | self.c_class_bytes(cls.ranges()) |
283 | | } else { |
284 | 0 | assert!(cls.is_all_ascii()); |
285 | 0 | let mut char_ranges = vec![]; |
286 | 0 | for r in cls.iter() { |
287 | 0 | let (s, e) = (r.start() as char, r.end() as char); |
288 | 0 | char_ranges.push(hir::ClassUnicodeRange::new(s, e)); |
289 | 0 | } |
290 | 0 | self.c_class(&char_ranges) |
291 | | } |
292 | | } |
293 | 0 | Anchor(hir::Anchor::StartLine) if self.compiled.is_reverse => { |
294 | 0 | self.byte_classes.set_range(b'\n', b'\n'); |
295 | 0 | self.c_empty_look(prog::EmptyLook::EndLine) |
296 | | } |
297 | | Anchor(hir::Anchor::StartLine) => { |
298 | 0 | self.byte_classes.set_range(b'\n', b'\n'); |
299 | 0 | self.c_empty_look(prog::EmptyLook::StartLine) |
300 | | } |
301 | 0 | Anchor(hir::Anchor::EndLine) if self.compiled.is_reverse => { |
302 | 0 | self.byte_classes.set_range(b'\n', b'\n'); |
303 | 0 | self.c_empty_look(prog::EmptyLook::StartLine) |
304 | | } |
305 | | Anchor(hir::Anchor::EndLine) => { |
306 | 0 | self.byte_classes.set_range(b'\n', b'\n'); |
307 | 0 | self.c_empty_look(prog::EmptyLook::EndLine) |
308 | | } |
309 | 18 | Anchor(hir::Anchor::StartText) if self.compiled.is_reverse => { |
310 | 18 | self.c_empty_look(prog::EmptyLook::EndText) |
311 | | } |
312 | | Anchor(hir::Anchor::StartText) => { |
313 | 36 | self.c_empty_look(prog::EmptyLook::StartText) |
314 | | } |
315 | 18 | Anchor(hir::Anchor::EndText) if self.compiled.is_reverse => { |
316 | 18 | self.c_empty_look(prog::EmptyLook::StartText) |
317 | | } |
318 | | Anchor(hir::Anchor::EndText) => { |
319 | 36 | self.c_empty_look(prog::EmptyLook::EndText) |
320 | | } |
321 | | WordBoundary(hir::WordBoundary::Unicode) => { |
322 | 0 | if !cfg!(feature = "unicode-perl") { |
323 | 0 | return Err(Error::Syntax( |
324 | 0 | "Unicode word boundaries are unavailable when \ |
325 | 0 | the unicode-perl feature is disabled" |
326 | 0 | .to_string(), |
327 | 0 | )); |
328 | 0 | } |
329 | 0 | self.compiled.has_unicode_word_boundary = true; |
330 | 0 | self.byte_classes.set_word_boundary(); |
331 | | // We also make sure that all ASCII bytes are in a different |
332 | | // class from non-ASCII bytes. Otherwise, it's possible for |
333 | | // ASCII bytes to get lumped into the same class as non-ASCII |
334 | | // bytes. This in turn may cause the lazy DFA to falsely start |
335 | | // when it sees an ASCII byte that maps to a byte class with |
336 | | // non-ASCII bytes. This ensures that never happens. |
337 | 0 | self.byte_classes.set_range(0, 0x7F); |
338 | 0 | self.c_empty_look(prog::EmptyLook::WordBoundary) |
339 | | } |
340 | | WordBoundary(hir::WordBoundary::UnicodeNegate) => { |
341 | 0 | if !cfg!(feature = "unicode-perl") { |
342 | 0 | return Err(Error::Syntax( |
343 | 0 | "Unicode word boundaries are unavailable when \ |
344 | 0 | the unicode-perl feature is disabled" |
345 | 0 | .to_string(), |
346 | 0 | )); |
347 | 0 | } |
348 | 0 | self.compiled.has_unicode_word_boundary = true; |
349 | 0 | self.byte_classes.set_word_boundary(); |
350 | | // See comments above for why we set the ASCII range here. |
351 | 0 | self.byte_classes.set_range(0, 0x7F); |
352 | 0 | self.c_empty_look(prog::EmptyLook::NotWordBoundary) |
353 | | } |
354 | | WordBoundary(hir::WordBoundary::Ascii) => { |
355 | 0 | self.byte_classes.set_word_boundary(); |
356 | 0 | self.c_empty_look(prog::EmptyLook::WordBoundaryAscii) |
357 | | } |
358 | | WordBoundary(hir::WordBoundary::AsciiNegate) => { |
359 | 0 | self.byte_classes.set_word_boundary(); |
360 | 0 | self.c_empty_look(prog::EmptyLook::NotWordBoundaryAscii) |
361 | | } |
362 | 324 | Group(ref g) => match g.kind { |
363 | 126 | hir::GroupKind::NonCapturing => self.c(&g.hir), |
364 | 198 | hir::GroupKind::CaptureIndex(index) => { |
365 | 198 | if index as usize >= self.compiled.captures.len() { |
366 | 168 | self.compiled.captures.push(None); |
367 | 168 | } |
368 | 198 | self.c_capture(2 * index as usize, &g.hir) |
369 | | } |
370 | 0 | hir::GroupKind::CaptureName { index, ref name } => { |
371 | 0 | if index as usize >= self.compiled.captures.len() { |
372 | 0 | let n = name.to_string(); |
373 | 0 | self.compiled.captures.push(Some(n.clone())); |
374 | 0 | self.capture_name_idx.insert(n, index as usize); |
375 | 0 | } |
376 | 0 | self.c_capture(2 * index as usize, &g.hir) |
377 | | } |
378 | | }, |
379 | 360 | Concat(ref es) => { |
380 | 360 | if self.compiled.is_reverse { |
381 | 120 | self.c_concat(es.iter().rev()) |
382 | | } else { |
383 | 240 | self.c_concat(es) |
384 | | } |
385 | | } |
386 | 36 | Alternation(ref es) => self.c_alternate(&**es), |
387 | 900 | Repetition(ref rep) => self.c_repeat(rep), |
388 | | } |
389 | 4.03k | } <regex::compile::Compiler>::c Line | Count | Source | 267 | 2.01k | fn c(&mut self, expr: &Hir) -> ResultOrEmpty { | 268 | | use crate::prog; | 269 | | use regex_syntax::hir::HirKind::*; | 270 | | | 271 | 2.01k | self.check_size()?; | 272 | 2.01k | match *expr.kind() { | 273 | 0 | Empty => self.c_empty(), | 274 | 837 | Literal(hir::Literal::Unicode(c)) => self.c_char(c), | 275 | 0 | Literal(hir::Literal::Byte(b)) => { | 276 | 0 | assert!(self.compiled.uses_bytes()); | 277 | 0 | self.c_byte(b) | 278 | | } | 279 | 315 | Class(hir::Class::Unicode(ref cls)) => self.c_class(cls.ranges()), | 280 | 0 | Class(hir::Class::Bytes(ref cls)) => { | 281 | 0 | if self.compiled.uses_bytes() { | 282 | 0 | self.c_class_bytes(cls.ranges()) | 283 | | } else { | 284 | 0 | assert!(cls.is_all_ascii()); | 285 | 0 | let mut char_ranges = vec![]; | 286 | 0 | for r in cls.iter() { | 287 | 0 | let (s, e) = (r.start() as char, r.end() as char); | 288 | 0 | char_ranges.push(hir::ClassUnicodeRange::new(s, e)); | 289 | 0 | } | 290 | 0 | self.c_class(&char_ranges) | 291 | | } | 292 | | } | 293 | 0 | Anchor(hir::Anchor::StartLine) if self.compiled.is_reverse => { | 294 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 295 | 0 | self.c_empty_look(prog::EmptyLook::EndLine) | 296 | | } | 297 | | Anchor(hir::Anchor::StartLine) => { | 298 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 299 | 0 | self.c_empty_look(prog::EmptyLook::StartLine) | 300 | | } | 301 | 0 | Anchor(hir::Anchor::EndLine) if self.compiled.is_reverse => { | 302 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 303 | 0 | self.c_empty_look(prog::EmptyLook::StartLine) | 304 | | } | 305 | | Anchor(hir::Anchor::EndLine) => { | 306 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 307 | 0 | self.c_empty_look(prog::EmptyLook::EndLine) | 308 | | } | 309 | 9 | Anchor(hir::Anchor::StartText) if self.compiled.is_reverse => { | 310 | 9 | self.c_empty_look(prog::EmptyLook::EndText) | 311 | | } | 312 | | Anchor(hir::Anchor::StartText) => { | 313 | 18 | self.c_empty_look(prog::EmptyLook::StartText) | 314 | | } | 315 | 9 | Anchor(hir::Anchor::EndText) if self.compiled.is_reverse => { | 316 | 9 | self.c_empty_look(prog::EmptyLook::StartText) | 317 | | } | 318 | | Anchor(hir::Anchor::EndText) => { | 319 | 18 | self.c_empty_look(prog::EmptyLook::EndText) | 320 | | } | 321 | | WordBoundary(hir::WordBoundary::Unicode) => { | 322 | 0 | if !cfg!(feature = "unicode-perl") { | 323 | 0 | return Err(Error::Syntax( | 324 | 0 | "Unicode word boundaries are unavailable when \ | 325 | 0 | the unicode-perl feature is disabled" | 326 | 0 | .to_string(), | 327 | 0 | )); | 328 | 0 | } | 329 | 0 | self.compiled.has_unicode_word_boundary = true; | 330 | 0 | self.byte_classes.set_word_boundary(); | 331 | | // We also make sure that all ASCII bytes are in a different | 332 | | // class from non-ASCII bytes. Otherwise, it's possible for | 333 | | // ASCII bytes to get lumped into the same class as non-ASCII | 334 | | // bytes. This in turn may cause the lazy DFA to falsely start | 335 | | // when it sees an ASCII byte that maps to a byte class with | 336 | | // non-ASCII bytes. This ensures that never happens. | 337 | 0 | self.byte_classes.set_range(0, 0x7F); | 338 | 0 | self.c_empty_look(prog::EmptyLook::WordBoundary) | 339 | | } | 340 | | WordBoundary(hir::WordBoundary::UnicodeNegate) => { | 341 | 0 | if !cfg!(feature = "unicode-perl") { | 342 | 0 | return Err(Error::Syntax( | 343 | 0 | "Unicode word boundaries are unavailable when \ | 344 | 0 | the unicode-perl feature is disabled" | 345 | 0 | .to_string(), | 346 | 0 | )); | 347 | 0 | } | 348 | 0 | self.compiled.has_unicode_word_boundary = true; | 349 | 0 | self.byte_classes.set_word_boundary(); | 350 | | // See comments above for why we set the ASCII range here. | 351 | 0 | self.byte_classes.set_range(0, 0x7F); | 352 | 0 | self.c_empty_look(prog::EmptyLook::NotWordBoundary) | 353 | | } | 354 | | WordBoundary(hir::WordBoundary::Ascii) => { | 355 | 0 | self.byte_classes.set_word_boundary(); | 356 | 0 | self.c_empty_look(prog::EmptyLook::WordBoundaryAscii) | 357 | | } | 358 | | WordBoundary(hir::WordBoundary::AsciiNegate) => { | 359 | 0 | self.byte_classes.set_word_boundary(); | 360 | 0 | self.c_empty_look(prog::EmptyLook::NotWordBoundaryAscii) | 361 | | } | 362 | 162 | Group(ref g) => match g.kind { | 363 | 63 | hir::GroupKind::NonCapturing => self.c(&g.hir), | 364 | 99 | hir::GroupKind::CaptureIndex(index) => { | 365 | 99 | if index as usize >= self.compiled.captures.len() { | 366 | 84 | self.compiled.captures.push(None); | 367 | 84 | } | 368 | 99 | self.c_capture(2 * index as usize, &g.hir) | 369 | | } | 370 | 0 | hir::GroupKind::CaptureName { index, ref name } => { | 371 | 0 | if index as usize >= self.compiled.captures.len() { | 372 | 0 | let n = name.to_string(); | 373 | 0 | self.compiled.captures.push(Some(n.clone())); | 374 | 0 | self.capture_name_idx.insert(n, index as usize); | 375 | 0 | } | 376 | 0 | self.c_capture(2 * index as usize, &g.hir) | 377 | | } | 378 | | }, | 379 | 180 | Concat(ref es) => { | 380 | 180 | if self.compiled.is_reverse { | 381 | 60 | self.c_concat(es.iter().rev()) | 382 | | } else { | 383 | 120 | self.c_concat(es) | 384 | | } | 385 | | } | 386 | 18 | Alternation(ref es) => self.c_alternate(&**es), | 387 | 450 | Repetition(ref rep) => self.c_repeat(rep), | 388 | | } | 389 | 2.01k | } |
<regex::compile::Compiler>::c Line | Count | Source | 267 | 2.01k | fn c(&mut self, expr: &Hir) -> ResultOrEmpty { | 268 | | use crate::prog; | 269 | | use regex_syntax::hir::HirKind::*; | 270 | | | 271 | 2.01k | self.check_size()?; | 272 | 2.01k | match *expr.kind() { | 273 | 0 | Empty => self.c_empty(), | 274 | 837 | Literal(hir::Literal::Unicode(c)) => self.c_char(c), | 275 | 0 | Literal(hir::Literal::Byte(b)) => { | 276 | 0 | assert!(self.compiled.uses_bytes()); | 277 | 0 | self.c_byte(b) | 278 | | } | 279 | 315 | Class(hir::Class::Unicode(ref cls)) => self.c_class(cls.ranges()), | 280 | 0 | Class(hir::Class::Bytes(ref cls)) => { | 281 | 0 | if self.compiled.uses_bytes() { | 282 | 0 | self.c_class_bytes(cls.ranges()) | 283 | | } else { | 284 | 0 | assert!(cls.is_all_ascii()); | 285 | 0 | let mut char_ranges = vec![]; | 286 | 0 | for r in cls.iter() { | 287 | 0 | let (s, e) = (r.start() as char, r.end() as char); | 288 | 0 | char_ranges.push(hir::ClassUnicodeRange::new(s, e)); | 289 | 0 | } | 290 | 0 | self.c_class(&char_ranges) | 291 | | } | 292 | | } | 293 | 0 | Anchor(hir::Anchor::StartLine) if self.compiled.is_reverse => { | 294 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 295 | 0 | self.c_empty_look(prog::EmptyLook::EndLine) | 296 | | } | 297 | | Anchor(hir::Anchor::StartLine) => { | 298 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 299 | 0 | self.c_empty_look(prog::EmptyLook::StartLine) | 300 | | } | 301 | 0 | Anchor(hir::Anchor::EndLine) if self.compiled.is_reverse => { | 302 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 303 | 0 | self.c_empty_look(prog::EmptyLook::StartLine) | 304 | | } | 305 | | Anchor(hir::Anchor::EndLine) => { | 306 | 0 | self.byte_classes.set_range(b'\n', b'\n'); | 307 | 0 | self.c_empty_look(prog::EmptyLook::EndLine) | 308 | | } | 309 | 9 | Anchor(hir::Anchor::StartText) if self.compiled.is_reverse => { | 310 | 9 | self.c_empty_look(prog::EmptyLook::EndText) | 311 | | } | 312 | | Anchor(hir::Anchor::StartText) => { | 313 | 18 | self.c_empty_look(prog::EmptyLook::StartText) | 314 | | } | 315 | 9 | Anchor(hir::Anchor::EndText) if self.compiled.is_reverse => { | 316 | 9 | self.c_empty_look(prog::EmptyLook::StartText) | 317 | | } | 318 | | Anchor(hir::Anchor::EndText) => { | 319 | 18 | self.c_empty_look(prog::EmptyLook::EndText) | 320 | | } | 321 | | WordBoundary(hir::WordBoundary::Unicode) => { | 322 | 0 | if !cfg!(feature = "unicode-perl") { | 323 | 0 | return Err(Error::Syntax( | 324 | 0 | "Unicode word boundaries are unavailable when \ | 325 | 0 | the unicode-perl feature is disabled" | 326 | 0 | .to_string(), | 327 | 0 | )); | 328 | 0 | } | 329 | 0 | self.compiled.has_unicode_word_boundary = true; | 330 | 0 | self.byte_classes.set_word_boundary(); | 331 | | // We also make sure that all ASCII bytes are in a different | 332 | | // class from non-ASCII bytes. Otherwise, it's possible for | 333 | | // ASCII bytes to get lumped into the same class as non-ASCII | 334 | | // bytes. This in turn may cause the lazy DFA to falsely start | 335 | | // when it sees an ASCII byte that maps to a byte class with | 336 | | // non-ASCII bytes. This ensures that never happens. | 337 | 0 | self.byte_classes.set_range(0, 0x7F); | 338 | 0 | self.c_empty_look(prog::EmptyLook::WordBoundary) | 339 | | } | 340 | | WordBoundary(hir::WordBoundary::UnicodeNegate) => { | 341 | 0 | if !cfg!(feature = "unicode-perl") { | 342 | 0 | return Err(Error::Syntax( | 343 | 0 | "Unicode word boundaries are unavailable when \ | 344 | 0 | the unicode-perl feature is disabled" | 345 | 0 | .to_string(), | 346 | 0 | )); | 347 | 0 | } | 348 | 0 | self.compiled.has_unicode_word_boundary = true; | 349 | 0 | self.byte_classes.set_word_boundary(); | 350 | | // See comments above for why we set the ASCII range here. | 351 | 0 | self.byte_classes.set_range(0, 0x7F); | 352 | 0 | self.c_empty_look(prog::EmptyLook::NotWordBoundary) | 353 | | } | 354 | | WordBoundary(hir::WordBoundary::Ascii) => { | 355 | 0 | self.byte_classes.set_word_boundary(); | 356 | 0 | self.c_empty_look(prog::EmptyLook::WordBoundaryAscii) | 357 | | } | 358 | | WordBoundary(hir::WordBoundary::AsciiNegate) => { | 359 | 0 | self.byte_classes.set_word_boundary(); | 360 | 0 | self.c_empty_look(prog::EmptyLook::NotWordBoundaryAscii) | 361 | | } | 362 | 162 | Group(ref g) => match g.kind { | 363 | 63 | hir::GroupKind::NonCapturing => self.c(&g.hir), | 364 | 99 | hir::GroupKind::CaptureIndex(index) => { | 365 | 99 | if index as usize >= self.compiled.captures.len() { | 366 | 84 | self.compiled.captures.push(None); | 367 | 84 | } | 368 | 99 | self.c_capture(2 * index as usize, &g.hir) | 369 | | } | 370 | 0 | hir::GroupKind::CaptureName { index, ref name } => { | 371 | 0 | if index as usize >= self.compiled.captures.len() { | 372 | 0 | let n = name.to_string(); | 373 | 0 | self.compiled.captures.push(Some(n.clone())); | 374 | 0 | self.capture_name_idx.insert(n, index as usize); | 375 | 0 | } | 376 | 0 | self.c_capture(2 * index as usize, &g.hir) | 377 | | } | 378 | | }, | 379 | 180 | Concat(ref es) => { | 380 | 180 | if self.compiled.is_reverse { | 381 | 60 | self.c_concat(es.iter().rev()) | 382 | | } else { | 383 | 120 | self.c_concat(es) | 384 | | } | 385 | | } | 386 | 18 | Alternation(ref es) => self.c_alternate(&**es), | 387 | 450 | Repetition(ref rep) => self.c_repeat(rep), | 388 | | } | 389 | 2.01k | } |
|
390 | | |
391 | 0 | fn c_empty(&mut self) -> ResultOrEmpty { |
392 | | // See: https://github.com/rust-lang/regex/security/advisories/GHSA-m5pq-gvj9-9vr8 |
393 | | // See: CVE-2022-24713 |
394 | | // |
395 | | // Since 'empty' sub-expressions don't increase the size of |
396 | | // the actual compiled object, we "fake" an increase in its |
397 | | // size so that our 'check_size_limit' routine will eventually |
398 | | // stop compilation if there are too many empty sub-expressions |
399 | | // (e.g., via a large repetition). |
400 | 0 | self.extra_inst_bytes += std::mem::size_of::<Inst>(); |
401 | 0 | Ok(None) |
402 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::c_empty Unexecuted instantiation: <regex::compile::Compiler>::c_empty |
403 | | |
404 | 252 | fn c_capture(&mut self, first_slot: usize, expr: &Hir) -> ResultOrEmpty { |
405 | 252 | if self.num_exprs > 1 || self.compiled.is_dfa { |
406 | | // Don't ever compile Save instructions for regex sets because |
407 | | // they are never used. They are also never used in DFA programs |
408 | | // because DFAs can't handle captures. |
409 | 168 | self.c(expr) |
410 | | } else { |
411 | 84 | let entry = self.insts.len(); |
412 | 84 | let hole = self.push_hole(InstHole::Save { slot: first_slot }); |
413 | 84 | let patch = self.c(expr)?.unwrap_or(self.next_inst()); |
414 | 84 | self.fill(hole, patch.entry); |
415 | 84 | self.fill_to_next(patch.hole); |
416 | 84 | let hole = self.push_hole(InstHole::Save { slot: first_slot + 1 }); |
417 | 84 | Ok(Some(Patch { hole: hole, entry: entry })) |
418 | | } |
419 | 252 | } <regex::compile::Compiler>::c_capture Line | Count | Source | 404 | 126 | fn c_capture(&mut self, first_slot: usize, expr: &Hir) -> ResultOrEmpty { | 405 | 126 | if self.num_exprs > 1 || self.compiled.is_dfa { | 406 | | // Don't ever compile Save instructions for regex sets because | 407 | | // they are never used. They are also never used in DFA programs | 408 | | // because DFAs can't handle captures. | 409 | 84 | self.c(expr) | 410 | | } else { | 411 | 42 | let entry = self.insts.len(); | 412 | 42 | let hole = self.push_hole(InstHole::Save { slot: first_slot }); | 413 | 42 | let patch = self.c(expr)?.unwrap_or(self.next_inst()); | 414 | 42 | self.fill(hole, patch.entry); | 415 | 42 | self.fill_to_next(patch.hole); | 416 | 42 | let hole = self.push_hole(InstHole::Save { slot: first_slot + 1 }); | 417 | 42 | Ok(Some(Patch { hole: hole, entry: entry })) | 418 | | } | 419 | 126 | } |
<regex::compile::Compiler>::c_capture Line | Count | Source | 404 | 126 | fn c_capture(&mut self, first_slot: usize, expr: &Hir) -> ResultOrEmpty { | 405 | 126 | if self.num_exprs > 1 || self.compiled.is_dfa { | 406 | | // Don't ever compile Save instructions for regex sets because | 407 | | // they are never used. They are also never used in DFA programs | 408 | | // because DFAs can't handle captures. | 409 | 84 | self.c(expr) | 410 | | } else { | 411 | 42 | let entry = self.insts.len(); | 412 | 42 | let hole = self.push_hole(InstHole::Save { slot: first_slot }); | 413 | 42 | let patch = self.c(expr)?.unwrap_or(self.next_inst()); | 414 | 42 | self.fill(hole, patch.entry); | 415 | 42 | self.fill_to_next(patch.hole); | 416 | 42 | let hole = self.push_hole(InstHole::Save { slot: first_slot + 1 }); | 417 | 42 | Ok(Some(Patch { hole: hole, entry: entry })) | 418 | | } | 419 | 126 | } |
|
420 | | |
421 | 0 | fn c_dotstar(&mut self) -> Result { |
422 | 0 | Ok(if !self.compiled.only_utf8() { |
423 | 0 | self.c(&Hir::repetition(hir::Repetition { |
424 | 0 | kind: hir::RepetitionKind::ZeroOrMore, |
425 | 0 | greedy: false, |
426 | 0 | hir: Box::new(Hir::any(true)), |
427 | 0 | }))? |
428 | 0 | .unwrap() |
429 | | } else { |
430 | 0 | self.c(&Hir::repetition(hir::Repetition { |
431 | 0 | kind: hir::RepetitionKind::ZeroOrMore, |
432 | 0 | greedy: false, |
433 | 0 | hir: Box::new(Hir::any(false)), |
434 | 0 | }))? |
435 | 0 | .unwrap() |
436 | | }) |
437 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::c_dotstar Unexecuted instantiation: <regex::compile::Compiler>::c_dotstar |
438 | | |
439 | 1.67k | fn c_char(&mut self, c: char) -> ResultOrEmpty { |
440 | 1.67k | if self.compiled.uses_bytes() { |
441 | 1.11k | if c.is_ascii() { |
442 | 1.11k | let b = c as u8; |
443 | 1.11k | let hole = |
444 | 1.11k | self.push_hole(InstHole::Bytes { start: b, end: b }); |
445 | 1.11k | self.byte_classes.set_range(b, b); |
446 | 1.11k | Ok(Some(Patch { hole, entry: self.insts.len() - 1 })) |
447 | | } else { |
448 | 0 | self.c_class(&[hir::ClassUnicodeRange::new(c, c)]) |
449 | | } |
450 | | } else { |
451 | 558 | let hole = self.push_hole(InstHole::Char { c: c }); |
452 | 558 | Ok(Some(Patch { hole, entry: self.insts.len() - 1 })) |
453 | | } |
454 | 1.67k | } <regex::compile::Compiler>::c_char Line | Count | Source | 439 | 837 | fn c_char(&mut self, c: char) -> ResultOrEmpty { | 440 | 837 | if self.compiled.uses_bytes() { | 441 | 558 | if c.is_ascii() { | 442 | 558 | let b = c as u8; | 443 | 558 | let hole = | 444 | 558 | self.push_hole(InstHole::Bytes { start: b, end: b }); | 445 | 558 | self.byte_classes.set_range(b, b); | 446 | 558 | Ok(Some(Patch { hole, entry: self.insts.len() - 1 })) | 447 | | } else { | 448 | 0 | self.c_class(&[hir::ClassUnicodeRange::new(c, c)]) | 449 | | } | 450 | | } else { | 451 | 279 | let hole = self.push_hole(InstHole::Char { c: c }); | 452 | 279 | Ok(Some(Patch { hole, entry: self.insts.len() - 1 })) | 453 | | } | 454 | 837 | } |
<regex::compile::Compiler>::c_char Line | Count | Source | 439 | 837 | fn c_char(&mut self, c: char) -> ResultOrEmpty { | 440 | 837 | if self.compiled.uses_bytes() { | 441 | 558 | if c.is_ascii() { | 442 | 558 | let b = c as u8; | 443 | 558 | let hole = | 444 | 558 | self.push_hole(InstHole::Bytes { start: b, end: b }); | 445 | 558 | self.byte_classes.set_range(b, b); | 446 | 558 | Ok(Some(Patch { hole, entry: self.insts.len() - 1 })) | 447 | | } else { | 448 | 0 | self.c_class(&[hir::ClassUnicodeRange::new(c, c)]) | 449 | | } | 450 | | } else { | 451 | 279 | let hole = self.push_hole(InstHole::Char { c: c }); | 452 | 279 | Ok(Some(Patch { hole, entry: self.insts.len() - 1 })) | 453 | | } | 454 | 837 | } |
|
455 | | |
456 | 630 | fn c_class(&mut self, ranges: &[hir::ClassUnicodeRange]) -> ResultOrEmpty { |
457 | | use std::mem::size_of; |
458 | | |
459 | 630 | assert!(!ranges.is_empty()); |
460 | 630 | if self.compiled.uses_bytes() { |
461 | 420 | Ok(Some(CompileClass { c: self, ranges: ranges }.compile()?)) |
462 | | } else { |
463 | 210 | let ranges: Vec<(char, char)> = |
464 | 378 | ranges.iter().map(|r| (r.start(), r.end())).collect(); <regex::compile::Compiler>::c_class::{closure#0}Line | Count | Source | 464 | 189 | ranges.iter().map(|r| (r.start(), r.end())).collect(); |
<regex::compile::Compiler>::c_class::{closure#0}Line | Count | Source | 464 | 189 | ranges.iter().map(|r| (r.start(), r.end())).collect(); |
|
465 | 210 | let hole = if ranges.len() == 1 && ranges[0].0 == ranges[0].1 { |
466 | 0 | self.push_hole(InstHole::Char { c: ranges[0].0 }) |
467 | | } else { |
468 | 210 | self.extra_inst_bytes += |
469 | 210 | ranges.len() * (size_of::<char>() * 2); |
470 | 210 | self.push_hole(InstHole::Ranges { ranges: ranges }) |
471 | | }; |
472 | 210 | Ok(Some(Patch { hole: hole, entry: self.insts.len() - 1 })) |
473 | | } |
474 | 630 | } <regex::compile::Compiler>::c_class Line | Count | Source | 456 | 315 | fn c_class(&mut self, ranges: &[hir::ClassUnicodeRange]) -> ResultOrEmpty { | 457 | | use std::mem::size_of; | 458 | | | 459 | 315 | assert!(!ranges.is_empty()); | 460 | 315 | if self.compiled.uses_bytes() { | 461 | 210 | Ok(Some(CompileClass { c: self, ranges: ranges }.compile()?)) | 462 | | } else { | 463 | 105 | let ranges: Vec<(char, char)> = | 464 | 105 | ranges.iter().map(|r| (r.start(), r.end())).collect(); | 465 | 105 | let hole = if ranges.len() == 1 && ranges[0].0 == ranges[0].1 { | 466 | 0 | self.push_hole(InstHole::Char { c: ranges[0].0 }) | 467 | | } else { | 468 | 105 | self.extra_inst_bytes += | 469 | 105 | ranges.len() * (size_of::<char>() * 2); | 470 | 105 | self.push_hole(InstHole::Ranges { ranges: ranges }) | 471 | | }; | 472 | 105 | Ok(Some(Patch { hole: hole, entry: self.insts.len() - 1 })) | 473 | | } | 474 | 315 | } |
<regex::compile::Compiler>::c_class Line | Count | Source | 456 | 315 | fn c_class(&mut self, ranges: &[hir::ClassUnicodeRange]) -> ResultOrEmpty { | 457 | | use std::mem::size_of; | 458 | | | 459 | 315 | assert!(!ranges.is_empty()); | 460 | 315 | if self.compiled.uses_bytes() { | 461 | 210 | Ok(Some(CompileClass { c: self, ranges: ranges }.compile()?)) | 462 | | } else { | 463 | 105 | let ranges: Vec<(char, char)> = | 464 | 105 | ranges.iter().map(|r| (r.start(), r.end())).collect(); | 465 | 105 | let hole = if ranges.len() == 1 && ranges[0].0 == ranges[0].1 { | 466 | 0 | self.push_hole(InstHole::Char { c: ranges[0].0 }) | 467 | | } else { | 468 | 105 | self.extra_inst_bytes += | 469 | 105 | ranges.len() * (size_of::<char>() * 2); | 470 | 105 | self.push_hole(InstHole::Ranges { ranges: ranges }) | 471 | | }; | 472 | 105 | Ok(Some(Patch { hole: hole, entry: self.insts.len() - 1 })) | 473 | | } | 474 | 315 | } |
|
475 | | |
476 | 0 | fn c_byte(&mut self, b: u8) -> ResultOrEmpty { |
477 | 0 | self.c_class_bytes(&[hir::ClassBytesRange::new(b, b)]) |
478 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::c_byte Unexecuted instantiation: <regex::compile::Compiler>::c_byte |
479 | | |
480 | 0 | fn c_class_bytes( |
481 | 0 | &mut self, |
482 | 0 | ranges: &[hir::ClassBytesRange], |
483 | 0 | ) -> ResultOrEmpty { |
484 | 0 | debug_assert!(!ranges.is_empty()); |
485 | | |
486 | 0 | let first_split_entry = self.insts.len(); |
487 | 0 | let mut holes = vec![]; |
488 | 0 | let mut prev_hole = Hole::None; |
489 | 0 | for r in &ranges[0..ranges.len() - 1] { |
490 | 0 | self.fill_to_next(prev_hole); |
491 | 0 | let split = self.push_split_hole(); |
492 | 0 | let next = self.insts.len(); |
493 | 0 | self.byte_classes.set_range(r.start(), r.end()); |
494 | 0 | holes.push(self.push_hole(InstHole::Bytes { |
495 | 0 | start: r.start(), |
496 | 0 | end: r.end(), |
497 | 0 | })); |
498 | 0 | prev_hole = self.fill_split(split, Some(next), None); |
499 | 0 | } |
500 | 0 | let next = self.insts.len(); |
501 | 0 | let r = &ranges[ranges.len() - 1]; |
502 | 0 | self.byte_classes.set_range(r.start(), r.end()); |
503 | 0 | holes.push( |
504 | 0 | self.push_hole(InstHole::Bytes { start: r.start(), end: r.end() }), |
505 | | ); |
506 | 0 | self.fill(prev_hole, next); |
507 | 0 | Ok(Some(Patch { hole: Hole::Many(holes), entry: first_split_entry })) |
508 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::c_class_bytes Unexecuted instantiation: <regex::compile::Compiler>::c_class_bytes |
509 | | |
510 | 108 | fn c_empty_look(&mut self, look: EmptyLook) -> ResultOrEmpty { |
511 | 108 | let hole = self.push_hole(InstHole::EmptyLook { look: look }); |
512 | 108 | Ok(Some(Patch { hole: hole, entry: self.insts.len() - 1 })) |
513 | 108 | } <regex::compile::Compiler>::c_empty_look Line | Count | Source | 510 | 54 | fn c_empty_look(&mut self, look: EmptyLook) -> ResultOrEmpty { | 511 | 54 | let hole = self.push_hole(InstHole::EmptyLook { look: look }); | 512 | 54 | Ok(Some(Patch { hole: hole, entry: self.insts.len() - 1 })) | 513 | 54 | } |
<regex::compile::Compiler>::c_empty_look Line | Count | Source | 510 | 54 | fn c_empty_look(&mut self, look: EmptyLook) -> ResultOrEmpty { | 511 | 54 | let hole = self.push_hole(InstHole::EmptyLook { look: look }); | 512 | 54 | Ok(Some(Patch { hole: hole, entry: self.insts.len() - 1 })) | 513 | 54 | } |
|
514 | | |
515 | 360 | fn c_concat<'a, I>(&mut self, exprs: I) -> ResultOrEmpty |
516 | 360 | where |
517 | 360 | I: IntoIterator<Item = &'a Hir>, |
518 | | { |
519 | 360 | let mut exprs = exprs.into_iter(); |
520 | 360 | let Patch { mut hole, entry } = loop { |
521 | 360 | match exprs.next() { |
522 | 0 | None => return self.c_empty(), |
523 | 360 | Some(e) => { |
524 | 360 | if let Some(p) = self.c(e)? { |
525 | 360 | break p; |
526 | 0 | } |
527 | | } |
528 | | } |
529 | | }; |
530 | 2.64k | for e in exprs { |
531 | 2.28k | if let Some(p) = self.c(e)? { |
532 | 2.28k | self.fill(hole, p.entry); |
533 | 2.28k | hole = p.hole; |
534 | 2.28k | } |
535 | | } |
536 | 360 | Ok(Some(Patch { hole: hole, entry: entry })) |
537 | 360 | } <regex::compile::Compiler>::c_concat::<core::iter::adapters::rev::Rev<core::slice::iter::Iter<regex_syntax::hir::Hir>>> Line | Count | Source | 515 | 60 | fn c_concat<'a, I>(&mut self, exprs: I) -> ResultOrEmpty | 516 | 60 | where | 517 | 60 | I: IntoIterator<Item = &'a Hir>, | 518 | | { | 519 | 60 | let mut exprs = exprs.into_iter(); | 520 | 60 | let Patch { mut hole, entry } = loop { | 521 | 60 | match exprs.next() { | 522 | 0 | None => return self.c_empty(), | 523 | 60 | Some(e) => { | 524 | 60 | if let Some(p) = self.c(e)? { | 525 | 60 | break p; | 526 | 0 | } | 527 | | } | 528 | | } | 529 | | }; | 530 | 441 | for e in exprs { | 531 | 381 | if let Some(p) = self.c(e)? { | 532 | 381 | self.fill(hole, p.entry); | 533 | 381 | hole = p.hole; | 534 | 381 | } | 535 | | } | 536 | 60 | Ok(Some(Patch { hole: hole, entry: entry })) | 537 | 60 | } |
Unexecuted instantiation: <regex::compile::Compiler>::c_concat::<core::iter::adapters::take::Take<core::iter::sources::repeat::Repeat<®ex_syntax::hir::Hir>>> <regex::compile::Compiler>::c_concat::<&alloc::vec::Vec<regex_syntax::hir::Hir>> Line | Count | Source | 515 | 120 | fn c_concat<'a, I>(&mut self, exprs: I) -> ResultOrEmpty | 516 | 120 | where | 517 | 120 | I: IntoIterator<Item = &'a Hir>, | 518 | | { | 519 | 120 | let mut exprs = exprs.into_iter(); | 520 | 120 | let Patch { mut hole, entry } = loop { | 521 | 120 | match exprs.next() { | 522 | 0 | None => return self.c_empty(), | 523 | 120 | Some(e) => { | 524 | 120 | if let Some(p) = self.c(e)? { | 525 | 120 | break p; | 526 | 0 | } | 527 | | } | 528 | | } | 529 | | }; | 530 | 882 | for e in exprs { | 531 | 762 | if let Some(p) = self.c(e)? { | 532 | 762 | self.fill(hole, p.entry); | 533 | 762 | hole = p.hole; | 534 | 762 | } | 535 | | } | 536 | 120 | Ok(Some(Patch { hole: hole, entry: entry })) | 537 | 120 | } |
<regex::compile::Compiler>::c_concat::<core::iter::adapters::rev::Rev<core::slice::iter::Iter<regex_syntax::hir::Hir>>> Line | Count | Source | 515 | 60 | fn c_concat<'a, I>(&mut self, exprs: I) -> ResultOrEmpty | 516 | 60 | where | 517 | 60 | I: IntoIterator<Item = &'a Hir>, | 518 | | { | 519 | 60 | let mut exprs = exprs.into_iter(); | 520 | 60 | let Patch { mut hole, entry } = loop { | 521 | 60 | match exprs.next() { | 522 | 0 | None => return self.c_empty(), | 523 | 60 | Some(e) => { | 524 | 60 | if let Some(p) = self.c(e)? { | 525 | 60 | break p; | 526 | 0 | } | 527 | | } | 528 | | } | 529 | | }; | 530 | 441 | for e in exprs { | 531 | 381 | if let Some(p) = self.c(e)? { | 532 | 381 | self.fill(hole, p.entry); | 533 | 381 | hole = p.hole; | 534 | 381 | } | 535 | | } | 536 | 60 | Ok(Some(Patch { hole: hole, entry: entry })) | 537 | 60 | } |
Unexecuted instantiation: <regex::compile::Compiler>::c_concat::<core::iter::adapters::take::Take<core::iter::sources::repeat::Repeat<®ex_syntax::hir::Hir>>> <regex::compile::Compiler>::c_concat::<&alloc::vec::Vec<regex_syntax::hir::Hir>> Line | Count | Source | 515 | 120 | fn c_concat<'a, I>(&mut self, exprs: I) -> ResultOrEmpty | 516 | 120 | where | 517 | 120 | I: IntoIterator<Item = &'a Hir>, | 518 | | { | 519 | 120 | let mut exprs = exprs.into_iter(); | 520 | 120 | let Patch { mut hole, entry } = loop { | 521 | 120 | match exprs.next() { | 522 | 0 | None => return self.c_empty(), | 523 | 120 | Some(e) => { | 524 | 120 | if let Some(p) = self.c(e)? { | 525 | 120 | break p; | 526 | 0 | } | 527 | | } | 528 | | } | 529 | | }; | 530 | 882 | for e in exprs { | 531 | 762 | if let Some(p) = self.c(e)? { | 532 | 762 | self.fill(hole, p.entry); | 533 | 762 | hole = p.hole; | 534 | 762 | } | 535 | | } | 536 | 120 | Ok(Some(Patch { hole: hole, entry: entry })) | 537 | 120 | } |
|
538 | | |
539 | 36 | fn c_alternate(&mut self, exprs: &[Hir]) -> ResultOrEmpty { |
540 | 36 | debug_assert!( |
541 | 0 | exprs.len() >= 2, |
542 | | "alternates must have at least 2 exprs" |
543 | | ); |
544 | | |
545 | | // Initial entry point is always the first split. |
546 | 36 | let first_split_entry = self.insts.len(); |
547 | | |
548 | | // Save up all of the holes from each alternate. They will all get |
549 | | // patched to point to the same location. |
550 | 36 | let mut holes = vec![]; |
551 | | |
552 | | // true indicates that the hole is a split where we want to fill |
553 | | // the second branch. |
554 | 36 | let mut prev_hole = (Hole::None, false); |
555 | 72 | for e in &exprs[0..exprs.len() - 1] { |
556 | 72 | if prev_hole.1 { |
557 | 0 | let next = self.insts.len(); |
558 | 0 | self.fill_split(prev_hole.0, None, Some(next)); |
559 | 72 | } else { |
560 | 72 | self.fill_to_next(prev_hole.0); |
561 | 72 | } |
562 | 72 | let split = self.push_split_hole(); |
563 | 72 | if let Some(Patch { hole, entry }) = self.c(e)? { |
564 | 72 | holes.push(hole); |
565 | 72 | prev_hole = (self.fill_split(split, Some(entry), None), false); |
566 | 72 | } else { |
567 | 0 | let (split1, split2) = split.dup_one(); |
568 | 0 | holes.push(split1); |
569 | 0 | prev_hole = (split2, true); |
570 | 0 | } |
571 | | } |
572 | 36 | if let Some(Patch { hole, entry }) = self.c(&exprs[exprs.len() - 1])? { |
573 | 36 | holes.push(hole); |
574 | 36 | if prev_hole.1 { |
575 | 0 | self.fill_split(prev_hole.0, None, Some(entry)); |
576 | 36 | } else { |
577 | 36 | self.fill(prev_hole.0, entry); |
578 | 36 | } |
579 | 0 | } else { |
580 | 0 | // We ignore prev_hole.1. When it's true, it means we have two |
581 | 0 | // empty branches both pushing prev_hole.0 into holes, so both |
582 | 0 | // branches will go to the same place anyway. |
583 | 0 | holes.push(prev_hole.0); |
584 | 0 | } |
585 | 36 | Ok(Some(Patch { hole: Hole::Many(holes), entry: first_split_entry })) |
586 | 36 | } <regex::compile::Compiler>::c_alternate Line | Count | Source | 539 | 18 | fn c_alternate(&mut self, exprs: &[Hir]) -> ResultOrEmpty { | 540 | 18 | debug_assert!( | 541 | 0 | exprs.len() >= 2, | 542 | | "alternates must have at least 2 exprs" | 543 | | ); | 544 | | | 545 | | // Initial entry point is always the first split. | 546 | 18 | let first_split_entry = self.insts.len(); | 547 | | | 548 | | // Save up all of the holes from each alternate. They will all get | 549 | | // patched to point to the same location. | 550 | 18 | let mut holes = vec![]; | 551 | | | 552 | | // true indicates that the hole is a split where we want to fill | 553 | | // the second branch. | 554 | 18 | let mut prev_hole = (Hole::None, false); | 555 | 36 | for e in &exprs[0..exprs.len() - 1] { | 556 | 36 | if prev_hole.1 { | 557 | 0 | let next = self.insts.len(); | 558 | 0 | self.fill_split(prev_hole.0, None, Some(next)); | 559 | 36 | } else { | 560 | 36 | self.fill_to_next(prev_hole.0); | 561 | 36 | } | 562 | 36 | let split = self.push_split_hole(); | 563 | 36 | if let Some(Patch { hole, entry }) = self.c(e)? { | 564 | 36 | holes.push(hole); | 565 | 36 | prev_hole = (self.fill_split(split, Some(entry), None), false); | 566 | 36 | } else { | 567 | 0 | let (split1, split2) = split.dup_one(); | 568 | 0 | holes.push(split1); | 569 | 0 | prev_hole = (split2, true); | 570 | 0 | } | 571 | | } | 572 | 18 | if let Some(Patch { hole, entry }) = self.c(&exprs[exprs.len() - 1])? { | 573 | 18 | holes.push(hole); | 574 | 18 | if prev_hole.1 { | 575 | 0 | self.fill_split(prev_hole.0, None, Some(entry)); | 576 | 18 | } else { | 577 | 18 | self.fill(prev_hole.0, entry); | 578 | 18 | } | 579 | 0 | } else { | 580 | 0 | // We ignore prev_hole.1. When it's true, it means we have two | 581 | 0 | // empty branches both pushing prev_hole.0 into holes, so both | 582 | 0 | // branches will go to the same place anyway. | 583 | 0 | holes.push(prev_hole.0); | 584 | 0 | } | 585 | 18 | Ok(Some(Patch { hole: Hole::Many(holes), entry: first_split_entry })) | 586 | 18 | } |
<regex::compile::Compiler>::c_alternate Line | Count | Source | 539 | 18 | fn c_alternate(&mut self, exprs: &[Hir]) -> ResultOrEmpty { | 540 | 18 | debug_assert!( | 541 | 0 | exprs.len() >= 2, | 542 | | "alternates must have at least 2 exprs" | 543 | | ); | 544 | | | 545 | | // Initial entry point is always the first split. | 546 | 18 | let first_split_entry = self.insts.len(); | 547 | | | 548 | | // Save up all of the holes from each alternate. They will all get | 549 | | // patched to point to the same location. | 550 | 18 | let mut holes = vec![]; | 551 | | | 552 | | // true indicates that the hole is a split where we want to fill | 553 | | // the second branch. | 554 | 18 | let mut prev_hole = (Hole::None, false); | 555 | 36 | for e in &exprs[0..exprs.len() - 1] { | 556 | 36 | if prev_hole.1 { | 557 | 0 | let next = self.insts.len(); | 558 | 0 | self.fill_split(prev_hole.0, None, Some(next)); | 559 | 36 | } else { | 560 | 36 | self.fill_to_next(prev_hole.0); | 561 | 36 | } | 562 | 36 | let split = self.push_split_hole(); | 563 | 36 | if let Some(Patch { hole, entry }) = self.c(e)? { | 564 | 36 | holes.push(hole); | 565 | 36 | prev_hole = (self.fill_split(split, Some(entry), None), false); | 566 | 36 | } else { | 567 | 0 | let (split1, split2) = split.dup_one(); | 568 | 0 | holes.push(split1); | 569 | 0 | prev_hole = (split2, true); | 570 | 0 | } | 571 | | } | 572 | 18 | if let Some(Patch { hole, entry }) = self.c(&exprs[exprs.len() - 1])? { | 573 | 18 | holes.push(hole); | 574 | 18 | if prev_hole.1 { | 575 | 0 | self.fill_split(prev_hole.0, None, Some(entry)); | 576 | 18 | } else { | 577 | 18 | self.fill(prev_hole.0, entry); | 578 | 18 | } | 579 | 0 | } else { | 580 | 0 | // We ignore prev_hole.1. When it's true, it means we have two | 581 | 0 | // empty branches both pushing prev_hole.0 into holes, so both | 582 | 0 | // branches will go to the same place anyway. | 583 | 0 | holes.push(prev_hole.0); | 584 | 0 | } | 585 | 18 | Ok(Some(Patch { hole: Hole::Many(holes), entry: first_split_entry })) | 586 | 18 | } |
|
587 | | |
588 | 900 | fn c_repeat(&mut self, rep: &hir::Repetition) -> ResultOrEmpty { |
589 | | use regex_syntax::hir::RepetitionKind::*; |
590 | 0 | match rep.kind { |
591 | 324 | ZeroOrOne => self.c_repeat_zero_or_one(&rep.hir, rep.greedy), |
592 | 360 | ZeroOrMore => self.c_repeat_zero_or_more(&rep.hir, rep.greedy), |
593 | 216 | OneOrMore => self.c_repeat_one_or_more(&rep.hir, rep.greedy), |
594 | 0 | Range(hir::RepetitionRange::Exactly(min_max)) => { |
595 | 0 | self.c_repeat_range(&rep.hir, rep.greedy, min_max, min_max) |
596 | | } |
597 | 0 | Range(hir::RepetitionRange::AtLeast(min)) => { |
598 | 0 | self.c_repeat_range_min_or_more(&rep.hir, rep.greedy, min) |
599 | | } |
600 | 0 | Range(hir::RepetitionRange::Bounded(min, max)) => { |
601 | 0 | self.c_repeat_range(&rep.hir, rep.greedy, min, max) |
602 | | } |
603 | | } |
604 | 900 | } <regex::compile::Compiler>::c_repeat Line | Count | Source | 588 | 450 | fn c_repeat(&mut self, rep: &hir::Repetition) -> ResultOrEmpty { | 589 | | use regex_syntax::hir::RepetitionKind::*; | 590 | 0 | match rep.kind { | 591 | 162 | ZeroOrOne => self.c_repeat_zero_or_one(&rep.hir, rep.greedy), | 592 | 180 | ZeroOrMore => self.c_repeat_zero_or_more(&rep.hir, rep.greedy), | 593 | 108 | OneOrMore => self.c_repeat_one_or_more(&rep.hir, rep.greedy), | 594 | 0 | Range(hir::RepetitionRange::Exactly(min_max)) => { | 595 | 0 | self.c_repeat_range(&rep.hir, rep.greedy, min_max, min_max) | 596 | | } | 597 | 0 | Range(hir::RepetitionRange::AtLeast(min)) => { | 598 | 0 | self.c_repeat_range_min_or_more(&rep.hir, rep.greedy, min) | 599 | | } | 600 | 0 | Range(hir::RepetitionRange::Bounded(min, max)) => { | 601 | 0 | self.c_repeat_range(&rep.hir, rep.greedy, min, max) | 602 | | } | 603 | | } | 604 | 450 | } |
<regex::compile::Compiler>::c_repeat Line | Count | Source | 588 | 450 | fn c_repeat(&mut self, rep: &hir::Repetition) -> ResultOrEmpty { | 589 | | use regex_syntax::hir::RepetitionKind::*; | 590 | 0 | match rep.kind { | 591 | 162 | ZeroOrOne => self.c_repeat_zero_or_one(&rep.hir, rep.greedy), | 592 | 180 | ZeroOrMore => self.c_repeat_zero_or_more(&rep.hir, rep.greedy), | 593 | 108 | OneOrMore => self.c_repeat_one_or_more(&rep.hir, rep.greedy), | 594 | 0 | Range(hir::RepetitionRange::Exactly(min_max)) => { | 595 | 0 | self.c_repeat_range(&rep.hir, rep.greedy, min_max, min_max) | 596 | | } | 597 | 0 | Range(hir::RepetitionRange::AtLeast(min)) => { | 598 | 0 | self.c_repeat_range_min_or_more(&rep.hir, rep.greedy, min) | 599 | | } | 600 | 0 | Range(hir::RepetitionRange::Bounded(min, max)) => { | 601 | 0 | self.c_repeat_range(&rep.hir, rep.greedy, min, max) | 602 | | } | 603 | | } | 604 | 450 | } |
|
605 | | |
606 | 324 | fn c_repeat_zero_or_one( |
607 | 324 | &mut self, |
608 | 324 | expr: &Hir, |
609 | 324 | greedy: bool, |
610 | 324 | ) -> ResultOrEmpty { |
611 | 324 | let split_entry = self.insts.len(); |
612 | 324 | let split = self.push_split_hole(); |
613 | 324 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { |
614 | 324 | Some(p) => p, |
615 | 0 | None => return self.pop_split_hole(), |
616 | | }; |
617 | 324 | let split_hole = if greedy { |
618 | 324 | self.fill_split(split, Some(entry_rep), None) |
619 | | } else { |
620 | 0 | self.fill_split(split, None, Some(entry_rep)) |
621 | | }; |
622 | 324 | let holes = vec![hole_rep, split_hole]; |
623 | 324 | Ok(Some(Patch { hole: Hole::Many(holes), entry: split_entry })) |
624 | 324 | } <regex::compile::Compiler>::c_repeat_zero_or_one Line | Count | Source | 606 | 162 | fn c_repeat_zero_or_one( | 607 | 162 | &mut self, | 608 | 162 | expr: &Hir, | 609 | 162 | greedy: bool, | 610 | 162 | ) -> ResultOrEmpty { | 611 | 162 | let split_entry = self.insts.len(); | 612 | 162 | let split = self.push_split_hole(); | 613 | 162 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { | 614 | 162 | Some(p) => p, | 615 | 0 | None => return self.pop_split_hole(), | 616 | | }; | 617 | 162 | let split_hole = if greedy { | 618 | 162 | self.fill_split(split, Some(entry_rep), None) | 619 | | } else { | 620 | 0 | self.fill_split(split, None, Some(entry_rep)) | 621 | | }; | 622 | 162 | let holes = vec![hole_rep, split_hole]; | 623 | 162 | Ok(Some(Patch { hole: Hole::Many(holes), entry: split_entry })) | 624 | 162 | } |
<regex::compile::Compiler>::c_repeat_zero_or_one Line | Count | Source | 606 | 162 | fn c_repeat_zero_or_one( | 607 | 162 | &mut self, | 608 | 162 | expr: &Hir, | 609 | 162 | greedy: bool, | 610 | 162 | ) -> ResultOrEmpty { | 611 | 162 | let split_entry = self.insts.len(); | 612 | 162 | let split = self.push_split_hole(); | 613 | 162 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { | 614 | 162 | Some(p) => p, | 615 | 0 | None => return self.pop_split_hole(), | 616 | | }; | 617 | 162 | let split_hole = if greedy { | 618 | 162 | self.fill_split(split, Some(entry_rep), None) | 619 | | } else { | 620 | 0 | self.fill_split(split, None, Some(entry_rep)) | 621 | | }; | 622 | 162 | let holes = vec![hole_rep, split_hole]; | 623 | 162 | Ok(Some(Patch { hole: Hole::Many(holes), entry: split_entry })) | 624 | 162 | } |
|
625 | | |
626 | 360 | fn c_repeat_zero_or_more( |
627 | 360 | &mut self, |
628 | 360 | expr: &Hir, |
629 | 360 | greedy: bool, |
630 | 360 | ) -> ResultOrEmpty { |
631 | 360 | let split_entry = self.insts.len(); |
632 | 360 | let split = self.push_split_hole(); |
633 | 360 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { |
634 | 360 | Some(p) => p, |
635 | 0 | None => return self.pop_split_hole(), |
636 | | }; |
637 | | |
638 | 360 | self.fill(hole_rep, split_entry); |
639 | 360 | let split_hole = if greedy { |
640 | 360 | self.fill_split(split, Some(entry_rep), None) |
641 | | } else { |
642 | 0 | self.fill_split(split, None, Some(entry_rep)) |
643 | | }; |
644 | 360 | Ok(Some(Patch { hole: split_hole, entry: split_entry })) |
645 | 360 | } <regex::compile::Compiler>::c_repeat_zero_or_more Line | Count | Source | 626 | 180 | fn c_repeat_zero_or_more( | 627 | 180 | &mut self, | 628 | 180 | expr: &Hir, | 629 | 180 | greedy: bool, | 630 | 180 | ) -> ResultOrEmpty { | 631 | 180 | let split_entry = self.insts.len(); | 632 | 180 | let split = self.push_split_hole(); | 633 | 180 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { | 634 | 180 | Some(p) => p, | 635 | 0 | None => return self.pop_split_hole(), | 636 | | }; | 637 | | | 638 | 180 | self.fill(hole_rep, split_entry); | 639 | 180 | let split_hole = if greedy { | 640 | 180 | self.fill_split(split, Some(entry_rep), None) | 641 | | } else { | 642 | 0 | self.fill_split(split, None, Some(entry_rep)) | 643 | | }; | 644 | 180 | Ok(Some(Patch { hole: split_hole, entry: split_entry })) | 645 | 180 | } |
<regex::compile::Compiler>::c_repeat_zero_or_more Line | Count | Source | 626 | 180 | fn c_repeat_zero_or_more( | 627 | 180 | &mut self, | 628 | 180 | expr: &Hir, | 629 | 180 | greedy: bool, | 630 | 180 | ) -> ResultOrEmpty { | 631 | 180 | let split_entry = self.insts.len(); | 632 | 180 | let split = self.push_split_hole(); | 633 | 180 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { | 634 | 180 | Some(p) => p, | 635 | 0 | None => return self.pop_split_hole(), | 636 | | }; | 637 | | | 638 | 180 | self.fill(hole_rep, split_entry); | 639 | 180 | let split_hole = if greedy { | 640 | 180 | self.fill_split(split, Some(entry_rep), None) | 641 | | } else { | 642 | 0 | self.fill_split(split, None, Some(entry_rep)) | 643 | | }; | 644 | 180 | Ok(Some(Patch { hole: split_hole, entry: split_entry })) | 645 | 180 | } |
|
646 | | |
647 | 216 | fn c_repeat_one_or_more( |
648 | 216 | &mut self, |
649 | 216 | expr: &Hir, |
650 | 216 | greedy: bool, |
651 | 216 | ) -> ResultOrEmpty { |
652 | 216 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { |
653 | 216 | Some(p) => p, |
654 | 0 | None => return Ok(None), |
655 | | }; |
656 | 216 | self.fill_to_next(hole_rep); |
657 | 216 | let split = self.push_split_hole(); |
658 | | |
659 | 216 | let split_hole = if greedy { |
660 | 216 | self.fill_split(split, Some(entry_rep), None) |
661 | | } else { |
662 | 0 | self.fill_split(split, None, Some(entry_rep)) |
663 | | }; |
664 | 216 | Ok(Some(Patch { hole: split_hole, entry: entry_rep })) |
665 | 216 | } <regex::compile::Compiler>::c_repeat_one_or_more Line | Count | Source | 647 | 108 | fn c_repeat_one_or_more( | 648 | 108 | &mut self, | 649 | 108 | expr: &Hir, | 650 | 108 | greedy: bool, | 651 | 108 | ) -> ResultOrEmpty { | 652 | 108 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { | 653 | 108 | Some(p) => p, | 654 | 0 | None => return Ok(None), | 655 | | }; | 656 | 108 | self.fill_to_next(hole_rep); | 657 | 108 | let split = self.push_split_hole(); | 658 | | | 659 | 108 | let split_hole = if greedy { | 660 | 108 | self.fill_split(split, Some(entry_rep), None) | 661 | | } else { | 662 | 0 | self.fill_split(split, None, Some(entry_rep)) | 663 | | }; | 664 | 108 | Ok(Some(Patch { hole: split_hole, entry: entry_rep })) | 665 | 108 | } |
<regex::compile::Compiler>::c_repeat_one_or_more Line | Count | Source | 647 | 108 | fn c_repeat_one_or_more( | 648 | 108 | &mut self, | 649 | 108 | expr: &Hir, | 650 | 108 | greedy: bool, | 651 | 108 | ) -> ResultOrEmpty { | 652 | 108 | let Patch { hole: hole_rep, entry: entry_rep } = match self.c(expr)? { | 653 | 108 | Some(p) => p, | 654 | 0 | None => return Ok(None), | 655 | | }; | 656 | 108 | self.fill_to_next(hole_rep); | 657 | 108 | let split = self.push_split_hole(); | 658 | | | 659 | 108 | let split_hole = if greedy { | 660 | 108 | self.fill_split(split, Some(entry_rep), None) | 661 | | } else { | 662 | 0 | self.fill_split(split, None, Some(entry_rep)) | 663 | | }; | 664 | 108 | Ok(Some(Patch { hole: split_hole, entry: entry_rep })) | 665 | 108 | } |
|
666 | | |
667 | 0 | fn c_repeat_range_min_or_more( |
668 | 0 | &mut self, |
669 | 0 | expr: &Hir, |
670 | 0 | greedy: bool, |
671 | 0 | min: u32, |
672 | 0 | ) -> ResultOrEmpty { |
673 | 0 | let min = u32_to_usize(min); |
674 | | // Using next_inst() is ok, because we can't return it (concat would |
675 | | // have to return Some(_) while c_repeat_range_min_or_more returns |
676 | | // None). |
677 | 0 | let patch_concat = self |
678 | 0 | .c_concat(iter::repeat(expr).take(min))? |
679 | 0 | .unwrap_or(self.next_inst()); |
680 | 0 | if let Some(patch_rep) = self.c_repeat_zero_or_more(expr, greedy)? { |
681 | 0 | self.fill(patch_concat.hole, patch_rep.entry); |
682 | 0 | Ok(Some(Patch { hole: patch_rep.hole, entry: patch_concat.entry })) |
683 | | } else { |
684 | 0 | Ok(None) |
685 | | } |
686 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::c_repeat_range_min_or_more Unexecuted instantiation: <regex::compile::Compiler>::c_repeat_range_min_or_more |
687 | | |
688 | 0 | fn c_repeat_range( |
689 | 0 | &mut self, |
690 | 0 | expr: &Hir, |
691 | 0 | greedy: bool, |
692 | 0 | min: u32, |
693 | 0 | max: u32, |
694 | 0 | ) -> ResultOrEmpty { |
695 | 0 | let (min, max) = (u32_to_usize(min), u32_to_usize(max)); |
696 | 0 | debug_assert!(min <= max); |
697 | 0 | let patch_concat = self.c_concat(iter::repeat(expr).take(min))?; |
698 | 0 | if min == max { |
699 | 0 | return Ok(patch_concat); |
700 | 0 | } |
701 | | // Same reasoning as in c_repeat_range_min_or_more (we know that min < |
702 | | // max at this point). |
703 | 0 | let patch_concat = patch_concat.unwrap_or(self.next_inst()); |
704 | 0 | let initial_entry = patch_concat.entry; |
705 | | // It is much simpler to compile, e.g., `a{2,5}` as: |
706 | | // |
707 | | // aaa?a?a? |
708 | | // |
709 | | // But you end up with a sequence of instructions like this: |
710 | | // |
711 | | // 0: 'a' |
712 | | // 1: 'a', |
713 | | // 2: split(3, 4) |
714 | | // 3: 'a' |
715 | | // 4: split(5, 6) |
716 | | // 5: 'a' |
717 | | // 6: split(7, 8) |
718 | | // 7: 'a' |
719 | | // 8: MATCH |
720 | | // |
721 | | // This is *incredibly* inefficient because the splits end |
722 | | // up forming a chain, which has to be resolved everything a |
723 | | // transition is followed. |
724 | 0 | let mut holes = vec![]; |
725 | 0 | let mut prev_hole = patch_concat.hole; |
726 | 0 | for _ in min..max { |
727 | 0 | self.fill_to_next(prev_hole); |
728 | 0 | let split = self.push_split_hole(); |
729 | 0 | let Patch { hole, entry } = match self.c(expr)? { |
730 | 0 | Some(p) => p, |
731 | 0 | None => return self.pop_split_hole(), |
732 | | }; |
733 | 0 | prev_hole = hole; |
734 | 0 | if greedy { |
735 | 0 | holes.push(self.fill_split(split, Some(entry), None)); |
736 | 0 | } else { |
737 | 0 | holes.push(self.fill_split(split, None, Some(entry))); |
738 | 0 | } |
739 | | } |
740 | 0 | holes.push(prev_hole); |
741 | 0 | Ok(Some(Patch { hole: Hole::Many(holes), entry: initial_entry })) |
742 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::c_repeat_range Unexecuted instantiation: <regex::compile::Compiler>::c_repeat_range |
743 | | |
744 | | /// Can be used as a default value for the c_* functions when the call to |
745 | | /// c_function is followed by inserting at least one instruction that is |
746 | | /// always executed after the ones written by the c* function. |
747 | 138 | fn next_inst(&self) -> Patch { |
748 | 138 | Patch { hole: Hole::None, entry: self.insts.len() } |
749 | 138 | } <regex::compile::Compiler>::next_inst Line | Count | Source | 747 | 69 | fn next_inst(&self) -> Patch { | 748 | 69 | Patch { hole: Hole::None, entry: self.insts.len() } | 749 | 69 | } |
<regex::compile::Compiler>::next_inst Line | Count | Source | 747 | 69 | fn next_inst(&self) -> Patch { | 748 | 69 | Patch { hole: Hole::None, entry: self.insts.len() } | 749 | 69 | } |
|
750 | | |
751 | 5.65k | fn fill(&mut self, hole: Hole, goto: InstPtr) { |
752 | 5.65k | match hole { |
753 | 498 | Hole::None => {} |
754 | 4.37k | Hole::One(pc) => { |
755 | 4.37k | self.insts[pc].fill(goto); |
756 | 4.37k | } |
757 | 780 | Hole::Many(holes) => { |
758 | 2.38k | for hole in holes { |
759 | 1.60k | self.fill(hole, goto); |
760 | 1.60k | } |
761 | | } |
762 | | } |
763 | 5.65k | } <regex::compile::Compiler>::fill Line | Count | Source | 751 | 2.82k | fn fill(&mut self, hole: Hole, goto: InstPtr) { | 752 | 2.82k | match hole { | 753 | 249 | Hole::None => {} | 754 | 2.18k | Hole::One(pc) => { | 755 | 2.18k | self.insts[pc].fill(goto); | 756 | 2.18k | } | 757 | 390 | Hole::Many(holes) => { | 758 | 1.19k | for hole in holes { | 759 | 804 | self.fill(hole, goto); | 760 | 804 | } | 761 | | } | 762 | | } | 763 | 2.82k | } |
<regex::compile::Compiler>::fill Line | Count | Source | 751 | 2.82k | fn fill(&mut self, hole: Hole, goto: InstPtr) { | 752 | 2.82k | match hole { | 753 | 249 | Hole::None => {} | 754 | 2.18k | Hole::One(pc) => { | 755 | 2.18k | self.insts[pc].fill(goto); | 756 | 2.18k | } | 757 | 390 | Hole::Many(holes) => { | 758 | 1.19k | for hole in holes { | 759 | 804 | self.fill(hole, goto); | 760 | 804 | } | 761 | | } | 762 | | } | 763 | 2.82k | } |
|
764 | | |
765 | 858 | fn fill_to_next(&mut self, hole: Hole) { |
766 | 858 | let next = self.insts.len(); |
767 | 858 | self.fill(hole, next); |
768 | 858 | } <regex::compile::Compiler>::fill_to_next Line | Count | Source | 765 | 429 | fn fill_to_next(&mut self, hole: Hole) { | 766 | 429 | let next = self.insts.len(); | 767 | 429 | self.fill(hole, next); | 768 | 429 | } |
<regex::compile::Compiler>::fill_to_next Line | Count | Source | 765 | 429 | fn fill_to_next(&mut self, hole: Hole) { | 766 | 429 | let next = self.insts.len(); | 767 | 429 | self.fill(hole, next); | 768 | 429 | } |
|
769 | | |
770 | 1.40k | fn fill_split( |
771 | 1.40k | &mut self, |
772 | 1.40k | hole: Hole, |
773 | 1.40k | goto1: Option<InstPtr>, |
774 | 1.40k | goto2: Option<InstPtr>, |
775 | 1.40k | ) -> Hole { |
776 | 1.40k | match hole { |
777 | 0 | Hole::None => Hole::None, |
778 | 1.40k | Hole::One(pc) => match (goto1, goto2) { |
779 | 0 | (Some(goto1), Some(goto2)) => { |
780 | 0 | self.insts[pc].fill_split(goto1, goto2); |
781 | 0 | Hole::None |
782 | | } |
783 | 1.40k | (Some(goto1), None) => { |
784 | 1.40k | self.insts[pc].half_fill_split_goto1(goto1); |
785 | 1.40k | Hole::One(pc) |
786 | | } |
787 | 0 | (None, Some(goto2)) => { |
788 | 0 | self.insts[pc].half_fill_split_goto2(goto2); |
789 | 0 | Hole::One(pc) |
790 | | } |
791 | 0 | (None, None) => unreachable!( |
792 | | "at least one of the split \ |
793 | | holes must be filled" |
794 | | ), |
795 | | }, |
796 | 0 | Hole::Many(holes) => { |
797 | 0 | let mut new_holes = vec![]; |
798 | 0 | for hole in holes { |
799 | 0 | new_holes.push(self.fill_split(hole, goto1, goto2)); |
800 | 0 | } |
801 | 0 | if new_holes.is_empty() { |
802 | 0 | Hole::None |
803 | 0 | } else if new_holes.len() == 1 { |
804 | 0 | new_holes.pop().unwrap() |
805 | | } else { |
806 | 0 | Hole::Many(new_holes) |
807 | | } |
808 | | } |
809 | | } |
810 | 1.40k | } <regex::compile::Compiler>::fill_split Line | Count | Source | 770 | 702 | fn fill_split( | 771 | 702 | &mut self, | 772 | 702 | hole: Hole, | 773 | 702 | goto1: Option<InstPtr>, | 774 | 702 | goto2: Option<InstPtr>, | 775 | 702 | ) -> Hole { | 776 | 702 | match hole { | 777 | 0 | Hole::None => Hole::None, | 778 | 702 | Hole::One(pc) => match (goto1, goto2) { | 779 | 0 | (Some(goto1), Some(goto2)) => { | 780 | 0 | self.insts[pc].fill_split(goto1, goto2); | 781 | 0 | Hole::None | 782 | | } | 783 | 702 | (Some(goto1), None) => { | 784 | 702 | self.insts[pc].half_fill_split_goto1(goto1); | 785 | 702 | Hole::One(pc) | 786 | | } | 787 | 0 | (None, Some(goto2)) => { | 788 | 0 | self.insts[pc].half_fill_split_goto2(goto2); | 789 | 0 | Hole::One(pc) | 790 | | } | 791 | 0 | (None, None) => unreachable!( | 792 | | "at least one of the split \ | 793 | | holes must be filled" | 794 | | ), | 795 | | }, | 796 | 0 | Hole::Many(holes) => { | 797 | 0 | let mut new_holes = vec![]; | 798 | 0 | for hole in holes { | 799 | 0 | new_holes.push(self.fill_split(hole, goto1, goto2)); | 800 | 0 | } | 801 | 0 | if new_holes.is_empty() { | 802 | 0 | Hole::None | 803 | 0 | } else if new_holes.len() == 1 { | 804 | 0 | new_holes.pop().unwrap() | 805 | | } else { | 806 | 0 | Hole::Many(new_holes) | 807 | | } | 808 | | } | 809 | | } | 810 | 702 | } |
<regex::compile::Compiler>::fill_split Line | Count | Source | 770 | 702 | fn fill_split( | 771 | 702 | &mut self, | 772 | 702 | hole: Hole, | 773 | 702 | goto1: Option<InstPtr>, | 774 | 702 | goto2: Option<InstPtr>, | 775 | 702 | ) -> Hole { | 776 | 702 | match hole { | 777 | 0 | Hole::None => Hole::None, | 778 | 702 | Hole::One(pc) => match (goto1, goto2) { | 779 | 0 | (Some(goto1), Some(goto2)) => { | 780 | 0 | self.insts[pc].fill_split(goto1, goto2); | 781 | 0 | Hole::None | 782 | | } | 783 | 702 | (Some(goto1), None) => { | 784 | 702 | self.insts[pc].half_fill_split_goto1(goto1); | 785 | 702 | Hole::One(pc) | 786 | | } | 787 | 0 | (None, Some(goto2)) => { | 788 | 0 | self.insts[pc].half_fill_split_goto2(goto2); | 789 | 0 | Hole::One(pc) | 790 | | } | 791 | 0 | (None, None) => unreachable!( | 792 | | "at least one of the split \ | 793 | | holes must be filled" | 794 | | ), | 795 | | }, | 796 | 0 | Hole::Many(holes) => { | 797 | 0 | let mut new_holes = vec![]; | 798 | 0 | for hole in holes { | 799 | 0 | new_holes.push(self.fill_split(hole, goto1, goto2)); | 800 | 0 | } | 801 | 0 | if new_holes.is_empty() { | 802 | 0 | Hole::None | 803 | 0 | } else if new_holes.len() == 1 { | 804 | 0 | new_holes.pop().unwrap() | 805 | | } else { | 806 | 0 | Hole::Many(new_holes) | 807 | | } | 808 | | } | 809 | | } | 810 | 702 | } |
|
811 | | |
812 | 246 | fn push_compiled(&mut self, inst: Inst) { |
813 | 246 | self.insts.push(MaybeInst::Compiled(inst)); |
814 | 246 | } <regex::compile::Compiler>::push_compiled Line | Count | Source | 812 | 123 | fn push_compiled(&mut self, inst: Inst) { | 813 | 123 | self.insts.push(MaybeInst::Compiled(inst)); | 814 | 123 | } |
<regex::compile::Compiler>::push_compiled Line | Count | Source | 812 | 123 | fn push_compiled(&mut self, inst: Inst) { | 813 | 123 | self.insts.push(MaybeInst::Compiled(inst)); | 814 | 123 | } |
|
815 | | |
816 | 2.97k | fn push_hole(&mut self, inst: InstHole) -> Hole { |
817 | 2.97k | let hole = self.insts.len(); |
818 | 2.97k | self.insts.push(MaybeInst::Uncompiled(inst)); |
819 | 2.97k | Hole::One(hole) |
820 | 2.97k | } <regex::compile::Compiler>::push_hole Line | Count | Source | 816 | 1.48k | fn push_hole(&mut self, inst: InstHole) -> Hole { | 817 | 1.48k | let hole = self.insts.len(); | 818 | 1.48k | self.insts.push(MaybeInst::Uncompiled(inst)); | 819 | 1.48k | Hole::One(hole) | 820 | 1.48k | } |
<regex::compile::Compiler>::push_hole Line | Count | Source | 816 | 1.48k | fn push_hole(&mut self, inst: InstHole) -> Hole { | 817 | 1.48k | let hole = self.insts.len(); | 818 | 1.48k | self.insts.push(MaybeInst::Uncompiled(inst)); | 819 | 1.48k | Hole::One(hole) | 820 | 1.48k | } |
|
821 | | |
822 | 1.40k | fn push_split_hole(&mut self) -> Hole { |
823 | 1.40k | let hole = self.insts.len(); |
824 | 1.40k | self.insts.push(MaybeInst::Split); |
825 | 1.40k | Hole::One(hole) |
826 | 1.40k | } <regex::compile::Compiler>::push_split_hole Line | Count | Source | 822 | 702 | fn push_split_hole(&mut self) -> Hole { | 823 | 702 | let hole = self.insts.len(); | 824 | 702 | self.insts.push(MaybeInst::Split); | 825 | 702 | Hole::One(hole) | 826 | 702 | } |
<regex::compile::Compiler>::push_split_hole Line | Count | Source | 822 | 702 | fn push_split_hole(&mut self) -> Hole { | 823 | 702 | let hole = self.insts.len(); | 824 | 702 | self.insts.push(MaybeInst::Split); | 825 | 702 | Hole::One(hole) | 826 | 702 | } |
|
827 | | |
828 | 0 | fn pop_split_hole(&mut self) -> ResultOrEmpty { |
829 | 0 | self.insts.pop(); |
830 | 0 | Ok(None) |
831 | 0 | } Unexecuted instantiation: <regex::compile::Compiler>::pop_split_hole Unexecuted instantiation: <regex::compile::Compiler>::pop_split_hole |
832 | | |
833 | 4.03k | fn check_size(&self) -> result::Result<(), Error> { |
834 | | use std::mem::size_of; |
835 | | |
836 | 4.03k | let size = |
837 | 4.03k | self.extra_inst_bytes + (self.insts.len() * size_of::<Inst>()); |
838 | 4.03k | if size > self.size_limit { |
839 | 0 | Err(Error::CompiledTooBig(self.size_limit)) |
840 | | } else { |
841 | 4.03k | Ok(()) |
842 | | } |
843 | 4.03k | } <regex::compile::Compiler>::check_size Line | Count | Source | 833 | 2.01k | fn check_size(&self) -> result::Result<(), Error> { | 834 | | use std::mem::size_of; | 835 | | | 836 | 2.01k | let size = | 837 | 2.01k | self.extra_inst_bytes + (self.insts.len() * size_of::<Inst>()); | 838 | 2.01k | if size > self.size_limit { | 839 | 0 | Err(Error::CompiledTooBig(self.size_limit)) | 840 | | } else { | 841 | 2.01k | Ok(()) | 842 | | } | 843 | 2.01k | } |
<regex::compile::Compiler>::check_size Line | Count | Source | 833 | 2.01k | fn check_size(&self) -> result::Result<(), Error> { | 834 | | use std::mem::size_of; | 835 | | | 836 | 2.01k | let size = | 837 | 2.01k | self.extra_inst_bytes + (self.insts.len() * size_of::<Inst>()); | 838 | 2.01k | if size > self.size_limit { | 839 | 0 | Err(Error::CompiledTooBig(self.size_limit)) | 840 | | } else { | 841 | 2.01k | Ok(()) | 842 | | } | 843 | 2.01k | } |
|
844 | | } |
845 | | |
846 | | #[derive(Debug)] |
847 | | enum Hole { |
848 | | None, |
849 | | One(InstPtr), |
850 | | Many(Vec<Hole>), |
851 | | } |
852 | | |
853 | | impl Hole { |
854 | 0 | fn dup_one(self) -> (Self, Self) { |
855 | 0 | match self { |
856 | 0 | Hole::One(pc) => (Hole::One(pc), Hole::One(pc)), |
857 | | Hole::None | Hole::Many(_) => { |
858 | 0 | unreachable!("must be called on single hole") |
859 | | } |
860 | | } |
861 | 0 | } Unexecuted instantiation: <regex::compile::Hole>::dup_one Unexecuted instantiation: <regex::compile::Hole>::dup_one |
862 | | } |
863 | | |
864 | | #[derive(Clone, Debug)] |
865 | | enum MaybeInst { |
866 | | Compiled(Inst), |
867 | | Uncompiled(InstHole), |
868 | | Split, |
869 | | Split1(InstPtr), |
870 | | Split2(InstPtr), |
871 | | } |
872 | | |
873 | | impl MaybeInst { |
874 | 4.37k | fn fill(&mut self, goto: InstPtr) { |
875 | 4.37k | let maybeinst = match *self { |
876 | 0 | MaybeInst::Split => MaybeInst::Split1(goto), |
877 | 2.97k | MaybeInst::Uncompiled(ref inst) => { |
878 | 2.97k | MaybeInst::Compiled(inst.fill(goto)) |
879 | | } |
880 | 1.40k | MaybeInst::Split1(goto1) => { |
881 | 1.40k | MaybeInst::Compiled(Inst::Split(InstSplit { |
882 | 1.40k | goto1: goto1, |
883 | 1.40k | goto2: goto, |
884 | 1.40k | })) |
885 | | } |
886 | 0 | MaybeInst::Split2(goto2) => { |
887 | 0 | MaybeInst::Compiled(Inst::Split(InstSplit { |
888 | 0 | goto1: goto, |
889 | 0 | goto2: goto2, |
890 | 0 | })) |
891 | | } |
892 | 0 | _ => unreachable!( |
893 | | "not all instructions were compiled! \ |
894 | | found uncompiled instruction: {:?}", |
895 | | self |
896 | | ), |
897 | | }; |
898 | 4.37k | *self = maybeinst; |
899 | 4.37k | } <regex::compile::MaybeInst>::fill Line | Count | Source | 874 | 2.18k | fn fill(&mut self, goto: InstPtr) { | 875 | 2.18k | let maybeinst = match *self { | 876 | 0 | MaybeInst::Split => MaybeInst::Split1(goto), | 877 | 1.48k | MaybeInst::Uncompiled(ref inst) => { | 878 | 1.48k | MaybeInst::Compiled(inst.fill(goto)) | 879 | | } | 880 | 702 | MaybeInst::Split1(goto1) => { | 881 | 702 | MaybeInst::Compiled(Inst::Split(InstSplit { | 882 | 702 | goto1: goto1, | 883 | 702 | goto2: goto, | 884 | 702 | })) | 885 | | } | 886 | 0 | MaybeInst::Split2(goto2) => { | 887 | 0 | MaybeInst::Compiled(Inst::Split(InstSplit { | 888 | 0 | goto1: goto, | 889 | 0 | goto2: goto2, | 890 | 0 | })) | 891 | | } | 892 | 0 | _ => unreachable!( | 893 | | "not all instructions were compiled! \ | 894 | | found uncompiled instruction: {:?}", | 895 | | self | 896 | | ), | 897 | | }; | 898 | 2.18k | *self = maybeinst; | 899 | 2.18k | } |
<regex::compile::MaybeInst>::fill Line | Count | Source | 874 | 2.18k | fn fill(&mut self, goto: InstPtr) { | 875 | 2.18k | let maybeinst = match *self { | 876 | 0 | MaybeInst::Split => MaybeInst::Split1(goto), | 877 | 1.48k | MaybeInst::Uncompiled(ref inst) => { | 878 | 1.48k | MaybeInst::Compiled(inst.fill(goto)) | 879 | | } | 880 | 702 | MaybeInst::Split1(goto1) => { | 881 | 702 | MaybeInst::Compiled(Inst::Split(InstSplit { | 882 | 702 | goto1: goto1, | 883 | 702 | goto2: goto, | 884 | 702 | })) | 885 | | } | 886 | 0 | MaybeInst::Split2(goto2) => { | 887 | 0 | MaybeInst::Compiled(Inst::Split(InstSplit { | 888 | 0 | goto1: goto, | 889 | 0 | goto2: goto2, | 890 | 0 | })) | 891 | | } | 892 | 0 | _ => unreachable!( | 893 | | "not all instructions were compiled! \ | 894 | | found uncompiled instruction: {:?}", | 895 | | self | 896 | | ), | 897 | | }; | 898 | 2.18k | *self = maybeinst; | 899 | 2.18k | } |
|
900 | | |
901 | 0 | fn fill_split(&mut self, goto1: InstPtr, goto2: InstPtr) { |
902 | 0 | let filled = match *self { |
903 | | MaybeInst::Split => { |
904 | 0 | Inst::Split(InstSplit { goto1: goto1, goto2: goto2 }) |
905 | | } |
906 | 0 | _ => unreachable!( |
907 | | "must be called on Split instruction, \ |
908 | | instead it was called on: {:?}", |
909 | | self |
910 | | ), |
911 | | }; |
912 | 0 | *self = MaybeInst::Compiled(filled); |
913 | 0 | } Unexecuted instantiation: <regex::compile::MaybeInst>::fill_split Unexecuted instantiation: <regex::compile::MaybeInst>::fill_split |
914 | | |
915 | 1.40k | fn half_fill_split_goto1(&mut self, goto1: InstPtr) { |
916 | 1.40k | let half_filled = match *self { |
917 | 1.40k | MaybeInst::Split => goto1, |
918 | 0 | _ => unreachable!( |
919 | | "must be called on Split instruction, \ |
920 | | instead it was called on: {:?}", |
921 | | self |
922 | | ), |
923 | | }; |
924 | 1.40k | *self = MaybeInst::Split1(half_filled); |
925 | 1.40k | } <regex::compile::MaybeInst>::half_fill_split_goto1 Line | Count | Source | 915 | 702 | fn half_fill_split_goto1(&mut self, goto1: InstPtr) { | 916 | 702 | let half_filled = match *self { | 917 | 702 | MaybeInst::Split => goto1, | 918 | 0 | _ => unreachable!( | 919 | | "must be called on Split instruction, \ | 920 | | instead it was called on: {:?}", | 921 | | self | 922 | | ), | 923 | | }; | 924 | 702 | *self = MaybeInst::Split1(half_filled); | 925 | 702 | } |
<regex::compile::MaybeInst>::half_fill_split_goto1 Line | Count | Source | 915 | 702 | fn half_fill_split_goto1(&mut self, goto1: InstPtr) { | 916 | 702 | let half_filled = match *self { | 917 | 702 | MaybeInst::Split => goto1, | 918 | 0 | _ => unreachable!( | 919 | | "must be called on Split instruction, \ | 920 | | instead it was called on: {:?}", | 921 | | self | 922 | | ), | 923 | | }; | 924 | 702 | *self = MaybeInst::Split1(half_filled); | 925 | 702 | } |
|
926 | | |
927 | 0 | fn half_fill_split_goto2(&mut self, goto2: InstPtr) { |
928 | 0 | let half_filled = match *self { |
929 | 0 | MaybeInst::Split => goto2, |
930 | 0 | _ => unreachable!( |
931 | | "must be called on Split instruction, \ |
932 | | instead it was called on: {:?}", |
933 | | self |
934 | | ), |
935 | | }; |
936 | 0 | *self = MaybeInst::Split2(half_filled); |
937 | 0 | } Unexecuted instantiation: <regex::compile::MaybeInst>::half_fill_split_goto2 Unexecuted instantiation: <regex::compile::MaybeInst>::half_fill_split_goto2 |
938 | | |
939 | 4.62k | fn unwrap(self) -> Inst { |
940 | 4.62k | match self { |
941 | 4.62k | MaybeInst::Compiled(inst) => inst, |
942 | 0 | _ => unreachable!( |
943 | | "must be called on a compiled instruction, \ |
944 | | instead it was called on: {:?}", |
945 | | self |
946 | | ), |
947 | | } |
948 | 4.62k | } <regex::compile::MaybeInst>::unwrap Line | Count | Source | 939 | 2.31k | fn unwrap(self) -> Inst { | 940 | 2.31k | match self { | 941 | 2.31k | MaybeInst::Compiled(inst) => inst, | 942 | 0 | _ => unreachable!( | 943 | | "must be called on a compiled instruction, \ | 944 | | instead it was called on: {:?}", | 945 | | self | 946 | | ), | 947 | | } | 948 | 2.31k | } |
<regex::compile::MaybeInst>::unwrap Line | Count | Source | 939 | 2.31k | fn unwrap(self) -> Inst { | 940 | 2.31k | match self { | 941 | 2.31k | MaybeInst::Compiled(inst) => inst, | 942 | 0 | _ => unreachable!( | 943 | | "must be called on a compiled instruction, \ | 944 | | instead it was called on: {:?}", | 945 | | self | 946 | | ), | 947 | | } | 948 | 2.31k | } |
|
949 | | } |
950 | | |
951 | | #[derive(Clone, Debug)] |
952 | | enum InstHole { |
953 | | Save { slot: usize }, |
954 | | EmptyLook { look: EmptyLook }, |
955 | | Char { c: char }, |
956 | | Ranges { ranges: Vec<(char, char)> }, |
957 | | Bytes { start: u8, end: u8 }, |
958 | | } |
959 | | |
960 | | impl InstHole { |
961 | 2.97k | fn fill(&self, goto: InstPtr) -> Inst { |
962 | 2.97k | match *self { |
963 | 168 | InstHole::Save { slot } => { |
964 | 168 | Inst::Save(InstSave { goto: goto, slot: slot }) |
965 | | } |
966 | 108 | InstHole::EmptyLook { look } => { |
967 | 108 | Inst::EmptyLook(InstEmptyLook { goto: goto, look: look }) |
968 | | } |
969 | 558 | InstHole::Char { c } => Inst::Char(InstChar { goto: goto, c: c }), |
970 | 210 | InstHole::Ranges { ref ranges } => Inst::Ranges(InstRanges { |
971 | 210 | goto: goto, |
972 | 210 | ranges: ranges.clone().into_boxed_slice(), |
973 | 210 | }), |
974 | 1.92k | InstHole::Bytes { start, end } => { |
975 | 1.92k | Inst::Bytes(InstBytes { goto: goto, start: start, end: end }) |
976 | | } |
977 | | } |
978 | 2.97k | } <regex::compile::InstHole>::fill Line | Count | Source | 961 | 1.48k | fn fill(&self, goto: InstPtr) -> Inst { | 962 | 1.48k | match *self { | 963 | 84 | InstHole::Save { slot } => { | 964 | 84 | Inst::Save(InstSave { goto: goto, slot: slot }) | 965 | | } | 966 | 54 | InstHole::EmptyLook { look } => { | 967 | 54 | Inst::EmptyLook(InstEmptyLook { goto: goto, look: look }) | 968 | | } | 969 | 279 | InstHole::Char { c } => Inst::Char(InstChar { goto: goto, c: c }), | 970 | 105 | InstHole::Ranges { ref ranges } => Inst::Ranges(InstRanges { | 971 | 105 | goto: goto, | 972 | 105 | ranges: ranges.clone().into_boxed_slice(), | 973 | 105 | }), | 974 | 963 | InstHole::Bytes { start, end } => { | 975 | 963 | Inst::Bytes(InstBytes { goto: goto, start: start, end: end }) | 976 | | } | 977 | | } | 978 | 1.48k | } |
<regex::compile::InstHole>::fill Line | Count | Source | 961 | 1.48k | fn fill(&self, goto: InstPtr) -> Inst { | 962 | 1.48k | match *self { | 963 | 84 | InstHole::Save { slot } => { | 964 | 84 | Inst::Save(InstSave { goto: goto, slot: slot }) | 965 | | } | 966 | 54 | InstHole::EmptyLook { look } => { | 967 | 54 | Inst::EmptyLook(InstEmptyLook { goto: goto, look: look }) | 968 | | } | 969 | 279 | InstHole::Char { c } => Inst::Char(InstChar { goto: goto, c: c }), | 970 | 105 | InstHole::Ranges { ref ranges } => Inst::Ranges(InstRanges { | 971 | 105 | goto: goto, | 972 | 105 | ranges: ranges.clone().into_boxed_slice(), | 973 | 105 | }), | 974 | 963 | InstHole::Bytes { start, end } => { | 975 | 963 | Inst::Bytes(InstBytes { goto: goto, start: start, end: end }) | 976 | | } | 977 | | } | 978 | 1.48k | } |
|
979 | | } |
980 | | |
981 | | struct CompileClass<'a, 'b> { |
982 | | c: &'a mut Compiler, |
983 | | ranges: &'b [hir::ClassUnicodeRange], |
984 | | } |
985 | | |
986 | | impl<'a, 'b> CompileClass<'a, 'b> { |
987 | 420 | fn compile(mut self) -> Result { |
988 | 420 | let mut holes = vec![]; |
989 | 420 | let mut initial_entry = None; |
990 | 420 | let mut last_split = Hole::None; |
991 | 420 | let mut utf8_seqs = self.c.utf8_seqs.take().unwrap(); |
992 | 420 | self.c.suffix_cache.clear(); |
993 | | |
994 | 756 | for (i, range) in self.ranges.iter().enumerate() { |
995 | 756 | let is_last_range = i + 1 == self.ranges.len(); |
996 | 756 | utf8_seqs.reset(range.start(), range.end()); |
997 | 756 | let mut it = (&mut utf8_seqs).peekable(); |
998 | | loop { |
999 | 1.60k | let utf8_seq = match it.next() { |
1000 | 756 | None => break, |
1001 | 852 | Some(utf8_seq) => utf8_seq, |
1002 | | }; |
1003 | 852 | if is_last_range && it.peek().is_none() { |
1004 | 420 | let Patch { hole, entry } = self.c_utf8_seq(&utf8_seq)?; |
1005 | 420 | holes.push(hole); |
1006 | 420 | self.c.fill(last_split, entry); |
1007 | 420 | last_split = Hole::None; |
1008 | 420 | if initial_entry.is_none() { |
1009 | 84 | initial_entry = Some(entry); |
1010 | 336 | } |
1011 | | } else { |
1012 | 432 | if initial_entry.is_none() { |
1013 | 336 | initial_entry = Some(self.c.insts.len()); |
1014 | 336 | } |
1015 | 432 | self.c.fill_to_next(last_split); |
1016 | 432 | last_split = self.c.push_split_hole(); |
1017 | 432 | let Patch { hole, entry } = self.c_utf8_seq(&utf8_seq)?; |
1018 | 432 | holes.push(hole); |
1019 | 432 | last_split = |
1020 | 432 | self.c.fill_split(last_split, Some(entry), None); |
1021 | | } |
1022 | | } |
1023 | | } |
1024 | 420 | self.c.utf8_seqs = Some(utf8_seqs); |
1025 | 420 | Ok(Patch { hole: Hole::Many(holes), entry: initial_entry.unwrap() }) |
1026 | 420 | } <regex::compile::CompileClass>::compile Line | Count | Source | 987 | 210 | fn compile(mut self) -> Result { | 988 | 210 | let mut holes = vec![]; | 989 | 210 | let mut initial_entry = None; | 990 | 210 | let mut last_split = Hole::None; | 991 | 210 | let mut utf8_seqs = self.c.utf8_seqs.take().unwrap(); | 992 | 210 | self.c.suffix_cache.clear(); | 993 | | | 994 | 378 | for (i, range) in self.ranges.iter().enumerate() { | 995 | 378 | let is_last_range = i + 1 == self.ranges.len(); | 996 | 378 | utf8_seqs.reset(range.start(), range.end()); | 997 | 378 | let mut it = (&mut utf8_seqs).peekable(); | 998 | | loop { | 999 | 804 | let utf8_seq = match it.next() { | 1000 | 378 | None => break, | 1001 | 426 | Some(utf8_seq) => utf8_seq, | 1002 | | }; | 1003 | 426 | if is_last_range && it.peek().is_none() { | 1004 | 210 | let Patch { hole, entry } = self.c_utf8_seq(&utf8_seq)?; | 1005 | 210 | holes.push(hole); | 1006 | 210 | self.c.fill(last_split, entry); | 1007 | 210 | last_split = Hole::None; | 1008 | 210 | if initial_entry.is_none() { | 1009 | 42 | initial_entry = Some(entry); | 1010 | 168 | } | 1011 | | } else { | 1012 | 216 | if initial_entry.is_none() { | 1013 | 168 | initial_entry = Some(self.c.insts.len()); | 1014 | 168 | } | 1015 | 216 | self.c.fill_to_next(last_split); | 1016 | 216 | last_split = self.c.push_split_hole(); | 1017 | 216 | let Patch { hole, entry } = self.c_utf8_seq(&utf8_seq)?; | 1018 | 216 | holes.push(hole); | 1019 | 216 | last_split = | 1020 | 216 | self.c.fill_split(last_split, Some(entry), None); | 1021 | | } | 1022 | | } | 1023 | | } | 1024 | 210 | self.c.utf8_seqs = Some(utf8_seqs); | 1025 | 210 | Ok(Patch { hole: Hole::Many(holes), entry: initial_entry.unwrap() }) | 1026 | 210 | } |
<regex::compile::CompileClass>::compile Line | Count | Source | 987 | 210 | fn compile(mut self) -> Result { | 988 | 210 | let mut holes = vec![]; | 989 | 210 | let mut initial_entry = None; | 990 | 210 | let mut last_split = Hole::None; | 991 | 210 | let mut utf8_seqs = self.c.utf8_seqs.take().unwrap(); | 992 | 210 | self.c.suffix_cache.clear(); | 993 | | | 994 | 378 | for (i, range) in self.ranges.iter().enumerate() { | 995 | 378 | let is_last_range = i + 1 == self.ranges.len(); | 996 | 378 | utf8_seqs.reset(range.start(), range.end()); | 997 | 378 | let mut it = (&mut utf8_seqs).peekable(); | 998 | | loop { | 999 | 804 | let utf8_seq = match it.next() { | 1000 | 378 | None => break, | 1001 | 426 | Some(utf8_seq) => utf8_seq, | 1002 | | }; | 1003 | 426 | if is_last_range && it.peek().is_none() { | 1004 | 210 | let Patch { hole, entry } = self.c_utf8_seq(&utf8_seq)?; | 1005 | 210 | holes.push(hole); | 1006 | 210 | self.c.fill(last_split, entry); | 1007 | 210 | last_split = Hole::None; | 1008 | 210 | if initial_entry.is_none() { | 1009 | 42 | initial_entry = Some(entry); | 1010 | 168 | } | 1011 | | } else { | 1012 | 216 | if initial_entry.is_none() { | 1013 | 168 | initial_entry = Some(self.c.insts.len()); | 1014 | 168 | } | 1015 | 216 | self.c.fill_to_next(last_split); | 1016 | 216 | last_split = self.c.push_split_hole(); | 1017 | 216 | let Patch { hole, entry } = self.c_utf8_seq(&utf8_seq)?; | 1018 | 216 | holes.push(hole); | 1019 | 216 | last_split = | 1020 | 216 | self.c.fill_split(last_split, Some(entry), None); | 1021 | | } | 1022 | | } | 1023 | | } | 1024 | 210 | self.c.utf8_seqs = Some(utf8_seqs); | 1025 | 210 | Ok(Patch { hole: Hole::Many(holes), entry: initial_entry.unwrap() }) | 1026 | 210 | } |
|
1027 | | |
1028 | 852 | fn c_utf8_seq(&mut self, seq: &Utf8Sequence) -> Result { |
1029 | 852 | if self.c.compiled.is_reverse { |
1030 | 426 | self.c_utf8_seq_(seq) |
1031 | | } else { |
1032 | 426 | self.c_utf8_seq_(seq.into_iter().rev()) |
1033 | | } |
1034 | 852 | } <regex::compile::CompileClass>::c_utf8_seq Line | Count | Source | 1028 | 426 | fn c_utf8_seq(&mut self, seq: &Utf8Sequence) -> Result { | 1029 | 426 | if self.c.compiled.is_reverse { | 1030 | 213 | self.c_utf8_seq_(seq) | 1031 | | } else { | 1032 | 213 | self.c_utf8_seq_(seq.into_iter().rev()) | 1033 | | } | 1034 | 426 | } |
<regex::compile::CompileClass>::c_utf8_seq Line | Count | Source | 1028 | 426 | fn c_utf8_seq(&mut self, seq: &Utf8Sequence) -> Result { | 1029 | 426 | if self.c.compiled.is_reverse { | 1030 | 213 | self.c_utf8_seq_(seq) | 1031 | | } else { | 1032 | 213 | self.c_utf8_seq_(seq.into_iter().rev()) | 1033 | | } | 1034 | 426 | } |
|
1035 | | |
1036 | 852 | fn c_utf8_seq_<'r, I>(&mut self, seq: I) -> Result |
1037 | 852 | where |
1038 | 852 | I: IntoIterator<Item = &'r Utf8Range>, |
1039 | | { |
1040 | | // The initial instruction for each UTF-8 sequence should be the same. |
1041 | 852 | let mut from_inst = ::std::usize::MAX; |
1042 | 852 | let mut last_hole = Hole::None; |
1043 | 1.92k | for byte_range in seq { |
1044 | 1.06k | let key = SuffixCacheKey { |
1045 | 1.06k | from_inst: from_inst, |
1046 | 1.06k | start: byte_range.start, |
1047 | 1.06k | end: byte_range.end, |
1048 | 1.06k | }; |
1049 | | { |
1050 | 1.06k | let pc = self.c.insts.len(); |
1051 | 1.06k | if let Some(cached_pc) = self.c.suffix_cache.get(key, pc) { |
1052 | 66 | from_inst = cached_pc; |
1053 | 66 | continue; |
1054 | 1.00k | } |
1055 | | } |
1056 | 1.00k | self.c.byte_classes.set_range(byte_range.start, byte_range.end); |
1057 | 1.00k | if from_inst == ::std::usize::MAX { |
1058 | 810 | last_hole = self.c.push_hole(InstHole::Bytes { |
1059 | 810 | start: byte_range.start, |
1060 | 810 | end: byte_range.end, |
1061 | 810 | }); |
1062 | 810 | } else { |
1063 | 192 | self.c.push_compiled(Inst::Bytes(InstBytes { |
1064 | 192 | goto: from_inst, |
1065 | 192 | start: byte_range.start, |
1066 | 192 | end: byte_range.end, |
1067 | 192 | })); |
1068 | 192 | } |
1069 | 1.00k | from_inst = self.c.insts.len().checked_sub(1).unwrap(); |
1070 | 1.00k | debug_assert!(from_inst < ::std::usize::MAX); |
1071 | | } |
1072 | 852 | debug_assert!(from_inst < ::std::usize::MAX); |
1073 | 852 | Ok(Patch { hole: last_hole, entry: from_inst }) |
1074 | 852 | } <regex::compile::CompileClass>::c_utf8_seq_::<core::iter::adapters::rev::Rev<core::slice::iter::Iter<regex_syntax::utf8::Utf8Range>>> Line | Count | Source | 1036 | 213 | fn c_utf8_seq_<'r, I>(&mut self, seq: I) -> Result | 1037 | 213 | where | 1038 | 213 | I: IntoIterator<Item = &'r Utf8Range>, | 1039 | | { | 1040 | | // The initial instruction for each UTF-8 sequence should be the same. | 1041 | 213 | let mut from_inst = ::std::usize::MAX; | 1042 | 213 | let mut last_hole = Hole::None; | 1043 | 480 | for byte_range in seq { | 1044 | 267 | let key = SuffixCacheKey { | 1045 | 267 | from_inst: from_inst, | 1046 | 267 | start: byte_range.start, | 1047 | 267 | end: byte_range.end, | 1048 | 267 | }; | 1049 | | { | 1050 | 267 | let pc = self.c.insts.len(); | 1051 | 267 | if let Some(cached_pc) = self.c.suffix_cache.get(key, pc) { | 1052 | 33 | from_inst = cached_pc; | 1053 | 33 | continue; | 1054 | 234 | } | 1055 | | } | 1056 | 234 | self.c.byte_classes.set_range(byte_range.start, byte_range.end); | 1057 | 234 | if from_inst == ::std::usize::MAX { | 1058 | 192 | last_hole = self.c.push_hole(InstHole::Bytes { | 1059 | 192 | start: byte_range.start, | 1060 | 192 | end: byte_range.end, | 1061 | 192 | }); | 1062 | 192 | } else { | 1063 | 42 | self.c.push_compiled(Inst::Bytes(InstBytes { | 1064 | 42 | goto: from_inst, | 1065 | 42 | start: byte_range.start, | 1066 | 42 | end: byte_range.end, | 1067 | 42 | })); | 1068 | 42 | } | 1069 | 234 | from_inst = self.c.insts.len().checked_sub(1).unwrap(); | 1070 | 234 | debug_assert!(from_inst < ::std::usize::MAX); | 1071 | | } | 1072 | 213 | debug_assert!(from_inst < ::std::usize::MAX); | 1073 | 213 | Ok(Patch { hole: last_hole, entry: from_inst }) | 1074 | 213 | } |
<regex::compile::CompileClass>::c_utf8_seq_::<®ex_syntax::utf8::Utf8Sequence> Line | Count | Source | 1036 | 213 | fn c_utf8_seq_<'r, I>(&mut self, seq: I) -> Result | 1037 | 213 | where | 1038 | 213 | I: IntoIterator<Item = &'r Utf8Range>, | 1039 | | { | 1040 | | // The initial instruction for each UTF-8 sequence should be the same. | 1041 | 213 | let mut from_inst = ::std::usize::MAX; | 1042 | 213 | let mut last_hole = Hole::None; | 1043 | 480 | for byte_range in seq { | 1044 | 267 | let key = SuffixCacheKey { | 1045 | 267 | from_inst: from_inst, | 1046 | 267 | start: byte_range.start, | 1047 | 267 | end: byte_range.end, | 1048 | 267 | }; | 1049 | | { | 1050 | 267 | let pc = self.c.insts.len(); | 1051 | 267 | if let Some(cached_pc) = self.c.suffix_cache.get(key, pc) { | 1052 | 0 | from_inst = cached_pc; | 1053 | 0 | continue; | 1054 | 267 | } | 1055 | | } | 1056 | 267 | self.c.byte_classes.set_range(byte_range.start, byte_range.end); | 1057 | 267 | if from_inst == ::std::usize::MAX { | 1058 | 213 | last_hole = self.c.push_hole(InstHole::Bytes { | 1059 | 213 | start: byte_range.start, | 1060 | 213 | end: byte_range.end, | 1061 | 213 | }); | 1062 | 213 | } else { | 1063 | 54 | self.c.push_compiled(Inst::Bytes(InstBytes { | 1064 | 54 | goto: from_inst, | 1065 | 54 | start: byte_range.start, | 1066 | 54 | end: byte_range.end, | 1067 | 54 | })); | 1068 | 54 | } | 1069 | 267 | from_inst = self.c.insts.len().checked_sub(1).unwrap(); | 1070 | 267 | debug_assert!(from_inst < ::std::usize::MAX); | 1071 | | } | 1072 | 213 | debug_assert!(from_inst < ::std::usize::MAX); | 1073 | 213 | Ok(Patch { hole: last_hole, entry: from_inst }) | 1074 | 213 | } |
<regex::compile::CompileClass>::c_utf8_seq_::<core::iter::adapters::rev::Rev<core::slice::iter::Iter<regex_syntax::utf8::Utf8Range>>> Line | Count | Source | 1036 | 213 | fn c_utf8_seq_<'r, I>(&mut self, seq: I) -> Result | 1037 | 213 | where | 1038 | 213 | I: IntoIterator<Item = &'r Utf8Range>, | 1039 | | { | 1040 | | // The initial instruction for each UTF-8 sequence should be the same. | 1041 | 213 | let mut from_inst = ::std::usize::MAX; | 1042 | 213 | let mut last_hole = Hole::None; | 1043 | 480 | for byte_range in seq { | 1044 | 267 | let key = SuffixCacheKey { | 1045 | 267 | from_inst: from_inst, | 1046 | 267 | start: byte_range.start, | 1047 | 267 | end: byte_range.end, | 1048 | 267 | }; | 1049 | | { | 1050 | 267 | let pc = self.c.insts.len(); | 1051 | 267 | if let Some(cached_pc) = self.c.suffix_cache.get(key, pc) { | 1052 | 33 | from_inst = cached_pc; | 1053 | 33 | continue; | 1054 | 234 | } | 1055 | | } | 1056 | 234 | self.c.byte_classes.set_range(byte_range.start, byte_range.end); | 1057 | 234 | if from_inst == ::std::usize::MAX { | 1058 | 192 | last_hole = self.c.push_hole(InstHole::Bytes { | 1059 | 192 | start: byte_range.start, | 1060 | 192 | end: byte_range.end, | 1061 | 192 | }); | 1062 | 192 | } else { | 1063 | 42 | self.c.push_compiled(Inst::Bytes(InstBytes { | 1064 | 42 | goto: from_inst, | 1065 | 42 | start: byte_range.start, | 1066 | 42 | end: byte_range.end, | 1067 | 42 | })); | 1068 | 42 | } | 1069 | 234 | from_inst = self.c.insts.len().checked_sub(1).unwrap(); | 1070 | 234 | debug_assert!(from_inst < ::std::usize::MAX); | 1071 | | } | 1072 | 213 | debug_assert!(from_inst < ::std::usize::MAX); | 1073 | 213 | Ok(Patch { hole: last_hole, entry: from_inst }) | 1074 | 213 | } |
<regex::compile::CompileClass>::c_utf8_seq_::<®ex_syntax::utf8::Utf8Sequence> Line | Count | Source | 1036 | 213 | fn c_utf8_seq_<'r, I>(&mut self, seq: I) -> Result | 1037 | 213 | where | 1038 | 213 | I: IntoIterator<Item = &'r Utf8Range>, | 1039 | | { | 1040 | | // The initial instruction for each UTF-8 sequence should be the same. | 1041 | 213 | let mut from_inst = ::std::usize::MAX; | 1042 | 213 | let mut last_hole = Hole::None; | 1043 | 480 | for byte_range in seq { | 1044 | 267 | let key = SuffixCacheKey { | 1045 | 267 | from_inst: from_inst, | 1046 | 267 | start: byte_range.start, | 1047 | 267 | end: byte_range.end, | 1048 | 267 | }; | 1049 | | { | 1050 | 267 | let pc = self.c.insts.len(); | 1051 | 267 | if let Some(cached_pc) = self.c.suffix_cache.get(key, pc) { | 1052 | 0 | from_inst = cached_pc; | 1053 | 0 | continue; | 1054 | 267 | } | 1055 | | } | 1056 | 267 | self.c.byte_classes.set_range(byte_range.start, byte_range.end); | 1057 | 267 | if from_inst == ::std::usize::MAX { | 1058 | 213 | last_hole = self.c.push_hole(InstHole::Bytes { | 1059 | 213 | start: byte_range.start, | 1060 | 213 | end: byte_range.end, | 1061 | 213 | }); | 1062 | 213 | } else { | 1063 | 54 | self.c.push_compiled(Inst::Bytes(InstBytes { | 1064 | 54 | goto: from_inst, | 1065 | 54 | start: byte_range.start, | 1066 | 54 | end: byte_range.end, | 1067 | 54 | })); | 1068 | 54 | } | 1069 | 267 | from_inst = self.c.insts.len().checked_sub(1).unwrap(); | 1070 | 267 | debug_assert!(from_inst < ::std::usize::MAX); | 1071 | | } | 1072 | 213 | debug_assert!(from_inst < ::std::usize::MAX); | 1073 | 213 | Ok(Patch { hole: last_hole, entry: from_inst }) | 1074 | 213 | } |
|
1075 | | } |
1076 | | |
1077 | | /// `SuffixCache` is a simple bounded hash map for caching suffix entries in |
1078 | | /// UTF-8 automata. For example, consider the Unicode range \u{0}-\u{FFFF}. |
1079 | | /// The set of byte ranges looks like this: |
1080 | | /// |
1081 | | /// [0-7F] |
1082 | | /// [C2-DF][80-BF] |
1083 | | /// [E0][A0-BF][80-BF] |
1084 | | /// [E1-EC][80-BF][80-BF] |
1085 | | /// [ED][80-9F][80-BF] |
1086 | | /// [EE-EF][80-BF][80-BF] |
1087 | | /// |
1088 | | /// Each line above translates to one alternate in the compiled regex program. |
1089 | | /// However, all but one of the alternates end in the same suffix, which is |
1090 | | /// a waste of an instruction. The suffix cache facilitates reusing them across |
1091 | | /// alternates. |
1092 | | /// |
1093 | | /// Note that a HashMap could be trivially used for this, but we don't need its |
1094 | | /// overhead. Some small bounded space (LRU style) is more than enough. |
1095 | | /// |
1096 | | /// This uses similar idea to [`SparseSet`](../sparse/struct.SparseSet.html), |
1097 | | /// except it uses hashes as original indices and then compares full keys for |
1098 | | /// validation against `dense` array. |
1099 | | #[derive(Debug)] |
1100 | | struct SuffixCache { |
1101 | | sparse: Box<[usize]>, |
1102 | | dense: Vec<SuffixCacheEntry>, |
1103 | | } |
1104 | | |
1105 | | #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] |
1106 | | struct SuffixCacheEntry { |
1107 | | key: SuffixCacheKey, |
1108 | | pc: InstPtr, |
1109 | | } |
1110 | | |
1111 | | #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] |
1112 | | struct SuffixCacheKey { |
1113 | | from_inst: InstPtr, |
1114 | | start: u8, |
1115 | | end: u8, |
1116 | | } |
1117 | | |
1118 | | impl SuffixCache { |
1119 | 54 | fn new(size: usize) -> Self { |
1120 | 54 | SuffixCache { |
1121 | 54 | sparse: vec![0usize; size].into(), |
1122 | 54 | dense: Vec::with_capacity(size), |
1123 | 54 | } |
1124 | 54 | } <regex::compile::SuffixCache>::new Line | Count | Source | 1119 | 27 | fn new(size: usize) -> Self { | 1120 | 27 | SuffixCache { | 1121 | 27 | sparse: vec![0usize; size].into(), | 1122 | 27 | dense: Vec::with_capacity(size), | 1123 | 27 | } | 1124 | 27 | } |
<regex::compile::SuffixCache>::new Line | Count | Source | 1119 | 27 | fn new(size: usize) -> Self { | 1120 | 27 | SuffixCache { | 1121 | 27 | sparse: vec![0usize; size].into(), | 1122 | 27 | dense: Vec::with_capacity(size), | 1123 | 27 | } | 1124 | 27 | } |
|
1125 | | |
1126 | 1.06k | fn get(&mut self, key: SuffixCacheKey, pc: InstPtr) -> Option<InstPtr> { |
1127 | 1.06k | let hash = self.hash(&key); |
1128 | 1.06k | let pos = &mut self.sparse[hash]; |
1129 | 1.06k | if let Some(entry) = self.dense.get(*pos) { |
1130 | 396 | if entry.key == key { |
1131 | 66 | return Some(entry.pc); |
1132 | 330 | } |
1133 | 672 | } |
1134 | 1.00k | *pos = self.dense.len(); |
1135 | 1.00k | self.dense.push(SuffixCacheEntry { key: key, pc: pc }); |
1136 | 1.00k | None |
1137 | 1.06k | } <regex::compile::SuffixCache>::get Line | Count | Source | 1126 | 534 | fn get(&mut self, key: SuffixCacheKey, pc: InstPtr) -> Option<InstPtr> { | 1127 | 534 | let hash = self.hash(&key); | 1128 | 534 | let pos = &mut self.sparse[hash]; | 1129 | 534 | if let Some(entry) = self.dense.get(*pos) { | 1130 | 198 | if entry.key == key { | 1131 | 33 | return Some(entry.pc); | 1132 | 165 | } | 1133 | 336 | } | 1134 | 501 | *pos = self.dense.len(); | 1135 | 501 | self.dense.push(SuffixCacheEntry { key: key, pc: pc }); | 1136 | 501 | None | 1137 | 534 | } |
<regex::compile::SuffixCache>::get Line | Count | Source | 1126 | 534 | fn get(&mut self, key: SuffixCacheKey, pc: InstPtr) -> Option<InstPtr> { | 1127 | 534 | let hash = self.hash(&key); | 1128 | 534 | let pos = &mut self.sparse[hash]; | 1129 | 534 | if let Some(entry) = self.dense.get(*pos) { | 1130 | 198 | if entry.key == key { | 1131 | 33 | return Some(entry.pc); | 1132 | 165 | } | 1133 | 336 | } | 1134 | 501 | *pos = self.dense.len(); | 1135 | 501 | self.dense.push(SuffixCacheEntry { key: key, pc: pc }); | 1136 | 501 | None | 1137 | 534 | } |
|
1138 | | |
1139 | 420 | fn clear(&mut self) { |
1140 | 420 | self.dense.clear(); |
1141 | 420 | } <regex::compile::SuffixCache>::clear Line | Count | Source | 1139 | 210 | fn clear(&mut self) { | 1140 | 210 | self.dense.clear(); | 1141 | 210 | } |
<regex::compile::SuffixCache>::clear Line | Count | Source | 1139 | 210 | fn clear(&mut self) { | 1140 | 210 | self.dense.clear(); | 1141 | 210 | } |
|
1142 | | |
1143 | 1.06k | fn hash(&self, suffix: &SuffixCacheKey) -> usize { |
1144 | | // Basic FNV-1a hash as described: |
1145 | | // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function |
1146 | | const FNV_PRIME: u64 = 1099511628211; |
1147 | 1.06k | let mut h = 14695981039346656037; |
1148 | 1.06k | h = (h ^ (suffix.from_inst as u64)).wrapping_mul(FNV_PRIME); |
1149 | 1.06k | h = (h ^ (suffix.start as u64)).wrapping_mul(FNV_PRIME); |
1150 | 1.06k | h = (h ^ (suffix.end as u64)).wrapping_mul(FNV_PRIME); |
1151 | 1.06k | (h as usize) % self.sparse.len() |
1152 | 1.06k | } <regex::compile::SuffixCache>::hash Line | Count | Source | 1143 | 534 | fn hash(&self, suffix: &SuffixCacheKey) -> usize { | 1144 | | // Basic FNV-1a hash as described: | 1145 | | // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function | 1146 | | const FNV_PRIME: u64 = 1099511628211; | 1147 | 534 | let mut h = 14695981039346656037; | 1148 | 534 | h = (h ^ (suffix.from_inst as u64)).wrapping_mul(FNV_PRIME); | 1149 | 534 | h = (h ^ (suffix.start as u64)).wrapping_mul(FNV_PRIME); | 1150 | 534 | h = (h ^ (suffix.end as u64)).wrapping_mul(FNV_PRIME); | 1151 | 534 | (h as usize) % self.sparse.len() | 1152 | 534 | } |
<regex::compile::SuffixCache>::hash Line | Count | Source | 1143 | 534 | fn hash(&self, suffix: &SuffixCacheKey) -> usize { | 1144 | | // Basic FNV-1a hash as described: | 1145 | | // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function | 1146 | | const FNV_PRIME: u64 = 1099511628211; | 1147 | 534 | let mut h = 14695981039346656037; | 1148 | 534 | h = (h ^ (suffix.from_inst as u64)).wrapping_mul(FNV_PRIME); | 1149 | 534 | h = (h ^ (suffix.start as u64)).wrapping_mul(FNV_PRIME); | 1150 | 534 | h = (h ^ (suffix.end as u64)).wrapping_mul(FNV_PRIME); | 1151 | 534 | (h as usize) % self.sparse.len() | 1152 | 534 | } |
|
1153 | | } |
1154 | | |
1155 | | struct ByteClassSet([bool; 256]); |
1156 | | |
1157 | | impl ByteClassSet { |
1158 | 54 | fn new() -> Self { |
1159 | 54 | ByteClassSet([false; 256]) |
1160 | 54 | } <regex::compile::ByteClassSet>::new Line | Count | Source | 1158 | 27 | fn new() -> Self { | 1159 | 27 | ByteClassSet([false; 256]) | 1160 | 27 | } |
<regex::compile::ByteClassSet>::new Line | Count | Source | 1158 | 27 | fn new() -> Self { | 1159 | 27 | ByteClassSet([false; 256]) | 1160 | 27 | } |
|
1161 | | |
1162 | 2.11k | fn set_range(&mut self, start: u8, end: u8) { |
1163 | 2.11k | debug_assert!(start <= end); |
1164 | 2.11k | if start > 0 { |
1165 | 2.10k | self.0[start as usize - 1] = true; |
1166 | 2.10k | } |
1167 | 2.11k | self.0[end as usize] = true; |
1168 | 2.11k | } <regex::compile::ByteClassSet>::set_range Line | Count | Source | 1162 | 1.05k | fn set_range(&mut self, start: u8, end: u8) { | 1163 | 1.05k | debug_assert!(start <= end); | 1164 | 1.05k | if start > 0 { | 1165 | 1.05k | self.0[start as usize - 1] = true; | 1166 | 1.05k | } | 1167 | 1.05k | self.0[end as usize] = true; | 1168 | 1.05k | } |
<regex::compile::ByteClassSet>::set_range Line | Count | Source | 1162 | 1.05k | fn set_range(&mut self, start: u8, end: u8) { | 1163 | 1.05k | debug_assert!(start <= end); | 1164 | 1.05k | if start > 0 { | 1165 | 1.05k | self.0[start as usize - 1] = true; | 1166 | 1.05k | } | 1167 | 1.05k | self.0[end as usize] = true; | 1168 | 1.05k | } |
|
1169 | | |
1170 | 0 | fn set_word_boundary(&mut self) { |
1171 | | // We need to mark all ranges of bytes whose pairs result in |
1172 | | // evaluating \b differently. |
1173 | 0 | let iswb = is_word_byte; |
1174 | 0 | let mut b1: u16 = 0; |
1175 | | let mut b2: u16; |
1176 | 0 | while b1 <= 255 { |
1177 | 0 | b2 = b1 + 1; |
1178 | 0 | while b2 <= 255 && iswb(b1 as u8) == iswb(b2 as u8) { |
1179 | 0 | b2 += 1; |
1180 | 0 | } |
1181 | 0 | self.set_range(b1 as u8, (b2 - 1) as u8); |
1182 | 0 | b1 = b2; |
1183 | | } |
1184 | 0 | } Unexecuted instantiation: <regex::compile::ByteClassSet>::set_word_boundary Unexecuted instantiation: <regex::compile::ByteClassSet>::set_word_boundary |
1185 | | |
1186 | 54 | fn byte_classes(&self) -> Vec<u8> { |
1187 | | // N.B. If you're debugging the DFA, it's useful to simply return |
1188 | | // `(0..256).collect()`, which effectively removes the byte classes |
1189 | | // and makes the transitions easier to read. |
1190 | | // (0usize..256).map(|x| x as u8).collect() |
1191 | 54 | let mut byte_classes = vec![0; 256]; |
1192 | 54 | let mut class = 0u8; |
1193 | 54 | let mut i = 0; |
1194 | | loop { |
1195 | 13.8k | byte_classes[i] = class as u8; |
1196 | 13.8k | if i >= 255 { |
1197 | 54 | break; |
1198 | 13.7k | } |
1199 | 13.7k | if self.0[i] { |
1200 | 1.18k | class = class.checked_add(1).unwrap(); |
1201 | 12.5k | } |
1202 | 13.7k | i += 1; |
1203 | | } |
1204 | 54 | byte_classes |
1205 | 54 | } <regex::compile::ByteClassSet>::byte_classes Line | Count | Source | 1186 | 27 | fn byte_classes(&self) -> Vec<u8> { | 1187 | | // N.B. If you're debugging the DFA, it's useful to simply return | 1188 | | // `(0..256).collect()`, which effectively removes the byte classes | 1189 | | // and makes the transitions easier to read. | 1190 | | // (0usize..256).map(|x| x as u8).collect() | 1191 | 27 | let mut byte_classes = vec![0; 256]; | 1192 | 27 | let mut class = 0u8; | 1193 | 27 | let mut i = 0; | 1194 | | loop { | 1195 | 6.91k | byte_classes[i] = class as u8; | 1196 | 6.91k | if i >= 255 { | 1197 | 27 | break; | 1198 | 6.88k | } | 1199 | 6.88k | if self.0[i] { | 1200 | 594 | class = class.checked_add(1).unwrap(); | 1201 | 6.29k | } | 1202 | 6.88k | i += 1; | 1203 | | } | 1204 | 27 | byte_classes | 1205 | 27 | } |
<regex::compile::ByteClassSet>::byte_classes Line | Count | Source | 1186 | 27 | fn byte_classes(&self) -> Vec<u8> { | 1187 | | // N.B. If you're debugging the DFA, it's useful to simply return | 1188 | | // `(0..256).collect()`, which effectively removes the byte classes | 1189 | | // and makes the transitions easier to read. | 1190 | | // (0usize..256).map(|x| x as u8).collect() | 1191 | 27 | let mut byte_classes = vec![0; 256]; | 1192 | 27 | let mut class = 0u8; | 1193 | 27 | let mut i = 0; | 1194 | | loop { | 1195 | 6.91k | byte_classes[i] = class as u8; | 1196 | 6.91k | if i >= 255 { | 1197 | 27 | break; | 1198 | 6.88k | } | 1199 | 6.88k | if self.0[i] { | 1200 | 594 | class = class.checked_add(1).unwrap(); | 1201 | 6.29k | } | 1202 | 6.88k | i += 1; | 1203 | | } | 1204 | 27 | byte_classes | 1205 | 27 | } |
|
1206 | | } |
1207 | | |
1208 | | impl fmt::Debug for ByteClassSet { |
1209 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1210 | 0 | f.debug_tuple("ByteClassSet").field(&&self.0[..]).finish() |
1211 | 0 | } Unexecuted instantiation: <regex::compile::ByteClassSet as core::fmt::Debug>::fmt Unexecuted instantiation: <regex::compile::ByteClassSet as core::fmt::Debug>::fmt |
1212 | | } |
1213 | | |
1214 | 0 | fn u32_to_usize(n: u32) -> usize { |
1215 | | // In case usize is less than 32 bits, we need to guard against overflow. |
1216 | | // On most platforms this compiles to nothing. |
1217 | | // TODO Use `std::convert::TryFrom` once it's stable. |
1218 | 0 | if (n as u64) > (::std::usize::MAX as u64) { |
1219 | 0 | panic!("BUG: {} is too big to be pointer sized", n) |
1220 | 0 | } |
1221 | 0 | n as usize |
1222 | 0 | } Unexecuted instantiation: regex::compile::u32_to_usize Unexecuted instantiation: regex::compile::u32_to_usize |
1223 | | |
1224 | | #[cfg(test)] |
1225 | | mod tests { |
1226 | | use super::ByteClassSet; |
1227 | | |
1228 | | #[test] |
1229 | | fn byte_classes() { |
1230 | | let mut set = ByteClassSet::new(); |
1231 | | set.set_range(b'a', b'z'); |
1232 | | let classes = set.byte_classes(); |
1233 | | assert_eq!(classes[0], 0); |
1234 | | assert_eq!(classes[1], 0); |
1235 | | assert_eq!(classes[2], 0); |
1236 | | assert_eq!(classes[b'a' as usize - 1], 0); |
1237 | | assert_eq!(classes[b'a' as usize], 1); |
1238 | | assert_eq!(classes[b'm' as usize], 1); |
1239 | | assert_eq!(classes[b'z' as usize], 1); |
1240 | | assert_eq!(classes[b'z' as usize + 1], 2); |
1241 | | assert_eq!(classes[254], 2); |
1242 | | assert_eq!(classes[255], 2); |
1243 | | |
1244 | | let mut set = ByteClassSet::new(); |
1245 | | set.set_range(0, 2); |
1246 | | set.set_range(4, 6); |
1247 | | let classes = set.byte_classes(); |
1248 | | assert_eq!(classes[0], 0); |
1249 | | assert_eq!(classes[1], 0); |
1250 | | assert_eq!(classes[2], 0); |
1251 | | assert_eq!(classes[3], 1); |
1252 | | assert_eq!(classes[4], 2); |
1253 | | assert_eq!(classes[5], 2); |
1254 | | assert_eq!(classes[6], 2); |
1255 | | assert_eq!(classes[7], 3); |
1256 | | assert_eq!(classes[255], 3); |
1257 | | } |
1258 | | |
1259 | | #[test] |
1260 | | fn full_byte_classes() { |
1261 | | let mut set = ByteClassSet::new(); |
1262 | | for i in 0..256u16 { |
1263 | | set.set_range(i as u8, i as u8); |
1264 | | } |
1265 | | assert_eq!(set.byte_classes().len(), 256); |
1266 | | } |
1267 | | } |