use std::fmt;
pub const DEFAULT_POLYNOMIAL: u32 = 0xEDB88320;
pub const DEFAULT_INIT: u32 = 0xFFFF_FFFF;
pub const DEFAULT_FINAL_XOR: u32 = 0xFFFF_FFFF;
#[derive(Clone)]
pub struct Crc32Builder {
polynomial: u32,
init: u32,
final_xor: u32,
reflect_in: bool,
reflect_out: bool,
}
impl fmt::Debug for Crc32Builder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Crc32Builder")
.field("polynomial", &format_args!("{:#X}", self.polynomial))
.field("init", &format_args!("{:#X}", self.init))
.field("final_xor", &format_args!("{:#X}", self.final_xor))
.field("reflect_in", &self.reflect_in)
.field("reflect_out", &self.reflect_out)
.finish()
}
}
impl Default for Crc32Builder {
fn default() -> Self {
Self {
polynomial: DEFAULT_POLYNOMIAL,
init: DEFAULT_INIT,
final_xor: DEFAULT_FINAL_XOR,
reflect_in: true,
reflect_out: true,
}
}
}
impl Crc32Builder {
pub fn new() -> Self {
Self::default()
}
pub fn polynomial(mut self, poly: u32) -> Self {
self.polynomial = poly;
self
}
pub fn initial_value(mut self, init: u32) -> Self {
self.init = init;
self
}
pub fn final_xor(mut self, fx: u32) -> Self {
self.final_xor = fx;
self
}
pub fn reflect_in(mut self, on: bool) -> Self {
self.reflect_in = on;
self
}
pub fn reflect_out(mut self, on: bool) -> Self {
self.reflect_out = on;
self
}
pub fn build(self) -> Crc32 {
let mut table = [0u32; 256];
for i in 0..256 {
let mut crc = i as u32;
if self.reflect_in {
for _ in 0..8 {
if (crc & 1) != 0 {
crc = (crc >> 1) ^ self.polynomial;
} else {
crc >>= 1;
}
}
} else {
crc <<= 24;
for _ in 0..8 {
if (crc & 0x80000000) != 0 {
crc = (crc << 1) ^ self.polynomial;
} else {
crc <<= 1;
}
}
}
table[i as usize] = crc;
}
let state = self.init;
Crc32 {
table,
#[allow(dead_code)]
polynomial: self.polynomial,
init: self.init,
final_xor: self.final_xor,
reflect_in: self.reflect_in,
reflect_out: self.reflect_out,
state,
}
}
}
#[derive(Debug, Clone)]
pub struct Crc32 {
table: [u32; 256],
#[allow(dead_code)]
polynomial: u32,
init: u32,
final_xor: u32,
reflect_in: bool,
reflect_out: bool,
state: u32, }
impl Crc32 {
pub fn reset(&mut self) {
self.state = self.init;
}
pub fn update(&mut self, data: &[u8]) {
if self.reflect_in {
let mut crc = self.state;
for &b in data {
let idx = (crc ^ (b as u32)) & 0xFF;
crc = (crc >> 8) ^ self.table[idx as usize];
}
self.state = crc;
} else {
let mut crc = self.state;
for &b in data {
let idx = ((crc >> 24) ^ (b as u32)) & 0xFF;
crc = (crc << 8) ^ self.table[idx as usize];
}
self.state = crc;
}
}
pub fn finalize(&self) -> u32 {
let mut crc = self.state;
if self.reflect_out != self.reflect_in {
crc = reflect_32(crc);
}
crc ^ self.final_xor
}
pub fn compute_one_shot(&self, data: &[u8]) -> u32 {
let mut temp = self.clone();
temp.reset();
temp.update(data);
temp.finalize()
}
pub fn current_state(&self) -> u32 {
self.state
}
}
fn reflect_32(mut x: u32) -> u32 {
let mut r = 0u32;
for _ in 0..32 {
r = (r << 1) | (x & 1);
x >>= 1;
}
r
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_crc32() {
let c = Crc32Builder::new().build();
let mut hasher = c.clone();
hasher.update(b"123456789");
let val = hasher.finalize();
assert_eq!(val, 0xcbf43926);
}
#[test]
fn test_no_reflection() {
let c = Crc32Builder::new()
.polynomial(0x04C11DB7)
.initial_value(0)
.final_xor(0)
.reflect_in(false)
.reflect_out(false)
.build();
let h = c.compute_one_shot(b"ABC");
assert_ne!(h, 0);
}
#[test]
fn test_incremental() {
let c = Crc32Builder::new().build();
let mut hasher = c.clone();
hasher.update(b"Hello, ");
hasher.update(b"World!");
let val1 = hasher.finalize();
let val2 = c.compute_one_shot(b"Hello, World!");
assert_eq!(val1, val2);
}
}