Skip to content

Commit 11f35d6

Browse files
committed
std: remove an allocation in Path::with_extension
`Path::with_extension` used to reallocate (and copy) paths twice per call, now it does it once, by checking the size of the previous and new extensions it's possible to call `PathBuf::with_capacity` and pass the exact capacity it takes. Also reduce the memory consumption of the path returned from `Path::with_extension` by using exact capacity instead of using amortized exponential growth.
1 parent 27e10c5 commit 11f35d6

File tree

1 file changed

+21
-3
lines changed

1 file changed

+21
-3
lines changed

library/std/src/path.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -2608,9 +2608,27 @@ impl Path {
26082608
}
26092609

26102610
fn _with_extension(&self, extension: &OsStr) -> PathBuf {
2611-
let mut buf = self.to_path_buf();
2612-
buf.set_extension(extension);
2613-
buf
2611+
let self_len = self.as_os_str().len();
2612+
let self_bytes = self.as_os_str().as_os_str_bytes();
2613+
2614+
let (new_capacity, slice_to_copy) = match self.extension() {
2615+
None => {
2616+
// Enough capacity for the extension and the dot
2617+
let capacity = self_len + extension.len() + 1;
2618+
let whole_path = self_bytes.iter();
2619+
(capacity, whole_path)
2620+
}
2621+
Some(previous_extension) => {
2622+
let capacity = self_len + extension.len() - previous_extension.len();
2623+
let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter();
2624+
(capacity, path_till_dot)
2625+
}
2626+
};
2627+
2628+
let mut new_path = PathBuf::with_capacity(new_capacity);
2629+
new_path.as_mut_vec().extend(slice_to_copy);
2630+
new_path.set_extension(extension);
2631+
new_path
26142632
}
26152633

26162634
/// Produces an iterator over the [`Component`]s of the path.

0 commit comments

Comments
 (0)