Skip to main content

ai_flate2/
crc.rs

1//! Simple CRC bindings backed by miniz.c
2
3use crate::io;
4use crate::io::prelude::*;
5
6/// The CRC calculated by a [`CrcReader`].
7#[derive(Debug, Default)]
8pub struct Crc {
9    inner: inner::Crc,
10}
11
12impl Crc {
13    /// Create a new CRC.
14    pub fn new() -> Self {
15        Self::default()
16    }
17
18    /// Returns the current crc32 checksum.
19    pub fn sum(&self) -> u32 {
20        self.inner.sum()
21    }
22
23    /// The number of bytes that have been used to calculate the CRC.
24    /// This value is only accurate if the amount is lower than 2<sup>32</sup>.
25    pub fn amount(&self) -> u32 {
26        self.inner.amount()
27    }
28
29    /// Update the CRC with the bytes in `data`.
30    pub fn update(&mut self, data: &[u8]) {
31        self.inner.update(data);
32    }
33
34    /// Reset the CRC, to start a new hash.
35    ///
36    /// Do this in favor of creating a new `Crc` instance.
37    pub fn reset(&mut self) {
38        self.inner.reset();
39    }
40
41    /// Combine the CRC with the CRC for the subsequent block of bytes.
42    pub fn combine(&mut self, additional_crc: &Self) {
43        self.inner.combine(&additional_crc.inner);
44    }
45}
46
47#[cfg(not(feature = "zlib-rs"))]
48mod inner {
49    use crc32fast::Hasher;
50
51    #[derive(Debug, Default)]
52    pub struct Crc {
53        amt: u32,
54        hasher: Hasher,
55    }
56
57    impl Crc {
58        #[inline]
59        pub fn sum(&self) -> u32 {
60            self.hasher.clone().finalize()
61        }
62
63        #[inline]
64        pub fn amount(&self) -> u32 {
65            self.amt
66        }
67
68        #[inline]
69        pub fn update(&mut self, data: &[u8]) {
70            self.amt = self.amt.wrapping_add(data.len() as u32);
71            self.hasher.update(data);
72        }
73
74        #[inline]
75        pub fn reset(&mut self) {
76            self.amt = 0;
77            self.hasher.reset();
78        }
79
80        #[inline]
81        pub fn combine(&mut self, additional_crc: &Self) {
82            self.amt = self.amt.wrapping_add(additional_crc.amt);
83            self.hasher.combine(&additional_crc.hasher);
84        }
85    }
86}
87
88#[cfg(feature = "zlib-rs")]
89mod inner {
90    #[derive(Debug, Default)]
91    pub struct Crc {
92        consumed: u64,
93        state: u32,
94    }
95
96    impl Crc {
97        #[inline]
98        pub fn sum(&self) -> u32 {
99            self.state
100        }
101
102        #[inline]
103        pub fn amount(&self) -> u32 {
104            self.consumed as u32
105        }
106
107        #[inline]
108        pub fn update(&mut self, data: &[u8]) {
109            self.consumed = self.consumed.wrapping_add(data.len() as u64);
110            self.state = zlib_rs::crc32::crc32(self.state, data);
111        }
112
113        #[inline]
114        pub fn reset(&mut self) {
115            self.consumed = 0;
116            self.state = 0
117        }
118
119        #[inline]
120        pub fn combine(&mut self, additional_crc: &Self) {
121            self.consumed = self.consumed.wrapping_add(additional_crc.consumed);
122            self.state = zlib_rs::crc32::crc32_combine(
123                self.state,
124                additional_crc.state,
125                additional_crc.consumed,
126            );
127        }
128    }
129}
130
131/// A wrapper around a [`Read`] that calculates the CRC.
132///
133/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
134#[derive(Debug)]
135pub struct CrcReader<R> {
136    inner: R,
137    crc: Crc,
138}
139
140impl<R: Read> CrcReader<R> {
141    /// Create a new `CrcReader`.
142    pub fn new(r: R) -> CrcReader<R> {
143        CrcReader {
144            inner: r,
145            crc: Crc::new(),
146        }
147    }
148}
149
150impl<R> CrcReader<R> {
151    /// Get the Crc for this `CrcReader`.
152    pub fn crc(&self) -> &Crc {
153        &self.crc
154    }
155
156    /// Get the reader that is wrapped by this `CrcReader`.
157    pub fn into_inner(self) -> R {
158        self.inner
159    }
160
161    /// Get the reader that is wrapped by this `CrcReader` by reference.
162    pub fn get_ref(&self) -> &R {
163        &self.inner
164    }
165
166    /// Get a mutable reference to the reader that is wrapped by this `CrcReader`.
167    pub fn get_mut(&mut self) -> &mut R {
168        &mut self.inner
169    }
170
171    /// Reset the Crc in this `CrcReader`.
172    pub fn reset(&mut self) {
173        self.crc.reset();
174    }
175}
176
177impl<R: Read> Read for CrcReader<R> {
178    fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
179        let amt = self.inner.read(into)?;
180        self.crc.update(&into[..amt]);
181        Ok(amt)
182    }
183}
184
185impl<R: BufRead> BufRead for CrcReader<R> {
186    fn fill_buf(&mut self) -> io::Result<&[u8]> {
187        self.inner.fill_buf()
188    }
189    fn consume(&mut self, amt: usize) {
190        if let Ok(data) = self.inner.fill_buf() {
191            self.crc.update(&data[..amt]);
192        }
193        self.inner.consume(amt);
194    }
195}
196
197/// A wrapper around a [`Write`] that calculates the CRC.
198///
199/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
200#[derive(Debug)]
201pub struct CrcWriter<W> {
202    inner: W,
203    crc: Crc,
204}
205
206impl<W> CrcWriter<W> {
207    /// Get the Crc for this `CrcWriter`.
208    pub fn crc(&self) -> &Crc {
209        &self.crc
210    }
211
212    /// Get the writer that is wrapped by this `CrcWriter`.
213    pub fn into_inner(self) -> W {
214        self.inner
215    }
216
217    /// Get the writer that is wrapped by this `CrcWriter` by reference.
218    pub fn get_ref(&self) -> &W {
219        &self.inner
220    }
221
222    /// Get a mutable reference to the writer that is wrapped by this `CrcWriter`.
223    pub fn get_mut(&mut self) -> &mut W {
224        &mut self.inner
225    }
226
227    /// Reset the Crc in this `CrcWriter`.
228    pub fn reset(&mut self) {
229        self.crc.reset();
230    }
231}
232
233impl<W: Write> CrcWriter<W> {
234    /// Create a new `CrcWriter`.
235    pub fn new(w: W) -> CrcWriter<W> {
236        CrcWriter {
237            inner: w,
238            crc: Crc::new(),
239        }
240    }
241}
242
243impl<W: Write> Write for CrcWriter<W> {
244    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
245        let amt = self.inner.write(buf)?;
246        self.crc.update(&buf[..amt]);
247        Ok(amt)
248    }
249
250    fn flush(&mut self) -> io::Result<()> {
251        self.inner.flush()
252    }
253}
254
255#[cfg(all(test, feature = "std"))]
256mod tests {
257    use super::Crc;
258
259    fn crc_of(data: &[u8]) -> Crc {
260        let mut c = Crc::new();
261        c.update(data);
262        c
263    }
264
265    fn sum_of(data: &[u8]) -> u32 {
266        crc_of(data).sum()
267    }
268
269    #[test]
270    fn new_is_empty() {
271        let c = Crc::new();
272        assert_eq!(c.amount(), 0);
273        assert_eq!(c.sum(), 0);
274    }
275
276    #[test]
277    fn known_vector_hello() {
278        assert_eq!(sum_of(b"hello"), 0x3610_A686);
279    }
280
281    #[test]
282    fn known_vector_quick_brown_fox() {
283        assert_eq!(
284            sum_of(b"The quick brown fox jumps over the lazy dog"),
285            0x414F_A339
286        );
287    }
288
289    #[test]
290    fn update_is_streaming() {
291        let mut c = Crc::new();
292        c.update(b"hello");
293        c.update(b" ");
294        c.update(b"world");
295
296        assert_eq!(c.amount(), 11);
297        assert_eq!(c.sum(), sum_of(b"hello world"));
298    }
299
300    #[test]
301    fn reset_restores_initial_state() {
302        let mut c = Crc::new();
303        c.update(b"abc");
304        assert_ne!(c.sum(), 0);
305        assert_eq!(c.amount(), 3);
306
307        c.reset();
308        assert_eq!(c.amount(), 0);
309        assert_eq!(c.sum(), 0);
310    }
311
312    #[test]
313    fn combine_matches_concatenation() {
314        let a = b"hello ";
315        let b = b"world";
316
317        let mut ca = crc_of(a);
318        let cb = crc_of(b);
319
320        ca.combine(&cb);
321
322        dbg!(&ca);
323
324        assert_eq!(ca.amount(), 11);
325        assert_eq!(ca.sum(), sum_of(b"hello world"));
326    }
327}