murmurs 1.0.5

🫆 A reliable and production-ready implementation of the Murmur3 hash function in Rust
Documentation
use std::usize;

#[cfg(target_endian = "little")]
fn read_data_32(data: &[u8], start: usize, length: usize) -> u32 {
    u32::from_le_bytes(data[start..start + length].try_into().unwrap())
}

#[cfg(target_endian = "little")]
fn read_data_64(data: &[u8], start: usize, length: usize) -> u64 {
    u64::from_le_bytes(data[start..start + length].try_into().unwrap())
}

#[cfg(target_endian = "big")]
fn read_data_32(data: &[u8], start: usize, length: usize) -> u64 {
    u64::from_be_bytes(data[start..start + length].try_into().unwrap())
}

#[cfg(target_endian = "big")]
fn read_data_64(data: &[u8], start: usize, length: usize) -> u64 {
    u64::from_be_bytes(data[start..start + length].try_into().unwrap())
}

fn fmix32(h: u32) -> u32 {
    let mut h = h;
    h ^= h >> 16;
    h = h.wrapping_mul(0x85ebca6b);
    h ^= h >> 13;
    h = h.wrapping_mul(0xc2b2ae35);
    h ^= h >> 16;
    h
}

fn fmix64(h: u64) -> u64 {
    let mut h = h;
    h ^= h >> 33;
    h = h.wrapping_mul(0xff51afd7ed558ccd_u64);
    h ^= h >> 33;
    h = h.wrapping_mul(0xc4ceb9fe1a85ec53_u64);
    h ^= h >> 33;
    h
}

/// Hash key into one unsigned 32 bits value
/// 
/// # Example
/// ```
/// use murmur3_rust::{murmur3_x86_32};
/// let data = [1, 2, 3];
/// println!("{}", murmur3_x86_32(&data, 0));
/// ```
pub fn murmur3_x86_32(key: &[u8], seed: u32) -> u32 {
    const DATA_SIZE: usize = 4;
    const C1: u32 = 0xcc9e2d51;
    const C2: u32 = 0x1b873593;

    let len = key.len();
    let n_blocks: usize = len / DATA_SIZE;

    let mut h1: u32 = seed;

    for i in 0..n_blocks {
        let base = i.wrapping_mul(DATA_SIZE);
        let mut k1 = read_data_32(key, base, DATA_SIZE);

        k1 = k1.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);

        h1 ^= k1;
        h1 = h1.rotate_left(13).wrapping_mul(5).wrapping_add(0xe6546b64);
    }

    let tail = &key[n_blocks * DATA_SIZE..];
    let mut k1: u32 = 0;
    let len3 = len & 3;

    if len3 >= 3 {
        k1 ^= (tail[2] as u32) << 16;
    }
    if len3 >= 2 {
        k1 ^= (tail[1] as u32) << 8;
    }
    if len3 >= 1 {
        k1 ^= tail[0] as u32;
        k1 = k1.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
        h1 ^= k1;
    }

    h1 ^= u32::try_from(len).unwrap();

    h1 = fmix32(h1);

    h1
}

