memchr/arch/aarch64/neon/
packedpair.rs

1/*!
2A 128-bit vector implementation of the "packed pair" SIMD algorithm.
3
4The "packed pair" algorithm is based on the [generic SIMD] algorithm. The main
5difference is that it (by default) uses a background distribution of byte
6frequencies to heuristically select the pair of bytes to search for.
7
8[generic SIMD]: http://0x80.pl/articles/simd-strfind.html#first-and-last
9*/
10
11use core::arch::aarch64::uint8x16_t;
12
13use crate::arch::{all::packedpair::Pair, generic::packedpair};
14
15/// A "packed pair" finder that uses 128-bit vector operations.
16///
17/// This finder picks two bytes that it believes have high predictive power
18/// for indicating an overall match of a needle. Depending on whether
19/// `Finder::find` or `Finder::find_prefilter` is used, it reports offsets
20/// where the needle matches or could match. In the prefilter case, candidates
21/// are reported whenever the [`Pair`] of bytes given matches.
22#[derive(Clone, Copy, Debug)]
23pub struct Finder(packedpair::Finder<uint8x16_t>);
24
25/// A "packed pair" finder that uses 128-bit vector operations.
26///
27/// This finder picks two bytes that it believes have high predictive power
28/// for indicating an overall match of a needle. Depending on whether
29/// `Finder::find` or `Finder::find_prefilter` is used, it reports offsets
30/// where the needle matches or could match. In the prefilter case, candidates
31/// are reported whenever the [`Pair`] of bytes given matches.
32impl Finder {
33    /// Create a new pair searcher. The searcher returned can either report
34    /// exact matches of `needle` or act as a prefilter and report candidate
35    /// positions of `needle`.
36    ///
37    /// If neon is unavailable in the current environment or if a [`Pair`]
38    /// could not be constructed from the needle given, then `None` is
39    /// returned.
40    #[inline]
41    pub fn new(needle: &[u8]) -> Option<Finder> {
42        Finder::with_pair(needle, Pair::new(needle)?)
43    }
44
45    /// Create a new "packed pair" finder using the pair of bytes given.
46    ///
47    /// This constructor permits callers to control precisely which pair of
48    /// bytes is used as a predicate.
49    ///
50    /// If neon is unavailable in the current environment, then `None` is
51    /// returned.
52    #[inline]
53    pub fn with_pair(needle: &[u8], pair: Pair) -> Option<Finder> {
54        if Finder::is_available() {
55            // SAFETY: we check that sse2 is available above. We are also
56            // guaranteed to have needle.len() > 1 because we have a valid
57            // Pair.
58            unsafe { Some(Finder::with_pair_impl(needle, pair)) }
59        } else {
60            None
61        }
62    }
63
64    /// Create a new `Finder` specific to neon vectors and routines.
65    ///
66    /// # Safety
67    ///
68    /// Same as the safety for `packedpair::Finder::new`, and callers must also
69    /// ensure that neon is available.
70    #[target_feature(enable = "neon")]
71    #[inline]
72    unsafe fn with_pair_impl(needle: &[u8], pair: Pair) -> Finder {
73        let finder = packedpair::Finder::<uint8x16_t>::new(needle, pair);
74        Finder(finder)
75    }
76
77    /// Returns true when this implementation is available in the current
78    /// environment.
79    ///
80    /// When this is true, it is guaranteed that [`Finder::with_pair`] will
81    /// return a `Some` value. Similarly, when it is false, it is guaranteed
82    /// that `Finder::with_pair` will return a `None` value. Notice that this
83    /// does not guarantee that [`Finder::new`] will return a `Finder`. Namely,
84    /// even when `Finder::is_available` is true, it is not guaranteed that a
85    /// valid [`Pair`] can be found from the needle given.
86    ///
87    /// Note also that for the lifetime of a single program, if this returns
88    /// true then it will always return true.
89    #[inline]
90    pub fn is_available() -> bool {
91        #[cfg(target_feature = "neon")]
92        {
93            true
94        }
95        #[cfg(not(target_feature = "neon"))]
96        {
97            false
98        }
99    }
100
101    /// Execute a search using neon vectors and routines.
102    ///
103    /// # Panics
104    ///
105    /// When `haystack.len()` is less than [`Finder::min_haystack_len`].
106    #[inline]
107    pub fn find(&self, haystack: &[u8], needle: &[u8]) -> Option<usize> {
108        // SAFETY: Building a `Finder` means it's safe to call 'neon' routines.
109        unsafe { self.find_impl(haystack, needle) }
110    }
111
112    /// Execute a search using neon vectors and routines.
113    ///
114    /// # Panics
115    ///
116    /// When `haystack.len()` is less than [`Finder::min_haystack_len`].
117    #[inline]
118    pub fn find_prefilter(&self, haystack: &[u8]) -> Option<usize> {
119        // SAFETY: Building a `Finder` means it's safe to call 'neon' routines.
120        unsafe { self.find_prefilter_impl(haystack) }
121    }
122
123    /// Execute a search using neon vectors and routines.
124    ///
125    /// # Panics
126    ///
127    /// When `haystack.len()` is less than [`Finder::min_haystack_len`].
128    ///
129    /// # Safety
130    ///
131    /// (The target feature safety obligation is automatically fulfilled by
132    /// virtue of being a method on `Finder`, which can only be constructed
133    /// when it is safe to call `neon` routines.)
134    #[target_feature(enable = "neon")]
135    #[inline]
136    unsafe fn find_impl(
137        &self,
138        haystack: &[u8],
139        needle: &[u8],
140    ) -> Option<usize> {
141        self.0.find(haystack, needle)
142    }
143
144    /// Execute a prefilter search using neon vectors and routines.
145    ///
146    /// # Panics
147    ///
148    /// When `haystack.len()` is less than [`Finder::min_haystack_len`].
149    ///
150    /// # Safety
151    ///
152    /// (The target feature safety obligation is automatically fulfilled by
153    /// virtue of being a method on `Finder`, which can only be constructed
154    /// when it is safe to call `neon` routines.)
155    #[target_feature(enable = "neon")]
156    #[inline]
157    unsafe fn find_prefilter_impl(&self, haystack: &[u8]) -> Option<usize> {
158        self.0.find_prefilter(haystack)
159    }
160
161    /// Returns the pair of offsets (into the needle) used to check as a
162    /// predicate before confirming whether a needle exists at a particular
163    /// position.
164    #[inline]
165    pub fn pair(&self) -> &Pair {
166        self.0.pair()
167    }
168
169    /// Returns the minimum haystack length that this `Finder` can search.
170    ///
171    /// Using a haystack with length smaller than this in a search will result
172    /// in a panic. The reason for this restriction is that this finder is
173    /// meant to be a low-level component that is part of a larger substring
174    /// strategy. In that sense, it avoids trying to handle all cases and
175    /// instead only handles the cases that it can handle very well.
176    #[inline]
177    pub fn min_haystack_len(&self) -> usize {
178        self.0.min_haystack_len()
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    fn find(haystack: &[u8], needle: &[u8]) -> Option<Option<usize>> {
187        let f = Finder::new(needle)?;
188        if haystack.len() < f.min_haystack_len() {
189            return None;
190        }
191        Some(f.find(haystack, needle))
192    }
193
194    define_substring_forward_quickcheck!(find);
195
196    #[test]
197    fn forward_substring() {
198        crate::tests::substring::Runner::new().fwd(find).run()
199    }
200
201    #[test]
202    fn forward_packedpair() {
203        fn find(
204            haystack: &[u8],
205            needle: &[u8],
206            index1: u8,
207            index2: u8,
208        ) -> Option<Option<usize>> {
209            let pair = Pair::with_indices(needle, index1, index2)?;
210            let f = Finder::with_pair(needle, pair)?;
211            if haystack.len() < f.min_haystack_len() {
212                return None;
213            }
214            Some(f.find(haystack, needle))
215        }
216        crate::tests::packedpair::Runner::new().fwd(find).run()
217    }
218
219    #[test]
220    fn forward_packedpair_prefilter() {
221        fn find(
222            haystack: &[u8],
223            needle: &[u8],
224            index1: u8,
225            index2: u8,
226        ) -> Option<Option<usize>> {
227            let pair = Pair::with_indices(needle, index1, index2)?;
228            let f = Finder::with_pair(needle, pair)?;
229            if haystack.len() < f.min_haystack_len() {
230                return None;
231            }
232            Some(f.find_prefilter(haystack))
233        }
234        crate::tests::packedpair::Runner::new().fwd(find).run()
235    }
236}