crc32c/
io.rs

1//! Provides wrappers for [Read] and [Write] types which checksum the bytes being read/written.
2use std::io::{Read, Write};
3
4use crate::crc32c_append;
5
6/// [Read]er wrapper which tracks the checksum of all bytes read.
7pub struct Crc32cReader<R: Read> {
8    checksum: u32,
9    inner: R,
10}
11
12impl<R: Read> Crc32cReader<R> {
13    /// Wrap an instance of a [Read]er.
14    pub fn new(r: R) -> Self {
15        Self::new_with_seed(r, 0)
16    }
17
18    /// Wrap a [Read]er, with the checksum seeded with a particular value.
19    pub fn new_with_seed(r: R, seed: u32) -> Self {
20        Self {
21            checksum: seed,
22            inner: r,
23        }
24    }
25
26    /// Unwrap the inner [Read]er.
27    pub fn into_inner(self) -> R {
28        self.inner
29    }
30
31    /// Get the checksum of all bytes read.
32    pub fn crc32c(&self) -> u32 {
33        self.checksum
34    }
35}
36
37impl<R: Read> Read for Crc32cReader<R> {
38    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
39        let out = self.inner.read(buf)?;
40        self.checksum = crc32c_append(self.checksum, &buf[..out]);
41        Ok(out)
42    }
43}
44
45/// [Write]r wrapper which tracks the checksum of all bytes written.
46pub struct Crc32cWriter<W: Write> {
47    checksum: u32,
48    inner: W,
49}
50
51impl<W: Write> Crc32cWriter<W> {
52    /// Wrap an instance of a [Write]r.
53    pub fn new(w: W) -> Self {
54        Self::new_with_seed(w, 0)
55    }
56
57    /// Wrap a [Write]r, with the checksum seeded with a particular value.
58    pub fn new_with_seed(w: W, seed: u32) -> Self {
59        Self {
60            checksum: seed,
61            inner: w,
62        }
63    }
64
65    /// Unwrap the inner [Write]r.
66    pub fn into_inner(self) -> W {
67        self.inner
68    }
69
70    /// Get the checksum of all bytes written.
71    pub fn crc32c(&self) -> u32 {
72        self.checksum
73    }
74}
75
76impl<W: Write> Write for Crc32cWriter<W> {
77    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
78        let out = self.inner.write(buf)?;
79        self.checksum = crc32c_append(self.checksum, &buf[..out]);
80        Ok(out)
81    }
82
83    fn flush(&mut self) -> std::io::Result<()> {
84        self.inner.flush()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use std::io::Cursor;
91
92    use super::*;
93
94    const TEST_STRING: &[u8] =
95        b"This is a very long string which is used to test the CRC-32-Castagnoli function.";
96    const CHECKSUM: u32 = 0x20_CB_1E_59;
97
98    #[test]
99    fn can_read() {
100        let mut reader = Crc32cReader::new(TEST_STRING);
101        let mut buf = Vec::default();
102        let n_read = reader.read_to_end(&mut buf).unwrap();
103        assert_eq!(n_read, TEST_STRING.len());
104        assert_eq!(buf.as_slice(), TEST_STRING);
105        assert_eq!(reader.crc32c(), CHECKSUM);
106    }
107
108    #[test]
109    fn can_write() {
110        let mut buf = Vec::<u8>::default();
111
112        let mut writer = Crc32cWriter::<Cursor<&mut Vec<u8>>>::new(Cursor::new(&mut buf));
113        writer.write_all(TEST_STRING).unwrap();
114        let checksum = writer.crc32c();
115
116        assert_eq!(buf.as_slice(), TEST_STRING);
117        assert_eq!(checksum, CHECKSUM);
118    }
119}