use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
use crate::error::{Result, io_context};
use crate::i18n::tr_path;
const HASH_BUFFER_SIZE: usize = 1024 * 1024;
pub type Blake3Digest = [u8; 32];
pub fn blake3_file(path: &Path) -> Result<Blake3Digest> {
let file = io_context(
tr_path("io.open_file_for_hash", path.display()),
File::open(path),
)?;
blake3_reader(path, file)
}
pub fn blake3_reader(path: &Path, reader: impl Read) -> Result<Blake3Digest> {
let mut reader = BufReader::with_capacity(HASH_BUFFER_SIZE, reader);
let mut hasher = blake3::Hasher::new();
let mut buffer = vec![0_u8; HASH_BUFFER_SIZE];
loop {
let read = io_context(
tr_path("io.read_file_for_hash", path.display()),
reader.read(&mut buffer),
)?;
if read == 0 {
break;
}
hasher.update(&buffer[..read]);
}
Ok(*hasher.finalize().as_bytes())
}
pub fn same_blake3(left: &Path, right: &Path) -> Result<bool> {
Ok(blake3_file(left)? == blake3_file(right)?)
}
#[cfg(test)]
mod tests {
use std::fs;
use tempfile::tempdir;
use super::*;
#[test]
fn blake3_file_works_on_small_stack_thread()
-> std::result::Result<(), Box<dyn std::error::Error>> {
let root = tempdir()?;
let path = root.path().join("sample.bin");
fs::write(&path, vec![42_u8; 64 * 1024])?;
let digest = std::thread::Builder::new()
.stack_size(128 * 1024)
.spawn({
let path = path.clone();
move || blake3_file(&path)
})?
.join()
.expect("small-stack hashing thread should not panic")?;
assert_eq!(digest, blake3_file(&path)?);
Ok(())
}
}