JenkHash 0.1.5

Bob Jenkins hash functions for Rust with a digest-compatible API.
Documentation
//! Implementation of the one-at-a-time hash algorithm.
//!
//! The one-at-a-time hash is a simple, fast hash function designed for hash tables.
//! It provides good distribution properties for typical use cases while being
//! computationally efficient.

use digest::consts::U4;
use digest::{FixedOutput, HashMarker, Output, OutputSizeUser, Update};

/// A hasher implementing the one-at-a-time hash algorithm.
///
/// This hash function processes input data incrementally and produces a 32-bit hash value.
///
/// # Examples
///
/// ```
/// use JenkHash::OneAtATime;
/// use digest::{Digest, Update, FixedOutput};
///
/// let mut hasher = OneAtATime::new();
/// hasher.update(b"hello");
/// hasher.update(b" world");
/// let result = hasher.finalize_fixed();
///
/// assert_eq!(result[..], 1045060183_i32.to_le_bytes());
/// ```
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 {
    /// Creates a new `OneAtATime` hasher.
    ///
    /// The hasher starts with a clean state and is ready to process input data.
    ///
    /// # Examples
    ///
    /// ```
    /// use JenkHash::OneAtATime;
    ///
    /// let hasher = OneAtATime::new();
    /// ```
    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);
    }
}