zrip-core 0.4.0

Shared types and codecs for zrip (internal crate)
Documentation
#![forbid(unsafe_code)]

pub const PRIME32_1: u32 = 0x9E37_79B1;
pub const PRIME32_2: u32 = 0x85EB_CA77;
pub const PRIME64_1: u64 = 0x9E37_79B1_85EB_CA87;

#[inline]
pub fn hash4(value: u32, hash_log: u32) -> u32 {
    (value.wrapping_mul(PRIME32_1)) >> (32 - hash_log)
}

#[inline]
pub fn hash5(value: u64, hash_log: u32) -> u32 {
    ((value << 24).wrapping_mul(PRIME64_1) >> (64 - hash_log)) as u32
}

#[inline]
pub fn hash6(value: u64, hash_log: u32) -> u32 {
    ((value << 16).wrapping_mul(PRIME64_1) >> (64 - hash_log)) as u32
}

#[inline]
pub fn hash7(value: u64, hash_log: u32) -> u32 {
    ((value << 8).wrapping_mul(PRIME64_1) >> (64 - hash_log)) as u32
}

#[inline]
pub fn hash8(value: u64, hash_log: u32) -> u32 {
    (value.wrapping_mul(PRIME64_1) >> (64 - hash_log)) as u32
}

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

    #[test]
    fn hash4_distributes() {
        let mut buckets = [0u32; 256];
        for i in 0..4096u32 {
            let h = hash4(i, 8) as usize;
            buckets[h] += 1;
        }
        let used = buckets.iter().filter(|&&c| c > 0).count();
        assert!(used > 200, "hash4 only hit {used}/256 buckets");
    }

    #[test]
    fn hash8_distributes() {
        let mut buckets = [0u32; 256];
        for i in 0..4096u64 {
            let h = hash8(i, 8) as usize;
            buckets[h] += 1;
        }
        let used = buckets.iter().filter(|&&c| c > 0).count();
        assert!(used > 200, "hash8 only hit {used}/256 buckets");
    }
}