Skip to main content

cheetah_string/
search.rs

1pub(crate) fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
2    if needle.is_empty() {
3        return Some(0);
4    }
5
6    if needle.len() == 1 {
7        return memchr::memchr(needle[0], haystack);
8    }
9
10    memchr::memmem::find(haystack, needle)
11}
12
13pub(crate) fn rfind_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
14    if needle.is_empty() {
15        return Some(haystack.len());
16    }
17
18    if needle.len() == 1 {
19        return memchr::memrchr(needle[0], haystack);
20    }
21
22    memchr::memmem::rfind(haystack, needle)
23}
24
25/// Reusable substring finder for repeated searches with the same needle.
26pub struct CheetahFinder<'a> {
27    needle: &'a str,
28    finder: Option<memchr::memmem::Finder<'a>>,
29}
30
31impl<'a> CheetahFinder<'a> {
32    #[inline]
33    pub fn new(needle: &'a str) -> Self {
34        let finder = (needle.len() > 1).then(|| memchr::memmem::Finder::new(needle.as_bytes()));
35        Self { needle, finder }
36    }
37
38    #[inline]
39    pub fn needle(&self) -> &'a str {
40        self.needle
41    }
42
43    #[inline]
44    pub fn find_in<S>(&self, haystack: &S) -> Option<usize>
45    where
46        S: AsRef<str> + ?Sized,
47    {
48        let haystack = haystack.as_ref().as_bytes();
49
50        if self.needle.is_empty() {
51            return Some(0);
52        }
53
54        if self.needle.len() == 1 {
55            return memchr::memchr(self.needle.as_bytes()[0], haystack);
56        }
57
58        self.finder
59            .as_ref()
60            .and_then(|finder| finder.find(haystack))
61    }
62
63    #[inline]
64    pub fn is_match<S>(&self, haystack: &S) -> bool
65    where
66        S: AsRef<str> + ?Sized,
67    {
68        self.find_in(haystack).is_some()
69    }
70}