#[allow(
clippy::indexing_slicing,
reason = "a const block cannot use get_mut; n < 256 is a loop invariant"
)]
const CRC_TABLE: [u32; 256] = {
let mut table = [0_u32; 256];
let mut n = 0;
while n < 256 {
let mut c = n as u32;
let mut k = 0;
while k < 8 {
c = if c & 1 != 0 {
0xEDB8_8320 ^ (c >> 1)
} else {
c >> 1
};
k += 1;
}
table[n] = c;
n += 1;
}
table
};
#[derive(Debug, Clone, Copy)]
pub struct Crc32 {
state: u32,
}
impl Crc32 {
#[must_use]
pub const fn new() -> Self {
Self { state: 0xFFFF_FFFF }
}
pub fn update(&mut self, bytes: &[u8]) {
let mut state = self.state;
for &byte in bytes {
let index = ((state ^ u32::from(byte)) & 0xFF) as usize;
let entry = CRC_TABLE.get(index).copied().unwrap_or(0);
state = entry ^ (state >> 8);
}
self.state = state;
}
#[must_use]
pub const fn finish(self) -> u32 {
self.state ^ 0xFFFF_FFFF
}
#[must_use]
pub fn of(bytes: &[u8]) -> u32 {
let mut crc = Self::new();
crc.update(bytes);
crc.finish()
}
}
impl Default for Crc32 {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Adler32 {
a: u32,
b: u32,
}
impl Adler32 {
const CHUNK: usize = 5552;
const BASE: u32 = 65521;
#[must_use]
pub const fn new() -> Self {
Self { a: 1, b: 0 }
}
pub fn update(&mut self, bytes: &[u8]) {
for chunk in bytes.chunks(Self::CHUNK) {
let (mut a, mut b) = (self.a, self.b);
for &byte in chunk {
a += u32::from(byte);
b += a;
}
self.a = a % Self::BASE;
self.b = b % Self::BASE;
}
}
#[must_use]
pub const fn finish(self) -> u32 {
(self.b << 16) | self.a
}
#[must_use]
pub fn of(bytes: &[u8]) -> u32 {
let mut adler = Self::new();
adler.update(bytes);
adler.finish()
}
}
impl Default for Adler32 {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic,
reason = "tests operate on known-good values and assert shapes directly"
)]
mod tests {
use super::*;
#[test]
fn crc32_matches_known_vectors() {
assert_eq!(Crc32::of(b""), 0x0000_0000);
assert_eq!(Crc32::of(b"a"), 0xE8B7_BE43);
assert_eq!(Crc32::of(b"abc"), 0x3524_41C2);
assert_eq!(Crc32::of(b"123456789"), 0xCBF4_3926);
assert_eq!(
Crc32::of(b"IEND"),
0xAE42_6082,
"the IEND chunk CRC in every PNG"
);
}
#[test]
fn crc32_is_incremental() {
let data = b"the quick brown fox jumps over the lazy dog";
let once = Crc32::of(data);
for split in 0..data.len() {
let mut crc = Crc32::new();
crc.update(&data[..split]);
crc.update(&data[split..]);
assert_eq!(crc.finish(), once, "split at {split}");
}
}
#[test]
fn adler32_matches_known_vectors() {
assert_eq!(Adler32::of(b""), 0x0000_0001);
assert_eq!(Adler32::of(b"a"), 0x0062_0062);
assert_eq!(Adler32::of(b"abc"), 0x024D_0127);
assert_eq!(Adler32::of(b"123456789"), 0x091E_01DE);
assert_eq!(Adler32::of(b"Wikipedia"), 0x11E6_0398);
}
#[test]
fn adler32_is_incremental() {
let data: Vec<u8> = (0..1000).map(|i| (i % 251) as u8).collect();
let once = Adler32::of(&data);
for split in [0, 1, 7, 499, 999, 1000] {
let mut adler = Adler32::new();
adler.update(&data[..split]);
adler.update(&data[split..]);
assert_eq!(adler.finish(), once, "split at {split}");
}
}
#[test]
fn adler32_survives_runs_longer_than_the_chunk_window() {
let data = vec![0xFF_u8; 5552 * 3 + 17];
let once = Adler32::of(&data);
let mut adler = Adler32::new();
for piece in data.chunks(1000) {
adler.update(piece);
}
assert_eq!(adler.finish(), once);
assert!(once & 0xFFFF < Adler32::BASE);
assert!(once >> 16 < Adler32::BASE);
}
#[test]
fn empty_updates_change_nothing() {
let mut crc = Crc32::new();
crc.update(b"abc");
crc.update(b"");
assert_eq!(crc.finish(), Crc32::of(b"abc"));
let mut adler = Adler32::new();
adler.update(b"abc");
adler.update(b"");
assert_eq!(adler.finish(), Adler32::of(b"abc"));
}
#[test]
fn defaults_are_fresh_checksums() {
assert_eq!(Crc32::default().finish(), Crc32::new().finish());
assert_eq!(Adler32::default().finish(), Adler32::new().finish());
}
}