use tokio::fs::File;
use tokio::io::{self, AsyncReadExt, AsyncSeekExt};
use super::*;
#[must_use = "the hash result should be used"]
pub async fn oshash_async<T: AsRef<str>>(path: T) -> Result<String, HashError> {
let mut f = File::open(path.as_ref()).await?;
let len: u64 = f.metadata().await?.len();
oshash_buf_async(&mut f, len).await
}
#[must_use = "the hash result should be used"]
pub async fn oshash_buf_async<T>(file: &mut T, len: u64) -> Result<String, HashError>
where
T: AsyncSeekExt + AsyncReadExt + Unpin,
{
if len < MIN_FILE_SIZE as u64 {
return Err(HashError::FileTooSmall);
}
let current_offset = file.stream_position().await?;
let mut file_hash: u64 = len;
let mut buffer = vec![0u8; CHUNK_SIZE];
file.seek(io::SeekFrom::Start(0)).await?;
file.read_exact(&mut buffer).await?;
for chunk in buffer.chunks_exact(8) {
file_hash = file_hash.wrapping_add(u64::from_le_bytes(
chunk.try_into().expect("chunk size is 8"),
));
}
file.seek(io::SeekFrom::End(-(CHUNK_SIZE as i64))).await?;
file.read_exact(&mut buffer).await?;
for chunk in buffer.chunks_exact(8) {
file_hash = file_hash.wrapping_add(u64::from_le_bytes(
chunk.try_into().expect("chunk size is 8"),
));
}
file.seek(io::SeekFrom::Start(current_offset)).await?;
Ok(format!("{file_hash:016x}"))
}