use std::slice::ChunksExact;
pub struct Fletcher64Context {
initial_value: u64,
checksum: u64,
}
impl Fletcher64Context {
pub fn new(initial_value: u64) -> Self {
Self {
initial_value,
checksum: initial_value,
}
}
pub fn finalize(&mut self) -> u64 {
let checksum: u64 = self.checksum;
self.checksum = self.initial_value;
checksum
}
pub fn update(&mut self, data: &[u8]) {
let mut lower_32bit: u64 = self.checksum & 0xffffffff;
let mut upper_32bit: u64 = self.checksum >> 32;
let mut chunks: ChunksExact<'_, u8> = data.chunks_exact(4);
for chunk in &mut chunks {
let value_32bit: u64 = u32::from_le_bytes(chunk.try_into().unwrap()) as u64;
lower_32bit = (lower_32bit + value_32bit) % 0xffffffff;
upper_32bit = (upper_32bit + lower_32bit) % 0xffffffff;
}
let remainder: &[u8] = chunks.remainder();
if !remainder.is_empty() {
let mut chunk: [u8; 4] = [0; 4];
chunk[0..remainder.len()].copy_from_slice(remainder);
let value_32bit: u64 = u32::from_le_bytes(chunk.try_into().unwrap()) as u64;
lower_32bit = (lower_32bit + value_32bit) % 0xffffffff;
upper_32bit = (upper_32bit + lower_32bit) % 0xffffffff;
}
self.checksum = (upper_32bit << 32) | lower_32bit;
}
}
#[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() {
let mut test_context: Fletcher64Context = Fletcher64Context::new(0);
let test_data: Vec<u8> = get_test_data(256);
test_context.update(&test_data);
let test_checksum: u64 = test_context.finalize();
assert_eq!(test_checksum, 0x9d754d45601fdfa0);
}
}