use sha2::Digest as _;
use std::fmt;
use std::io::{self, Read};
use std::path::Path;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Sha256Digest([u8; 32]);
impl Sha256Digest {
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn to_hex(self) -> String {
let mut output = String::with_capacity(64);
const HEX: &[u8; 16] = b"0123456789abcdef";
for byte in self.0 {
output.push(HEX[usize::from(byte >> 4)] as char);
output.push(HEX[usize::from(byte & 15)] as char);
}
output
}
}
impl fmt::LowerHex for Sha256Digest {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.to_hex())
}
}
impl fmt::Display for Sha256Digest {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.to_hex())
}
}
#[derive(Clone, Default)]
pub struct Sha256Hasher(sha2::Sha256);
impl Sha256Hasher {
pub fn new() -> Self {
Self::default()
}
pub fn update(&mut self, bytes: impl AsRef<[u8]>) {
self.0.update(bytes);
}
pub fn finalize(self) -> Sha256Digest {
Sha256Digest(self.0.finalize().into())
}
pub fn digest(bytes: impl AsRef<[u8]>) -> Sha256Digest {
sha256_bytes(bytes.as_ref())
}
}
pub fn sha256_bytes(bytes: &[u8]) -> Sha256Digest {
let mut hasher = Sha256Hasher::new();
hasher.update(bytes);
hasher.finalize()
}
pub fn sha256_reader(mut reader: impl Read, max_bytes: u64) -> io::Result<Sha256Digest> {
let mut hasher = Sha256Hasher::new();
let mut remaining = max_bytes;
let mut buffer = [0; 64 * 1024];
loop {
let capacity = remaining.saturating_add(1).min(buffer.len() as u64) as usize;
let count = match reader.read(&mut buffer[..capacity]) {
Ok(0) => return Ok(hasher.finalize()),
Ok(count) => count,
Err(error) if error.kind() == io::ErrorKind::Interrupted => continue,
Err(error) => return Err(error),
};
if count as u64 > remaining {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"SHA-256 input exceeds byte limit",
));
}
hasher.update(&buffer[..count]);
remaining -= count as u64;
}
}
pub fn sha256_file(path: impl AsRef<Path>, max_bytes: u64) -> io::Result<Sha256Digest> {
sha256_reader(std::fs::File::open(path)?, max_bytes)
}