use crc32fast::Hasher;
use std::io::{self, BufReader, Read, Seek, SeekFrom};
pub(crate) enum Either<A, B> {
Left(A),
Right(B),
}
pub struct Reader<'a> {
pub(crate) decoder:
Either<zstd::Decoder<'static, BufReader<io::Cursor<&'a [u8]>>>, io::Cursor<&'a [u8]>>,
pub(crate) crc32_hasher: Hasher,
pub(crate) expected_crc32: u32,
}
impl<'a> Read for Reader<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = match &mut self.decoder {
Either::Left(x) => x.read(buf)?,
Either::Right(x) => x.read(buf)?,
};
if n > 0 {
self.crc32_hasher.update(&buf[..n]);
}
Ok(n)
}
}
impl<'a> Seek for Reader<'a> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match &mut self.decoder {
Either::Left(_) => Err(io::Error::new(
io::ErrorKind::Unsupported,
"Seeking not supported on compressed streams",
)),
Either::Right(x) => x.seek(pos),
}
}
}
impl<'a> Reader<'a> {
pub fn verify_crc32(&self) -> io::Result<()> {
let computed_crc = self.crc32_hasher.clone().finalize();
if computed_crc != self.expected_crc32 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"CRC32 mismatch: expected {:x}, got {:x}",
self.expected_crc32, computed_crc
),
));
}
Ok(())
}
}