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("index checksum mismatch: expected {expected}, got {actual}")]
14 Mismatch {
15 expected: gix_hash::ObjectId,
16 actual: gix_hash::ObjectId,
17 },
18 }
19}
20
21pub fn fan(data: &[u32]) -> Option<usize> {
23 data.windows(2)
24 .enumerate()
25 .find_map(|(win_index, v)| (v[0] > v[1]).then_some(win_index))
26}
27
28pub fn checksum_on_disk_or_mmap(
32 data_path: &Path,
33 data: &[u8],
34 expected: gix_hash::ObjectId,
35 object_hash: gix_hash::Kind,
36 progress: &mut dyn Progress,
37 should_interrupt: &AtomicBool,
38) -> Result<gix_hash::ObjectId, checksum::Error> {
39 let data_len_without_trailer = data.len() - object_hash.len_in_bytes();
40 let actual = match gix_features::hash::bytes_of_file(
41 data_path,
42 data_len_without_trailer as u64,
43 object_hash,
44 progress,
45 should_interrupt,
46 ) {
47 Ok(id) => id,
48 Err(err) if err.kind() == std::io::ErrorKind::Interrupted => return Err(checksum::Error::Interrupted),
49 Err(_io_err) => {
50 let start = std::time::Instant::now();
51 let mut hasher = gix_features::hash::hasher(object_hash);
52 hasher.update(&data[..data_len_without_trailer]);
53 progress.inc_by(data_len_without_trailer);
54 progress.show_throughput(start);
55 gix_hash::ObjectId::from(hasher.digest())
56 }
57 };
58
59 if actual == expected {
60 Ok(actual)
61 } else {
62 Err(checksum::Error::Mismatch { actual, expected })
63 }
64}