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 input::Char; 10 use 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. new() -> Self80 pub fn new() -> Self { 81 Program { 82 insts: vec![], 83 matches: vec![], 84 captures: vec![], 85 capture_name_idx: Arc::new(HashMap::new()), 86 start: 0, 87 byte_classes: vec![0; 256], 88 only_utf8: true, 89 is_bytes: false, 90 is_dfa: false, 91 is_reverse: false, 92 is_anchored_start: false, 93 is_anchored_end: false, 94 has_unicode_word_boundary: false, 95 prefixes: LiteralSearcher::empty(), 96 dfa_size_limit: 2 * (1 << 20), 97 } 98 } 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. skip(&self, mut pc: usize) -> usize102 pub fn skip(&self, mut pc: usize) -> usize { 103 loop { 104 match self[pc] { 105 Inst::Save(ref i) => pc = i.goto, 106 _ => return pc, 107 } 108 } 109 } 110 111 /// Return true if and only if an execution engine at instruction `pc` will 112 /// always lead to a match. leads_to_match(&self, pc: usize) -> bool113 pub fn leads_to_match(&self, pc: usize) -> bool { 114 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 return false; 119 } 120 match self[self.skip(pc)] { 121 Inst::Match(_) => true, 122 _ => false, 123 } 124 } 125 126 /// Returns true if the current configuration demands that an implicit 127 /// `.*?` be prepended to the instruction sequence. needs_dotstar(&self) -> bool128 pub fn needs_dotstar(&self) -> bool { 129 self.is_dfa && !self.is_reverse && !self.is_anchored_start 130 } 131 132 /// Returns true if this program uses Byte instructions instead of 133 /// Char/Range instructions. uses_bytes(&self) -> bool134 pub fn uses_bytes(&self) -> bool { 135 self.is_bytes || self.is_dfa 136 } 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. only_utf8(&self) -> bool141 pub fn only_utf8(&self) -> bool { 142 self.only_utf8 143 } 144 145 /// Return the approximate heap usage of this instruction sequence in 146 /// bytes. approximate_size(&self) -> usize147 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 (self.len() * mem::size_of::<Inst>()) 152 + (self.matches.len() * mem::size_of::<InstPtr>()) 153 + (self.captures.len() * mem::size_of::<Option<String>>()) 154 + (self.capture_name_idx.len() 155 * (mem::size_of::<String>() + mem::size_of::<usize>())) 156 + (self.byte_classes.len() * mem::size_of::<u8>()) 157 + self.prefixes.approximate_size() 158 } 159 } 160 161 impl Deref for Program { 162 type Target = [Inst]; 163 164 #[cfg_attr(feature = "perf-inline", inline(always))] deref(&self) -> &Self::Target165 fn deref(&self) -> &Self::Target { 166 &*self.insts 167 } 168 } 169 170 impl fmt::Debug for Program { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 172 use self::Inst::*; 173 174 fn with_goto(cur: usize, goto: usize, fmtd: String) -> String { 175 if goto == cur + 1 { 176 fmtd 177 } else { 178 format!("{} (goto: {})", fmtd, goto) 179 } 180 } 181 182 fn visible_byte(b: u8) -> String { 183 use std::ascii::escape_default; 184 let escaped = escape_default(b).collect::<Vec<u8>>(); 185 String::from_utf8_lossy(&escaped).into_owned() 186 } 187 188 for (pc, inst) in self.iter().enumerate() { 189 match *inst { 190 Match(slot) => write!(f, "{:04} Match({:?})", pc, slot)?, 191 Save(ref inst) => { 192 let s = format!("{:04} Save({})", pc, inst.slot); 193 write!(f, "{}", with_goto(pc, inst.goto, s))?; 194 } 195 Split(ref inst) => { 196 write!( 197 f, 198 "{:04} Split({}, {})", 199 pc, inst.goto1, inst.goto2 200 )?; 201 } 202 EmptyLook(ref inst) => { 203 let s = format!("{:?}", inst.look); 204 write!(f, "{:04} {}", pc, with_goto(pc, inst.goto, s))?; 205 } 206 Char(ref inst) => { 207 let s = format!("{:?}", inst.c); 208 write!(f, "{:04} {}", pc, with_goto(pc, inst.goto, s))?; 209 } 210 Ranges(ref inst) => { 211 let ranges = inst 212 .ranges 213 .iter() 214 .map(|r| format!("{:?}-{:?}", r.0, r.1)) 215 .collect::<Vec<String>>() 216 .join(", "); 217 write!( 218 f, 219 "{:04} {}", 220 pc, 221 with_goto(pc, inst.goto, ranges) 222 )?; 223 } 224 Bytes(ref inst) => { 225 let s = format!( 226 "Bytes({}, {})", 227 visible_byte(inst.start), 228 visible_byte(inst.end) 229 ); 230 write!(f, "{:04} {}", pc, with_goto(pc, inst.goto, s))?; 231 } 232 } 233 if pc == self.start { 234 write!(f, " (start)")?; 235 } 236 write!(f, "\n")?; 237 } 238 Ok(()) 239 } 240 } 241 242 impl<'a> IntoIterator for &'a Program { 243 type Item = &'a Inst; 244 type IntoIter = slice::Iter<'a, Inst>; into_iter(self) -> Self::IntoIter245 fn into_iter(self) -> Self::IntoIter { 246 self.iter() 247 } 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 40 bytes to 263 /// 24 bytes. (This is because of the removal of a `Vec` 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. is_match(&self) -> bool300 pub fn is_match(&self) -> bool { 301 match *self { 302 Inst::Match(_) => true, 303 _ => false, 304 } 305 } 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: Vec<(char, char)>, 378 } 379 380 impl InstRanges { 381 /// Tests whether the given input character matches this instruction. matches(&self, c: Char) -> bool382 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 for r in self.ranges.iter().take(4) { 387 if c < r.0 { 388 return false; 389 } 390 if c <= r.1 { 391 return true; 392 } 393 } 394 self.ranges 395 .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 .is_ok() 405 } 406 407 /// Return the number of distinct characters represented by all of the 408 /// ranges. num_chars(&self) -> usize409 pub fn num_chars(&self) -> usize { 410 self.ranges 411 .iter() 412 .map(|&(s, e)| 1 + (e as u32) - (s as u32)) 413 .sum::<u32>() as usize 414 } 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. matches(&self, byte: u8) -> bool431 pub fn matches(&self, byte: u8) -> bool { 432 self.start <= byte && byte <= self.end 433 } 434 } 435