use core::{error, fmt};
use crate::{
crc::Crc16,
error::{DecompressionError, LhaHeaderError, LhaError, LhaResult},
header::{CompressionMethod, LhaHeader},
stub_io::{Read, Take, discard_to_end},
};
#[cfg(feature = "lz")]
mod lzs;
#[cfg(feature = "lz")]
mod lz5;
#[cfg(feature = "lh1")]
mod lhv1;
mod lhv2;
#[cfg(feature = "pm")]
mod pmarc;
#[cfg(feature = "lz")]
#[cfg_attr(docsrs, doc(cfg(feature = "lz")))]
pub use lzs::*;
#[cfg(feature = "lz")]
#[cfg_attr(docsrs, doc(cfg(feature = "lz")))]
pub use lz5::*;
#[cfg(feature = "lh1")]
#[cfg_attr(docsrs, doc(cfg(feature = "lh1")))]
pub use lhv1::*;
pub use lhv2::*;
#[cfg(feature = "pm")]
#[cfg_attr(docsrs, doc(cfg(feature = "pm")))]
pub use pmarc::*;
pub trait Decoder<R> {
type Error: error::Error;
fn into_inner(self) -> R;
fn get_ref(&self) -> &R;
fn get_mut(&mut self) -> &mut R;
fn fill_buffer(&mut self, buffer: &mut[u8]) -> Result<(), LhaError<Self::Error>>;
}
#[derive(Debug)]
pub struct LhaDecodeReader<R> {
header: LhaHeader,
crc: Crc16,
output_length: u64,
decoder: Option<DecoderAny<Take<R>>>
}
#[derive(Debug)]
pub struct PassthroughDecoder<R> {
inner: R
}
#[derive(Debug)]
pub struct UnsupportedDecoder<R> {
inner: R
}
pub struct LhaDecodeError<R: Read> {
read: R,
source: LhaError<R::Error>
}
#[non_exhaustive]
#[derive(Debug)]
pub enum DecoderAny<R> {
PassthroughDecoder(PassthroughDecoder<R>),
UnsupportedDecoder(UnsupportedDecoder<R>),
#[cfg(feature = "lz")]
#[cfg_attr(docsrs, doc(cfg(feature = "lz")))]
LzsDecoder(LzsDecoder<R>),
#[cfg(feature = "lz")]
#[cfg_attr(docsrs, doc(cfg(feature = "lz")))]
Lz5Decoder(Lz5Decoder<R>),
#[cfg(feature = "lh1")]
#[cfg_attr(docsrs, doc(cfg(feature = "lh1")))]
Lh1Decoder(Lh1Decoder<R>),
Lh4Decoder(Lh5Decoder<R>),
Lh5Decoder(Lh5Decoder<R>),
Lh6Decoder(Lh7Decoder<R>),
Lh7Decoder(Lh7Decoder<R>),
#[cfg(feature = "lhx")]
#[cfg_attr(docsrs, doc(cfg(feature = "lhx")))]
LhxDecoder(LhxDecoder<R>),
#[cfg(feature = "pm")]
#[cfg_attr(docsrs, doc(cfg(feature = "pm")))]
Pm1Decoder(Pm1Decoder<R>),
#[cfg(feature = "pm")]
#[cfg_attr(docsrs, doc(cfg(feature = "pm")))]
Pm2Decoder(Pm2Decoder<R>),
}
macro_rules! decoder_any_dispatch {
(($model:expr)($($spec:tt)*) => $expr:expr) => {
match $model {
DecoderAny::PassthroughDecoder($($spec)*) => $expr,
DecoderAny::UnsupportedDecoder($($spec)*) => $expr,
#[cfg(feature = "lz")]
DecoderAny::LzsDecoder($($spec)*) => $expr,
#[cfg(feature = "lz")]
DecoderAny::Lz5Decoder($($spec)*) => $expr,
#[cfg(feature = "lh1")]
DecoderAny::Lh1Decoder($($spec)*) => $expr,
DecoderAny::Lh4Decoder($($spec)*)|
DecoderAny::Lh5Decoder($($spec)*) => $expr,
DecoderAny::Lh6Decoder($($spec)*)|
DecoderAny::Lh7Decoder($($spec)*) => $expr,
#[cfg(feature = "lhx")]
DecoderAny::LhxDecoder($($spec)*) => $expr,
#[cfg(feature = "pm")]
DecoderAny::Pm1Decoder($($spec)*) => $expr,
#[cfg(feature = "pm")]
DecoderAny::Pm2Decoder($($spec)*) => $expr,
}
};
}
impl<R: Read> Default for LhaDecodeReader<R> {
fn default() -> Self {
LhaDecodeReader {
header: Default::default(),
crc: Crc16::default(),
output_length: 0,
decoder: None
}
}
}
impl<R: Read> LhaDecodeReader<R> where R::Error: error::Error {
pub fn new(mut rd: R) -> Result<LhaDecodeReader<R>, LhaDecodeError<R>> {
let header = match LhaHeader::read(rd.by_ref()).and_then(|h|
h.ok_or_else(|| LhaError::HeaderParse(LhaHeaderError::HeaderNotFound)))
{
Ok(h) => h,
Err(e) => return Err(wrap_err(rd, e))
};
let decoder = DecoderAny::new_from_header(&header, rd);
let crc = Crc16::default();
Ok(LhaDecodeReader {
header,
crc,
output_length: 0,
decoder: Some(decoder)
})
}
pub fn begin_new(&mut self, mut rd: R) -> Result<bool, LhaDecodeError<R>> {
let res = match LhaHeader::read(rd.by_ref()) {
Ok(Some(header)) => {
let decoder = DecoderAny::new_from_header(&header, rd);
self.decoder = Some(decoder);
self.header = header;
true
}
Ok(None) => {
let decoder = UnsupportedDecoder::new(rd.take(0));
self.decoder = Some(DecoderAny::UnsupportedDecoder(decoder));
false
}
Err(e) => return Err(wrap_err(rd, e))
};
self.crc.reset();
self.output_length = 0;
Ok(res)
}
pub fn begin_with_header_and_decoder(&mut self, header: LhaHeader, decoder: DecoderAny<Take<R>>) {
self.decoder = Some(decoder);
self.header = header;
self.crc.reset();
self.output_length = 0;
}
pub fn next_file(&mut self) -> Result<bool, LhaDecodeError<R>> {
#[cfg(feature = "std")]
{
self.next_file_with_sink::<{8*1024}>()
}
#[cfg(not(feature = "std"))]
{
self.next_file_with_sink::<512>()
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn seek_next_file(&mut self) -> Result<bool, LhaDecodeError<R>>
where R: std::io::Seek + Read<Error=std::io::Error>
{
use std::io::SeekFrom;
let limited_rd = self.decoder.take().expect("decoder not empty").into_inner();
let remaining = limited_rd.limit();
let mut rd = limited_rd.into_inner();
if remaining != 0 {
let res = match i64::try_from(remaining) {
Ok(seek) => rd.seek_relative(seek),
Err(..) => {
rd.stream_position().and_then(|current| {
let absolute = current.checked_add(remaining)
.ok_or_else(|| R::unexpected_eof())?;
rd.seek(SeekFrom::Start(absolute))?;
Ok(())
})
}
};
if let Err(e) = res {
return Err(wrap_err(rd, LhaError::Io(e)))
}
}
self.begin_new(rd)
}
pub fn next_file_with_sink<const BUF: usize>(&mut self) -> Result<bool, LhaDecodeError<R>> {
let mut limited_rd = self.decoder.take().expect("decoder not empty").into_inner();
if limited_rd.limit() != 0 &&
let Err(e) = discard_to_end::<_, BUF>(&mut limited_rd).map_err(LhaError::Io)
{
return Err(wrap_err(limited_rd.into_inner(), e))
}
self.begin_new(limited_rd.into_inner())
}
pub fn header(&self) -> &LhaHeader {
&self.header
}
pub fn into_parts(self) -> (LhaHeader, Option<DecoderAny<Take<R>>>) {
(self.header, self.decoder)
}
pub fn into_inner(self) -> R {
self.decoder.expect("decoder not empty").into_inner().into_inner()
}
pub fn take_decoder(&mut self) -> Option<DecoderAny<Take<R>>> {
self.header.original_size = 0;
self.output_length = 0;
self.crc.reset();
self.decoder.take()
}
pub fn get_decoder(&self) -> Option<&DecoderAny<Take<R>>> {
self.decoder.as_ref()
}
pub fn get_mut_decoder(&mut self) -> Option<&mut DecoderAny<Take<R>>> {
self.decoder.as_mut()
}
pub fn take_inner(&mut self) -> Option<R> {
self.take_decoder().map(|decoder| decoder.into_inner().into_inner())
}
pub fn get_ref(&self) -> Option<&R> {
self.decoder.as_ref().map(|decoder| decoder.get_ref().get_ref())
}
pub fn get_mut(&mut self) -> Option<&mut R> {
self.decoder.as_mut().map(|decoder| decoder.get_mut().get_mut())
}
pub fn len(&self) -> u64 {
self.header.original_size - self.output_length
}
pub fn is_empty(&self) -> bool {
self.header.original_size == self.output_length
}
pub fn is_present(&self) -> bool {
self.decoder.is_some()
}
pub fn is_absent(&self) -> bool {
self.decoder.is_none()
}
pub fn crc_is_ok(&self) -> bool {
self.crc.sum16() == self.header.file_crc
}
pub fn crc_check(&self) -> LhaResult<u16, R> {
if self.crc_is_ok() {
Ok(self.header.file_crc)
}
else {
Err(LhaError::Checksum)
}
}
pub fn is_decoder_supported(&self) -> bool {
self.decoder.as_ref().map(|d| d.is_supported()).unwrap_or(false)
}
}
#[cfg(feature = "std")]
impl<R: Read<Error=std::io::Error>> std::io::Read for LhaDecodeReader<R> {
fn read(&mut self, buf: &mut[u8]) -> std::io::Result<usize> {
let len = buf.len().min((self.header.original_size - self.output_length) as usize);
let target = &mut buf[..len];
self.decoder.as_mut().unwrap().fill_buffer(target)?;
self.output_length += len as u64;
self.crc.digest(target);
Ok(len)
}
}
#[cfg(not(feature = "std"))]
impl<R: Read> Read for LhaDecodeReader<R> where R::Error: error::Error {
type Error = LhaError<R::Error>;
fn unexpected_eof() -> Self::Error {
LhaError::Io(R::unexpected_eof())
}
fn read_all(&mut self, buf: &mut[u8]) -> Result<usize, Self::Error> {
let len = buf.len().min((self.header.original_size - self.output_length) as usize);
let target = &mut buf[..len];
self.decoder.as_mut().unwrap().fill_buffer(target)?;
self.output_length += len as u64;
self.crc.digest(target);
Ok(len)
}
}
impl<R: Read> DecoderAny<R> {
pub fn new_from_header(header: &LhaHeader, rd: R) -> DecoderAny<Take<R>> {
let limited_rd = rd.take(header.compressed_size);
match header.compression_method() {
Ok(compression) => DecoderAny::new_from_compression(compression, limited_rd),
Err(..) => DecoderAny::UnsupportedDecoder(UnsupportedDecoder::new(limited_rd))
}
}
pub fn new_from_compression(
compression: CompressionMethod,
rd: R
) -> Self
{
match compression {
CompressionMethod::Pm0|
CompressionMethod::Lz4|
CompressionMethod::Lh0 => DecoderAny::PassthroughDecoder(PassthroughDecoder::new(rd)),
#[cfg(feature = "lz")]
CompressionMethod::Lzs => DecoderAny::LzsDecoder(LzsDecoder::new(rd)),
#[cfg(feature = "lz")]
CompressionMethod::Lz5 => DecoderAny::Lz5Decoder(Lz5Decoder::new(rd)),
#[cfg(feature = "lh1")]
CompressionMethod::Lh1 => DecoderAny::Lh1Decoder(Lh1Decoder::new(rd)),
CompressionMethod::Lh4 => DecoderAny::Lh4Decoder(Lh5Decoder::new(rd)),
CompressionMethod::Lh5 => DecoderAny::Lh5Decoder(Lh5Decoder::new(rd)),
CompressionMethod::Lh6 => DecoderAny::Lh6Decoder(Lh7Decoder::new(rd)),
CompressionMethod::Lh7 => DecoderAny::Lh7Decoder(Lh7Decoder::new(rd)),
#[cfg(feature = "lhx")]
CompressionMethod::Lhx => DecoderAny::LhxDecoder(LhxDecoder::new(rd)),
#[cfg(feature = "pm")]
CompressionMethod::Pm1 => DecoderAny::Pm1Decoder(Pm1Decoder::new(rd)),
#[cfg(feature = "pm")]
CompressionMethod::Pm2 => DecoderAny::Pm2Decoder(Pm2Decoder::new(rd)),
_ => DecoderAny::UnsupportedDecoder(UnsupportedDecoder::new(rd))
}
}
pub fn is_supported(&self) -> bool {
!matches!(self, DecoderAny::UnsupportedDecoder(..))
}
}
impl<R: Read> Decoder<R> for DecoderAny<R> where R::Error: error::Error {
type Error = R::Error;
fn into_inner(self) -> R {
decoder_any_dispatch!((self)(decoder) => decoder.into_inner())
}
fn get_ref(&self) -> &R {
decoder_any_dispatch!((self)(decoder) => decoder.get_ref())
}
fn get_mut(&mut self) -> &mut R {
decoder_any_dispatch!((self)(decoder) => decoder.get_mut())
}
#[inline]
fn fill_buffer(&mut self, buf: &mut[u8]) -> Result<(), LhaError<Self::Error>> {
decoder_any_dispatch!((self)(decoder) => decoder.fill_buffer(buf))
}
}
impl<R: Read> PassthroughDecoder<R> {
pub fn new(inner: R) -> Self {
PassthroughDecoder { inner }
}
}
impl<R: Read> Decoder<R> for PassthroughDecoder<R> where R::Error: error::Error {
type Error = R::Error;
fn into_inner(self) -> R {
self.inner
}
fn get_ref(&self) -> &R {
&self.inner
}
fn get_mut(&mut self) -> &mut R {
&mut self.inner
}
#[inline]
fn fill_buffer(&mut self, buf: &mut[u8]) -> Result<(), LhaError<Self::Error>> {
self.inner.read_exact(buf).map_err(LhaError::Io)
}
}
impl<R: Read> UnsupportedDecoder<R> {
pub fn new(inner: R) -> Self {
UnsupportedDecoder { inner }
}
}
impl<R: Read> Decoder<R> for UnsupportedDecoder<R> where R::Error: error::Error {
type Error = R::Error;
fn into_inner(self) -> R {
self.inner
}
fn get_ref(&self) -> &R {
&self.inner
}
fn get_mut(&mut self) -> &mut R {
&mut self.inner
}
#[inline]
fn fill_buffer(&mut self, _buf: &mut[u8]) -> Result<(), LhaError<Self::Error>> {
Err(LhaError::Decompress(DecompressionError::UnsupportedCompression))
}
}
impl<R: Read> LhaDecodeError<R> {
pub fn into_inner(self) -> R {
self.read
}
pub fn get_ref(&self) -> &R {
&self.read
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.read
}
}
impl<R: Read> error::Error for LhaDecodeError<R>
where LhaError<R::Error>: error::Error + 'static
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.source)
}
}
impl<R: Read> fmt::Debug for LhaDecodeError<R>
where LhaError<R::Error>: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LhaDecodeError")
.field("source", &self.source)
.finish()
}
}
impl<R: Read> fmt::Display for LhaDecodeError<R>
where LhaError<R::Error>: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "LHA decode error: {}", self.source)
}
}
impl<R: Read> From<LhaDecodeError<R>> for LhaError<R::Error> {
fn from(e: LhaDecodeError<R>) -> Self {
e.source
}
}
#[cfg(feature = "std")]
impl<R: Read> From<LhaDecodeError<R>> for std::io::Error
where std::io::Error: From<LhaError<R::Error>>
{
fn from(e: LhaDecodeError<R>) -> Self {
e.source.into()
}
}
fn wrap_err<R: Read>(read: R, source: LhaError<R::Error>) -> LhaDecodeError<R> {
LhaDecodeError { read, source }
}
macro_rules! unsafe_assert {
($expr:expr) => {
#[cfg(all(not(feature = "no-unsafe-assertions"), not(debug_assertions)))]
unsafe {
core::hint::assert_unchecked($expr)
}
debug_assert!($expr)
};
}
use unsafe_assert;
#[cfg(feature = "std")]
#[cfg(test)]
fn build_random_tree_lengths(
max_values: usize,
mut max_depth: u8,
final_size: usize,
rng: &mut impl rand::Rng,
out: &mut Vec<u8>
)
{
use rand::{RngExt, seq::SliceRandom};
out.clear();
let mut max_leaves = 2usize;
for level in 1..max_depth {
let n = out.len();
let remaining = max_values - n;
let num_leaves;
if let Some(margin) = (max_leaves * 2).checked_sub(remaining) {
if remaining <= max_leaves {
max_depth = level;
break
}
num_leaves = margin;
}
else {
num_leaves = rng.random_range(0..max_leaves);
};
max_leaves = (max_leaves - num_leaves) * 2;
out.resize(n + num_leaves, level);
}
out.resize(out.len() + max_leaves, max_depth);
assert!(final_size >= out.len(), "final_size: {} < out.len: {}", final_size, out.len());
out.resize(final_size, 0);
out.shuffle(rng);
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
#[cfg(not(feature = "std"))]
use crate::UnexpectedEofError;
#[cfg(feature = "std")]
use std::{io, error::Error};
use crate::OsType;
use super::*;
#[test]
fn decode_error_works() {
let mut data: &[u8] = &[0u8;3];
let rd = &mut data;
let mut err = LhaDecodeReader::new(rd).unwrap_err();
assert_eq!(err.to_string(), "LHA decode error: while parsing LHA header: header not found");
assert_eq!(err.get_ref(), &&[0u8;2]);
assert_eq!(err.get_mut(), &mut &[0u8;2]);
assert_eq!(err.into_inner(), &[0u8;2]);
#[cfg(not(feature = "std"))]
assert_eq!(<LhaDecodeReader::<&[u8]> as Read>::unexpected_eof(),
LhaError::Io(UnexpectedEofError));
}
#[cfg(feature = "std")]
#[test]
fn decode_std_error_works() {
static HEADER_BAD: &[u8] = {
b"\x01\0-lh0-\0\0\0\0\0\0\0\0\0\0\0\0\x20\x04"
};
let mut reader = LhaDecodeReader::default();
println!("{:?}", reader);
let mut data: &[u8] = HEADER_BAD;
let err = reader.begin_new(&mut data).unwrap_err();
assert_eq!(err.source().unwrap()
.downcast_ref::<LhaError::<io::Error>>().unwrap()
.source().unwrap()
.downcast_ref::<LhaHeaderError>().unwrap(),
&LhaHeaderError::UnknownLevel);
assert!(matches!(LhaError::from(err), LhaError::HeaderParse(LhaHeaderError::UnknownLevel)));
let rdlimit = io::Cursor::new(Vec::new()).take(u64::MAX);
let header = reader.header().clone();
let mut reader = LhaDecodeReader::default();
reader.begin_with_header_and_decoder(header.clone(),
DecoderAny::new_from_compression(CompressionMethod::Lh0, rdlimit));
reader.seek_next_file().unwrap();
assert_eq!(data, &[]);
struct PhonySeek;
impl io::Read for PhonySeek {
fn read(&mut self, _buf: &mut[u8]) -> io::Result<usize> {
Err(io::ErrorKind::UnexpectedEof.into())
}
}
impl io::Seek for PhonySeek {
fn seek(&mut self, _style: io::SeekFrom) -> io::Result<u64> {
Err(io::ErrorKind::UnexpectedEof.into())
}
}
let rdlimit = PhonySeek.take(u64::MAX);
let mut reader = LhaDecodeReader::<PhonySeek>::default();
reader.begin_with_header_and_decoder(header.clone(),
DecoderAny::new_from_compression(CompressionMethod::Lh0, rdlimit));
let err = reader.seek_next_file().unwrap_err();
assert!(reader.take_inner().is_none());
assert_eq!(io::Error::from(err).kind(), io::ErrorKind::UnexpectedEof);
let rdlimit = PhonySeek.take(u64::MAX);
reader.begin_with_header_and_decoder(header,
DecoderAny::new_from_compression(CompressionMethod::Lh0, rdlimit));
let err = reader.next_file().unwrap_err();
assert_eq!(io::Error::from(err).kind(), io::ErrorKind::UnexpectedEof);
let mut data: &[u8] = &[];
let err = LhaDecodeReader::new(&mut data).unwrap_err();
println!("{:?}", err);
assert_eq!(err.to_string(), "LHA decode error: while parsing LHA header: header not found");
assert_eq!(io::Error::from(err).to_string(), "while parsing LHA header: header not found");
assert_eq!(<LhaDecodeReader::<&[u8]> as Read>::unexpected_eof().kind(),
io::ErrorKind::UnexpectedEof);
}
#[should_panic]
#[test]
fn decode_into_inner_panics() {
LhaDecodeReader::<&[u8]>::default().into_inner();
}
#[should_panic]
#[test]
fn decode_next_file_panics() {
let _ = LhaDecodeReader::<&[u8]>::default().next_file();
}
#[cfg(feature = "std")]
#[should_panic]
#[test]
fn decode_seek_next_file_panics() {
let _ = LhaDecodeReader::<std::fs::File>::default().seek_next_file();
}
#[test]
fn decode_works() {
static HEADER_0: &[u8] = {
b"\x1F\xC1-lh0-\0\0\0\0\0\0\0\0\xCA\x83\xE7\x2C\x20\x00\x09test\\test\xFF\xFF"
};
let mut data = HEADER_0;
let mut reader = LhaDecodeReader::new(&mut data).unwrap();
assert!(reader.is_present());
assert!(matches!(reader.get_decoder().unwrap(), &DecoderAny::PassthroughDecoder(..)));
assert!(matches!(reader.get_mut_decoder().unwrap(), &mut DecoderAny::PassthroughDecoder(..)));
assert_eq!(reader.get_ref().unwrap(), &&mut &[]);
assert_eq!(reader.get_mut().unwrap(), &mut &mut &[]);
assert!(matches!(reader.crc_check().unwrap_err(), LhaError::Checksum));
let (header, decoder) = reader.into_parts();
let mut decoder = decoder.unwrap();
assert!(decoder.is_supported());
assert!(matches!(decoder, DecoderAny::PassthroughDecoder(..)));
assert_eq!(decoder.get_ref().get_ref(), &&mut &[]);
assert_eq!(decoder.get_mut().get_mut(), &mut &mut &[]);
let data = decoder.into_inner().into_inner();
assert!(data.is_empty());
assert_eq!(header.level, 0);
assert_eq!(header.original_size, 0);
assert_eq!(header.compressed_size, 0);
assert_eq!(header.parse_os_type().unwrap(), OsType::Generic);
assert!(!header.is_directory());
assert!(!header.compression_method().unwrap().is_compressed());
assert_eq!(header.parse_pathname_to_str(), "test/test");
assert_eq!(header.file_crc, u16::MAX);
static HEADER_1: &[u8] = {
b"\x1D\xFF-lh0-\x0D\0\0\0\0\0\0\0\xCA\x83\xE7\x2C\x20\x01\x04test\0\0J\x05\x00\x00\x40\x7F\x08\x00\x02test\xFF\0\0"
};
let mut data = HEADER_1;
let mut reader = LhaDecodeReader::new(&mut data).unwrap();
assert!(reader.is_present());
assert!(!reader.is_absent());
{
let header = reader.header();
assert_eq!(header.level, 1);
assert_eq!(header.original_size, 0);
assert_eq!(header.compressed_size, 0);
assert_eq!(header.parse_os_type().unwrap(), OsType::Java);
assert!(!header.compression_method().unwrap().is_compressed());
assert!(!header.is_directory());
assert_eq!(header.parse_pathname_to_str(), "test/test");
assert_eq!(header.file_crc, 0);
}
assert!(matches!(reader.get_decoder().unwrap(), &DecoderAny::PassthroughDecoder(..)));
assert!(matches!(reader.get_mut_decoder().unwrap(), &mut DecoderAny::PassthroughDecoder(..)));
assert_eq!(reader.get_ref().unwrap(), &&mut &[]);
assert_eq!(reader.get_mut().unwrap(), &mut &mut &[]);
let mut decoder = reader.take_decoder().unwrap();
assert!(decoder.is_supported());
assert!(matches!(decoder, DecoderAny::PassthroughDecoder(..)));
assert_eq!(decoder.get_ref().get_ref(), &&mut &[]);
assert_eq!(decoder.get_mut().get_mut(), &mut &mut &[]);
let data = decoder.into_inner().into_inner();
assert!(data.is_empty());
let decoder = DecoderAny::new_from_header(&header, data);
assert!(!reader.is_present());
assert!(reader.is_absent());
reader.begin_with_header_and_decoder(header.clone(), decoder);
assert_eq!(&header, reader.header());
assert_eq!(reader.header().level, 0);
let rd = reader.take_decoder().unwrap().into_inner();
reader.begin_with_header_and_decoder(header.clone(),
DecoderAny::new_from_compression(CompressionMethod::Lhd, rd));
assert!(matches!(reader.get_decoder().unwrap(), &DecoderAny::UnsupportedDecoder(..)));
assert!(matches!(reader.get_mut_decoder().unwrap(), &mut DecoderAny::UnsupportedDecoder(..)));
assert_eq!(reader.get_ref().unwrap(), &&mut &[]);
assert_eq!(reader.get_mut().unwrap(), &mut &mut &[]);
assert!(!reader.next_file().unwrap());
static HEADER_1A: &[u8] = {
static INNER: [[u8; 16];11] = [
*b"\x19\x85-lh0-\x8A\0\0\0\0\0\0\0\xBC",
*b"\x54\xF1\x2C\x20\x01\x00\0\0\x4A\x05\0\0\x63\xAF\x85\0",
*b"\x01use a filename ", *b"header as a subs",
*b"titute for the f", *b"ilename field wh",
*b"en byte count of", *b" filename is in ",
*b"excess of the fo", *b"regoing restrict",
*b"ion\0\0___________",
];
INNER.as_flattened()
};
let mut data = HEADER_1A;
let mut reader = LhaDecodeReader::new(&mut data).unwrap();
{
let header = reader.header();
assert_eq!(header.level, 1);
assert_eq!(header.original_size, 0);
assert_eq!(header.compressed_size, 0);
assert!(!header.is_directory());
assert_eq!(header.parse_os_type().unwrap(), OsType::Java);
assert!(!header.compression_method().unwrap().is_compressed());
assert_eq!(header.parse_pathname_to_str(), concat!(
"use a filename header as a substitute for the filename field when ",
"byte count of filename is in excess of the foregoing restriction"));
assert_eq!(header.file_crc, 0);
}
let data = reader.take_inner().unwrap();
assert_eq!(data, b"___________");
let mut data = HEADER_0;
reader.begin_new(&mut data).unwrap();
assert_eq!(&header, reader.header());
assert_eq!(reader.header().level, 0);
let data = reader.into_inner();
assert_eq!(data, &[]);
}
}