diff options
author | evuez <julien@mulga.net> | 2024-04-03 22:43:16 +0200 |
---|---|---|
committer | evuez <julien@mulga.net> | 2024-04-03 22:43:16 +0200 |
commit | 43e1a12b5bce11b4a28a53acca243e35c2be6d3e (patch) | |
tree | 07d64823718bfee063ab7b3d5721ac1e950ae17c /src/common/hash.rs | |
download | carton-43e1a12b5bce11b4a28a53acca243e35c2be6d3e.tar.gz |
Initial commit
Diffstat (limited to 'src/common/hash.rs')
-rw-r--r-- | src/common/hash.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/common/hash.rs b/src/common/hash.rs new file mode 100644 index 0000000..0d46da0 --- /dev/null +++ b/src/common/hash.rs @@ -0,0 +1,109 @@ +use super::BASE32; + +#[derive(Debug)] +pub enum Error { + ReadHash(String), + InvalidBytes, +} + +pub const BLAKE3_BYTES: usize = 32; + +#[derive(Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub enum Hash { + Blake3([u8; BLAKE3_BYTES]), +} + +pub enum Hasher { + Blake3(blake3::Hasher), +} + +impl Default for Hasher { + fn default() -> Self { + Hasher::Blake3(blake3::Hasher::new()) + } +} + +impl Hasher { + pub fn update(&mut self, bytes: &[u8]) { + match self { + Hasher::Blake3(ref mut h) => { + h.update(bytes); + } + } + } + + pub 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)] +enum Algo { + Blake3 = 1, +} + +impl Hash { + pub fn to_bytes(self) -> [u8; 1 + BLAKE3_BYTES] { + match self { + Hash::Blake3(ref s) => { + let mut out = [0; 1 + BLAKE3_BYTES]; + out[0] = Algo::Blake3 as u8; + out[1..].clone_from_slice(s); + out + } + } + } + + pub fn from_bytes(s: &[u8]) -> Option<Self> { + if s.len() >= 1 + BLAKE3_BYTES && s[0] == Algo::Blake3 as u8 { + let mut out = [0; BLAKE3_BYTES]; + out.clone_from_slice(&s[1..]); + Some(Hash::Blake3(out)) + } else { + None + } + } + + pub fn validate(s: &[u8]) -> Result<(), Error> { + if s.len() >= 1 + BLAKE3_BYTES && s[0] == Algo::Blake3 as u8 { + Ok(()) + } else { + Err(Error::InvalidBytes) + } + } + + pub fn to_base32(self) -> String { + let hash = self.to_bytes(); + BASE32.encode(&hash) + } + + pub fn from_base32(s: &[u8]) -> Option<Self> { + let bytes = BASE32.decode(s).ok()?; + Self::from_bytes(&bytes) + } +} + +impl std::str::FromStr for Hash { + type Err = Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + if let Some(b) = Self::from_base32(s.as_bytes()) { + Ok(b) + } else { + Err(Error::ReadHash(s.to_string())) + } + } +} |