use std::{
fmt,
io::{self, Error, ErrorKind, Read, Seek, SeekFrom, Take},
};
use flate2::read::ZlibDecoder;
use lzma_rust2::{Lzma2Reader, LzmaReader};
use zerocopy::{KnownLayout, TryFromBytes};
use crate::{
ReadBytesExt,
error::{InnoError, InnoResult},
header::Compression,
lzma_stream_header::LzmaStreamHeader,
read::chunk::Chunk,
};
#[derive(Clone, Copy, TryFromBytes, KnownLayout)]
#[repr(u32)]
pub enum ZlibID {
Magic = u32::from_le_bytes(*b"zlb\x1a"),
}
impl ZlibID {
pub fn try_read_from_io<R>(mut src: R) -> InnoResult<Self>
where
Self: Sized,
R: Read,
{
let mut magic = [0; size_of::<Self>()];
src.read_exact(&mut magic)?;
Self::try_read_from_bytes(&magic).map_err(|_| InnoError::InvalidChunkMagic(magic))
}
#[must_use]
#[inline]
pub const fn as_str(self) -> &'static str {
"zlb\u{1a}"
}
}
impl fmt::Display for ZlibID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
pub enum DataChunkReader<R: Read> {
Stored(Take<R>),
Zlib(ZlibDecoder<Take<R>>),
Lzma1(Box<LzmaReader<Take<R>>>),
Lzma2(Box<Lzma2Reader<Take<R>>>),
}
fn read_lzma2_dict_size<R: Read>(reader: &mut R) -> io::Result<u32> {
let prop = reader.read_u8()?;
if prop > 40 {
return Err(Error::new(
ErrorKind::InvalidData,
"invalid LZMA2 dictionary size",
));
}
Ok(if prop == 40 {
u32::MAX
} else {
let base = 2 | u32::from(prop & 1);
base << (u32::from(prop) / 2 + 11)
})
}
impl<R: Read + Seek> DataChunkReader<R> {
pub fn new(mut reader: R, data_offset: u64, chunk: &Chunk) -> InnoResult<Self> {
if chunk.is_encrypted() {
return Err(InnoError::Encrypted);
}
reader.seek(SeekFrom::Start(data_offset + chunk.start_offset()))?;
ZlibID::try_read_from_io(&mut reader)?;
let mut chunk_reader = reader.take(chunk.original_size());
match chunk.compression() {
Compression::Stored => Ok(Self::Stored(chunk_reader)),
Compression::Zlib => Ok(Self::Zlib(ZlibDecoder::new(chunk_reader))),
Compression::LZMA1 => {
let header = chunk_reader.read_t::<LzmaStreamHeader>()?;
let lzma = LzmaReader::new_with_props(
chunk_reader,
u64::MAX,
header.props(),
header.dictionary_size(),
None,
)
.map_err(|err| Error::new(ErrorKind::InvalidData, err))?;
Ok(Self::Lzma1(Box::new(lzma)))
}
Compression::LZMA2 => {
let dict_size = read_lzma2_dict_size(&mut chunk_reader)?;
Ok(Self::Lzma2(Box::new(Lzma2Reader::new(
chunk_reader,
dict_size,
None,
))))
}
other => Err(InnoError::UnsupportedCompression(other)),
}
}
#[must_use]
pub fn get_ref(&self) -> &Take<R> {
match self {
Self::Stored(reader) => reader,
Self::Zlib(reader) => reader.get_ref(),
Self::Lzma1(reader) => reader.inner(),
Self::Lzma2(reader) => reader.inner(),
}
}
#[must_use]
pub fn get_mut(&mut self) -> &mut Take<R> {
match self {
Self::Stored(reader) => reader,
Self::Zlib(reader) => reader.get_mut(),
Self::Lzma1(reader) => reader.inner_mut(),
Self::Lzma2(reader) => reader.inner_mut(),
}
}
#[must_use]
pub fn into_inner(self) -> Take<R> {
match self {
Self::Stored(reader) => reader,
Self::Zlib(reader) => reader.into_inner(),
Self::Lzma1(reader) => reader.into_inner(),
Self::Lzma2(reader) => reader.into_inner(),
}
}
}
impl<R: Read> Read for DataChunkReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Self::Stored(reader) => reader.read(buf),
Self::Zlib(reader) => reader.read(buf),
Self::Lzma1(reader) => reader.read(buf),
Self::Lzma2(reader) => reader.read(buf),
}
}
}