From dbf39339decb9b36560227abe7c1f632f1f9a318 Mon Sep 17 00:00:00 2001 From: Chris Hennick Date: Sun, 14 May 2023 09:20:15 -0700 Subject: [PATCH] Wrap extra data in Rc so FileOptions and ZipFileData can be cloned faster --- src/read.rs | 15 ++++++------- src/types.rs | 9 ++++---- src/write.rs | 59 ++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/read.rs b/src/read.rs index 1c2faca2..051b11b6 100644 --- a/src/read.rs +++ b/src/read.rs @@ -14,6 +14,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::io::{self, prelude::*}; use std::path::Path; +use std::rc::Rc; use std::sync::Arc; #[cfg(any( @@ -358,13 +359,13 @@ impl ZipArchive { } if footer64.number_of_files_on_this_disk > footer64.number_of_files { return Err(ZipError::InvalidArchive( - "ZIP64 footer indicates more files on this disk than in the whole archive" + "ZIP64 footer indicates more files on this disk than in the whole archive", )); } if footer64.version_needed_to_extract > footer64.version_made_by { return Err(ZipError::InvalidArchive( "ZIP64 footer indicates a new version is needed to extract this archive than the \ - version that wrote it" + version that wrote it", )); } @@ -737,8 +738,8 @@ fn central_header_to_zip_file_inner( uncompressed_size: uncompressed_size as u64, file_name, file_name_raw, - extra_field, - central_extra_field: vec![], + extra_field: Rc::new(extra_field), + central_extra_field: Rc::new(vec![]), file_comment, header_start: offset, central_header_start, @@ -770,7 +771,7 @@ fn central_header_to_zip_file_inner( } fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> { - let mut reader = io::Cursor::new(&file.extra_field); + let mut reader = io::Cursor::new(file.extra_field.as_ref()); while (reader.position() as usize) < file.extra_field.len() { let kind = reader.read_u16::()?; @@ -1098,8 +1099,8 @@ pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult, /// Extra field usually used for storage expansion - pub extra_field: Vec, + pub extra_field: Rc>, /// Extra field only written to central directory - pub central_extra_field: Vec, + pub central_extra_field: Rc>, /// File comment pub file_comment: String, /// Specifies where the local header of the file starts @@ -530,8 +531,8 @@ mod test { uncompressed_size: 0, file_name: file_name.clone(), file_name_raw: file_name.into_bytes(), - extra_field: Vec::new(), - central_extra_field: vec![], + extra_field: Rc::new(vec![]), + central_extra_field: Rc::new(vec![]), file_comment: String::new(), header_start: 0, data_start: AtomicU64::new(0), diff --git a/src/write.rs b/src/write.rs index 98629dea..27b70150 100644 --- a/src/write.rs +++ b/src/write.rs @@ -14,6 +14,7 @@ use std::io; use std::io::prelude::*; use std::io::{BufReader, SeekFrom}; use std::mem; +use std::rc::Rc; #[cfg(any( feature = "deflate", @@ -133,8 +134,8 @@ pub struct FileOptions { pub(crate) permissions: Option, pub(crate) large_file: bool, encrypt_with: Option, - extra_data: Vec, - central_extra_data: Vec, + extra_data: Rc>, + central_extra_data: Rc>, alignment: u16, } @@ -251,9 +252,17 @@ impl FileOptions { } else { &mut self.extra_data }; - field.write_u16::(header_id)?; - field.write_u16::(data.len() as u16)?; - field.write_all(data)?; + let vec = Rc::get_mut(field); + let vec = match vec { + Some(exclusive) => exclusive, + None => { + *field = Rc::new(field.to_vec()); + Rc::get_mut(field).unwrap() + } + }; + vec.write_u16::(header_id)?; + vec.write_u16::(data.len() as u16)?; + vec.write_all(data)?; Ok(()) } } @@ -261,8 +270,12 @@ impl FileOptions { /// Removes the extra data fields. #[must_use] pub fn clear_extra_data(mut self) -> FileOptions { - self.extra_data.clear(); - self.central_extra_data.clear(); + if self.extra_data.len() > 0 { + self.extra_data = Rc::new(vec![]); + } + if self.central_extra_data.len() > 0 { + self.central_extra_data = Rc::new(vec![]); + } self } } @@ -291,8 +304,8 @@ impl Default for FileOptions { permissions: None, large_file: false, encrypt_with: None, - extra_data: Vec::with_capacity(u16::MAX as usize), - central_extra_data: Vec::with_capacity(u16::MAX as usize), + extra_data: Rc::new(vec![]), + central_extra_data: Rc::new(vec![]), alignment: 1, } } @@ -1384,6 +1397,7 @@ mod test { use crate::ZipArchive; use std::io; use std::io::{Read, Write}; + use std::rc::Rc; #[test] fn write_empty_zip() { @@ -1503,8 +1517,8 @@ mod test { permissions: Some(33188), large_file: false, encrypt_with: None, - extra_data: vec![], - central_extra_data: vec![], + extra_data: Rc::new(vec![]), + central_extra_data: Rc::new(vec![]), alignment: 1, }; writer.start_file("mimetype", options).unwrap(); @@ -1543,8 +1557,8 @@ mod test { permissions: Some(33188), large_file: false, encrypt_with: None, - extra_data: vec![], - central_extra_data: vec![], + extra_data: Rc::new(vec![]), + central_extra_data: Rc::new(vec![]), alignment: 0, }; writer.start_file(RT_TEST_FILENAME, options).unwrap(); @@ -1593,8 +1607,8 @@ mod test { permissions: Some(33188), large_file: false, encrypt_with: None, - extra_data: vec![], - central_extra_data: vec![], + extra_data: Rc::new(vec![]), + central_extra_data: Rc::new(vec![]), alignment: 0, }; writer.start_file(RT_TEST_FILENAME, options).unwrap(); @@ -1705,12 +1719,21 @@ mod test { #[test] fn test_filename_looks_like_zip64_locator_4() { let mut writer = ZipWriter::new(io::Cursor::new(Vec::new())); - writer.start_file("PK\u{6}\u{6}", FileOptions::default()).unwrap(); - writer.start_file("\0\0\0\0\0\0", FileOptions::default()).unwrap(); + writer + .start_file("PK\u{6}\u{6}", FileOptions::default()) + .unwrap(); + writer + .start_file("\0\0\0\0\0\0", FileOptions::default()) + .unwrap(); writer.start_file("\0", FileOptions::default()).unwrap(); writer.start_file("", FileOptions::default()).unwrap(); writer.start_file("\0\0", FileOptions::default()).unwrap(); - writer.start_file("\0\0\0PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", FileOptions::default()).unwrap(); + writer + .start_file( + "\0\0\0PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", + FileOptions::default(), + ) + .unwrap(); let zip = writer.finish().unwrap(); println!("{:02x?}", zip.get_ref()); let _ = ZipArchive::new(zip).unwrap();