use std::io::{self, Read};
use checksum;
use gzip::{Header, Trailer};
use non_blocking::deflate;
#[derive(Debug)]
pub struct Decoder<R> {
header: Option<Header>,
reader: deflate::Decoder<R>,
crc32: checksum::Crc32,
eos: bool,
}
impl<R: Read> Decoder<R> {
pub fn new(inner: R) -> Self {
Decoder {
header: None,
reader: deflate::Decoder::new(inner),
crc32: checksum::Crc32::new(),
eos: false,
}
}
pub fn header(&mut self) -> io::Result<&Header> {
if let Some(ref header) = self.header {
Ok(header)
} else {
let header = self
.reader
.bit_reader_mut()
.transaction(|r| Header::read_from(r.as_inner_mut()))?;
self.header = Some(header);
self.header()
}
}
pub fn as_inner_ref(&self) -> &R {
self.reader.as_inner_ref()
}
pub fn as_inner_mut(&mut self) -> &mut R {
self.reader.as_inner_mut()
}
pub fn into_inner(self) -> R {
self.reader.into_inner()
}
}
impl<R: Read> Read for Decoder<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.header.is_none() {
self.header()?;
}
if self.eos {
Ok(0)
} else {
let read_size = self.reader.read(buf)?;
if read_size == 0 {
let trailer = self
.reader
.bit_reader_mut()
.transaction(|r| Trailer::read_from(r.as_inner_mut()))?;
self.eos = true;
if cfg!(not(fuzzing)) && trailer.crc32() != self.crc32.value() {
Err(invalid_data_error!(
"CRC32 mismatched: value={}, expected={}",
self.crc32.value(),
trailer.crc32()
))
} else {
Ok(0)
}
} else {
self.crc32.update(&buf[..read_size]);
Ok(read_size)
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use gzip::Encoder;
use std::io;
use util::{nb_read_to_end, WouldBlockReader};
fn decode_all(buf: &[u8]) -> io::Result<Vec<u8>> {
let decoder = Decoder::new(WouldBlockReader::new(buf));
nb_read_to_end(decoder)
}
#[test]
fn encode_works() {
let plain = b"Hello World! Hello GZIP!!";
let mut encoder = Encoder::new(Vec::new()).unwrap();
io::copy(&mut &plain[..], &mut encoder).unwrap();
let encoded = encoder.finish().into_result().unwrap();
assert_eq!(decode_all(&encoded).unwrap(), plain);
}
#[test]
fn decode_works_noncompressed_block_offset_sync() {
let encoded = include_bytes!("../../data/noncompressed_block_offset_sync/offset.gz");
let decoded = include_bytes!("../../data/noncompressed_block_offset_sync/offset");
assert_eq!(decode_all(encoded).unwrap(), decoded.to_vec());
}
}