pub struct Crc32Context {
polynomial: u32,
initial_value: u32,
checksum: u32,
table: [u32; 256],
table_initialized: bool,
}
impl Crc32Context {
pub fn new(polynomial: u32, initial_value: u32) -> Self {
Self {
polynomial,
initial_value,
checksum: initial_value ^ 0xffffffff,
table: [0; 256],
table_initialized: false,
}
}
fn initialize_table(&mut self, polynomial: u32) {
for table_index in 0..256 {
let mut checksum: u32 = (table_index as u32) << 24;
for _ in 0..8 {
if checksum & 0x80000000 != 0 {
checksum = polynomial ^ (checksum << 1);
} else {
checksum <<= 1;
}
}
self.table[table_index] = checksum;
}
self.table_initialized = true
}
pub fn finalize(&mut self) -> u32 {
let checksum: u32 = self.checksum ^ 0xffffffff;
self.checksum = self.initial_value ^ 0xffffffff;
checksum
}
pub fn update(&mut self, data: &[u8]) {
if !self.table_initialized {
self.initialize_table(self.polynomial);
}
let mut checksum: u32 = self.checksum;
for byte_value in data.iter() {
let table_index: u32 = ((checksum >> 24) ^ (*byte_value as u32)) & 0x000000ff;
checksum = self.table[table_index as usize] ^ (checksum << 8);
}
self.checksum = checksum
}
}
pub struct ReversedCrc32Context {
polynomial: u32,
initial_value: u32,
checksum: u32,
table: [u32; 256],
table_initialized: bool,
}
impl ReversedCrc32Context {
pub fn new(polynomial: u32, initial_value: u32) -> Self {
Self {
polynomial,
initial_value,
checksum: initial_value ^ 0xffffffff,
table: [0; 256],
table_initialized: false,
}
}
fn initialize_table(&mut self, polynomial: u32) {
for table_index in 0..256 {
let mut checksum: u32 = table_index as u32;
for _ in 0..8 {
if checksum & 1 != 0 {
checksum = polynomial ^ (checksum >> 1);
} else {
checksum >>= 1;
}
}
self.table[table_index] = checksum;
}
self.table_initialized = true
}
pub fn finalize(&mut self) -> u32 {
let checksum: u32 = self.checksum ^ 0xffffffff;
self.checksum = self.initial_value ^ 0xffffffff;
checksum
}
pub fn update(&mut self, data: &[u8]) {
if !self.table_initialized {
self.initialize_table(self.polynomial);
}
let mut checksum: u32 = self.checksum;
for byte_value in data.iter() {
let table_index: u32 = (checksum ^ (*byte_value as u32)) & 0x000000ff;
checksum = self.table[table_index as usize] ^ (checksum >> 8);
}
self.checksum = checksum
}
}
#[cfg(test)]
mod tests {
use super::*;
fn get_test_data(data_size: usize) -> Vec<u8> {
(0..data_size)
.map(|value| (value % 256) as u8)
.collect::<Vec<u8>>()
}
#[test]
fn test_update_and_finalize_with_context() {
let mut test_context: Crc32Context = Crc32Context::new(0x04c11db7, 0);
let test_data: Vec<u8> = get_test_data(256);
test_context.update(&test_data);
let test_checksum: u32 = test_context.finalize();
assert_eq!(test_checksum, 0xb6b5ee95);
}
#[test]
fn test_update_and_finalize_with_reversed_context() {
let mut test_context: ReversedCrc32Context = ReversedCrc32Context::new(0xedb88320, 0);
let test_data: Vec<u8> = get_test_data(256);
test_context.update(&test_data);
let test_checksum: u32 = test_context.finalize();
assert_eq!(test_checksum, 0x29058c73);
}
}