aboutsummaryrefslogtreecommitdiff
path: root/src/zip/file
diff options
context:
space:
mode:
Diffstat (limited to 'src/zip/file')
-rw-r--r--src/zip/file/info.rs161
-rw-r--r--src/zip/file/mod.rs7
-rw-r--r--src/zip/file/read.rs125
-rw-r--r--src/zip/file/write.rs30
4 files changed, 323 insertions, 0 deletions
diff --git a/src/zip/file/info.rs b/src/zip/file/info.rs
new file mode 100644
index 0000000..2891b59
--- /dev/null
+++ b/src/zip/file/info.rs
@@ -0,0 +1,161 @@
1use crate::driver::ArchiveFileInfo;
2use crate::zip::{ZipError, ZipResult};
3use chrono::{DateTime, Local};
4
5#[derive(Debug, Clone)]
6pub enum CompressionMethod {
7 Store,
8 Deflate,
9 BZip2,
10 LZMA,
11 XZ,
12}
13
14impl CompressionMethod {
15 pub(crate) fn from_struct_id(id: u16) -> ZipResult<Self> {
16 match id {
17 0 => Ok(Self::Store),
18 8 => Ok(Self::Deflate),
19 12 => Ok(Self::BZip2),
20 14 => Ok(Self::LZMA),
21 95 => Ok(Self::XZ),
22 1..=7 | 9..=11 | 13 | 15..=20 | 93..=94 | 96..=99 => {
23 Err(ZipError::UnsupportedCompressionMethod.into())
24 }
25 21..=92 | 100.. => Err(ZipError::InvalidCompressionMethod.into()),
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
31pub struct BitFlag {
32 flag: u16,
33}
34
35pub mod bit {
36 #[derive(Debug, PartialEq, Eq)]
37 pub enum DeflateMode {
38 Normal,
39 Maximum,
40 Fast,
41 SuperFast,
42 }
43}
44
45macro_rules! get_set_bit_flag {
46 {$($get:ident $set:ident $bit:expr)+} => {
47 $(
48 pub fn $get(&self) -> bool {
49 self.get_bit($bit)
50 }
51
52 pub fn $set(&mut self, enable: bool) {
53 self.set_bit($bit, enable);
54 }
55 )*
56 };
57}
58
59impl BitFlag {
60 pub fn new(flag: u16) -> Self {
61 Self { flag }
62 }
63
64 #[inline]
65 fn get_bit(&self, bit: u32) -> bool {
66 (self.flag & 2u16.pow(bit)) > 0
67 }
68
69 #[inline]
70 fn set_bit(&mut self, bit: u32, enable: bool) {
71 if enable {
72 self.flag |= 2u16.pow(bit);
73 } else {
74 self.flag &= !2u16.pow(bit);
75 }
76 }
77
78 pub fn deflate_mode(&self) -> bit::DeflateMode {
79 match self.flag & 6 {
80 0 => bit::DeflateMode::Normal,
81 2 => bit::DeflateMode::Maximum,
82 4 => bit::DeflateMode::Fast,
83 6 => bit::DeflateMode::SuperFast,
84 _ => panic!("impossible"),
85 }
86 }
87
88 pub fn set_deflate_mode(&mut self, mode: bit::DeflateMode) {
89 match mode {
90 bit::DeflateMode::Normal => {
91 self.set_bit(1, false);
92 self.set_bit(2, false);
93 }
94 bit::DeflateMode::Maximum => {
95 self.set_bit(1, true);
96 self.set_bit(2, false);
97 }
98 bit::DeflateMode::Fast => {
99 self.set_bit(1, false);
100 self.set_bit(2, true);
101 }
102 bit::DeflateMode::SuperFast => {
103 self.set_bit(1, true);
104 self.set_bit(2, true);
105 }
106 }
107 }
108
109 get_set_bit_flag! {
110 is_encrypted set_encrypted 0
111 is_imploding_8k set_imploding_8k 1
112 is_imploding_3sf_trees set_imploding_3sf_trees 2
113 is_lzma_has_eos_marker set_lzma_has_eos_marker 1
114 is_has_data_descriptor set_has_data_descriptor 3
115 is_patched_data set_patched_data 5
116 is_strong_encryption set_strong_encryption 6
117 is_utf8 set_utf8 11
118 is_cd_encryption set_cd_encryption 13
119 }
120}
121
122#[derive(Debug, Clone)]
123pub struct ZipFileInfo {
124 pub compression_method: CompressionMethod,
125 pub bit_flag: BitFlag,
126 pub datetime: DateTime<Local>,
127 pub crc: u32,
128 pub compressed_size: u64,
129 pub size: u64,
130 pub header_pointer: u64,
131 pub name: String,
132 pub comment: String,
133}
134
135impl ZipFileInfo {
136 pub fn new(
137 compression_method: CompressionMethod,
138 bit_flag: BitFlag,
139 datetime: DateTime<Local>,
140 crc: u32,
141 compressed_size: u64,
142 size: u64,
143 header_pointer: u64,
144 name: String,
145 comment: String,
146 ) -> Self {
147 Self {
148 compression_method,
149 bit_flag,
150 datetime,
151 crc,
152 compressed_size,
153 size,
154 header_pointer,
155 name,
156 comment,
157 }
158 }
159}
160
161impl ArchiveFileInfo for ZipFileInfo {}
diff --git a/src/zip/file/mod.rs b/src/zip/file/mod.rs
new file mode 100644
index 0000000..43ccc04
--- /dev/null
+++ b/src/zip/file/mod.rs
@@ -0,0 +1,7 @@
1mod info;
2mod read;
3mod write;
4
5pub use info::{bit, BitFlag, CompressionMethod, ZipFileInfo};
6pub use read::ZipFileReader;
7pub use write::ZipFileWriter;
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs
new file mode 100644
index 0000000..56f42da
--- /dev/null
+++ b/src/zip/file/read.rs
@@ -0,0 +1,125 @@
1use crate::driver::FileDriver;
2use crate::zip::{CompressionMethod, ZipError, ZipFileInfo, ZipResult};
3use bzip2::read::BzDecoder;
4use flate2::read::DeflateDecoder;
5use std::io::{
6 Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom,
7};
8use xz2::read::XzDecoder;
9
10enum IoProxy<Io: Read> {
11 Store(Io),
12 Deflate(DeflateDecoder<Io>),
13 BZip2(BzDecoder<Io>),
14 XZ(XzDecoder<Io>),
15}
16
17pub struct ZipFileReader<'d, Io: Read> {
18 io: IoProxy<&'d mut Io>,
19 info: &'d ZipFileInfo,
20
21 bounds: (u64, u64),
22 cursor: u64,
23}
24
25impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> {
26 type Io = Io;
27 type FileInfo = ZipFileInfo;
28
29 fn info(&self) -> &Self::FileInfo {
30 self.info
31 }
32}
33
34impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> {
35 pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult<Self> {
36 io.seek(SeekFrom::Start(info.header_pointer))?;
37 let buf = {
38 let mut buf = [0; 30];
39 io.read(&mut buf)?;
40 buf
41 };
42 if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 {
43 return Err(ZipError::InvalidFileHeaderSignature.into());
44 }
45 let data_pointer = info.header_pointer
46 + 30
47 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64
48 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64;
49 io.seek(SeekFrom::Start(data_pointer))?;
50
51 Ok(Self {
52 io: match info.compression_method {
53 CompressionMethod::Store => IoProxy::Store(io),
54 CompressionMethod::Deflate => IoProxy::Deflate(DeflateDecoder::new(io)),
55 CompressionMethod::BZip2 => IoProxy::BZip2(BzDecoder::new(io)),
56 CompressionMethod::LZMA => IoProxy::XZ(XzDecoder::new(io)),
57 CompressionMethod::XZ => IoProxy::XZ(XzDecoder::new(io)),
58 },
59 info,
60
61 bounds: (data_pointer, data_pointer + info.compressed_size),
62 cursor: data_pointer,
63 })
64 }
65
66 pub fn seekable(&self) -> bool {
67 match self.io {
68 IoProxy::Store(..) => true,
69 _ => false,
70 }
71 }
72}
73
74impl<'d, Io: Read> Read for ZipFileReader<'d, Io> {
75 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
76 let upper = buf.len().min((self.bounds.1 - self.cursor) as usize);
77 let bytes = match &mut self.io {
78 IoProxy::Store(io) => io.read(&mut buf[..upper]),
79 IoProxy::Deflate(io) => io.read(&mut buf[..upper]),
80 IoProxy::BZip2(io) => io.read(&mut buf[..upper]),
81 IoProxy::XZ(io) => io.read(&mut buf[..upper]),
82 }?;
83 self.cursor += upper as u64;
84 Ok(bytes)
85 }
86}
87
88impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> {
89 fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
90 match &mut self.io {
91 IoProxy::Store(io) => {
92 self.cursor = match pos {
93 SeekFrom::Start(offset) => self.bounds.0 + offset,
94 SeekFrom::End(offset) => {
95 let cursor = self.bounds.1.saturating_add_signed(offset);
96 if cursor < self.bounds.0 {
97 return Err(IoError::new(
98 IoErrorKind::InvalidInput,
99 ZipError::NegativeFileOffset,
100 ));
101 }
102 cursor
103 }
104 SeekFrom::Current(offset) => {
105 let cursor = self.cursor.saturating_add_signed(offset);
106 if cursor < self.bounds.0 {
107 return Err(IoError::new(
108 IoErrorKind::InvalidInput,
109 ZipError::NegativeFileOffset,
110 ));
111 }
112 cursor
113 }
114 }
115 .min(self.bounds.1);
116
117 Ok(io.seek(SeekFrom::Start(self.cursor))? - self.bounds.0)
118 }
119 _ => Err(IoError::new(
120 IoErrorKind::Unsupported,
121 ZipError::CompressionDataIsUnseekable,
122 )),
123 }
124 }
125}
diff --git a/src/zip/file/write.rs b/src/zip/file/write.rs
new file mode 100644
index 0000000..627db6d
--- /dev/null
+++ b/src/zip/file/write.rs
@@ -0,0 +1,30 @@
1use crate::driver::FileDriver;
2use crate::zip::ZipFileInfo;
3use bzip2::write::BzEncoder;
4use flate2::write::DeflateEncoder;
5use std::io::Write;
6use xz2::write::XzEncoder;
7
8enum IoProxy<Io: Write> {
9 Store(Io),
10 Deflate(DeflateEncoder<Io>),
11 BZip2(BzEncoder<Io>),
12 XZ(XzEncoder<Io>),
13}
14
15pub struct ZipFileWriter<'d, Io: Write> {
16 io: IoProxy<&'d mut Io>,
17 info: &'d ZipFileInfo,
18
19 bounds: (u64, u64),
20 cursor: u64,
21}
22
23impl<'d, Io: Write> FileDriver for ZipFileWriter<'d, Io> {
24 type Io = Io;
25 type FileInfo = ZipFileInfo;
26
27 fn info(&self) -> &Self::FileInfo {
28 self.info
29 }
30}