I have a HashMap with different hashing algorithms which implement digest::DynDigest
, but when they are looped over and update()
d the final hash is always the same (Sha224 d14a...
) (you can change the file PathBuf parameter to anything and get the exact same hash every time). So the update()
and/or finalize()
is not updating the instance in the HashMap.
What is the correct way to loop over the different hashers so that HashMap keeps correct updated state?
use digest::{Digest, DynDigest}; // https://crates.io/crates/digest/reverse_dependencies
use sha2; // implements digest::DynDigest
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::{BufReader, Read};
use std::path::PathBuf;
fn checksum_to_hex(bytes: &[u8]) -> String {
let mut s: String = String::new();
for b in bytes {
s.push_str(format!("{:02x}", b).as_str());
}
s
}
pub fn hash_file(
fpath: PathBuf,
hashers: HashMap<&str, Box<dyn DynDigest>>,
) -> io::Result<HashMap<&str, String>> {
let f = File::open(fpath).expect("failed to open file for hashing");
let mut buffer = [0u8; 1048576];
let mut reader = BufReader::new(f);
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
for (_, mut hasher) in hashers.to_owned() {
hasher.update(&buffer[..count]);
}
}
// Hash results per hasher
let mut hashes: HashMap<&str, String> = HashMap::new();
for (k, hasher) in hashers.to_owned() {
let res = checksum_to_hex(hasher.finalize().to_vec().as_slice());
hashes.insert(k, res);
}
Ok(hashes)
}
fn main() {
let mut hashers: HashMap<&str, Box<dyn DynDigest>> = HashMap::new();
hashers.insert("Sha224", Box::new(sha2::Sha224::new()));
hashers.insert("Sha256", Box::new(sha2::Sha256::new()));
hashers.insert("Sha512", Box::new(sha2::Sha512::new()));
for (htype, hashres) in hash_file(PathBuf::from("/bin/ls"), hashers).expect("") {
println!(" {} {}", htype, hashres);
}
}
CodePudding user response:
You need to replace the first hashers.to_owned()
with hashers.iter_mut()
:
pub fn hash_file(
fpath: PathBuf,
mut hashers: HashMap<&str, Box<dyn DynDigest>>,
) -> io::Result<HashMap<&str, String>> {
let f = File::open(fpath).expect("failed to open file for hashing");
let mut buffer = [0u8; 1048576];
let mut reader = BufReader::new(f);
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
for (_, hasher) in hashers.iter_mut() {
// or `for hasher in hashers.values_mut()`
hasher.update(&buffer[..count]);
}
}
// Hash results per hasher
let mut hashes: HashMap<&str, String> = HashMap::new();
for (k, hasher) in hashers {
let res = checksum_to_hex(hasher.finalize().to_vec().as_slice());
hashes.insert(k, res);
}
Ok(hashes)
}
.to_owned()
will clone (create an independent deep copy) the map. When you iterate over that, you're iterating through a different map than the one you eventually return.
Instead, you want to iterate over references to the elements: this is what .iter_mut()
(or .values_mut()
if you only need the values) will give you.
This way, you don't need the second .to_owned()
, but you do need to mark the hashers
argument as mutable.