pub(crate) fn is_fast(haystack: &[u8], _needle: &[u8]) -> bool {
haystack.len() < 16
}
pub(crate) fn find(haystack: &[u8], needle: &[u8]) -> Option<usize> {
find_with(&NeedleHash::forward(needle), haystack, needle)
}
pub(crate) fn find_with(
nhash: &NeedleHash,
mut haystack: &[u8],
needle: &[u8],
) -> Option<usize> {
if haystack.len() < needle.len() {
return None;
}
let start = haystack.as_ptr() as usize;
let mut hash = Hash::from_bytes_fwd(&haystack[..needle.len()]);
loop {
if nhash.eq(hash) && is_prefix(haystack, needle) {
return Some(haystack.as_ptr() as usize - start);
}
if needle.len() >= haystack.len() {
return None;
}
hash.roll(&nhash, haystack[0], haystack[needle.len()]);
haystack = &haystack[1..];
}
}
pub(crate) fn rfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
rfind_with(&NeedleHash::reverse(needle), haystack, needle)
}
pub(crate) fn rfind_with(
nhash: &NeedleHash,
mut haystack: &[u8],
needle: &[u8],
) -> Option<usize> {
if haystack.len() < needle.len() {
return None;
}
let mut hash =
Hash::from_bytes_rev(&haystack[haystack.len() - needle.len()..]);
loop {
if nhash.eq(hash) && is_suffix(haystack, needle) {
return Some(haystack.len() - needle.len());
}
if needle.len() >= haystack.len() {
return None;
}
hash.roll(
&nhash,
haystack[haystack.len() - 1],
haystack[haystack.len() - needle.len() - 1],
);
haystack = &haystack[..haystack.len() - 1];
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct NeedleHash {
hash: Hash,
hash_2pow: u32,
}
impl NeedleHash {
pub(crate) fn forward(needle: &[u8]) -> NeedleHash {
let mut nh = NeedleHash { hash: Hash::new(), hash_2pow: 1 };
if needle.is_empty() {
return nh;
}
nh.hash.add(needle[0]);
for &b in needle.iter().skip(1) {
nh.hash.add(b);
nh.hash_2pow = nh.hash_2pow.wrapping_shl(1);
}
nh
}
pub(crate) fn reverse(needle: &[u8]) -> NeedleHash {
let mut nh = NeedleHash { hash: Hash::new(), hash_2pow: 1 };
if needle.is_empty() {
return nh;
}
nh.hash.add(needle[needle.len() - 1]);
for &b in needle.iter().rev().skip(1) {
nh.hash.add(b);
nh.hash_2pow = nh.hash_2pow.wrapping_shl(1);
}
nh
}
fn eq(&self, hash: Hash) -> bool {
self.hash == hash
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(crate) struct Hash(u32);
impl Hash {
pub(crate) fn new() -> Hash {
Hash(0)
}
pub(crate) fn from_bytes_fwd(bytes: &[u8]) -> Hash {
let mut hash = Hash::new();
for &b in bytes {
hash.add(b);
}
hash
}
fn from_bytes_rev(bytes: &[u8]) -> Hash {
let mut hash = Hash::new();
for &b in bytes.iter().rev() {
hash.add(b);
}
hash
}
fn roll(&mut self, nhash: &NeedleHash, old: u8, new: u8) {
self.del(nhash, old);
self.add(new);
}
fn add(&mut self, byte: u8) {
self.0 = self.0.wrapping_shl(1).wrapping_add(byte as u32);
}
fn del(&mut self, nhash: &NeedleHash, byte: u8) {
let factor = nhash.hash_2pow;
self.0 = self.0.wrapping_sub((byte as u32).wrapping_mul(factor));
}
}
#[cold]
#[inline(never)]
fn is_prefix(haystack: &[u8], needle: &[u8]) -> bool {
crate::memmem::util::is_prefix(haystack, needle)
}
#[cold]
#[inline(never)]
fn is_suffix(haystack: &[u8], needle: &[u8]) -> bool {
crate::memmem::util::is_suffix(haystack, needle)
}
#[cfg(test)]
mod simpletests {
define_memmem_simple_tests!(super::find, super::rfind);
}
#[cfg(all(test, feature = "std", not(miri)))]
mod proptests {
define_memmem_quickcheck_tests!(super::find, super::rfind);
}