use io::prelude::*;
use std::fs::File;
use std::io;
use super::*;
#[must_use = "the hash result should be used"]
pub fn oshash<T: AsRef<str>>(path: T) -> Result<String, HashError> {
let mut f = File::open(path.as_ref())?;
let len: u64 = f.metadata()?.len();
oshash_buf(&mut f, len)
}
#[must_use = "the hash result should be used"]
pub fn oshash_buf<T>(file: &mut T, len: u64) -> Result<String, HashError>
where
T: Seek + Read,
{
if len < MIN_FILE_SIZE as u64 {
return Err(HashError::FileTooSmall);
}
let current_offset = file.stream_position()?;
let mut file_hash: u64 = len;
let mut buffer = vec![0u8; CHUNK_SIZE];
file.seek(io::SeekFrom::Start(0))?;
file.read_exact(&mut buffer)?;
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)))?;
file.read_exact(&mut buffer)?;
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))?;
Ok(format!("{file_hash:016x}"))
}