Coverage Report

Created: 2026-10-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_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::<&regex_automata::dfa::dense::DFA<&[u32]>>
Unexecuted instantiation: regex_automata::dfa::search::prefilter_restart::<&regex_automata::dfa::sparse::DFA<&[u8]>>