1mod hash64;
37
38pub use hash64::Hash64;
39
40use crate::bases::*;
41use crate::rand::UnspecifiedRandError;
42#[cfg(target_arch = "wasm32")]
43use cryptoxide::{digest::Digest, sha2::Sha256};
44#[cfg(not(target_arch = "wasm32"))]
45use ring::digest;
46use serde::{Deserialize, Serialize};
47use std::{
48 fmt::{Debug, Display, Error, Formatter},
49 str::FromStr,
50};
51
52const BLAKE3_CUTOFF: usize = 1 << 17;
53
54#[derive(
58 Copy,
59 Clone,
60 Deserialize,
61 Eq,
62 Hash,
63 Ord,
64 PartialEq,
65 PartialOrd,
66 Serialize,
67 zerocopy::AsBytes,
68 zerocopy::FromBytes,
69)]
70#[repr(transparent)]
71pub struct Hash(pub [u8; 32]);
72
73impl AsRef<[u8]> for Hash {
74 fn as_ref(&self) -> &[u8] {
75 &self.0[..]
76 }
77}
78
79impl Display for Hash {
80 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
81 write!(f, "{}", self.to_hex())
82 }
83}
84
85impl Debug for Hash {
86 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
87 write!(f, "Hash({})", self)
88 }
89}
90
91impl Default for Hash {
92 fn default() -> Hash {
93 Hash([0; 32])
94 }
95}
96
97impl FromStr for Hash {
98 type Err = BaseConversionError;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 Hash::from_hex(s)
102 }
103}
104
105impl Hash {
106 pub const SIZE_IN_BYTES: usize = 32;
108
109 #[inline]
111 pub fn random() -> Result<Self, UnspecifiedRandError> {
112 let random_bytes = crate::rand::gen_32_bytes().map_err(|_| UnspecifiedRandError)?;
113 Ok(Hash(random_bytes))
114 }
115
116 #[cfg(target_arch = "wasm32")]
117 #[cfg(not(tarpaulin_include))]
118 pub fn compute(datas: &[u8]) -> Hash {
120 let mut hasher = Sha256::new();
121 hasher.input(datas);
122 let mut hash_buffer = [0u8; 32];
123 hasher.result(&mut hash_buffer);
124 Hash(hash_buffer)
125 }
126 #[cfg(not(target_arch = "wasm32"))]
127 pub fn compute(datas: &[u8]) -> Hash {
129 let mut hash_buffer = [0u8; 32];
130 hash_buffer.copy_from_slice(digest::digest(&digest::SHA256, datas).as_ref());
131 Hash(hash_buffer)
132 }
133
134 #[cfg(target_arch = "wasm32")]
135 #[cfg(not(tarpaulin_include))]
136 pub fn compute_multipart(data_parts: &[&[u8]]) -> Hash {
138 let mut hasher = Sha256::new();
139 for data in data_parts {
140 hasher.input(data);
141 }
142 let mut hash_buffer = [0u8; 32];
143 hasher.result(&mut hash_buffer);
144 Hash(hash_buffer)
145 }
146 #[cfg(not(target_arch = "wasm32"))]
147 pub fn compute_multipart(data_parts: &[&[u8]]) -> Hash {
149 let mut ctx = digest::Context::new(&digest::SHA256);
150 for data in data_parts {
151 ctx.update(data);
152 }
153 let mut hash_buffer = [0u8; 32];
154 hash_buffer.copy_from_slice(ctx.finish().as_ref());
155 Hash(hash_buffer)
156 }
157
158 pub fn compute_blake3(datas: &[u8]) -> Hash {
160 if datas.len() > BLAKE3_CUTOFF {
161 let mut hasher = blake3::Hasher::new();
162 hasher.update_with_join::<blake3::join::RayonJoin>(datas);
163 let hash = hasher.finalize();
164 Hash(hash.into())
165 } else {
166 Hash(blake3::hash(datas).into())
167 }
168 }
169
170 pub fn to_bytes_vector(&self) -> Vec<u8> {
172 self.0.to_vec()
173 }
174
175 pub fn to_hex(&self) -> String {
177 let strings: Vec<String> = self.0.iter().map(|b| format!("{:02X}", b)).collect();
178
179 strings.join("")
180 }
181
182 #[inline]
187 pub fn from_hex(text: &str) -> Result<Hash, BaseConversionError> {
188 Ok(Hash(b16::str_hex_to_32bytes(text)?))
189 }
190
191 pub const fn max() -> Hash {
195 Hash([255; 32])
196 }
197}
198
199#[cfg(test)]
200mod tests {
201
202 use super::*;
203 use unwrap::unwrap;
204
205 #[test]
206 fn test_hash_random() {
207 let hash1 = Hash::random();
208 let hash2 = Hash::random();
209 assert_ne!(hash1, hash2);
210 }
211
212 #[test]
213 fn test_hash_debug() {
214 assert_eq!(
215 "Hash(0000000000000000000000000000000000000000000000000000000000000000)".to_owned(),
216 format!("{:?}", Hash::default()),
217 );
218 }
219
220 #[test]
221 fn test_hash_to_bytes() {
222 assert_eq!(
223 vec![
224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
225 0, 0, 0, 0
226 ],
227 Hash::default().to_bytes_vector(),
228 );
229 }
230
231 #[test]
232 fn test_hash_computation() {
233 assert_eq!(
234 unwrap!(Hash::from_hex(
235 "2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824"
236 )),
237 Hash::compute(b"hello"),
238 );
239
240 assert_eq!(
241 unwrap!(Hash::from_hex(
242 "2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824"
243 )),
244 Hash::compute(b"hello"),
245 );
246 }
247
248 #[test]
249 fn test_hash_from_hex() {
250 assert_eq!(
251 Ok(Hash::default()),
252 Hash::from_hex("0000000000000000000000000000000000000000000000000000000000000000")
253 );
254 assert_eq!(
255 Err(BaseConversionError::InvalidLength {
256 expected: 64,
257 found: 65,
258 }),
259 Hash::from_hex("00000000000000000000000000000000000000000000000000000000000000000")
260 );
261 assert_eq!(
262 Err(BaseConversionError::InvalidCharacter {
263 character: '_',
264 offset: 0,
265 }),
266 Hash::from_hex("_000000000000000000000000000000000000000000000000000000000000000")
267 );
268 assert_eq!(
269 Err(BaseConversionError::InvalidCharacter {
270 character: '_',
271 offset: 1,
272 }),
273 Hash::from_hex("0_00000000000000000000000000000000000000000000000000000000000000")
274 );
275 }
276}