use sha2::Digest;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use super::error::Error;
#[inline]
pub fn crc32<P: AsRef<Path>>(file: P) -> Result<String, Error> {
let mut reader = File::open(&file)?;
let mut hasher = crc32fast::Hasher::new();
let mut buffer = Vec::with_capacity(16 * 1024);
loop {
let n_read = reader.read_to_end(&mut buffer)?;
if n_read == 0 {
break;
}
hasher.update(&buffer[..n_read]);
}
let checksum = hasher.finalize();
Ok(checksum.to_string())
}
#[inline]
pub fn b2sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::B2, options)
}
#[inline]
pub fn md5sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::Md5, options)
}
#[inline]
pub fn sha1sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::Sha1, options)
}
#[inline]
pub fn sha224sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::Sha224, options)
}
#[inline]
pub fn sha256sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::Sha256, options)
}
#[inline]
pub fn sha384sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::Sha384, options)
}
#[inline]
pub fn sha512sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
checksum(file, HashAlgo::Sha512, options)
}
#[derive(Debug)]
pub struct Options {
pub binary_mode: bool,
}
#[inline]
fn default_binary_mode() -> bool {
cfg!(windows)
}
impl Default for Options {
fn default() -> Self {
Options {
binary_mode: default_binary_mode(),
}
}
}
#[derive(Debug)]
pub struct CheckOptions {
pub binary_mode: bool,
pub status: bool,
pub quiet: bool,
pub strict: bool,
pub warn: bool,
}
impl Default for CheckOptions {
fn default() -> Self {
CheckOptions {
binary_mode: default_binary_mode(),
status: false,
quiet: false,
strict: false,
warn: false,
}
}
}
pub fn md5sum_check<P: AsRef<Path>>(_file: P, _option: &CheckOptions) -> Result<bool, Error> {
Ok(false)
}
#[derive(Debug)]
enum HashAlgo {
B2,
Md5,
Sha1,
Sha224,
Sha256,
Sha384,
Sha512,
}
trait FusionDigest {
fn input(&mut self, input: &[u8]);
fn result(&mut self, out: &mut [u8]);
fn output_bits(&self) -> usize;
fn output_bytes(&self) -> usize {
(self.output_bits() + 7) / 8
}
fn result_str(&mut self) -> String {
let mut buf: Vec<u8> = vec![0; self.output_bytes()];
self.result(&mut buf);
hex::encode(&buf)
}
}
impl FusionDigest for blake2b_simd::State {
fn input(&mut self, input: &[u8]) {
self.update(input);
}
fn result(&mut self, output: &mut [u8]) {
output.copy_from_slice(self.finalize().as_bytes());
}
fn output_bits(&self) -> usize {
512
}
}
impl FusionDigest for md5::Context {
fn input(&mut self, input: &[u8]) {
self.consume(input);
}
fn result(&mut self, output: &mut [u8]) {
output.copy_from_slice(&*self.clone().compute());
}
fn output_bits(&self) -> usize {
128
}
}
impl FusionDigest for sha1::Sha1 {
fn input(&mut self, input: &[u8]) {
self.update(input);
}
fn result(&mut self, output: &mut [u8]) {
output.copy_from_slice(&self.finalize_reset());
}
fn output_bits(&self) -> usize {
160
}
}
macro_rules! impl_digest_for_sha2 {
($type: ty, $bits: expr) => {
impl FusionDigest for $type {
fn input(&mut self, input: &[u8]) {
self.update(input);
}
fn result(&mut self, output: &mut [u8]) {
output.copy_from_slice(&self.clone().finalize());
}
fn output_bits(&self) -> usize {
$bits
}
}
};
}
impl_digest_for_sha2!(sha2::Sha224, 224);
impl_digest_for_sha2!(sha2::Sha256, 256);
impl_digest_for_sha2!(sha2::Sha384, 384);
impl_digest_for_sha2!(sha2::Sha512, 512);
fn digest_for_algo(algo: HashAlgo) -> Box<dyn FusionDigest> {
match algo {
HashAlgo::B2 => Box::new(blake2b_simd::State::new()),
HashAlgo::Md5 => Box::new(md5::Context::new()),
HashAlgo::Sha1 => Box::new(sha1::Sha1::new()),
HashAlgo::Sha224 => Box::new(sha2::Sha224::new()),
HashAlgo::Sha256 => Box::new(sha2::Sha256::new()),
HashAlgo::Sha384 => Box::new(sha2::Sha384::new()),
HashAlgo::Sha512 => Box::new(sha2::Sha512::new()),
}
}
fn checksum<P: AsRef<Path>>(file: P, algo: HashAlgo, _options: &Options) -> Result<String, Error> {
let mut digest = digest_for_algo(algo);
let mut reader = File::open(&file)?;
let mut buffer = Vec::with_capacity(16 * 1024);
loop {
let n_read = reader.read_to_end(&mut buffer)?;
if n_read == 0 {
break;
}
digest.input(&buffer[..n_read]);
}
Ok(digest.result_str())
}
#[cfg(test)]
mod tests {
use super::*;
const RUST_WIKIPEDIA: &str = "tests/Rust_Wikipedia.pdf";
#[test]
fn test_crc32() {
let ret = crc32(RUST_WIKIPEDIA);
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(&hex_str, "2751965731");
}
#[test]
fn test_b2sum() {
let ret = b2sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(&hex_str, "e5bd6bbb61edd4cc800ed4982a8226cf0f9582b717702ee5ff79fa286b0f6c70b0533d1f63661b50fa5e739dd78e74616955d7008d6f5c18715ee52235ef32a3");
}
#[test]
fn test_md5sum() {
let ret = md5sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(&hex_str, "c7f5281f3a03cdd3a247966869cf0ba3");
}
#[test]
fn test_sha1sum() {
let ret = sha1sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(&hex_str, "671c0b590385ac473ec3bd4d1c196363252d5d2b");
}
#[test]
fn test_sha224sum() {
let ret = sha224sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(
&hex_str,
"2cbb67e54546408c512cef08964bb49a3071452347ce1e0ced931a0d"
);
}
#[test]
fn test_sha256sum() {
let ret = sha256sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(
&hex_str,
"79e1819c944cdbe4c02b92647d63a9f9d18f6cc6f3795dcedecc685a8a4d476b"
);
}
#[test]
fn test_sha384sum() {
let ret = sha384sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(
&hex_str,
"e45720b17f461732d4787edd37e23f62149ffb05482fbf2201b885ea6d4f79afcbbd1134112e56d83a479b8fdc02d6cf"
);
}
#[test]
fn test_sha512sum() {
let ret = sha512sum(RUST_WIKIPEDIA, &Options::default());
assert!(ret.is_ok());
let hex_str = ret.unwrap();
assert_eq!(
&hex_str,
"91a731cb4e6d2f3e63eaff98a99ea0dbd7d3ad70c2e671f533dd98271d270f63356bf8dd154a73d539548a0aed1dcfc69f499ed72d91655f451bb029bbd26c82"
);
}
}