1#[cfg(feature = "b64")]
10use crate::error::DecodeError;
11use crate::error::TryFromError;
12
13use std::convert::{TryFrom, TryInto};
14use std::mem::ManuallyDrop;
15use std::{fmt, ptr};
16
17use blake2::{Blake2b512, Digest};
18use generic_array::{typenum::U64, GenericArray};
19
20#[cfg(feature = "b64")]
21use base64::engine::{general_purpose::URL_SAFE_NO_PAD, Engine};
22
23pub fn hash(data: impl AsRef<[u8]>) -> Hash {
24 Hasher::hash(data)
25}
26
27pub struct Hasher {
28 inner: Blake2b512,
29}
30
31impl Hasher {
32 pub fn new() -> Self {
33 Self {
34 inner: Blake2b512::new(),
35 }
36 }
37
38 pub fn update(&mut self, data: impl AsRef<[u8]>) {
39 self.inner.update(data);
40 }
41
42 pub fn finalize(self) -> Hash {
43 let arr = self.inner.finalize();
44 Hash {
45 bytes: convert_generic_array(arr),
46 }
47 }
48
49 pub fn hash(data: impl AsRef<[u8]>) -> Hash {
50 let mut hasher = Hasher::new();
51 hasher.update(data);
52 hasher.finalize()
53 }
54}
55
56fn convert_generic_array<T>(arr: GenericArray<T, U64>) -> [T; 64] {
57 unsafe {
60 let a = ManuallyDrop::new(arr);
62 ptr::read(&*a as *const GenericArray<T, U64> as *const [T; 64])
63 }
64}
65
66#[derive(Clone, PartialEq, Eq)]
67pub struct Hash {
68 bytes: [u8; 64],
69}
70
71impl Hash {
72 pub const LEN: usize = 64;
73
74 pub fn from_slice(slice: &[u8]) -> Self {
77 slice.try_into().unwrap()
78 }
79
80 pub fn to_bytes(&self) -> [u8; 64] {
81 self.bytes
82 }
83}
84
85#[cfg(not(feature = "b64"))]
86impl fmt::Debug for Hash {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 f.debug_tuple("Hash").field(&self.as_ref()).finish()
89 }
90}
91
92#[cfg(feature = "b64")]
93impl fmt::Debug for Hash {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 f.debug_tuple("Hash").field(&self.to_string()).finish()
96 }
97}
98
99#[cfg(feature = "b64")]
100impl fmt::Display for Hash {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 base64::display::Base64Display::new(self.as_ref(), &URL_SAFE_NO_PAD)
103 .fmt(f)
104 }
105}
106
107impl From<[u8; 64]> for Hash {
108 fn from(bytes: [u8; 64]) -> Self {
109 Self { bytes }
110 }
111}
112
113impl TryFrom<&[u8]> for Hash {
114 type Error = TryFromError;
115
116 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
117 <[u8; 64]>::try_from(v)
118 .map_err(TryFromError::from_any)
119 .map(Self::from)
120 }
121}
122
123#[cfg(feature = "b64")]
124impl crate::FromStr for Hash {
125 type Err = DecodeError;
126
127 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 if s.len() != crate::calculate_b64_len(Self::LEN) {
129 return Err(DecodeError::InvalidLength);
130 }
131
132 let mut bytes = [0u8; Self::LEN];
133 URL_SAFE_NO_PAD
134 .decode_slice_unchecked(s, &mut bytes)
135 .map_err(DecodeError::inv_bytes)
136 .and_then(|_| {
137 Self::try_from(bytes.as_ref()).map_err(DecodeError::inv_bytes)
138 })
139 }
140}
141
142impl AsRef<[u8]> for Hash {
143 fn as_ref(&self) -> &[u8] {
144 &self.bytes
145 }
146}
147
148#[cfg(all(feature = "b64", feature = "serde"))]
149mod impl_serde {
150
151 use super::*;
152
153 use std::borrow::Cow;
154 use std::str::FromStr;
155
156 use _serde::de::Error;
157 use _serde::{Deserialize, Deserializer, Serialize, Serializer};
158
159 impl Serialize for Hash {
160 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 S: Serializer,
163 {
164 serializer.collect_str(&self)
165 }
166 }
167
168 impl<'de> Deserialize<'de> for Hash {
169 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170 where
171 D: Deserializer<'de>,
172 {
173 let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
174 Self::from_str(s.as_ref()).map_err(D::Error::custom)
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181
182 use super::*;
183
184 #[test]
185 fn hash_something() {
186 let bytes: Vec<u8> = (0..=255).collect();
187
188 let hash = Hasher::hash(bytes);
189
190 let hash_bytes = [
191 30, 204, 137, 111, 52, 211, 249, 202, 196, 132, 199, 63, 117, 246,
192 165, 251, 88, 238, 103, 132, 190, 65, 179, 95, 70, 6, 123, 156,
193 101, 198, 58, 103, 148, 211, 215, 68, 17, 44, 101, 63, 115, 221,
194 125, 235, 102, 102, 32, 76, 90, 155, 250, 91, 70, 8, 31, 193, 15,
195 219, 231, 136, 79, 165, 203, 248,
196 ];
197 assert_eq!(hash.to_bytes(), hash_bytes);
198 }
199
200 #[test]
201 #[cfg(feature = "b64")]
202 fn hash_b64() {
203 let bytes: Vec<u8> = (0..=255).collect();
204
205 let hash = Hasher::hash(bytes);
206
207 assert_eq!(
208 hash.to_string(),
209 "HsyJbzTT-crEhMc_dfal-1juZ4S-QbNfRgZ7nGXGOme\
210 U09dEESxlP3PdfetmZiBMWpv6W0YIH8EP2-eIT6XL-A"
211 );
212 }
213}