use crate::{ConcurrencyExt, Dimension, DimensionError, FileInfo, WallSwitchResult};
use blake3::Hasher;
use image::ImageReader;
use std::{
fs::File,
io::{BufReader, Error, Read},
path::PathBuf,
thread,
};
const BUFFER_SIZE: usize = 64 * 1024;
pub fn probe_image_dimension(path: &PathBuf) -> WallSwitchResult<Dimension> {
let reader = ImageReader::open(path)?.with_guessed_format()?;
let (width, height) = reader
.into_dimensions()
.map_err(|err| DimensionError::ReadFailed {
path: path.clone(),
source: err,
})?;
Ok(Dimension {
width: width as u64,
height: height as u64,
})
}
fn compute_single_hash(file_info: &mut FileInfo) -> WallSwitchResult<()> {
let file = File::open(&file_info.path)?;
let reader = BufReader::with_capacity(BUFFER_SIZE, file);
let hash = get_hash(reader).map_err(|err| Error::other(err.to_string()))?;
file_info.hash = hash;
Ok(())
}
pub fn compute_hashes_parallel(files: &mut [FileInfo]) {
let chunk_size = files.get_chunk_size(files.len());
thread::scope(|scope| {
for chunk in files.chunks_mut(chunk_size) {
scope.spawn(move || {
for file_info in chunk {
if let Err(err) = compute_single_hash(file_info) {
eprintln!(
"Failed to compute BLAKE3 hash for '{}': {}",
file_info.path.display(),
err
);
}
}
});
}
});
}
pub fn get_hash(mut reader: impl Read) -> WallSwitchResult<String> {
let mut hasher = Hasher::new();
let mut buffer = [0_u8; BUFFER_SIZE];
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
hasher.update(&buffer[..count]);
}
Ok(hasher.finalize().to_hex().to_string())
}