//! Simple CRC bindings backed by miniz.c
use std::io;
use std::io::prelude::*;
#[cfg(not(feature = "zlib-rs"))]
pub use impl_crc32fast::Crc;
#[cfg(feature = "zlib-rs")]
pub use impl_zlib_rs::Crc;
#[cfg(not(feature = "zlib-rs"))]
mod impl_crc32fast {
use crc32fast::Hasher;
/// The CRC calculated by a [`CrcReader`].
///
/// [`CrcReader`]: struct.CrcReader.html
#[derive(Debug, Default)]
pub struct Crc {
amt: u32,
hasher: Hasher,
}
impl Crc {
/// Create a new CRC.
pub fn new() -> Self {
Self::default()
}
/// Returns the current crc32 checksum.
pub fn sum(&self) -> u32 {
self.hasher.clone().finalize()
}
/// The number of bytes that have been used to calculate the CRC.
/// This value is only accurate if the amount is lower than 232.
pub fn amount(&self) -> u32 {
self.amt
}
/// Update the CRC with the bytes in `data`.
pub fn update(&mut self, data: &[u8]) {
self.amt = self.amt.wrapping_add(data.len() as u32);
self.hasher.update(data);
}
/// Reset the CRC.
pub fn reset(&mut self) {
self.amt = 0;
self.hasher.reset();
}
/// Combine the CRC with the CRC for the subsequent block of bytes.
pub fn combine(&mut self, additional_crc: &Self) {
self.amt = self.amt.wrapping_add(additional_crc.amt);
self.hasher.combine(&additional_crc.hasher);
}
}
}
#[cfg(feature = "zlib-rs")]
mod impl_zlib_rs {
/// The CRC calculated by a [`CrcReader`].
///
/// [`CrcReader`]: struct.CrcReader.html
#[derive(Debug, Default)]
pub struct Crc {
consumed: u64,
state: u32,
}
impl Crc {
/// Create a new CRC.
pub fn new() -> Self {
Self::default()
}
/// Returns the current crc32 checksum.
pub fn sum(&self) -> u32 {
self.state
}
/// The number of bytes that have been used to calculate the CRC.
/// This value is only accurate if the amount is lower than 232.
pub fn amount(&self) -> u32 {
self.consumed as u32
}
/// Update the CRC with the bytes in `data`.
pub fn update(&mut self, data: &[u8]) {
self.consumed = self.consumed.wrapping_add(data.len() as u64);
self.state = zlib_rs::crc32::crc32(self.state, data);
}
/// Reset the CRC.
pub fn reset(&mut self) {
self.consumed = 0;
self.state = 0
}
/// Combine the CRC with the CRC for the subsequent block of bytes.
pub fn combine(&mut self, additional_crc: &Self) {
self.consumed = self.consumed.wrapping_add(additional_crc.consumed);
self.state = zlib_rs::crc32::crc32_combine(
self.state,
additional_crc.state,
additional_crc.consumed,
);
}
}
}
/// A wrapper around a [`Read`] that calculates the CRC.
///
/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
#[derive(Debug)]
pub struct CrcReader {
inner: R,
crc: Crc,
}
impl CrcReader {
/// Create a new `CrcReader`.
pub fn new(r: R) -> CrcReader {
CrcReader {
inner: r,
crc: Crc::new(),
}
}
}
impl CrcReader {
/// Get the Crc for this `CrcReader`.
pub fn crc(&self) -> &Crc {
&self.crc
}
/// Get the reader that is wrapped by this `CrcReader`.
pub fn into_inner(self) -> R {
self.inner
}
/// Get the reader that is wrapped by this `CrcReader` by reference.
pub fn get_ref(&self) -> &R {
&self.inner
}
/// Get a mutable reference to the reader that is wrapped by this `CrcReader`.
pub fn get_mut(&mut self) -> &mut R {
&mut self.inner
}
/// Reset the Crc in this `CrcReader`.
pub fn reset(&mut self) {
self.crc.reset();
}
}
impl Read for CrcReader {
fn read(&mut self, into: &mut [u8]) -> io::Result {
let amt = self.inner.read(into)?;
self.crc.update(&into[..amt]);
Ok(amt)
}
}
impl BufRead for CrcReader {
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);
}
}
/// A wrapper around a [`Write`] that calculates the CRC.
///
/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
#[derive(Debug)]
pub struct CrcWriter {
inner: W,
crc: Crc,
}
impl CrcWriter {
/// Get the Crc for this `CrcWriter`.
pub fn crc(&self) -> &Crc {
&self.crc
}
/// Get the writer that is wrapped by this `CrcWriter`.
pub fn into_inner(self) -> W {
self.inner
}
/// Get the writer that is wrapped by this `CrcWriter` by reference.
pub fn get_ref(&self) -> &W {
&self.inner
}
/// Get a mutable reference to the writer that is wrapped by this `CrcWriter`.
pub fn get_mut(&mut self) -> &mut W {
&mut self.inner
}
/// Reset the Crc in this `CrcWriter`.
pub fn reset(&mut self) {
self.crc.reset();
}
}
impl CrcWriter {
/// Create a new `CrcWriter`.
pub fn new(w: W) -> CrcWriter {
CrcWriter {
inner: w,
crc: Crc::new(),
}
}
}
impl Write for CrcWriter {
fn write(&mut self, buf: &[u8]) -> io::Result {
let amt = self.inner.write(buf)?;
self.crc.update(&buf[..amt]);
Ok(amt)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
#[cfg(test)]
mod tests {
use super::Crc;
fn crc_of(data: &[u8]) -> Crc {
let mut c = Crc::new();
c.update(data);
c
}
fn sum_of(data: &[u8]) -> u32 {
crc_of(data).sum()
}
#[test]
fn new_is_empty() {
let c = Crc::new();
assert_eq!(c.amount(), 0);
assert_eq!(c.sum(), 0);
}
#[test]
fn known_vector_hello() {
assert_eq!(sum_of(b"hello"), 0x3610_A686);
}
#[test]
fn known_vector_quick_brown_fox() {
assert_eq!(
sum_of(b"The quick brown fox jumps over the lazy dog"),
0x414F_A339
);
}
#[test]
fn update_is_streaming() {
let mut c = Crc::new();
c.update(b"hello");
c.update(b" ");
c.update(b"world");
assert_eq!(c.amount(), 11);
assert_eq!(c.sum(), sum_of(b"hello world"));
}
#[test]
fn reset_restores_initial_state() {
let mut c = Crc::new();
c.update(b"abc");
assert_ne!(c.sum(), 0);
assert_eq!(c.amount(), 3);
c.reset();
assert_eq!(c.amount(), 0);
assert_eq!(c.sum(), 0);
}
#[test]
fn combine_matches_concatenation() {
let a = b"hello ";
let b = b"world";
let mut ca = crc_of(a);
let cb = crc_of(b);
ca.combine(&cb);
dbg!(&ca);
assert_eq!(ca.amount(), 11);
assert_eq!(ca.sum(), sum_of(b"hello world"));
}
}