Skip to content

Commit 366282c

Browse files
make PathSplitError avoid consing a String until necessary
1 parent 9bbbc38 commit 366282c

File tree

2 files changed

+40
-32
lines changed

2 files changed

+40
-32
lines changed

src/read/pipelining.rs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,27 @@ pub mod path_splitting {
1010

1111
/// Errors encountered during path splitting.
1212
#[derive(Debug, Display, Error)]
13-
pub enum PathSplitError {
14-
/// entry path format error: {0:?}
15-
PathFormat(String),
16-
/// duplicate path used for file and directory: {0:?}
17-
DuplicateFileDirPath(String),
13+
pub enum PathSplitError<'a> {
14+
/// entry path {0:?} would escape extraction dir: {0:?}
15+
ExtractionPathEscapesDirectory(&'a str, &'static str),
16+
/// duplicate entry path {0:?} used: {0:?}
17+
DuplicatePath(&'a str, &'static str),
1818
}
1919

2020
/* NB: path_to_string() performs some of this logic, but is intended to coerce filesystems
2121
* paths into zip entry names, whereas we are processing entry names from a zip archive, so we
2222
* perform much less error handling. */
2323
pub(crate) fn normalize_parent_dirs<'a>(
2424
entry_path: &'a str,
25-
) -> Result<(Vec<&'a str>, bool), PathSplitError> {
25+
) -> Result<(Vec<&'a str>, bool), PathSplitError<'a>> {
2626
/* The ZIP spec states (APPNOTE 4.4.17) that file paths are in Unix format, and Unix
2727
* filesystems treat a backslash as a normal character. Thus they should be allowed on Unix
2828
* and replaced with \u{fffd} on Windows. */
2929
if entry_path.starts_with('/') {
30-
return Err(PathSplitError::PathFormat(format!(
31-
"path {:?} began with '/' and is absolute",
32-
entry_path
33-
)));
30+
return Err(PathSplitError::ExtractionPathEscapesDirectory(
31+
entry_path,
32+
"path began with '/' and is absolute",
33+
));
3434
}
3535
let is_dir = is_dir(entry_path);
3636

@@ -45,10 +45,10 @@ pub mod path_splitting {
4545
/* If ".." is present, pop off the last element or return an error. */
4646
".." => {
4747
if ret.pop().is_none() {
48-
return Err(PathSplitError::PathFormat(format!(
49-
"path {:?} has too many '..' components and would escape the containing dir",
50-
entry_path
51-
)));
48+
return Err(PathSplitError::ExtractionPathEscapesDirectory(
49+
entry_path,
50+
"path has too many '..' components and would escape the containing dir",
51+
));
5252
}
5353
}
5454
_ => {
@@ -57,10 +57,10 @@ pub mod path_splitting {
5757
}
5858
}
5959
if ret.is_empty() {
60-
return Err(PathSplitError::PathFormat(format!(
61-
"path {:?} resolves to the top-level directory",
62-
entry_path
63-
)));
60+
return Err(PathSplitError::ExtractionPathEscapesDirectory(
61+
entry_path,
62+
"path resolves to the top-level directory",
63+
));
6464
}
6565

6666
Ok((ret, is_dir))
@@ -113,7 +113,7 @@ pub mod path_splitting {
113113
* any other data for the top-level extraction directory. */
114114
pub(crate) fn lexicographic_entry_trie<'a, Data>(
115115
all_entries: impl IntoIterator<Item = (&'a str, Data)>,
116-
) -> Result<BTreeMap<&'a str, Box<FSEntry<'a, Data>>>, PathSplitError>
116+
) -> Result<BTreeMap<&'a str, Box<FSEntry<'a, Data>>>, PathSplitError<'a>>
117117
where
118118
Data: DirByMode,
119119
{
@@ -139,10 +139,10 @@ pub mod path_splitting {
139139
.or_insert_with(|| Box::new(FSEntry::Dir(DirEntry::default())));
140140
cur_dir = match next_subdir.as_mut() {
141141
FSEntry::File(_) => {
142-
return Err(PathSplitError::DuplicateFileDirPath(format!(
143-
"a file was already registered at the same path as the dir entry {:?}",
144-
entry_path
145-
)));
142+
return Err(PathSplitError::DuplicatePath(
143+
entry_path,
144+
"a file was already registered at the same path as this dir entry",
145+
));
146146
}
147147
FSEntry::Dir(ref mut subdir) => subdir,
148148
}
@@ -152,10 +152,10 @@ pub mod path_splitting {
152152
/* We can't handle duplicate file paths, as that might mess up our
153153
* parallelization strategy. */
154154
if cur_dir.children.contains_key(filename) {
155-
return Err(PathSplitError::DuplicateFileDirPath(format!(
156-
"another file or directory was already registered at the same path as the file entry {:?}",
157-
entry_path
158-
)));
155+
return Err(PathSplitError::DuplicatePath(
156+
entry_path,
157+
"an entry was already registered at the same path as this file entry",
158+
));
159159
}
160160
cur_dir
161161
.children
@@ -166,10 +166,10 @@ pub mod path_splitting {
166166
* path, as it's not clear how to merge the possibility of two separate file
167167
* permissions. */
168168
if cur_dir.properties.replace(data).is_some() {
169-
return Err(PathSplitError::DuplicateFileDirPath(format!(
170-
"another directory was already registered at the path {:?}",
171-
entry_path
172-
)));
169+
return Err(PathSplitError::DuplicatePath(
170+
entry_path,
171+
"another directory was already registered at this path",
172+
));
173173
}
174174
}
175175
}
@@ -640,11 +640,18 @@ pub mod split_extraction {
640640
/// zip error: {0}
641641
Zip(#[from] ZipError),
642642
/// path split error: {0}
643-
PathSplit(#[from] PathSplitError),
643+
PathSplit(String),
644644
/// handle creation error: {0}
645645
HandleCreation(#[from] HandleCreationError),
646646
}
647647

648+
impl<'a> From<PathSplitError<'a>> for SplitExtractionError {
649+
fn from(e: PathSplitError<'a>) -> Self {
650+
let msg = format!("{}", e);
651+
Self::PathSplit(msg)
652+
}
653+
}
654+
648655
/* TODO: make this share code with find_data_start()! */
649656
fn get_or_find_data_start<InF>(data: &ZipFileData, input_file: InF) -> Result<u64, ZipError>
650657
where

src/spec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ pub(crate) trait FixedSizeBlock: Pod {
173173
#[allow(clippy::wrong_self_convention)]
174174
fn from_le(self) -> Self;
175175

176+
#[allow(dead_code)]
176177
fn interpret(input_block: &[u8]) -> ZipResult<Self> {
177178
let mut block = Self::zeroed();
178179
block.as_bytes_mut().copy_from_slice(input_block);

0 commit comments

Comments
 (0)