JenkHash 0.3.0

Bob Jenkins hash functions for Rust with a digest-compatible API.
Documentation
use super::Lookup2;
use core::cmp::min;
use digest::{Digest, FixedOutput, Output, Update};

/// Type marker for the byte-oriented hash variant.
///
/// This variant works on all machines and processes input as a byte slice.
/// It provides maximum portability across all architectures but is slower
/// than the optimized variants.
///
/// # Deprecation Notice
///
/// This variant is deprecated in favor of [`Hash3`] which provides better
/// performance while maintaining portability. Use [`Hash3`] for new code
/// unless you specifically need the exact behavior of this variant.
///
/// [`Hash3`]: crate::lookup2::Hash3
///
/// # Examples
///
/// ```
/// use digest::Digest;
/// use JenkHash::lookup2::{Lookup2, Hash};
///
/// #[allow(deprecated)]
/// let hash = Lookup2::<Hash>::digest(b"hello world");
/// let hash_value = u32::from_le_bytes(hash[..].try_into().unwrap());
/// ```
#[deprecated = "Use [`Hash3`] for better performance while maintaining portability"]
#[derive(Default)]
pub struct Hash;

impl Lookup2<Hash> {
    /// Computes the hash of the input data and returns it as a `u32`.
    ///
    /// This is a convenience method that combines [`Digest::digest`] with
    /// byte conversion. Equivalent to calling `digest()` and converting
    /// the result to `u32`.
    ///
    /// # Arguments
    ///
    /// * `data` - The data to hash
    ///
    /// # Returns
    ///
    /// The hash value as a 32-bit unsigned integer.
    ///
    /// # Examples
    ///
    /// ```
    /// use JenkHash::lookup2::{Lookup2, Hash};
    /// #[allow(deprecated)]
    /// let hash = Lookup2::<Hash>::digest_unchecked(b"hello world");
    /// ```
    pub fn digest_unchecked(data: &[u8]) -> u32 {
        let hash = Lookup2::<Hash>::digest(data);

        u32::from_le_bytes(hash[..].try_into().unwrap())
    }

    #[inline]
    pub(crate) const fn add_u32_le(acc: u32, b1: u8, b2: u8, b3: u8, b4: u8) -> u32 {
        acc.wrapping_add(b1 as u32)
            .wrapping_add((b2 as u32) << 8)
            .wrapping_add((b3 as u32) << 16)
            .wrapping_add((b4 as u32) << 24)
    }
    #[inline]
    pub(crate) const fn add_msb(acc: u32, b1: u8, mask: u8) -> u32 {
        acc.wrapping_add((b1 as u32) << mask)
    }

    #[inline]
    pub(crate) const fn add_lsb(acc: u32, b1: u8) -> u32 {
        acc.wrapping_add(b1 as u32)
    }
}
impl Update for Lookup2<Hash> {
    #[rustfmt::skip]
    fn update(&mut self, mut data: &[u8]) {
        self.total_length += data.len();

        if self.rem_offset > 0 {
            let remaining = min(12_u8.saturating_sub(self.rem_offset) as usize, data.len());

            self.rem[self.rem_offset as usize..remaining + self.rem_offset as usize].copy_from_slice(&data[..remaining]);
            self.rem_offset += remaining as u8;
            data = &data[remaining..];

            if self.rem_offset < 12 { return;}

            self.state[0] = Self::add_u32_le(self.state[0], self.rem[0], self.rem[1], self.rem[02], self.rem[03] );
            self.state[1] = Self::add_u32_le(self.state[1], self.rem[4], self.rem[5], self.rem[06], self.rem[07] );
            self.state[2] = Self::add_u32_le(self.state[2], self.rem[8], self.rem[9], self.rem[10], self.rem[11] );
            self.state = Self::mix(self.state);

            self.rem_offset = 0;
        }

        while data.len() >= 12 {
            self.state[0] = Self::add_u32_le(self.state[0], data[0], data[1], data[02], data[03]);
            self.state[1] = Self::add_u32_le(self.state[1], data[4], data[5], data[06], data[07]);
            self.state[2] = Self::add_u32_le(self.state[2], data[8], data[9], data[10], data[11]);
            self.state = Self::mix(self.state);
            data = &data[12..];
        }

        self.rem[self.rem_offset as usize..self.rem_offset as usize + data.len()].copy_from_slice(data);
        self.rem_offset += data.len() as u8
    }
}
impl FixedOutput for Lookup2<Hash> {
    #[rustfmt::skip]
    fn finalize_into(mut self, out: &mut Output<Self>) {
        self.state[2] = self.state[2].wrapping_add(self.total_length as u32);

        if self.rem_offset == 11 { self.state[2] = Self::add_msb(self.state[2], self.rem[10], 24); }
        if self.rem_offset >= 10 { self.state[2] = Self::add_msb(self.state[2], self.rem[09], 16); }
        if self.rem_offset >= 09 { self.state[2] = Self::add_msb(self.state[2], self.rem[08], 08); }
        if self.rem_offset >= 08 { self.state[1] = Self::add_msb(self.state[1], self.rem[07], 24); }
        if self.rem_offset >= 07 { self.state[1] = Self::add_msb(self.state[1], self.rem[06], 16); }
        if self.rem_offset >= 06 { self.state[1] = Self::add_msb(self.state[1], self.rem[05], 08); }
        if self.rem_offset >= 05 { self.state[1] = Self::add_lsb(self.state[1], self.rem[04]); }
        if self.rem_offset >= 04 { self.state[0] = Self::add_msb(self.state[0], self.rem[03], 24); }
        if self.rem_offset >= 03 { self.state[0] = Self::add_msb(self.state[0], self.rem[02], 16); }
        if self.rem_offset >= 02 { self.state[0] = Self::add_msb(self.state[0], self.rem[01], 08); }
        if self.rem_offset >= 01 { self.state[0] = Self::add_lsb(self.state[0], self.rem[00]); }

        self.state = Self::mix(self.state);

        out.copy_from_slice(&self.state[2].to_le_bytes())
    }
}