JenkHash 0.3.0

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::typenum::U4;
use digest::{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.
/// It implements the [`Hasher`](crate::hash::Hasher) trait, allowing it to be used with
/// the unified hashing API.
///
/// # Examples
///
/// ```
/// use JenkHash::one_at_a_time::OneAtATime;
/// use digest::{Digest};
/// let mut hasher = OneAtATime::new();
/// hasher.update(b"hello");
/// hasher.update(b" world");
/// let hash = hasher.finalize();
///
/// assert_eq!(u32::from_le_bytes(hash[..].try_into().unwrap()), 1045060183);
/// ```
#[derive(Default)]
pub struct OneAtATime {
    state: u32,
}

impl OneAtATime {
    pub fn digest_unchecked(data: &[u8]) -> u32 {
        let hash = Self::digest(data);
        
        u32::from_le_bytes(hash[..].try_into().unwrap())
    }
}

impl HashMarker for OneAtATime {}
impl OutputSizeUser for OneAtATime {
    type OutputSize = U4;
}

impl Update for OneAtATime {
    fn update(&mut self, data: &[u8]) {
        for &b in data {
            self.state = self.state.wrapping_add(b as u32);
            self.state = self.state.wrapping_add(self.state << 10);
            self.state ^= self.state >> 6;
        }
    }
}

impl FixedOutput for OneAtATime {
    fn finalize_into(mut self, out: &mut Output<Self>) {
        self.state = self.state.wrapping_add(self.state << 3);
        self.state ^= self.state >> 11;
        self.state = self.state.wrapping_add(self.state << 15);

        let hash = self.state.to_le_bytes();

        out.copy_from_slice(&hash)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use digest::Digest;

    #[test]
    fn test_simple() {
        let mut hasher = OneAtATime::new();
        Digest::update(&mut hasher, b"kt1_lod_1_2_5_6_17");
        let hash = hasher.finalize();

        assert_eq!(u32::from_le_bytes(hash[..].try_into().unwrap()), 373823972);
    }

    #[test]
    fn test_digest() {
        let hash = OneAtATime::digest_unchecked(b"kt1_lod_1_2_5_6_17");

        // assert_eq!(u32::from_le_bytes(hash[..].try_into().unwrap()), 373823972);
        assert_eq!(hash, 373823972);
    }
}