Skip to main content

motedb/storage/
checksum.rs

1//! Checksum 验证模块
2//!
3//! 提供统一的数据完整性校验,防止数据损坏和静默错误。
4//!
5//! ## 使用场景
6//! - Superblock 元数据校验
7//! - LSM 文件数据块校验
8//! - WAL 日志记录校验
9//! - Manifest 文件校验
10//!
11//! ## 算法选择
12//! - **CRC32C**: 硬件加速(SSE4.2),适合频繁校验(默认)
13//! - **xxHash**: 极速,适合大数据块
14//!
15//! ## 使用示例
16//! ```ignore
17//! use motedb::storage::checksum::{Checksum, ChecksumType};
18//!
19//! // 写入时计算 checksum
20//! let data = b"Hello, MoteDB!";
21//! let checksum = Checksum::compute(ChecksumType::CRC32C, data);
22//!
23//! // 读取时验证 checksum
24//! Checksum::verify(ChecksumType::CRC32C, data, checksum)?;
25//! ```text
26use crc32fast::Hasher;
27use std::io::{self, Write};
28
29/// Checksum 类型
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31pub enum ChecksumType {
32    /// CRC32C (硬件加速,SSE4.2)
33    #[default]
34    CRC32C,
35    /// 无校验(仅测试用)
36    None,
37}
38
39/// Checksum 计算器
40pub struct Checksum;
41
42impl Checksum {
43    /// 计算数据的 checksum
44    ///
45    /// # 参数
46    /// - `checksum_type`: 校验类型
47    /// - `data`: 待校验的数据
48    ///
49    /// # 返回
50    /// - 32-bit checksum 值
51    ///
52    /// # 示例
53    /// ```ignore
54    /// let checksum = Checksum::compute(ChecksumType::CRC32C, b"data");
55    /// ```
56    pub fn compute(checksum_type: ChecksumType, data: &[u8]) -> u32 {
57        match checksum_type {
58            ChecksumType::CRC32C => {
59                let mut hasher = Hasher::new();
60                hasher.update(data);
61                hasher.finalize()
62            }
63            ChecksumType::None => 0,
64        }
65    }
66
67    /// 验证数据的 checksum
68    ///
69    /// # 参数
70    /// - `checksum_type`: 校验类型
71    /// - `data`: 待验证的数据
72    /// - `expected`: 期望的 checksum 值
73    ///
74    /// # 返回
75    /// - `Ok(())`: 校验通过
76    /// - `Err(ChecksumError)`: 校验失败
77    ///
78    /// # 示例
79    /// ```ignore
80    /// Checksum::verify(ChecksumType::CRC32C, b"data", expected_checksum)?;
81    /// ```
82    pub fn verify(
83        checksum_type: ChecksumType,
84        data: &[u8],
85        expected: u32,
86    ) -> Result<(), ChecksumError> {
87        if checksum_type == ChecksumType::None {
88            return Ok(());
89        }
90
91        let actual = Self::compute(checksum_type, data);
92        if actual != expected {
93            return Err(ChecksumError::Mismatch {
94                expected,
95                actual,
96                data_len: data.len(),
97            });
98        }
99
100        Ok(())
101    }
102
103    /// 增量计算 checksum(用于流式数据)
104    ///
105    /// # 示例
106    /// ```ignore
107    /// let mut builder = ChecksumBuilder::new(ChecksumType::CRC32C);
108    /// builder.update(b"Hello, ");
109    /// builder.update(b"World!");
110    /// let checksum = builder.finalize();
111    /// ```
112    pub fn builder(checksum_type: ChecksumType) -> ChecksumBuilder {
113        ChecksumBuilder::new(checksum_type)
114    }
115
116    /// 为带 checksum 的数据块编码
117    ///
118    /// 格式: [data_len: u32][data: [u8]][checksum: u32]
119    ///
120    /// # 示例
121    /// ```ignore
122    /// let encoded = Checksum::encode_with_checksum(ChecksumType::CRC32C, b"data");
123    /// ```
124    pub fn encode_with_checksum(checksum_type: ChecksumType, data: &[u8]) -> Vec<u8> {
125        let checksum = Self::compute(checksum_type, data);
126        let mut encoded = Vec::with_capacity(4 + data.len() + 4);
127
128        // Data length
129        encoded.extend_from_slice(&(data.len() as u32).to_le_bytes());
130
131        // Data
132        encoded.extend_from_slice(data);
133
134        // Checksum
135        encoded.extend_from_slice(&checksum.to_le_bytes());
136
137        encoded
138    }
139
140    /// 解码并验证带 checksum 的数据块
141    ///
142    /// # 返回
143    /// - `Ok(Vec<u8>)`: 验证通过的数据
144    /// - `Err(ChecksumError)`: 解码失败或校验失败
145    ///
146    /// # 示例
147    /// ```ignore
148    /// let data = Checksum::decode_with_checksum(ChecksumType::CRC32C, &encoded)?;
149    /// ```
150    pub fn decode_with_checksum(
151        checksum_type: ChecksumType,
152        encoded: &[u8],
153    ) -> Result<Vec<u8>, ChecksumError> {
154        if encoded.len() < 8 {
155            return Err(ChecksumError::InvalidFormat("Data too short".to_string()));
156        }
157
158        // Parse data length
159        let data_len =
160            u32::from_le_bytes([encoded[0], encoded[1], encoded[2], encoded[3]]) as usize;
161
162        if encoded.len() != 4 + data_len + 4 {
163            return Err(ChecksumError::InvalidFormat(format!(
164                "Expected {} bytes, got {}",
165                4 + data_len + 4,
166                encoded.len()
167            )));
168        }
169
170        // Extract data and checksum
171        let data = &encoded[4..4 + data_len];
172        let expected_checksum = u32::from_le_bytes([
173            encoded[4 + data_len],
174            encoded[4 + data_len + 1],
175            encoded[4 + data_len + 2],
176            encoded[4 + data_len + 3],
177        ]);
178
179        // Verify checksum
180        Self::verify(checksum_type, data, expected_checksum)?;
181
182        Ok(data.to_vec())
183    }
184}
185
186/// Checksum 增量构建器(用于流式数据)
187pub struct ChecksumBuilder {
188    hasher: Option<Hasher>,
189}
190
191impl ChecksumBuilder {
192    /// 创建新的构建器
193    pub fn new(checksum_type: ChecksumType) -> Self {
194        let hasher = match checksum_type {
195            ChecksumType::CRC32C => Some(Hasher::new()),
196            ChecksumType::None => None,
197        };
198
199        Self { hasher }
200    }
201
202    /// 更新数据
203    pub fn update(&mut self, data: &[u8]) {
204        if let Some(hasher) = &mut self.hasher {
205            hasher.update(data);
206        }
207    }
208
209    /// 完成并返回 checksum
210    pub fn finalize(self) -> u32 {
211        match self.hasher {
212            Some(hasher) => hasher.finalize(),
213            None => 0,
214        }
215    }
216}
217
218impl Write for ChecksumBuilder {
219    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
220        self.update(buf);
221        Ok(buf.len())
222    }
223
224    fn flush(&mut self) -> io::Result<()> {
225        Ok(())
226    }
227}
228
229/// Checksum 错误类型
230#[derive(Debug, thiserror::Error)]
231pub enum ChecksumError {
232    #[error(
233        "Checksum mismatch: expected {expected:#010x}, got {actual:#010x} (data_len={data_len})"
234    )]
235    Mismatch {
236        expected: u32,
237        actual: u32,
238        data_len: usize,
239    },
240
241    #[error("Invalid checksum format: {0}")]
242    InvalidFormat(String),
243
244    #[error("IO error: {0}")]
245    Io(#[from] std::io::Error),
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251
252    #[test]
253    fn test_checksum_basic() {
254        let data = b"Hello, MoteDB!";
255        let checksum = Checksum::compute(ChecksumType::CRC32C, data);
256
257        // 验证成功
258        assert!(Checksum::verify(ChecksumType::CRC32C, data, checksum).is_ok());
259
260        // 验证失败(错误的 checksum)
261        assert!(Checksum::verify(ChecksumType::CRC32C, data, checksum + 1).is_err());
262
263        // 验证失败(数据被篡改)
264        let corrupted = b"Hello, MoteDB?";
265        assert!(Checksum::verify(ChecksumType::CRC32C, corrupted, checksum).is_err());
266    }
267
268    #[test]
269    fn test_checksum_none() {
270        let data = b"Hello, MoteDB!";
271        let checksum = Checksum::compute(ChecksumType::None, data);
272        assert_eq!(checksum, 0);
273
274        // None 类型总是验证通过
275        assert!(Checksum::verify(ChecksumType::None, data, 12345).is_ok());
276    }
277
278    #[test]
279    fn test_checksum_builder() {
280        let data1 = b"Hello, ";
281        let data2 = b"MoteDB!";
282
283        // 增量计算
284        let mut builder = Checksum::builder(ChecksumType::CRC32C);
285        builder.update(data1);
286        builder.update(data2);
287        let checksum1 = builder.finalize();
288
289        // 一次性计算
290        let checksum2 = Checksum::compute(ChecksumType::CRC32C, b"Hello, MoteDB!");
291
292        // 两种方式结果相同
293        assert_eq!(checksum1, checksum2);
294    }
295
296    #[test]
297    fn test_checksum_encode_decode() {
298        let data = b"Hello, MoteDB! This is a test message.";
299
300        // 编码
301        let encoded = Checksum::encode_with_checksum(ChecksumType::CRC32C, data);
302
303        // 解码
304        let decoded = Checksum::decode_with_checksum(ChecksumType::CRC32C, &encoded).unwrap();
305
306        assert_eq!(data, decoded.as_slice());
307    }
308
309    #[test]
310    fn test_checksum_decode_corrupted() {
311        let data = b"Hello, MoteDB!";
312        let mut encoded = Checksum::encode_with_checksum(ChecksumType::CRC32C, data);
313
314        // 篡改数据
315        encoded[10] ^= 0xFF;
316
317        // 解码失败
318        let result = Checksum::decode_with_checksum(ChecksumType::CRC32C, &encoded);
319        assert!(result.is_err());
320        assert!(matches!(
321            result.unwrap_err(),
322            ChecksumError::Mismatch { .. }
323        ));
324    }
325
326    #[test]
327    fn test_checksum_decode_invalid_format() {
328        // 数据太短
329        let short_data = b"abc";
330        let result = Checksum::decode_with_checksum(ChecksumType::CRC32C, short_data);
331        assert!(result.is_err());
332
333        // 长度不匹配
334        let mut invalid = vec![0u8; 20];
335        invalid[0] = 100; // 声称有 100 字节数据,但实际只有 20 字节
336        let result = Checksum::decode_with_checksum(ChecksumType::CRC32C, &invalid);
337        assert!(result.is_err());
338    }
339
340    #[test]
341    fn test_checksum_deterministic() {
342        let data = b"Deterministic test";
343
344        // 多次计算应该得到相同结果
345        let checksum1 = Checksum::compute(ChecksumType::CRC32C, data);
346        let checksum2 = Checksum::compute(ChecksumType::CRC32C, data);
347        let checksum3 = Checksum::compute(ChecksumType::CRC32C, data);
348
349        assert_eq!(checksum1, checksum2);
350        assert_eq!(checksum2, checksum3);
351    }
352
353    #[test]
354    fn test_checksum_empty_data() {
355        let data = b"";
356        let checksum = Checksum::compute(ChecksumType::CRC32C, data);
357
358        // CRC32 对空数据返回 0(这是正确的行为)
359        assert_eq!(checksum, 0);
360        assert!(Checksum::verify(ChecksumType::CRC32C, data, checksum).is_ok());
361    }
362
363    #[test]
364    fn test_checksum_builder_write_trait() {
365        use std::io::Write;
366
367        let mut builder = Checksum::builder(ChecksumType::CRC32C);
368
369        // 使用 Write trait
370        builder.write_all(b"Hello, ").unwrap();
371        builder.write_all(b"MoteDB!").unwrap();
372        builder.flush().unwrap();
373
374        let checksum = builder.finalize();
375
376        // 验证结果
377        let expected = Checksum::compute(ChecksumType::CRC32C, b"Hello, MoteDB!");
378        assert_eq!(checksum, expected);
379    }
380}