diff options
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/fs.rs | 9 | ||||
| -rw-r--r-- | library/std/src/fs/tests.rs | 15 | ||||
| -rw-r--r-- | library/std/src/lib.rs | 7 | ||||
| -rw-r--r-- | library/std/src/os/unix/process.rs | 2 | ||||
| -rw-r--r-- | library/std/src/os/wasi/fs.rs | 8 | ||||
| -rw-r--r-- | library/std/src/path.rs | 70 | ||||
| -rw-r--r-- | library/std/src/path/tests.rs | 80 | ||||
| -rw-r--r-- | library/std/src/process.rs | 14 | ||||
| -rw-r--r-- | library/std/src/sys/unix/path.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys/unix/process/process_unix.rs | 3 |
10 files changed, 177 insertions, 32 deletions
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index e13add799bc..a14f1e2ecb2 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -358,7 +358,7 @@ impl File { /// /// It is equivalent to `OpenOptions::new()` but allows you to write more /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` - /// you can write `File::with_options().read(true).open("foo.txt")`. This + /// you can write `File::options().read(true).open("foo.txt")`. This /// also avoids the need to import `OpenOptions`. /// /// See the [`OpenOptions::new`] function for more details. @@ -366,17 +366,16 @@ impl File { /// # Examples /// /// ```no_run - /// #![feature(with_options)] /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let mut f = File::with_options().read(true).open("foo.txt")?; + /// let mut f = File::options().read(true).open("foo.txt")?; /// Ok(()) /// } /// ``` #[must_use] - #[unstable(feature = "with_options", issue = "65439")] - pub fn with_options() -> OpenOptions { + #[stable(feature = "with_options", since = "1.58.0")] + pub fn options() -> OpenOptions { OpenOptions::new() } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 628de13156c..1417d860c47 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -833,20 +833,11 @@ fn symlink_noexist() { fn read_link() { if cfg!(windows) { // directory symlink - assert_eq!( - check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(), - r"C:\ProgramData" - ); + assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData")); // junction - assert_eq!( - check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(), - r"C:\Users\Default" - ); + assert_eq!(check!(fs::read_link(r"C:\Users\Default User")), Path::new(r"C:\Users\Default")); // junction with special permissions - assert_eq!( - check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(), - r"C:\Users" - ); + assert_eq!(check!(fs::read_link(r"C:\Documents and Settings\")), Path::new(r"C:\Users")); } let tmpdir = tmpdir(); let link = tmpdir.join("link"); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c2243b25953..f2490a77ce0 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -264,7 +264,8 @@ #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(const_option)] -#![feature(const_raw_ptr_deref)] +#![cfg_attr(bootstrap, feature(const_raw_ptr_deref))] +#![cfg_attr(not(bootstrap), feature(const_mut_refs))] #![feature(const_socketaddr)] #![feature(const_trait_impl)] #![feature(container_error_extra)] @@ -320,6 +321,7 @@ #![feature(panic_internals)] #![feature(panic_unwind)] #![feature(pin_static_ref)] +#![cfg_attr(not(bootstrap), feature(portable_simd))] #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(rustc_attrs)] @@ -471,6 +473,9 @@ pub use core::pin; pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; +#[unstable(feature = "portable_simd", issue = "86656")] +#[cfg(not(bootstrap))] +pub use core::simd; #[unstable(feature = "async_stream", issue = "79024")] pub use core::stream; #[stable(feature = "i128", since = "1.26.0")] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 01b8303a6c3..855f900430c 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -207,7 +207,7 @@ impl CommandExt for process::Command { /// [`ExitStatusError`](process::ExitStatusError). /// /// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as -/// passed to the `exit` system call or returned by +/// passed to the `_exit` system call or returned by /// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status** /// as returned by one of the `wait` family of system /// calls. diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 907368061d7..5c62679f552 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -444,18 +444,22 @@ pub trait FileTypeExt { /// Returns `true` if this file type is a block device. fn is_block_device(&self) -> bool; /// Returns `true` if this file type is a character device. - fn is_character_device(&self) -> bool; + fn is_char_device(&self) -> bool; /// Returns `true` if this file type is a socket datagram. fn is_socket_dgram(&self) -> bool; /// Returns `true` if this file type is a socket stream. fn is_socket_stream(&self) -> bool; + /// Returns `true` if this file type is any type of socket. + fn is_socket(&self) -> bool { + self.is_socket_stream() || self.is_socket_dgram() + } } impl FileTypeExt for fs::FileType { fn is_block_device(&self) -> bool { self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE } - fn is_character_device(&self) -> bool { + fn is_char_device(&self) -> bool { self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE } fn is_socket_dgram(&self) -> bool { diff --git a/library/std/src/path.rs b/library/std/src/path.rs index dc0c735a06c..cf2cd5adc48 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -979,6 +979,25 @@ impl FusedIterator for Components<'_> {} impl<'a> cmp::PartialEq for Components<'a> { #[inline] fn eq(&self, other: &Components<'a>) -> bool { + let Components { path: _, front: _, back: _, has_physical_root: _, prefix: _ } = self; + + // Fast path for exact matches, e.g. for hashmap lookups. + // Don't explicitly compare the prefix or has_physical_root fields since they'll + // either be covered by the `path` buffer or are only relevant for `prefix_verbatim()`. + if self.path.len() == other.path.len() + && self.front == other.front + && self.back == State::Body + && other.back == State::Body + && self.prefix_verbatim() == other.prefix_verbatim() + { + // possible future improvement: this could bail out earlier if there were a + // reverse memcmp/bcmp comparing back to front + if self.path == other.path { + return true; + } + } + + // compare back to front since absolute paths often share long prefixes Iterator::eq(self.clone().rev(), other.clone().rev()) } } @@ -1013,13 +1032,12 @@ fn compare_components(mut left: Components<'_>, mut right: Components<'_>) -> cm // The fast path isn't taken for paths with a PrefixComponent to avoid backtracking into // the middle of one if left.prefix.is_none() && right.prefix.is_none() && left.front == right.front { - // this might benefit from a [u8]::first_mismatch simd implementation, if it existed - let first_difference = - match left.path.iter().zip(right.path.iter()).position(|(&a, &b)| a != b) { - None if left.path.len() == right.path.len() => return cmp::Ordering::Equal, - None => left.path.len().min(right.path.len()), - Some(diff) => diff, - }; + // possible future improvement: a [u8]::first_mismatch simd implementation + let first_difference = match left.path.iter().zip(right.path).position(|(&a, &b)| a != b) { + None if left.path.len() == right.path.len() => return cmp::Ordering::Equal, + None => left.path.len().min(right.path.len()), + Some(diff) => diff, + }; if let Some(previous_sep) = left.path[..first_difference].iter().rposition(|&b| left.is_sep_byte(b)) @@ -2873,9 +2891,43 @@ impl cmp::PartialEq for Path { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Path { fn hash<H: Hasher>(&self, h: &mut H) { - for component in self.components() { - component.hash(h); + let bytes = self.as_u8_slice(); + let prefix_len = match parse_prefix(&self.inner) { + Some(prefix) => { + prefix.hash(h); + prefix.len() + } + None => 0, + }; + let bytes = &bytes[prefix_len..]; + + let mut component_start = 0; + let mut bytes_hashed = 0; + + for i in 0..bytes.len() { + if is_sep_byte(bytes[i]) { + if i > component_start { + let to_hash = &bytes[component_start..i]; + h.write(to_hash); + bytes_hashed += to_hash.len(); + } + + // skip over separator and optionally a following CurDir item + // since components() would normalize these away + component_start = i + match bytes[i..] { + [_, b'.', b'/', ..] | [_, b'.'] => 2, + _ => 1, + }; + } + } + + if component_start < bytes.len() { + let to_hash = &bytes[component_start..]; + h.write(to_hash); + bytes_hashed += to_hash.len(); } + + h.write_usize(bytes_hashed); } } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 0a16ff2a721..2bf499e1ab8 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1,6 +1,8 @@ use super::*; -use crate::collections::BTreeSet; +use crate::collections::hash_map::DefaultHasher; +use crate::collections::{BTreeSet, HashSet}; +use crate::hash::Hasher; use crate::rc::Rc; use crate::sync::Arc; use core::hint::black_box; @@ -1632,7 +1634,25 @@ fn into_rc() { fn test_ord() { macro_rules! ord( ($ord:ident, $left:expr, $right:expr) => ( { - assert_eq!(Path::new($left).cmp(&Path::new($right)), core::cmp::Ordering::$ord); + use core::cmp::Ordering; + + let left = Path::new($left); + let right = Path::new($right); + assert_eq!(left.cmp(&right), Ordering::$ord); + if (core::cmp::Ordering::$ord == Ordering::Equal) { + assert_eq!(left, right); + + let mut hasher = DefaultHasher::new(); + left.hash(&mut hasher); + let left_hash = hasher.finish(); + hasher = DefaultHasher::new(); + right.hash(&mut hasher); + let right_hash = hasher.finish(); + + assert_eq!(left_hash, right_hash, "hashes for {:?} and {:?} must match", left, right); + } else { + assert_ne!(left, right); + } }); ); @@ -1693,3 +1713,59 @@ fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { set.insert(paths[500].as_path()); }); } + +#[bench] +fn bench_path_hashset(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(black_box(paths[500].as_path())) + }); +} + +#[bench] +fn bench_path_hashset_miss(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + let probe = PathBuf::from(prefix).join("other"); + + b.iter(|| set.remove(black_box(probe.as_path()))); +} + +#[bench] +fn bench_hash_path_short(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = Path::new("explorer.exe"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} + +#[bench] +fn bench_hash_path_long(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = + Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 9cc7fc2f035..4e9fd51f282 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -106,6 +106,7 @@ mod tests; use crate::io::prelude::*; +use crate::convert::Infallible; use crate::ffi::OsStr; use crate::fmt; use crate::fs; @@ -1417,6 +1418,11 @@ impl From<fs::File> for Stdio { /// /// [`status`]: Command::status /// [`wait`]: Child::wait +// +// We speak slightly loosely (here and in various other places in the stdlib docs) about `exit` +// vs `_exit`. Naming of Unix system calls is not standardised across Unices, so terminology is a +// matter of convention and tradition. For clarity we usually speak of `exit`, even when we might +// mean an underlying system call such as `_exit`. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[stable(feature = "process", since = "1.0.0")] pub struct ExitStatus(imp::ExitStatus); @@ -2061,6 +2067,14 @@ impl<E: fmt::Debug> Termination for Result<!, E> { } #[unstable(feature = "termination_trait_lib", issue = "43301")] +impl<E: fmt::Debug> Termination for Result<Infallible, E> { + fn report(self) -> i32 { + let Err(err) = self; + Err::<!, _>(err).report() + } +} + +#[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for ExitCode { #[inline] fn report(self) -> i32 { diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index 840a7ae0426..717add9ec48 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -11,6 +11,7 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'/' } +#[inline] pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { None } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 326382d9038..3bf1493f3b8 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -617,6 +617,9 @@ impl Process { } /// Unix exit statuses +// +// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status". +// See the discussion in comments and doc comments for `std::process::ExitStatus`. #[derive(PartialEq, Eq, Clone, Copy)] pub struct ExitStatus(c_int); |