/// Hash key into four unsigned 32 bits blocks
/// 
/// # Example
/// ```
/// use murmur3_rust::{murmur3_x86_128};
/// let data = [1, 2, 3];
/// println!("{:?}", murmur3_x86_128(&data, 0));
/// ```
pub fn murmur3_x86_128(key: &[u8], seed: u32) -> [u32; 4] {
    const DATA_SIZE: usize = 16;
    const C1: u32 = 0x239b961b;
    const C2: u32 = 0xab0e9789;
    const C3: u32 = 0x38b34ae5;
    const C4: u32 = 0xa1e38b93;

    let len = key.len();
    let n_blocks: usize = len / DATA_SIZE;

    let mut h1: u32 = seed;
    let mut h2: u32 = h1;
    let mut h3: u32 = h1;
    let mut h4: u32 = h1;

    for i in 0..n_blocks {
        let base = i.wrapping_mul(DATA_SIZE);
        let mut k1 = read_data_32(key, base, 4);
        let mut k2 = read_data_32(key, base + 4, 4);
        let mut k3 = read_data_32(key, base + 8, 4);
        let mut k4 = read_data_32(key, base + 12, 4);

        k1 = k1.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
        h1 ^= k1;
        h1 = h1
            .rotate_left(19)
            .wrapping_add(h2)
            .wrapping_mul(5)
            .wrapping_add(0x561ccd1b);

        k2 = k2.wrapping_mul(C2).rotate_left(16).wrapping_mul(C3);
        h2 ^= k2;
        h2 = h2
            .rotate_left(17)
            .wrapping_add(h3)
            .wrapping_mul(5)
            .wrapping_add(0x0bcaa747);

        k3 = k3.wrapping_mul(C3).rotate_left(17).wrapping_mul(C4);
        h3 ^= k3;
        h3 = h3
            .rotate_left(15)
            .wrapping_add(h4)
            .wrapping_mul(5)
            .wrapping_add(0x96cd1c35);

        k4 = k4.wrapping_mul(C4).rotate_left(18).wrapping_mul(C1);
        h4 ^= k4;
        h4 = h4
            .rotate_left(13)
            .wrapping_add(h1)
            .wrapping_mul(5)
            .wrapping_add(0x32ac3b17);
    }

    let tail = &key[n_blocks * DATA_SIZE..];
    let len15 = len & 15;

    let mut k1: u32 = 0;
    let mut k2: u32 = 0;
    let mut k3: u32 = 0;
    let mut k4: u32 = 0;

    if len15 >= 15 {
        k4 ^= (tail[14] as u32) << 16;
    }
    if len15 >= 14 {
        k4 ^= (tail[13] as u32) << 8;
    }
    if len15 >= 13 {
        k4 ^= (tail[12] as u32) << 0;
        k4 = k4.wrapping_mul(C4).rotate_left(18).wrapping_mul(C1);
        h4 ^= k4;
    }

    if len15 >= 12 {
        k3 ^= (tail[11] as u32) << 24;
    }
    if len15 >= 11 {
        k3 ^= (tail[10] as u32) << 16;
    }
    if len15 >= 10 {
        k3 ^= (tail[9] as u32) << 8;
    }
    if len15 >= 9 {
        k3 ^= (tail[8] as u32) << 0;
        k3 = k3.wrapping_mul(C3).rotate_left(17).wrapping_mul(C4);
        h3 ^= k3;
    }

    if len15 >= 8 {
        k2 ^= (tail[7] as u32) << 24;
    }
    if len15 >= 7 {
        k2 ^= (tail[6] as u32) << 16;
    }
    if len15 >= 6 {
        k2 ^= (tail[5] as u32) << 8;
    }
    if len15 >= 5 {
        k2 ^= (tail[4] as u32) << 0;
        k2 = k2.wrapping_mul(C2).rotate_left(16).wrapping_mul(C3);
        h2 ^= k2;
    }

    if len15 >= 4 {
        k1 ^= (tail[3] as u32) << 24;
    }
    if len15 >= 3 {
        k1 ^= (tail[2] as u32) << 16;
    }
    if len15 >= 2 {
        k1 ^= (tail[1] as u32) << 8;
    }
    if len15 >= 1 {
        k1 ^= (tail[0] as u32) << 0;
        k1 = k1.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
        h1 ^= k1;
    }

    let len32 = u32::try_from(len).unwrap();
    h1 ^= len32;
    h2 ^= len32;
    h3 ^= len32;
    h4 ^= len32;

    h1 = h1.wrapping_add(h2);
    h1 = h1.wrapping_add(h3);
    h1 = h1.wrapping_add(h4);
    h2 = h2.wrapping_add(h1);
    h3 = h3.wrapping_add(h1);
    h4 = h4.wrapping_add(h1);

    h1 = fmix32(h1);
    h2 = fmix32(h2);
    h3 = fmix32(h3);
    h4 = fmix32(h4);

    h1 = h1.wrapping_add(h2);
    h1 = h1.wrapping_add(h3);
    h1 = h1.wrapping_add(h4);
    h2 = h2.wrapping_add(h1);
    h3 = h3.wrapping_add(h1);
    h4 = h4.wrapping_add(h1);

    [h1, h2, h3, h4]
}

