Skip to content

Commit

Permalink
fix: avoid scanning through all local file headers while opening an a…
Browse files Browse the repository at this point in the history
…rchive

Fixes #280
  • Loading branch information
jrudolph committed Jan 17, 2025
1 parent 7c20fa3 commit 41c262f
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 17 deletions.
17 changes: 3 additions & 14 deletions src/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,7 @@ pub(crate) fn find_content<'a>(
reader: &'a mut (impl Read + Seek),
) -> ZipResult<io::Take<&'a mut dyn Read>> {
// TODO: use .get_or_try_init() once stabilized to provide a closure returning a Result!
let data_start = match data.data_start.get() {
Some(data_start) => *data_start,
None => find_data_start(data, reader)?,
};
let data_start = data.data_start(reader)?;

reader.seek(SeekFrom::Start(data_start))?;
Ok((reader as &mut dyn Read).take(data.compressed_size))
Expand All @@ -337,14 +334,14 @@ fn find_content_seek<'a, R: Read + Seek>(
reader: &'a mut R,
) -> ZipResult<SeekableTake<'a, R>> {
// Parse local header
let data_start = find_data_start(data, reader)?;
let data_start = data.data_start(reader)?;
reader.seek(SeekFrom::Start(data_start))?;

// Explicit Ok and ? are needed to convert io::Error to ZipError
Ok(SeekableTake::new(reader, data.compressed_size)?)
}

fn find_data_start(
pub(crate) fn find_data_start(
data: &ZipFileData,
reader: &mut (impl Read + Seek + Sized),
) -> Result<u64, ZipError> {
Expand Down Expand Up @@ -1068,14 +1065,6 @@ pub(crate) fn central_header_to_zip_file<R: Read + Seek>(
));
}

let data_start = find_data_start(&file, reader)?;

if data_start > central_directory.directory_start {
return Err(InvalidArchive(
"File data can't start after the central directory",
));
}

reader.seek(SeekFrom::Start(central_header_end))?;
Ok(file)
}
Expand Down
9 changes: 7 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ pub(crate) mod ffi {
}

use crate::extra_fields::ExtraField;
use crate::read::find_data_start;
use crate::result::DateTimeRangeError;
use crate::spec::is_dir;
use crate::types::ffi::S_IFDIR;
use crate::{CompressionMethod, ZIP64_BYTES_THR};
use std::io::{Read, Seek};
#[cfg(feature = "time")]
use time::{error::ComponentRange, Date, Month, OffsetDateTime, PrimitiveDateTime, Time};

Expand Down Expand Up @@ -488,8 +490,11 @@ pub struct ZipFileData {

impl ZipFileData {
/// Get the starting offset of the data of the compressed file
pub fn data_start(&self) -> u64 {
*self.data_start.get().unwrap()
pub fn data_start(&self, reader: &mut (impl Read + Seek + Sized)) -> ZipResult<u64> {
match self.data_start.get() {
Some(data_start) => Ok(*data_start),
None => Ok(find_data_start(self, reader)?),
}
}

#[allow(dead_code)]
Expand Down
2 changes: 1 addition & 1 deletion src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ impl<A: Read + Write + Seek> ZipWriter<A> {
let write_position = self.inner.get_plain().stream_position()?;
let src_index = self.index_by_name(src_name)?;
let src_data = &mut self.files[src_index];
let src_data_start = src_data.data_start();
let src_data_start = src_data.data_start(self.inner.get_plain())?;
debug_assert!(src_data_start <= write_position);
let mut compressed_size = src_data.compressed_size;
if compressed_size > (write_position - src_data_start) {
Expand Down

0 comments on commit 41c262f

Please sign in to comment.