adler32fast 1.1.0

Fast, SIMD-accelerated Adler-32 checksum computation
Documentation
pub(crate) const BASE: u32 = 65521;
const NMAX: usize = 5552;
const CHUNK_SIZE: usize = 16;

#[derive(Copy, Clone, Debug)]
pub struct State {
    state: (u32, u32),
}

impl State {
    pub fn new(initial: u32) -> Self {
        Self {
            state: (initial & 0xffff, initial >> 16),
        }
    }

    pub fn finalize(&self) -> u32 {
        self.state.0 | (self.state.1 << 16)
    }

    pub fn reset(&mut self) {
        self.state = (1, 0);
    }

    pub fn update(&mut self, buf: &[u8]) {
        self.state = update_fast(self.state.0, self.state.1, buf);
    }
}

#[inline(always)]
pub(crate) fn update_slow(mut a: u32, mut b: u32, buf: &[u8]) -> (u32, u32) {
    for &byte in buf {
        a += u32::from(byte);
        b += a;
    }
    (a % BASE, b % BASE)
}

fn update_fast<'a>(mut a: u32, mut b: u32, buf: &'a [u8]) -> (u32, u32) {
    let chunks = buf.chunks_exact(NMAX);
    let mut remainder = chunks.remainder();
    for chunk in chunks {
        add_reduce(&mut a, &mut b, chunk);
        a %= BASE;
        b %= BASE;
    }
    remainder = add_reduce(&mut a, &mut b, remainder);
    update_slow(a, b, remainder)
}

#[inline(always)]
fn add_reduce<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
    if chunk.len() < CHUNK_SIZE {
        return chunk;
    }
    let inner_chunks = chunk.chunks_exact(CHUNK_SIZE);
    let remainder = inner_chunks.remainder();
    for inner_chunk in inner_chunks {
        update_16(a, b, inner_chunk);
    }
    remainder
}

#[inline(always)]
fn update_16(a: &mut u32, b: &mut u32, buf: &[u8]) {
    debug_assert!(buf.len() >= 16);
    for i in 0..16 {
        *a += u32::from(buf[i]);
        *b += *a;
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn baseline_is_valid() {
        fn golden(expected: u32, input: &[u8]) {
            let mut adler32 = super::State::new(1);
            adler32.update(input);
            assert_eq!(adler32.finalize(), expected);
        }

        // Goldens shamelessly borrowed from https://golang.org/src/hash/adler32/adler32_test.go
        golden(0x00000001, b"");
        golden(0x00620062, b"a");
        golden(0x012600c4, b"ab");
        golden(0x024d0127, b"abc");
        golden(0x03d8018b, b"abcd");
        golden(0x05c801f0, b"abcde");
        golden(0x081e0256, b"abcdef");
        golden(0x0adb02bd, b"abcdefg");
        golden(0x0e000325, b"abcdefgh");
        golden(0x118e038e, b"abcdefghi");
        golden(0x158603f8, b"abcdefghij");
        golden(0x3f090f02, b"Discard medicine more than two years old.");
        golden(
            0x46d81477,
            b"He who has a shady past knows that nice guys finish last.",
        );
        golden(0x40ee0ee1, b"I wouldn't marry him with a ten foot pole.");
        golden(
            0x16661315,
            b"Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave",
        );
        golden(
            0x5b2e1480,
            b"The days of the digital watch are numbered.  -Tom Stoppard",
        );
        golden(0x8c3c09ea, b"Nepal premier won't resign.");
        golden(
            0x45ac18fd,
            b"For every action there is an equal and opposite government program.",
        );
        golden(
            0x53c61462,
            b"His money is twice tainted: 'taint yours and 'taint mine.",
        );
        golden(0x7e511e63, b"There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977");
        golden(
            0xe4801a6a,
            b"It's a tiny change to the code and not completely disgusting. - Bob Manchek",
        );
        golden(0x61b507df, b"size:  a.out:  bad magic");
        golden(
            0xb8631171,
            b"The major problem is with sendmail.  -Mark Horton",
        );
        golden(
            0x8b5e1904,
            b"Give me a rock, paper and scissors and I will move the world.  CCFestoon",
        );
        golden(
            0x7cc6102b,
            b"If the enemy is within range, then so are you.",
        );
        golden(
            0x700318e7,
            b"It's well we cannot hear the screams/That we create in others' dreams.",
        );
        golden(
            0x1e601747,
            b"You remind me of a TV show, but that's all right: I watch it anyway.",
        );
        golden(0xb55b0b09, b"C is as portable as Stonehedge!!");
        golden(0x39111dd0, b"Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley");
        golden(0x91dd304f, b"The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction.  Lewis-Randall Rule");
        golden(
            0x2e5d1316,
            b"How can you write a big system without C++?  -Paul Glick",
        );
        golden(
            0xd0201df6,
            b"'Invariant assertions' is the most elegant programming technique!  -Tom Szymanski",
        );
        golden(0x86af0001, &b"\x00".repeat(100_000));
        golden(0x79660b4d, &b"a".repeat(100_000));
        golden(0x110588ee, &b"ABCDEFGHIJKLMNOPQRSTUVWXYZ".repeat(10_000));
    }
}