use crate::hasher; /// The error type for I/O operations that compute hashes. #[derive(Debug, thiserror::Error)] #[expect(missing_docs)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), #[error("Failed to hash data")] Hasher(#[from] hasher::Error), } pub(super) mod _impl { use crate::{Hasher, hasher, io::Error}; /// Compute the hash of `kind` for the bytes in the file at `path`, hashing only the first `num_bytes_from_start` /// while initializing and calling `progress`. /// /// `num_bytes_from_start` is useful to avoid reading trailing hashes, which are never part of the hash itself, /// denoting the amount of bytes to hash starting from the beginning of the file. /// /// # Note /// /// * [Interrupts][gix_features::interrupt] are supported. pub fn bytes_of_file( path: &std::path::Path, num_bytes_from_start: u64, kind: crate::Kind, progress: &mut dyn gix_features::progress::Progress, should_interrupt: &std::sync::atomic::AtomicBool, ) -> Result { bytes( &mut std::fs::File::open(path)?, num_bytes_from_start, kind, progress, should_interrupt, ) } /// Similar to [`bytes_of_file`], but operates on a stream of bytes. pub fn bytes( read: &mut dyn std::io::Read, num_bytes_from_start: u64, kind: crate::Kind, progress: &mut dyn gix_features::progress::Progress, should_interrupt: &std::sync::atomic::AtomicBool, ) -> Result { bytes_with_hasher(read, num_bytes_from_start, hasher(kind), progress, should_interrupt) } /// Similar to [`bytes()`], but takes a `hasher` instead of a hash kind. pub fn bytes_with_hasher( read: &mut dyn std::io::Read, num_bytes_from_start: u64, mut hasher: Hasher, progress: &mut dyn gix_features::progress::Progress, should_interrupt: &std::sync::atomic::AtomicBool, ) -> Result { let start = std::time::Instant::now(); // init progress before the possibility for failure, as convenience in case people want to recover progress.init( Some(num_bytes_from_start as gix_features::progress::prodash::progress::Step), gix_features::progress::bytes(), ); const BUF_SIZE: usize = u16::MAX as usize; let mut buf = [0u8; BUF_SIZE]; let mut bytes_left = num_bytes_from_start; while bytes_left > 0 { let out = &mut buf[..BUF_SIZE.min(bytes_left as usize)]; read.read_exact(out)?; bytes_left -= out.len() as u64; progress.inc_by(out.len()); hasher.update(out); if should_interrupt.load(std::sync::atomic::Ordering::SeqCst) { return Err(std::io::Error::other("Interrupted").into()); } } let id = hasher.try_finalize()?; progress.show_throughput(start); Ok(id) } /// A utility to automatically generate a hash while writing into an inner writer. pub struct Write { /// The hash implementation. pub hash: Hasher, /// The inner writer. pub inner: T, } impl std::io::Write for Write where T: std::io::Write, { fn write(&mut self, buf: &[u8]) -> std::io::Result { let written = self.inner.write(buf)?; self.hash.update(&buf[..written]); Ok(written) } fn flush(&mut self) -> std::io::Result<()> { self.inner.flush() } } impl Write where T: std::io::Write, { /// Create a new hash writer which hashes all bytes written to `inner` with a hash of `kind`. pub fn new(inner: T, object_hash: crate::Kind) -> Self { match object_hash { #[cfg(feature = "sha1")] crate::Kind::Sha1 => Write { inner, hash: crate::hasher(object_hash), }, #[cfg(feature = "sha256")] crate::Kind::Sha256 => Write { inner, hash: crate::hasher(object_hash), }, } } } } pub use _impl::Write;