1use std::{path::Path, sync::atomic::AtomicBool};
2
3use gix_features::progress::Progress;
4
5pub mod checksum {
7 #[derive(thiserror::Error, Debug)]
9 #[allow(missing_docs)]
10 pub enum Error {
11 #[error("Interrupted by user")]
12 Interrupted,
13 #[error("Failed to hash data")]
14 Hasher(#[from] gix_hash::hasher::Error),
15 #[error(transparent)]
16 Verify(#[from] gix_hash::verify::Error),
17 }
18}
19
20pub fn fan(data: &[u32]) -> Option<usize> {
22 data.windows(2)
23 .enumerate()
24 .find_map(|(win_index, v)| (v[0] > v[1]).then_some(win_index))
25}
26
27pub fn checksum_on_disk_or_mmap(
31 data_path: &Path,
32 data: &[u8],
33 expected: gix_hash::ObjectId,
34 object_hash: gix_hash::Kind,
35 progress: &mut dyn Progress,
36 should_interrupt: &AtomicBool,
37) -> Result<gix_hash::ObjectId, checksum::Error> {
38 let data_len_without_trailer = data.len() - object_hash.len_in_bytes();
39 let actual = match gix_hash::bytes_of_file(
40 data_path,
41 data_len_without_trailer as u64,
42 object_hash,
43 progress,
44 should_interrupt,
45 ) {
46 Ok(id) => id,
47 Err(gix_hash::io::Error::Io(err)) if err.kind() == std::io::ErrorKind::Interrupted => {
48 return Err(checksum::Error::Interrupted);
49 }
50 Err(gix_hash::io::Error::Io(_io_err)) => {
51 let start = std::time::Instant::now();
52 let mut hasher = gix_hash::hasher(object_hash);
53 hasher.update(&data[..data_len_without_trailer]);
54 progress.inc_by(data_len_without_trailer);
55 progress.show_throughput(start);
56 hasher.try_finalize()?
57 }
58 Err(gix_hash::io::Error::Hasher(err)) => return Err(checksum::Error::Hasher(err)),
59 };
60
61 actual.verify(&expected)?;
62 Ok(actual)
63}