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<crate::ObjectId, Error> { 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<crate::ObjectId, Error> { 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<crate::ObjectId, Error> { 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<T> { /// The hash implementation. pub hash: Hasher, /// The inner writer. pub inner: T, } impl<T> std::io::Write for Write<T> where T: std::io::Write, { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { let written = self.inner.write(buf)?; self.hash.update(&buf[..written]); Ok(written) } fn flush(&mut self) -> std::io::Result<()> { self.inner.flush() } } impl<T> Write<T> 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;
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<crate::ObjectId, Error> {
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<crate::ObjectId, Error> {
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<crate::ObjectId, Error> {
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<T> {
/// The hash implementation.
pub hash: Hasher,
/// The inner writer.
pub inner: T,
}
impl<T> std::io::Write for Write<T>
where
T: std::io::Write,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let written = self.inner.write(buf)?;
self.hash.update(&buf[..written]);
Ok(written)
}
fn flush(&mut self) -> std::io::Result<()> {
self.inner.flush()
}
}
impl<T> Write<T>
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;