blob: 715cc5e13dbf4093047a4a9bda721f6fee816f0f [file] [log] [blame]
//! Simple CRC wrappers backed by the crc32 crate.
use std::io::{BufRead, Read};
use std::io;
use std::fmt;
use crc::crc32;
/// 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 fn new() -> Crc {
Crc {
checksum: 0,
amt: 0,
}
}
pub fn with_initial(checksum: u32, amount: u32) -> Crc {
Crc {
checksum: checksum,
amt: amount,
}
}
/// Return the current checksum value.
pub fn sum(&self) -> u32 {
self.checksum
}
/// Return the number of bytes input.
pub 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);
self.checksum = crc32::update(self.checksum, &crc32::IEEE_TABLE, data);
}
/// 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 = try!(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);
}
}