git_internal/internal/pack/
wrapper.rs

1use std::io::{self, BufRead, Read};
2
3use sha1::{Digest, Sha1};
4
5use crate::hash::{HashKind, ObjectHash, get_hash_kind};
6use crate::utils::HashAlgorithm;
7/// [`Wrapper`] is a wrapper around a reader that also computes the SHA1/ SHA256 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  hash state.
14/// * `count_hash`: A flag to indicate whether to compute the hash while reading.
15///
16pub struct Wrapper<R> {
17    inner: R,
18    hash: HashAlgorithm,
19    bytes_read: usize,
20}
21
22impl<R> Wrapper<R>
23where
24    R: BufRead,
25{
26    /// Constructs a new [`Wrapper`] with the given reader and a flag to enable or disable hashing.
27    ///
28    /// # Parameters
29    /// * `inner`: The reader to wrap.
30    /// * `count_hash`: If `true`, the hash is computed while reading; otherwise, it is not.
31    pub fn new(inner: R) -> Self {
32        Self {
33            inner,
34            hash: match get_hash_kind() {
35                HashKind::Sha1 => HashAlgorithm::Sha1(Sha1::new()),
36                HashKind::Sha256 => HashAlgorithm::Sha256(sha2::Sha256::new()),
37            }, // Initialize a new SHA1/ SHA256 hasher
38            bytes_read: 0,
39        }
40    }
41
42    pub fn bytes_read(&self) -> usize {
43        self.bytes_read
44    }
45
46    /// Returns the final SHA1/ SHA256 hash of the data read so far.
47    ///
48    /// This is a clone of the internal hash state finalized into a SHA1/ SHA256 hash.
49    pub fn final_hash(&self) -> ObjectHash {
50        match &self.hash.clone() {
51            HashAlgorithm::Sha1(hasher) => {
52                let re: [u8; 20] = hasher.clone().finalize().into(); // Clone, finalize, and convert the hash into bytes
53                ObjectHash::from_bytes(&re).unwrap()
54            }
55            HashAlgorithm::Sha256(hasher) => {
56                let re: [u8; 32] = hasher.clone().finalize().into(); // Clone, finalize, and convert the hash into bytes
57                ObjectHash::from_bytes(&re).unwrap()
58            }
59        }
60    }
61}
62
63impl<R> BufRead for Wrapper<R>
64where
65    R: BufRead,
66{
67    /// Provides access to the internal buffer of the wrapped reader without consuming it.
68    fn fill_buf(&mut self) -> io::Result<&[u8]> {
69        self.inner.fill_buf() // Delegate to the inner reader
70    }
71
72    /// Consumes data from the buffer and updates the hash if `count_hash` is true.
73    ///
74    /// # Parameters
75    /// * `amt`: The amount of data to consume from the buffer.
76    fn consume(&mut self, amt: usize) {
77        let buffer = self.inner.fill_buf().expect("Failed to fill buffer");
78        match &mut self.hash {
79            HashAlgorithm::Sha1(hasher) => hasher.update(&buffer[..amt]), // Update SHA1 hash with the data being consumed
80            HashAlgorithm::Sha256(hasher) => hasher.update(&buffer[..amt]), // Update SHA256 hash with the data being consumed
81        }
82        self.inner.consume(amt); // Consume the data from the inner reader
83        self.bytes_read += amt;
84    }
85}
86
87impl<R> Read for Wrapper<R>
88where
89    R: BufRead,
90{
91    /// Reads data into the provided buffer and updates the hash if `count_hash` is true.
92    /// <br> [Read::read_exact] calls it internally.
93    ///
94    /// # Parameters
95    /// * `buf`: The buffer to read data into.
96    ///
97    /// # Returns
98    /// Returns the number of bytes read.
99    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
100        let o = self.inner.read(buf)?; // Read data into the buffer
101        match &mut self.hash {
102            HashAlgorithm::Sha1(hasher) => hasher.update(&buf[..o]), // Update SHA1 hash with the data being read
103            HashAlgorithm::Sha256(hasher) => hasher.update(&buf[..o]), // Update SHA256 hash with the data being read
104        }
105        self.bytes_read += o;
106        Ok(o) // Return the number of bytes read
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use std::io::{self, BufReader, Cursor, Read};
113
114    use sha1::{Digest, Sha1};
115
116    use crate::hash::{HashKind, set_hash_kind};
117    use crate::internal::pack::wrapper::Wrapper;
118    #[test]
119    fn test_wrapper_read() -> io::Result<()> {
120        let _guard = set_hash_kind(HashKind::Sha1);
121        let data = b"Hello, world!"; // Sample data
122        let cursor = Cursor::new(data.as_ref());
123        let buf_reader = BufReader::new(cursor);
124        let mut wrapper = Wrapper::new(buf_reader);
125
126        let mut buffer = vec![0; data.len()];
127        wrapper.read_exact(&mut buffer)?;
128
129        assert_eq!(buffer, data);
130        Ok(())
131    }
132    #[test]
133    fn test_wrapper_read_sha256() -> io::Result<()> {
134        let _guard = set_hash_kind(HashKind::Sha256);
135        let data = b"Hello, world!"; // Sample data
136        let cursor = Cursor::new(data.as_ref());
137        let buf_reader = BufReader::new(cursor);
138        let mut wrapper = Wrapper::new(buf_reader);
139
140        let mut buffer = vec![0; data.len()];
141        wrapper.read_exact(&mut buffer)?;
142
143        assert_eq!(buffer, data);
144        Ok(())
145    }
146
147    #[test]
148    fn test_wrapper_hash() -> io::Result<()> {
149        let _guard = set_hash_kind(HashKind::Sha1);
150        let data = b"Hello, world!";
151        let cursor = Cursor::new(data.as_ref());
152        let buf_reader = BufReader::new(cursor);
153        let mut wrapper = Wrapper::new(buf_reader);
154
155        let mut buffer = vec![0; data.len()];
156        wrapper.read_exact(&mut buffer)?;
157
158        let hash_result = wrapper.final_hash();
159        let mut hasher = Sha1::new();
160        hasher.update(data);
161        let expected_hash: [u8; 20] = hasher.finalize().into();
162
163        assert_eq!(hash_result.as_ref(), expected_hash);
164        Ok(())
165    }
166    #[test]
167    fn test_wrapper_hash_sha256() -> io::Result<()> {
168        let _guard = set_hash_kind(HashKind::Sha256);
169        let data = b"Hello, world!";
170        let cursor = Cursor::new(data.as_ref());
171        let buf_reader = BufReader::new(cursor);
172        let mut wrapper = Wrapper::new(buf_reader);
173
174        let mut buffer = vec![0; data.len()];
175        wrapper.read_exact(&mut buffer)?;
176
177        let hash_result = wrapper.final_hash();
178        let mut hasher = sha2::Sha256::new();
179        hasher.update(data);
180        let expected_hash: [u8; 32] = hasher.finalize().into();
181
182        assert_eq!(hash_result.as_ref(), &expected_hash);
183        Ok(())
184    }
185}