1#[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#[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")]
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
93pub 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 delegate_digest_mindigest!(blake2::Blake2b512);
218 delegate_digest_mindigest!(blake2::Blake2s256);
223 }
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
251pub 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 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 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 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 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 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 let y = hasher.finalize();
445 assert_eq!(x, y);
446 assert_eq!(3178921470, x);
447 assert_eq!(3178921470, y);
448 }
449}