write_hasher/
lib.rs

1//! A hasher that will be a wrapper over any  
2//! [`std::io::Write`][std::io::Write] /  
3//! [`futures::io::AsyncWrite`][futures::io::AsyncWrite] /  
4//! [`tokio::io::AsyncWrite`][tokio::io::AsyncWrite] object  
5//!
6//!  You can wrap any of the previous trait object inside and that will transparently hash the data that is being
7//!  written to it.  
8//!
9//!
10//! The object should implement AsyncRead so that it can wrap some data and then read from that
11//! transparently while offloading the hashing to another thread.
12//! ```rust
13//! extern crate sha2;
14//! use write_hasher::{WriteHasher, MinDigest};
15//! let mut src = std::fs::File::open(".gitignore").unwrap();
16//! let sink = std::io::sink();
17//! let mut hasher = WriteHasher::<sha2::Sha256, _>::new(sink);
18//! std::io::copy(&mut src, &mut hasher).unwrap();
19//! let x = hasher.finalize();
20//! let x = format!("{:x}", x);
21//! assert_eq!(
22//!     "c1e953ee360e77de57f7b02f1b7880bd6a3dc22d1a69e953c2ac2c52cc52d247",
23//!     x
24//! );
25//! ```
26
27#[cfg(all(
28    feature = "digest",
29    any(
30        feature = "concrete_impls",
31        feature = "sha1",
32        feature = "sha2",
33        feature = "md2",
34        feature = "md4",
35        feature = "md5",
36        feature = "blake2",
37        feature = "crc32fast"
38    )
39))]
40compile_error!("Please either use digest feature (for generic impls) or
41               concrete_impls (sha1, sha2, md2, md4, md5, blake2, crc32fast) features (for concrete impls),
42               but not both");
43
44#[cfg(any(feature = "futures", feature = "tokio"))]
45use core::pin::Pin;
46use core::task::Poll;
47#[cfg(feature = "digest")]
48use digest::Digest;
49
50/// A hasher that will be a wrapper over any Write / AsyncWrite object and transparently calculate
51/// hash for any data written to it
52#[cfg_attr(any(feature = "futures", feature = "tokio"), pin_project::pin_project)]
53#[derive(Default)]
54pub struct WriteHasher<D, T> {
55    hasher: D,
56    #[cfg_attr(any(feature = "futures", feature = "tokio"), pin)]
57    inner: T,
58}
59
60impl<D, T> WriteHasher<D, T> {
61    pub fn new_with_hasher(inner: T, hasher: D) -> Self {
62        Self { hasher, inner }
63    }
64
65    pub fn new(inner: T) -> Self
66    where
67        D: Default,
68    {
69        Self {
70            hasher: Default::default(),
71            inner,
72        }
73    }
74}
75
76// #[cfg(feature = "digest")]
77// impl<D: Digest, T> WriteHasher<D, T> {
78//     pub fn new(inner: T) -> Self {
79//         Self {
80//             hasher: D::new(),
81//             inner,
82//         }
83//     }
84// }
85
86#[cfg(feature = "digest")]
87impl<D: Digest + digest::Reset, T> WriteHasher<D, T> {
88    pub fn reset(&mut self) {
89        <D as Digest>::reset(&mut self.hasher)
90    }
91}
92
93/// A minimal version of [`Digest`][digest::digest] trait that is used to implement the WriteHasher
94/// and all implementations of the Digest trait.
95pub trait MinDigest {
96    type Output;
97    fn update(&mut self, data: impl AsRef<[u8]>);
98    fn finalize(self) -> Self::Output;
99}
100
101impl<MD: MinDigest, T> MinDigest for WriteHasher<MD, T> {
102    type Output = MD::Output;
103    fn update(&mut self, data: impl AsRef<[u8]>) {
104        self.hasher.update(data)
105    }
106    fn finalize(self) -> MD::Output {
107        self.hasher.finalize()
108    }
109}
110
111#[cfg(feature = "digest")]
112impl<T: Digest> MinDigest for T {
113    type Output = digest::Output<T>;
114    fn update(&mut self, data: impl AsRef<[u8]>) {
115        <T as Digest>::update(self, data)
116    }
117    fn finalize(self) -> Self::Output {
118        <T as Digest>::finalize(self)
119    }
120}
121
122#[cfg(any(
123    feature = "sha2",
124    feature = "sha1",
125    feature = "md2",
126    feature = "md4",
127    feature = "md5",
128    feature = "blake2"
129))]
130macro_rules! delegate_digest_mindigest {
131    ($($x:ty),*) => {
132        $(
133            impl MinDigest for $x {
134                type Output = digest::Output<$x>;
135                fn update(&mut self, data: impl AsRef<[u8]>) {
136                    <Self as digest::Digest>::update(self, data)
137                }
138                fn finalize(self) -> Self::Output {
139                    <Self as digest::Digest>::finalize(self)
140                }
141            }
142
143            impl<T> crate::WriteHasher<$x, T> {
144                pub fn new(inner: T) -> Self {
145                    Self {
146                        hasher: <$x as ::digest::Digest>::new(),
147                        inner,
148                    }
149                }
150            }
151
152        )*
153    };
154}
155
156#[cfg(feature = "sha2")]
157mod sha2 {
158    use super::MinDigest;
159    delegate_digest_mindigest!(
160        sha2::Sha224,
161        sha2::Sha256,
162        sha2::Sha384,
163        sha2::Sha512,
164        sha2::Sha512_224,
165        sha2::Sha512_256
166    );
167}
168
169#[cfg(feature = "sha1")]
170mod sha1 {
171    use super::MinDigest;
172    delegate_digest_mindigest!(sha1::Sha1);
173}
174
175#[cfg(feature = "md2")]
176mod md2 {
177    use super::MinDigest;
178    delegate_digest_mindigest!(md2::Md2);
179}
180
181#[cfg(feature = "md4")]
182mod md4 {
183    use super::MinDigest;
184    delegate_digest_mindigest!(md4::Md4);
185}
186
187#[cfg(feature = "md5")]
188mod md5 {
189    use super::MinDigest;
190    impl MinDigest for md5::Context {
191        type Output = md5::Digest;
192        fn update(&mut self, data: impl AsRef<[u8]>) {
193            self.consume(data)
194        }
195        fn finalize(self) -> Self::Output {
196            self.compute()
197        }
198    }
199
200    impl<T> crate::WriteHasher<md5::Context, T> {
201        pub fn new(inner: T) -> Self {
202            Self {
203                hasher: md5::Context::new(),
204                inner,
205            }
206        }
207    }
208}
209
210#[cfg(feature = "blake2")]
211mod blake2 {
212    use super::MinDigest;
213    // use digest::consts::*;
214    // use digest::typenum::*;
215
216    // delegate_digest_mindigest!(blake2::Blake2b);
217    delegate_digest_mindigest!(blake2::Blake2b512);
218    // delegate_digest_mindigest!(blake2::Blake2bCore);
219    // delegate_digest_mindigest!(blake2::Blake2bMac512);
220    // delegate_digest_mindigest!(blake2::Blake2bVar);
221    // delegate_digest_mindigest!(blake2::Blake2s);
222    delegate_digest_mindigest!(blake2::Blake2s256);
223    // delegate_digest_mindigest!(blake2::Blake2sCore);
224    // delegate_digest_mindigest!(blake2::Blake2sMac256);
225    // delegate_digest_mindigest!(blake2::Blake2sVar);
226}
227
228#[cfg(feature = "crc32fast")]
229mod crc32fast {
230    use super::MinDigest;
231    impl MinDigest for crc32fast::Hasher {
232        type Output = u32;
233        fn update(&mut self, data: impl AsRef<[u8]>) {
234            self.update(data.as_ref())
235        }
236        fn finalize(self) -> Self::Output {
237            self.finalize()
238        }
239    }
240
241    impl<T> crate::WriteHasher<crc32fast::Hasher, T> {
242        pub fn new(inner: T) -> Self {
243            Self {
244                hasher: crc32fast::Hasher::new(),
245                inner,
246            }
247        }
248    }
249}
250
251// #[cfg(feature = "crc32c")]
252pub mod crc32c {
253    use super::MinDigest;
254    #[repr(transparent)]
255    #[derive(Debug, Default)]
256    pub struct Crc32c(u32);
257
258    impl Crc32c {
259        pub fn new() -> Self {
260            Default::default()
261        }
262    }
263
264    impl MinDigest for Crc32c {
265        type Output = u32;
266        fn update(&mut self, data: impl AsRef<[u8]>) {
267            self.0 = crc32c::crc32c_append(self.0, data.as_ref())
268        }
269        fn finalize(self) -> Self::Output {
270            self.0
271        }
272    }
273}
274
275#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
276#[cfg(feature = "tokio")]
277impl<D: MinDigest, T: tokio::io::AsyncWrite + std::marker::Unpin> tokio::io::AsyncWrite
278    for WriteHasher<D, T>
279{
280    fn poll_write(
281        self: Pin<&mut Self>,
282        cx: &mut std::task::Context<'_>,
283        buf: &[u8],
284    ) -> std::task::Poll<std::io::Result<usize>> {
285        let ah = self.project();
286        let r = ah.inner.poll_write(cx, buf);
287        if let Poll::Ready(Ok(n)) = r {
288            ah.hasher.update(&buf[..n]);
289        }
290        r
291    }
292    fn poll_flush(
293        self: Pin<&mut Self>,
294        cx: &mut std::task::Context<'_>,
295    ) -> std::task::Poll<std::io::Result<()>> {
296        let ah = self.project();
297        ah.inner.poll_flush(cx)
298    }
299    fn poll_shutdown(
300        self: Pin<&mut Self>,
301        cx: &mut std::task::Context<'_>,
302    ) -> std::task::Poll<std::io::Result<()>> {
303        let ah = self.project();
304        ah.inner.poll_shutdown(cx)
305    }
306}
307
308#[cfg(feature = "futures")]
309impl<D: MinDigest, T: futures::io::AsyncWrite + std::marker::Unpin> futures::io::AsyncWrite
310    for WriteHasher<D, T>
311{
312    fn poll_write(
313        self: Pin<&mut Self>,
314        cx: &mut std::task::Context<'_>,
315        buf: &[u8],
316    ) -> std::task::Poll<futures::io::Result<usize>> {
317        let ah = self.project();
318        let r = ah.inner.poll_write(cx, buf);
319        if let Poll::Ready(Ok(n)) = r {
320            ah.hasher.update(&buf[..n]);
321        }
322        r
323    }
324    fn poll_flush(
325        self: Pin<&mut Self>,
326        cx: &mut std::task::Context<'_>,
327    ) -> std::task::Poll<futures::io::Result<()>> {
328        let ah = self.project();
329        ah.inner.poll_flush(cx)
330    }
331    fn poll_close(
332        self: Pin<&mut Self>,
333        cx: &mut std::task::Context<'_>,
334    ) -> std::task::Poll<futures::io::Result<()>> {
335        let ah = self.project();
336        ah.inner.poll_close(cx)
337    }
338}
339
340#[cfg(feature = "stdio")]
341impl<D: MinDigest, T: std::io::Write> std::io::Write for WriteHasher<D, T> {
342    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
343        let r = std::io::Write::write(&mut self.inner, buf);
344        if let Ok(n) = r {
345            MinDigest::update(&mut self.hasher, &buf[..n]);
346        }
347        r
348    }
349    fn flush(&mut self) -> std::io::Result<()> {
350        self.inner.flush()
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    #[tokio::test]
358    #[cfg(feature = "tokio")]
359    #[cfg(any(feature = "sha2", feature = "digest"))]
360    async fn test_read() {
361        extern crate sha2;
362        let mut src = tokio::fs::File::open(".gitignore").await.unwrap();
363        let sink = tokio::io::sink();
364        let mut hasher = WriteHasher::<sha2::Sha256, _>::new(sink);
365        tokio::io::copy(&mut src, &mut hasher).await.unwrap();
366        // hasher.write_all(b"hello worlding").await.unwrap();
367        let x = hasher.finalize();
368        let x = format!("{:x}", x);
369        assert_eq!(
370            "c1e953ee360e77de57f7b02f1b7880bd6a3dc22d1a69e953c2ac2c52cc52d247",
371            x
372        );
373    }
374
375    #[tokio::test]
376    #[cfg(feature = "futures")]
377    #[cfg(any(feature = "sha2", feature = "digest"))]
378    async fn test_read_futures() {
379        extern crate sha2;
380        let src = std::fs::read(".gitignore").unwrap();
381        let src = futures::io::Cursor::new(src);
382        let sink = futures::io::sink();
383        let mut hasher = WriteHasher::<sha2::Sha256, _>::new(sink);
384        futures::io::copy(src, &mut hasher).await.unwrap();
385        // hasher.write_all(b"hello worlding").await.unwrap();
386        let x = hasher.finalize();
387        let x = format!("{:x}", x);
388        assert_eq!(
389            "c1e953ee360e77de57f7b02f1b7880bd6a3dc22d1a69e953c2ac2c52cc52d247",
390            x
391        );
392    }
393
394    #[tokio::test]
395    #[cfg(feature = "tokio")]
396    #[cfg(feature = "crc32fast")]
397    async fn test_crc32() {
398        extern crate crc32fast;
399        let mut src = tokio::fs::File::open(".gitignore").await.unwrap();
400        let sink = tokio::io::sink();
401        let mut hasher =
402            WriteHasher::<crc32fast::Hasher, _>::new_with_hasher(sink, Default::default());
403        tokio::io::copy(&mut src, &mut hasher).await.unwrap();
404        // hasher.write_all(b"hello worlding").await.unwrap();
405        let x = hasher.finalize();
406        assert_eq!(x, 0x705ffe14);
407    }
408
409    #[test]
410    #[cfg(feature = "stdio")]
411    #[cfg(any(feature = "sha2", feature = "digest"))]
412    fn test_read_stdio() {
413        extern crate sha2;
414        let mut src = std::fs::File::open(".gitignore").unwrap();
415        let sink = std::io::sink();
416        let mut hasher = WriteHasher::<sha2::Sha256, _>::new(sink);
417        std::io::copy(&mut src, &mut hasher).unwrap();
418        // hasher.write_all(b"hello worlding").await.unwrap();
419        let x = hasher.finalize();
420        let x = format!("{:x}", x);
421        assert_eq!(
422            "c1e953ee360e77de57f7b02f1b7880bd6a3dc22d1a69e953c2ac2c52cc52d247",
423            x
424        );
425    }
426
427    #[tokio::test]
428    #[ignore]
429    #[cfg(all(feature = "tokio", feature = "stdio"))]
430    #[cfg(any(feature = "crc32c", feature = "digest"))]
431    async fn test_tokio_bigfile() {
432        let mut src = tokio::fs::File::open("file.zip").await.unwrap();
433        let sink = tokio::io::sink();
434        let mut hasher = WriteHasher::<crc32c::Crc32c, _>::new(sink);
435        tokio::io::copy(&mut src, &mut hasher).await.unwrap();
436        // hasher.write_all(b"hello worlding").await.unwrap();
437        let x = hasher.finalize();
438        assert_eq!(x, 0xbd7a7dfe);
439        let mut src = std::fs::File::open("file.zip").unwrap();
440        let sink = std::io::sink();
441        let mut hasher = WriteHasher::<crc32c::Crc32c, _>::new(sink);
442        std::io::copy(&mut src, &mut hasher).unwrap();
443        // hasher.write_all(b"hello worlding").await.unwrap();
444        let y = hasher.finalize();
445        assert_eq!(x, y);
446        assert_eq!(3178921470, x);
447        assert_eq!(3178921470, y);
448    }
449}