Skip to main content

alopex_core/storage/
checksum.rs

1//! チェックサムアルゴリズムの抽象化とユーティリティ。
2//!
3//! CRC32 は常に利用可能、XXH64 は `checksum-xxh64` feature で有効化する。
4
5use crate::storage::format::FormatError;
6use std::io::Read;
7
8/// チェックサムアルゴリズム識別子。
9#[repr(u8)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ChecksumAlgorithm {
12    /// CRC32 (IEEE)。
13    Crc32 = 0,
14    /// XXH64(feature `checksum-xxh64` が必要)。
15    Xxh64 = 1,
16}
17
18/// バイト列に対してチェックサムを計算する(圧縮後データを想定)。
19pub fn compute(data: &[u8], algorithm: ChecksumAlgorithm) -> Result<u64, FormatError> {
20    match algorithm {
21        ChecksumAlgorithm::Crc32 => {
22            let mut hasher = crc32fast::Hasher::new();
23            hasher.update(data);
24            Ok(hasher.finalize() as u64)
25        }
26        ChecksumAlgorithm::Xxh64 => {
27            #[cfg(feature = "checksum-xxh64")]
28            {
29                let mut hasher = xxhash_rust::xxh64::Xxh64::new(0);
30                hasher.update(data);
31                Ok(hasher.digest())
32            }
33            #[cfg(not(feature = "checksum-xxh64"))]
34            {
35                Err(FormatError::UnsupportedChecksum {
36                    algorithm: algorithm as u8,
37                })
38            }
39        }
40    }
41}
42
43/// ストリームに対してチェックサムを計算する(大容量データ向け)。
44pub fn compute_stream<R: Read>(
45    reader: &mut R,
46    algorithm: ChecksumAlgorithm,
47) -> Result<u64, FormatError> {
48    const BUF_SIZE: usize = 16 * 1024;
49    let mut buf = [0u8; BUF_SIZE];
50    match algorithm {
51        ChecksumAlgorithm::Crc32 => {
52            let mut hasher = crc32fast::Hasher::new();
53            loop {
54                let read = reader
55                    .read(&mut buf)
56                    .map_err(|_| FormatError::ChecksumMismatch {
57                        expected: 0,
58                        found: 0,
59                    })?;
60                if read == 0 {
61                    break;
62                }
63                hasher.update(&buf[..read]);
64            }
65            Ok(hasher.finalize() as u64)
66        }
67        ChecksumAlgorithm::Xxh64 => {
68            #[cfg(feature = "checksum-xxh64")]
69            {
70                let mut hasher = xxhash_rust::xxh64::Xxh64::new(0);
71                loop {
72                    let read =
73                        reader
74                            .read(&mut buf)
75                            .map_err(|_| FormatError::ChecksumMismatch {
76                                expected: 0,
77                                found: 0,
78                            })?;
79                    if read == 0 {
80                        break;
81                    }
82                    hasher.update(&buf[..read]);
83                }
84                Ok(hasher.digest())
85            }
86            #[cfg(not(feature = "checksum-xxh64"))]
87            {
88                Err(FormatError::UnsupportedChecksum {
89                    algorithm: algorithm as u8,
90                })
91            }
92        }
93    }
94}
95
96/// バイト列のチェックサムを検証する。計算値が一致しない場合は [`FormatError::ChecksumMismatch`] を返す。
97pub fn verify(data: &[u8], algorithm: ChecksumAlgorithm, expected: u64) -> Result<(), FormatError> {
98    let found = compute(data, algorithm)?;
99    if found == expected {
100        Ok(())
101    } else {
102        Err(FormatError::ChecksumMismatch { expected, found })
103    }
104}
105
106/// ストリームのチェックサムを検証する。計算値が一致しない場合は [`FormatError::ChecksumMismatch`] を返す。
107pub fn verify_stream<R: Read>(
108    reader: &mut R,
109    algorithm: ChecksumAlgorithm,
110    expected: u64,
111) -> Result<(), FormatError> {
112    let found = compute_stream(reader, algorithm)?;
113    if found == expected {
114        Ok(())
115    } else {
116        Err(FormatError::ChecksumMismatch { expected, found })
117    }
118}