1use core::arch::x86_64::__m128i;
2
3use crate::memmem::{genericsimd, NeedleInfo};
4
5/// An SSE accelerated vectorized substring search routine that only works on
6/// small needles.
7#[derive(Clone, Copy, Debug)]
8pub(crate) struct Forward(genericsimd::Forward);
9
10impl Forward {
11    /// Create a new "generic simd" forward searcher. If one could not be
12    /// created from the given inputs, then None is returned.
13    pub(crate) fn new(ninfo: &NeedleInfo, needle: &[u8]) -> Option<Forward> {
14        if !cfg!(memchr_runtime_sse2) {
15            return None;
16        }
17        genericsimd::Forward::new(ninfo, needle).map(Forward)
18    }
19
20    /// Returns the minimum length of haystack that is needed for this searcher
21    /// to work. Passing a haystack with a length smaller than this will cause
22    /// `find` to panic.
23    #[inline(always)]
24    pub(crate) fn min_haystack_len(&self) -> usize {
25        self.0.min_haystack_len::<__m128i>()
26    }
27
28    #[inline(always)]
29    pub(crate) fn find(
30        &self,
31        haystack: &[u8],
32        needle: &[u8],
33    ) -> Option<usize> {
34        // SAFETY: sse2 is enabled on all x86_64 targets, so this is always
35        // safe to call.
36        unsafe { self.find_impl(haystack, needle) }
37    }
38
39    /// The implementation of find marked with the appropriate target feature.
40    ///
41    /// # Safety
42    ///
43    /// This is safe to call in all cases since sse2 is guaranteed to be part
44    /// of x86_64. It is marked as unsafe because of the target feature
45    /// attribute.
46    #[target_feature(enable = "sse2")]
47    unsafe fn find_impl(
48        &self,
49        haystack: &[u8],
50        needle: &[u8],
51    ) -> Option<usize> {
52        genericsimd::fwd_find::<__m128i>(&self.0, haystack, needle)
53    }
54}
55
56#[cfg(all(test, feature = "std", not(miri)))]
57mod tests {
58    use crate::memmem::{prefilter::PrefilterState, NeedleInfo};
59
60    fn find(
61        _: &mut PrefilterState,
62        ninfo: &NeedleInfo,
63        haystack: &[u8],
64        needle: &[u8],
65    ) -> Option<usize> {
66        super::Forward::new(ninfo, needle).unwrap().find(haystack, needle)
67    }
68
69    #[test]
70    fn prefilter_permutations() {
71        use crate::memmem::prefilter::tests::PrefilterTest;
72
73        // SAFETY: sse2 is enabled on all x86_64 targets, so this is always
74        // safe to call.
75        unsafe {
76            PrefilterTest::run_all_tests_filter(find, |t| {
77                // This substring searcher only works on certain configs, so
78                // filter our tests such that Forward::new will be guaranteed
79                // to succeed. (And also remove tests with a haystack that is
80                // too small.)
81                let fwd = match super::Forward::new(&t.ninfo, &t.needle) {
82                    None => return false,
83                    Some(fwd) => fwd,
84                };
85                t.haystack.len() >= fwd.min_haystack_len()
86            })
87        }
88    }
89}
90