1use std::io::{self, Read, Seek, SeekFrom};
8
9use crate::format::parser::ArchiveHeader;
10use crate::format::streams::Folder;
11use crate::read::Entry;
12use crate::{Error, READ_BUFFER_SIZE, Result};
13
14#[cfg(feature = "aes")]
15use crate::Password;
16
17use super::config::StreamingConfig;
18
19pub struct EntryIterator<'a, R: Read + Seek> {
41 header: &'a ArchiveHeader,
43 entries: &'a [Entry],
45 source: &'a mut R,
47 #[cfg(feature = "aes")]
49 #[allow(dead_code)] password: &'a Password,
51 config: StreamingConfig,
53 current_index: usize,
55 current_folder: Option<usize>,
57 folder_decoder: Option<Box<dyn Read + Send + 'static>>,
59 stream_position_in_folder: usize,
61 bytes_remaining: u64,
63 pack_start: u64,
65 finished: bool,
67}
68
69impl<'a, R: Read + Seek + Send> EntryIterator<'a, R> {
70 #[cfg(feature = "aes")]
72 pub(crate) fn new(
73 header: &'a ArchiveHeader,
74 entries: &'a [Entry],
75 source: &'a mut R,
76 password: &'a Password,
77 config: StreamingConfig,
78 ) -> Result<Self> {
79 let pack_start = super::calculate_pack_start(header);
80
81 Ok(Self {
82 header,
83 entries,
84 source,
85 password,
86 config,
87 current_index: 0,
88 current_folder: None,
89 folder_decoder: None,
90 stream_position_in_folder: 0,
91 bytes_remaining: 0,
92 pack_start,
93 finished: false,
94 })
95 }
96
97 #[cfg(not(feature = "aes"))]
99 pub(crate) fn new(
100 header: &'a ArchiveHeader,
101 entries: &'a [Entry],
102 source: &'a mut R,
103 config: StreamingConfig,
104 ) -> Result<Self> {
105 let pack_start = super::calculate_pack_start(header);
106
107 Ok(Self {
108 header,
109 entries,
110 source,
111 config,
112 current_index: 0,
113 current_folder: None,
114 folder_decoder: None,
115 stream_position_in_folder: 0,
116 bytes_remaining: 0,
117 pack_start,
118 finished: false,
119 })
120 }
121
122 pub fn len(&self) -> usize {
124 self.entries.len()
125 }
126
127 pub fn is_empty(&self) -> bool {
129 self.entries.is_empty()
130 }
131
132 pub fn remaining(&self) -> usize {
134 self.entries.len().saturating_sub(self.current_index)
135 }
136
137 pub fn config(&self) -> &StreamingConfig {
139 &self.config
140 }
141
142 fn next_internal(&mut self) -> Result<Option<StreamingEntry<'a>>> {
143 if self.finished || self.current_index >= self.entries.len() {
144 return Ok(None);
145 }
146
147 if self.bytes_remaining > 0 {
152 self.skip_remaining()?;
153 }
154
155 let entry = &self.entries[self.current_index];
156 self.current_index += 1;
157
158 if entry.is_directory {
160 return Ok(Some(StreamingEntry::directory(entry)));
161 }
162
163 let folder_index = match entry.folder_index {
165 Some(idx) => idx,
166 None => {
167 return Ok(Some(StreamingEntry::empty(entry)));
169 }
170 };
171
172 let stream_index = entry.stream_index.unwrap_or(0);
173
174 if self.current_folder != Some(folder_index) {
176 self.init_folder_decoder(folder_index)?;
177 self.stream_position_in_folder = 0;
178 }
179
180 while self.stream_position_in_folder < stream_index {
182 let skip_size = self.get_stream_size(folder_index, self.stream_position_in_folder);
183 self.skip_bytes(skip_size)?;
184 self.stream_position_in_folder += 1;
185 }
186
187 let size = self.get_stream_size(folder_index, stream_index);
189 self.bytes_remaining = size;
190 self.stream_position_in_folder = stream_index + 1;
191
192 Ok(Some(StreamingEntry::with_size(entry, size)))
194 }
195
196 fn skip_bytes(&mut self, bytes: u64) -> Result<()> {
197 if let Some(decoder) = &mut self.folder_decoder {
198 io::copy(&mut decoder.take(bytes), &mut io::sink()).map_err(Error::Io)?;
199 }
200 Ok(())
201 }
202
203 fn init_folder_decoder(&mut self, folder_index: usize) -> Result<()> {
204 let folders = match &self.header.unpack_info {
205 Some(ui) => &ui.folders,
206 None => return Err(Error::InvalidFormat("missing unpack info".into())),
207 };
208
209 if folder_index >= folders.len() {
210 return Err(Error::InvalidFormat(format!(
211 "folder index {} out of range",
212 folder_index
213 )));
214 }
215
216 let folder = &folders[folder_index];
217
218 let pack_offset = self.calculate_folder_offset(folder_index)?;
220 self.source
221 .seek(SeekFrom::Start(pack_offset))
222 .map_err(Error::Io)?;
223
224 let decoder = self.build_folder_decoder(folder, folder_index)?;
226
227 self.folder_decoder = Some(decoder);
228 self.current_folder = Some(folder_index);
229
230 Ok(())
231 }
232
233 fn get_stream_size(&self, folder_index: usize, stream_index: usize) -> u64 {
234 let ss = match &self.header.substreams_info {
236 Some(ss) => ss,
237 None => {
238 return self
240 .header
241 .unpack_info
242 .as_ref()
243 .and_then(|ui| ui.folders.get(folder_index))
244 .and_then(|f| f.final_unpack_size())
245 .unwrap_or(0);
246 }
247 };
248
249 let mut offset = 0usize;
251 for (i, &count) in ss.num_unpack_streams_in_folders.iter().enumerate() {
252 if i == folder_index {
253 return ss
254 .unpack_sizes
255 .get(offset + stream_index)
256 .copied()
257 .unwrap_or(0);
258 }
259 offset += count as usize;
260 }
261
262 0
263 }
264
265 fn calculate_folder_offset(&self, folder_index: usize) -> Result<u64> {
266 let pack_info = self
267 .header
268 .pack_info
269 .as_ref()
270 .ok_or_else(|| Error::InvalidFormat("missing pack info".into()))?;
271
272 let mut offset = self.pack_start;
273
274 for i in 0..folder_index {
276 if i < pack_info.pack_sizes.len() {
277 offset += pack_info.pack_sizes[i];
278 }
279 }
280
281 Ok(offset)
282 }
283
284 fn build_folder_decoder(
285 &mut self,
286 folder: &Folder,
287 folder_index: usize,
288 ) -> Result<Box<dyn Read + Send + 'static>> {
289 if folder.coders.is_empty() {
290 return Err(Error::InvalidFormat("folder has no coders".into()));
291 }
292
293 let uncompressed_size = folder.final_unpack_size().unwrap_or(0);
294
295 let pack_size = self
299 .header
300 .pack_info
301 .as_ref()
302 .and_then(|pi| pi.pack_sizes.get(folder_index).copied())
303 .unwrap_or(0);
304
305 let mut packed_data = vec![0u8; pack_size as usize];
307 self.source
308 .read_exact(&mut packed_data)
309 .map_err(Error::Io)?;
310
311 let cursor = std::io::Cursor::new(packed_data);
314 #[cfg(feature = "aes")]
315 let decoder = crate::codec::build_folder_decoder_for(
316 cursor,
317 folder,
318 uncompressed_size,
319 Some(self.password),
320 )?;
321 #[cfg(not(feature = "aes"))]
322 let decoder = crate::codec::build_folder_decoder_for(cursor, folder, uncompressed_size)?;
323
324 Ok(decoder)
325 }
326
327 pub fn read_entry_data(&mut self, buf: &mut [u8]) -> io::Result<usize> {
340 if self.bytes_remaining == 0 {
341 return Ok(0);
342 }
343
344 let decoder = match &mut self.folder_decoder {
345 Some(d) => d,
346 None => return Ok(0),
347 };
348
349 let to_read = buf.len().min(self.bytes_remaining as usize);
350 let n = decoder.read(&mut buf[..to_read])?;
351 self.bytes_remaining -= n as u64;
352
353 Ok(n)
354 }
355
356 pub(crate) fn skip_remaining(&mut self) -> Result<()> {
358 self.skip_bytes(self.bytes_remaining)?;
359 self.bytes_remaining = 0;
360 Ok(())
361 }
362
363 pub fn extract_current_to<W: io::Write>(&mut self, sink: &mut W) -> Result<u64> {
392 let mut total_written = 0u64;
393 let mut buf = [0u8; READ_BUFFER_SIZE];
394
395 loop {
396 let n = self.read_entry_data(&mut buf)?;
397 if n == 0 {
398 break;
399 }
400 sink.write_all(&buf[..n]).map_err(Error::Io)?;
401 total_written += n as u64;
402 }
403
404 Ok(total_written)
405 }
406
407 pub fn extract_current_to_with_progress<W, F>(
418 &mut self,
419 sink: &mut W,
420 mut on_progress: F,
421 ) -> Result<u64>
422 where
423 W: io::Write,
424 F: FnMut(u64, u64),
425 {
426 let total = self.bytes_remaining;
427 let mut total_written = 0u64;
428 let mut buf = [0u8; READ_BUFFER_SIZE];
429
430 loop {
431 let n = self.read_entry_data(&mut buf)?;
432 if n == 0 {
433 break;
434 }
435 sink.write_all(&buf[..n]).map_err(Error::Io)?;
436 total_written += n as u64;
437 on_progress(total_written, total);
438 }
439
440 Ok(total_written)
441 }
442
443 pub fn extract_current_to_vec(&mut self) -> Result<Vec<u8>> {
449 let mut data = Vec::with_capacity(self.bytes_remaining as usize);
450 self.extract_current_to(&mut data)?;
451 Ok(data)
452 }
453
454 pub fn current_entry_remaining(&self) -> u64 {
456 self.bytes_remaining
457 }
458}
459
460impl<'a, R: Read + Seek + Send> Iterator for EntryIterator<'a, R> {
461 type Item = Result<StreamingEntry<'a>>;
462
463 fn next(&mut self) -> Option<Self::Item> {
464 match self.next_internal() {
465 Ok(Some(entry)) => Some(Ok(entry)),
466 Ok(None) => None,
467 Err(e) => {
468 self.finished = true;
469 Some(Err(e))
470 }
471 }
472 }
473
474 fn size_hint(&self) -> (usize, Option<usize>) {
475 let remaining = self.remaining();
476 (remaining, Some(remaining))
477 }
478}
479
480impl<R: Read + Seek + Send> ExactSizeIterator for EntryIterator<'_, R> {}
481
482#[allow(dead_code)] pub struct StreamingEntry<'a> {
488 entry: &'a Entry,
490 size: u64,
492 is_directory: bool,
494 bytes_read: u64,
496 buffer: Vec<u8>,
498}
499
500impl<'a> StreamingEntry<'a> {
501 fn directory(entry: &'a Entry) -> Self {
503 Self {
504 entry,
505 size: 0,
506 is_directory: true,
507 bytes_read: 0,
508 buffer: Vec::new(),
509 }
510 }
511
512 fn empty(entry: &'a Entry) -> Self {
514 Self {
515 entry,
516 size: 0,
517 is_directory: false,
518 bytes_read: 0,
519 buffer: Vec::new(),
520 }
521 }
522
523 fn with_size(entry: &'a Entry, size: u64) -> Self {
525 Self {
526 entry,
527 size,
528 is_directory: false,
529 bytes_read: 0,
530 buffer: Vec::new(),
531 }
532 }
533
534 pub fn entry(&self) -> &Entry {
536 self.entry
537 }
538
539 pub fn is_directory(&self) -> bool {
541 self.is_directory
542 }
543
544 pub fn size(&self) -> u64 {
546 self.size
547 }
548
549 pub fn name(&self) -> &str {
551 self.entry.path.as_str()
552 }
553
554 pub fn bytes_read(&self) -> u64 {
556 self.bytes_read
557 }
558
559 pub fn remaining(&self) -> u64 {
561 self.size.saturating_sub(self.bytes_read)
562 }
563
564 pub fn skip(self) -> Result<()> {
569 Ok(())
571 }
572}
573
574#[cfg(test)]
575mod tests {
576 use super::*;
577
578 #[test]
579 fn test_streaming_config_defaults() {
580 let config = StreamingConfig::default();
581 assert!(config.max_memory_buffer > 0);
582 assert!(config.read_buffer_size > 0);
583 }
584
585 #[test]
586 fn test_streaming_entry_directory() {
587 use crate::ArchivePath;
588
589 let entry = Entry {
590 path: ArchivePath::new("test").unwrap(),
591 is_directory: true,
592 size: 0,
593 crc32: None,
594 crc64: None,
595 modification_time: None,
596 creation_time: None,
597 access_time: None,
598 attributes: None,
599 is_encrypted: false,
600 is_symlink: false,
601 is_anti: false,
602 ownership: None,
603 index: 0,
604 folder_index: None,
605 stream_index: None,
606 };
607
608 let streaming = StreamingEntry::directory(&entry);
609 assert!(streaming.is_directory());
610 assert_eq!(streaming.size(), 0);
611 }
612}