use core::ops::AddAssign;
use crate::internals::macros::invariant;
pub const ROLLING_WINDOW: usize = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RollingHash {
index: u32,
h1: u32,
h2: u32,
h3: u32,
window: [u8; ROLLING_WINDOW],
}
impl RollingHash {
pub const WINDOW_SIZE: usize = ROLLING_WINDOW;
const H3_LSHIFT: usize = 5;
pub fn new() -> Self {
RollingHash {
index: 0,
h1: 0,
h2: 0,
h3: 0,
window: [0; ROLLING_WINDOW],
}
}
#[inline]
pub fn update_by_byte(&mut self, ch: u8) -> &mut Self {
invariant!((self.index as usize) < Self::WINDOW_SIZE);
self.h2 = self.h2.wrapping_sub(self.h1);
self.h2 = self
.h2
.wrapping_add(u32::wrapping_mul(ROLLING_WINDOW as u32, ch as u32));
self.h1 = self.h1.wrapping_add(ch as u32);
self.h1 = self
.h1
.wrapping_sub(self.window[self.index as usize] as u32); self.window[self.index as usize] = ch; self.index += 1;
if self.index as usize == ROLLING_WINDOW {
self.index = 0;
}
self.h3 <<= Self::H3_LSHIFT;
self.h3 ^= ch as u32;
self
}
pub fn update_by_iter(&mut self, iter: impl Iterator<Item = u8>) -> &mut Self {
for ch in iter {
self.update_by_byte(ch);
}
self
}
pub fn update(&mut self, buf: &[u8]) -> &mut Self {
for &ch in buf.iter() {
self.update_by_byte(ch);
}
self
}
#[inline]
pub fn value(&self) -> u32 {
self.h1.wrapping_add(self.h2).wrapping_add(self.h3)
}
}
impl AddAssign<&[u8]> for RollingHash {
#[inline(always)]
fn add_assign(&mut self, buffer: &[u8]) {
self.update(buffer);
}
}
impl<const N: usize> AddAssign<&[u8; N]> for RollingHash {
#[inline(always)]
fn add_assign(&mut self, buffer: &[u8; N]) {
self.update(&buffer[..]);
}
}
impl AddAssign<u8> for RollingHash {
#[inline(always)]
fn add_assign(&mut self, byte: u8) {
self.update_by_byte(byte);
}
}
impl Default for RollingHash {
fn default() -> Self {
Self::new()
}
}
#[doc(hidden)]
mod const_asserts {
use static_assertions::const_assert_eq;
use super::*;
const_assert_eq!(ROLLING_WINDOW, 7);
const_assert_eq!(ROLLING_WINDOW, RollingHash::WINDOW_SIZE);
#[cfg(test)]
#[test]
fn rolling_hash_h3_shift_amount() {
assert!(RollingHash::WINDOW_SIZE
.checked_mul(RollingHash::H3_LSHIFT)
.and_then(|x| u32::try_from(x).ok())
.map(|x| x >= u32::BITS)
.unwrap_or(false));
}
}
pub(crate) mod test_utils;
mod tests;