diff options
| author | Igor Tolmachev <me@igorek.dev> | 2024-07-12 21:40:08 +0900 |
|---|---|---|
| committer | Igor Tolmachev <me@igorek.dev> | 2024-07-12 21:40:08 +0900 |
| commit | b77106b526930990f51a306fd70cd00856f481e8 (patch) | |
| tree | 3d4d1b4e08f70ec204fdc986c8e20687044eb90d /src | |
| parent | eb3dd960e3988f43fb74383505801a0b42a1eeea (diff) | |
| download | archivator-b77106b526930990f51a306fd70cd00856f481e8.tar.gz archivator-b77106b526930990f51a306fd70cd00856f481e8.zip | |
Add zstd compression and fix bugs
Diffstat (limited to 'src')
| -rw-r--r-- | src/utils/cursor.rs | 60 | ||||
| -rw-r--r-- | src/utils/mod.rs | 5 | ||||
| -rw-r--r-- | src/utils/read.rs (renamed from src/utils.rs) | 0 | ||||
| -rw-r--r-- | src/zip/driver.rs | 9 | ||||
| -rw-r--r-- | src/zip/error.rs | 2 | ||||
| -rw-r--r-- | src/zip/file/info.rs | 7 | ||||
| -rw-r--r-- | src/zip/file/read.rs | 106 | ||||
| -rw-r--r-- | src/zip/file/write.rs | 4 |
8 files changed, 124 insertions, 69 deletions
diff --git a/src/utils/cursor.rs b/src/utils/cursor.rs new file mode 100644 index 0000000..c41270a --- /dev/null +++ b/src/utils/cursor.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Write}; | ||
| 2 | |||
| 3 | pub struct IoCursor<Io> { | ||
| 4 | io: Io, | ||
| 5 | cursor: u64, | ||
| 6 | bounds: (u64, u64), | ||
| 7 | } | ||
| 8 | |||
| 9 | impl<Io: Seek> IoCursor<Io> { | ||
| 10 | pub fn new(mut io: Io, start: u64, end: u64) -> Result<Self> { | ||
| 11 | let cursor = io.seek(SeekFrom::Start(start))?; | ||
| 12 | Ok(Self { | ||
| 13 | io, | ||
| 14 | cursor, | ||
| 15 | bounds: (cursor, end), | ||
| 16 | }) | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<Io: Read> Read for IoCursor<Io> { | ||
| 21 | fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
| 22 | let upper = buf.len().min((self.bounds.1 - self.cursor) as usize); | ||
| 23 | let bytes = self.io.read(&mut buf[..upper])?; | ||
| 24 | self.cursor += bytes as u64; | ||
| 25 | Ok(bytes) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | impl<Io: Write> Write for IoCursor<Io> { | ||
| 30 | fn write(&mut self, buf: &[u8]) -> Result<usize> { | ||
| 31 | let upper = buf.len().min((self.bounds.1 - self.cursor) as usize); | ||
| 32 | let bytes = self.io.write(&buf[..upper])?; | ||
| 33 | self.cursor += bytes as u64; | ||
| 34 | Ok(bytes) | ||
| 35 | } | ||
| 36 | |||
| 37 | #[inline] | ||
| 38 | fn flush(&mut self) -> Result<()> { | ||
| 39 | self.io.flush() | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | impl<Io: Seek> Seek for IoCursor<Io> { | ||
| 44 | fn seek(&mut self, pos: SeekFrom) -> Result<u64> { | ||
| 45 | self.cursor = match pos { | ||
| 46 | SeekFrom::Start(0) => return Ok(self.cursor - self.bounds.0), | ||
| 47 | SeekFrom::Start(offset) => self.bounds.0.checked_add(offset), | ||
| 48 | SeekFrom::End(offset) => self.bounds.1.checked_add_signed(offset), | ||
| 49 | SeekFrom::Current(offset) => self.cursor.checked_add_signed(offset), | ||
| 50 | } | ||
| 51 | .filter(|v| *v >= self.bounds.0) | ||
| 52 | .ok_or(Error::new( | ||
| 53 | ErrorKind::InvalidInput, | ||
| 54 | "Invalid seek to a negative or overflowing position", | ||
| 55 | ))? | ||
| 56 | .min(self.bounds.1); | ||
| 57 | |||
| 58 | Ok(self.io.seek(SeekFrom::Start(self.cursor))? - self.bounds.0) | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..99a4e13 --- /dev/null +++ b/src/utils/mod.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | mod cursor; | ||
| 2 | mod read; | ||
| 3 | |||
| 4 | pub use cursor::IoCursor; | ||
| 5 | pub use read::ReadUtils; | ||
diff --git a/src/utils.rs b/src/utils/read.rs index 185758a..185758a 100644 --- a/src/utils.rs +++ b/src/utils/read.rs | |||
diff --git a/src/zip/driver.rs b/src/zip/driver.rs index 3793e31..99b409d 100644 --- a/src/zip/driver.rs +++ b/src/zip/driver.rs | |||
| @@ -5,7 +5,7 @@ use crate::zip::{ | |||
| 5 | BitFlag, CompressionMethod, ZipError, ZipFileInfo, ZipFileReader, ZipFileWriter, ZipResult, | 5 | BitFlag, CompressionMethod, ZipError, ZipFileInfo, ZipFileReader, ZipFileWriter, ZipResult, |
| 6 | }; | 6 | }; |
| 7 | use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; | 7 | use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; |
| 8 | use std::collections::HashMap as Map; | 8 | use std::collections::BTreeMap as Map; |
| 9 | use std::fs::File; | 9 | use std::fs::File; |
| 10 | use std::io::{Read, Seek, SeekFrom, Write}; | 10 | use std::io::{Read, Seek, SeekFrom, Write}; |
| 11 | 11 | ||
| @@ -105,7 +105,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 105 | }; | 105 | }; |
| 106 | 106 | ||
| 107 | // Read cd records | 107 | // Read cd records |
| 108 | let mut files = Map::with_capacity(cd_records as usize); | 108 | let mut files = Map::new(); |
| 109 | io.seek(SeekFrom::Start(cd_pointer))?; | 109 | io.seek(SeekFrom::Start(cd_pointer))?; |
| 110 | let buf = io.read_vec(cd_size as usize)?; | 110 | let buf = io.read_vec(cd_size as usize)?; |
| 111 | 111 | ||
| @@ -200,6 +200,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 200 | ))?; | 200 | ))?; |
| 201 | ep += header.size as usize | 201 | ep += header.size as usize |
| 202 | } | 202 | } |
| 203 | // Skip unrecognized header | ||
| 203 | _ => ep += header.size as usize, | 204 | _ => ep += header.size as usize, |
| 204 | } | 205 | } |
| 205 | } | 206 | } |
| @@ -226,9 +227,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 226 | } | 227 | } |
| 227 | 228 | ||
| 228 | fn files(&self) -> Vec<&Self::FileInfo> { | 229 | fn files(&self) -> Vec<&Self::FileInfo> { |
| 229 | let mut files: Vec<&Self::FileInfo> = self.files.values().collect(); | 230 | self.files.values().collect() |
| 230 | files.sort_by_key(|f| &f.name); | ||
| 231 | files | ||
| 232 | } | 231 | } |
| 233 | 232 | ||
| 234 | fn get_file_info(&self, name: &str) -> ZipResult<&Self::FileInfo> { | 233 | fn get_file_info(&self, name: &str) -> ZipResult<&Self::FileInfo> { |
diff --git a/src/zip/error.rs b/src/zip/error.rs index 963daad..c77370b 100644 --- a/src/zip/error.rs +++ b/src/zip/error.rs | |||
| @@ -19,7 +19,6 @@ pub enum ZipError { | |||
| 19 | InvalidFileName, | 19 | InvalidFileName, |
| 20 | InvalidFileComment, | 20 | InvalidFileComment, |
| 21 | 21 | ||
| 22 | NegativeFileOffset, | ||
| 23 | FileNotFound, | 22 | FileNotFound, |
| 24 | CompressedDataIsUnseekable, | 23 | CompressedDataIsUnseekable, |
| 25 | } | 24 | } |
| @@ -58,7 +57,6 @@ impl Display for ZipError { | |||
| 58 | Self::InvalidFileName => write!(f, "Invalid file name"), | 57 | Self::InvalidFileName => write!(f, "Invalid file name"), |
| 59 | Self::InvalidFileComment => write!(f, "Invalid file comment"), | 58 | Self::InvalidFileComment => write!(f, "Invalid file comment"), |
| 60 | 59 | ||
| 61 | Self::NegativeFileOffset => write!(f, "Negative file offset"), | ||
| 62 | Self::FileNotFound => write!(f, "File not found"), | 60 | Self::FileNotFound => write!(f, "File not found"), |
| 63 | Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"), | 61 | Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"), |
| 64 | } | 62 | } |
diff --git a/src/zip/file/info.rs b/src/zip/file/info.rs index bfec0b7..4e1b293 100644 --- a/src/zip/file/info.rs +++ b/src/zip/file/info.rs | |||
| @@ -8,7 +8,9 @@ pub enum CompressionMethod { | |||
| 8 | Deflate, | 8 | Deflate, |
| 9 | BZip2, | 9 | BZip2, |
| 10 | Lzma, | 10 | Lzma, |
| 11 | Zstd, | ||
| 11 | Xz, | 12 | Xz, |
| 13 | Unsupported, | ||
| 12 | } | 14 | } |
| 13 | 15 | ||
| 14 | impl CompressionMethod { | 16 | impl CompressionMethod { |
| @@ -18,10 +20,9 @@ impl CompressionMethod { | |||
| 18 | 8 => Ok(Self::Deflate), | 20 | 8 => Ok(Self::Deflate), |
| 19 | 12 => Ok(Self::BZip2), | 21 | 12 => Ok(Self::BZip2), |
| 20 | 14 => Ok(Self::Lzma), | 22 | 14 => Ok(Self::Lzma), |
| 23 | 93 => Ok(Self::Zstd), | ||
| 21 | 95 => Ok(Self::Xz), | 24 | 95 => Ok(Self::Xz), |
| 22 | 1..=7 | 9..=11 | 13 | 15..=20 | 93..=94 | 96..=99 => { | 25 | 1..=7 | 9..=11 | 13 | 15..=20 | 94 | 96..=99 => Ok(Self::Unsupported), |
| 23 | Err(ZipError::UnsupportedCompressionMethod.into()) | ||
| 24 | } | ||
| 25 | 21..=92 | 100.. => Err(ZipError::InvalidCompressionMethod.into()), | 26 | 21..=92 | 100.. => Err(ZipError::InvalidCompressionMethod.into()), |
| 26 | } | 27 | } |
| 27 | } | 28 | } |
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs index 7d683db..f5a54f3 100644 --- a/src/zip/file/read.rs +++ b/src/zip/file/read.rs | |||
| @@ -1,27 +1,26 @@ | |||
| 1 | use crate::driver::FileDriver; | 1 | use crate::driver::FileDriver; |
| 2 | use crate::utils::ReadUtils; | 2 | use crate::utils::{IoCursor, ReadUtils}; |
| 3 | use crate::zip::{CompressionMethod, ZipError, ZipFileInfo, ZipResult}; | 3 | use crate::zip::{CompressionMethod, ZipError, ZipFileInfo, ZipResult}; |
| 4 | use bzip2::read::BzDecoder; | 4 | use bzip2::read::BzDecoder; |
| 5 | use flate2::read::DeflateDecoder; | 5 | use flate2::read::DeflateDecoder; |
| 6 | use liblzma::read::XzDecoder; | 6 | use liblzma::read::XzDecoder; |
| 7 | use liblzma::stream::{Filters, LzmaOptions, Stream}; | 7 | use liblzma::stream::{Filters, LzmaOptions, Stream}; |
| 8 | use std::io::{ | 8 | use std::io::{ |
| 9 | Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, | 9 | BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, |
| 10 | }; | 10 | }; |
| 11 | use zstd::stream::Decoder as ZstdDecoder; | ||
| 11 | 12 | ||
| 12 | enum IoProxy<Io: Read> { | 13 | enum Compression<Io: Read> { |
| 13 | Store(Io), | 14 | Store(Io), |
| 14 | Deflate(DeflateDecoder<Io>), | 15 | Deflate(DeflateDecoder<Io>), |
| 15 | BZip2(BzDecoder<Io>), | 16 | BZip2(BzDecoder<Io>), |
| 17 | Zstd(ZstdDecoder<'static, BufReader<Io>>), | ||
| 16 | Xz(XzDecoder<Io>), | 18 | Xz(XzDecoder<Io>), |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 19 | pub struct ZipFileReader<'d, Io: Read> { | 21 | pub struct ZipFileReader<'d, Io: Read> { |
| 20 | io: IoProxy<&'d mut Io>, | 22 | io: Compression<IoCursor<&'d mut Io>>, |
| 21 | info: &'d ZipFileInfo, | 23 | info: &'d ZipFileInfo, |
| 22 | |||
| 23 | bounds: (u64, u64), | ||
| 24 | cursor: u64, | ||
| 25 | } | 24 | } |
| 26 | 25 | ||
| 27 | impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { | 26 | impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { |
| @@ -36,8 +35,8 @@ impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { | |||
| 36 | impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | 35 | impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { |
| 37 | pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult<Self> { | 36 | pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult<Self> { |
| 38 | io.seek(SeekFrom::Start(info.header_pointer))?; | 37 | io.seek(SeekFrom::Start(info.header_pointer))?; |
| 39 | let buf = io.read_arr::<30>()?; | ||
| 40 | 38 | ||
| 39 | let buf = io.read_arr::<30>()?; | ||
| 41 | if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { | 40 | if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { |
| 42 | return Err(ZipError::InvalidFileHeaderSignature.into()); | 41 | return Err(ZipError::InvalidFileHeaderSignature.into()); |
| 43 | } | 42 | } |
| @@ -45,18 +44,28 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 45 | + 30 | 44 | + 30 |
| 46 | + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 | 45 | + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 |
| 47 | + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; | 46 | + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; |
| 48 | let mut cursor = io.seek(SeekFrom::Start(data_pointer))?; | ||
| 49 | 47 | ||
| 50 | Ok(Self { | 48 | Ok(Self { |
| 51 | io: match info.compression_method { | 49 | io: match info.compression_method { |
| 52 | CompressionMethod::Store => IoProxy::Store(io), | 50 | CompressionMethod::Store => Compression::Store(IoCursor::new( |
| 53 | CompressionMethod::Deflate => IoProxy::Deflate(DeflateDecoder::new(io)), | 51 | io, |
| 54 | CompressionMethod::BZip2 => IoProxy::BZip2(BzDecoder::new(io)), | 52 | data_pointer, |
| 53 | data_pointer + info.compressed_size, | ||
| 54 | )?), | ||
| 55 | CompressionMethod::Deflate => Compression::Deflate(DeflateDecoder::new( | ||
| 56 | IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, | ||
| 57 | )), | ||
| 58 | CompressionMethod::BZip2 => Compression::BZip2(BzDecoder::new(IoCursor::new( | ||
| 59 | io, | ||
| 60 | data_pointer, | ||
| 61 | data_pointer + info.compressed_size, | ||
| 62 | )?)), | ||
| 55 | CompressionMethod::Lzma => { | 63 | CompressionMethod::Lzma => { |
| 64 | io.seek(SeekFrom::Start(data_pointer))?; | ||
| 56 | let buf = io.read_arr::<9>()?; | 65 | let buf = io.read_arr::<9>()?; |
| 57 | cursor += 9; | 66 | |
| 58 | IoProxy::Xz(XzDecoder::new_stream( | 67 | Compression::Xz(XzDecoder::new_stream( |
| 59 | io, | 68 | IoCursor::new(io, data_pointer + 9, data_pointer + info.compressed_size)?, |
| 60 | Stream::new_raw_decoder( | 69 | Stream::new_raw_decoder( |
| 61 | Filters::new().lzma1( | 70 | Filters::new().lzma1( |
| 62 | LzmaOptions::new() | 71 | LzmaOptions::new() |
| @@ -71,18 +80,30 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 71 | .unwrap(), | 80 | .unwrap(), |
| 72 | )) | 81 | )) |
| 73 | } | 82 | } |
| 74 | CompressionMethod::Xz => IoProxy::Xz(XzDecoder::new(io)), | 83 | CompressionMethod::Zstd => Compression::Zstd( |
| 84 | ZstdDecoder::new(IoCursor::new( | ||
| 85 | io, | ||
| 86 | data_pointer, | ||
| 87 | data_pointer + info.compressed_size, | ||
| 88 | )?) | ||
| 89 | .unwrap(), | ||
| 90 | ), | ||
| 91 | CompressionMethod::Xz => Compression::Xz(XzDecoder::new(IoCursor::new( | ||
| 92 | io, | ||
| 93 | data_pointer, | ||
| 94 | data_pointer + info.compressed_size, | ||
| 95 | )?)), | ||
| 96 | CompressionMethod::Unsupported => { | ||
| 97 | return Err(ZipError::UnsupportedCompressionMethod.into()) | ||
| 98 | } | ||
| 75 | }, | 99 | }, |
| 76 | info, | 100 | info, |
| 77 | |||
| 78 | bounds: (cursor, data_pointer + info.compressed_size), | ||
| 79 | cursor, | ||
| 80 | }) | 101 | }) |
| 81 | } | 102 | } |
| 82 | 103 | ||
| 83 | pub fn seekable(&self) -> bool { | 104 | pub fn seekable(&self) -> bool { |
| 84 | match self.io { | 105 | match self.io { |
| 85 | IoProxy::Store(..) => true, | 106 | Compression::Store(..) => true, |
| 86 | _ => false, | 107 | _ => false, |
| 87 | } | 108 | } |
| 88 | } | 109 | } |
| @@ -90,49 +111,20 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 90 | 111 | ||
| 91 | impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { | 112 | impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { |
| 92 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | 113 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
| 93 | let upper = buf.len().min((self.bounds.1 - self.cursor) as usize); | 114 | match &mut self.io { |
| 94 | let bytes = match &mut self.io { | 115 | Compression::Store(io) => io.read(buf), |
| 95 | IoProxy::Store(io) => io.read(&mut buf[..upper]), | 116 | Compression::Deflate(io) => io.read(buf), |
| 96 | IoProxy::Deflate(io) => io.read(&mut buf[..upper]), | 117 | Compression::BZip2(io) => io.read(buf), |
| 97 | IoProxy::BZip2(io) => io.read(&mut buf[..upper]), | 118 | Compression::Zstd(io) => io.read(buf), |
| 98 | IoProxy::Xz(io) => io.read(&mut buf[..upper]), | 119 | Compression::Xz(io) => io.read(buf), |
| 99 | }?; | 120 | } |
| 100 | self.cursor += upper as u64; | ||
| 101 | Ok(bytes) | ||
| 102 | } | 121 | } |
| 103 | } | 122 | } |
| 104 | 123 | ||
| 105 | impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { | 124 | impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { |
| 106 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | 125 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { |
| 107 | match &mut self.io { | 126 | match &mut self.io { |
| 108 | IoProxy::Store(io) => { | 127 | Compression::Store(io) => io.seek(pos), |
| 109 | self.cursor = match pos { | ||
| 110 | SeekFrom::Start(offset) => self.bounds.0 + offset, | ||
| 111 | SeekFrom::End(offset) => { | ||
| 112 | let cursor = self.bounds.1.saturating_add_signed(offset); | ||
| 113 | if cursor < self.bounds.0 { | ||
| 114 | return Err(IoError::new( | ||
| 115 | IoErrorKind::InvalidInput, | ||
| 116 | ZipError::NegativeFileOffset, | ||
| 117 | )); | ||
| 118 | } | ||
| 119 | cursor | ||
| 120 | } | ||
| 121 | SeekFrom::Current(offset) => { | ||
| 122 | let cursor = self.cursor.saturating_add_signed(offset); | ||
| 123 | if cursor < self.bounds.0 { | ||
| 124 | return Err(IoError::new( | ||
| 125 | IoErrorKind::InvalidInput, | ||
| 126 | ZipError::NegativeFileOffset, | ||
| 127 | )); | ||
| 128 | } | ||
| 129 | cursor | ||
| 130 | } | ||
| 131 | } | ||
| 132 | .min(self.bounds.1); | ||
| 133 | |||
| 134 | Ok(io.seek(SeekFrom::Start(self.cursor))? - self.bounds.0) | ||
| 135 | } | ||
| 136 | _ => Err(IoError::new( | 128 | _ => Err(IoError::new( |
| 137 | IoErrorKind::Unsupported, | 129 | IoErrorKind::Unsupported, |
| 138 | ZipError::CompressedDataIsUnseekable, | 130 | ZipError::CompressedDataIsUnseekable, |
diff --git a/src/zip/file/write.rs b/src/zip/file/write.rs index 6f5756a..d20c378 100644 --- a/src/zip/file/write.rs +++ b/src/zip/file/write.rs | |||
| @@ -5,7 +5,7 @@ use flate2::write::DeflateEncoder; | |||
| 5 | use liblzma::write::XzEncoder; | 5 | use liblzma::write::XzEncoder; |
| 6 | use std::io::Write; | 6 | use std::io::Write; |
| 7 | 7 | ||
| 8 | enum IoProxy<Io: Write> { | 8 | enum Compression<Io: Write> { |
| 9 | Store(Io), | 9 | Store(Io), |
| 10 | Deflate(DeflateEncoder<Io>), | 10 | Deflate(DeflateEncoder<Io>), |
| 11 | BZip2(BzEncoder<Io>), | 11 | BZip2(BzEncoder<Io>), |
| @@ -13,7 +13,7 @@ enum IoProxy<Io: Write> { | |||
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | pub struct ZipFileWriter<'d, Io: Write> { | 15 | pub struct ZipFileWriter<'d, Io: Write> { |
| 16 | io: IoProxy<&'d mut Io>, | 16 | io: Compression<&'d mut Io>, |
| 17 | info: &'d ZipFileInfo, | 17 | info: &'d ZipFileInfo, |
| 18 | 18 | ||
| 19 | bounds: (u64, u64), | 19 | bounds: (u64, u64), |
