async_hash/
lib.rs

1//! Provides traits `Hash`, `HashStream`, and `HashTryStream` for SHA-2 hashing data
2//! which must be accessed asynchronously, e.g. a [`Stream`] or database table.
3//!
4//! `Hash` is implemented for standard Rust types:
5//!
6//!  - **Primitive types**:
7//!    - bool
8//!    - i8, i16, i32, i64, i128, isize
9//!    - u8, u16, u32, u64, u128, usize
10//!    - f32, f64
11//!    - &str
12//!    - String
13//!  - **Common standard library types**:
14//!    - Option\<T\>
15//!    - PhantomData\<T\>
16//!  - **Compound types**:
17//!    - \[T; 0\] through \[T; 32\]
18//!    - tuples up to size 16
19//!  - **Collection types**:
20//!    - BTreeMap\<K, V\>
21//!    - BTreeSet\<T\>
22//!    - BinaryHeap\<T\>
23//!    - LinkedList\<T\>
24//!    - VecDeque\<T\>
25//!    - Vec\<T\>
26//!  - **Other types**:
27//!    - SmallVec\<V\> (requires the `smallvec` feature flag)
28//!
29//! **IMPORTANT**: hashing is order-dependent. Do not implement the traits in this crate for
30//! any data structure which does not have a consistent order. Consider using the [`collate`] crate
31//! if you need to use a type which does not implement [`Ord`].
32//!
33//! [`collate`]: http://docs.rs/collate
34
35use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
36
37use futures::future::{FutureExt, TryFutureExt};
38use futures::stream::{Stream, StreamExt, TryStream, TryStreamExt};
39
40pub use sha2::digest::generic_array;
41pub use sha2::digest::{Digest, Output};
42pub use sha2::Sha256;
43
44/// Trait to compute a SHA-2 hash using the digest type `D`
45pub trait Hash<D: Digest>: Sized {
46    /// Compute the SHA-2 hash of this value
47    fn hash(self) -> Output<D>;
48}
49
50impl<D: Digest> Hash<D> for () {
51    fn hash(self) -> Output<D> {
52        default_hash::<D>()
53    }
54}
55
56impl<D: Digest> Hash<D> for bool {
57    fn hash(self) -> Output<D> {
58        D::digest([self as u8])
59    }
60}
61
62macro_rules! hash_number {
63    ($n:literal, $ty:ty) => {
64        impl<D: Digest> Hash<D> for $ty {
65            fn hash(self) -> Output<D> {
66                D::digest(self.to_be_bytes())
67            }
68        }
69    };
70}
71
72hash_number!(4, f32);
73hash_number!(8, f64);
74hash_number!(1, i8);
75hash_number!(2, i16);
76hash_number!(4, i32);
77hash_number!(8, i64);
78hash_number!(16, i128);
79hash_number!(1, u8);
80hash_number!(2, u16);
81hash_number!(4, u32);
82hash_number!(8, u64);
83hash_number!(16, u128);
84
85impl<D: Digest> Hash<D> for isize {
86    fn hash(self) -> Output<D> {
87        Hash::<D>::hash(self as i64)
88    }
89}
90
91impl<D: Digest> Hash<D> for usize {
92    fn hash(self) -> Output<D> {
93        Hash::<D>::hash(self as u64)
94    }
95}
96
97impl<'a, D: Digest> Hash<D> for &'a str {
98    fn hash(self) -> Output<D> {
99        D::digest(self.as_bytes())
100    }
101}
102
103impl<D: Digest> Hash<D> for String {
104    fn hash(self) -> Output<D> {
105        Hash::<D>::hash(self.as_str())
106    }
107}
108
109impl<'a, D: Digest> Hash<D> for &'a String {
110    fn hash(self) -> Output<D> {
111        Hash::<D>::hash(self.as_str())
112    }
113}
114
115impl<D: Digest, T: Hash<D>> Hash<D> for Option<T> {
116    fn hash(self) -> Output<D> {
117        if let Some(value) = self {
118            value.hash()
119        } else {
120            default_hash::<D>()
121        }
122    }
123}
124
125macro_rules! hash_tuple {
126    ($($len:expr => ($($n:tt $name:ident)+))+) => {
127        $(
128            impl<D: Digest, $($name),+> Hash<D> for ($($name,)+)
129            where
130                $($name: Hash<D>,)+
131            {
132                fn hash(self) -> Output<D> {
133                    let mut hasher = D::new();
134                    $(
135                        let hash = self.$n.hash();
136                        hasher.update(hash);
137                    )+
138                    hasher.finalize()
139                }
140            }
141
142            impl<'a, D: Digest, $($name),+> Hash<D> for &'a ($($name,)+)
143            where
144                $(&'a $name: Hash<D>,)+
145            {
146                fn hash(self) -> Output<D> {
147                    let mut hasher = D::new();
148                    $(
149                        let hash = self.$n.hash();
150                        hasher.update(hash);
151                    )+
152                    hasher.finalize()
153                }
154            }
155        )+
156    }
157}
158
159hash_tuple! {
160    1 => (0 T0)
161    2 => (0 T0 1 T1)
162    3 => (0 T0 1 T1 2 T2)
163    4 => (0 T0 1 T1 2 T2 3 T3)
164    5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
165    6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
166    7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
167    8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
168    9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
169    10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
170    11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
171    12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
172    13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
173    14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
174    15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
175    16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
176}
177
178impl<D: Digest, T: Hash<D>> Hash<D> for [T; 0] {
179    fn hash(self) -> Output<D> {
180        default_hash::<D>()
181    }
182}
183
184macro_rules! hash_array {
185    ($($len:tt)+) => {
186        $(
187            impl<D: Digest, T: Hash<D>> Hash<D> for [T; $len] {
188                fn hash(self) -> Output<D> {
189                    if self.is_empty() {
190                        return default_hash::<D>();
191                    }
192
193                    let mut hasher = D::new();
194
195                    for item in self {
196                        hasher.update(item.hash());
197                    }
198
199                    hasher.finalize()
200                }
201            }
202        )+
203    }
204}
205
206hash_array! {
207    01 02 03 04 05 06 07 08 09 10
208    11 12 13 14 15 16 17 18 19 20
209    21 22 23 24 25 26 27 28 29 30
210    31 32
211}
212
213macro_rules! hash_seq {
214    ($ty:ty) => {
215        impl<D: Digest, T: Hash<D>> Hash<D> for $ty {
216            fn hash(self) -> Output<D> {
217                if self.is_empty() {
218                    return default_hash::<D>();
219                }
220
221                let mut hasher = D::new();
222
223                for item in self.into_iter() {
224                    hasher.update(item.hash());
225                }
226
227                hasher.finalize()
228            }
229        }
230
231        impl<'a, D, T> Hash<D> for &'a $ty
232        where
233            D: Digest,
234            &'a T: Hash<D>,
235        {
236            fn hash(self) -> Output<D> {
237                if self.is_empty() {
238                    return default_hash::<D>();
239                }
240
241                let mut hasher = D::new();
242                for item in self.into_iter() {
243                    hasher.update(item.hash());
244                }
245                hasher.finalize()
246            }
247        }
248    };
249}
250
251hash_seq!(BTreeSet<T>);
252hash_seq!(BinaryHeap<T>);
253hash_seq!(LinkedList<T>);
254hash_seq!(Vec<T>);
255hash_seq!(VecDeque<T>);
256
257#[cfg(feature = "smallvec")]
258impl<const N: usize, D: Digest, T> Hash<D> for smallvec::SmallVec<[T; N]>
259where
260    [T; N]: smallvec::Array,
261    <smallvec::SmallVec<[T; N]> as IntoIterator>::Item: Hash<D>,
262{
263    fn hash(self) -> Output<D> {
264        if self.is_empty() {
265            return default_hash::<D>();
266        }
267
268        let mut hasher = D::new();
269
270        for item in self.into_iter() {
271            hasher.update(item.hash());
272        }
273
274        hasher.finalize()
275    }
276}
277
278#[cfg(feature = "smallvec")]
279impl<'a, const N: usize, D: Digest, T> Hash<D> for &'a smallvec::SmallVec<[T; N]>
280where
281    [T; N]: smallvec::Array,
282    <&'a smallvec::SmallVec<[T; N]> as IntoIterator>::Item: Hash<D>,
283{
284    fn hash(self) -> Output<D> {
285        if self.is_empty() {
286            return default_hash::<D>();
287        }
288
289        let mut hasher = D::new();
290
291        for item in self.into_iter() {
292            hasher.update(item.hash());
293        }
294
295        hasher.finalize()
296    }
297}
298
299impl<D: Digest, K: Hash<D>, V: Hash<D>> Hash<D> for BTreeMap<K, V> {
300    fn hash(self) -> Output<D> {
301        if self.is_empty() {
302            return default_hash::<D>();
303        }
304
305        let mut hasher = D::new();
306
307        for item in self {
308            hasher.update(item.hash());
309        }
310
311        hasher.finalize()
312    }
313}
314
315impl<'a, D, K, V> Hash<D> for &'a BTreeMap<K, V>
316where
317    D: Digest,
318    &'a K: Hash<D>,
319    &'a V: Hash<D>,
320{
321    fn hash(self) -> Output<D> {
322        if self.is_empty() {
323            return default_hash::<D>();
324        }
325
326        let mut hasher = D::new();
327
328        for item in self {
329            hasher.update(item.hash());
330        }
331
332        hasher.finalize()
333    }
334}
335
336/// Hash a [`Stream`]
337pub async fn hash_stream<D, T, S>(stream: S) -> Output<D>
338where
339    D: Digest,
340    T: Hash<D>,
341    S: Stream<Item = T>,
342{
343    stream
344        .map(|item| Hash::<D>::hash(item))
345        .fold(D::new(), |mut hasher, hash| {
346            hasher.update(hash);
347            futures::future::ready(hasher)
348        })
349        .map(|hasher| hasher.finalize())
350        .await
351}
352
353/// Hash a [`TryStream`]
354pub async fn hash_try_stream<D, T, E, S>(stream: S) -> Result<Output<D>, E>
355where
356    D: Digest,
357    T: Hash<D>,
358    E: std::error::Error,
359    S: TryStream<Ok = T, Error = E>,
360{
361    stream
362        .map_ok(|item| Hash::<D>::hash(item))
363        .try_fold(D::new(), |mut hasher, hash| {
364            hasher.update(hash);
365            futures::future::ready(Ok(hasher))
366        })
367        .map_ok(|hasher| hasher.finalize())
368        .await
369}
370
371/// Construct an empty hash
372pub fn default_hash<D: Digest>() -> Output<D> {
373    generic_array::GenericArray::default()
374}