/// Hash key into two unsigned 64 bits blocks
/// 
/// # Example
/// ```
/// use murmur3_rust::{murmur3_x64_128};
/// let data = [1, 2, 3];
/// println!("{:?}", murmur3_x64_128(&data, 0));
/// ```
pub fn murmur3_x64_128(key: &[u8], seed: u32) -> [u64; 2] {
    const DATA_SIZE: usize = 16;
    const C1: u64 = 0x87c37b91114253d5;
    const C2: u64 = 0x4cf5ad432745937f;

    let len = key.len();
    let n_blocks: usize = len / DATA_SIZE;

    let mut h1: u64 = seed as u64;
    let mut h2: u64 = h1;

    for i in 0..n_blocks {
        let base = i.wrapping_mul(DATA_SIZE);
        let mut k1 = read_data_64(key, base, 8);
        let mut k2 = read_data_64(key, base+8, 8);

        k1 = k1.wrapping_mul(C1).rotate_left(31).wrapping_mul(C2);
        h1 ^= k1;
        h1 = h1
            .rotate_left(27)
            .wrapping_add(h2)
            .wrapping_mul(5)
            .wrapping_add(0x52dce729);

        k2 = k2.wrapping_mul(C2).rotate_left(33).wrapping_mul(C1);
        h2 ^= k2;
        h2 = h2
            .rotate_left(31)
            .wrapping_add(h1)
            .wrapping_mul(5)
            .wrapping_add(0x38495ab5);
    }

    let tail = &key[n_blocks * DATA_SIZE..];
    let len15 = len & 15;

    let mut k1: u64 = 0;
    let mut k2: u64 = 0;

    if len15 >= 15 {
        k2 ^= (tail[14] as u64) << 48;
    }
    if len15 >= 14 {
        k2 ^= (tail[13] as u64) << 40;
    }
    if len15 >= 13 {
        k2 ^= (tail[12] as u64) << 32;
    }
    if len15 >= 12 {
        k2 ^= (tail[11] as u64) << 24;
    }
    if len15 >= 11 {
        k2 ^= (tail[10] as u64) << 16;
    }
    if len15 >= 10 {
        k2 ^= (tail[9] as u64) << 8;
    }
    if len15 >= 9 {
        k2 ^= (tail[8] as u64) << 0;
        k2 = k2.wrapping_mul(C2).rotate_left(33).wrapping_mul(C1);
        h2 ^= k2;
    }

    if len15 >= 8 {
        k1 ^= (tail[7] as u64) << 56;
    }
    if len15 >= 7 {
        k1 ^= (tail[6] as u64) << 48;
    }
    if len15 >= 6 {
        k1 ^= (tail[5] as u64) << 40;
    }
    if len15 >= 5 {
        k1 ^= (tail[4] as u64) << 32;
    }
    if len15 >= 4 {
        k1 ^= (tail[3] as u64) << 24;
    }
    if len15 >= 3 {
        k1 ^= (tail[2] as u64) << 16;
    }
    if len15 >= 2 {
        k1 ^= (tail[1] as u64) << 8;
    }
    if len15 >= 1 {
        k1 ^= (tail[0] as u64) << 0;
        k1 = k1.wrapping_mul(C1).rotate_left(31).wrapping_mul(C2);
        h1 ^= k1;
    }

    let len64 = u64::try_from(len).unwrap();
    h1 ^= len64;
    h2 ^= len64;

    h1 = h1.wrapping_add(h2);
    h2 = h2.wrapping_add(h1);

    h1 = fmix64(h1);
    h2 = fmix64(h2);

    h1 = h1.wrapping_add(h2);
    h2 = h2.wrapping_add(h1);

    [h1, h2]
}