pub fn fletcher64(data: &[u8]) -> u64 {
let mod_val: u64 = 0xFFFFFFFF;
let mut sum1: u64 = 0;
let mut sum2: u64 = 0;
for chunk in data.chunks_exact(4) {
let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) as u64;
sum1 = (sum1 + word) % mod_val;
sum2 = (sum2 + sum1) % mod_val;
}
let check1 = mod_val - ((sum1 + sum2) % mod_val);
let check2 = mod_val - ((sum1 + check1) % mod_val);
(check2 << 32) | check1
}
pub fn verify_object(block: &[u8]) -> bool {
if block.len() < 8 {
return false;
}
let stored = u64::from_le_bytes([
block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
]);
let computed = fletcher64(&block[8..]);
stored == computed
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_fletcher64_known() {
let mut file = std::fs::File::open("../tests/appfs.raw").unwrap();
use std::io::Read;
let mut block = vec![0u8; 4096];
file.read_exact(&mut block).unwrap();
assert!(verify_object(&block), "Block 0 checksum should be valid");
let stored = u64::from_le_bytes([
block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
]);
let computed = fletcher64(&block[8..]);
assert_eq!(
stored, computed,
"Stored checksum 0x{:016X} should match computed 0x{:016X}",
stored, computed
);
}
#[test]
fn test_fletcher64_known_words() {
let data = [
1u8, 0, 0, 0, 2, 0, 0, 0, ];
let m: u64 = 0xFFFFFFFF;
let checksum = fletcher64(&data);
let check1 = checksum & 0xFFFFFFFF;
let check2 = checksum >> 32;
assert_eq!(check1, m - 7);
assert_eq!(check2, 4);
}
#[test]
fn test_verify_object_valid() {
let mut block = vec![0u8; 64];
for (i, byte) in block[8..].iter_mut().enumerate() {
*byte = (i & 0xFF) as u8;
}
let checksum = fletcher64(&block[8..]);
block[..8].copy_from_slice(&checksum.to_le_bytes());
assert!(verify_object(&block));
}
#[test]
fn test_verify_object_invalid() {
let mut block = vec![0u8; 64];
for (i, byte) in block[8..].iter_mut().enumerate() {
*byte = (i & 0xFF) as u8;
}
let checksum = fletcher64(&block[8..]);
block[..8].copy_from_slice(&checksum.to_le_bytes());
block[16] ^= 0xFF;
assert!(!verify_object(&block));
}
}