This repository has no description
2.5 kB
65 lines
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 #[error("Failed to read pack data for checksum verification")]
18 Io(#[from] std::io::Error),
19 }
20}
21
22/// Returns the `index` at which the following `index + 1` value is not an increment over the value at `index`.
23pub fn fan(data: &[u32]) -> Option<usize> {
24 data.windows(2)
25 .enumerate()
26 .find_map(|(win_index, v)| (v[0] > v[1]).then_some(win_index))
27}
28
29/// 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`.
30/// `Ok(expected)` or [`checksum::Error::Verify`] is returned if the hash matches or mismatches.
31/// If the [`checksum::Error::Interrupted`] is returned, the operation was interrupted.
32pub fn checksum_on_disk_or_mmap(
33 data_path: &Path,
34 data: &[u8],
35 expected: gix_hash::ObjectId,
36 object_hash: gix_hash::Kind,
37 progress: &mut dyn Progress,
38 should_interrupt: &AtomicBool,
39) -> Result<gix_hash::ObjectId, checksum::Error> {
40 let data_len_without_trailer = data.len() - object_hash.len_in_bytes();
41 let actual = match gix_hash::bytes_of_file(
42 data_path,
43 data_len_without_trailer as u64,
44 object_hash,
45 progress,
46 should_interrupt,
47 ) {
48 Ok(id) => id,
49 Err(gix_hash::io::Error::Io(err)) if err.kind() == std::io::ErrorKind::Interrupted => {
50 return Err(checksum::Error::Interrupted);
51 }
52 Err(gix_hash::io::Error::Io(_io_err)) => {
53 let start = std::time::Instant::now();
54 let mut hasher = gix_hash::hasher(object_hash);
55 hasher.update(&data[..data_len_without_trailer]);
56 progress.inc_by(data_len_without_trailer);
57 progress.show_throughput(start);
58 hasher.try_finalize()?
59 }
60 Err(gix_hash::io::Error::Hasher(err)) => return Err(checksum::Error::Hasher(err)),
61 };
62
63 actual.verify(&expected)?;
64 Ok(actual)
65}