Skip to main content

digest_io/
reader.rs

1use digest::{Digest, FixedOutputReset, Output, Reset};
2use std::io;
3
4/// Abstraction over a reader which hashes the data being read
5#[derive(Debug)]
6pub struct HashReader<D: Digest, R: io::Read> {
7    reader: R,
8    hasher: D,
9}
10
11impl<D: Digest, R: io::Read> HashReader<D, R> {
12    /// Construct a new `HashReader` given an existing `reader` by value.
13    pub fn new(reader: R) -> Self {
14        Self::new_from_parts(D::new(), reader)
15    }
16
17    /// Construct a new `HashReader` given an existing `hasher` and `reader` by value.
18    pub fn new_from_parts(hasher: D, reader: R) -> Self {
19        HashReader { reader, hasher }
20    }
21
22    /// Replace the reader with another reader
23    pub fn replace_reader(&mut self, reader: R) {
24        self.reader = reader;
25    }
26
27    /// Gets a reference to the underlying hasher
28    pub fn get_hasher(&self) -> &D {
29        &self.hasher
30    }
31
32    /// Gets a reference to the underlying reader
33    pub fn get_reader(&self) -> &R {
34        &self.reader
35    }
36
37    /// Gets a mutable reference to the underlying hasher
38    pub fn get_hasher_mut(&mut self) -> &mut D {
39        &mut self.hasher
40    }
41
42    /// Gets a mutable reference to the underlying reader
43    /// Direct reads from the underlying reader are not hashed
44    pub fn get_reader_mut(&mut self) -> &mut R {
45        &mut self.reader
46    }
47
48    /// Consume the HashReader and return its hasher
49    pub fn into_hasher(self) -> D {
50        self.hasher
51    }
52
53    /// Consume the HashReader and return its internal reader
54    pub fn into_inner_reader(self) -> R {
55        self.reader
56    }
57
58    /// Consume the HashReader and return its hasher and internal reader
59    pub fn into_parts(self) -> (D, R) {
60        (self.hasher, self.reader)
61    }
62
63    /// Retrieve result and consume HashReader instance.
64    pub fn finalize(self) -> Output<D> {
65        self.hasher.finalize()
66    }
67
68    /// Write result into provided array and consume the HashReader instance.
69    pub fn finalize_into(self, out: &mut Output<D>) {
70        self.hasher.finalize_into(out)
71    }
72
73    /// Get output size of the hasher
74    pub fn output_size() -> usize {
75        <D as Digest>::output_size()
76    }
77}
78
79impl<D: Digest + Clone, R: io::Read + Clone> Clone for HashReader<D, R> {
80    fn clone(&self) -> HashReader<D, R> {
81        HashReader {
82            reader: self.reader.clone(),
83            hasher: self.hasher.clone(),
84        }
85    }
86}
87
88impl<D: Digest, R: io::Read> io::Read for HashReader<D, R> {
89    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
90        let bytes = self.reader.read(buf)?;
91
92        if bytes > 0 {
93            self.hasher.update(&buf[0..bytes]);
94        }
95
96        Ok(bytes)
97    }
98}
99
100impl<D: Digest + FixedOutputReset, R: io::Read> HashReader<D, R> {
101    /// Retrieve result and reset hasher instance.
102    pub fn finalize_reset(&mut self) -> Output<D> {
103        Digest::finalize_reset(&mut self.hasher)
104    }
105
106    /// Rrite result into provided array and reset the hasher instance.
107    pub fn finalize_into_reset(&mut self, out: &mut Output<D>) {
108        Digest::finalize_into_reset(&mut self.hasher, out)
109    }
110}
111
112impl<D: Digest + Reset, R: io::Read> Reset for HashReader<D, R> {
113    fn reset(&mut self) {
114        Digest::reset(&mut self.hasher)
115    }
116}
117
118impl<D: Digest, R: io::BufRead> HashReader<D, R> {
119    /// Read and hash all bytes remaining in the reader, discarding the data
120    /// Based on implementation in b2sum crate, MIT License Copyright (c) 2017 John Downey
121    pub fn hash_to_end(&mut self) {
122        loop {
123            let count = {
124                let data = self.reader.fill_buf().unwrap();
125                if data.is_empty() {
126                    break;
127                }
128
129                self.hasher.update(data);
130                data.len()
131            };
132
133            self.reader.consume(count);
134        }
135    }
136}