Add support for decompressing LZMA
This commit is contained in:
parent
4a5d7d49aa
commit
7f8311efea
5 changed files with 82 additions and 2 deletions
|
@ -28,6 +28,7 @@ time = { version = "0.3.34", optional = true, default-features = false, features
|
||||||
zstd = { version = "0.13.1", optional = true, default-features = false }
|
zstd = { version = "0.13.1", optional = true, default-features = false }
|
||||||
zopfli = { version = "0.8.0", optional = true }
|
zopfli = { version = "0.8.0", optional = true }
|
||||||
deflate64 = { version = "0.1.8", optional = true }
|
deflate64 = { version = "0.1.8", optional = true }
|
||||||
|
lzma-rs = { version = "0.3.0", optional = true }
|
||||||
|
|
||||||
[target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies]
|
[target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies]
|
||||||
crossbeam-utils = "0.8.19"
|
crossbeam-utils = "0.8.19"
|
||||||
|
@ -49,8 +50,9 @@ deflate-miniz = ["flate2/default"]
|
||||||
deflate-zlib = ["flate2/zlib"]
|
deflate-zlib = ["flate2/zlib"]
|
||||||
deflate-zlib-ng = ["flate2/zlib-ng"]
|
deflate-zlib-ng = ["flate2/zlib-ng"]
|
||||||
deflate-zopfli = ["zopfli"]
|
deflate-zopfli = ["zopfli"]
|
||||||
|
lzma = ["lzma-rs/stream"]
|
||||||
unreserved = []
|
unreserved = []
|
||||||
default = ["aes-crypto", "bzip2", "deflate", "deflate64", "deflate-zlib-ng", "deflate-zopfli", "time", "zstd"]
|
default = ["aes-crypto", "bzip2", "deflate", "deflate64", "deflate-zlib-ng", "deflate-zopfli", "lzma", "time", "zstd"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "read_entry"
|
name = "read_entry"
|
||||||
|
|
|
@ -41,6 +41,9 @@ pub enum CompressionMethod {
|
||||||
/// Compress the file using ZStandard
|
/// Compress the file using ZStandard
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
Zstd,
|
Zstd,
|
||||||
|
/// Compress the file using LZMA
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
Lzma,
|
||||||
/// Unsupported compression method
|
/// Unsupported compression method
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
not(fuzzing),
|
not(fuzzing),
|
||||||
|
@ -83,7 +86,10 @@ impl CompressionMethod {
|
||||||
pub const BZIP2: Self = CompressionMethod::Bzip2;
|
pub const BZIP2: Self = CompressionMethod::Bzip2;
|
||||||
#[cfg(not(feature = "bzip2"))]
|
#[cfg(not(feature = "bzip2"))]
|
||||||
pub const BZIP2: Self = CompressionMethod::Unsupported(12);
|
pub const BZIP2: Self = CompressionMethod::Unsupported(12);
|
||||||
|
#[cfg(not(feature = "lzma"))]
|
||||||
pub const LZMA: Self = CompressionMethod::Unsupported(14);
|
pub const LZMA: Self = CompressionMethod::Unsupported(14);
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
pub const LZMA: Self = CompressionMethod::Lzma;
|
||||||
pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
|
pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
|
||||||
pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
|
pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
|
||||||
pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
|
pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
|
||||||
|
@ -123,6 +129,8 @@ impl CompressionMethod {
|
||||||
9 => CompressionMethod::Deflate64,
|
9 => CompressionMethod::Deflate64,
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
12 => CompressionMethod::Bzip2,
|
12 => CompressionMethod::Bzip2,
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
14 => CompressionMethod::Lzma,
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
93 => CompressionMethod::Zstd,
|
93 => CompressionMethod::Zstd,
|
||||||
#[cfg(feature = "aes-crypto")]
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
@ -157,6 +165,8 @@ impl CompressionMethod {
|
||||||
CompressionMethod::Aes => 99,
|
CompressionMethod::Aes => 99,
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
CompressionMethod::Zstd => 93,
|
CompressionMethod::Zstd => 93,
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
CompressionMethod::Lzma => 14,
|
||||||
|
|
||||||
CompressionMethod::Unsupported(v) => v,
|
CompressionMethod::Unsupported(v) => v,
|
||||||
}
|
}
|
||||||
|
|
19
src/read.rs
19
src/read.rs
|
@ -37,6 +37,9 @@ use zstd::stream::read::Decoder as ZstdDecoder;
|
||||||
/// Provides high level API for reading from a stream.
|
/// Provides high level API for reading from a stream.
|
||||||
pub(crate) mod stream;
|
pub(crate) mod stream;
|
||||||
|
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
pub(crate) mod lzma;
|
||||||
|
|
||||||
// Put the struct declaration in a private module to convince rustdoc to display ZipArchive nicely
|
// Put the struct declaration in a private module to convince rustdoc to display ZipArchive nicely
|
||||||
pub(crate) mod zip_archive {
|
pub(crate) mod zip_archive {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -81,6 +84,8 @@ pub(crate) mod zip_archive {
|
||||||
|
|
||||||
use crate::result::ZipError::InvalidPassword;
|
use crate::result::ZipError::InvalidPassword;
|
||||||
pub use zip_archive::ZipArchive;
|
pub use zip_archive::ZipArchive;
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
use crate::read::lzma::LzmaReader;
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub(crate) enum CryptoReader<'a> {
|
pub(crate) enum CryptoReader<'a> {
|
||||||
|
@ -147,6 +152,8 @@ pub(crate) enum ZipFileReader<'a> {
|
||||||
Bzip2(Crc32Reader<BzDecoder<CryptoReader<'a>>>),
|
Bzip2(Crc32Reader<BzDecoder<CryptoReader<'a>>>),
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
Zstd(Crc32Reader<ZstdDecoder<'a, io::BufReader<CryptoReader<'a>>>>),
|
Zstd(Crc32Reader<ZstdDecoder<'a, io::BufReader<CryptoReader<'a>>>>),
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
Lzma(Crc32Reader<LzmaReader<CryptoReader<'a>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Read for ZipFileReader<'a> {
|
impl<'a> Read for ZipFileReader<'a> {
|
||||||
|
@ -168,6 +175,8 @@ impl<'a> Read for ZipFileReader<'a> {
|
||||||
ZipFileReader::Bzip2(r) => r.read(buf),
|
ZipFileReader::Bzip2(r) => r.read(buf),
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
ZipFileReader::Zstd(r) => r.read(buf),
|
ZipFileReader::Zstd(r) => r.read(buf),
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
ZipFileReader::Lzma(r) => r.read(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,6 +201,11 @@ impl<'a> ZipFileReader<'a> {
|
||||||
ZipFileReader::Bzip2(r) => r.into_inner().into_inner().into_inner(),
|
ZipFileReader::Bzip2(r) => r.into_inner().into_inner().into_inner(),
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
ZipFileReader::Zstd(r) => r.into_inner().finish().into_inner().into_inner(),
|
ZipFileReader::Zstd(r) => r.into_inner().finish().into_inner().into_inner(),
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
ZipFileReader::Lzma(r) => {
|
||||||
|
let inner: Box<_> = r.into_inner().finish().unwrap().into();
|
||||||
|
Read::take(Box::leak(inner), u64::MAX)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,6 +327,11 @@ pub(crate) fn make_reader(
|
||||||
let zstd_reader = ZstdDecoder::new(reader).unwrap();
|
let zstd_reader = ZstdDecoder::new(reader).unwrap();
|
||||||
ZipFileReader::Zstd(Crc32Reader::new(zstd_reader, crc32, ae2_encrypted))
|
ZipFileReader::Zstd(Crc32Reader::new(zstd_reader, crc32, ae2_encrypted))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
CompressionMethod::Lzma => {
|
||||||
|
let reader = LzmaReader::new(reader);
|
||||||
|
ZipFileReader::Lzma(Crc32Reader::new(reader, crc32, ae2_encrypted))
|
||||||
|
}
|
||||||
_ => panic!("Compression method not supported"),
|
_ => panic!("Compression method not supported"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
src/read/lzma.rs
Normal file
47
src/read/lzma.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::io::{copy, Error, Read, Result, Write};
|
||||||
|
use lzma_rs::decompress::{Options, Stream, UnpackedSize};
|
||||||
|
|
||||||
|
const COMPRESSED_BYTES_TO_BUFFER: usize = 4096;
|
||||||
|
|
||||||
|
const OPTIONS: Options = Options {
|
||||||
|
unpacked_size: UnpackedSize::ReadFromHeader,
|
||||||
|
memlimit: None,
|
||||||
|
allow_incomplete: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LzmaReader<R> {
|
||||||
|
compressed_reader: R,
|
||||||
|
stream: Stream<VecDeque<u8>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <R: Read> LzmaReader<R> {
|
||||||
|
pub fn new(inner: R) -> Self {
|
||||||
|
LzmaReader {
|
||||||
|
compressed_reader: inner,
|
||||||
|
stream: Stream::new_with_options(&OPTIONS, VecDeque::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(mut self) -> Result<VecDeque<u8>> {
|
||||||
|
copy(&mut self.compressed_reader, &mut self.stream)?;
|
||||||
|
self.stream.finish().map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <R: Read> Read for LzmaReader<R> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
let mut bytes_read = self.stream.get_output_mut().unwrap().read(buf)?;
|
||||||
|
while bytes_read < buf.len() {
|
||||||
|
let mut next_compressed = [0u8; COMPRESSED_BYTES_TO_BUFFER];
|
||||||
|
let compressed_bytes_read = self.compressed_reader.read(&mut next_compressed)?;
|
||||||
|
if compressed_bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.stream.write_all(&next_compressed[..compressed_bytes_read])?;
|
||||||
|
bytes_read += self.stream.get_output_mut().unwrap().read(&mut buf[bytes_read..])?;
|
||||||
|
}
|
||||||
|
Ok(bytes_read)
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,7 +129,7 @@ pub(crate) mod zip_writer {
|
||||||
pub(super) flush_on_finish_file: bool,
|
pub(super) flush_on_finish_file: bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
use crate::result::ZipError::InvalidArchive;
|
use crate::result::ZipError::{InvalidArchive, UnsupportedArchive};
|
||||||
use crate::write::GenericZipWriter::{Closed, Storer};
|
use crate::write::GenericZipWriter::{Closed, Storer};
|
||||||
use crate::zipcrypto::ZipCryptoKeys;
|
use crate::zipcrypto::ZipCryptoKeys;
|
||||||
use crate::CompressionMethod::Stored;
|
use crate::CompressionMethod::Stored;
|
||||||
|
@ -1269,6 +1269,8 @@ impl<W: Write + Seek> GenericZipWriter<W> {
|
||||||
GenericZipWriter::Zstd(ZstdEncoder::new(bare, level as i32).unwrap())
|
GenericZipWriter::Zstd(ZstdEncoder::new(bare, level as i32).unwrap())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "lzma")]
|
||||||
|
CompressionMethod::Lzma => Err(UnsupportedArchive("LZMA isn't supported for compression")),
|
||||||
CompressionMethod::Unsupported(..) => {
|
CompressionMethod::Unsupported(..) => {
|
||||||
Err(ZipError::UnsupportedArchive("Unsupported compression"))
|
Err(ZipError::UnsupportedArchive("Unsupported compression"))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue