git_internal/internal/pack/
wrapper.rs

1use std::io::{self, BufRead, Read};
2
3use sha1::{Digest, Sha1};
4
5use crate::hash::SHA1;
6
7/// [`Wrapper`] is a wrapper around a reader that also computes the SHA1 hash of the data read.
8///
9/// It is designed to work with any reader that implements `BufRead`.
10///
11/// Fields:
12/// * `inner`: The inner reader.
13/// * `hash`: The SHA1 hash state.
14/// * `count_hash`: A flag to indicate whether to compute the hash while reading.
15pub struct Wrapper<R> {
16    inner: R,
17    hash: Sha1,
18    bytes_read: usize,
19}
20
21impl<R> Wrapper<R>
22where
23    R: BufRead,
24{
25    /// Constructs a new [`Wrapper`] with the given reader and a flag to enable or disable hashing.
26    ///
27    /// # Parameters
28    /// * `inner`: The reader to wrap.
29    /// * `count_hash`: If `true`, the hash is computed while reading; otherwise, it is not.
30    pub fn new(inner: R) -> Self {
31        Self {
32            inner,
33            hash: Sha1::new(), // Initialize a new SHA1 hasher
34            bytes_read: 0,
35        }
36    }
37
38    pub fn bytes_read(&self) -> usize {
39        self.bytes_read
40    }
41
42    /// Returns the final SHA1 hash of the data read so far.
43    ///
44    /// This is a clone of the internal hash state finalized into a SHA1 hash.
45    pub fn final_hash(&self) -> SHA1 {
46        let re: [u8; 20] = self.hash.clone().finalize().into(); // Clone, finalize, and convert the hash into bytes
47        SHA1(re)
48    }
49}
50
51impl<R> BufRead for Wrapper<R>
52where
53    R: BufRead,
54{
55    /// Provides access to the internal buffer of the wrapped reader without consuming it.
56    fn fill_buf(&mut self) -> io::Result<&[u8]> {
57        self.inner.fill_buf() // Delegate to the inner reader
58    }
59
60    /// Consumes data from the buffer and updates the hash if `count_hash` is true.
61    ///
62    /// # Parameters
63    /// * `amt`: The amount of data to consume from the buffer.
64    fn consume(&mut self, amt: usize) {
65        let buffer = self.inner.fill_buf().expect("Failed to fill buffer");
66        self.hash.update(&buffer[..amt]); // Update hash with the data being consumed
67        self.inner.consume(amt); // Consume the data from the inner reader
68        self.bytes_read += amt;
69    }
70}
71
72impl<R> Read for Wrapper<R>
73where
74    R: BufRead,
75{
76    /// Reads data into the provided buffer and updates the hash if `count_hash` is true.
77    /// <br> [Read::read_exact] calls it internally.
78    ///
79    /// # Parameters
80    /// * `buf`: The buffer to read data into.
81    ///
82    /// # Returns
83    /// Returns the number of bytes read.
84    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
85        let o = self.inner.read(buf)?; // Read data into the buffer
86        self.hash.update(&buf[..o]); // Update hash with the data being read
87        self.bytes_read += o;
88        Ok(o) // Return the number of bytes read
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use std::io::{self, BufReader, Cursor, Read};
95
96    use sha1::{Digest, Sha1};
97
98    use crate::internal::pack::wrapper::Wrapper;
99
100    #[test]
101    fn test_wrapper_read() -> io::Result<()> {
102        let data = b"Hello, world!"; // Sample data
103        let cursor = Cursor::new(data.as_ref());
104        let buf_reader = BufReader::new(cursor);
105        let mut wrapper = Wrapper::new(buf_reader);
106
107        let mut buffer = vec![0; data.len()];
108        wrapper.read_exact(&mut buffer)?;
109
110        assert_eq!(buffer, data);
111        Ok(())
112    }
113
114    #[test]
115    fn test_wrapper_hash() -> io::Result<()> {
116        let data = b"Hello, world!";
117        let cursor = Cursor::new(data.as_ref());
118        let buf_reader = BufReader::new(cursor);
119        let mut wrapper = Wrapper::new(buf_reader);
120
121        let mut buffer = vec![0; data.len()];
122        wrapper.read_exact(&mut buffer)?;
123
124        let hash_result = wrapper.final_hash();
125        let mut hasher = Sha1::new();
126        hasher.update(data);
127        let expected_hash: [u8; 20] = hasher.finalize().into();
128
129        assert_eq!(hash_result.0, expected_hash);
130        Ok(())
131    }
132}