flate2/
crc.rs

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