1use crc32fast::Hasher;
2use std::io::{self, BufReader, Read, Seek, SeekFrom};
3
4pub(crate) enum Either<A, B> {
5 Left(A),
6 Right(B),
7}
8
9pub struct Reader<'a> {
24 pub(crate) decoder:
25 Either<zstd::Decoder<'static, BufReader<io::Cursor<&'a [u8]>>>, io::Cursor<&'a [u8]>>,
26 pub(crate) crc32_hasher: Hasher,
27 pub(crate) expected_crc32: u32,
28}
29
30impl<'a> Read for Reader<'a> {
31 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
32 let n = match &mut self.decoder {
33 Either::Left(x) => x.read(buf)?,
34 Either::Right(x) => x.read(buf)?,
35 };
36
37 if n > 0 {
38 self.crc32_hasher.update(&buf[..n]);
39 }
40
41 Ok(n)
42 }
43}
44
45impl<'a> Seek for Reader<'a> {
48 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
49 match &mut self.decoder {
50 Either::Left(_) => Err(io::Error::new(
51 io::ErrorKind::Unsupported,
52 "Seeking not supported on compressed streams",
53 )),
54 Either::Right(x) => x.seek(pos),
55 }
56 }
57}
58
59impl<'a> Reader<'a> {
60 pub fn verify_crc32(&self) -> io::Result<()> {
65 let computed_crc = self.crc32_hasher.clone().finalize();
66 if computed_crc != self.expected_crc32 {
67 return Err(io::Error::new(
68 io::ErrorKind::InvalidData,
69 format!(
70 "CRC32 mismatch: expected {:x}, got {:x}",
71 self.expected_crc32, computed_crc
72 ),
73 ));
74 }
75 Ok(())
76 }
77}