mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-04-01 17:35:13 +08:00
Rework path_normalize_for_cd() to be less allocation trigger happy
Lots of resizing, splicing, and full-on allocating going on here.
This commit is contained in:
parent
d14d8d5733
commit
0b5e41b268
@ -276,49 +276,50 @@ fn test_normalize_path() {
|
||||
/// path. The intent here is to allow 'cd' out of a directory which may no longer exist, without
|
||||
/// allowing 'cd' into a directory that may not exist; see #5341.
|
||||
pub fn path_normalize_for_cd(wd: &wstr, path: &wstr) -> WString {
|
||||
use std::collections::VecDeque;
|
||||
|
||||
// Fast paths.
|
||||
const sep: char = '/';
|
||||
const SEP: char = '/';
|
||||
assert!(
|
||||
wd.as_char_slice().first() == Some(&'/') && wd.as_char_slice().last() == Some(&'/'),
|
||||
"Invalid working directory, it must start and end with /"
|
||||
);
|
||||
if path.is_empty() {
|
||||
return wd.to_owned();
|
||||
} else if path.as_char_slice().first() == Some(&sep) {
|
||||
} else if path.as_char_slice().first() == Some(&SEP) {
|
||||
return path.to_owned();
|
||||
} else if path.as_char_slice().first() != Some(&'.') {
|
||||
return wd.to_owned() + path;
|
||||
}
|
||||
|
||||
// Split our strings by the sep.
|
||||
let mut wd_comps = wd.split(sep).collect::<Vec<_>>();
|
||||
let path_comps = path.split(sep).collect::<Vec<_>>();
|
||||
|
||||
// Remove empty segments from wd_comps.
|
||||
// In particular this removes the leading and trailing empties.
|
||||
wd_comps.retain(|comp| !comp.is_empty());
|
||||
let mut wd_comps: VecDeque<_> = wd
|
||||
.split(SEP)
|
||||
// Remove empty segments from wd_comps, especially leading/trailing empties.
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect();
|
||||
let mut path_comps = path.split(SEP).peekable();
|
||||
|
||||
// Erase leading . and .. components from path_comps, popping from wd_comps as we go.
|
||||
let mut erase_count = 0;
|
||||
for comp in &path_comps {
|
||||
let mut erase_it = false;
|
||||
while let Some(comp) = path_comps.peek() {
|
||||
if comp.is_empty() || comp == "." {
|
||||
erase_it = true;
|
||||
} else if comp == ".." && !wd_comps.is_empty() {
|
||||
erase_it = true;
|
||||
wd_comps.pop();
|
||||
}
|
||||
if erase_it {
|
||||
erase_count += 1;
|
||||
path_comps.next();
|
||||
} else if comp == ".." && wd_comps.pop_back().is_some() {
|
||||
path_comps.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Append un-erased elements to wd_comps and join them, then prepend the leading /.
|
||||
wd_comps.extend(path_comps.into_iter().skip(erase_count));
|
||||
|
||||
let mut result = join_strings(&wd_comps, sep);
|
||||
result.insert(0, '/');
|
||||
// Append un-erased elements to wd_comps and join them, prepending with a leading /
|
||||
let mut paths = wd_comps;
|
||||
paths.extend(path_comps);
|
||||
let mut result =
|
||||
WString::with_capacity(paths.iter().fold(0, |sum, s| sum + s.len()) + paths.len() + 1);
|
||||
for p in &paths {
|
||||
result.push(SEP);
|
||||
result.push_utfstr(*p);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user