use std::fmt;
use std::hash::Hasher;
use std::io;
use byteorder::BigEndian;
use byteorder::ReadBytesExt;
use crate::config::CodeqConfig;
#[cfg_attr(not(feature = "crc32fast"), doc = "```ignore")]
#[cfg_attr(feature = "crc32fast", doc = "```rust")]
pub struct ChecksumReader<C, R>
where C: CodeqConfig
{
hasher: C::Hasher,
inner: R,
}
impl<C, R> ChecksumReader<C, R>
where
C: CodeqConfig,
R: io::Read,
{
pub fn new(inner: R) -> Self {
Self {
hasher: C::Hasher::default(),
inner,
}
}
#[allow(dead_code)]
pub fn finalize_checksum(self) -> u64 {
self.hasher.finish()
}
pub fn verify_checksum<D: fmt::Display>(self, context: impl Fn() -> D) -> io::Result<()> {
let mut r = self.inner;
let actual = self.hasher.finish();
let got = r.read_u64::<BigEndian>()?;
if actual != got {
Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"crc32 checksum mismatch: expected {:x}, got {:x}, while {}",
actual,
got,
context()
),
))
} else {
Ok(())
}
}
}
impl<C, R> io::Read for ChecksumReader<C, R>
where
C: CodeqConfig,
R: io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let read = self.inner.read(buf)?;
if read > 0 {
self.hasher.write(&buf[..read]);
}
Ok(read)
}
}
#[cfg(feature = "crc32fast")]
#[cfg(test)]
#[allow(clippy::redundant_clone)]
mod tests_crc32fast {
use std::io::Read;
use std::io::Write;
use crate::config::CodeqConfig;
use crate::config::Crc32fast;
#[test]
fn test_checksum_reader() -> anyhow::Result<()> {
let mut b = Vec::new();
{
let mut n = 0;
let mut w = Crc32fast::new_writer(&mut b);
n += w.write(b"foo")?;
n += w.write(b"bar")?;
n += w.write_checksum()?;
assert_eq!(n, 14);
}
{
let mut r = Crc32fast::new_reader(&b[..]);
let mut read_buf = [0u8; 6];
r.read_exact(&mut read_buf)?;
let crc = r.finalize_checksum();
assert_eq!(crc32fast::hash(b"foobar") as u64, crc);
}
{
let mut r = Crc32fast::new_reader(&b[..]);
let mut read_buf = [0u8; 6];
r.read_exact(&mut read_buf)?;
r.verify_checksum(|| "")?; }
let last = b.len() - 1;
b[last] = b[last].wrapping_add(1);
{
let mut r = Crc32fast::new_reader(&b[..]);
let mut read_buf = [0; 6];
r.read_exact(&mut read_buf)?;
let res = r.verify_checksum(|| ""); assert!(res.is_err());
}
Ok(())
}
}
#[cfg(feature = "crc64fast-nvme")]
#[cfg(test)]
#[allow(clippy::redundant_clone)]
mod tests_crc64fast_nvme {
use std::io::Read;
use std::io::Write;
use crate::config::CodeqConfig;
use crate::config::Crc64fastNvme;
#[test]
fn test_checksum_reader() -> anyhow::Result<()> {
let mut b = Vec::new();
{
let mut n = 0;
let mut w = Crc64fastNvme::new_writer(&mut b);
n += w.write(b"foo")?;
n += w.write(b"bar")?;
n += w.write_checksum()?;
assert_eq!(n, 14);
}
{
let mut r = Crc64fastNvme::new_reader(&b[..]);
let mut read_buf = [0u8; 6];
r.read_exact(&mut read_buf)?;
let crc = r.finalize_checksum();
assert_eq!(Crc64fastNvme::hash(b"foobar") as u64, crc);
}
{
let mut r = Crc64fastNvme::new_reader(&b[..]);
let mut read_buf = [0u8; 6];
r.read_exact(&mut read_buf)?;
r.verify_checksum(|| "")?; }
let last = b.len() - 1;
b[last] = b[last].wrapping_add(1);
{
let mut r = Crc64fastNvme::new_reader(&b[..]);
let mut read_buf = [0; 6];
r.read_exact(&mut read_buf)?;
let res = r.verify_checksum(|| ""); assert!(res.is_err());
}
Ok(())
}
}