diff options
Diffstat (limited to 'library/std/src')
26 files changed, 495 insertions, 126 deletions
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index a39565d2159..6c098034eea 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -137,7 +137,7 @@ impl OsString { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "1.91.0")] pub const fn new() -> OsString { OsString { inner: Buf::from_string(String::new()) } } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index a45edd08e8c..25a4661a0bc 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3234,7 +3234,7 @@ fn inlined_slow_read_byte<R: Read>(reader: &mut R) -> Option<Result<u8>> { } } -// Used by `BufReader::spec_read_byte`, for which the `inline(ever)` is +// Used by `BufReader::spec_read_byte`, for which the `inline(never)` is // important. #[inline(never)] fn uninlined_slow_read_byte<R: Read>(reader: &mut R) -> Option<Result<u8>> { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 233e41aa345..da41c1216c4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -94,7 +94,7 @@ //! pull-requests for your suggested changes. //! //! Contributions are appreciated! If you see a part of the docs that can be -//! improved, submit a PR, or chat with us first on [Discord][rust-discord] +//! improved, submit a PR, or chat with us first on [Zulip][rust-zulip] //! #docs. //! //! # A Tour of The Rust Standard Library @@ -212,7 +212,7 @@ //! [multithreading]: thread //! [other]: #what-is-in-the-standard-library-documentation //! [primitive types]: ../book/ch03-02-data-types.html -//! [rust-discord]: https://discord.gg/rust-lang +//! [rust-zulip]: https://rust-lang.zulipchat.com/ //! [array]: prim@array //! [slice]: prim@slice @@ -235,7 +235,7 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] #![doc(rust_logo)] -#![doc(cfg_hide(not(test), no_global_oom_handling, not(no_global_oom_handling)))] +#![doc(auto_cfg(hide(no_global_oom_handling)))] // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind @@ -285,7 +285,6 @@ #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] -#![feature(doc_cfg_hide)] #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] diff --git a/library/std/src/net/hostname.rs b/library/std/src/net/hostname.rs new file mode 100644 index 00000000000..b1010cec600 --- /dev/null +++ b/library/std/src/net/hostname.rs @@ -0,0 +1,22 @@ +use crate::ffi::OsString; + +/// Returns the system hostname. +/// +/// This can error out in platform-specific error cases; +/// for example, uefi and wasm, where hostnames aren't +/// supported. +/// +/// # Underlying system calls +/// +/// | Platform | System call | +/// |----------|---------------------------------------------------------------------------------------------------------| +/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) | +/// | Windows | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) | +/// +/// Note that platform-specific behavior [may change in the future][changes]. +/// +/// [changes]: crate::io#platform-specific-behavior +#[unstable(feature = "gethostname", issue = "135142")] +pub fn hostname() -> crate::io::Result<OsString> { + crate::sys::net::hostname() +} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 40f1a93e39d..3e4447eb33f 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -1,7 +1,8 @@ //! Networking primitives for TCP/UDP communication. //! //! This module provides networking functionality for the Transmission Control and User -//! Datagram Protocols, as well as types for IP and socket addresses. +//! Datagram Protocols, as well as types for IP and socket addresses and functions related +//! to network properties. //! //! # Organization //! @@ -24,6 +25,8 @@ #[stable(feature = "rust1", since = "1.0.0")] pub use core::net::AddrParseError; +#[unstable(feature = "gethostname", issue = "135142")] +pub use self::hostname::hostname; #[stable(feature = "rust1", since = "1.0.0")] pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] @@ -35,6 +38,7 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; +mod hostname; mod ip_addr; mod socket_addr; mod tcp; diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index fde53ec4257..3f9b2bd3f4b 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -4,6 +4,8 @@ use crate::sealed::Sealed; use crate::sys_common::AsInner; +#[cfg(target_os = "linux")] +use crate::time::Duration; use crate::{io, net}; /// Os-specific extensions for [`TcpStream`] @@ -59,11 +61,13 @@ pub trait TcpStreamExt: Sealed { /// A socket listener will be awakened solely when data arrives. /// - /// The `accept` argument set the delay in seconds until the + /// The `accept` argument set the maximum delay until the /// data is available to read, reducing the number of short lived /// connections without data to process. /// Contrary to other platforms `SO_ACCEPTFILTER` feature equivalent, there is /// no necessity to set it after the `listen` call. + /// Note that the delay is expressed as Duration from user's perspective + /// the call rounds it down to the nearest second expressible as a `c_int`. /// /// See [`man 7 tcp`](https://man7.org/linux/man-pages/man7/tcp.7.html) /// @@ -73,16 +77,17 @@ pub trait TcpStreamExt: Sealed { /// #![feature(tcp_deferaccept)] /// use std::net::TcpStream; /// use std::os::linux::net::TcpStreamExt; + /// use std::time::Duration; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_deferaccept(1).expect("set_deferaccept call failed"); + /// stream.set_deferaccept(Duration::from_secs(1u64)).expect("set_deferaccept call failed"); /// ``` #[unstable(feature = "tcp_deferaccept", issue = "119639")] #[cfg(target_os = "linux")] - fn set_deferaccept(&self, accept: u32) -> io::Result<()>; + fn set_deferaccept(&self, accept: Duration) -> io::Result<()>; - /// Gets the accept delay value (in seconds) of the `TCP_DEFER_ACCEPT` option. + /// Gets the accept delay value of the `TCP_DEFER_ACCEPT` option. /// /// For more information about this option, see [`TcpStreamExt::set_deferaccept`]. /// @@ -92,15 +97,16 @@ pub trait TcpStreamExt: Sealed { /// #![feature(tcp_deferaccept)] /// use std::net::TcpStream; /// use std::os::linux::net::TcpStreamExt; + /// use std::time::Duration; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_deferaccept(1).expect("set_deferaccept call failed"); - /// assert_eq!(stream.deferaccept().unwrap_or(0), 1); + /// stream.set_deferaccept(Duration::from_secs(1u64)).expect("set_deferaccept call failed"); + /// assert_eq!(stream.deferaccept().unwrap(), Duration::from_secs(1u64)); /// ``` #[unstable(feature = "tcp_deferaccept", issue = "119639")] #[cfg(target_os = "linux")] - fn deferaccept(&self) -> io::Result<u32>; + fn deferaccept(&self) -> io::Result<Duration>; } #[stable(feature = "tcp_quickack", since = "1.89.0")] @@ -117,12 +123,12 @@ impl TcpStreamExt for net::TcpStream { } #[cfg(target_os = "linux")] - fn set_deferaccept(&self, accept: u32) -> io::Result<()> { + fn set_deferaccept(&self, accept: Duration) -> io::Result<()> { self.as_inner().as_inner().set_deferaccept(accept) } #[cfg(target_os = "linux")] - fn deferaccept(&self) -> io::Result<u32> { + fn deferaccept(&self) -> io::Result<Duration> { self.as_inner().as_inner().deferaccept() } } diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index 12f35696abc..0758b426ccc 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -32,6 +32,7 @@ fn deferaccept() { use crate::net::test::next_test_ip4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; + use crate::time::Duration; macro_rules! t { ($e:expr) => { @@ -43,10 +44,12 @@ fn deferaccept() { } let addr = next_test_ip4(); + let one = Duration::from_secs(1u64); + let zero = Duration::from_secs(0u64); let _listener = t!(TcpListener::bind(&addr)); let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - stream.set_deferaccept(1).expect("set_deferaccept failed"); - assert_eq!(stream.deferaccept().unwrap(), 1); - stream.set_deferaccept(0).expect("set_deferaccept failed"); - assert_eq!(stream.deferaccept().unwrap(), 0); + stream.set_deferaccept(one).expect("set_deferaccept failed"); + assert_eq!(stream.deferaccept().unwrap(), one); + stream.set_deferaccept(zero).expect("set_deferaccept failed"); + assert_eq!(stream.deferaccept().unwrap(), zero); } diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index 345d5b74285..20e5383dc09 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -141,7 +141,7 @@ impl OsStrExt for OsStr { pub struct EncodeWide<'a> { inner: alloc::wtf8::EncodeWide<'a>, } -#[stable(feature = "encode_wide_debug", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "encode_wide_debug", since = "1.91.0")] impl fmt::Debug for EncodeWide<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, f) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 5e8d2f8e78e..1997785885d 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -122,7 +122,7 @@ impl<'a> PanicHookInfo<'a> { /// ``` #[must_use] #[inline] - #[stable(feature = "panic_payload_as_str", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "panic_payload_as_str", since = "1.91.0")] pub fn payload_as_str(&self) -> Option<&str> { if let Some(s) = self.payload.downcast_ref::<&str>() { Some(s) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 88d8a4f21ca..6e3b1e6e47d 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1191,7 +1191,7 @@ impl PathBuf { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "1.91.0")] pub const fn new() -> PathBuf { PathBuf { inner: OsString::new() } } @@ -1412,6 +1412,99 @@ impl PathBuf { } } + /// Sets whether the path has a trailing [separator](MAIN_SEPARATOR). + /// + /// The value returned by [`has_trailing_sep`](Path::has_trailing_sep) will be equivalent to + /// the provided value if possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir"); + /// + /// assert!(!p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(!p.has_trailing_sep()); + /// p.set_trailing_sep(true); + /// assert!(p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(!p.has_trailing_sep()); + /// + /// p = PathBuf::from("/"); + /// assert!(p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(p.has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn set_trailing_sep(&mut self, trailing_sep: bool) { + if trailing_sep { self.push_trailing_sep() } else { self.pop_trailing_sep() } + } + + /// Adds a trailing [separator](MAIN_SEPARATOR) to the path. + /// + /// This acts similarly to [`Path::with_trailing_sep`], but mutates the underlying `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir"); + /// + /// assert!(!p.has_trailing_sep()); + /// p.push_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// p.push_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// + /// p = PathBuf::from("dir/"); + /// p.push_trailing_sep(); + /// assert_eq!(p.as_os_str(), OsStr::new("dir/")); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn push_trailing_sep(&mut self) { + if !self.has_trailing_sep() { + self.push(""); + } + } + + /// Removes a trailing [separator](MAIN_SEPARATOR) from the path, if possible. + /// + /// This acts similarly to [`Path::trim_trailing_sep`], but mutates the underlying `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir//"); + /// + /// assert!(p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir//")); + /// p.pop_trailing_sep(); + /// assert!(!p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir")); + /// p.pop_trailing_sep(); + /// assert!(!p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir")); + /// + /// p = PathBuf::from("/"); + /// assert!(p.has_trailing_sep()); + /// p.pop_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn pop_trailing_sep(&mut self) { + self.inner.truncate(self.trim_trailing_sep().as_os_str().len()); + } + /// Updates [`self.file_name`] to `file_name`. /// /// If [`self.file_name`] was [`None`], this is equivalent to pushing @@ -1594,7 +1687,7 @@ impl PathBuf { /// p.add_extension(""); /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); /// ``` - #[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "path_add_extension", since = "1.91.0")] pub fn add_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool { self._add_extension(extension.as_ref()) } @@ -1610,7 +1703,7 @@ impl PathBuf { let new = extension.as_encoded_bytes(); if !new.is_empty() { // truncate until right after the file name - // this is necessary for trimming the trailing slash + // this is necessary for trimming the trailing separator let end_file_name = file_name[file_name.len()..].as_ptr().addr(); let start = self.inner.as_encoded_bytes().as_ptr().addr(); self.inner.truncate(end_file_name.wrapping_sub(start)); @@ -2103,7 +2196,7 @@ impl PartialEq for PathBuf { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<str> for PathBuf { #[inline] fn eq(&self, other: &str) -> bool { @@ -2111,7 +2204,7 @@ impl cmp::PartialEq<str> for PathBuf { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<PathBuf> for str { #[inline] fn eq(&self, other: &PathBuf) -> bool { @@ -2119,7 +2212,7 @@ impl cmp::PartialEq<PathBuf> for str { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<String> for PathBuf { #[inline] fn eq(&self, other: &String) -> bool { @@ -2127,7 +2220,7 @@ impl cmp::PartialEq<String> for PathBuf { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<PathBuf> for String { #[inline] fn eq(&self, other: &PathBuf) -> bool { @@ -2724,7 +2817,7 @@ impl Path { /// /// [`Path::file_stem`]: Path::file_stem /// - #[stable(feature = "path_file_prefix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "path_file_prefix", since = "1.91.0")] #[must_use] pub fn file_prefix(&self) -> Option<&OsStr> { self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) @@ -2755,6 +2848,94 @@ impl Path { self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after)) } + /// Checks whether the path ends in a trailing [separator](MAIN_SEPARATOR). + /// + /// This is generally done to ensure that a path is treated as a directory, not a file, + /// although it does not actually guarantee that such a path is a directory on the underlying + /// file system. + /// + /// Despite this behavior, two paths are still considered the same in Rust whether they have a + /// trailing separator or not. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::path::Path; + /// + /// assert!(Path::new("dir/").has_trailing_sep()); + /// assert!(!Path::new("file.rs").has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn has_trailing_sep(&self) -> bool { + self.as_os_str().as_encoded_bytes().last().copied().is_some_and(is_sep_byte) + } + + /// Ensures that a path has a trailing [separator](MAIN_SEPARATOR), + /// allocating a [`PathBuf`] if necessary. + /// + /// The resulting path will return true for [`has_trailing_sep`](Self::has_trailing_sep). + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::Path; + /// + /// assert_eq!(Path::new("dir//").with_trailing_sep().as_os_str(), OsStr::new("dir//")); + /// assert_eq!(Path::new("dir/").with_trailing_sep().as_os_str(), OsStr::new("dir/")); + /// assert!(!Path::new("dir").has_trailing_sep()); + /// assert!(Path::new("dir").with_trailing_sep().has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn with_trailing_sep(&self) -> Cow<'_, Path> { + if self.has_trailing_sep() { Cow::Borrowed(self) } else { Cow::Owned(self.join("")) } + } + + /// Trims a trailing [separator](MAIN_SEPARATOR) from a path, if possible. + /// + /// The resulting path will return false for [`has_trailing_sep`](Self::has_trailing_sep) for + /// most paths. + /// + /// Some paths, like `/`, cannot be trimmed in this way. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::Path; + /// + /// assert_eq!(Path::new("dir//").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("dir/").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("dir").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("/").trim_trailing_sep().as_os_str(), OsStr::new("/")); + /// assert_eq!(Path::new("//").trim_trailing_sep().as_os_str(), OsStr::new("//")); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn trim_trailing_sep(&self) -> &Path { + if self.has_trailing_sep() && (!self.has_root() || self.parent().is_some()) { + let mut bytes = self.inner.as_encoded_bytes(); + while let Some((last, init)) = bytes.split_last() + && is_sep_byte(*last) + { + bytes = init; + } + + // SAFETY: Trimming trailing ASCII bytes will retain the validity of the string. + Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }) + } else { + self + } + } + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. /// /// If `path` is absolute, it replaces the current path. @@ -2888,7 +3069,7 @@ impl Path { /// assert_eq!(path.with_added_extension("xz"), PathBuf::from("foo.tar.gz.xz")); /// assert_eq!(path.with_added_extension("").with_added_extension("txt"), PathBuf::from("foo.tar.gz.txt")); /// ``` - #[stable(feature = "path_add_extension", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "path_add_extension", since = "1.91.0")] pub fn with_added_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf { let mut new_path = self.to_path_buf(); new_path.add_extension(extension); @@ -2907,7 +3088,7 @@ impl Path { /// `a/b` all have `a` and `b` as components, but `./a/b` starts with /// an additional [`CurDir`] component. /// - /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// * Trailing separators are normalized away, so `/a/b` and `/a/b/` are equivalent. /// /// Note that no other normalization takes place; in particular, `a/c` /// and `a/b/../c` are distinct, to account for the possibility that `b` @@ -3405,7 +3586,7 @@ impl PartialEq for Path { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<str> for Path { #[inline] fn eq(&self, other: &str) -> bool { @@ -3414,7 +3595,7 @@ impl cmp::PartialEq<str> for Path { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<Path> for str { #[inline] fn eq(&self, other: &Path) -> bool { @@ -3422,7 +3603,7 @@ impl cmp::PartialEq<Path> for str { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<String> for Path { #[inline] fn eq(&self, other: &String) -> bool { @@ -3430,7 +3611,7 @@ impl cmp::PartialEq<String> for Path { } } -#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "eq_str_for_path", since = "1.91.0")] impl cmp::PartialEq<Path> for String { #[inline] fn eq(&self, other: &Path) -> bool { @@ -3718,7 +3899,7 @@ impl Error for NormalizeError {} /// /// On POSIX platforms, the path is resolved using [POSIX semantics][posix-semantics], /// except that it stops short of resolving symlinks. This means it will keep `..` -/// components and trailing slashes. +/// components and trailing separators. /// /// On Windows, for verbatim paths, this will simply return the path as given. For other /// paths, this is currently equivalent to calling diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 8216f8d2fd5..a191576d93b 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -485,14 +485,15 @@ impl Socket { // bionic libc makes no use of this flag #[cfg(target_os = "linux")] - pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) + pub fn set_deferaccept(&self, accept: Duration) -> io::Result<()> { + let val = cmp::min(accept.as_secs(), c_int::MAX as u64) as c_int; + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val) } #[cfg(target_os = "linux")] - pub fn deferaccept(&self) -> io::Result<u32> { + pub fn deferaccept(&self) -> io::Result<Duration> { let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; - Ok(raw as u32) + Ok(Duration::from_secs(raw as _)) } #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index b71d8b1357b..5b6f4cedf1b 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -8,9 +8,8 @@ use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; -use crate::sync::atomic::Atomic; -use crate::sync::atomic::Ordering::{AcqRel, Relaxed}; use crate::sys::c; +use crate::sys::pal::winsock::last_error; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -112,84 +111,11 @@ pub(super) mod netc { } } +pub use crate::sys::pal::winsock::{cleanup, cvt, cvt_gai, cvt_r, startup as init}; + #[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); -static WSA_INITIALIZED: Atomic<bool> = Atomic::<bool>::new(false); - -/// Checks whether the Windows socket interface has been started already, and -/// if not, starts it. -#[inline] -pub fn init() { - if !WSA_INITIALIZED.load(Relaxed) { - wsa_startup(); - } -} - -#[cold] -fn wsa_startup() { - unsafe { - let mut data: c::WSADATA = mem::zeroed(); - let ret = c::WSAStartup( - 0x202, // version 2.2 - &mut data, - ); - assert_eq!(ret, 0); - if WSA_INITIALIZED.swap(true, AcqRel) { - // If another thread raced with us and called WSAStartup first then call - // WSACleanup so it's as though WSAStartup was only called once. - c::WSACleanup(); - } - } -} - -pub fn cleanup() { - // We don't need to call WSACleanup here because exiting the process will cause - // the OS to clean everything for us, which is faster than doing it manually. - // See #141799. -} - -/// Returns the last error from the Windows socket interface. -fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) -/// and if so, returns the last error from the Windows socket interface. This -/// function must be called before another call to the socket API is made. -pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { - if t.is_minus_one() { Err(last_error()) } else { Ok(t) } -} - -/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { Ok(()) } else { Err(last_error()) } -} - -/// Just to provide the same interface as sys/pal/unix/net.rs -pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - impl Socket { pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { let family = match *addr { diff --git a/library/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs new file mode 100644 index 00000000000..8ffe4894d71 --- /dev/null +++ b/library/std/src/sys/net/hostname/mod.rs @@ -0,0 +1,14 @@ +cfg_select! { + all(target_family = "unix", not(target_os = "espidf")) => { + mod unix; + pub use unix::hostname; + } + target_os = "windows" => { + mod windows; + pub use windows::hostname; + } + _ => { + mod unsupported; + pub use unsupported::hostname; + } +} diff --git a/library/std/src/sys/net/hostname/unix.rs b/library/std/src/sys/net/hostname/unix.rs new file mode 100644 index 00000000000..bc6fa82a38f --- /dev/null +++ b/library/std/src/sys/net/hostname/unix.rs @@ -0,0 +1,62 @@ +use crate::ffi::OsString; +use crate::io; +use crate::os::unix::ffi::OsStringExt; +use crate::sys::pal::os::errno; + +pub fn hostname() -> io::Result<OsString> { + // Query the system for the maximum host name length. + let host_name_max = match unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) } { + // If this fails (possibly because there is no maximum length), then + // assume a maximum length of _POSIX_HOST_NAME_MAX (255). + -1 => 255, + max => max as usize, + }; + + // Reserve space for the nul terminator too. + let mut buf = Vec::<u8>::try_with_capacity(host_name_max + 1)?; + loop { + // SAFETY: `buf.capacity()` bytes of `buf` are writable. + let r = unsafe { libc::gethostname(buf.as_mut_ptr().cast(), buf.capacity()) }; + match (r != 0).then(errno) { + None => { + // Unfortunately, the UNIX specification says that the name will + // be truncated if it does not fit in the buffer, without returning + // an error. As additionally, the truncated name may still be null- + // terminated, there is no reliable way to detect truncation. + // Fortunately, most platforms ignore what the specification says + // and return an error (mostly ENAMETOOLONG). Should that not be + // the case, the following detects truncation if the null-terminator + // was omitted. Note that this check does not impact performance at + // all as we need to find the length of the string anyways. + // + // Use `strnlen` as it does not place an initialization requirement + // on the bytes after the nul terminator. + // + // SAFETY: `buf.capacity()` bytes of `buf` are accessible, and are + // initialized up to and including a possible nul terminator. + let len = unsafe { libc::strnlen(buf.as_ptr().cast(), buf.capacity()) }; + if len < buf.capacity() { + // If the string is nul-terminated, we assume that is has not + // been truncated, as the capacity *should be* enough to hold + // `HOST_NAME_MAX` bytes. + // SAFETY: `len + 1` bytes have been initialized (we exclude + // the nul terminator from the string). + unsafe { buf.set_len(len) }; + return Ok(OsString::from_vec(buf)); + } + } + // As `buf.capacity()` is always less than or equal to `isize::MAX` + // (Rust allocations cannot exceed that limit), the only way `EINVAL` + // can be returned is if the system uses `EINVAL` to report that the + // name does not fit in the provided buffer. In that case (or in the + // case of `ENAMETOOLONG`), resize the buffer and try again. + Some(libc::EINVAL | libc::ENAMETOOLONG) => {} + // Other error codes (e.g. EPERM) have nothing to do with the buffer + // size and should be returned to the user. + Some(err) => return Err(io::Error::from_raw_os_error(err)), + } + + // Resize the buffer (according to `Vec`'s resizing rules) and try again. + buf.try_reserve(buf.capacity() + 1)?; + } +} diff --git a/library/std/src/sys/net/hostname/unsupported.rs b/library/std/src/sys/net/hostname/unsupported.rs new file mode 100644 index 00000000000..d868f68f32d --- /dev/null +++ b/library/std/src/sys/net/hostname/unsupported.rs @@ -0,0 +1,6 @@ +use crate::ffi::OsString; +use crate::io::{Error, Result}; + +pub fn hostname() -> Result<OsString> { + Err(Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/net/hostname/windows.rs b/library/std/src/sys/net/hostname/windows.rs new file mode 100644 index 00000000000..24eed100f32 --- /dev/null +++ b/library/std/src/sys/net/hostname/windows.rs @@ -0,0 +1,24 @@ +use crate::ffi::OsString; +use crate::io::Result; +use crate::mem::MaybeUninit; +use crate::os::windows::ffi::OsStringExt; +use crate::sys::pal::c; +use crate::sys::pal::winsock::{self, cvt}; + +pub fn hostname() -> Result<OsString> { + winsock::startup(); + + // The documentation of GetHostNameW says that a buffer size of 256 is + // always enough. + let mut buffer = [const { MaybeUninit::<u16>::uninit() }; 256]; + // SAFETY: these parameters specify a valid, writable region of memory. + cvt(unsafe { c::GetHostNameW(buffer.as_mut_ptr().cast(), buffer.len() as i32) })?; + // Use `lstrlenW` here as it does not require the bytes after the nul + // terminator to be initialized. + // SAFETY: if `GetHostNameW` returns successfully, the name is nul-terminated. + let len = unsafe { c::lstrlenW(buffer.as_ptr().cast()) }; + // SAFETY: the length of the name is `len`, hence `len` bytes have been + // initialized by `GetHostNameW`. + let name = unsafe { buffer[..len as usize].assume_init_ref() }; + Ok(OsString::from_wide(name)) +} diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs index dffc4ea7f81..bfe5cf53128 100644 --- a/library/std/src/sys/net/mod.rs +++ b/library/std/src/sys/net/mod.rs @@ -2,3 +2,6 @@ /// `UdpSocket` as well as related functionality like DNS resolving. mod connection; pub use connection::*; + +mod hostname; +pub use hostname::hostname; diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index f0b6068e06c..7c9f3b7992f 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -16,7 +16,7 @@ use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; -const PATH_SEPARATOR: u8 = if cfg!(target_os = "redox") { b';' } else { b':' }; +const PATH_SEPARATOR: u8 = b':'; unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 0d2100d66bc..28b05d8a68a 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -72,7 +72,7 @@ mod imp { use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, panic, ptr}; + use crate::{io, mem, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -148,6 +148,13 @@ mod imp { let mut guard_page_range = unsafe { install_main_guard() }; + // Even for panic=immediate-abort, installing the guard pages is important for soundness. + // That said, we do not care about giving nice stackoverflow messages via our custom + // signal handler, just exit early and let the user enjoy the segfault. + if cfg!(panic = "immediate-abort") { + return; + } + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; for &signal in &[SIGSEGV, SIGBUS] { @@ -179,6 +186,9 @@ mod imp { /// Must be called only once #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn cleanup() { + if cfg!(panic = "immediate-abort") { + return; + } // FIXME: I probably cause more bugs than I'm worth! // see https://github.com/rust-lang/rust/issues/111272 unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) }; @@ -230,7 +240,7 @@ mod imp { /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool, thread_name: Option<Box<str>>) -> Handler { - if !NEED_ALTSTACK.load(Ordering::Acquire) { + if cfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index abc1c19827f..9009aa09f48 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2170,6 +2170,7 @@ GetFileType GETFINALPATHNAMEBYHANDLE_FLAGS GetFinalPathNameByHandleW GetFullPathNameW +GetHostNameW GetLastError GetModuleFileNameW GetModuleHandleA @@ -2270,6 +2271,7 @@ LPPROGRESS_ROUTINE LPPROGRESS_ROUTINE_CALLBACK_REASON LPTHREAD_START_ROUTINE LPWSAOVERLAPPED_COMPLETION_ROUTINE +lstrlenW M128A MAX_PATH MAXIMUM_REPARSE_DATA_BUFFER_SIZE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 989a1246650..98f277b3378 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -49,6 +49,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE, windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE); windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32); +windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32); windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE); @@ -134,6 +135,7 @@ windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : * windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32); +windows_targets::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32); windows_targets::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32); windows_targets::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn select(nfds : i32, readfds : *mut FD_SET, writefds : *mut FD_SET, exceptfds : *mut FD_SET, timeout : *const TIMEVAL) -> i32); diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 3357946b8f7..18ab3498267 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -22,7 +22,8 @@ pub mod os; pub mod pipe; pub mod time; cfg_select! { - not(target_vendor = "uwp") => { + // We don't care about printing nice error messages for panic=immediate-abort + all(not(target_vendor = "uwp"), not(panic = "immediate-abort")) => { pub mod stack_overflow; } _ => { @@ -30,6 +31,7 @@ cfg_select! { pub use self::stack_overflow_uwp as stack_overflow; } } +pub mod winsock; /// Map a [`Result<T, WinError>`] to [`io::Result<T>`](crate::io::Result<T>). pub trait IoResult<T> { diff --git a/library/std/src/sys/pal/windows/winsock.rs b/library/std/src/sys/pal/windows/winsock.rs new file mode 100644 index 00000000000..b110a43ef3a --- /dev/null +++ b/library/std/src/sys/pal/windows/winsock.rs @@ -0,0 +1,80 @@ +use super::c; +use crate::ffi::c_int; +use crate::sync::atomic::Atomic; +use crate::sync::atomic::Ordering::{AcqRel, Relaxed}; +use crate::{io, mem}; + +static WSA_STARTED: Atomic<bool> = Atomic::<bool>::new(false); + +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +#[inline] +pub fn startup() { + if !WSA_STARTED.load(Relaxed) { + wsa_startup(); + } +} + +#[cold] +fn wsa_startup() { + unsafe { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup( + 0x202, // version 2.2 + &mut data, + ); + assert_eq!(ret, 0); + if WSA_STARTED.swap(true, AcqRel) { + // If another thread raced with us and called WSAStartup first then call + // WSACleanup so it's as though WSAStartup was only called once. + c::WSACleanup(); + } + } +} + +pub fn cleanup() { + // We don't need to call WSACleanup here because exiting the process will cause + // the OS to clean everything for us, which is faster than doing it manually. + // See #141799. +} + +/// Returns the last error from the Windows socket interface. +pub fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) +/// and if so, returns the last error from the Windows socket interface. This +/// function must be called before another call to the socket API is made. +pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { Ok(()) } else { Err(last_error()) } +} + +/// Just to provide the same interface as sys/pal/unix/net.rs +pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} diff --git a/library/std/src/sys/stdio/vexos.rs b/library/std/src/sys/stdio/vexos.rs index 1f2251c6421..9a391feb7a8 100644 --- a/library/std/src/sys/stdio/vexos.rs +++ b/library/std/src/sys/stdio/vexos.rs @@ -13,7 +13,7 @@ impl Stdin { } impl io::Read for Stdin { - fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { let mut count = 0; for out_byte in buf.iter_mut() { diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index 7da1621da45..f00212bfcb6 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -133,12 +133,32 @@ pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { Ok(()) } -/// Gets the id of the thread that invokes it. +/// Gets the unique identifier of the thread which invokes it. +/// +/// Calling this function may be more efficient than accessing the current +/// thread id through the current thread handle. i.e. `thread::current().id()`. /// /// This function will always succeed, will always return the same value for /// one thread and is guaranteed not to call the global allocator. +/// +/// # Examples +/// +/// ``` +/// #![feature(current_thread_id)] +/// +/// use std::thread; +/// +/// let other_thread = thread::spawn(|| { +/// thread::current_id() +/// }); +/// +/// let other_thread_id = other_thread.join().unwrap(); +/// assert_ne!(thread::current_id(), other_thread_id); +/// ``` #[inline] -pub(crate) fn current_id() -> ThreadId { +#[must_use] +#[unstable(feature = "current_thread_id", issue = "147194")] +pub fn current_id() -> ThreadId { // If accessing the persistent thread ID takes multiple TLS accesses, try // to retrieve it from the current thread handle, which will only take one // TLS access. diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 4d09b2b4e9d..1768369792a 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -183,7 +183,9 @@ mod current; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; -pub(crate) use current::{current_id, current_or_unnamed, current_os_id, drop_current}; +#[unstable(feature = "current_thread_id", issue = "147194")] +pub use current::current_id; +pub(crate) use current::{current_or_unnamed, current_os_id, drop_current}; use current::{set_current, try_with_current}; mod spawnhook; |
