/src/regex/regex-automata/src/dfa/search.rs
Line | Count | Source |
1 | | use crate::{ |
2 | | dfa::{ |
3 | | accel, |
4 | | automaton::{Automaton, OverlappingState}, |
5 | | }, |
6 | | util::{ |
7 | | prefilter::Prefilter, |
8 | | primitives::StateID, |
9 | | search::{Anchored, HalfMatch, Input, Span}, |
10 | | }, |
11 | | MatchError, |
12 | | }; |
13 | | |
14 | | #[inline(never)] |
15 | 127k | pub fn find_fwd<A: Automaton + ?Sized>( |
16 | 127k | dfa: &A, |
17 | 127k | input: &Input<'_>, |
18 | 127k | ) -> Result<Option<HalfMatch>, MatchError> { |
19 | 127k | if input.is_done() { |
20 | 0 | return Ok(None); |
21 | 127k | } |
22 | 127k | let pre = if input.get_anchored().is_anchored() { |
23 | 84 | None |
24 | | } else { |
25 | 127k | dfa.get_prefilter() |
26 | | }; |
27 | | // Searching with a pattern ID is always anchored, so we should never use |
28 | | // a prefilter. |
29 | 127k | if pre.is_some() { |
30 | 28.0k | if input.get_earliest() { |
31 | 11.5k | find_fwd_imp(dfa, input, pre, true) |
32 | | } else { |
33 | 16.5k | find_fwd_imp(dfa, input, pre, false) |
34 | | } |
35 | | } else { |
36 | 99.2k | if input.get_earliest() { |
37 | 20.8k | find_fwd_imp(dfa, input, None, true) |
38 | | } else { |
39 | 78.4k | find_fwd_imp(dfa, input, None, false) |
40 | | } |
41 | | } |
42 | 127k | } regex_automata::dfa::search::find_fwd::<®ex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> Line | Count | Source | 15 | 83.0k | pub fn find_fwd<A: Automaton + ?Sized>( | 16 | 83.0k | dfa: &A, | 17 | 83.0k | input: &Input<'_>, | 18 | 83.0k | ) -> Result<Option<HalfMatch>, MatchError> { | 19 | 83.0k | if input.is_done() { | 20 | 0 | return Ok(None); | 21 | 83.0k | } | 22 | 83.0k | let pre = if input.get_anchored().is_anchored() { | 23 | 84 | None | 24 | | } else { | 25 | 83.0k | dfa.get_prefilter() | 26 | | }; | 27 | | // Searching with a pattern ID is always anchored, so we should never use | 28 | | // a prefilter. | 29 | 83.0k | if pre.is_some() { | 30 | 28.0k | if input.get_earliest() { | 31 | 11.5k | find_fwd_imp(dfa, input, pre, true) | 32 | | } else { | 33 | 16.5k | find_fwd_imp(dfa, input, pre, false) | 34 | | } | 35 | | } else { | 36 | 54.9k | if input.get_earliest() { | 37 | 20.8k | find_fwd_imp(dfa, input, None, true) | 38 | | } else { | 39 | 34.1k | find_fwd_imp(dfa, input, None, false) | 40 | | } | 41 | | } | 42 | 83.0k | } |
regex_automata::dfa::search::find_fwd::<®ex_automata::dfa::dense::DFA<&[u32]>> Line | Count | Source | 15 | 18.7k | pub fn find_fwd<A: Automaton + ?Sized>( | 16 | 18.7k | dfa: &A, | 17 | 18.7k | input: &Input<'_>, | 18 | 18.7k | ) -> Result<Option<HalfMatch>, MatchError> { | 19 | 18.7k | if input.is_done() { | 20 | 0 | return Ok(None); | 21 | 18.7k | } | 22 | 18.7k | let pre = if input.get_anchored().is_anchored() { | 23 | 0 | None | 24 | | } else { | 25 | 18.7k | dfa.get_prefilter() | 26 | | }; | 27 | | // Searching with a pattern ID is always anchored, so we should never use | 28 | | // a prefilter. | 29 | 18.7k | if pre.is_some() { | 30 | 0 | if input.get_earliest() { | 31 | 0 | find_fwd_imp(dfa, input, pre, true) | 32 | | } else { | 33 | 0 | find_fwd_imp(dfa, input, pre, false) | 34 | | } | 35 | | } else { | 36 | 18.7k | if input.get_earliest() { | 37 | 0 | find_fwd_imp(dfa, input, None, true) | 38 | | } else { | 39 | 18.7k | find_fwd_imp(dfa, input, None, false) | 40 | | } | 41 | | } | 42 | 18.7k | } |
regex_automata::dfa::search::find_fwd::<®ex_automata::dfa::sparse::DFA<&[u8]>> Line | Count | Source | 15 | 25.5k | pub fn find_fwd<A: Automaton + ?Sized>( | 16 | 25.5k | dfa: &A, | 17 | 25.5k | input: &Input<'_>, | 18 | 25.5k | ) -> Result<Option<HalfMatch>, MatchError> { | 19 | 25.5k | if input.is_done() { | 20 | 0 | return Ok(None); | 21 | 25.5k | } | 22 | 25.5k | let pre = if input.get_anchored().is_anchored() { | 23 | 0 | None | 24 | | } else { | 25 | 25.5k | dfa.get_prefilter() | 26 | | }; | 27 | | // Searching with a pattern ID is always anchored, so we should never use | 28 | | // a prefilter. | 29 | 25.5k | if pre.is_some() { | 30 | 0 | if input.get_earliest() { | 31 | 0 | find_fwd_imp(dfa, input, pre, true) | 32 | | } else { | 33 | 0 | find_fwd_imp(dfa, input, pre, false) | 34 | | } | 35 | | } else { | 36 | 25.5k | if input.get_earliest() { | 37 | 0 | find_fwd_imp(dfa, input, None, true) | 38 | | } else { | 39 | 25.5k | find_fwd_imp(dfa, input, None, false) | 40 | | } | 41 | | } | 42 | 25.5k | } |
|
43 | | |
44 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
45 | 127k | fn find_fwd_imp<A: Automaton + ?Sized>( |
46 | 127k | dfa: &A, |
47 | 127k | input: &Input<'_>, |
48 | 127k | pre: Option<&'_ Prefilter>, |
49 | 127k | earliest: bool, |
50 | 127k | ) -> Result<Option<HalfMatch>, MatchError> { |
51 | | // See 'prefilter_restart' docs for explanation. |
52 | 127k | let universal_start = dfa.universal_start_state(Anchored::No).is_some(); |
53 | 127k | let mut mat = None; |
54 | 127k | let mut sid = init_fwd(dfa, input)?; |
55 | 126k | let 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. |
59 | | macro_rules! next_unchecked { |
60 | | ($sid:expr, $at:expr) => {{ |
61 | | let byte = *input.haystack().get_unchecked($at); |
62 | | dfa.next_state_unchecked($sid, byte) |
63 | | }}; |
64 | | } |
65 | | |
66 | 126k | if let Some(ref pre) = pre { |
67 | 28.0k | let 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. |
73 | 28.0k | match pre.find(input.haystack(), span) { |
74 | 8.98k | None => return Ok(mat), |
75 | 19.1k | Some(ref span) => { |
76 | 19.1k | at = span.start; |
77 | 19.1k | if !universal_start { |
78 | 2.70k | sid = prefilter_restart(dfa, &input, at)?; |
79 | 16.4k | } |
80 | | } |
81 | | } |
82 | 98.5k | } |
83 | 868k | while 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. |
97 | | let mut prev_sid; |
98 | 1.50M | while at < input.end() { |
99 | 1.50M | prev_sid = unsafe { next_unchecked!(sid, at) }; |
100 | 1.50M | if dfa.is_special_state(prev_sid) || at + 3 >= input.end() { |
101 | 666k | core::mem::swap(&mut prev_sid, &mut sid); |
102 | 666k | break; |
103 | 839k | } |
104 | 839k | at += 1; |
105 | | |
106 | 839k | sid = unsafe { next_unchecked!(prev_sid, at) }; |
107 | 839k | if dfa.is_special_state(sid) { |
108 | 78.6k | break; |
109 | 761k | } |
110 | 761k | at += 1; |
111 | | |
112 | 761k | prev_sid = unsafe { next_unchecked!(sid, at) }; |
113 | 761k | if dfa.is_special_state(prev_sid) { |
114 | 40.0k | core::mem::swap(&mut prev_sid, &mut sid); |
115 | 40.0k | break; |
116 | 720k | } |
117 | 720k | at += 1; |
118 | | |
119 | 720k | sid = unsafe { next_unchecked!(prev_sid, at) }; |
120 | 720k | if dfa.is_special_state(sid) { |
121 | 28.9k | break; |
122 | 692k | } |
123 | 692k | at += 1; |
124 | | } |
125 | 815k | if dfa.is_special_state(sid) { |
126 | 802k | if dfa.is_start_state(sid) { |
127 | 432k | if 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. |
132 | 375k | if mat.is_none() { |
133 | 375k | let span = Span::from(at..input.end()); |
134 | 375k | match pre.find(input.haystack(), span) { |
135 | 3.40k | None => return Ok(mat), |
136 | 371k | Some(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. |
146 | 371k | if span.start > at { |
147 | 91.4k | at = span.start; |
148 | 91.4k | if !universal_start { |
149 | 31.3k | sid = prefilter_restart( |
150 | 31.3k | dfa, &input, at, |
151 | 412 | )?; |
152 | 60.0k | } |
153 | 91.0k | continue; |
154 | 280k | } |
155 | | } |
156 | | } |
157 | 246 | } |
158 | 57.3k | } else if dfa.is_accel_state(sid) { |
159 | 24.6k | let needles = dfa.accelerator(sid); |
160 | 24.6k | at = accel::find_fwd(needles, input.haystack(), at + 1) |
161 | 24.6k | .unwrap_or(input.end()); |
162 | 24.6k | continue; |
163 | 32.6k | } |
164 | 369k | } else if dfa.is_match_state(sid) { |
165 | 200k | let pattern = dfa.match_pattern(sid, 0); |
166 | 200k | mat = Some(HalfMatch::new(pattern, at)); |
167 | 200k | if earliest { |
168 | 9.83k | return Ok(mat); |
169 | 190k | } |
170 | 190k | if dfa.is_accel_state(sid) { |
171 | 41.2k | let needles = dfa.accelerator(sid); |
172 | 41.2k | at = accel::find_fwd(needles, input.haystack(), at + 1) |
173 | 41.2k | .unwrap_or(input.end()); |
174 | 41.2k | continue; |
175 | 149k | } |
176 | 168k | } else if dfa.is_accel_state(sid) { |
177 | 118k | let needs = dfa.accelerator(sid); |
178 | 118k | at = accel::find_fwd(needs, input.haystack(), at + 1) |
179 | 118k | .unwrap_or(input.end()); |
180 | 118k | continue; |
181 | 50.3k | } else if dfa.is_dead_state(sid) { |
182 | 25.6k | return 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. |
187 | 24.7k | return Err(MatchError::quit(input.haystack()[at], at)); |
188 | | } |
189 | 13.3k | } |
190 | 476k | at += 1; |
191 | | } |
192 | 53.4k | eoi_fwd(dfa, input, &mut sid, &mut mat)?; |
193 | 53.4k | Ok(mat) |
194 | 127k | } regex_automata::dfa::search::find_fwd_imp::<®ex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> Line | Count | Source | 45 | 83.0k | fn find_fwd_imp<A: Automaton + ?Sized>( | 46 | 83.0k | dfa: &A, | 47 | 83.0k | input: &Input<'_>, | 48 | 83.0k | pre: Option<&'_ Prefilter>, | 49 | 83.0k | earliest: bool, | 50 | 83.0k | ) -> Result<Option<HalfMatch>, MatchError> { | 51 | | // See 'prefilter_restart' docs for explanation. | 52 | 83.0k | let universal_start = dfa.universal_start_state(Anchored::No).is_some(); | 53 | 83.0k | let mut mat = None; | 54 | 83.0k | let mut sid = init_fwd(dfa, input)?; | 55 | 83.0k | let 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. | 59 | | macro_rules! next_unchecked { | 60 | | ($sid:expr, $at:expr) => {{ | 61 | | let byte = *input.haystack().get_unchecked($at); | 62 | | dfa.next_state_unchecked($sid, byte) | 63 | | }}; | 64 | | } | 65 | | | 66 | 83.0k | if let Some(ref pre) = pre { | 67 | 28.0k | let 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. | 73 | 28.0k | match pre.find(input.haystack(), span) { | 74 | 8.98k | None => return Ok(mat), | 75 | 19.1k | Some(ref span) => { | 76 | 19.1k | at = span.start; | 77 | 19.1k | if !universal_start { | 78 | 2.70k | sid = prefilter_restart(dfa, &input, at)?; | 79 | 16.4k | } | 80 | | } | 81 | | } | 82 | 54.9k | } | 83 | 602k | while 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. | 97 | | let mut prev_sid; | 98 | 1.26M | while at < input.end() { | 99 | 1.26M | prev_sid = unsafe { next_unchecked!(sid, at) }; | 100 | 1.26M | if dfa.is_special_state(prev_sid) || at + 3 >= input.end() { | 101 | 438k | core::mem::swap(&mut prev_sid, &mut sid); | 102 | 438k | break; | 103 | 826k | } | 104 | 826k | at += 1; | 105 | | | 106 | 826k | sid = unsafe { next_unchecked!(prev_sid, at) }; | 107 | 826k | if dfa.is_special_state(sid) { | 108 | 70.9k | break; | 109 | 755k | } | 110 | 755k | at += 1; | 111 | | | 112 | 755k | prev_sid = unsafe { next_unchecked!(sid, at) }; | 113 | 755k | if dfa.is_special_state(prev_sid) { | 114 | 39.0k | core::mem::swap(&mut prev_sid, &mut sid); | 115 | 39.0k | break; | 116 | 716k | } | 117 | 716k | at += 1; | 118 | | | 119 | 716k | sid = unsafe { next_unchecked!(prev_sid, at) }; | 120 | 716k | if dfa.is_special_state(sid) { | 121 | 28.0k | break; | 122 | 688k | } | 123 | 688k | at += 1; | 124 | | } | 125 | 577k | if dfa.is_special_state(sid) { | 126 | 565k | if dfa.is_start_state(sid) { | 127 | 376k | if 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. | 132 | 375k | if mat.is_none() { | 133 | 375k | let span = Span::from(at..input.end()); | 134 | 375k | match pre.find(input.haystack(), span) { | 135 | 3.40k | None => return Ok(mat), | 136 | 371k | Some(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. | 146 | 371k | if span.start > at { | 147 | 91.4k | at = span.start; | 148 | 91.4k | if !universal_start { | 149 | 31.3k | sid = prefilter_restart( | 150 | 31.3k | dfa, &input, at, | 151 | 412 | )?; | 152 | 60.0k | } | 153 | 91.0k | continue; | 154 | 280k | } | 155 | | } | 156 | | } | 157 | 246 | } | 158 | 1.17k | } else if dfa.is_accel_state(sid) { | 159 | 0 | let needles = dfa.accelerator(sid); | 160 | 0 | at = accel::find_fwd(needles, input.haystack(), at + 1) | 161 | 0 | .unwrap_or(input.end()); | 162 | 0 | continue; | 163 | 1.17k | } | 164 | 188k | } else if dfa.is_match_state(sid) { | 165 | 96.0k | let pattern = dfa.match_pattern(sid, 0); | 166 | 96.0k | mat = Some(HalfMatch::new(pattern, at)); | 167 | 96.0k | if earliest { | 168 | 9.83k | return Ok(mat); | 169 | 86.2k | } | 170 | 86.2k | if dfa.is_accel_state(sid) { | 171 | 6.77k | let needles = dfa.accelerator(sid); | 172 | 6.77k | at = accel::find_fwd(needles, input.haystack(), at + 1) | 173 | 6.77k | .unwrap_or(input.end()); | 174 | 6.77k | continue; | 175 | 79.4k | } | 176 | 92.8k | } else if dfa.is_accel_state(sid) { | 177 | 57.1k | let needs = dfa.accelerator(sid); | 178 | 57.1k | at = accel::find_fwd(needs, input.haystack(), at + 1) | 179 | 57.1k | .unwrap_or(input.end()); | 180 | 57.1k | continue; | 181 | 35.7k | } else if dfa.is_dead_state(sid) { | 182 | 11.1k | return 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. | 187 | 24.6k | return Err(MatchError::quit(input.haystack()[at], at)); | 188 | | } | 189 | 11.9k | } | 190 | 373k | at += 1; | 191 | | } | 192 | 24.5k | eoi_fwd(dfa, input, &mut sid, &mut mat)?; | 193 | 24.5k | Ok(mat) | 194 | 83.0k | } |
regex_automata::dfa::search::find_fwd_imp::<®ex_automata::dfa::dense::DFA<&[u32]>> Line | Count | Source | 45 | 18.7k | fn find_fwd_imp<A: Automaton + ?Sized>( | 46 | 18.7k | dfa: &A, | 47 | 18.7k | input: &Input<'_>, | 48 | 18.7k | pre: Option<&'_ Prefilter>, | 49 | 18.7k | earliest: bool, | 50 | 18.7k | ) -> Result<Option<HalfMatch>, MatchError> { | 51 | | // See 'prefilter_restart' docs for explanation. | 52 | 18.7k | let universal_start = dfa.universal_start_state(Anchored::No).is_some(); | 53 | 18.7k | let mut mat = None; | 54 | 18.7k | let mut sid = init_fwd(dfa, input)?; | 55 | 18.3k | let 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. | 59 | | macro_rules! next_unchecked { | 60 | | ($sid:expr, $at:expr) => {{ | 61 | | let byte = *input.haystack().get_unchecked($at); | 62 | | dfa.next_state_unchecked($sid, byte) | 63 | | }}; | 64 | | } | 65 | | | 66 | 18.3k | if let Some(ref pre) = pre { | 67 | 0 | let 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. | 73 | 0 | match pre.find(input.haystack(), span) { | 74 | 0 | None => return Ok(mat), | 75 | 0 | Some(ref span) => { | 76 | 0 | at = span.start; | 77 | 0 | if !universal_start { | 78 | 0 | sid = prefilter_restart(dfa, &input, at)?; | 79 | 0 | } | 80 | | } | 81 | | } | 82 | 18.3k | } | 83 | 174k | while 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. | 97 | | let mut prev_sid; | 98 | 160k | while at < input.end() { | 99 | 160k | prev_sid = unsafe { next_unchecked!(sid, at) }; | 100 | 160k | if dfa.is_special_state(prev_sid) || at + 3 >= input.end() { | 101 | 147k | core::mem::swap(&mut prev_sid, &mut sid); | 102 | 147k | break; | 103 | 12.7k | } | 104 | 12.7k | at += 1; | 105 | | | 106 | 12.7k | sid = unsafe { next_unchecked!(prev_sid, at) }; | 107 | 12.7k | if dfa.is_special_state(sid) { | 108 | 7.67k | break; | 109 | 5.03k | } | 110 | 5.03k | at += 1; | 111 | | | 112 | 5.03k | prev_sid = unsafe { next_unchecked!(sid, at) }; | 113 | 5.03k | if dfa.is_special_state(prev_sid) { | 114 | 1.00k | core::mem::swap(&mut prev_sid, &mut sid); | 115 | 1.00k | break; | 116 | 4.02k | } | 117 | 4.02k | at += 1; | 118 | | | 119 | 4.02k | sid = unsafe { next_unchecked!(prev_sid, at) }; | 120 | 4.02k | if dfa.is_special_state(sid) { | 121 | 935 | break; | 122 | 3.09k | } | 123 | 3.09k | at += 1; | 124 | | } | 125 | 157k | if dfa.is_special_state(sid) { | 126 | 156k | if dfa.is_start_state(sid) { | 127 | 49.9k | if 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. | 132 | 0 | if mat.is_none() { | 133 | 0 | let span = Span::from(at..input.end()); | 134 | 0 | match pre.find(input.haystack(), span) { | 135 | 0 | None => return Ok(mat), | 136 | 0 | Some(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. | 146 | 0 | if span.start > at { | 147 | 0 | at = span.start; | 148 | 0 | if !universal_start { | 149 | 0 | sid = prefilter_restart( | 150 | 0 | dfa, &input, at, | 151 | 0 | )?; | 152 | 0 | } | 153 | 0 | continue; | 154 | 0 | } | 155 | | } | 156 | | } | 157 | 0 | } | 158 | 49.9k | } else if dfa.is_accel_state(sid) { | 159 | 19.1k | let needles = dfa.accelerator(sid); | 160 | 19.1k | at = accel::find_fwd(needles, input.haystack(), at + 1) | 161 | 19.1k | .unwrap_or(input.end()); | 162 | 19.1k | continue; | 163 | 30.7k | } | 164 | 106k | } else if dfa.is_match_state(sid) { | 165 | 80.7k | let pattern = dfa.match_pattern(sid, 0); | 166 | 80.7k | mat = Some(HalfMatch::new(pattern, at)); | 167 | 80.7k | if earliest { | 168 | 0 | return Ok(mat); | 169 | 80.7k | } | 170 | 80.7k | if dfa.is_accel_state(sid) { | 171 | 34.4k | let needles = dfa.accelerator(sid); | 172 | 34.4k | at = accel::find_fwd(needles, input.haystack(), at + 1) | 173 | 34.4k | .unwrap_or(input.end()); | 174 | 34.4k | continue; | 175 | 46.2k | } | 176 | 25.5k | } else if dfa.is_accel_state(sid) { | 177 | 24.4k | let needs = dfa.accelerator(sid); | 178 | 24.4k | at = accel::find_fwd(needs, input.haystack(), at + 1) | 179 | 24.4k | .unwrap_or(input.end()); | 180 | 24.4k | continue; | 181 | 1.05k | } else if dfa.is_dead_state(sid) { | 182 | 907 | return 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. | 187 | 150 | return Err(MatchError::quit(input.haystack()[at], at)); | 188 | | } | 189 | 1.26k | } | 190 | 78.2k | at += 1; | 191 | | } | 192 | 17.2k | eoi_fwd(dfa, input, &mut sid, &mut mat)?; | 193 | 17.2k | Ok(mat) | 194 | 18.7k | } |
regex_automata::dfa::search::find_fwd_imp::<®ex_automata::dfa::sparse::DFA<&[u8]>> Line | Count | Source | 45 | 25.5k | fn find_fwd_imp<A: Automaton + ?Sized>( | 46 | 25.5k | dfa: &A, | 47 | 25.5k | input: &Input<'_>, | 48 | 25.5k | pre: Option<&'_ Prefilter>, | 49 | 25.5k | earliest: bool, | 50 | 25.5k | ) -> Result<Option<HalfMatch>, MatchError> { | 51 | | // See 'prefilter_restart' docs for explanation. | 52 | 25.5k | let universal_start = dfa.universal_start_state(Anchored::No).is_some(); | 53 | 25.5k | let mut mat = None; | 54 | 25.5k | let mut sid = init_fwd(dfa, input)?; | 55 | 25.2k | let 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. | 59 | | macro_rules! next_unchecked { | 60 | | ($sid:expr, $at:expr) => {{ | 61 | | let byte = *input.haystack().get_unchecked($at); | 62 | | dfa.next_state_unchecked($sid, byte) | 63 | | }}; | 64 | | } | 65 | | | 66 | 25.2k | if let Some(ref pre) = pre { | 67 | 0 | let 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. | 73 | 0 | match pre.find(input.haystack(), span) { | 74 | 0 | None => return Ok(mat), | 75 | 0 | Some(ref span) => { | 76 | 0 | at = span.start; | 77 | 0 | if !universal_start { | 78 | 0 | sid = prefilter_restart(dfa, &input, at)?; | 79 | 0 | } | 80 | | } | 81 | | } | 82 | 25.2k | } | 83 | 92.1k | while 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. | 97 | | let mut prev_sid; | 98 | 80.6k | while at < input.end() { | 99 | 80.6k | prev_sid = unsafe { next_unchecked!(sid, at) }; | 100 | 80.6k | if dfa.is_special_state(prev_sid) || at + 3 >= input.end() { | 101 | 80.3k | core::mem::swap(&mut prev_sid, &mut sid); | 102 | 80.3k | break; | 103 | 321 | } | 104 | 321 | at += 1; | 105 | | | 106 | 321 | sid = unsafe { next_unchecked!(prev_sid, at) }; | 107 | 321 | if dfa.is_special_state(sid) { | 108 | 66 | break; | 109 | 255 | } | 110 | 255 | at += 1; | 111 | | | 112 | 255 | prev_sid = unsafe { next_unchecked!(sid, at) }; | 113 | 255 | if dfa.is_special_state(prev_sid) { | 114 | 6 | core::mem::swap(&mut prev_sid, &mut sid); | 115 | 6 | break; | 116 | 249 | } | 117 | 249 | at += 1; | 118 | | | 119 | 249 | sid = unsafe { next_unchecked!(prev_sid, at) }; | 120 | 249 | if dfa.is_special_state(sid) { | 121 | 4 | break; | 122 | 245 | } | 123 | 245 | at += 1; | 124 | | } | 125 | 80.4k | if dfa.is_special_state(sid) { | 126 | 80.3k | if dfa.is_start_state(sid) { | 127 | 6.16k | if 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. | 132 | 0 | if mat.is_none() { | 133 | 0 | let span = Span::from(at..input.end()); | 134 | 0 | match pre.find(input.haystack(), span) { | 135 | 0 | None => return Ok(mat), | 136 | 0 | Some(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. | 146 | 0 | if span.start > at { | 147 | 0 | at = span.start; | 148 | 0 | if !universal_start { | 149 | 0 | sid = prefilter_restart( | 150 | 0 | dfa, &input, at, | 151 | 0 | )?; | 152 | 0 | } | 153 | 0 | continue; | 154 | 0 | } | 155 | | } | 156 | | } | 157 | 0 | } | 158 | 6.16k | } else if dfa.is_accel_state(sid) { | 159 | 5.51k | let needles = dfa.accelerator(sid); | 160 | 5.51k | at = accel::find_fwd(needles, input.haystack(), at + 1) | 161 | 5.51k | .unwrap_or(input.end()); | 162 | 5.51k | continue; | 163 | 658 | } | 164 | 74.1k | } else if dfa.is_match_state(sid) { | 165 | 24.0k | let pattern = dfa.match_pattern(sid, 0); | 166 | 24.0k | mat = Some(HalfMatch::new(pattern, at)); | 167 | 24.0k | if earliest { | 168 | 0 | return Ok(mat); | 169 | 24.0k | } | 170 | 24.0k | if dfa.is_accel_state(sid) { | 171 | 0 | let needles = dfa.accelerator(sid); | 172 | 0 | at = accel::find_fwd(needles, input.haystack(), at + 1) | 173 | 0 | .unwrap_or(input.end()); | 174 | 0 | continue; | 175 | 24.0k | } | 176 | 50.1k | } else if dfa.is_accel_state(sid) { | 177 | 36.5k | let needs = dfa.accelerator(sid); | 178 | 36.5k | at = accel::find_fwd(needs, input.haystack(), at + 1) | 179 | 36.5k | .unwrap_or(input.end()); | 180 | 36.5k | continue; | 181 | 13.5k | } else if dfa.is_dead_state(sid) { | 182 | 13.5k | return 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. | 187 | 18 | return Err(MatchError::quit(input.haystack()[at], at)); | 188 | | } | 189 | 79 | } | 190 | 24.7k | at += 1; | 191 | | } | 192 | 11.6k | eoi_fwd(dfa, input, &mut sid, &mut mat)?; | 193 | 11.6k | Ok(mat) | 194 | 25.5k | } |
|
195 | | |
196 | | #[inline(never)] |
197 | 5.21k | pub fn find_rev<A: Automaton + ?Sized>( |
198 | 5.21k | dfa: &A, |
199 | 5.21k | input: &Input<'_>, |
200 | 5.21k | ) -> Result<Option<HalfMatch>, MatchError> { |
201 | 5.21k | if input.is_done() { |
202 | 0 | return Ok(None); |
203 | 5.21k | } |
204 | 5.21k | if input.get_earliest() { |
205 | 755 | find_rev_imp(dfa, input, true) |
206 | | } else { |
207 | 4.45k | find_rev_imp(dfa, input, false) |
208 | | } |
209 | 5.21k | } |
210 | | |
211 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
212 | 5.21k | fn find_rev_imp<A: Automaton + ?Sized>( |
213 | 5.21k | dfa: &A, |
214 | 5.21k | input: &Input<'_>, |
215 | 5.21k | earliest: bool, |
216 | 5.21k | ) -> Result<Option<HalfMatch>, MatchError> { |
217 | 5.21k | let mut mat = None; |
218 | 5.21k | let 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. |
225 | 5.21k | if input.start() == input.end() { |
226 | 256 | eoi_rev(dfa, input, &mut sid, &mut mat)?; |
227 | 256 | return Ok(mat); |
228 | 4.95k | } |
229 | | |
230 | 4.95k | let mut at = input.end() - 1; |
231 | | macro_rules! next_unchecked { |
232 | | ($sid:expr, $at:expr) => {{ |
233 | | let byte = *input.haystack().get_unchecked($at); |
234 | | dfa.next_state_unchecked($sid, byte) |
235 | | }}; |
236 | | } |
237 | | loop { |
238 | | // SAFETY: See comments in 'find_fwd' for a safety argument. |
239 | | let mut prev_sid; |
240 | 152k | while at >= input.start() { |
241 | 152k | prev_sid = unsafe { next_unchecked!(sid, at) }; |
242 | 152k | if dfa.is_special_state(prev_sid) |
243 | 50.6k | || at <= input.start().saturating_add(3) |
244 | | { |
245 | 104k | core::mem::swap(&mut prev_sid, &mut sid); |
246 | 104k | break; |
247 | 48.3k | } |
248 | 48.3k | at -= 1; |
249 | | |
250 | 48.3k | sid = unsafe { next_unchecked!(prev_sid, at) }; |
251 | 48.3k | if dfa.is_special_state(sid) { |
252 | 14.9k | break; |
253 | 33.4k | } |
254 | 33.4k | at -= 1; |
255 | | |
256 | 33.4k | prev_sid = unsafe { next_unchecked!(sid, at) }; |
257 | 33.4k | if dfa.is_special_state(prev_sid) { |
258 | 10.0k | core::mem::swap(&mut prev_sid, &mut sid); |
259 | 10.0k | break; |
260 | 23.3k | } |
261 | 23.3k | at -= 1; |
262 | | |
263 | 23.3k | sid = unsafe { next_unchecked!(prev_sid, at) }; |
264 | 23.3k | if dfa.is_special_state(sid) { |
265 | 9.27k | break; |
266 | 14.1k | } |
267 | 14.1k | at -= 1; |
268 | | } |
269 | 138k | if dfa.is_special_state(sid) { |
270 | 136k | if dfa.is_start_state(sid) { |
271 | 0 | if dfa.is_accel_state(sid) { |
272 | 0 | let needles = dfa.accelerator(sid); |
273 | 0 | at = accel::find_rev(needles, input.haystack(), at) |
274 | 0 | .map(|i| i + 1) |
275 | 0 | .unwrap_or(input.start()); |
276 | 0 | } |
277 | 136k | } else if dfa.is_match_state(sid) { |
278 | 99.5k | let 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. |
282 | 99.5k | mat = Some(HalfMatch::new(pattern, at + 1)); |
283 | 99.5k | if earliest { |
284 | 205 | return Ok(mat); |
285 | 99.3k | } |
286 | 99.3k | if dfa.is_accel_state(sid) { |
287 | 5.32k | let needles = dfa.accelerator(sid); |
288 | 5.32k | at = accel::find_rev(needles, input.haystack(), at) |
289 | 5.32k | .map(|i| i + 1) |
290 | 5.32k | .unwrap_or(input.start()); |
291 | 93.9k | } |
292 | 36.7k | } else if dfa.is_accel_state(sid) { |
293 | 34.7k | let 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. |
301 | 34.7k | at = accel::find_rev(needles, input.haystack(), at) |
302 | 34.7k | .map(|i| i + 1) |
303 | 34.7k | .unwrap_or(input.start()); |
304 | 2.06k | } else if dfa.is_dead_state(sid) { |
305 | 1.37k | return Ok(mat); |
306 | | } else { |
307 | 695 | return Err(MatchError::quit(input.haystack()[at], at)); |
308 | | } |
309 | 2.37k | } |
310 | 136k | if at == input.start() { |
311 | 2.68k | break; |
312 | 133k | } |
313 | 133k | at -= 1; |
314 | | } |
315 | 2.68k | eoi_rev(dfa, input, &mut sid, &mut mat)?; |
316 | 2.68k | Ok(mat) |
317 | 5.21k | } |
318 | | |
319 | | #[inline(never)] |
320 | 0 | pub fn find_overlapping_fwd<A: Automaton + ?Sized>( |
321 | 0 | dfa: &A, |
322 | 0 | input: &Input<'_>, |
323 | 0 | state: &mut OverlappingState, |
324 | 0 | ) -> Result<(), MatchError> { |
325 | 0 | state.mat = None; |
326 | 0 | if input.is_done() { |
327 | 0 | return Ok(()); |
328 | 0 | } |
329 | 0 | let pre = if input.get_anchored().is_anchored() { |
330 | 0 | None |
331 | | } else { |
332 | 0 | dfa.get_prefilter() |
333 | | }; |
334 | 0 | if pre.is_some() { |
335 | 0 | find_overlapping_fwd_imp(dfa, input, pre, state) |
336 | | } else { |
337 | 0 | find_overlapping_fwd_imp(dfa, input, None, state) |
338 | | } |
339 | 0 | } |
340 | | |
341 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
342 | 0 | fn find_overlapping_fwd_imp<A: Automaton + ?Sized>( |
343 | 0 | dfa: &A, |
344 | 0 | input: &Input<'_>, |
345 | 0 | pre: Option<&'_ Prefilter>, |
346 | 0 | state: &mut OverlappingState, |
347 | 0 | ) -> Result<(), MatchError> { |
348 | | // See 'prefilter_restart' docs for explanation. |
349 | 0 | let universal_start = dfa.universal_start_state(Anchored::No).is_some(); |
350 | 0 | let mut sid = match state.id { |
351 | | None => { |
352 | 0 | state.at = input.start(); |
353 | 0 | init_fwd(dfa, input)? |
354 | | } |
355 | 0 | Some(sid) => { |
356 | 0 | if let Some(match_index) = state.next_match_index { |
357 | 0 | let match_len = dfa.match_len(sid); |
358 | 0 | if match_index < match_len { |
359 | 0 | state.next_match_index = Some(match_index + 1); |
360 | 0 | let pattern = dfa.match_pattern(sid, match_index); |
361 | 0 | state.mat = Some(HalfMatch::new(pattern, state.at)); |
362 | 0 | return Ok(()); |
363 | 0 | } |
364 | 0 | } |
365 | | // Once we've reported all matches at a given position, we need to |
366 | | // advance the search to the next position. |
367 | 0 | state.at += 1; |
368 | 0 | if state.at > input.end() { |
369 | 0 | return Ok(()); |
370 | 0 | } |
371 | 0 | sid |
372 | | } |
373 | | }; |
374 | | |
375 | | // 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. |
379 | 0 | while state.at < input.end() { |
380 | 0 | sid = dfa.next_state(sid, input.haystack()[state.at]); |
381 | 0 | if dfa.is_special_state(sid) { |
382 | 0 | state.id = Some(sid); |
383 | 0 | if dfa.is_start_state(sid) { |
384 | 0 | if let Some(ref pre) = pre { |
385 | 0 | let span = Span::from(state.at..input.end()); |
386 | 0 | match pre.find(input.haystack(), span) { |
387 | 0 | None => return Ok(()), |
388 | 0 | Some(ref span) => { |
389 | 0 | if span.start > state.at { |
390 | 0 | state.at = span.start; |
391 | 0 | if !universal_start { |
392 | 0 | sid = prefilter_restart( |
393 | 0 | dfa, &input, state.at, |
394 | 0 | )?; |
395 | 0 | } |
396 | 0 | continue; |
397 | 0 | } |
398 | | } |
399 | | } |
400 | 0 | } else if dfa.is_accel_state(sid) { |
401 | 0 | let needles = dfa.accelerator(sid); |
402 | 0 | state.at = accel::find_fwd( |
403 | 0 | needles, |
404 | 0 | input.haystack(), |
405 | 0 | state.at + 1, |
406 | 0 | ) |
407 | 0 | .unwrap_or(input.end()); |
408 | 0 | continue; |
409 | 0 | } |
410 | 0 | } else if dfa.is_match_state(sid) { |
411 | 0 | state.next_match_index = Some(1); |
412 | 0 | let pattern = dfa.match_pattern(sid, 0); |
413 | 0 | state.mat = Some(HalfMatch::new(pattern, state.at)); |
414 | 0 | return Ok(()); |
415 | 0 | } else if dfa.is_accel_state(sid) { |
416 | 0 | let 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. |
424 | 0 | state.at = |
425 | 0 | accel::find_fwd(needs, input.haystack(), state.at + 1) |
426 | 0 | .unwrap_or(input.end()); |
427 | 0 | continue; |
428 | 0 | } else if dfa.is_dead_state(sid) { |
429 | 0 | return Ok(()); |
430 | | } else { |
431 | 0 | return Err(MatchError::quit( |
432 | 0 | input.haystack()[state.at], |
433 | 0 | state.at, |
434 | 0 | )); |
435 | | } |
436 | 0 | } |
437 | 0 | state.at += 1; |
438 | | } |
439 | | |
440 | 0 | let result = eoi_fwd(dfa, input, &mut sid, &mut state.mat); |
441 | 0 | state.id = Some(sid); |
442 | 0 | if state.mat.is_some() { |
443 | 0 | // '1' is always correct here since if we get to this point, this |
444 | 0 | // always corresponds to the first (index '0') match discovered at |
445 | 0 | // this position. So the next match to report at this position (if |
446 | 0 | // it exists) is at index '1'. |
447 | 0 | state.next_match_index = Some(1); |
448 | 0 | } |
449 | 0 | result |
450 | 0 | } |
451 | | |
452 | | #[inline(never)] |
453 | | pub(crate) fn find_overlapping_rev<A: Automaton + ?Sized>( |
454 | | dfa: &A, |
455 | | input: &Input<'_>, |
456 | | state: &mut OverlappingState, |
457 | | ) -> Result<(), MatchError> { |
458 | | state.mat = None; |
459 | | if input.is_done() { |
460 | | return Ok(()); |
461 | | } |
462 | | let mut sid = match state.id { |
463 | | None => { |
464 | | let sid = init_rev(dfa, input)?; |
465 | | state.id = Some(sid); |
466 | | if input.start() == input.end() { |
467 | | state.rev_eoi = true; |
468 | | } else { |
469 | | state.at = input.end() - 1; |
470 | | } |
471 | | sid |
472 | | } |
473 | | Some(sid) => { |
474 | | if let Some(match_index) = state.next_match_index { |
475 | | let match_len = dfa.match_len(sid); |
476 | | if match_index < match_len { |
477 | | state.next_match_index = Some(match_index + 1); |
478 | | let pattern = dfa.match_pattern(sid, match_index); |
479 | | state.mat = Some(HalfMatch::new(pattern, state.at)); |
480 | | return 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. |
487 | | if state.rev_eoi { |
488 | | return 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. |
493 | | state.rev_eoi = true; |
494 | | } else { |
495 | | // We haven't hit the end of the search yet, so move on. |
496 | | state.at -= 1; |
497 | | } |
498 | | sid |
499 | | } |
500 | | }; |
501 | | while !state.rev_eoi { |
502 | | sid = dfa.next_state(sid, input.haystack()[state.at]); |
503 | | if dfa.is_special_state(sid) { |
504 | | state.id = Some(sid); |
505 | | if dfa.is_start_state(sid) { |
506 | | if dfa.is_accel_state(sid) { |
507 | | let 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); |
515 | | let pattern = dfa.match_pattern(sid, 0); |
516 | | state.mat = Some(HalfMatch::new(pattern, state.at + 1)); |
517 | | return Ok(()); |
518 | | } else if dfa.is_accel_state(sid) { |
519 | | let 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. |
527 | | state.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) { |
532 | | return Ok(()); |
533 | | } else { |
534 | | return Err(MatchError::quit( |
535 | | input.haystack()[state.at], |
536 | | state.at, |
537 | | )); |
538 | | } |
539 | | } |
540 | | if state.at == input.start() { |
541 | | break; |
542 | | } |
543 | | state.at -= 1; |
544 | | } |
545 | | |
546 | | let result = eoi_rev(dfa, input, &mut sid, &mut state.mat); |
547 | | state.rev_eoi = true; |
548 | | state.id = Some(sid); |
549 | | if 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'. |
554 | | state.next_match_index = Some(1); |
555 | | } |
556 | | result |
557 | | } |
558 | | |
559 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
560 | 161k | fn init_fwd<A: Automaton + ?Sized>( |
561 | 161k | dfa: &A, |
562 | 161k | input: &Input<'_>, |
563 | 161k | ) -> Result<StateID, MatchError> { |
564 | 161k | let sid = dfa.start_state_forward(input)?; |
565 | | // Start states can never be match states, since all matches are delayed |
566 | | // by 1 byte. |
567 | 160k | debug_assert!(!dfa.is_match_state(sid)); |
568 | 160k | Ok(sid) |
569 | 161k | } Unexecuted instantiation: regex_automata::dfa::search::init_fwd::<regex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> regex_automata::dfa::search::init_fwd::<®ex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> Line | Count | Source | 560 | 117k | fn init_fwd<A: Automaton + ?Sized>( | 561 | 117k | dfa: &A, | 562 | 117k | input: &Input<'_>, | 563 | 117k | ) -> Result<StateID, MatchError> { | 564 | 117k | let sid = dfa.start_state_forward(input)?; | 565 | | // Start states can never be match states, since all matches are delayed | 566 | | // by 1 byte. | 567 | 116k | debug_assert!(!dfa.is_match_state(sid)); | 568 | 116k | Ok(sid) | 569 | 117k | } |
regex_automata::dfa::search::init_fwd::<®ex_automata::dfa::dense::DFA<&[u32]>> Line | Count | Source | 560 | 18.7k | fn init_fwd<A: Automaton + ?Sized>( | 561 | 18.7k | dfa: &A, | 562 | 18.7k | input: &Input<'_>, | 563 | 18.7k | ) -> Result<StateID, MatchError> { | 564 | 18.7k | let sid = dfa.start_state_forward(input)?; | 565 | | // Start states can never be match states, since all matches are delayed | 566 | | // by 1 byte. | 567 | 18.3k | debug_assert!(!dfa.is_match_state(sid)); | 568 | 18.3k | Ok(sid) | 569 | 18.7k | } |
regex_automata::dfa::search::init_fwd::<®ex_automata::dfa::sparse::DFA<&[u8]>> Line | Count | Source | 560 | 25.5k | fn init_fwd<A: Automaton + ?Sized>( | 561 | 25.5k | dfa: &A, | 562 | 25.5k | input: &Input<'_>, | 563 | 25.5k | ) -> Result<StateID, MatchError> { | 564 | 25.5k | let sid = dfa.start_state_forward(input)?; | 565 | | // Start states can never be match states, since all matches are delayed | 566 | | // by 1 byte. | 567 | 25.2k | debug_assert!(!dfa.is_match_state(sid)); | 568 | 25.2k | Ok(sid) | 569 | 25.5k | } |
|
570 | | |
571 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
572 | 5.21k | fn init_rev<A: Automaton + ?Sized>( |
573 | 5.21k | dfa: &A, |
574 | 5.21k | input: &Input<'_>, |
575 | 5.21k | ) -> Result<StateID, MatchError> { |
576 | 5.21k | let sid = dfa.start_state_reverse(input)?; |
577 | | // Start states can never be match states, since all matches are delayed |
578 | | // by 1 byte. |
579 | 5.21k | debug_assert!(!dfa.is_match_state(sid)); |
580 | 5.21k | Ok(sid) |
581 | 5.21k | } |
582 | | |
583 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
584 | 53.4k | fn eoi_fwd<A: Automaton + ?Sized>( |
585 | 53.4k | dfa: &A, |
586 | 53.4k | input: &Input<'_>, |
587 | 53.4k | sid: &mut StateID, |
588 | 53.4k | mat: &mut Option<HalfMatch>, |
589 | 53.4k | ) -> Result<(), MatchError> { |
590 | 53.4k | let sp = input.get_span(); |
591 | 53.4k | match input.haystack().get(sp.end) { |
592 | 0 | Some(&b) => { |
593 | 0 | *sid = dfa.next_state(*sid, b); |
594 | 0 | if dfa.is_match_state(*sid) { |
595 | 0 | let pattern = dfa.match_pattern(*sid, 0); |
596 | 0 | *mat = Some(HalfMatch::new(pattern, sp.end)); |
597 | 0 | } else if dfa.is_quit_state(*sid) { |
598 | 0 | return Err(MatchError::quit(b, sp.end)); |
599 | 0 | } |
600 | | } |
601 | | None => { |
602 | 53.4k | *sid = dfa.next_eoi_state(*sid); |
603 | 53.4k | if dfa.is_match_state(*sid) { |
604 | 11.3k | let pattern = dfa.match_pattern(*sid, 0); |
605 | 11.3k | *mat = Some(HalfMatch::new(pattern, input.haystack().len())); |
606 | 42.0k | } |
607 | | } |
608 | | } |
609 | 53.4k | Ok(()) |
610 | 53.4k | } Unexecuted instantiation: regex_automata::dfa::search::eoi_fwd::<regex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> regex_automata::dfa::search::eoi_fwd::<®ex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> Line | Count | Source | 584 | 24.5k | fn eoi_fwd<A: Automaton + ?Sized>( | 585 | 24.5k | dfa: &A, | 586 | 24.5k | input: &Input<'_>, | 587 | 24.5k | sid: &mut StateID, | 588 | 24.5k | mat: &mut Option<HalfMatch>, | 589 | 24.5k | ) -> Result<(), MatchError> { | 590 | 24.5k | let sp = input.get_span(); | 591 | 24.5k | match input.haystack().get(sp.end) { | 592 | 0 | Some(&b) => { | 593 | 0 | *sid = dfa.next_state(*sid, b); | 594 | 0 | if dfa.is_match_state(*sid) { | 595 | 0 | let pattern = dfa.match_pattern(*sid, 0); | 596 | 0 | *mat = Some(HalfMatch::new(pattern, sp.end)); | 597 | 0 | } else if dfa.is_quit_state(*sid) { | 598 | 0 | return Err(MatchError::quit(b, sp.end)); | 599 | 0 | } | 600 | | } | 601 | | None => { | 602 | 24.5k | *sid = dfa.next_eoi_state(*sid); | 603 | 24.5k | if dfa.is_match_state(*sid) { | 604 | 11.1k | let pattern = dfa.match_pattern(*sid, 0); | 605 | 11.1k | *mat = Some(HalfMatch::new(pattern, input.haystack().len())); | 606 | 13.3k | } | 607 | | } | 608 | | } | 609 | 24.5k | Ok(()) | 610 | 24.5k | } |
regex_automata::dfa::search::eoi_fwd::<®ex_automata::dfa::dense::DFA<&[u32]>> Line | Count | Source | 584 | 17.2k | fn eoi_fwd<A: Automaton + ?Sized>( | 585 | 17.2k | dfa: &A, | 586 | 17.2k | input: &Input<'_>, | 587 | 17.2k | sid: &mut StateID, | 588 | 17.2k | mat: &mut Option<HalfMatch>, | 589 | 17.2k | ) -> Result<(), MatchError> { | 590 | 17.2k | let sp = input.get_span(); | 591 | 17.2k | match input.haystack().get(sp.end) { | 592 | 0 | Some(&b) => { | 593 | 0 | *sid = dfa.next_state(*sid, b); | 594 | 0 | if dfa.is_match_state(*sid) { | 595 | 0 | let pattern = dfa.match_pattern(*sid, 0); | 596 | 0 | *mat = Some(HalfMatch::new(pattern, sp.end)); | 597 | 0 | } else if dfa.is_quit_state(*sid) { | 598 | 0 | return Err(MatchError::quit(b, sp.end)); | 599 | 0 | } | 600 | | } | 601 | | None => { | 602 | 17.2k | *sid = dfa.next_eoi_state(*sid); | 603 | 17.2k | if dfa.is_match_state(*sid) { | 604 | 62 | let pattern = dfa.match_pattern(*sid, 0); | 605 | 62 | *mat = Some(HalfMatch::new(pattern, input.haystack().len())); | 606 | 17.2k | } | 607 | | } | 608 | | } | 609 | 17.2k | Ok(()) | 610 | 17.2k | } |
regex_automata::dfa::search::eoi_fwd::<®ex_automata::dfa::sparse::DFA<&[u8]>> Line | Count | Source | 584 | 11.6k | fn eoi_fwd<A: Automaton + ?Sized>( | 585 | 11.6k | dfa: &A, | 586 | 11.6k | input: &Input<'_>, | 587 | 11.6k | sid: &mut StateID, | 588 | 11.6k | mat: &mut Option<HalfMatch>, | 589 | 11.6k | ) -> Result<(), MatchError> { | 590 | 11.6k | let sp = input.get_span(); | 591 | 11.6k | match input.haystack().get(sp.end) { | 592 | 0 | Some(&b) => { | 593 | 0 | *sid = dfa.next_state(*sid, b); | 594 | 0 | if dfa.is_match_state(*sid) { | 595 | 0 | let pattern = dfa.match_pattern(*sid, 0); | 596 | 0 | *mat = Some(HalfMatch::new(pattern, sp.end)); | 597 | 0 | } else if dfa.is_quit_state(*sid) { | 598 | 0 | return Err(MatchError::quit(b, sp.end)); | 599 | 0 | } | 600 | | } | 601 | | None => { | 602 | 11.6k | *sid = dfa.next_eoi_state(*sid); | 603 | 11.6k | if dfa.is_match_state(*sid) { | 604 | 112 | let pattern = dfa.match_pattern(*sid, 0); | 605 | 112 | *mat = Some(HalfMatch::new(pattern, input.haystack().len())); | 606 | 11.5k | } | 607 | | } | 608 | | } | 609 | 11.6k | Ok(()) | 610 | 11.6k | } |
|
611 | | |
612 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
613 | 2.93k | fn eoi_rev<A: Automaton + ?Sized>( |
614 | 2.93k | dfa: &A, |
615 | 2.93k | input: &Input<'_>, |
616 | 2.93k | sid: &mut StateID, |
617 | 2.93k | mat: &mut Option<HalfMatch>, |
618 | 2.93k | ) -> Result<(), MatchError> { |
619 | 2.93k | let sp = input.get_span(); |
620 | 2.93k | if sp.start > 0 { |
621 | 0 | let byte = input.haystack()[sp.start - 1]; |
622 | 0 | *sid = dfa.next_state(*sid, byte); |
623 | 0 | if dfa.is_match_state(*sid) { |
624 | 0 | let pattern = dfa.match_pattern(*sid, 0); |
625 | 0 | *mat = Some(HalfMatch::new(pattern, sp.start)); |
626 | 0 | } else if dfa.is_quit_state(*sid) { |
627 | 0 | return Err(MatchError::quit(byte, sp.start - 1)); |
628 | 0 | } |
629 | | } else { |
630 | 2.93k | *sid = dfa.next_eoi_state(*sid); |
631 | 2.93k | if dfa.is_match_state(*sid) { |
632 | 1.68k | let pattern = dfa.match_pattern(*sid, 0); |
633 | 1.68k | *mat = Some(HalfMatch::new(pattern, 0)); |
634 | 1.68k | } |
635 | | } |
636 | 2.93k | Ok(()) |
637 | 2.93k | } |
638 | | |
639 | | /// 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))] |
644 | 34.0k | fn prefilter_restart<A: Automaton + ?Sized>( |
645 | 34.0k | dfa: &A, |
646 | 34.0k | input: &Input<'_>, |
647 | 34.0k | at: usize, |
648 | 34.0k | ) -> Result<StateID, MatchError> { |
649 | 34.0k | let mut input = input.clone(); |
650 | 34.0k | input.set_start(at); |
651 | 34.0k | init_fwd(dfa, &input) |
652 | 34.0k | } Unexecuted instantiation: regex_automata::dfa::search::prefilter_restart::<regex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> regex_automata::dfa::search::prefilter_restart::<®ex_automata::dfa::dense::DFA<alloc::vec::Vec<u32>>> Line | Count | Source | 644 | 34.0k | fn prefilter_restart<A: Automaton + ?Sized>( | 645 | 34.0k | dfa: &A, | 646 | 34.0k | input: &Input<'_>, | 647 | 34.0k | at: usize, | 648 | 34.0k | ) -> Result<StateID, MatchError> { | 649 | 34.0k | let mut input = input.clone(); | 650 | 34.0k | input.set_start(at); | 651 | 34.0k | init_fwd(dfa, &input) | 652 | 34.0k | } |
Unexecuted instantiation: regex_automata::dfa::search::prefilter_restart::<®ex_automata::dfa::dense::DFA<&[u32]>> Unexecuted instantiation: regex_automata::dfa::search::prefilter_restart::<®ex_automata::dfa::sparse::DFA<&[u8]>> |