aboutsummaryrefslogtreecommitdiff
path: root/src/zip/file/read.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/zip/file/read.rs')
-rw-r--r--src/zip/file/read.rs125
1 files changed, 125 insertions, 0 deletions
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}