use blake2::{Blake2b512, Digest};
use std::{
cmp, fmt,
hash::{Hash, Hasher},
};
#[derive(Clone)]
pub struct HashSum {
sum: [u8; Self::MAX_LEN],
length: usize,
}
impl HashSum {
pub const MAX_LEN: usize = 64;
pub(crate) fn b2_digest(data: &[u8]) -> Self {
let mut b2 = Blake2b512::new();
b2.update(data);
let mut sum: [u8; Self::MAX_LEN] = [0; Self::MAX_LEN];
sum.copy_from_slice(&b2.finalize());
Self {
sum,
length: Self::MAX_LEN,
}
}
pub fn to_vec(&self) -> Vec<u8> {
self.slice().to_vec()
}
pub fn slice(&self) -> &[u8] {
&self.sum[..self.length]
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
pub fn truncate(&mut self, new_len: usize) {
if self.length > new_len {
self.length = new_len;
}
}
}
impl<T> From<T> for HashSum
where
T: AsRef<[u8]>,
{
fn from(v: T) -> Self {
let min_len = cmp::min(v.as_ref().len(), Self::MAX_LEN);
let mut sum: [u8; Self::MAX_LEN] = [0; Self::MAX_LEN];
sum[0..min_len].copy_from_slice(&v.as_ref()[0..min_len]);
Self {
sum,
length: min_len,
}
}
}
impl Hash for HashSum {
fn hash<H: Hasher>(&self, state: &mut H) {
self.slice().hash(state);
}
}
impl Eq for HashSum {}
impl PartialEq<Vec<u8>> for HashSum {
fn eq(&self, other: &Vec<u8>) -> bool {
let min_len = cmp::min(self.len(), other.len());
self.sum[0..min_len] == other[0..min_len]
}
}
impl PartialEq<&[u8]> for HashSum {
fn eq(&self, other: &&[u8]) -> bool {
let min_len = cmp::min(self.len(), other.len());
self.sum[0..min_len] == other[0..min_len]
}
}
impl PartialEq<HashSum> for HashSum {
fn eq(&self, other: &Self) -> bool {
let min_len = cmp::min(self.len(), other.len());
self.sum[0..min_len] == other.sum[0..min_len]
}
}
impl fmt::Display for HashSum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.slice() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl fmt::Debug for HashSum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.slice() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_length() {
let zero_length_hash = HashSum::from(&[]);
let hash_with_length = HashSum::from(&[0, 1, 2, 3, 4]);
assert_eq!(zero_length_hash, hash_with_length);
}
#[test]
fn same_sum() {
let hash1 = HashSum::from(&[0, 1, 2, 3, 4]);
let hash2 = HashSum::from(&[0, 1, 2, 3, 4]);
assert_eq!(hash1, hash2);
}
#[test]
fn compare_different_sum_diferent_lengths() {
let hash1 = HashSum::from(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
let hash2 = HashSum::from(&[0, 1, 2, 3, 4, 0]);
assert_ne!(hash1, hash2);
}
#[test]
fn compare_same_sum_diferent_lengths() {
let hash1 = HashSum::from(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
let hash2 = HashSum::from(&[0, 1, 2, 3, 4]);
assert_eq!(hash1, hash2);
}
#[test]
fn compare_different_sum_same_lengths() {
let hash1 = HashSum::from(&[0, 1, 2, 3, 4, 5]);
let hash2 = HashSum::from(&[0, 1, 2, 3, 4, 0]);
assert_ne!(hash1, hash2);
}
}