1use crc32fast::Hasher;
5
6#[derive(Debug, thiserror::Error)]
7pub enum ChecksumError {
8 #[error("data too short for checksum")]
9 DataTooShort,
10 #[error("checksum mismatch")]
11 ChecksumMismatch,
12}
13
14#[must_use]
15pub fn compute_checksum(data: &[u8]) -> u32 {
16 let mut hasher = Hasher::new();
17 hasher.update(data);
18 hasher.finalize()
19}
20
21#[must_use]
22pub fn encode_with_checksum(data: &[u8]) -> Vec<u8> {
23 let checksum = compute_checksum(data);
24 let mut result = Vec::with_capacity(4 + data.len());
25 result.extend_from_slice(&checksum.to_be_bytes());
26 result.extend_from_slice(data);
27 result
28}
29
30pub fn decode_and_verify(data: &[u8]) -> Result<&[u8], ChecksumError> {
33 if data.len() < 4 {
34 return Err(ChecksumError::DataTooShort);
35 }
36
37 let stored_checksum = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
38 let payload = &data[4..];
39 let computed_checksum = compute_checksum(payload);
40
41 if stored_checksum == computed_checksum {
42 Ok(payload)
43 } else {
44 Err(ChecksumError::ChecksumMismatch)
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn test_compute_checksum_consistency() {
54 let data = b"hello world";
55 let checksum1 = compute_checksum(data);
56 let checksum2 = compute_checksum(data);
57 assert_eq!(checksum1, checksum2);
58 }
59
60 #[test]
61 fn test_encode_with_checksum() {
62 let data = b"test data";
63 let encoded = encode_with_checksum(data);
64
65 assert_eq!(encoded.len(), 4 + data.len());
66 assert_eq!(&encoded[4..], data);
67 }
68
69 #[test]
70 fn test_decode_and_verify_valid() {
71 let data = b"test data";
72 let encoded = encode_with_checksum(data);
73 let decoded = decode_and_verify(&encoded).unwrap();
74 assert_eq!(decoded, data);
75 }
76
77 #[test]
78 fn test_decode_and_verify_corrupted() {
79 let data = b"test data";
80 let mut encoded = encode_with_checksum(data);
81
82 encoded[5] ^= 0xFF;
83
84 let result = decode_and_verify(&encoded);
85 assert!(result.is_err());
86 }
87
88 #[test]
89 fn test_decode_and_verify_too_short() {
90 let data = &[1, 2, 3];
91 let result = decode_and_verify(data);
92 assert!(result.is_err());
93 }
94
95 #[test]
96 fn test_bitflip_detection() {
97 let data = b"important data that must not be corrupted";
98 let encoded = encode_with_checksum(data);
99
100 for i in 4..encoded.len() {
101 let mut corrupted = encoded.clone();
102 corrupted[i] ^= 0x01;
103
104 let result = decode_and_verify(&corrupted);
105 assert!(result.is_err(), "Failed to detect bitflip at position {i}");
106 }
107 }
108}