/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regex-1.5.6/src/prog.rs
Line | Count | Source |
1 | | use std::cmp::Ordering; |
2 | | use std::collections::HashMap; |
3 | | use std::fmt; |
4 | | use std::mem; |
5 | | use std::ops::Deref; |
6 | | use std::slice; |
7 | | use std::sync::Arc; |
8 | | |
9 | | use crate::input::Char; |
10 | | use crate::literal::LiteralSearcher; |
11 | | |
12 | | /// `InstPtr` represents the index of an instruction in a regex program. |
13 | | pub type InstPtr = usize; |
14 | | |
15 | | /// Program is a sequence of instructions and various facts about thos |
16 | | /// instructions. |
17 | | #[derive(Clone)] |
18 | | pub struct Program { |
19 | | /// A sequence of instructions that represents an NFA. |
20 | | pub insts: Vec<Inst>, |
21 | | /// Pointers to each Match instruction in the sequence. |
22 | | /// |
23 | | /// This is always length 1 unless this program represents a regex set. |
24 | | pub matches: Vec<InstPtr>, |
25 | | /// The ordered sequence of all capture groups extracted from the AST. |
26 | | /// Unnamed groups are `None`. |
27 | | pub captures: Vec<Option<String>>, |
28 | | /// Pointers to all named capture groups into `captures`. |
29 | | pub capture_name_idx: Arc<HashMap<String, usize>>, |
30 | | /// A pointer to the start instruction. This can vary depending on how |
31 | | /// the program was compiled. For example, programs for use with the DFA |
32 | | /// engine have a `.*?` inserted at the beginning of unanchored regular |
33 | | /// expressions. The actual starting point of the program is after the |
34 | | /// `.*?`. |
35 | | pub start: InstPtr, |
36 | | /// A set of equivalence classes for discriminating bytes in the compiled |
37 | | /// program. |
38 | | pub byte_classes: Vec<u8>, |
39 | | /// When true, this program can only match valid UTF-8. |
40 | | pub only_utf8: bool, |
41 | | /// When true, this program uses byte range instructions instead of Unicode |
42 | | /// range instructions. |
43 | | pub is_bytes: bool, |
44 | | /// When true, the program is compiled for DFA matching. For example, this |
45 | | /// implies `is_bytes` and also inserts a preceding `.*?` for unanchored |
46 | | /// regexes. |
47 | | pub is_dfa: bool, |
48 | | /// When true, the program matches text in reverse (for use only in the |
49 | | /// DFA). |
50 | | pub is_reverse: bool, |
51 | | /// Whether the regex must match from the start of the input. |
52 | | pub is_anchored_start: bool, |
53 | | /// Whether the regex must match at the end of the input. |
54 | | pub is_anchored_end: bool, |
55 | | /// Whether this program contains a Unicode word boundary instruction. |
56 | | pub has_unicode_word_boundary: bool, |
57 | | /// A possibly empty machine for very quickly matching prefix literals. |
58 | | pub prefixes: LiteralSearcher, |
59 | | /// A limit on the size of the cache that the DFA is allowed to use while |
60 | | /// matching. |
61 | | /// |
62 | | /// The cache limit specifies approximately how much space we're willing to |
63 | | /// give to the state cache. Once the state cache exceeds the size, it is |
64 | | /// wiped and all states must be re-computed. |
65 | | /// |
66 | | /// Note that this value does not impact correctness. It can be set to 0 |
67 | | /// and the DFA will run just fine. (It will only ever store exactly one |
68 | | /// state in the cache, and will likely run very slowly, but it will work.) |
69 | | /// |
70 | | /// Also note that this limit is *per thread of execution*. That is, |
71 | | /// if the same regex is used to search text across multiple threads |
72 | | /// simultaneously, then the DFA cache is not shared. Instead, copies are |
73 | | /// made. |
74 | | pub dfa_size_limit: usize, |
75 | | } |
76 | | |
77 | | impl Program { |
78 | | /// Creates an empty instruction sequence. Fields are given default |
79 | | /// values. |
80 | 63 | pub fn new() -> Self { |
81 | 63 | Program { |
82 | 63 | insts: vec![], |
83 | 63 | matches: vec![], |
84 | 63 | captures: vec![], |
85 | 63 | capture_name_idx: Arc::new(HashMap::new()), |
86 | 63 | start: 0, |
87 | 63 | byte_classes: vec![0; 256], |
88 | 63 | only_utf8: true, |
89 | 63 | is_bytes: false, |
90 | 63 | is_dfa: false, |
91 | 63 | is_reverse: false, |
92 | 63 | is_anchored_start: false, |
93 | 63 | is_anchored_end: false, |
94 | 63 | has_unicode_word_boundary: false, |
95 | 63 | prefixes: LiteralSearcher::empty(), |
96 | 63 | dfa_size_limit: 2 * (1 << 20), |
97 | 63 | } |
98 | 63 | } <regex::prog::Program>::new Line | Count | Source | 80 | 27 | pub fn new() -> Self { | 81 | 27 | Program { | 82 | 27 | insts: vec![], | 83 | 27 | matches: vec![], | 84 | 27 | captures: vec![], | 85 | 27 | capture_name_idx: Arc::new(HashMap::new()), | 86 | 27 | start: 0, | 87 | 27 | byte_classes: vec![0; 256], | 88 | 27 | only_utf8: true, | 89 | 27 | is_bytes: false, | 90 | 27 | is_dfa: false, | 91 | 27 | is_reverse: false, | 92 | 27 | is_anchored_start: false, | 93 | 27 | is_anchored_end: false, | 94 | 27 | has_unicode_word_boundary: false, | 95 | 27 | prefixes: LiteralSearcher::empty(), | 96 | 27 | dfa_size_limit: 2 * (1 << 20), | 97 | 27 | } | 98 | 27 | } |
<regex::prog::Program>::new Line | Count | Source | 80 | 36 | pub fn new() -> Self { | 81 | 36 | Program { | 82 | 36 | insts: vec![], | 83 | 36 | matches: vec![], | 84 | 36 | captures: vec![], | 85 | 36 | capture_name_idx: Arc::new(HashMap::new()), | 86 | 36 | start: 0, | 87 | 36 | byte_classes: vec![0; 256], | 88 | 36 | only_utf8: true, | 89 | 36 | is_bytes: false, | 90 | 36 | is_dfa: false, | 91 | 36 | is_reverse: false, | 92 | 36 | is_anchored_start: false, | 93 | 36 | is_anchored_end: false, | 94 | 36 | has_unicode_word_boundary: false, | 95 | 36 | prefixes: LiteralSearcher::empty(), | 96 | 36 | dfa_size_limit: 2 * (1 << 20), | 97 | 36 | } | 98 | 36 | } |
|
99 | | |
100 | | /// If pc is an index to a no-op instruction (like Save), then return the |
101 | | /// next pc that is not a no-op instruction. |
102 | 0 | pub fn skip(&self, mut pc: usize) -> usize { |
103 | | loop { |
104 | 0 | match self[pc] { |
105 | 0 | Inst::Save(ref i) => pc = i.goto, |
106 | 0 | _ => return pc, |
107 | | } |
108 | | } |
109 | 0 | } Unexecuted instantiation: <regex::prog::Program>::skip Unexecuted instantiation: <regex::prog::Program>::skip |
110 | | |
111 | | /// Return true if and only if an execution engine at instruction `pc` will |
112 | | /// always lead to a match. |
113 | 0 | pub fn leads_to_match(&self, pc: usize) -> bool { |
114 | 0 | if self.matches.len() > 1 { |
115 | | // If we have a regex set, then we have more than one ending |
116 | | // state, so leading to one of those states is generally |
117 | | // meaningless. |
118 | 0 | return false; |
119 | 0 | } |
120 | 0 | match self[self.skip(pc)] { |
121 | 0 | Inst::Match(_) => true, |
122 | 0 | _ => false, |
123 | | } |
124 | 0 | } Unexecuted instantiation: <regex::prog::Program>::leads_to_match Unexecuted instantiation: <regex::prog::Program>::leads_to_match |
125 | | |
126 | | /// Returns true if the current configuration demands that an implicit |
127 | | /// `.*?` be prepended to the instruction sequence. |
128 | 126 | pub fn needs_dotstar(&self) -> bool { |
129 | 126 | self.is_dfa && !self.is_reverse && !self.is_anchored_start |
130 | 126 | } <regex::prog::Program>::needs_dotstar Line | Count | Source | 128 | 54 | pub fn needs_dotstar(&self) -> bool { | 129 | 54 | self.is_dfa && !self.is_reverse && !self.is_anchored_start | 130 | 54 | } |
<regex::prog::Program>::needs_dotstar Line | Count | Source | 128 | 72 | pub fn needs_dotstar(&self) -> bool { | 129 | 72 | self.is_dfa && !self.is_reverse && !self.is_anchored_start | 130 | 72 | } |
|
131 | | |
132 | | /// Returns true if this program uses Byte instructions instead of |
133 | | /// Char/Range instructions. |
134 | 72.4k | pub fn uses_bytes(&self) -> bool { |
135 | 72.4k | self.is_bytes || self.is_dfa |
136 | 72.4k | } <regex::prog::Program>::uses_bytes Line | Count | Source | 134 | 10.5k | pub fn uses_bytes(&self) -> bool { | 135 | 10.5k | self.is_bytes || self.is_dfa | 136 | 10.5k | } |
<regex::prog::Program>::uses_bytes Line | Count | Source | 134 | 61.9k | pub fn uses_bytes(&self) -> bool { | 135 | 61.9k | self.is_bytes || self.is_dfa | 136 | 61.9k | } |
|
137 | | |
138 | | /// Returns true if this program exclusively matches valid UTF-8 bytes. |
139 | | /// |
140 | | /// That is, if an invalid UTF-8 byte is seen, then no match is possible. |
141 | 0 | pub fn only_utf8(&self) -> bool { |
142 | 0 | self.only_utf8 |
143 | 0 | } Unexecuted instantiation: <regex::prog::Program>::only_utf8 Unexecuted instantiation: <regex::prog::Program>::only_utf8 |
144 | | |
145 | | /// Return the approximate heap usage of this instruction sequence in |
146 | | /// bytes. |
147 | 0 | pub fn approximate_size(&self) -> usize { |
148 | | // The only instruction that uses heap space is Ranges (for |
149 | | // Unicode codepoint programs) to store non-overlapping codepoint |
150 | | // ranges. To keep this operation constant time, we ignore them. |
151 | 0 | (self.len() * mem::size_of::<Inst>()) |
152 | 0 | + (self.matches.len() * mem::size_of::<InstPtr>()) |
153 | 0 | + (self.captures.len() * mem::size_of::<Option<String>>()) |
154 | 0 | + (self.capture_name_idx.len() |
155 | 0 | * (mem::size_of::<String>() + mem::size_of::<usize>())) |
156 | 0 | + (self.byte_classes.len() * mem::size_of::<u8>()) |
157 | 0 | + self.prefixes.approximate_size() |
158 | 0 | } Unexecuted instantiation: <regex::prog::Program>::approximate_size Unexecuted instantiation: <regex::prog::Program>::approximate_size |
159 | | } |
160 | | |
161 | | impl Deref for Program { |
162 | | type Target = [Inst]; |
163 | | |
164 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
165 | 3.02M | fn deref(&self) -> &Self::Target { |
166 | 3.02M | &*self.insts |
167 | 3.02M | } <regex::prog::Program as core::ops::deref::Deref>::deref Line | Count | Source | 165 | 471k | fn deref(&self) -> &Self::Target { | 166 | 471k | &*self.insts | 167 | 471k | } |
<regex::prog::Program as core::ops::deref::Deref>::deref Line | Count | Source | 165 | 2.55M | fn deref(&self) -> &Self::Target { | 166 | 2.55M | &*self.insts | 167 | 2.55M | } |
|
168 | | } |
169 | | |
170 | | impl fmt::Debug for Program { |
171 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
172 | | use self::Inst::*; |
173 | | |
174 | 0 | fn with_goto(cur: usize, goto: usize, fmtd: String) -> String { |
175 | 0 | if goto == cur + 1 { |
176 | 0 | fmtd |
177 | | } else { |
178 | 0 | format!("{} (goto: {})", fmtd, goto) |
179 | | } |
180 | 0 | } Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt::with_goto Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt::with_goto |
181 | | |
182 | 0 | fn visible_byte(b: u8) -> String { |
183 | | use std::ascii::escape_default; |
184 | 0 | let escaped = escape_default(b).collect::<Vec<u8>>(); |
185 | 0 | String::from_utf8_lossy(&escaped).into_owned() |
186 | 0 | } Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt::visible_byte Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt::visible_byte |
187 | | |
188 | 0 | for (pc, inst) in self.iter().enumerate() { |
189 | 0 | match *inst { |
190 | 0 | Match(slot) => write!(f, "{:04} Match({:?})", pc, slot)?, |
191 | 0 | Save(ref inst) => { |
192 | 0 | let s = format!("{:04} Save({})", pc, inst.slot); |
193 | 0 | write!(f, "{}", with_goto(pc, inst.goto, s))?; |
194 | | } |
195 | 0 | Split(ref inst) => { |
196 | 0 | write!( |
197 | 0 | f, |
198 | 0 | "{:04} Split({}, {})", |
199 | | pc, inst.goto1, inst.goto2 |
200 | 0 | )?; |
201 | | } |
202 | 0 | EmptyLook(ref inst) => { |
203 | 0 | let s = format!("{:?}", inst.look); |
204 | 0 | write!(f, "{:04} {}", pc, with_goto(pc, inst.goto, s))?; |
205 | | } |
206 | 0 | Char(ref inst) => { |
207 | 0 | let s = format!("{:?}", inst.c); |
208 | 0 | write!(f, "{:04} {}", pc, with_goto(pc, inst.goto, s))?; |
209 | | } |
210 | 0 | Ranges(ref inst) => { |
211 | 0 | let ranges = inst |
212 | 0 | .ranges |
213 | 0 | .iter() |
214 | 0 | .map(|r| format!("{:?}-{:?}", r.0, r.1))Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt::{closure#0}Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt::{closure#0} |
215 | 0 | .collect::<Vec<String>>() |
216 | 0 | .join(", "); |
217 | 0 | write!( |
218 | 0 | f, |
219 | 0 | "{:04} {}", |
220 | | pc, |
221 | 0 | with_goto(pc, inst.goto, ranges) |
222 | 0 | )?; |
223 | | } |
224 | 0 | Bytes(ref inst) => { |
225 | 0 | let s = format!( |
226 | 0 | "Bytes({}, {})", |
227 | 0 | visible_byte(inst.start), |
228 | 0 | visible_byte(inst.end) |
229 | | ); |
230 | 0 | write!(f, "{:04} {}", pc, with_goto(pc, inst.goto, s))?; |
231 | | } |
232 | | } |
233 | 0 | if pc == self.start { |
234 | 0 | write!(f, " (start)")?; |
235 | 0 | } |
236 | 0 | write!(f, "\n")?; |
237 | | } |
238 | 0 | Ok(()) |
239 | 0 | } Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt Unexecuted instantiation: <regex::prog::Program as core::fmt::Debug>::fmt |
240 | | } |
241 | | |
242 | | impl<'a> IntoIterator for &'a Program { |
243 | | type Item = &'a Inst; |
244 | | type IntoIter = slice::Iter<'a, Inst>; |
245 | 21 | fn into_iter(self) -> Self::IntoIter { |
246 | 21 | self.iter() |
247 | 21 | } <®ex::prog::Program as core::iter::traits::collect::IntoIterator>::into_iter Line | Count | Source | 245 | 9 | fn into_iter(self) -> Self::IntoIter { | 246 | 9 | self.iter() | 247 | 9 | } |
<®ex::prog::Program as core::iter::traits::collect::IntoIterator>::into_iter Line | Count | Source | 245 | 12 | fn into_iter(self) -> Self::IntoIter { | 246 | 12 | self.iter() | 247 | 12 | } |
|
248 | | } |
249 | | |
250 | | /// Inst is an instruction code in a Regex program. |
251 | | /// |
252 | | /// Regrettably, a regex program either contains Unicode codepoint |
253 | | /// instructions (Char and Ranges) or it contains byte instructions (Bytes). |
254 | | /// A regex program can never contain both. |
255 | | /// |
256 | | /// It would be worth investigating splitting this into two distinct types and |
257 | | /// then figuring out how to make the matching engines polymorphic over those |
258 | | /// types without sacrificing performance. |
259 | | /// |
260 | | /// Other than the benefit of moving invariants into the type system, another |
261 | | /// benefit is the decreased size. If we remove the `Char` and `Ranges` |
262 | | /// instructions from the `Inst` enum, then its size shrinks from 32 bytes to |
263 | | /// 24 bytes. (This is because of the removal of a `Box<[]>` in the `Ranges` |
264 | | /// variant.) Given that byte based machines are typically much bigger than |
265 | | /// their Unicode analogues (because they can decode UTF-8 directly), this ends |
266 | | /// up being a pretty significant savings. |
267 | | #[derive(Clone, Debug)] |
268 | | pub enum Inst { |
269 | | /// Match indicates that the program has reached a match state. |
270 | | /// |
271 | | /// The number in the match corresponds to the Nth logical regular |
272 | | /// expression in this program. This index is always 0 for normal regex |
273 | | /// programs. Values greater than 0 appear when compiling regex sets, and |
274 | | /// each match instruction gets its own unique value. The value corresponds |
275 | | /// to the Nth regex in the set. |
276 | | Match(usize), |
277 | | /// Save causes the program to save the current location of the input in |
278 | | /// the slot indicated by InstSave. |
279 | | Save(InstSave), |
280 | | /// Split causes the program to diverge to one of two paths in the |
281 | | /// program, preferring goto1 in InstSplit. |
282 | | Split(InstSplit), |
283 | | /// EmptyLook represents a zero-width assertion in a regex program. A |
284 | | /// zero-width assertion does not consume any of the input text. |
285 | | EmptyLook(InstEmptyLook), |
286 | | /// Char requires the regex program to match the character in InstChar at |
287 | | /// the current position in the input. |
288 | | Char(InstChar), |
289 | | /// Ranges requires the regex program to match the character at the current |
290 | | /// position in the input with one of the ranges specified in InstRanges. |
291 | | Ranges(InstRanges), |
292 | | /// Bytes is like Ranges, except it expresses a single byte range. It is |
293 | | /// used in conjunction with Split instructions to implement multi-byte |
294 | | /// character classes. |
295 | | Bytes(InstBytes), |
296 | | } |
297 | | |
298 | | impl Inst { |
299 | | /// Returns true if and only if this is a match instruction. |
300 | 0 | pub fn is_match(&self) -> bool { |
301 | 0 | match *self { |
302 | 0 | Inst::Match(_) => true, |
303 | 0 | _ => false, |
304 | | } |
305 | 0 | } Unexecuted instantiation: <regex::prog::Inst>::is_match Unexecuted instantiation: <regex::prog::Inst>::is_match |
306 | | } |
307 | | |
308 | | /// Representation of the Save instruction. |
309 | | #[derive(Clone, Debug)] |
310 | | pub struct InstSave { |
311 | | /// The next location to execute in the program. |
312 | | pub goto: InstPtr, |
313 | | /// The capture slot (there are two slots for every capture in a regex, |
314 | | /// including the zeroth capture for the entire match). |
315 | | pub slot: usize, |
316 | | } |
317 | | |
318 | | /// Representation of the Split instruction. |
319 | | #[derive(Clone, Debug)] |
320 | | pub struct InstSplit { |
321 | | /// The first instruction to try. A match resulting from following goto1 |
322 | | /// has precedence over a match resulting from following goto2. |
323 | | pub goto1: InstPtr, |
324 | | /// The second instruction to try. A match resulting from following goto1 |
325 | | /// has precedence over a match resulting from following goto2. |
326 | | pub goto2: InstPtr, |
327 | | } |
328 | | |
329 | | /// Representation of the `EmptyLook` instruction. |
330 | | #[derive(Clone, Debug)] |
331 | | pub struct InstEmptyLook { |
332 | | /// The next location to execute in the program if this instruction |
333 | | /// succeeds. |
334 | | pub goto: InstPtr, |
335 | | /// The type of zero-width assertion to check. |
336 | | pub look: EmptyLook, |
337 | | } |
338 | | |
339 | | /// The set of zero-width match instructions. |
340 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
341 | | pub enum EmptyLook { |
342 | | /// Start of line or input. |
343 | | StartLine, |
344 | | /// End of line or input. |
345 | | EndLine, |
346 | | /// Start of input. |
347 | | StartText, |
348 | | /// End of input. |
349 | | EndText, |
350 | | /// Word character on one side and non-word character on other. |
351 | | WordBoundary, |
352 | | /// Word character on both sides or non-word character on both sides. |
353 | | NotWordBoundary, |
354 | | /// ASCII word boundary. |
355 | | WordBoundaryAscii, |
356 | | /// Not ASCII word boundary. |
357 | | NotWordBoundaryAscii, |
358 | | } |
359 | | |
360 | | /// Representation of the Char instruction. |
361 | | #[derive(Clone, Debug)] |
362 | | pub struct InstChar { |
363 | | /// The next location to execute in the program if this instruction |
364 | | /// succeeds. |
365 | | pub goto: InstPtr, |
366 | | /// The character to test. |
367 | | pub c: char, |
368 | | } |
369 | | |
370 | | /// Representation of the Ranges instruction. |
371 | | #[derive(Clone, Debug)] |
372 | | pub struct InstRanges { |
373 | | /// The next location to execute in the program if this instruction |
374 | | /// succeeds. |
375 | | pub goto: InstPtr, |
376 | | /// The set of Unicode scalar value ranges to test. |
377 | | pub ranges: Box<[(char, char)]>, |
378 | | } |
379 | | |
380 | | impl InstRanges { |
381 | | /// Tests whether the given input character matches this instruction. |
382 | 803k | pub fn matches(&self, c: Char) -> bool { |
383 | | // This speeds up the `match_class_unicode` benchmark by checking |
384 | | // some common cases quickly without binary search. e.g., Matching |
385 | | // a Unicode class on predominantly ASCII text. |
386 | 1.50M | for r in self.ranges.iter().take(4) { |
387 | 1.50M | if c < r.0 { |
388 | 12.9k | return false; |
389 | 1.49M | } |
390 | 1.49M | if c <= r.1 { |
391 | 428k | return true; |
392 | 1.06M | } |
393 | | } |
394 | 362k | self.ranges |
395 | 715k | .binary_search_by(|r| { |
396 | 715k | if r.1 < c { |
397 | 715k | Ordering::Less |
398 | 0 | } else if r.0 > c { |
399 | 0 | Ordering::Greater |
400 | | } else { |
401 | 0 | Ordering::Equal |
402 | | } |
403 | 715k | }) <regex::prog::InstRanges>::matches::{closure#0}Line | Count | Source | 395 | 75.8k | .binary_search_by(|r| { | 396 | 75.8k | if r.1 < c { | 397 | 75.8k | Ordering::Less | 398 | 0 | } else if r.0 > c { | 399 | 0 | Ordering::Greater | 400 | | } else { | 401 | 0 | Ordering::Equal | 402 | | } | 403 | 75.8k | }) |
<regex::prog::InstRanges>::matches::{closure#0}Line | Count | Source | 395 | 639k | .binary_search_by(|r| { | 396 | 639k | if r.1 < c { | 397 | 639k | Ordering::Less | 398 | 0 | } else if r.0 > c { | 399 | 0 | Ordering::Greater | 400 | | } else { | 401 | 0 | Ordering::Equal | 402 | | } | 403 | 639k | }) |
|
404 | 362k | .is_ok() |
405 | 803k | } <regex::prog::InstRanges>::matches Line | Count | Source | 382 | 125k | pub fn matches(&self, c: Char) -> bool { | 383 | | // This speeds up the `match_class_unicode` benchmark by checking | 384 | | // some common cases quickly without binary search. e.g., Matching | 385 | | // a Unicode class on predominantly ASCII text. | 386 | 217k | for r in self.ranges.iter().take(4) { | 387 | 217k | if c < r.0 { | 388 | 1.70k | return false; | 389 | 215k | } | 390 | 215k | if c <= r.1 { | 391 | 85.6k | return true; | 392 | 129k | } | 393 | | } | 394 | 38.4k | self.ranges | 395 | 38.4k | .binary_search_by(|r| { | 396 | | if r.1 < c { | 397 | | Ordering::Less | 398 | | } else if r.0 > c { | 399 | | Ordering::Greater | 400 | | } else { | 401 | | Ordering::Equal | 402 | | } | 403 | | }) | 404 | 38.4k | .is_ok() | 405 | 125k | } |
<regex::prog::InstRanges>::matches Line | Count | Source | 382 | 678k | pub fn matches(&self, c: Char) -> bool { | 383 | | // This speeds up the `match_class_unicode` benchmark by checking | 384 | | // some common cases quickly without binary search. e.g., Matching | 385 | | // a Unicode class on predominantly ASCII text. | 386 | 1.28M | for r in self.ranges.iter().take(4) { | 387 | 1.28M | if c < r.0 { | 388 | 11.2k | return false; | 389 | 1.27M | } | 390 | 1.27M | if c <= r.1 { | 391 | 343k | return true; | 392 | 934k | } | 393 | | } | 394 | 323k | self.ranges | 395 | 323k | .binary_search_by(|r| { | 396 | | if r.1 < c { | 397 | | Ordering::Less | 398 | | } else if r.0 > c { | 399 | | Ordering::Greater | 400 | | } else { | 401 | | Ordering::Equal | 402 | | } | 403 | | }) | 404 | 323k | .is_ok() | 405 | 678k | } |
|
406 | | |
407 | | /// Return the number of distinct characters represented by all of the |
408 | | /// ranges. |
409 | 0 | pub fn num_chars(&self) -> usize { |
410 | 0 | self.ranges |
411 | 0 | .iter() |
412 | 0 | .map(|&(s, e)| 1 + (e as u32) - (s as u32)) Unexecuted instantiation: <regex::prog::InstRanges>::num_chars::{closure#0}Unexecuted instantiation: <regex::prog::InstRanges>::num_chars::{closure#0} |
413 | 0 | .sum::<u32>() as usize |
414 | 0 | } Unexecuted instantiation: <regex::prog::InstRanges>::num_chars Unexecuted instantiation: <regex::prog::InstRanges>::num_chars |
415 | | } |
416 | | |
417 | | /// Representation of the Bytes instruction. |
418 | | #[derive(Clone, Debug)] |
419 | | pub struct InstBytes { |
420 | | /// The next location to execute in the program if this instruction |
421 | | /// succeeds. |
422 | | pub goto: InstPtr, |
423 | | /// The start (inclusive) of this byte range. |
424 | | pub start: u8, |
425 | | /// The end (inclusive) of this byte range. |
426 | | pub end: u8, |
427 | | } |
428 | | |
429 | | impl InstBytes { |
430 | | /// Returns true if and only if the given byte is in this range. |
431 | 0 | pub fn matches(&self, byte: u8) -> bool { |
432 | 0 | self.start <= byte && byte <= self.end |
433 | 0 | } Unexecuted instantiation: <regex::prog::InstBytes>::matches Unexecuted instantiation: <regex::prog::InstBytes>::matches |
434 | | } |
435 | | |
436 | | #[cfg(test)] |
437 | | mod test { |
438 | | #[test] |
439 | | #[cfg(target_pointer_width = "64")] |
440 | | fn test_size_of_inst() { |
441 | | use std::mem::size_of; |
442 | | |
443 | | use super::Inst; |
444 | | |
445 | | assert_eq!(32, size_of::<Inst>()); |
446 | | } |
447 | | } |