use digest::consts::U4;
use digest::{FixedOutput, HashMarker, Output, OutputSizeUser, Update};
pub struct OneAtATime {
state: u32,
}
impl HashMarker for OneAtATime {}
impl OutputSizeUser for OneAtATime {
type OutputSize = U4;
}
impl FixedOutput for OneAtATime {
fn finalize_into(self, out: &mut Output<Self>) {
let result = Self::finish(self.state);
out.copy_from_slice(&result.to_le_bytes());
}
}
impl Update for OneAtATime {
fn update(&mut self, data: &[u8]) {
self.state = Self::partial_hash(self.state, data)
}
}
impl OneAtATime {
pub fn new() -> Self {
Self { state: 0 }
}
fn partial_hash(mut state: u32, data: &[u8]) -> u32 {
for &b in data {
state = state.wrapping_add(b as u32);
state = state.wrapping_add(state << 10);
state ^= state >> 6;
}
state
}
fn finish(mut state: u32) -> u32 {
state = state.wrapping_add(state << 3);
state ^= state >> 11;
state = state.wrapping_add(state << 15);
state
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple() {
let mut hasher = OneAtATime::new();
hasher.update(b"kt1_lod_1_2_5_6_17");
let hash = hasher.finalize_fixed();
let val = u32::from_le_bytes(hash[..].try_into().unwrap());
assert_eq!(val, 373823972);
}
}