#![cfg(all(feature = "std", feature = "easy-functions"))]
use std::fs::File;
use std::io::Read;
use std::path::Path;
use crate::generate::{Generator, GeneratorError};
use crate::hash::RawFuzzyHash;
use crate::macros::{invariant, optionally_unsafe};
#[derive(Debug)]
pub enum GeneratorOrIOError {
GeneratorError(GeneratorError),
IOError(std::io::Error),
}
impl core::fmt::Display for GeneratorOrIOError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
GeneratorOrIOError::GeneratorError(err) => err.fmt(f),
GeneratorOrIOError::IOError(err) => err.fmt(f),
}
}
}
impl From<GeneratorError> for GeneratorOrIOError {
fn from(value: GeneratorError) -> Self {
GeneratorOrIOError::GeneratorError(value)
}
}
impl From<std::io::Error> for GeneratorOrIOError {
fn from(value: std::io::Error) -> Self {
GeneratorOrIOError::IOError(value)
}
}
impl std::error::Error for GeneratorOrIOError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
GeneratorOrIOError::GeneratorError(err) => Some(err),
GeneratorOrIOError::IOError(err) => Some(err),
}
}
}
const BUFFER_SIZE: usize = 32768;
#[inline]
fn hash_stream_common<R: Read>(
generator: &mut Generator,
reader: &mut R,
) -> Result<RawFuzzyHash, GeneratorOrIOError> {
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let len = reader.read(&mut buffer)?; if len == 0 {
break;
}
optionally_unsafe! {
invariant!(len <= buffer.len());
}
generator.update(&buffer[0..len]);
}
Ok(generator.finalize()?)
}
pub fn hash_stream<R: Read>(reader: &mut R) -> Result<RawFuzzyHash, GeneratorOrIOError> {
let mut generator = Generator::new();
hash_stream_common(&mut generator, reader)
}
pub fn hash_file<P: AsRef<Path>>(path: P) -> Result<RawFuzzyHash, GeneratorOrIOError> {
let mut file = File::open(path)?;
let mut generator = Generator::new();
generator.set_fixed_input_size(file.metadata()?.len())?; hash_stream_common(&mut generator, &mut file)
}
mod tests;