use alloc::vec::Vec;
use super::{HEADER_SIZE, LzipHeader, LzipTrailer, TRAILER_SIZE};
use crate::{
CountingReader, LzmaReader, Read, Result, crc::Crc32, error_invalid_data, error_invalid_input,
};
pub struct LzipReader<R> {
inner: Option<R>,
lzma_reader: Option<LzmaReader<CountingReader<R>>>,
current_header: Option<LzipHeader>,
finished: bool,
trailer_buf: Vec<u8>,
crc_digest: Option<Crc32>,
data_size: u64,
}
impl<R> LzipReader<R> {
pub fn into_inner(mut self) -> R {
if let Some(lzma_reader) = self.lzma_reader.take() {
return lzma_reader.into_inner().inner;
}
self.inner.take().expect("inner reader not set")
}
pub fn inner(&self) -> &R {
self.lzma_reader
.as_ref()
.map(|reader| reader.inner().inner())
.unwrap_or_else(|| self.inner.as_ref().expect("inner reader not set"))
}
pub fn inner_mut(&mut self) -> &mut R {
self.lzma_reader
.as_mut()
.map(|reader| reader.inner_mut().inner_mut())
.unwrap_or_else(|| self.inner.as_mut().expect("inner reader not set"))
}
}
impl<R: Read> LzipReader<R> {
pub fn new(inner: R) -> Self {
Self {
inner: Some(inner),
lzma_reader: None,
current_header: None,
finished: false,
trailer_buf: Vec::with_capacity(TRAILER_SIZE),
crc_digest: None,
data_size: 0,
}
}
fn start_next_member(&mut self) -> Result<bool> {
let mut reader = self.inner.take().expect("inner reader not set");
let header = match LzipHeader::parse(&mut reader) {
Ok(header) => header,
Err(_) => {
self.inner = Some(reader);
return Ok(false);
}
};
if header.version != 1 {
return Err(error_invalid_input("unsupported LZIP version"));
}
let counting_reader = CountingReader::new(reader);
let lzma_reader =
LzmaReader::new(counting_reader, u64::MAX, 3, 0, 2, header.dict_size, None)?;
self.current_header = Some(header);
self.lzma_reader = Some(lzma_reader);
self.trailer_buf.clear();
self.crc_digest = Some(Crc32::new());
self.data_size = 0;
Ok(true)
}
fn finish_current_member(&mut self) -> Result<()> {
let lzma_reader = self.lzma_reader.take().expect("lzma reader not set");
let counting_reader = lzma_reader.into_inner();
let compressed_bytes = counting_reader.bytes_read();
let mut inner_reader = counting_reader.inner;
let trailer = LzipTrailer::parse(&mut inner_reader)?;
let computed_crc = self.crc_digest.take().expect("no CRC digest").finalize();
if computed_crc != trailer.crc32 {
self.inner = Some(inner_reader);
return Err(error_invalid_data("LZIP CRC32 mismatch"));
}
if self.data_size != trailer.data_size {
self.inner = Some(inner_reader);
return Err(error_invalid_data("LZIP data size mismatch"));
}
let actual_member_size = HEADER_SIZE as u64 + compressed_bytes + TRAILER_SIZE as u64;
if actual_member_size != trailer.member_size {
self.inner = Some(inner_reader);
return Err(error_invalid_data("LZIP member size mismatch"));
}
self.inner = Some(inner_reader);
Ok(())
}
}
impl<R: Read> Read for LzipReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if buf.is_empty() {
return Ok(0);
}
loop {
if let Some(ref mut lzma_reader) = self.lzma_reader {
match lzma_reader.read(buf) {
Ok(0) => {
self.finish_current_member()?;
if !self.start_next_member()? {
self.finished = true;
return Ok(0);
}
continue;
}
Ok(bytes_read) => {
if let Some(ref mut crc_digest) = self.crc_digest {
crc_digest.update(&buf[..bytes_read]);
self.data_size += bytes_read as u64;
}
return Ok(bytes_read);
}
Err(e) => {
return Err(e);
}
}
} else if self.finished {
return Ok(0);
} else {
if !self.start_next_member()? {
self.finished = true;
return Ok(0);
}
}
}
}
}