bee_crypto/ternary/
hash.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use bee_ternary::{raw::RawEncoding, Btrit, Trits, T1B1};
5
6use std::{
7    cmp::PartialEq,
8    fmt, hash,
9    ops::{Deref, DerefMut},
10};
11
12#[derive(Debug)]
13pub enum Error {
14    WrongLength,
15}
16
17/// The length of a hash in units of balanced trits.
18pub const HASH_LENGTH: usize = 243;
19
20/// Ternary cryptographic hash.
21#[derive(Copy, Clone)]
22pub struct Hash([Btrit; HASH_LENGTH]);
23
24impl Hash {
25    /// Creates a hash filled with zeros.
26    pub fn zeros() -> Self {
27        Self([Btrit::Zero; HASH_LENGTH])
28    }
29
30    /// Interpret the `Hash` as a trit slice.
31    pub fn as_trits(&self) -> &Trits<T1B1> {
32        &*self
33    }
34
35    /// Interpret the `Hash` as a mutable trit slice.
36    pub fn as_trits_mut(&mut self) -> &mut Trits<T1B1> {
37        &mut *self
38    }
39
40    /// Returns the weight - number of ending 0s - of the `Hash`.
41    #[allow(clippy::cast_possible_truncation)] // `HASH_LENGTH` is smaller than `u8::MAX`.
42    pub fn weight(&self) -> u8 {
43        self.iter().rev().take_while(|t| *t == Btrit::Zero).count() as u8
44    }
45}
46
47impl<'a, T: RawEncoding<Trit = Btrit>> TryFrom<&'a Trits<T>> for Hash {
48    type Error = Error;
49
50    fn try_from(trits: &'a Trits<T>) -> Result<Self, Self::Error> {
51        if trits.len() == HASH_LENGTH {
52            let mut hash = Self([Btrit::Zero; HASH_LENGTH]);
53            hash.copy_from(trits);
54            Ok(hash)
55        } else {
56            Err(Error::WrongLength)
57        }
58    }
59}
60
61impl Deref for Hash {
62    type Target = Trits<T1B1>;
63
64    fn deref(&self) -> &Trits<T1B1> {
65        <&Trits>::from(&self.0 as &[_])
66    }
67}
68
69impl DerefMut for Hash {
70    fn deref_mut(&mut self) -> &mut Trits<T1B1> {
71        <&mut Trits>::from(&mut self.0 as &mut [_])
72    }
73}
74
75impl PartialEq for Hash {
76    fn eq(&self, other: &Self) -> bool {
77        self.as_trits() == other.as_trits()
78    }
79}
80
81impl Eq for Hash {}
82
83impl fmt::Display for Hash {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        fmt::Debug::fmt(self, f)
86    }
87}
88
89impl fmt::Debug for Hash {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        write!(f, "{:?}", self.as_trits())
92    }
93}
94
95impl hash::Hash for Hash {
96    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
97        self.0.hash(hasher)
98    }
99}