1pub use blake3::traits::digest::Digest;
2
3use generic_array::{typenum::U32, GenericArray};
4use serde::{Deserialize, Serialize};
5use std::{
6 array::TryFromSliceError,
7 collections::{BTreeMap, BTreeSet},
8 fmt,
9 ops::Deref,
10 slice,
11};
12
13#[cfg(test)]
14use rand::{
15 distributions::{Distribution, Standard},
16 Rng,
17};
18
19#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
21#[repr(transparent)]
22#[serde(from = "[u8; Self::SIZE]", into = "[u8; Self::SIZE]")]
23pub struct Hash(blake3::Hash);
24
25impl Hash {
26 pub const SIZE: usize = blake3::OUT_LEN;
27}
28
29impl From<[u8; Self::SIZE]> for Hash {
30 fn from(array: [u8; Self::SIZE]) -> Self {
31 Hash(array.into())
32 }
33}
34
35impl From<GenericArray<u8, U32>> for Hash {
36 fn from(array: GenericArray<u8, U32>) -> Self {
37 let array: [u8; Self::SIZE] = array.into();
38 Hash(array.into())
39 }
40}
41
42impl TryFrom<&'_ [u8]> for Hash {
43 type Error = TryFromSliceError;
44
45 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
46 let array: [u8; Self::SIZE] = slice.try_into()?;
47 Ok(Self(array.into()))
48 }
49}
50
51impl From<Hash> for [u8; Hash::SIZE] {
52 fn from(hash: Hash) -> [u8; Hash::SIZE] {
53 hash.0.into()
54 }
55}
56
57impl AsRef<[u8]> for Hash {
58 fn as_ref(&self) -> &[u8] {
59 self.0.as_bytes().as_slice()
60 }
61}
62
63impl PartialOrd for Hash {
64 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
65 Some(self.cmp(other))
66 }
67}
68
69impl Ord for Hash {
70 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
71 self.0.as_bytes().cmp(other.0.as_bytes())
72 }
73}
74
75impl fmt::Display for Hash {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 write!(f, "{self:x}")
78 }
79}
80
81impl fmt::Debug for Hash {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 write!(f, "{self:<8x}")
84 }
85}
86
87impl fmt::LowerHex for Hash {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 hex_fmt::HexFmt(self.as_ref()).fmt(f)
90 }
91}
92
93impl Hashable for Hash {
94 fn update_hash<S: Digest>(&self, state: &mut S) {
95 self.as_ref().update_hash(state)
96 }
97}
98
99derive_sqlx_traits_for_byte_array_wrapper!(Hash);
100
101#[cfg(test)]
102impl Distribution<Hash> for Standard {
103 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Hash {
104 let array: [u8; Hash::SIZE] = rng.r#gen();
105 Hash(array.into())
106 }
107}
108
109pub trait Hashable {
111 fn update_hash<S: Digest>(&self, state: &mut S);
113
114 fn update_hash_slice<S>(slice: &[Self], state: &mut S)
116 where
117 S: Digest,
118 Self: Sized,
119 {
120 for item in slice {
121 item.update_hash(state)
122 }
123 }
124
125 fn hash_with<H>(&self) -> Hash
127 where
128 H: Digest<OutputSize = U32>,
129 {
130 let mut h = H::new();
131 self.update_hash(&mut h);
132 h.finalize().into()
133 }
134
135 fn hash(&self) -> Hash {
137 self.hash_with::<blake3::Hasher>()
138 }
139}
140
141impl<T> Hashable for &'_ T
142where
143 T: Hashable + ?Sized,
144{
145 fn update_hash<S: Digest>(&self, state: &mut S) {
146 (**self).update_hash(state);
147 }
148}
149
150impl Hashable for u8 {
151 fn update_hash<S: Digest>(&self, state: &mut S) {
152 state.update(slice::from_ref(self))
153 }
154
155 fn update_hash_slice<S: Digest>(slice: &[Self], state: &mut S) {
156 state.update(slice)
157 }
158}
159
160impl Hashable for u32 {
161 fn update_hash<S: Digest>(&self, state: &mut S) {
162 state.update(self.to_le_bytes())
163 }
164}
165
166impl Hashable for u64 {
167 fn update_hash<S: Digest>(&self, state: &mut S) {
168 state.update(self.to_le_bytes())
169 }
170}
171
172impl<T> Hashable for [T]
173where
174 T: Hashable,
175{
176 fn update_hash<S: Digest>(&self, state: &mut S) {
177 (self.len() as u64).update_hash(state);
178 Hashable::update_hash_slice(self, state);
179 }
180}
181
182impl<T, const N: usize> Hashable for [T; N]
183where
184 T: Hashable,
185{
186 fn update_hash<S: Digest>(&self, state: &mut S) {
187 self.as_slice().update_hash(state)
188 }
189}
190
191impl<T> Hashable for Vec<T>
192where
193 T: Hashable,
194{
195 fn update_hash<S: Digest>(&self, state: &mut S) {
196 self.as_slice().update_hash(state);
197 }
198}
199
200impl<K, V> Hashable for BTreeMap<K, V>
201where
202 K: Hashable,
203 V: Hashable,
204{
205 fn update_hash<S: Digest>(&self, state: &mut S) {
206 (self.len() as u64).update_hash(state);
207 for (key, value) in self {
208 key.update_hash(state);
209 value.update_hash(state);
210 }
211 }
212}
213
214impl<T> Hashable for BTreeSet<T>
215where
216 T: Hashable,
217{
218 fn update_hash<S: Digest>(&self, state: &mut S) {
219 (self.len() as u64).update_hash(state);
220 for item in self {
221 item.update_hash(state);
222 }
223 }
224}
225
226macro_rules! impl_hashable_for_tuple {
231 ($($name:ident)+) => {
232 impl<$($name: Hashable),+> Hashable for ($($name,)+) {
233 #[allow(non_snake_case)]
234 fn update_hash<S: Digest>(&self, state: &mut S) {
235 let ($($name,)+) = self;
236 $($name.update_hash(state);)+
237 }
238 }
239 }
240}
241
242impl_hashable_for_tuple!(T0 T1);
243impl_hashable_for_tuple!(T0 T1 T2);
244impl_hashable_for_tuple!(T0 T1 T2 T3);
245
246pub(crate) struct CacheHash<T> {
248 owner: T,
249 hash: Hash,
250}
251
252impl<T> CacheHash<T> {
253 pub fn into_inner(self) -> T {
254 self.owner
255 }
256}
257
258impl<T> Deref for CacheHash<T> {
259 type Target = T;
260
261 fn deref(&self) -> &Self::Target {
262 &self.owner
263 }
264}
265
266impl<T> From<T> for CacheHash<T>
267where
268 T: Hashable,
269{
270 fn from(owner: T) -> Self {
271 let hash = owner.hash();
272 Self { owner, hash }
273 }
274}
275
276impl<T> Clone for CacheHash<T>
277where
278 T: Clone,
279{
280 fn clone(&self) -> Self {
281 Self {
282 owner: self.owner.clone(),
283 hash: self.hash,
284 }
285 }
286}
287
288impl<T> Hashable for CacheHash<T>
289where
290 T: Hashable,
291{
292 fn update_hash<S: Digest>(&self, state: &mut S) {
293 self.owner.update_hash(state)
294 }
295
296 fn hash(&self) -> Hash {
297 self.hash
298 }
299}
300
301impl<T: fmt::Debug> fmt::Debug for CacheHash<T> {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 self.owner.fmt(f)
304 }
305}
306
307#[cfg(test)]
308mod test_utils {
309 use super::Hash;
310 use proptest::{
311 arbitrary::{any, Arbitrary},
312 array::UniformArrayStrategy,
313 num,
314 strategy::{Map, NoShrink, Strategy},
315 };
316
317 impl Arbitrary for Hash {
318 type Parameters = ();
319 type Strategy = Map<
320 NoShrink<UniformArrayStrategy<num::u8::Any, [u8; Hash::SIZE]>>,
321 fn([u8; Hash::SIZE]) -> Self,
322 >;
323
324 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
325 any::<[u8; Hash::SIZE]>().no_shrink().prop_map(Hash::from)
326 }
327 }
328}