1use crate::{
2 dfa::{
3accel,
4 automaton::{Automaton, OverlappingState},
5 },
6 util::{
7prefilter::Prefilter,
8primitives::StateID,
9 search::{Anchored, HalfMatch, Input, Span},
10 },
11MatchError,
12};
1314#[inline(never)]
15pub fn find_fwd<A: Automaton + ?Sized>(
16 dfa: &A,
17 input: &Input<'_>,
18) -> Result<Option<HalfMatch>, MatchError> {
19if input.is_done() {
20return Ok(None);
21 }
22let pre = if input.get_anchored().is_anchored() {
23None24 } else {
25dfa.get_prefilter()
26 };
27// Searching with a pattern ID is always anchored, so we should never use
28 // a prefilter.
29if pre.is_some() {
30if input.get_earliest() {
31find_fwd_imp(dfa, input, pre, true)
32 } else {
33find_fwd_imp(dfa, input, pre, false)
34 }
35 } else {
36if input.get_earliest() {
37find_fwd_imp(dfa, input, None, true)
38 } else {
39find_fwd_imp(dfa, input, None, false)
40 }
41 }
42}
4344#[cfg_attr(feature = "perf-inline", inline(always))]
45fn find_fwd_imp<A: Automaton + ?Sized>(
46 dfa: &A,
47 input: &Input<'_>,
48 pre: Option<&'_ Prefilter>,
49 earliest: bool,
50) -> Result<Option<HalfMatch>, MatchError> {
51// See 'prefilter_restart' docs for explanation.
52let universal_start = dfa.universal_start_state(Anchored::No).is_some();
53let mut mat = None;
54let mut sid = init_fwd(dfa, input)?;
55let mut at = input.start();
56// This could just be a closure, but then I think it would be unsound
57 // because it would need to be safe to invoke. This way, the lack of safety
58 // is clearer in the code below.
59macro_rules! next_unchecked {
60 ($sid:expr, $at:expr) => {{
61let byte = *input.haystack().get_unchecked($at);
62 dfa.next_state_unchecked($sid, byte)
63 }};
64 }
6566if let Some(ref pre) = pre {
67let span = Span::from(at..input.end());
68// If a prefilter doesn't report false positives, then we don't need to
69 // touch the DFA at all. However, since all matches include the pattern
70 // ID, and the prefilter infrastructure doesn't report pattern IDs, we
71 // limit this optimization to cases where there is exactly one pattern.
72 // In that case, any match must be the 0th pattern.
73match pre.find(input.haystack(), span) {
74None => return Ok(mat),
75Some(ref span) => {
76at = span.start;
77if !universal_start {
78sid = prefilter_restart(dfa, &input, at)?;
79 }
80 }
81 }
82 }
83while at < input.end() {
84// SAFETY: There are two safety invariants we need to uphold here in
85 // the loops below: that 'sid' and 'prev_sid' are valid state IDs
86 // for this DFA, and that 'at' is a valid index into 'haystack'.
87 // For the former, we rely on the invariant that next_state* and
88 // start_state_forward always returns a valid state ID (given a valid
89 // state ID in the former case). For the latter safety invariant, we
90 // always guard unchecked access with a check that 'at' is less than
91 // 'end', where 'end <= haystack.len()'. In the unrolled loop below, we
92 // ensure that 'at' is always in bounds.
93 //
94 // PERF: See a similar comment in src/hybrid/search.rs that justifies
95 // this extra work to make the search loop fast. The same reasoning and
96 // benchmarks apply here.
97let mut prev_sid;
98while at < input.end() {
99 prev_sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(sid, byte)
}next_unchecked!(sid, at) };
100if dfa.is_special_state(prev_sid) || at + 3 >= input.end() {
101 core::mem::swap(&mut prev_sid, &mut sid);
102break;
103 }
104 at += 1;
105106 sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(prev_sid, byte)
}next_unchecked!(prev_sid, at) };
107if dfa.is_special_state(sid) {
108break;
109 }
110 at += 1;
111112 prev_sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(sid, byte)
}next_unchecked!(sid, at) };
113if dfa.is_special_state(prev_sid) {
114 core::mem::swap(&mut prev_sid, &mut sid);
115break;
116 }
117 at += 1;
118119 sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(prev_sid, byte)
}next_unchecked!(prev_sid, at) };
120if dfa.is_special_state(sid) {
121break;
122 }
123 at += 1;
124 }
125if dfa.is_special_state(sid) {
126if dfa.is_start_state(sid) {
127if let Some(ref pre) = pre {
128// A prefilter finds possible starts of new matches. Once
129 // a match is pending, the DFA may still be resolving its
130 // priority. Skipping ahead here could discard that match
131 // and replace it with a later one.
132if mat.is_none() {
133let span = Span::from(at..input.end());
134match pre.find(input.haystack(), span) {
135None => return Ok(mat),
136Some(ref span) => {
137// We want to skip any update to 'at' below
138 // at the end of this iteration and just
139 // jump immediately back to the next state
140 // transition at the leading position of the
141 // candidate match.
142 //
143 // ... but only if we actually made progress
144 // with our prefilter, otherwise if the start
145 // state has a self-loop, we can get stuck.
146if span.start > at {
147 at = span.start;
148if !universal_start {
149 sid = prefilter_restart(
150 dfa, &input, at,
151 )?;
152 }
153continue;
154 }
155 }
156 }
157 }
158 } else if dfa.is_accel_state(sid) {
159let needles = dfa.accelerator(sid);
160 at = accel::find_fwd(needles, input.haystack(), at + 1)
161 .unwrap_or(input.end());
162continue;
163 }
164 } else if dfa.is_match_state(sid) {
165let pattern = dfa.match_pattern(sid, 0);
166 mat = Some(HalfMatch::new(pattern, at));
167if earliest {
168return Ok(mat);
169 }
170if dfa.is_accel_state(sid) {
171let needles = dfa.accelerator(sid);
172 at = accel::find_fwd(needles, input.haystack(), at + 1)
173 .unwrap_or(input.end());
174continue;
175 }
176 } else if dfa.is_accel_state(sid) {
177let needs = dfa.accelerator(sid);
178 at = accel::find_fwd(needs, input.haystack(), at + 1)
179 .unwrap_or(input.end());
180continue;
181 } else if dfa.is_dead_state(sid) {
182return Ok(mat);
183 } else {
184// It's important that this is a debug_assert, since this can
185 // actually be tripped even if DFA::from_bytes succeeds and
186 // returns a supposedly valid DFA.
187return Err(MatchError::quit(input.haystack()[at], at));
188 }
189 }
190 at += 1;
191 }
192 eoi_fwd(dfa, input, &mut sid, &mut mat)?;
193Ok(mat)
194}
195196#[inline(never)]
197pub fn find_rev<A: Automaton + ?Sized>(
198 dfa: &A,
199 input: &Input<'_>,
200) -> Result<Option<HalfMatch>, MatchError> {
201if input.is_done() {
202return Ok(None);
203 }
204if input.get_earliest() {
205find_rev_imp(dfa, input, true)
206 } else {
207find_rev_imp(dfa, input, false)
208 }
209}
210211#[cfg_attr(feature = "perf-inline", inline(always))]
212fn find_rev_imp<A: Automaton + ?Sized>(
213 dfa: &A,
214 input: &Input<'_>,
215 earliest: bool,
216) -> Result<Option<HalfMatch>, MatchError> {
217let mut mat = None;
218let mut sid = init_rev(dfa, input)?;
219// In reverse search, the loop below can't handle the case of searching an
220 // empty slice. Ideally we could write something congruent to the forward
221 // search, i.e., 'while at >= start', but 'start' might be 0. Since we use
222 // an unsigned offset, 'at >= 0' is trivially always true. We could avoid
223 // this extra case handling by using a signed offset, but Rust makes it
224 // annoying to do. So... We just handle the empty case separately.
225if input.start() == input.end() {
226 eoi_rev(dfa, input, &mut sid, &mut mat)?;
227return Ok(mat);
228 }
229230let mut at = input.end() - 1;
231macro_rules! next_unchecked {
232 ($sid:expr, $at:expr) => {{
233let byte = *input.haystack().get_unchecked($at);
234 dfa.next_state_unchecked($sid, byte)
235 }};
236 }
237loop {
238// SAFETY: See comments in 'find_fwd' for a safety argument.
239let mut prev_sid;
240while at >= input.start() {
241 prev_sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(sid, byte)
}next_unchecked!(sid, at) };
242if dfa.is_special_state(prev_sid)
243 || at <= input.start().saturating_add(3)
244 {
245 core::mem::swap(&mut prev_sid, &mut sid);
246break;
247 }
248 at -= 1;
249250 sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(prev_sid, byte)
}next_unchecked!(prev_sid, at) };
251if dfa.is_special_state(sid) {
252break;
253 }
254 at -= 1;
255256 prev_sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(sid, byte)
}next_unchecked!(sid, at) };
257if dfa.is_special_state(prev_sid) {
258 core::mem::swap(&mut prev_sid, &mut sid);
259break;
260 }
261 at -= 1;
262263 sid = unsafe { {
let byte = *input.haystack().get_unchecked(at);
dfa.next_state_unchecked(prev_sid, byte)
}next_unchecked!(prev_sid, at) };
264if dfa.is_special_state(sid) {
265break;
266 }
267 at -= 1;
268 }
269if dfa.is_special_state(sid) {
270if dfa.is_start_state(sid) {
271if dfa.is_accel_state(sid) {
272let needles = dfa.accelerator(sid);
273at = accel::find_rev(needles, input.haystack(), at)
274 .map(|i| i + 1)
275 .unwrap_or(input.start());
276 }
277 } else if dfa.is_match_state(sid) {
278let pattern = dfa.match_pattern(sid, 0);
279// Since reverse searches report the beginning of a match
280 // and the beginning is inclusive (not exclusive like the
281 // end of a match), we add 1 to make it inclusive.
282mat = Some(HalfMatch::new(pattern, at + 1));
283if earliest {
284return Ok(mat);
285 }
286if dfa.is_accel_state(sid) {
287let needles = dfa.accelerator(sid);
288at = accel::find_rev(needles, input.haystack(), at)
289 .map(|i| i + 1)
290 .unwrap_or(input.start());
291 }
292 } else if dfa.is_accel_state(sid) {
293let needles = dfa.accelerator(sid);
294// If the accelerator returns nothing, why don't we quit the
295 // search? Well, if the accelerator doesn't find anything, that
296 // doesn't mean we don't have a match. It just means that we
297 // can't leave the current state given one of the 255 possible
298 // byte values. However, there might be an EOI transition. So
299 // we set 'at' to the end of the haystack, which will cause
300 // this loop to stop and fall down into the EOI transition.
301at = accel::find_rev(needles, input.haystack(), at)
302 .map(|i| i + 1)
303 .unwrap_or(input.start());
304 } else if dfa.is_dead_state(sid) {
305return Ok(mat);
306 } else {
307return Err(MatchError::quit(input.haystack()[at], at));
308 }
309 }
310if at == input.start() {
311break;
312 }
313at -= 1;
314 }
315 eoi_rev(dfa, input, &mut sid, &mut mat)?;
316Ok(mat)
317}
318319#[inline(never)]
320pub fn find_overlapping_fwd<A: Automaton + ?Sized>(
321 dfa: &A,
322 input: &Input<'_>,
323 state: &mut OverlappingState,
324) -> Result<(), MatchError> {
325state.mat = None;
326if input.is_done() {
327return Ok(());
328 }
329let pre = if input.get_anchored().is_anchored() {
330None331 } else {
332dfa.get_prefilter()
333 };
334if pre.is_some() {
335find_overlapping_fwd_imp(dfa, input, pre, state)
336 } else {
337find_overlapping_fwd_imp(dfa, input, None, state)
338 }
339}
340341#[cfg_attr(feature = "perf-inline", inline(always))]
342fn find_overlapping_fwd_imp<A: Automaton + ?Sized>(
343 dfa: &A,
344 input: &Input<'_>,
345 pre: Option<&'_ Prefilter>,
346 state: &mut OverlappingState,
347) -> Result<(), MatchError> {
348// See 'prefilter_restart' docs for explanation.
349let universal_start = dfa.universal_start_state(Anchored::No).is_some();
350let mut sid = match state.id {
351None => {
352state.at = input.start();
353 init_fwd(dfa, input)?
354}
355Some(sid) => {
356if let Some(match_index) = state.next_match_index {
357let match_len = dfa.match_len(sid);
358if match_index < match_len {
359state.next_match_index = Some(match_index + 1);
360let pattern = dfa.match_pattern(sid, match_index);
361state.mat = Some(HalfMatch::new(pattern, state.at));
362return Ok(());
363 }
364 }
365// Once we've reported all matches at a given position, we need to
366 // advance the search to the next position.
367state.at += 1;
368if state.at > input.end() {
369return Ok(());
370 }
371sid372 }
373 };
374375// NOTE: We don't optimize the crap out of this routine primarily because
376 // it seems like most find_overlapping searches will have higher match
377 // counts, and thus, throughput is perhaps not as important. But if you
378 // have a use case for something faster, feel free to file an issue.
379while state.at < input.end() {
380 sid = dfa.next_state(sid, input.haystack()[state.at]);
381if dfa.is_special_state(sid) {
382 state.id = Some(sid);
383if dfa.is_start_state(sid) {
384if let Some(ref pre) = pre {
385let span = Span::from(state.at..input.end());
386match pre.find(input.haystack(), span) {
387None => return Ok(()),
388Some(ref span) => {
389if span.start > state.at {
390 state.at = span.start;
391if !universal_start {
392 sid = prefilter_restart(
393 dfa, &input, state.at,
394 )?;
395 }
396continue;
397 }
398 }
399 }
400 } else if dfa.is_accel_state(sid) {
401let needles = dfa.accelerator(sid);
402 state.at = accel::find_fwd(
403 needles,
404 input.haystack(),
405 state.at + 1,
406 )
407 .unwrap_or(input.end());
408continue;
409 }
410 } else if dfa.is_match_state(sid) {
411 state.next_match_index = Some(1);
412let pattern = dfa.match_pattern(sid, 0);
413 state.mat = Some(HalfMatch::new(pattern, state.at));
414return Ok(());
415 } else if dfa.is_accel_state(sid) {
416let needs = dfa.accelerator(sid);
417// If the accelerator returns nothing, why don't we quit the
418 // search? Well, if the accelerator doesn't find anything, that
419 // doesn't mean we don't have a match. It just means that we
420 // can't leave the current state given one of the 255 possible
421 // byte values. However, there might be an EOI transition. So
422 // we set 'at' to the end of the haystack, which will cause
423 // this loop to stop and fall down into the EOI transition.
424state.at =
425 accel::find_fwd(needs, input.haystack(), state.at + 1)
426 .unwrap_or(input.end());
427continue;
428 } else if dfa.is_dead_state(sid) {
429return Ok(());
430 } else {
431return Err(MatchError::quit(
432 input.haystack()[state.at],
433 state.at,
434 ));
435 }
436 }
437 state.at += 1;
438 }
439440let result = eoi_fwd(dfa, input, &mut sid, &mut state.mat);
441state.id = Some(sid);
442if state.mat.is_some() {
443// '1' is always correct here since if we get to this point, this
444 // always corresponds to the first (index '0') match discovered at
445 // this position. So the next match to report at this position (if
446 // it exists) is at index '1'.
447state.next_match_index = Some(1);
448 }
449result450}
451452#[inline(never)]
453pub(crate) fn find_overlapping_rev<A: Automaton + ?Sized>(
454 dfa: &A,
455 input: &Input<'_>,
456 state: &mut OverlappingState,
457) -> Result<(), MatchError> {
458state.mat = None;
459if input.is_done() {
460return Ok(());
461 }
462let mut sid = match state.id {
463None => {
464let sid = init_rev(dfa, input)?;
465state.id = Some(sid);
466if input.start() == input.end() {
467state.rev_eoi = true;
468 } else {
469state.at = input.end() - 1;
470 }
471sid472 }
473Some(sid) => {
474if let Some(match_index) = state.next_match_index {
475let match_len = dfa.match_len(sid);
476if match_index < match_len {
477state.next_match_index = Some(match_index + 1);
478let pattern = dfa.match_pattern(sid, match_index);
479state.mat = Some(HalfMatch::new(pattern, state.at));
480return Ok(());
481 }
482 }
483// Once we've reported all matches at a given position, we need
484 // to advance the search to the next position. However, if we've
485 // already followed the EOI transition, then we know we're done
486 // with the search and there cannot be any more matches to report.
487if state.rev_eoi {
488return Ok(());
489 } else if state.at == input.start() {
490// At this point, we should follow the EOI transition. This
491 // will cause us the skip the main loop below and fall through
492 // to the final 'eoi_rev' transition.
493state.rev_eoi = true;
494 } else {
495// We haven't hit the end of the search yet, so move on.
496state.at -= 1;
497 }
498sid499 }
500 };
501while !state.rev_eoi {
502 sid = dfa.next_state(sid, input.haystack()[state.at]);
503if dfa.is_special_state(sid) {
504 state.id = Some(sid);
505if dfa.is_start_state(sid) {
506if dfa.is_accel_state(sid) {
507let needles = dfa.accelerator(sid);
508 state.at =
509 accel::find_rev(needles, input.haystack(), state.at)
510 .map(|i| i + 1)
511 .unwrap_or(input.start());
512 }
513 } else if dfa.is_match_state(sid) {
514 state.next_match_index = Some(1);
515let pattern = dfa.match_pattern(sid, 0);
516 state.mat = Some(HalfMatch::new(pattern, state.at + 1));
517return Ok(());
518 } else if dfa.is_accel_state(sid) {
519let needles = dfa.accelerator(sid);
520// If the accelerator returns nothing, why don't we quit the
521 // search? Well, if the accelerator doesn't find anything, that
522 // doesn't mean we don't have a match. It just means that we
523 // can't leave the current state given one of the 255 possible
524 // byte values. However, there might be an EOI transition. So
525 // we set 'at' to the end of the haystack, which will cause
526 // this loop to stop and fall down into the EOI transition.
527state.at =
528 accel::find_rev(needles, input.haystack(), state.at)
529 .map(|i| i + 1)
530 .unwrap_or(input.start());
531 } else if dfa.is_dead_state(sid) {
532return Ok(());
533 } else {
534return Err(MatchError::quit(
535 input.haystack()[state.at],
536 state.at,
537 ));
538 }
539 }
540if state.at == input.start() {
541break;
542 }
543 state.at -= 1;
544 }
545546let result = eoi_rev(dfa, input, &mut sid, &mut state.mat);
547state.rev_eoi = true;
548state.id = Some(sid);
549if state.mat.is_some() {
550// '1' is always correct here since if we get to this point, this
551 // always corresponds to the first (index '0') match discovered at
552 // this position. So the next match to report at this position (if
553 // it exists) is at index '1'.
554state.next_match_index = Some(1);
555 }
556result557}
558559#[cfg_attr(feature = "perf-inline", inline(always))]
560fn init_fwd<A: Automaton + ?Sized>(
561 dfa: &A,
562 input: &Input<'_>,
563) -> Result<StateID, MatchError> {
564let sid = dfa.start_state_forward(input)?;
565// Start states can never be match states, since all matches are delayed
566 // by 1 byte.
567if true {
if !!dfa.is_match_state(sid) {
::core::panicking::panic("assertion failed: !dfa.is_match_state(sid)")
};
};debug_assert!(!dfa.is_match_state(sid));
568Ok(sid)
569}
570571#[cfg_attr(feature = "perf-inline", inline(always))]
572fn init_rev<A: Automaton + ?Sized>(
573 dfa: &A,
574 input: &Input<'_>,
575) -> Result<StateID, MatchError> {
576let sid = dfa.start_state_reverse(input)?;
577// Start states can never be match states, since all matches are delayed
578 // by 1 byte.
579if true {
if !!dfa.is_match_state(sid) {
::core::panicking::panic("assertion failed: !dfa.is_match_state(sid)")
};
};debug_assert!(!dfa.is_match_state(sid));
580Ok(sid)
581}
582583#[cfg_attr(feature = "perf-inline", inline(always))]
584fn eoi_fwd<A: Automaton + ?Sized>(
585 dfa: &A,
586 input: &Input<'_>,
587 sid: &mut StateID,
588 mat: &mut Option<HalfMatch>,
589) -> Result<(), MatchError> {
590let sp = input.get_span();
591match input.haystack().get(sp.end) {
592Some(&b) => {
593*sid = dfa.next_state(*sid, b);
594if dfa.is_match_state(*sid) {
595let pattern = dfa.match_pattern(*sid, 0);
596*mat = Some(HalfMatch::new(pattern, sp.end));
597 } else if dfa.is_quit_state(*sid) {
598return Err(MatchError::quit(b, sp.end));
599 }
600 }
601None => {
602*sid = dfa.next_eoi_state(*sid);
603if dfa.is_match_state(*sid) {
604let pattern = dfa.match_pattern(*sid, 0);
605*mat = Some(HalfMatch::new(pattern, input.haystack().len()));
606 }
607 }
608 }
609Ok(())
610}
611612#[cfg_attr(feature = "perf-inline", inline(always))]
613fn eoi_rev<A: Automaton + ?Sized>(
614 dfa: &A,
615 input: &Input<'_>,
616 sid: &mut StateID,
617 mat: &mut Option<HalfMatch>,
618) -> Result<(), MatchError> {
619let sp = input.get_span();
620if sp.start > 0 {
621let byte = input.haystack()[sp.start - 1];
622*sid = dfa.next_state(*sid, byte);
623if dfa.is_match_state(*sid) {
624let pattern = dfa.match_pattern(*sid, 0);
625*mat = Some(HalfMatch::new(pattern, sp.start));
626 } else if dfa.is_quit_state(*sid) {
627return Err(MatchError::quit(byte, sp.start - 1));
628 }
629 } else {
630*sid = dfa.next_eoi_state(*sid);
631if dfa.is_match_state(*sid) {
632let pattern = dfa.match_pattern(*sid, 0);
633*mat = Some(HalfMatch::new(pattern, 0));
634 }
635 }
636Ok(())
637}
638639/// Re-compute the starting state that a DFA should be in after finding a
640/// prefilter candidate match at the position `at`.
641///
642/// The function with the same name has a bit more docs in hybrid/search.rs.
643#[cfg_attr(feature = "perf-inline", inline(always))]
644fn prefilter_restart<A: Automaton + ?Sized>(
645 dfa: &A,
646 input: &Input<'_>,
647 at: usize,
648) -> Result<StateID, MatchError> {
649let mut input = input.clone();
650input.set_start(at);
651init_fwd(dfa, &input)
652}