Find Substrings with Rabin-Karp Hashing

Search text for every occurrence of a pattern with a rolling Rabin-Karp hash and verify collisions by direct comparison.

RustData Integrity

Code

snippet
fn find_all(haystack: &str, needle: &str) -> Vec<usize> {
    let hay: Vec<char> = haystack.chars().collect();
    let pattern: Vec<char> = needle.chars().collect();
    if pattern.is_empty() || pattern.len() > hay.len() {
        return Vec::new();
    }

    const BASE: u64 = 257;
    let mut highest = 1u64;
    for _ in 1..pattern.len() {
        highest = highest.wrapping_mul(BASE);
    }

    let mut pattern_hash = 0u64;
    let mut window_hash = 0u64;
    for index in 0..pattern.len() {
        pattern_hash = pattern_hash
            .wrapping_mul(BASE)
            .wrapping_add(pattern[index] as u64);
        window_hash = window_hash
            .wrapping_mul(BASE)
            .wrapping_add(hay[index] as u64);
    }

    let mut matches = Vec::new();
    for start in 0..=hay.len() - pattern.len() {
        if window_hash == pattern_hash && hay[start..start + pattern.len()] == pattern[..] {
            matches.push(start);
        }
        if start + pattern.len() < hay.len() {
            window_hash = window_hash
                .wrapping_sub((hay[start] as u64).wrapping_mul(highest));
            window_hash = window_hash
                .wrapping_mul(BASE)
                .wrapping_add(hay[start + pattern.len()] as u64);
        }
    }
    matches
}

fn main() {
    assert_eq!(find_all("abracadabra", "abra"), vec![0, 7]);
    assert_eq!(find_all("aaaa", "aa"), vec![0, 1, 2]);
    assert_eq!(find_all("short", "longer pattern"), Vec::<usize>::new());
    assert_eq!(find_all("anything", ""), Vec::<usize>::new());
    println!("{:?}", find_all("abracadabra", "abra"));
}