1use crate::errors::FxfsError;
6use anyhow::Error;
7use byteorder::{ByteOrder, LittleEndian};
8use fprint::TypeFingerprint;
9use serde::{Deserialize, Serialize};
10use static_assertions::assert_cfg;
11use zerocopy::{FromBytes as _, IntoBytes as _};
12
13pub type Checksum = u64;
15
16pub fn fletcher64(buf: &[u8], previous: Checksum) -> Checksum {
23 assert!(buf.len() % 4 == 0);
24 let mut lo = previous as u32;
25 let mut hi = (previous >> 32) as u32;
26 for chunk in buf.chunks(4) {
27 lo = lo.wrapping_add(LittleEndian::read_u32(chunk));
28 hi = hi.wrapping_add(lo);
29 }
30 (hi as u64) << 32 | lo as u64
31}
32
33pub type Checksums = ChecksumsV38;
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TypeFingerprint)]
38#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
39pub struct ChecksumsV38 {
40 sums: Vec<u8>,
41}
42
43impl Checksums {
44 pub fn fletcher(checksums: Vec<Checksum>) -> Self {
45 assert_cfg!(target_endian = "little");
46 let checksums_as_u8: &[u8] = &*checksums.as_bytes();
47 Self { sums: checksums_as_u8.to_owned() }
48 }
49
50 pub fn len(&self) -> usize {
51 self.sums.len() / std::mem::size_of::<Checksum>()
52 }
53
54 pub fn maybe_as_ref(&self) -> Result<&[Checksum], Error> {
55 assert_cfg!(target_endian = "little");
56 <[Checksum]>::ref_from_bytes(&self.sums).map_err(|_| FxfsError::Inconsistent.into())
57 }
58
59 pub fn offset_by(&self, amount: usize) -> Self {
60 Checksums { sums: self.sums[amount * std::mem::size_of::<Checksum>()..].to_vec() }
61 }
62
63 pub fn shrunk(&self, len: usize) -> Self {
64 Checksums { sums: self.sums[..len * std::mem::size_of::<Checksum>()].to_vec() }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use crate::checksum::Checksums;
71 use crate::errors::FxfsError;
72
73 #[test]
74 fn checksum_encoding_idempotent() {
75 let mut checksums = vec![0xabu64 << 56, 0x11002200u64, u64::MAX, 0];
76 checksums.reserve_exact(5);
77
78 let encoded = Checksums::fletcher(checksums.clone());
79 let decoded = encoded.maybe_as_ref().unwrap();
80
81 assert_eq!(decoded, &checksums[..]);
82 }
83
84 #[test]
85 fn deserialize_invalid_checksum() {
86 let bad = Checksums { sums: vec![0, 1, 2, 3, 4, 5, 6] };
87 let res = bad.maybe_as_ref().expect_err("deserialization should fail");
88 assert!(FxfsError::Inconsistent.matches(&res));
89 }
90}