1use sha2::{Digest, Sha256};
2
3const ALGORITHM: &str = "SHA256";
4const BLOCK_SIZE: usize = 4 * 1024 * 1024; fn encode_hex(bytes: &[u8]) -> String {
8 let mut s = String::with_capacity(bytes.len() * 2);
9 for byte in bytes {
10 s.push(char::from_digit((byte >> 4) as u32, 16).unwrap());
11 s.push(char::from_digit((byte & 0x0f) as u32, 16).unwrap());
12 }
13 s
14}
15
16#[derive(Debug, Clone)]
18pub struct FileIntegrity {
19 pub algorithm: String,
20 pub hash: String,
21 pub block_size: usize,
22 pub blocks: Vec<String>,
23}
24
25impl FileIntegrity {
26 pub fn from_buffer(data: &[u8]) -> Self {
27 let hash = encode_hex(&Sha256::digest(data));
28 let block_count = data.len().div_ceil(BLOCK_SIZE);
29 let mut blocks = Vec::with_capacity(std::cmp::max(block_count, 1));
30
31 if data.is_empty() {
32 blocks.push(encode_hex(&Sha256::digest(data)));
33 } else {
34 let mut offset = 0;
35 while offset < data.len() {
36 let end = std::cmp::min(offset + BLOCK_SIZE, data.len());
37 blocks.push(encode_hex(&Sha256::digest(&data[offset..end])));
38 offset = end;
39 }
40 }
41
42 FileIntegrity {
43 algorithm: ALGORITHM.to_string(),
44 hash,
45 block_size: BLOCK_SIZE,
46 blocks,
47 }
48 }
49
50 pub fn from_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
51 let mut file_hasher = Sha256::new();
52 let mut block_hasher = Sha256::new();
53 let mut current_block_size = 0usize;
54 let mut blocks = Vec::new();
55 let mut buf = vec![0u8; 65536];
56
57 loop {
58 let n = reader.read(&mut buf)?;
59 if n == 0 {
60 break;
61 }
62 let chunk = &buf[..n];
63 file_hasher.update(chunk);
64
65 let mut offset = 0;
66 while offset < chunk.len() {
67 let remaining = BLOCK_SIZE - current_block_size;
68 let end = std::cmp::min(offset + remaining, chunk.len());
69 block_hasher.update(&chunk[offset..end]);
70 current_block_size += end - offset;
71 if current_block_size == BLOCK_SIZE {
72 blocks.push(encode_hex(&block_hasher.finalize_reset()));
73 current_block_size = 0;
74 }
75 offset = end;
76 }
77 }
78
79 if current_block_size > 0 || blocks.is_empty() {
80 blocks.push(encode_hex(&block_hasher.finalize()));
81 }
82
83 Ok(FileIntegrity {
84 algorithm: ALGORITHM.to_string(),
85 hash: encode_hex(&file_hasher.finalize()),
86 block_size: BLOCK_SIZE,
87 blocks,
88 })
89 }
90}