1use std::collections::VecDeque;
14use std::io::{Read, Seek, SeekFrom};
15
16use rayon::prelude::*;
17
18use crate::central_dir;
19use crate::entry::{self, ZipEntry, ZipError};
20
21const BATCH_SIZE: usize = 64;
23
24const LOCAL_HEADER_SIG: u32 = 0x04034b50;
25
26pub struct StreamingZipRead<R: Read + Seek> {
41 source: R,
42 locations: Vec<crate::central_dir::EntryLocation>,
44 next_entry: usize,
45 buf: VecDeque<ZipEntry>,
46 error: bool,
47}
48
49impl<R: Read + Seek> StreamingZipRead<R> {
50 pub fn new(mut source: R) -> Result<Self, ZipError> {
55 let all_locs = central_dir::read_central_directory_from(&mut source)?;
56 let mut locations: Vec<_> = all_locs.into_iter().filter(|l| !l.is_directory).collect();
57 locations.sort_by_key(|l| l.local_header_offset);
58 Ok(Self {
59 source,
60 locations,
61 next_entry: 0,
62 buf: VecDeque::new(),
63 error: false,
64 })
65 }
66
67 pub fn with_filter(mut source: R, needle: &str) -> Result<Self, ZipError> {
72 let all_locs = central_dir::read_central_directory_from(&mut source)?;
73 let mut locations: Vec<_> = all_locs.into_iter()
74 .filter(|l| !l.is_directory && l.name.contains(needle))
75 .collect();
76 locations.sort_by_key(|l| l.local_header_offset);
77 Ok(Self {
78 source,
79 locations,
80 next_entry: 0,
81 buf: VecDeque::new(),
82 error: false,
83 })
84 }
85
86 fn fill_batch(&mut self) -> Result<(), ZipError> {
87 let remaining = self.locations.len() - self.next_entry;
88 if remaining == 0 {
89 return Ok(());
90 }
91
92 let batch_end = self.next_entry + remaining.min(BATCH_SIZE);
93 let batch = &self.locations[self.next_entry..batch_end];
94
95 let mut raw: Vec<(String, Vec<u8>, u16, usize)> = Vec::with_capacity(batch.len());
97 for loc in batch {
98 self.source.seek(SeekFrom::Start(loc.local_header_offset))
99 .map_err(|_| ZipError("seek to local header failed"))?;
100
101 let mut hdr = [0u8; 30];
102 self.source.read_exact(&mut hdr)
103 .map_err(|_| ZipError("read local header failed"))?;
104
105 if u32::from_le_bytes(hdr[0..4].try_into().unwrap()) != LOCAL_HEADER_SIG {
106 return Err(ZipError("invalid local file header signature"));
107 }
108
109 let skip = u16::from_le_bytes([hdr[26], hdr[27]]) as i64
111 + u16::from_le_bytes([hdr[28], hdr[29]]) as i64;
112 self.source.seek(SeekFrom::Current(skip))
113 .map_err(|_| ZipError("seek past local header name/extra failed"))?;
114
115 let mut compressed = vec![0u8; loc.compressed_size as usize];
116 self.source.read_exact(&mut compressed)
117 .map_err(|_| ZipError("read compressed data failed"))?;
118
119 raw.push((loc.name.clone(), compressed, loc.compression_method, loc.uncompressed_size as usize));
120 }
121
122 let results: Vec<Result<ZipEntry, ZipError>> = crate::thread_pool().install(|| {
124 raw.par_iter().map(|(name, compressed, method, expected_size)| {
125 let data = entry::decompress_entry_raw(compressed, *method, *expected_size)?;
126 Ok(ZipEntry { name: name.clone(), data })
127 }).collect()
128 });
129
130 for r in results {
132 self.buf.push_back(r?);
133 }
134 self.next_entry = batch_end;
135 Ok(())
136 }
137}
138
139impl<R: Read + Seek> Iterator for StreamingZipRead<R> {
140 type Item = Result<ZipEntry, ZipError>;
141
142 fn next(&mut self) -> Option<Self::Item> {
143 if self.error {
144 return None;
145 }
146 if let Some(entry) = self.buf.pop_front() {
147 return Some(Ok(entry));
148 }
149 if self.next_entry >= self.locations.len() {
150 return None;
151 }
152 match self.fill_batch() {
153 Err(e) => {
154 self.error = true;
155 Some(Err(e))
156 }
157 Ok(()) => self.buf.pop_front().map(Ok),
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use std::io::{Cursor, Write};
166
167 fn make_jar(items: &[(&str, &[u8])]) -> Vec<u8> {
168 use zip::write::SimpleFileOptions;
169 let mut buf = Vec::new();
170 let mut zw = zip::ZipWriter::new(Cursor::new(&mut buf));
171 let opts = SimpleFileOptions::default()
172 .compression_method(zip::CompressionMethod::Deflated);
173 for (name, data) in items {
174 zw.start_file(*name, opts).unwrap();
175 zw.write_all(data).unwrap();
176 }
177 zw.finish().unwrap();
178 buf
179 }
180
181 #[test]
182 fn streaming_single() {
183 let jar = make_jar(&[("Hello.class", b"cafebabe")]);
184 let entries: Vec<_> = StreamingZipRead::new(Cursor::new(&jar))
185 .unwrap()
186 .collect::<Result<Vec<_>, _>>()
187 .unwrap();
188 assert_eq!(entries.len(), 1);
189 assert_eq!(entries[0].name, "Hello.class");
190 assert_eq!(entries[0].data, b"cafebabe");
191 }
192
193 #[test]
194 fn streaming_multi() {
195 let jar = make_jar(&[("a.txt", b"aaa"), ("b.txt", b"bbb"), ("c.txt", b"ccc")]);
196 let entries: Vec<_> = StreamingZipRead::new(Cursor::new(&jar))
197 .unwrap()
198 .collect::<Result<Vec<_>, _>>()
199 .unwrap();
200 assert_eq!(entries.len(), 3);
201 }
202
203 #[test]
204 fn streaming_skips_directories() {
205 let jar = make_jar(&[("META-INF/", b""), ("META-INF/MANIFEST.MF", b"Manifest-Version: 1.0\n")]);
206 let entries: Vec<_> = StreamingZipRead::new(Cursor::new(&jar))
207 .unwrap()
208 .collect::<Result<Vec<_>, _>>()
209 .unwrap();
210 assert_eq!(entries.len(), 1);
211 assert_eq!(entries[0].name, "META-INF/MANIFEST.MF");
212 }
213
214 #[test]
215 fn streaming_many_batches() {
216 let items: Vec<(String, Vec<u8>)> = (0..200)
217 .map(|i| (format!("Class{i}.class"), format!("data{i}").into_bytes()))
218 .collect();
219 let item_refs: Vec<(&str, &[u8])> = items.iter().map(|(n, d)| (n.as_str(), d.as_slice())).collect();
220 let jar = make_jar(&item_refs);
221
222 let entries: Vec<_> = StreamingZipRead::new(Cursor::new(&jar))
223 .unwrap()
224 .collect::<Result<Vec<_>, _>>()
225 .unwrap();
226 assert_eq!(entries.len(), 200);
227 }
228
229 #[test]
230 fn streaming_matches_parallel() {
231 let items: Vec<(String, Vec<u8>)> = (0..100)
232 .map(|i| (format!("{i}.txt"), format!("content {i}").into_bytes()))
233 .collect();
234 let item_refs: Vec<(&str, &[u8])> = items.iter().map(|(n, d)| (n.as_str(), d.as_slice())).collect();
235 let jar = make_jar(&item_refs);
236
237 let par = crate::parallel::decompress_parallel(&jar).unwrap();
238 let stream: Vec<_> = StreamingZipRead::new(Cursor::new(&jar))
239 .unwrap()
240 .collect::<Result<Vec<_>, _>>()
241 .unwrap();
242
243 let mut par_sorted = par;
245 par_sorted.sort_by(|a, b| a.name.cmp(&b.name));
246 let mut stream_sorted = stream;
247 stream_sorted.sort_by(|a, b| a.name.cmp(&b.name));
248
249 assert_eq!(par_sorted.len(), stream_sorted.len());
250 for (p, s) in par_sorted.iter().zip(stream_sorted.iter()) {
251 assert_eq!(p.name, s.name);
252 assert_eq!(p.data, s.data);
253 }
254 }
255}