intermodal/src/hasher.rs
Casey Rodarmor 9f83661374
Configure clippy and lint on push
Enable as many lints as I can stand. I'll definitely add more exceptions as
`clippy::pedantic` and `clippy::restriction` wear me down.

type: testing
2020-04-07 18:55:45 -07:00

135 lines
3.0 KiB
Rust

use crate::common::*;
pub(crate) struct Hasher {
buffer: Vec<u8>,
length: u64,
md5sum: bool,
piece_bytes_hashed: u64,
piece_length: u32,
pieces: Vec<u8>,
sha1: Sha1,
}
impl Hasher {
pub(crate) fn hash(
root: &Path,
md5sum: bool,
piece_length: u32,
) -> Result<(Mode, Vec<u8>), Error> {
Self::new(md5sum, piece_length).hash_root(root)
}
fn new(md5sum: bool, piece_length: u32) -> Self {
Self {
buffer: vec![0; piece_length.into_usize()],
length: 0,
piece_bytes_hashed: 0,
pieces: Vec::new(),
sha1: Sha1::new(),
md5sum,
piece_length,
}
}
fn hash_root(mut self, root: &Path) -> Result<(Mode, Vec<u8>), Error> {
let single = root
.metadata()
.context(error::Filesystem { path: root })?
.is_file();
if single {
let (md5sum, length) = self.hash_file(&root)?;
if self.piece_bytes_hashed > 0 {
self.pieces.extend(&self.sha1.digest().bytes());
self.sha1.reset();
self.piece_bytes_hashed = 0;
}
Ok((
Mode::Single {
md5sum: md5sum.map(|md5sum| format!("{:x}", md5sum)),
length,
},
self.pieces,
))
} else {
let files = self.hash_dir(root)?;
if self.piece_bytes_hashed > 0 {
self.pieces.extend(&self.sha1.digest().bytes());
self.sha1.reset();
self.piece_bytes_hashed = 0;
}
Ok((Mode::Multiple { files }, self.pieces))
}
}
fn hash_dir(&mut self, dir: &Path) -> Result<Vec<FileInfo>, Error> {
for result in WalkDir::new(dir).sort_by(|a, b| a.file_name().cmp(b.file_name())) {
let entry = result?;
let path = entry.path();
if entry.metadata()?.is_file() {
let (_md5sum, _length) = self.hash_file(path)?;
}
}
Ok(Vec::new())
}
fn hash_file(&mut self, file: &Path) -> Result<(Option<md5::Digest>, u64), Error> {
self
.hash_file_io(file)
.context(error::Filesystem { path: file })
}
fn hash_file_io(&mut self, file: &Path) -> io::Result<(Option<md5::Digest>, u64)> {
let length = file.metadata()?.len();
let mut remaining = length;
let mut file = File::open(file)?;
let mut md5 = if self.md5sum {
Some(md5::Context::new())
} else {
None
};
while remaining > 0 {
let to_buffer: usize = remaining
.min(self.buffer.len().into_u64())
.try_into()
.unwrap();
let buffer = &mut self.buffer[0..to_buffer];
file.read_exact(buffer)?;
for byte in buffer.iter().cloned() {
self.sha1.update(&[byte]);
self.piece_bytes_hashed += 1;
if self.piece_bytes_hashed == self.piece_length.into() {
self.pieces.extend(&self.sha1.digest().bytes());
self.sha1.reset();
self.piece_bytes_hashed = 0;
}
}
if let Some(md5) = md5.as_mut() {
md5.consume(&buffer);
}
remaining -= buffer.len() as u64;
}
self.length += length;
Ok((md5.map(md5::Context::compute), length))
}
}