use sha2::Sha256;
use sha2::Digest;
use byteorder::{LittleEndian, WriteBytesExt};
use paste::paste;
pub use simple_hash_macro::Hashable;
pub trait Hasher {
type Output;
fn update<D: AsRef<[u8]>>(&mut self, data: D);
fn finish(self) -> Self::Output;
fn digest<H: Hashable>(data: &H) -> Self::Output;
}
pub trait Hashable {
fn update<H: Hasher>(&self, h: &mut H);
fn digest<H: Hasher>(&self) -> <H as Hasher>::Output where Self: Sized {
H::digest(self)
}
}
impl Hasher for Sha256 {
type Output = [u8; 32];
fn update<D: AsRef<[u8]>>(&mut self, data: D) {
Digest::update(self, data);
}
fn finish(self) -> Self::Output {
let res = self.finalize();
let mut out = [0; 32];
for i in 0..res.len() {
out[i] = res[i];
}
out
}
fn digest<H: Hashable>(data: &H) -> Self::Output {
let mut sha = Sha256::new();
data.update(&mut sha);
sha.finish()
}
}
impl Hashable for u8 {
fn update<H: Hasher>(&self, h: &mut H) {
let mut buf = [0u8; std::mem::size_of::<u8>()];
let mut b = &mut buf[..];
b.write_u8(*self).unwrap();
h.update(buf.as_slice());
}
}
impl Hashable for bool {
fn update<H: Hasher>(&self, h: &mut H) {
let mut buf = [0u8; std::mem::size_of::<u8>()];
let mut b = &mut buf[..];
b.write_u8(*self as u8).unwrap();
h.update(buf.as_slice());
}
}
impl Hashable for i8 {
fn update<H: Hasher>(&self, h: &mut H) {
let mut buf = [0u8; std::mem::size_of::<u8>()];
let mut b = &mut buf[..];
b.write_i8(*self).unwrap();
h.update(buf.as_slice());
}
}
macro_rules! impl_hashable_for {
($t:ty) => {
impl crate::Hashable for $t {
fn update<H: Hasher>(&self, h: &mut H) {
let mut buf = [0u8; std::mem::size_of::<$t>()];
let mut b = &mut buf[..];
paste! {
b.[<write_ $t>]::<LittleEndian>(*self).unwrap();
}
h.update(buf.as_slice());
}
}
};
}
impl_hashable_for!(i16);
impl_hashable_for!(u16);
impl_hashable_for!(i32);
impl_hashable_for!(u32);
impl_hashable_for!(i64);
impl_hashable_for!(u64);
impl<T: Hashable> Hashable for Vec<T> {
fn update<H: Hasher>(&self, h: &mut H) {
for t in self {
t.update(h);
}
}
}
impl Hashable for String {
fn update<H: Hasher>(&self, h: &mut H) {
for t in self.as_bytes() {
t.update(h);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate as simple_hash;
#[derive(Hashable)]
struct Foo {
a: u8,
b: u16,
c: Vec<u32>,
}
#[test]
fn test_u8() {
let res = (9u8).digest::<sha2::Sha256>();
assert_eq!(res, hex_literal::hex!("2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9"));
}
#[test]
fn test_derive() {
let foo = Foo {
a: 8,
b: 99,
c: vec![0,1,2,3],
};
let res = foo.digest::<sha2::Sha256>();
assert_eq!(res, hex_literal::hex!("929863ce588951eae0cc88755216f96951d431e7d15adbb836d8f1960bb65a9d"));
}
}