use super::Base32;
pub(crate) const BLAKE3_BYTES: usize = 32;
pub(crate) const BASE32_BYTES: usize = 53;
#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub enum Hash {
None,
Blake3([u8; BLAKE3_BYTES]),
}
pub(crate) enum Hasher {
Blake3(blake3::Hasher),
}
impl Default for Hasher {
fn default() -> Self {
Hasher::Blake3(blake3::Hasher::new())
}
}
impl Hasher {
pub(crate) fn update(&mut self, bytes: &[u8]) {
match self {
Hasher::Blake3(ref mut h) => {
h.update(bytes);
}
}
}
pub(crate) fn finish(&self) -> Hash {
match self {
Hasher::Blake3(ref h) => {
let result = h.finalize();
let mut hash = [0; BLAKE3_BYTES];
hash.clone_from_slice(result.as_bytes());
Hash::Blake3(hash)
}
}
}
}
impl std::fmt::Debug for Hash {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{:?}", self.to_base32())
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[repr(u8)]
pub enum HashAlgorithm {
None = 0,
Blake3 = 1,
}
impl Hash {
pub fn to_bytes(&self) -> [u8; 1 + BLAKE3_BYTES] {
match *self {
Hash::None => unimplemented!(),
Hash::Blake3(ref s) => {
let mut out = [0; 1 + BLAKE3_BYTES];
out[0] = HashAlgorithm::Blake3 as u8;
(&mut out[1..]).clone_from_slice(s);
out
}
}
}
pub fn from_bytes(s: &[u8]) -> Option<Self> {
if s.len() >= 1 + BLAKE3_BYTES && s[0] == HashAlgorithm::Blake3 as u8 {
let mut out = [0; BLAKE3_BYTES];
out.clone_from_slice(&s[1..]);
Some(Hash::Blake3(out))
} else {
None
}
}
pub fn from_prefix(s: &str) -> Option<Self> {
let mut b32 = [b'A'; BASE32_BYTES];
if s.len() > BASE32_BYTES {
return None;
}
(&mut b32[..s.len()]).clone_from_slice(s.as_bytes());
let bytes = if let Ok(bytes) = data_encoding::BASE32_NOPAD.decode(&b32) {
bytes
} else {
return None;
};
let mut hash = [0; BLAKE3_BYTES];
hash.clone_from_slice(&bytes[..BLAKE3_BYTES]);
Some(Hash::Blake3(hash))
}
}
impl super::Base32 for Hash {
fn to_base32(&self) -> String {
match *self {
Hash::None => data_encoding::BASE32_NOPAD.encode(&[0]),
Hash::Blake3(ref s) => {
let mut hash = [0; 1 + BLAKE3_BYTES];
hash[BLAKE3_BYTES] = HashAlgorithm::Blake3 as u8;
(&mut hash[..BLAKE3_BYTES]).clone_from_slice(s);
data_encoding::BASE32_NOPAD.encode(&hash)
}
}
}
fn from_base32(s: &[u8]) -> Option<Self> {
let bytes = if let Ok(s) = data_encoding::BASE32_NOPAD.decode(s) {
s
} else {
return None;
};
if bytes == [0] {
Some(Hash::None)
} else if bytes.len() == BLAKE3_BYTES + 1
&& bytes[BLAKE3_BYTES] == HashAlgorithm::Blake3 as u8
{
let mut hash = [0; BLAKE3_BYTES];
hash.clone_from_slice(&bytes[..BLAKE3_BYTES]);
Some(Hash::Blake3(hash))
} else {
None
}
}
}
impl std::str::FromStr for Hash {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(b) = Self::from_base32(s.as_bytes()) {
Ok(b)
} else {
Err(crate::Error::ParseError { s: s.to_string() })
}
}
}
#[test]
fn from_to() {
let mut h = Hasher::default();
h.update(b"blabla");
let h = h.finish();
assert_eq!(Hash::from_base32(&h.to_base32().as_bytes()), Some(h));
let h = Hash::None;
assert_eq!(Hash::from_base32(&h.to_base32().as_bytes()), Some(h));
let b = data_encoding::BASE32_NOPAD.encode(&[19, 18, 17]);
assert_eq!(Hash::from_base32(&b.as_bytes()), None);
}