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
25pub 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}