gix_pack/
verify.rs

1use std::{path::Path, sync::atomic::AtomicBool};
2
3use gix_features::progress::Progress;
4
5///
6pub mod checksum {
7    /// Returned by various methods to verify the checksum of a memory mapped file that might also exist on disk.
8    #[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
20/// Returns the `index` at which the following `index + 1` value is not an increment over the value at `index`.
21pub 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
27/// Calculate the hash of the given kind by trying to read the file from disk at `data_path` or falling back on the mapped content in `data`.
28/// `Ok(expected)` or [`checksum::Error::Verify`] is returned if the hash matches or mismatches.
29/// If the [`checksum::Error::Interrupted`] is returned, the operation was interrupted.
30pub 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}