diff --git a/library/std/benches/path.rs b/library/std/benches/path.rs index 094c00894a8ee..912c783b31e4c 100644 --- a/library/std/benches/path.rs +++ b/library/std/benches/path.rs @@ -55,6 +55,30 @@ fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { }); } +#[bench] +fn bench_path_components_iter(b: &mut test::Bencher) { + let p = Path::new("/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"); + + b.iter(|| { + for c in black_box(p).components() { + black_box(c); + } + }) +} + +#[bench] +fn bench_path_file_name(b: &mut test::Bencher) { + let p1 = Path::new("foo.bar"); + let p2 = Path::new("foo/bar"); + let p3 = Path::new("/bar"); + + b.iter(|| { + black_box(black_box(p1).file_name()); + black_box(black_box(p2).file_name()); + black_box(black_box(p3).file_name()); + }) +} + #[bench] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_path_hashset(b: &mut test::Bencher) { diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 6e3b1e6e47d0e..8c19f50e787a5 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -79,7 +79,7 @@ use crate::ops::{self, Deref}; use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; -use crate::sys::path::{MAIN_SEP_STR, is_sep_byte, is_verbatim_sep, parse_prefix}; +use crate::sys::path::{HAS_PREFIXES, MAIN_SEP_STR, is_sep_byte, is_verbatim_sep, parse_prefix}; use crate::{cmp, fmt, fs, io, sys}; //////////////////////////////////////////////////////////////////////////////// @@ -643,17 +643,26 @@ impl<'a> Components<'a> { // how long is the prefix, if any? #[inline] fn prefix_len(&self) -> usize { + if !HAS_PREFIXES { + return 0; + } self.prefix.as_ref().map(Prefix::len).unwrap_or(0) } #[inline] fn prefix_verbatim(&self) -> bool { + if !HAS_PREFIXES { + return false; + } self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false) } /// how much of the prefix is left from the point of view of iteration? #[inline] fn prefix_remaining(&self) -> usize { + if !HAS_PREFIXES { + return 0; + } if self.front == State::Prefix { self.prefix_len() } else { 0 } } @@ -707,7 +716,7 @@ impl<'a> Components<'a> { if self.has_physical_root { return true; } - if let Some(p) = self.prefix { + if HAS_PREFIXES && let Some(p) = self.prefix { if p.has_implicit_root() { return true; } @@ -720,10 +729,10 @@ impl<'a> Components<'a> { if self.has_root() { return false; } - let mut iter = self.path[self.prefix_remaining()..].iter(); - match (iter.next(), iter.next()) { - (Some(&b'.'), None) => true, - (Some(&b'.'), Some(&b)) => self.is_sep_byte(b), + let slice = &self.path[self.prefix_remaining()..]; + match slice { + [b'.'] => true, + [b'.', b, ..] => self.is_sep_byte(*b), _ => false, } } @@ -732,7 +741,7 @@ impl<'a> Components<'a> { // corresponding path component unsafe fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option> { match comp { - b"." if self.prefix_verbatim() => Some(Component::CurDir), + b"." if HAS_PREFIXES && self.prefix_verbatim() => Some(Component::CurDir), b"." => None, // . components are normalized away, except at // the beginning of a path, which is treated // separately via `include_cur_dir` @@ -889,18 +898,16 @@ impl<'a> Iterator for Components<'a> { fn next(&mut self) -> Option> { while !self.finished() { match self.front { - State::Prefix if self.prefix_len() > 0 => { - self.front = State::StartDir; - debug_assert!(self.prefix_len() <= self.path.len()); - let raw = &self.path[..self.prefix_len()]; - self.path = &self.path[self.prefix_len()..]; - return Some(Component::Prefix(PrefixComponent { - raw: unsafe { OsStr::from_encoded_bytes_unchecked(raw) }, - parsed: self.prefix.unwrap(), - })); + // most likely case first + State::Body if !self.path.is_empty() => { + let (size, comp) = self.parse_next_component(); + self.path = &self.path[size..]; + if comp.is_some() { + return comp; + } } - State::Prefix => { - self.front = State::StartDir; + State::Body => { + self.front = State::Done; } State::StartDir => { self.front = State::Body; @@ -908,7 +915,7 @@ impl<'a> Iterator for Components<'a> { debug_assert!(!self.path.is_empty()); self.path = &self.path[1..]; return Some(Component::RootDir); - } else if let Some(p) = self.prefix { + } else if HAS_PREFIXES && let Some(p) = self.prefix { if p.has_implicit_root() && !p.is_verbatim() { return Some(Component::RootDir); } @@ -918,15 +925,19 @@ impl<'a> Iterator for Components<'a> { return Some(Component::CurDir); } } - State::Body if !self.path.is_empty() => { - let (size, comp) = self.parse_next_component(); - self.path = &self.path[size..]; - if comp.is_some() { - return comp; - } + _ if const { !HAS_PREFIXES } => unreachable!(), + State::Prefix if self.prefix_len() == 0 => { + self.front = State::StartDir; } - State::Body => { - self.front = State::Done; + State::Prefix => { + self.front = State::StartDir; + debug_assert!(self.prefix_len() <= self.path.len()); + let raw = &self.path[..self.prefix_len()]; + self.path = &self.path[self.prefix_len()..]; + return Some(Component::Prefix(PrefixComponent { + raw: unsafe { OsStr::from_encoded_bytes_unchecked(raw) }, + parsed: self.prefix.unwrap(), + })); } State::Done => unreachable!(), } @@ -951,11 +962,11 @@ impl<'a> DoubleEndedIterator for Components<'a> { self.back = State::StartDir; } State::StartDir => { - self.back = State::Prefix; + self.back = if HAS_PREFIXES { State::Prefix } else { State::Done }; if self.has_physical_root { self.path = &self.path[..self.path.len() - 1]; return Some(Component::RootDir); - } else if let Some(p) = self.prefix { + } else if HAS_PREFIXES && let Some(p) = self.prefix { if p.has_implicit_root() && !p.is_verbatim() { return Some(Component::RootDir); } @@ -964,6 +975,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { return Some(Component::CurDir); } } + _ if !HAS_PREFIXES => unreachable!(), State::Prefix if self.prefix_len() > 0 => { self.back = State::Done; return Some(Component::Prefix(PrefixComponent { @@ -3116,7 +3128,9 @@ impl Path { path: self.as_u8_slice(), prefix, has_physical_root: has_physical_root(self.as_u8_slice(), prefix), - front: State::Prefix, + // use a platform-specific initial state to avoid one turn of + // the state-machine when the platform doesn't have a Prefix. + front: const { if HAS_PREFIXES { State::Prefix } else { State::StartDir } }, back: State::Body, } } diff --git a/library/std/src/sys/path/cygwin.rs b/library/std/src/sys/path/cygwin.rs index da0982384b0e6..416877a7280d2 100644 --- a/library/std/src/sys/path/cygwin.rs +++ b/library/std/src/sys/path/cygwin.rs @@ -21,6 +21,7 @@ pub fn is_verbatim_sep(b: u8) -> bool { pub use super::windows_prefix::parse_prefix; +pub const HAS_PREFIXES: bool = true; pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/library/std/src/sys/path/sgx.rs b/library/std/src/sys/path/sgx.rs index 32c7752f605d9..dca61f3ea145d 100644 --- a/library/std/src/sys/path/sgx.rs +++ b/library/std/src/sys/path/sgx.rs @@ -17,6 +17,7 @@ pub fn parse_prefix(_: &OsStr) -> Option> { None } +pub const HAS_PREFIXES: bool = false; pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs index a3f4a3bfe1b67..6b8258685aa0f 100644 --- a/library/std/src/sys/path/uefi.rs +++ b/library/std/src/sys/path/uefi.rs @@ -21,6 +21,7 @@ pub fn parse_prefix(_: &OsStr) -> Option> { None } +pub const HAS_PREFIXES: bool = true; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 15530323a198d..611d250db4050 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -17,6 +17,7 @@ pub fn parse_prefix(_: &OsStr) -> Option> { None } +pub const HAS_PREFIXES: bool = false; pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 30b06c132c98d..8ed1fdc36e23f 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -18,6 +18,7 @@ pub fn parse_prefix(_: &OsStr) -> Option> { None } +pub const HAS_PREFIXES: bool = true; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index f124e1e5a71c7..509b13b6ae812 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -9,6 +9,7 @@ mod tests; pub use super::windows_prefix::parse_prefix; +pub const HAS_PREFIXES: bool = true; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\';