gzip_header/
crc_reader.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Simple CRC wrappers backed by the crc32 crate.

use std::fmt;
use std::io;
use std::io::{BufRead, Read};

use crc32fast::Hasher;

/// A wrapper struct containing a CRC checksum in the format used by gzip and the amount of bytes
/// input to it mod 2^32.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Crc {
    // We don't use the Digest struct from the Crc crate for now as it doesn't implement `Display`
    // and other common traits.
    checksum: u32,
    amt: u32,
}

impl fmt::Display for Crc {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({:#x},{})", self.checksum, self.amt)
    }
}

/// A reader that updates the checksum and counter of a `Crc` struct when reading from the wrapped
/// reader.
#[derive(Debug)]
pub struct CrcReader<R> {
    inner: R,
    crc: Crc,
}

impl Crc {
    /// Create a new empty CRC struct.
    pub const fn new() -> Crc {
        Crc {
            checksum: 0,
            amt: 0,
        }
    }

    pub const fn with_initial(checksum: u32, amt: u32) -> Crc {
        Crc { checksum, amt }
    }

    /// Return the current checksum value.
    pub const fn sum(&self) -> u32 {
        self.checksum
    }

    /// Return the number of bytes input.
    pub const fn amt_as_u32(&self) -> u32 {
        self.amt
    }

    /// Update the checksum and byte counter with the provided data.
    pub fn update(&mut self, data: &[u8]) {
        self.amt = self.amt.wrapping_add(data.len() as u32);
        let mut h = Hasher::new_with_initial(self.checksum);
        h.update(data);
        self.checksum = h.finalize();
    }

    /// Reset the checksum and byte counter.
    pub fn reset(&mut self) {
        self.checksum = 0;
        self.amt = 0;
    }
}

impl<R: Read> CrcReader<R> {
    /// Create a new `CrcReader` with a blank checksum.
    pub fn new(r: R) -> CrcReader<R> {
        CrcReader {
            inner: r,
            crc: Crc::new(),
        }
    }

    /// Return a reference to the underlying checksum struct.
    pub fn crc(&self) -> &Crc {
        &self.crc
    }

    /// Consume this `CrcReader` and return the wrapped `Read` instance.
    pub fn into_inner(self) -> R {
        self.inner
    }

    /// Return a mutable reference to the wrapped reader.
    pub fn inner(&mut self) -> &mut R {
        &mut self.inner
    }

    /// Reset the checksum and counter.
    pub fn reset(&mut self) {
        self.crc.reset();
    }
}

impl<R: Read> Read for CrcReader<R> {
    fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
        let amt = self.inner.read(into)?;
        self.crc.update(&into[..amt]);
        Ok(amt)
    }
}

impl<R: BufRead> BufRead for CrcReader<R> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }
    fn consume(&mut self, amt: usize) {
        if let Ok(data) = self.inner.fill_buf() {
            self.crc.update(&data[..amt]);
        }
        self.inner.consume(amt);
    }
}

#[cfg(test)]
mod test {
    use super::Crc;
    #[test]
    fn checksum_correct() {
        let mut c = Crc::new();
        c.update(b"abcdefg12345689\n");
        assert_eq!(c.sum(), 0x141ddb83);
        assert_eq!(c.amt_as_u32(), 16);
    }
}