Wrap extra data in Rc so FileOptions and ZipFileData can be cloned faster

This commit is contained in:
Chris Hennick 2023-05-14 09:20:15 -07:00
parent bb49f1c414
commit dbf39339de
No known key found for this signature in database
GPG key ID: 25653935CC8B6C74
3 changed files with 54 additions and 29 deletions

View file

@ -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<R: Read + Seek> ZipArchive<R> {
}
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<R: Read>(
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<R: Read>(
}
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::<LittleEndian>()?;
@ -1098,8 +1099,8 @@ pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult<Opt
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: String::new(), // file comment is only available in the central directory
// header_start and data start are not available, but also don't matter, since seeking is
// not available.

View file

@ -1,6 +1,7 @@
//! Types that specify what is contained in a ZIP.
use path::{Component, Path, PathBuf};
use std::path;
use std::rc::Rc;
#[cfg(not(any(
all(target_arch = "arm", target_pointer_width = "32"),
@ -361,9 +362,9 @@ pub struct ZipFileData {
/// Raw file name. To be used when file_name was incorrectly decoded.
pub file_name_raw: Vec<u8>,
/// Extra field usually used for storage expansion
pub extra_field: Vec<u8>,
pub extra_field: Rc<Vec<u8>>,
/// Extra field only written to central directory
pub central_extra_field: Vec<u8>,
pub central_extra_field: Rc<Vec<u8>>,
/// 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),

View file

@ -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<u32>,
pub(crate) large_file: bool,
encrypt_with: Option<ZipCryptoKeys>,
extra_data: Vec<u8>,
central_extra_data: Vec<u8>,
extra_data: Rc<Vec<u8>>,
central_extra_data: Rc<Vec<u8>>,
alignment: u16,
}
@ -251,9 +252,17 @@ impl FileOptions {
} else {
&mut self.extra_data
};
field.write_u16::<LittleEndian>(header_id)?;
field.write_u16::<LittleEndian>(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::<LittleEndian>(header_id)?;
vec.write_u16::<LittleEndian>(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();