summaryrefslogtreecommitdiffstats
path: root/library/std/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/std/src/alloc.rs5
-rw-r--r--library/std/src/backtrace.rs33
-rw-r--r--library/std/src/collections/hash/map.rs14
-rw-r--r--library/std/src/collections/hash/map/tests.rs30
-rw-r--r--library/std/src/collections/hash/set.rs7
-rw-r--r--library/std/src/collections/hash/set/tests.rs6
-rw-r--r--library/std/src/env.rs2
-rw-r--r--library/std/src/error.rs1196
-rw-r--r--library/std/src/f32.rs2
-rw-r--r--library/std/src/f32/tests.rs78
-rw-r--r--library/std/src/f64.rs2
-rw-r--r--library/std/src/f64/tests.rs76
-rw-r--r--library/std/src/ffi/os_str.rs3
-rw-r--r--library/std/src/fs.rs108
-rw-r--r--library/std/src/io/buffered/bufreader.rs22
-rw-r--r--library/std/src/io/buffered/bufreader/buffer.rs27
-rw-r--r--library/std/src/io/buffered/tests.rs58
-rw-r--r--library/std/src/io/copy.rs34
-rw-r--r--library/std/src/io/cursor.rs10
-rw-r--r--library/std/src/io/error.rs16
-rw-r--r--library/std/src/io/error/repr_bitpacked.rs4
-rw-r--r--library/std/src/io/error/tests.rs2
-rw-r--r--library/std/src/io/impls.rs22
-rw-r--r--library/std/src/io/mod.rs100
-rw-r--r--library/std/src/io/readbuf.rs312
-rw-r--r--library/std/src/io/readbuf/tests.rs220
-rw-r--r--library/std/src/io/stdio.rs108
-rw-r--r--library/std/src/io/tests.rs23
-rw-r--r--library/std/src/io/util.rs14
-rw-r--r--library/std/src/io/util/tests.rs48
-rw-r--r--library/std/src/keyword_docs.rs157
-rw-r--r--library/std/src/lazy.rs1
-rw-r--r--library/std/src/lib.rs48
-rw-r--r--library/std/src/macros.rs36
-rw-r--r--library/std/src/net/addr.rs988
-rw-r--r--library/std/src/net/addr/tests.rs237
-rw-r--r--library/std/src/net/display_buffer.rs40
-rw-r--r--library/std/src/net/ip.rs2040
-rw-r--r--library/std/src/net/ip/tests.rs969
-rw-r--r--library/std/src/net/ip_addr.rs2095
-rw-r--r--library/std/src/net/ip_addr/tests.rs1039
-rw-r--r--library/std/src/net/mod.rs13
-rw-r--r--library/std/src/net/parser.rs138
-rw-r--r--library/std/src/net/socket_addr.rs974
-rw-r--r--library/std/src/net/socket_addr/tests.rs306
-rw-r--r--library/std/src/os/android/mod.rs1
-rw-r--r--library/std/src/os/android/net.rs4
-rw-r--r--library/std/src/os/fd/mod.rs13
-rw-r--r--library/std/src/os/fd/owned.rs78
-rw-r--r--library/std/src/os/fd/raw.rs26
-rw-r--r--library/std/src/os/fortanix_sgx/mod.rs5
-rw-r--r--library/std/src/os/linux/mod.rs1
-rw-r--r--library/std/src/os/linux/net.rs4
-rw-r--r--library/std/src/os/mod.rs7
-rw-r--r--library/std/src/os/net/mod.rs7
-rw-r--r--library/std/src/os/net/tcp.rs70
-rw-r--r--library/std/src/os/net/tests.rs29
-rw-r--r--library/std/src/os/unix/io/fd.rs8
-rw-r--r--library/std/src/os/unix/io/mod.rs11
-rw-r--r--library/std/src/os/unix/io/raw.rs6
-rw-r--r--library/std/src/os/unix/io/tests.rs (renamed from library/std/src/os/unix/io/fd/tests.rs)0
-rw-r--r--library/std/src/os/unix/mod.rs2
-rw-r--r--library/std/src/os/unix/net/addr.rs18
-rw-r--r--library/std/src/os/unix/net/datagram.rs25
-rw-r--r--library/std/src/os/unix/net/listener.rs10
-rw-r--r--library/std/src/os/unix/net/stream.rs25
-rw-r--r--library/std/src/os/wasi/io/mod.rs12
-rw-r--r--library/std/src/os/watchos/fs.rs142
-rw-r--r--library/std/src/os/watchos/mod.rs6
-rw-r--r--library/std/src/os/watchos/raw.rs83
-rw-r--r--library/std/src/os/windows/io/handle.rs17
-rw-r--r--library/std/src/panic.rs33
-rw-r--r--library/std/src/panicking.rs160
-rw-r--r--library/std/src/path.rs4
-rw-r--r--library/std/src/path/tests.rs5
-rw-r--r--library/std/src/personality.rs46
-rw-r--r--library/std/src/personality/dwarf/eh.rs (renamed from library/panic_unwind/src/dwarf/eh.rs)9
-rw-r--r--library/std/src/personality/dwarf/mod.rs (renamed from library/panic_unwind/src/dwarf/mod.rs)0
-rw-r--r--library/std/src/personality/dwarf/tests.rs (renamed from library/panic_unwind/src/dwarf/tests.rs)0
-rw-r--r--library/std/src/personality/emcc.rs20
-rw-r--r--library/std/src/personality/gcc.rs279
-rw-r--r--library/std/src/primitive_docs.rs95
-rw-r--r--library/std/src/process.rs28
-rw-r--r--library/std/src/rt.rs28
-rw-r--r--library/std/src/sync/mpsc/mpsc_queue/tests.rs2
-rw-r--r--library/std/src/sync/mpsc/spsc_queue/tests.rs5
-rw-r--r--library/std/src/sync/mpsc/stream.rs2
-rw-r--r--library/std/src/sync/mpsc/sync_tests.rs21
-rw-r--r--library/std/src/sync/mpsc/tests.rs12
-rw-r--r--library/std/src/sync/mutex.rs1
-rw-r--r--library/std/src/sync/once.rs312
-rw-r--r--library/std/src/sync/once_lock.rs55
-rw-r--r--library/std/src/sync/rwlock.rs66
-rw-r--r--library/std/src/sync/rwlock/tests.rs2
-rw-r--r--library/std/src/sys/common/mod.rs4
-rw-r--r--library/std/src/sys/common/small_c_string.rs58
-rw-r--r--library/std/src/sys/common/tests.rs66
-rw-r--r--library/std/src/sys/hermit/args.rs74
-rw-r--r--library/std/src/sys/hermit/condvar.rs90
-rw-r--r--library/std/src/sys/hermit/fs.rs31
-rw-r--r--library/std/src/sys/hermit/futex.rs39
-rw-r--r--library/std/src/sys/hermit/mod.rs21
-rw-r--r--library/std/src/sys/hermit/mutex.rs216
-rw-r--r--library/std/src/sys/hermit/net.rs2
-rw-r--r--library/std/src/sys/hermit/rwlock.rs144
-rw-r--r--library/std/src/sys/itron/mutex.rs6
-rw-r--r--library/std/src/sys/mod.rs2
-rw-r--r--library/std/src/sys/sgx/abi/thread.rs8
-rw-r--r--library/std/src/sys/sgx/abi/tls/mod.rs1
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/alloc.rs183
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/mod.rs8
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/raw.rs24
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/tests.rs34
-rw-r--r--library/std/src/sys/sgx/mod.rs2
-rw-r--r--library/std/src/sys/sgx/mutex.rs3
-rw-r--r--library/std/src/sys/sgx/thread_local_key.rs5
-rw-r--r--library/std/src/sys/solid/fs.rs44
-rw-r--r--library/std/src/sys/solid/mod.rs2
-rw-r--r--library/std/src/sys/solid/os.rs44
-rw-r--r--library/std/src/sys/solid/thread_local_key.rs5
-rw-r--r--library/std/src/sys/unix/fd.rs11
-rw-r--r--library/std/src/sys/unix/fs.rs497
-rw-r--r--library/std/src/sys/unix/io.rs6
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs2
-rw-r--r--library/std/src/sys/unix/locks/fuchsia_mutex.rs5
-rw-r--r--library/std/src/sys/unix/locks/futex_mutex.rs5
-rw-r--r--library/std/src/sys/unix/locks/futex_rwlock.rs2
-rw-r--r--library/std/src/sys/unix/locks/mod.rs6
-rw-r--r--library/std/src/sys/unix/locks/pthread_condvar.rs2
-rw-r--r--library/std/src/sys/unix/locks/pthread_mutex.rs2
-rw-r--r--library/std/src/sys/unix/mod.rs76
-rw-r--r--library/std/src/sys/unix/net.rs22
-rw-r--r--library/std/src/sys/unix/os.rs59
-rw-r--r--library/std/src/sys/unix/os_str.rs40
-rw-r--r--library/std/src/sys/unix/os_str/tests.rs8
-rw-r--r--library/std/src/sys/unix/process/process_common.rs61
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs103
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs2
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs80
-rw-r--r--library/std/src/sys/unix/rand.rs18
-rw-r--r--library/std/src/sys/unix/stdio.rs50
-rw-r--r--library/std/src/sys/unix/thread.rs59
-rw-r--r--library/std/src/sys/unix/thread_local_dtor.rs1
-rw-r--r--library/std/src/sys/unix/thread_local_key.rs5
-rw-r--r--library/std/src/sys/unix/thread_parker.rs281
-rw-r--r--library/std/src/sys/unix/thread_parker/darwin.rs131
-rw-r--r--library/std/src/sys/unix/thread_parker/mod.rs32
-rw-r--r--library/std/src/sys/unix/thread_parker/netbsd.rs113
-rw-r--r--library/std/src/sys/unix/thread_parker/pthread.rs271
-rw-r--r--library/std/src/sys/unix/time.rs34
-rw-r--r--library/std/src/sys/unsupported/alloc.rs7
-rw-r--r--library/std/src/sys/unsupported/common.rs2
-rw-r--r--library/std/src/sys/unsupported/fs.rs4
-rw-r--r--library/std/src/sys/unsupported/io.rs4
-rw-r--r--library/std/src/sys/unsupported/locks/condvar.rs1
-rw-r--r--library/std/src/sys/unsupported/locks/mod.rs2
-rw-r--r--library/std/src/sys/unsupported/locks/mutex.rs4
-rw-r--r--library/std/src/sys/unsupported/locks/rwlock.rs1
-rw-r--r--library/std/src/sys/unsupported/process.rs3
-rw-r--r--library/std/src/sys/unsupported/thread_local_dtor.rs1
-rw-r--r--library/std/src/sys/unsupported/thread_local_key.rs5
-rw-r--r--library/std/src/sys/wasi/fs.rs111
-rw-r--r--library/std/src/sys/wasi/io.rs6
-rw-r--r--library/std/src/sys/wasi/mod.rs3
-rw-r--r--library/std/src/sys/wasi/os.rs81
-rw-r--r--library/std/src/sys/wasi/time.rs4
-rw-r--r--library/std/src/sys/wasm/mod.rs2
-rw-r--r--library/std/src/sys/windows/alloc.rs5
-rw-r--r--library/std/src/sys/windows/c.rs65
-rw-r--r--library/std/src/sys/windows/cmath.rs2
-rw-r--r--library/std/src/sys/windows/compat.rs232
-rw-r--r--library/std/src/sys/windows/fs.rs156
-rw-r--r--library/std/src/sys/windows/handle.rs12
-rw-r--r--library/std/src/sys/windows/io.rs76
-rw-r--r--library/std/src/sys/windows/locks/mod.rs2
-rw-r--r--library/std/src/sys/windows/locks/mutex.rs2
-rw-r--r--library/std/src/sys/windows/mod.rs28
-rw-r--r--library/std/src/sys/windows/os.rs6
-rw-r--r--library/std/src/sys/windows/os_str.rs4
-rw-r--r--library/std/src/sys/windows/path/tests.rs2
-rw-r--r--library/std/src/sys/windows/process.rs6
-rw-r--r--library/std/src/sys/windows/rand.rs117
-rw-r--r--library/std/src/sys/windows/stdio.rs41
-rw-r--r--library/std/src/sys/windows/thread_local_dtor.rs4
-rw-r--r--library/std/src/sys/windows/thread_local_key.rs196
-rw-r--r--library/std/src/sys/windows/thread_local_key/tests.rs53
-rw-r--r--library/std/src/sys/windows/thread_parker.rs22
-rw-r--r--library/std/src/sys_common/backtrace.rs9
-rw-r--r--library/std/src/sys_common/condvar.rs1
-rw-r--r--library/std/src/sys_common/condvar/check.rs1
-rw-r--r--library/std/src/sys_common/mod.rs10
-rw-r--r--library/std/src/sys_common/mutex.rs45
-rw-r--r--library/std/src/sys_common/net.rs28
-rw-r--r--library/std/src/sys_common/once/futex.rs134
-rw-r--r--library/std/src/sys_common/once/generic.rs282
-rw-r--r--library/std/src/sys_common/once/mod.rs43
-rw-r--r--library/std/src/sys_common/remutex.rs46
-rw-r--r--library/std/src/sys_common/remutex/tests.rs37
-rw-r--r--library/std/src/sys_common/rwlock.rs61
-rw-r--r--library/std/src/sys_common/thread_local_key.rs26
-rw-r--r--library/std/src/sys_common/thread_local_key/tests.rs9
-rw-r--r--library/std/src/sys_common/thread_parker/mod.rs1
-rw-r--r--library/std/src/sys_common/wtf8.rs95
-rw-r--r--library/std/src/sys_common/wtf8/tests.rs295
-rw-r--r--library/std/src/thread/local.rs9
-rw-r--r--library/std/src/thread/mod.rs151
-rw-r--r--library/std/src/thread/tests.rs72
-rw-r--r--library/std/src/time.rs6
-rw-r--r--library/std/src/time/tests.rs3
209 files changed, 10542 insertions, 8652 deletions
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs
index a05e0db3a..61c1ff578 100644
--- a/library/std/src/alloc.rs
+++ b/library/std/src/alloc.rs
@@ -68,7 +68,10 @@ pub use alloc_crate::alloc::*;
/// The default memory allocator provided by the operating system.
///
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
-/// plus related functions.
+/// plus related functions. However, it is not valid to mix use of the backing
+/// system allocator with `System`, as this implementation may include extra
+/// work, such as to serve alignment requests greater than the alignment
+/// provided directly by the backing system allocator.
///
/// This type implements the `GlobalAlloc` trait and Rust programs by default
/// work as if they had this definition:
diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs
index 05e9b2eb6..9cb74f951 100644
--- a/library/std/src/backtrace.rs
+++ b/library/std/src/backtrace.rs
@@ -9,19 +9,13 @@
//! implementing `std::error::Error`) to get a causal chain of where an error
//! was generated.
//!
-//! > **Note**: this module is unstable and is designed in [RFC 2504], and you
-//! > can learn more about its status in the [tracking issue].
-//!
-//! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md
-//! [tracking issue]: https://github.com/rust-lang/rust/issues/53487
-//!
//! ## Accuracy
//!
//! Backtraces are attempted to be as accurate as possible, but no guarantees
//! are provided about the exact accuracy of a backtrace. Instruction pointers,
//! symbol names, filenames, line numbers, etc, may all be incorrect when
-//! reported. Accuracy is attempted on a best-effort basis, however, and bugs
-//! are always welcome to indicate areas of improvement!
+//! reported. Accuracy is attempted on a best-effort basis, however, any bug
+//! reports are always welcome to indicate areas of improvement!
//!
//! For most platforms a backtrace with a filename/line number requires that
//! programs be compiled with debug information. Without debug information
@@ -45,7 +39,7 @@
//! default. Its behavior is governed by two environment variables:
//!
//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
-//! will never capture a backtrace. Any other value this is set to will enable
+//! will never capture a backtrace. Any other value set will enable
//! `Backtrace::capture`.
//!
//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable
@@ -64,7 +58,7 @@
//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime might not actually change
//! how backtraces are captured.
-#![unstable(feature = "backtrace", issue = "53487")]
+#![stable(feature = "backtrace", since = "1.65.0")]
#[cfg(test)]
mod tests;
@@ -110,6 +104,7 @@ use crate::vec::Vec;
/// previous point in time. In some instances the `Backtrace` type may
/// internally be empty due to configuration. For more information see
/// `Backtrace::capture`.
+#[stable(feature = "backtrace", since = "1.65.0")]
#[must_use]
pub struct Backtrace {
inner: Inner,
@@ -117,17 +112,21 @@ pub struct Backtrace {
/// The current status of a backtrace, indicating whether it was captured or
/// whether it is empty for some other reason.
+#[stable(feature = "backtrace", since = "1.65.0")]
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub enum BacktraceStatus {
/// Capturing a backtrace is not supported, likely because it's not
/// implemented for the current platform.
+ #[stable(feature = "backtrace", since = "1.65.0")]
Unsupported,
/// Capturing a backtrace has been disabled through either the
/// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables.
+ #[stable(feature = "backtrace", since = "1.65.0")]
Disabled,
/// A backtrace has been captured and the `Backtrace` should print
/// reasonable information when rendered.
+ #[stable(feature = "backtrace", since = "1.65.0")]
Captured,
}
@@ -174,6 +173,7 @@ enum BytesOrWide {
Wide(Vec<u16>),
}
+#[stable(feature = "backtrace", since = "1.65.0")]
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let capture = match &self.inner {
@@ -200,6 +200,7 @@ impl fmt::Debug for Backtrace {
}
}
+#[unstable(feature = "backtrace_frames", issue = "79676")]
impl fmt::Debug for BacktraceFrame {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut dbg = fmt.debug_list();
@@ -288,6 +289,7 @@ impl Backtrace {
///
/// To forcibly capture a backtrace regardless of environment variables, use
/// the `Backtrace::force_capture` function.
+ #[stable(feature = "backtrace", since = "1.65.0")]
#[inline(never)] // want to make sure there's a frame here to remove
pub fn capture() -> Backtrace {
if !Backtrace::enabled() {
@@ -306,6 +308,7 @@ impl Backtrace {
/// Note that capturing a backtrace can be an expensive operation on some
/// platforms, so this should be used with caution in performance-sensitive
/// parts of code.
+ #[stable(feature = "backtrace", since = "1.65.0")]
#[inline(never)] // want to make sure there's a frame here to remove
pub fn force_capture() -> Backtrace {
Backtrace::create(Backtrace::force_capture as usize)
@@ -313,6 +316,8 @@ impl Backtrace {
/// Forcibly captures a disabled backtrace, regardless of environment
/// variable configuration.
+ #[stable(feature = "backtrace", since = "1.65.0")]
+ #[rustc_const_stable(feature = "backtrace", since = "1.65.0")]
pub const fn disabled() -> Backtrace {
Backtrace { inner: Inner::Disabled }
}
@@ -320,8 +325,7 @@ impl Backtrace {
// Capture a backtrace which start just before the function addressed by
// `ip`
fn create(ip: usize) -> Backtrace {
- // SAFETY: We don't attempt to lock this reentrantly.
- let _lock = unsafe { lock() };
+ let _lock = lock();
let mut frames = Vec::new();
let mut actual_start = None;
unsafe {
@@ -356,6 +360,7 @@ impl Backtrace {
/// Returns the status of this backtrace, indicating whether this backtrace
/// request was unsupported, disabled, or a stack trace was actually
/// captured.
+ #[stable(feature = "backtrace", since = "1.65.0")]
#[must_use]
pub fn status(&self) -> BacktraceStatus {
match self.inner {
@@ -375,6 +380,7 @@ impl<'a> Backtrace {
}
}
+#[stable(feature = "backtrace", since = "1.65.0")]
impl fmt::Display for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let capture = match &self.inner {
@@ -462,8 +468,7 @@ impl Capture {
// Use the global backtrace lock to synchronize this as it's a
// requirement of the `backtrace` crate, and then actually resolve
// everything.
- // SAFETY: We don't attempt to lock this reentrantly.
- let _lock = unsafe { lock() };
+ let _lock = lock();
for frame in self.frames.iter_mut() {
let symbols = &mut frame.symbols;
let frame = match &frame.frame {
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index db811343f..708edc5de 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -9,6 +9,7 @@ use crate::borrow::Borrow;
use crate::cell::Cell;
use crate::collections::TryReserveError;
use crate::collections::TryReserveErrorKind;
+use crate::error::Error;
use crate::fmt::{self, Debug};
#[allow(deprecated)]
use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
@@ -279,7 +280,8 @@ impl<K, V, S> HashMap<K, V, S> {
/// ```
#[inline]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
- pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
+ #[rustc_const_unstable(feature = "const_collections_with_hasher", issue = "102575")]
+ pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
HashMap { base: base::HashMap::with_hasher(hash_builder) }
}
@@ -757,7 +759,7 @@ where
/// Tries to reserve capacity for at least `additional` more elements to be inserted
/// in the `HashMap`. The collection may reserve more space to speculatively
- /// avoid frequent reallocations. After calling `reserve`,
+ /// avoid frequent reallocations. After calling `try_reserve`,
/// capacity will be greater than or equal to `self.len() + additional` if
/// it returns `Ok(())`.
/// Does nothing if capacity is already sufficient.
@@ -2158,6 +2160,14 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> {
}
}
+#[unstable(feature = "map_try_insert", issue = "82766")]
+impl<'a, K: fmt::Debug, V: fmt::Debug> Error for OccupiedError<'a, K, V> {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "key already exists"
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> {
type Item = (&'a K, &'a V);
diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs
index 7ebc41588..65634f206 100644
--- a/library/std/src/collections/hash/map/tests.rs
+++ b/library/std/src/collections/hash/map/tests.rs
@@ -268,10 +268,13 @@ fn test_lots_of_insertions() {
// Try this a few times to make sure we never screw up the hashmap's
// internal state.
- for _ in 0..10 {
+ let loops = if cfg!(miri) { 2 } else { 10 };
+ for _ in 0..loops {
assert!(m.is_empty());
- for i in 1..1001 {
+ let count = if cfg!(miri) { 101 } else { 1001 };
+
+ for i in 1..count {
assert!(m.insert(i, i).is_none());
for j in 1..=i {
@@ -279,42 +282,42 @@ fn test_lots_of_insertions() {
assert_eq!(r, Some(&j));
}
- for j in i + 1..1001 {
+ for j in i + 1..count {
let r = m.get(&j);
assert_eq!(r, None);
}
}
- for i in 1001..2001 {
+ for i in count..(2 * count) {
assert!(!m.contains_key(&i));
}
// remove forwards
- for i in 1..1001 {
+ for i in 1..count {
assert!(m.remove(&i).is_some());
for j in 1..=i {
assert!(!m.contains_key(&j));
}
- for j in i + 1..1001 {
+ for j in i + 1..count {
assert!(m.contains_key(&j));
}
}
- for i in 1..1001 {
+ for i in 1..count {
assert!(!m.contains_key(&i));
}
- for i in 1..1001 {
+ for i in 1..count {
assert!(m.insert(i, i).is_none());
}
// remove backwards
- for i in (1..1001).rev() {
+ for i in (1..count).rev() {
assert!(m.remove(&i).is_some());
- for j in i..1001 {
+ for j in i..count {
assert!(!m.contains_key(&j));
}
@@ -817,6 +820,7 @@ fn test_retain() {
}
#[test]
+#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
fn test_try_reserve() {
let mut empty_bytes: HashMap<u8, u8> = HashMap::new();
@@ -1111,3 +1115,9 @@ fn from_array() {
// that's a problem!
let _must_not_require_type_annotation = HashMap::from([(1, 2)]);
}
+
+#[test]
+fn const_with_hasher() {
+ const X: HashMap<(), (), ()> = HashMap::with_hasher(());
+ assert_eq!(X.len(), 0);
+}
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index abff82788..cee884145 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -239,7 +239,7 @@ impl<T, S> HashSet<T, S> {
///
/// If the returned iterator is dropped before being fully consumed, it
/// drops the remaining elements. The returned iterator keeps a mutable
- /// borrow on the vector to optimize its implementation.
+ /// borrow on the set to optimize its implementation.
///
/// # Examples
///
@@ -376,7 +376,8 @@ impl<T, S> HashSet<T, S> {
/// ```
#[inline]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
- pub fn with_hasher(hasher: S) -> HashSet<T, S> {
+ #[rustc_const_unstable(feature = "const_collections_with_hasher", issue = "102575")]
+ pub const fn with_hasher(hasher: S) -> HashSet<T, S> {
HashSet { base: base::HashSet::with_hasher(hasher) }
}
@@ -461,7 +462,7 @@ where
/// Tries to reserve capacity for at least `additional` more elements to be inserted
/// in the `HashSet`. The collection may reserve more space to speculatively
- /// avoid frequent reallocations. After calling `reserve`,
+ /// avoid frequent reallocations. After calling `try_reserve`,
/// capacity will be greater than or equal to `self.len() + additional` if
/// it returns `Ok(())`.
/// Does nothing if capacity is already sufficient.
diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs
index 233db276b..941a0450c 100644
--- a/library/std/src/collections/hash/set/tests.rs
+++ b/library/std/src/collections/hash/set/tests.rs
@@ -496,3 +496,9 @@ fn from_array() {
// that's a problem!
let _must_not_require_type_annotation = HashSet::from([1, 2]);
}
+
+#[test]
+fn const_with_hasher() {
+ const X: HashSet<(), ()> = HashSet::with_hasher(());
+ assert_eq!(X.len(), 0);
+}
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 463f71406..6eb7cbea6 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -603,7 +603,7 @@ pub fn home_dir() -> Option<PathBuf> {
/// # Platform-specific behavior
///
/// On Unix, returns the value of the `TMPDIR` environment variable if it is
-/// set, otherwise for non-Android it returns `/tmp`. If Android, since there
+/// set, otherwise for non-Android it returns `/tmp`. On Android, since there
/// is no global temporary folder (it is usually allocated per-app), it returns
/// `/data/local/tmp`.
/// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] /
diff --git a/library/std/src/error.rs b/library/std/src/error.rs
index 722df119d..05f8fd8de 100644
--- a/library/std/src/error.rs
+++ b/library/std/src/error.rs
@@ -1,366 +1,14 @@
-//! Interfaces for working with Errors.
-//!
-//! # Error Handling In Rust
-//!
-//! The Rust language provides two complementary systems for constructing /
-//! representing, reporting, propagating, reacting to, and discarding errors.
-//! These responsibilities are collectively known as "error handling." The
-//! components of the first system, the panic runtime and interfaces, are most
-//! commonly used to represent bugs that have been detected in your program. The
-//! components of the second system, `Result`, the error traits, and user
-//! defined types, are used to represent anticipated runtime failure modes of
-//! your program.
-//!
-//! ## The Panic Interfaces
-//!
-//! The following are the primary interfaces of the panic system and the
-//! responsibilities they cover:
-//!
-//! * [`panic!`] and [`panic_any`] (Constructing, Propagated automatically)
-//! * [`PanicInfo`] (Reporting)
-//! * [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting)
-//! * [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating)
-//!
-//! The following are the primary interfaces of the error system and the
-//! responsibilities they cover:
-//!
-//! * [`Result`] (Propagating, Reacting)
-//! * The [`Error`] trait (Reporting)
-//! * User defined types (Constructing / Representing)
-//! * [`match`] and [`downcast`] (Reacting)
-//! * The question mark operator ([`?`]) (Propagating)
-//! * The partially stable [`Try`] traits (Propagating, Constructing)
-//! * [`Termination`] (Reporting)
-//!
-//! ## Converting Errors into Panics
-//!
-//! The panic and error systems are not entirely distinct. Often times errors
-//! that are anticipated runtime failures in an API might instead represent bugs
-//! to a caller. For these situations the standard library provides APIs for
-//! constructing panics with an `Error` as it's source.
-//!
-//! * [`Result::unwrap`]
-//! * [`Result::expect`]
-//!
-//! These functions are equivalent, they either return the inner value if the
-//! `Result` is `Ok` or panic if the `Result` is `Err` printing the inner error
-//! as the source. The only difference between them is that with `expect` you
-//! provide a panic error message to be printed alongside the source, whereas
-//! `unwrap` has a default message indicating only that you unwraped an `Err`.
-//!
-//! Of the two, `expect` is generally preferred since its `msg` field allows you
-//! to convey your intent and assumptions which makes tracking down the source
-//! of a panic easier. `unwrap` on the other hand can still be a good fit in
-//! situations where you can trivially show that a piece of code will never
-//! panic, such as `"127.0.0.1".parse::<std::net::IpAddr>().unwrap()` or early
-//! prototyping.
-//!
-//! # Common Message Styles
-//!
-//! There are two common styles for how people word `expect` messages. Using
-//! the message to present information to users encountering a panic
-//! ("expect as error message") or using the message to present information
-//! to developers debugging the panic ("expect as precondition").
-//!
-//! In the former case the expect message is used to describe the error that
-//! has occurred which is considered a bug. Consider the following example:
-//!
-//! ```should_panic
-//! // Read environment variable, panic if it is not present
-//! let path = std::env::var("IMPORTANT_PATH").unwrap();
-//! ```
-//!
-//! In the "expect as error message" style we would use expect to describe
-//! that the environment variable was not set when it should have been:
-//!
-//! ```should_panic
-//! let path = std::env::var("IMPORTANT_PATH")
-//! .expect("env variable `IMPORTANT_PATH` is not set");
-//! ```
-//!
-//! In the "expect as precondition" style, we would instead describe the
-//! reason we _expect_ the `Result` should be `Ok`. With this style we would
-//! prefer to write:
-//!
-//! ```should_panic
-//! let path = std::env::var("IMPORTANT_PATH")
-//! .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`");
-//! ```
-//!
-//! The "expect as error message" style does not work as well with the
-//! default output of the std panic hooks, and often ends up repeating
-//! information that is already communicated by the source error being
-//! unwrapped:
-//!
-//! ```text
-//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` is not set: NotPresent', src/main.rs:4:6
-//! ```
-//!
-//! In this example we end up mentioning that an env variable is not set,
-//! followed by our source message that says the env is not present, the
-//! only additional information we're communicating is the name of the
-//! environment variable being checked.
-//!
-//! The "expect as precondition" style instead focuses on source code
-//! readability, making it easier to understand what must have gone wrong in
-//! situations where panics are being used to represent bugs exclusively.
-//! Also, by framing our expect in terms of what "SHOULD" have happened to
-//! prevent the source error, we end up introducing new information that is
-//! independent from our source error.
-//!
-//! ```text
-//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`: NotPresent', src/main.rs:4:6
-//! ```
-//!
-//! In this example we are communicating not only the name of the
-//! environment variable that should have been set, but also an explanation
-//! for why it should have been set, and we let the source error display as
-//! a clear contradiction to our expectation.
-//!
-//! **Hint**: If you're having trouble remembering how to phrase
-//! expect-as-precondition style error messages remember to focus on the word
-//! "should" as in "env variable should be set by blah" or "the given binary
-//! should be available and executable by the current user".
-//!
-//! [`panic_any`]: crate::panic::panic_any
-//! [`PanicInfo`]: crate::panic::PanicInfo
-//! [`catch_unwind`]: crate::panic::catch_unwind
-//! [`resume_unwind`]: crate::panic::resume_unwind
-//! [`downcast`]: crate::error::Error
-//! [`Termination`]: crate::process::Termination
-//! [`Try`]: crate::ops::Try
-//! [panic hook]: crate::panic::set_hook
-//! [`set_hook`]: crate::panic::set_hook
-//! [`take_hook`]: crate::panic::take_hook
-//! [panic-handler]: <https://doc.rust-lang.org/nomicon/panic-handler.html>
-//! [`match`]: ../../std/keyword.match.html
-//! [`?`]: ../../std/result/index.html#the-question-mark-operator-
-
+#![doc = include_str!("../../core/src/error.md")]
#![stable(feature = "rust1", since = "1.0.0")]
-// A note about crates and the facade:
-//
-// Originally, the `Error` trait was defined in libcore, and the impls
-// were scattered about. However, coherence objected to this
-// arrangement, because to create the blanket impls for `Box` required
-// knowing that `&str: !Error`, and we have no means to deal with that
-// sort of conflict just now. Therefore, for the time being, we have
-// moved the `Error` trait into libstd. As we evolve a sol'n to the
-// coherence challenge (e.g., specialization, neg impls, etc) we can
-// reconsider what crate these items belong in.
-
#[cfg(test)]
mod tests;
-use core::array;
-use core::convert::Infallible;
-
-use crate::alloc::{AllocError, LayoutError};
-use crate::any::{Demand, Provider, TypeId};
use crate::backtrace::Backtrace;
-use crate::borrow::Cow;
-use crate::cell;
-use crate::char;
-use crate::fmt::{self, Debug, Display, Write};
-use crate::io;
-use crate::mem::transmute;
-use crate::num;
-use crate::str;
-use crate::string;
-use crate::sync::Arc;
-use crate::time;
+use crate::fmt::{self, Write};
-/// `Error` is a trait representing the basic expectations for error values,
-/// i.e., values of type `E` in [`Result<T, E>`].
-///
-/// Errors must describe themselves through the [`Display`] and [`Debug`]
-/// traits. Error messages are typically concise lowercase sentences without
-/// trailing punctuation:
-///
-/// ```
-/// let err = "NaN".parse::<u32>().unwrap_err();
-/// assert_eq!(err.to_string(), "invalid digit found in string");
-/// ```
-///
-/// Errors may provide cause chain information. [`Error::source()`] is generally
-/// used when errors cross "abstraction boundaries". If one module must report
-/// an error that is caused by an error from a lower-level module, it can allow
-/// accessing that error via [`Error::source()`]. This makes it possible for the
-/// high-level module to provide its own errors while also revealing some of the
-/// implementation for debugging via `source` chains.
#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
-pub trait Error: Debug + Display {
- /// The lower-level source of this error, if any.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::fmt;
- ///
- /// #[derive(Debug)]
- /// struct SuperError {
- /// source: SuperErrorSideKick,
- /// }
- ///
- /// impl fmt::Display for SuperError {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "SuperError is here!")
- /// }
- /// }
- ///
- /// impl Error for SuperError {
- /// fn source(&self) -> Option<&(dyn Error + 'static)> {
- /// Some(&self.source)
- /// }
- /// }
- ///
- /// #[derive(Debug)]
- /// struct SuperErrorSideKick;
- ///
- /// impl fmt::Display for SuperErrorSideKick {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "SuperErrorSideKick is here!")
- /// }
- /// }
- ///
- /// impl Error for SuperErrorSideKick {}
- ///
- /// fn get_super_error() -> Result<(), SuperError> {
- /// Err(SuperError { source: SuperErrorSideKick })
- /// }
- ///
- /// fn main() {
- /// match get_super_error() {
- /// Err(e) => {
- /// println!("Error: {e}");
- /// println!("Caused by: {}", e.source().unwrap());
- /// }
- /// _ => println!("No error"),
- /// }
- /// }
- /// ```
- #[stable(feature = "error_source", since = "1.30.0")]
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- None
- }
-
- /// Gets the `TypeId` of `self`.
- #[doc(hidden)]
- #[unstable(
- feature = "error_type_id",
- reason = "this is memory-unsafe to override in user code",
- issue = "60784"
- )]
- fn type_id(&self, _: private::Internal) -> TypeId
- where
- Self: 'static,
- {
- TypeId::of::<Self>()
- }
-
- /// ```
- /// if let Err(e) = "xc".parse::<u32>() {
- /// // Print `e` itself, no need for description().
- /// eprintln!("Error: {e}");
- /// }
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- #[deprecated(since = "1.42.0", note = "use the Display impl or to_string()")]
- fn description(&self) -> &str {
- "description() is deprecated; use Display"
- }
-
- #[stable(feature = "rust1", since = "1.0.0")]
- #[deprecated(
- since = "1.33.0",
- note = "replaced by Error::source, which can support downcasting"
- )]
- #[allow(missing_docs)]
- fn cause(&self) -> Option<&dyn Error> {
- self.source()
- }
-
- /// Provides type based access to context intended for error reports.
- ///
- /// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract
- /// references to member variables from `dyn Error` trait objects.
- ///
- /// # Example
- ///
- /// ```rust
- /// #![feature(provide_any)]
- /// #![feature(error_generic_member_access)]
- /// use core::fmt;
- /// use core::any::Demand;
- ///
- /// #[derive(Debug)]
- /// struct MyBacktrace {
- /// // ...
- /// }
- ///
- /// impl MyBacktrace {
- /// fn new() -> MyBacktrace {
- /// // ...
- /// # MyBacktrace {}
- /// }
- /// }
- ///
- /// #[derive(Debug)]
- /// struct SourceError {
- /// // ...
- /// }
- ///
- /// impl fmt::Display for SourceError {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "Example Source Error")
- /// }
- /// }
- ///
- /// impl std::error::Error for SourceError {}
- ///
- /// #[derive(Debug)]
- /// struct Error {
- /// source: SourceError,
- /// backtrace: MyBacktrace,
- /// }
- ///
- /// impl fmt::Display for Error {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "Example Error")
- /// }
- /// }
- ///
- /// impl std::error::Error for Error {
- /// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
- /// req
- /// .provide_ref::<MyBacktrace>(&self.backtrace)
- /// .provide_ref::<dyn std::error::Error + 'static>(&self.source);
- /// }
- /// }
- ///
- /// fn main() {
- /// let backtrace = MyBacktrace::new();
- /// let source = SourceError {};
- /// let error = Error { source, backtrace };
- /// let dyn_error = &error as &dyn std::error::Error;
- /// let backtrace_ref = dyn_error.request_ref::<MyBacktrace>().unwrap();
- ///
- /// assert!(core::ptr::eq(&error.backtrace, backtrace_ref));
- /// }
- /// ```
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- #[allow(unused_variables)]
- fn provide<'a>(&'a self, req: &mut Demand<'a>) {}
-}
-
-#[unstable(feature = "error_generic_member_access", issue = "99301")]
-impl<'b> Provider for dyn Error + 'b {
- fn provide<'a>(&'a self, req: &mut Demand<'a>) {
- self.provide(req)
- }
-}
+pub use core::error::Error;
mod private {
// This is a hack to prevent `type_id` from being overridden by `Error`
@@ -370,742 +18,10 @@ mod private {
pub struct Internal;
}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
- /// Converts a type of [`Error`] into a box of dyn [`Error`].
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::fmt;
- /// use std::mem;
- ///
- /// #[derive(Debug)]
- /// struct AnError;
- ///
- /// impl fmt::Display for AnError {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "An error")
- /// }
- /// }
- ///
- /// impl Error for AnError {}
- ///
- /// let an_error = AnError;
- /// assert!(0 == mem::size_of_val(&an_error));
- /// let a_boxed_error = Box::<dyn Error>::from(an_error);
- /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- fn from(err: E) -> Box<dyn Error + 'a> {
- Box::new(err)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a> {
- /// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of
- /// dyn [`Error`] + [`Send`] + [`Sync`].
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::fmt;
- /// use std::mem;
- ///
- /// #[derive(Debug)]
- /// struct AnError;
- ///
- /// impl fmt::Display for AnError {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "An error")
- /// }
- /// }
- ///
- /// impl Error for AnError {}
- ///
- /// unsafe impl Send for AnError {}
- ///
- /// unsafe impl Sync for AnError {}
- ///
- /// let an_error = AnError;
- /// assert!(0 == mem::size_of_val(&an_error));
- /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(an_error);
- /// assert!(
- /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- fn from(err: E) -> Box<dyn Error + Send + Sync + 'a> {
- Box::new(err)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl From<String> for Box<dyn Error + Send + Sync> {
- /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::mem;
- ///
- /// let a_string_error = "a string error".to_string();
- /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_string_error);
- /// assert!(
- /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- #[inline]
- fn from(err: String) -> Box<dyn Error + Send + Sync> {
- struct StringError(String);
-
- impl Error for StringError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- &self.0
- }
- }
-
- impl Display for StringError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- Display::fmt(&self.0, f)
- }
- }
-
- // Purposefully skip printing "StringError(..)"
- impl Debug for StringError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- Debug::fmt(&self.0, f)
- }
- }
-
- Box::new(StringError(err))
- }
-}
-
-#[stable(feature = "string_box_error", since = "1.6.0")]
-impl From<String> for Box<dyn Error> {
- /// Converts a [`String`] into a box of dyn [`Error`].
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::mem;
- ///
- /// let a_string_error = "a string error".to_string();
- /// let a_boxed_error = Box::<dyn Error>::from(a_string_error);
- /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- fn from(str_err: String) -> Box<dyn Error> {
- let err1: Box<dyn Error + Send + Sync> = From::from(str_err);
- let err2: Box<dyn Error> = err1;
- err2
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
- /// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
- ///
- /// [`str`]: prim@str
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::mem;
- ///
- /// let a_str_error = "a str error";
- /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_str_error);
- /// assert!(
- /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- #[inline]
- fn from(err: &str) -> Box<dyn Error + Send + Sync + 'a> {
- From::from(String::from(err))
- }
-}
-
-#[stable(feature = "string_box_error", since = "1.6.0")]
-impl From<&str> for Box<dyn Error> {
- /// Converts a [`str`] into a box of dyn [`Error`].
- ///
- /// [`str`]: prim@str
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::mem;
- ///
- /// let a_str_error = "a str error";
- /// let a_boxed_error = Box::<dyn Error>::from(a_str_error);
- /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- fn from(err: &str) -> Box<dyn Error> {
- From::from(String::from(err))
- }
-}
-
-#[stable(feature = "cow_box_error", since = "1.22.0")]
-impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> {
- /// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::mem;
- /// use std::borrow::Cow;
- ///
- /// let a_cow_str_error = Cow::from("a str error");
- /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_cow_str_error);
- /// assert!(
- /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- fn from(err: Cow<'b, str>) -> Box<dyn Error + Send + Sync + 'a> {
- From::from(String::from(err))
- }
-}
-
-#[stable(feature = "cow_box_error", since = "1.22.0")]
-impl<'a> From<Cow<'a, str>> for Box<dyn Error> {
- /// Converts a [`Cow`] into a box of dyn [`Error`].
- ///
- /// # Examples
- ///
- /// ```
- /// use std::error::Error;
- /// use std::mem;
- /// use std::borrow::Cow;
- ///
- /// let a_cow_str_error = Cow::from("a str error");
- /// let a_boxed_error = Box::<dyn Error>::from(a_cow_str_error);
- /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
- /// ```
- fn from(err: Cow<'a, str>) -> Box<dyn Error> {
- From::from(String::from(err))
- }
-}
-
-#[unstable(feature = "never_type", issue = "35121")]
-impl Error for ! {}
-
-#[unstable(
- feature = "allocator_api",
- reason = "the precise API and guarantees it provides may be tweaked.",
- issue = "32838"
-)]
-impl Error for AllocError {}
-
-#[stable(feature = "alloc_layout", since = "1.28.0")]
-impl Error for LayoutError {}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for str::ParseBoolError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "failed to parse bool"
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for str::Utf8Error {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "invalid utf-8: corrupt contents"
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for num::ParseIntError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.__description()
- }
-}
-
-#[stable(feature = "try_from", since = "1.34.0")]
-impl Error for num::TryFromIntError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.__description()
- }
-}
-
-#[stable(feature = "try_from", since = "1.34.0")]
-impl Error for array::TryFromSliceError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.__description()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for num::ParseFloatError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.__description()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for string::FromUtf8Error {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "invalid utf-8"
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for string::FromUtf16Error {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "invalid utf-16"
- }
-}
-
-#[stable(feature = "str_parse_error2", since = "1.8.0")]
-impl Error for Infallible {
- fn description(&self) -> &str {
- match *self {}
- }
-}
-
-#[stable(feature = "decode_utf16", since = "1.9.0")]
-impl Error for char::DecodeUtf16Error {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "unpaired surrogate found"
- }
-}
-
-#[stable(feature = "u8_from_char", since = "1.59.0")]
-impl Error for char::TryFromCharError {}
-
-#[unstable(feature = "map_try_insert", issue = "82766")]
-impl<'a, K: Debug + Ord, V: Debug> Error
- for crate::collections::btree_map::OccupiedError<'a, K, V>
-{
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "key already exists"
- }
-}
-
-#[unstable(feature = "map_try_insert", issue = "82766")]
-impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedError<'a, K, V> {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "key already exists"
- }
-}
-
-#[stable(feature = "box_error", since = "1.8.0")]
-impl<T: Error> Error for Box<T> {
- #[allow(deprecated, deprecated_in_future)]
- fn description(&self) -> &str {
- Error::description(&**self)
- }
-
- #[allow(deprecated)]
- fn cause(&self) -> Option<&dyn Error> {
- Error::cause(&**self)
- }
-
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- Error::source(&**self)
- }
-}
-
-#[unstable(feature = "thin_box", issue = "92791")]
-impl<T: ?Sized + crate::error::Error> crate::error::Error for crate::boxed::ThinBox<T> {
- fn source(&self) -> Option<&(dyn crate::error::Error + 'static)> {
- use core::ops::Deref;
- self.deref().source()
- }
-}
-
-#[stable(feature = "error_by_ref", since = "1.51.0")]
-impl<'a, T: Error + ?Sized> Error for &'a T {
- #[allow(deprecated, deprecated_in_future)]
- fn description(&self) -> &str {
- Error::description(&**self)
- }
-
- #[allow(deprecated)]
- fn cause(&self) -> Option<&dyn Error> {
- Error::cause(&**self)
- }
-
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- Error::source(&**self)
- }
-
- fn provide<'b>(&'b self, req: &mut Demand<'b>) {
- Error::provide(&**self, req);
- }
-}
-
-#[stable(feature = "arc_error", since = "1.52.0")]
-impl<T: Error + ?Sized> Error for Arc<T> {
- #[allow(deprecated, deprecated_in_future)]
- fn description(&self) -> &str {
- Error::description(&**self)
- }
-
- #[allow(deprecated)]
- fn cause(&self) -> Option<&dyn Error> {
- Error::cause(&**self)
- }
-
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- Error::source(&**self)
- }
-
- fn provide<'a>(&'a self, req: &mut Demand<'a>) {
- Error::provide(&**self, req);
- }
-}
-
-#[stable(feature = "fmt_error", since = "1.11.0")]
-impl Error for fmt::Error {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "an error occurred when formatting an argument"
- }
-}
-
-#[stable(feature = "try_borrow", since = "1.13.0")]
-impl Error for cell::BorrowError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "already mutably borrowed"
- }
-}
-
-#[stable(feature = "try_borrow", since = "1.13.0")]
-impl Error for cell::BorrowMutError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "already borrowed"
- }
-}
-
-#[stable(feature = "try_from", since = "1.34.0")]
-impl Error for char::CharTryFromError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "converted integer out of range for `char`"
- }
-}
-
-#[stable(feature = "char_from_str", since = "1.20.0")]
-impl Error for char::ParseCharError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.__description()
- }
-}
-
-#[stable(feature = "try_reserve", since = "1.57.0")]
-impl Error for alloc::collections::TryReserveError {}
-
-#[unstable(feature = "duration_checked_float", issue = "83400")]
-impl Error for time::FromFloatSecsError {}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Error for alloc::ffi::NulError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "nul byte found in data"
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl From<alloc::ffi::NulError> for io::Error {
- /// Converts a [`alloc::ffi::NulError`] into a [`io::Error`].
- fn from(_: alloc::ffi::NulError) -> io::Error {
- io::const_io_error!(io::ErrorKind::InvalidInput, "data provided contains a nul byte")
- }
-}
-
-#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
-impl Error for core::ffi::FromBytesWithNulError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.__description()
- }
-}
-
-#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
-impl Error for core::ffi::FromBytesUntilNulError {}
-
-#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
-impl Error for alloc::ffi::FromVecWithNulError {}
-
-#[stable(feature = "cstring_into", since = "1.7.0")]
-impl Error for alloc::ffi::IntoStringError {
- #[allow(deprecated)]
- fn description(&self) -> &str {
- "C string contained non-utf8 bytes"
- }
-
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- Some(self.__source())
- }
-}
-
-impl<'a> dyn Error + 'a {
- /// Request a reference of type `T` as context about this error.
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- pub fn request_ref<T: ?Sized + 'static>(&'a self) -> Option<&'a T> {
- core::any::request_ref(self)
- }
-
- /// Request a value of type `T` as context about this error.
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- pub fn request_value<T: 'static>(&'a self) -> Option<T> {
- core::any::request_value(self)
- }
-}
-
-// Copied from `any.rs`.
-impl dyn Error + 'static {
- /// Returns `true` if the inner type is the same as `T`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn is<T: Error + 'static>(&self) -> bool {
- // Get `TypeId` of the type this function is instantiated with.
- let t = TypeId::of::<T>();
-
- // Get `TypeId` of the type in the trait object (`self`).
- let concrete = self.type_id(private::Internal);
-
- // Compare both `TypeId`s on equality.
- t == concrete
- }
-
- /// Returns some reference to the inner value if it is of type `T`, or
- /// `None` if it isn't.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
- if self.is::<T>() {
- unsafe { Some(&*(self as *const dyn Error as *const T)) }
- } else {
- None
- }
- }
-
- /// Returns some mutable reference to the inner value if it is of type `T`, or
- /// `None` if it isn't.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
- if self.is::<T>() {
- unsafe { Some(&mut *(self as *mut dyn Error as *mut T)) }
- } else {
- None
- }
- }
-}
-
-impl dyn Error + 'static + Send {
- /// Forwards to the method defined on the type `dyn Error`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn is<T: Error + 'static>(&self) -> bool {
- <dyn Error + 'static>::is::<T>(self)
- }
-
- /// Forwards to the method defined on the type `dyn Error`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
- <dyn Error + 'static>::downcast_ref::<T>(self)
- }
-
- /// Forwards to the method defined on the type `dyn Error`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
- <dyn Error + 'static>::downcast_mut::<T>(self)
- }
-
- /// Request a reference of type `T` as context about this error.
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
- <dyn Error>::request_ref(self)
- }
-
- /// Request a value of type `T` as context about this error.
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- pub fn request_value<T: 'static>(&self) -> Option<T> {
- <dyn Error>::request_value(self)
- }
-}
-
-impl dyn Error + 'static + Send + Sync {
- /// Forwards to the method defined on the type `dyn Error`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn is<T: Error + 'static>(&self) -> bool {
- <dyn Error + 'static>::is::<T>(self)
- }
-
- /// Forwards to the method defined on the type `dyn Error`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
- <dyn Error + 'static>::downcast_ref::<T>(self)
- }
-
- /// Forwards to the method defined on the type `dyn Error`.
- #[stable(feature = "error_downcast", since = "1.3.0")]
- #[inline]
- pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
- <dyn Error + 'static>::downcast_mut::<T>(self)
- }
-
- /// Request a reference of type `T` as context about this error.
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
- <dyn Error>::request_ref(self)
- }
-
- /// Request a value of type `T` as context about this error.
- #[unstable(feature = "error_generic_member_access", issue = "99301")]
- pub fn request_value<T: 'static>(&self) -> Option<T> {
- <dyn Error>::request_value(self)
- }
-}
-
-impl dyn Error {
- #[inline]
- #[stable(feature = "error_downcast", since = "1.3.0")]
- /// Attempts to downcast the box to a concrete type.
- pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
- if self.is::<T>() {
- unsafe {
- let raw: *mut dyn Error = Box::into_raw(self);
- Ok(Box::from_raw(raw as *mut T))
- }
- } else {
- Err(self)
- }
- }
-
- /// Returns an iterator starting with the current error and continuing with
- /// recursively calling [`Error::source`].
- ///
- /// If you want to omit the current error and only use its sources,
- /// use `skip(1)`.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(error_iter)]
- /// use std::error::Error;
- /// use std::fmt;
- ///
- /// #[derive(Debug)]
- /// struct A;
- ///
- /// #[derive(Debug)]
- /// struct B(Option<Box<dyn Error + 'static>>);
- ///
- /// impl fmt::Display for A {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "A")
- /// }
- /// }
- ///
- /// impl fmt::Display for B {
- /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- /// write!(f, "B")
- /// }
- /// }
- ///
- /// impl Error for A {}
- ///
- /// impl Error for B {
- /// fn source(&self) -> Option<&(dyn Error + 'static)> {
- /// self.0.as_ref().map(|e| e.as_ref())
- /// }
- /// }
- ///
- /// let b = B(Some(Box::new(A)));
- ///
- /// // let err : Box<Error> = b.into(); // or
- /// let err = &b as &(dyn Error);
- ///
- /// let mut iter = err.chain();
- ///
- /// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
- /// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
- /// assert!(iter.next().is_none());
- /// assert!(iter.next().is_none());
- /// ```
- #[unstable(feature = "error_iter", issue = "58520")]
- #[inline]
- pub fn chain(&self) -> Chain<'_> {
- Chain { current: Some(self) }
- }
-}
-
-/// An iterator over an [`Error`] and its sources.
-///
-/// If you want to omit the initial error and only process
-/// its sources, use `skip(1)`.
-#[unstable(feature = "error_iter", issue = "58520")]
-#[derive(Clone, Debug)]
-pub struct Chain<'a> {
- current: Option<&'a (dyn Error + 'static)>,
-}
-
-#[unstable(feature = "error_iter", issue = "58520")]
-impl<'a> Iterator for Chain<'a> {
- type Item = &'a (dyn Error + 'static);
-
- fn next(&mut self) -> Option<Self::Item> {
- let current = self.current;
- self.current = self.current.and_then(Error::source);
- current
- }
-}
-
-impl dyn Error + Send {
- #[inline]
- #[stable(feature = "error_downcast", since = "1.3.0")]
- /// Attempts to downcast the box to a concrete type.
- pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
- let err: Box<dyn Error> = self;
- <dyn Error>::downcast(err).map_err(|s| unsafe {
- // Reapply the `Send` marker.
- transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s)
- })
- }
-}
-
-impl dyn Error + Send + Sync {
- #[inline]
- #[stable(feature = "error_downcast", since = "1.3.0")]
- /// Attempts to downcast the box to a concrete type.
- pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
- let err: Box<dyn Error> = self;
- <dyn Error>::downcast(err).map_err(|s| unsafe {
- // Reapply the `Send + Sync` marker.
- transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s)
- })
- }
-}
-
/// An error reporter that prints an error and its sources.
///
-/// Report also exposes configuration options for formatting the error chain, either entirely on a
-/// single line, or in multi-line format with each cause in the error chain on a new line.
+/// Report also exposes configuration options for formatting the error sources, either entirely on a
+/// single line, or in multi-line format with each source on a new line.
///
/// `Report` only requires that the wrapped error implement `Error`. It doesn't require that the
/// wrapped error be `Send`, `Sync`, or `'static`.
@@ -1246,7 +162,7 @@ impl dyn Error + Send + Sync {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
-/// fn main() -> Result<(), Report> {
+/// fn main() -> Result<(), Report<SuperError>> {
/// get_super_error()?;
/// Ok(())
/// }
@@ -1293,7 +209,7 @@ impl dyn Error + Send + Sync {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
-/// fn main() -> Result<(), Report> {
+/// fn main() -> Result<(), Report<SuperError>> {
/// get_super_error()
/// .map_err(Report::from)
/// .map_err(|r| r.pretty(true).show_backtrace(true))?;
@@ -1450,11 +366,10 @@ impl<E> Report<E> {
///
/// **Note**: Report will search for the first `Backtrace` it can find starting from the
/// outermost error. In this example it will display the backtrace from the second error in the
- /// chain, `SuperErrorSideKick`.
+ /// sources, `SuperErrorSideKick`.
///
/// ```rust
/// #![feature(error_reporter)]
- /// #![feature(backtrace)]
/// #![feature(provide_any)]
/// #![feature(error_generic_member_access)]
/// # use std::error::Error;
@@ -1489,9 +404,8 @@ impl<E> Report<E> {
/// }
///
/// impl Error for SuperErrorSideKick {
- /// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
- /// req
- /// .provide_ref::<Backtrace>(&self.backtrace);
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// demand.provide_ref::<Backtrace>(&self.backtrace);
/// }
/// }
///
@@ -1548,73 +462,7 @@ where
let backtrace = backtrace.or_else(|| {
self.error
.source()
- .map(|source| source.chain().find_map(|source| source.request_ref()))
- .flatten()
- });
- backtrace
- }
-
- /// Format the report as a single line.
- #[unstable(feature = "error_reporter", issue = "90172")]
- fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.error)?;
-
- let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
-
- for cause in sources {
- write!(f, ": {cause}")?;
- }
-
- Ok(())
- }
-
- /// Format the report as multiple lines, with each error cause on its own line.
- #[unstable(feature = "error_reporter", issue = "90172")]
- fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let error = &self.error;
-
- write!(f, "{error}")?;
-
- if let Some(cause) = error.source() {
- write!(f, "\n\nCaused by:")?;
-
- let multiple = cause.source().is_some();
-
- for (ind, error) in cause.chain().enumerate() {
- writeln!(f)?;
- let mut indented = Indented { inner: f };
- if multiple {
- write!(indented, "{ind: >4}: {error}")?;
- } else {
- write!(indented, " {error}")?;
- }
- }
- }
-
- if self.show_backtrace {
- let backtrace = self.backtrace();
-
- if let Some(backtrace) = backtrace {
- let backtrace = backtrace.to_string();
-
- f.write_str("\n\nStack backtrace:\n")?;
- f.write_str(backtrace.trim_end())?;
- }
- }
-
- Ok(())
- }
-}
-
-impl Report<Box<dyn Error>> {
- fn backtrace(&self) -> Option<&Backtrace> {
- // have to grab the backtrace on the first error directly since that error may not be
- // 'static
- let backtrace = self.error.request_ref();
- let backtrace = backtrace.or_else(|| {
- self.error
- .source()
- .map(|source| source.chain().find_map(|source| source.request_ref()))
+ .map(|source| source.sources().find_map(|source| source.request_ref()))
.flatten()
});
backtrace
@@ -1625,7 +473,7 @@ impl Report<Box<dyn Error>> {
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.error)?;
- let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
+ let sources = self.error.source().into_iter().flat_map(<dyn Error>::sources);
for cause in sources {
write!(f, ": {cause}")?;
@@ -1646,7 +494,7 @@ impl Report<Box<dyn Error>> {
let multiple = cause.source().is_some();
- for (ind, error) in cause.chain().enumerate() {
+ for (ind, error) in cause.sources().enumerate() {
writeln!(f)?;
let mut indented = Indented { inner: f };
if multiple {
@@ -1683,17 +531,6 @@ where
}
#[unstable(feature = "error_reporter", issue = "90172")]
-impl<'a, E> From<E> for Report<Box<dyn Error + 'a>>
-where
- E: Error + 'a,
-{
- fn from(error: E) -> Self {
- let error = box error;
- Report { error, show_backtrace: false, pretty: false }
- }
-}
-
-#[unstable(feature = "error_reporter", issue = "90172")]
impl<E> fmt::Display for Report<E>
where
E: Error,
@@ -1703,13 +540,6 @@ where
}
}
-#[unstable(feature = "error_reporter", issue = "90172")]
-impl fmt::Display for Report<Box<dyn Error>> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
- }
-}
-
// This type intentionally outputs the same format for `Display` and `Debug`for
// situations where you unwrap a `Report` or return it from main.
#[unstable(feature = "error_reporter", issue = "90172")]
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index 933b52b4d..3dd5b1250 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -1,4 +1,4 @@
-//! Constants specific to the `f32` single-precision floating point type.
+//! Constants for the `f32` single-precision floating point type.
//!
//! *[See also the `f32` primitive type](primitive@f32).*
//!
diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs
index 69fa203ff..4ec16c84a 100644
--- a/library/std/src/f32/tests.rs
+++ b/library/std/src/f32/tests.rs
@@ -299,6 +299,84 @@ fn test_is_sign_negative() {
assert!((-f32::NAN).is_sign_negative());
}
+#[allow(unused_macros)]
+macro_rules! assert_f32_biteq {
+ ($left : expr, $right : expr) => {
+ let l: &f32 = &$left;
+ let r: &f32 = &$right;
+ let lb = l.to_bits();
+ let rb = r.to_bits();
+ assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
+ };
+}
+
+// Ignore test on x87 floating point, these platforms do not guarantee NaN
+// payloads are preserved and flush denormals to zero, failing the tests.
+#[cfg(not(target_arch = "x86"))]
+#[test]
+fn test_next_up() {
+ let tiny = f32::from_bits(1);
+ let tiny_up = f32::from_bits(2);
+ let max_down = f32::from_bits(0x7f7f_fffe);
+ let largest_subnormal = f32::from_bits(0x007f_ffff);
+ let smallest_normal = f32::from_bits(0x0080_0000);
+ assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
+ assert_f32_biteq!(f32::MIN.next_up(), -max_down);
+ assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);
+ assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal);
+ assert_f32_biteq!((-tiny_up).next_up(), -tiny);
+ assert_f32_biteq!((-tiny).next_up(), -0.0f32);
+ assert_f32_biteq!((-0.0f32).next_up(), tiny);
+ assert_f32_biteq!(0.0f32.next_up(), tiny);
+ assert_f32_biteq!(tiny.next_up(), tiny_up);
+ assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal);
+ assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
+ assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY);
+ assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY);
+
+ // Check that NaNs roundtrip.
+ let nan0 = f32::NAN;
+ let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
+ let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
+ assert_f32_biteq!(nan0.next_up(), nan0);
+ assert_f32_biteq!(nan1.next_up(), nan1);
+ assert_f32_biteq!(nan2.next_up(), nan2);
+}
+
+// Ignore test on x87 floating point, these platforms do not guarantee NaN
+// payloads are preserved and flush denormals to zero, failing the tests.
+#[cfg(not(target_arch = "x86"))]
+#[test]
+fn test_next_down() {
+ let tiny = f32::from_bits(1);
+ let tiny_up = f32::from_bits(2);
+ let max_down = f32::from_bits(0x7f7f_fffe);
+ let largest_subnormal = f32::from_bits(0x007f_ffff);
+ let smallest_normal = f32::from_bits(0x0080_0000);
+ assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
+ assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY);
+ assert_f32_biteq!((-max_down).next_down(), f32::MIN);
+ assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON);
+ assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal);
+ assert_f32_biteq!((-tiny).next_down(), -tiny_up);
+ assert_f32_biteq!((-0.0f32).next_down(), -tiny);
+ assert_f32_biteq!((0.0f32).next_down(), -tiny);
+ assert_f32_biteq!(tiny.next_down(), 0.0f32);
+ assert_f32_biteq!(tiny_up.next_down(), tiny);
+ assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal);
+ assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32);
+ assert_f32_biteq!(f32::MAX.next_down(), max_down);
+ assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX);
+
+ // Check that NaNs roundtrip.
+ let nan0 = f32::NAN;
+ let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
+ let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
+ assert_f32_biteq!(nan0.next_down(), nan0);
+ assert_f32_biteq!(nan1.next_down(), nan1);
+ assert_f32_biteq!(nan2.next_down(), nan2);
+}
+
#[test]
fn test_mul_add() {
let nan: f32 = f32::NAN;
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index a9aa84f70..31351a879 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -1,4 +1,4 @@
-//! Constants specific to the `f64` double-precision floating point type.
+//! Constants for the `f64` double-precision floating point type.
//!
//! *[See also the `f64` primitive type](primitive@f64).*
//!
diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs
index 5c163cfe9..12baa68f4 100644
--- a/library/std/src/f64/tests.rs
+++ b/library/std/src/f64/tests.rs
@@ -289,6 +289,82 @@ fn test_is_sign_negative() {
assert!((-f64::NAN).is_sign_negative());
}
+#[allow(unused_macros)]
+macro_rules! assert_f64_biteq {
+ ($left : expr, $right : expr) => {
+ let l: &f64 = &$left;
+ let r: &f64 = &$right;
+ let lb = l.to_bits();
+ let rb = r.to_bits();
+ assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
+ };
+}
+
+// Ignore test on x87 floating point, these platforms do not guarantee NaN
+// payloads are preserved and flush denormals to zero, failing the tests.
+#[cfg(not(target_arch = "x86"))]
+#[test]
+fn test_next_up() {
+ let tiny = f64::from_bits(1);
+ let tiny_up = f64::from_bits(2);
+ let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
+ let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
+ let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
+ assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN);
+ assert_f64_biteq!(f64::MIN.next_up(), -max_down);
+ assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0);
+ assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal);
+ assert_f64_biteq!((-tiny_up).next_up(), -tiny);
+ assert_f64_biteq!((-tiny).next_up(), -0.0f64);
+ assert_f64_biteq!((-0.0f64).next_up(), tiny);
+ assert_f64_biteq!(0.0f64.next_up(), tiny);
+ assert_f64_biteq!(tiny.next_up(), tiny_up);
+ assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal);
+ assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
+ assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY);
+ assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY);
+
+ let nan0 = f64::NAN;
+ let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
+ let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
+ assert_f64_biteq!(nan0.next_up(), nan0);
+ assert_f64_biteq!(nan1.next_up(), nan1);
+ assert_f64_biteq!(nan2.next_up(), nan2);
+}
+
+// Ignore test on x87 floating point, these platforms do not guarantee NaN
+// payloads are preserved and flush denormals to zero, failing the tests.
+#[cfg(not(target_arch = "x86"))]
+#[test]
+fn test_next_down() {
+ let tiny = f64::from_bits(1);
+ let tiny_up = f64::from_bits(2);
+ let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
+ let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
+ let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
+ assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY);
+ assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY);
+ assert_f64_biteq!((-max_down).next_down(), f64::MIN);
+ assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON);
+ assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal);
+ assert_f64_biteq!((-tiny).next_down(), -tiny_up);
+ assert_f64_biteq!((-0.0f64).next_down(), -tiny);
+ assert_f64_biteq!((0.0f64).next_down(), -tiny);
+ assert_f64_biteq!(tiny.next_down(), 0.0f64);
+ assert_f64_biteq!(tiny_up.next_down(), tiny);
+ assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal);
+ assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64);
+ assert_f64_biteq!(f64::MAX.next_down(), max_down);
+ assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX);
+
+ let nan0 = f64::NAN;
+ let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
+ let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
+ assert_f64_biteq!(nan0.next_down(), nan0);
+ assert_f64_biteq!(nan1.next_down(), nan1);
+ assert_f64_biteq!(nan2.next_down(), nan2);
+}
+
#[test]
fn test_mul_add() {
let nan: f64 = f64::NAN;
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index a0a5c003d..80ed34157 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -290,7 +290,8 @@ impl OsString {
/// in the given `OsString`. The string may reserve more space to speculatively avoid
/// frequent reallocations. After calling `try_reserve`, capacity will be
/// greater than or equal to `self.len() + additional` if it returns `Ok(())`.
- /// Does nothing if capacity is already sufficient.
+ /// Does nothing if capacity is already sufficient. This method preserves
+ /// the contents even if an error occurs.
///
/// See the main `OsString` documentation information about encoding and capacity units.
///
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index c8e131b6e..188ff00e1 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -13,13 +13,13 @@ mod tests;
use crate::ffi::OsString;
use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
use crate::path::{Path, PathBuf};
use crate::sys::fs as fs_imp;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
use crate::time::SystemTime;
-/// A reference to an open file on the filesystem.
+/// An object providing access to an open file on the filesystem.
///
/// An instance of a `File` can be read and/or written depending on what options
/// it was opened with. Files also implement [`Seek`] to alter the logical cursor
@@ -377,6 +377,35 @@ impl File {
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}
+ /// Creates a new file in read-write mode; error if the file exists.
+ ///
+ /// This function will create a file if it does not exist, or return an error if it does. This
+ /// way, if the call succeeds, the file returned is guaranteed to be new.
+ ///
+ /// This option is useful because it is atomic. Otherwise between checking whether a file
+ /// exists and creating a new one, the file may have been created by another process (a TOCTOU
+ /// race condition / attack).
+ ///
+ /// This can also be written using
+ /// `File::options().read(true).write(true).create_new(true).open(...)`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(file_create_new)]
+ ///
+ /// use std::fs::File;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let mut f = File::create_new("foo.txt")?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "file_create_new", issue = "none")]
+ pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
+ OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
+ }
+
/// Returns a new OpenOptions object.
///
/// This function returns a new OpenOptions object that you can use to
@@ -703,8 +732,8 @@ impl Read for File {
self.inner.read_vectored(bufs)
}
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- self.inner.read_buf(buf)
+ fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.read_buf(cursor)
}
#[inline]
@@ -755,8 +784,8 @@ impl Read for &File {
self.inner.read(buf)
}
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- self.inner.read_buf(buf)
+ fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.read_buf(cursor)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
@@ -1336,6 +1365,34 @@ impl FileTimes {
impl Permissions {
/// Returns `true` if these permissions describe a readonly (unwritable) file.
///
+ /// # Note
+ ///
+ /// This function does not take Access Control Lists (ACLs) or Unix group
+ /// membership into account.
+ ///
+ /// # Windows
+ ///
+ /// On Windows this returns [`FILE_ATTRIBUTE_READONLY`](https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants).
+ /// If `FILE_ATTRIBUTE_READONLY` is set then writes to the file will fail
+ /// but the user may still have permission to change this flag. If
+ /// `FILE_ATTRIBUTE_READONLY` is *not* set then writes may still fail due
+ /// to lack of write permission.
+ /// The behavior of this attribute for directories depends on the Windows
+ /// version.
+ ///
+ /// # Unix (including macOS)
+ ///
+ /// On Unix-based platforms this checks if *any* of the owner, group or others
+ /// write permission bits are set. It does not check if the current
+ /// user is in the file's assigned group. It also does not check ACLs.
+ /// Therefore even if this returns true you may not be able to write to the
+ /// file, and vice versa. The [`PermissionsExt`] trait gives direct access
+ /// to the permission bits but also does not read ACLs. If you need to
+ /// accurately know whether or not a file is writable use the `access()`
+ /// function from libc.
+ ///
+ /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt
+ ///
/// # Examples
///
/// ```no_run
@@ -1361,8 +1418,40 @@ impl Permissions {
/// using the resulting `Permission` will update file permissions to allow
/// writing.
///
- /// This operation does **not** modify the filesystem. To modify the
- /// filesystem use the [`set_permissions`] function.
+ /// This operation does **not** modify the files attributes. This only
+ /// changes the in-memory value of these attributes for this `Permissions`
+ /// instance. To modify the files attributes use the [`set_permissions`]
+ /// function which commits these attribute changes to the file.
+ ///
+ /// # Note
+ ///
+ /// `set_readonly(false)` makes the file *world-writable* on Unix.
+ /// You can use the [`PermissionsExt`] trait on Unix to avoid this issue.
+ ///
+ /// It also does not take Access Control Lists (ACLs) or Unix group
+ /// membership into account.
+ ///
+ /// # Windows
+ ///
+ /// On Windows this sets or clears [`FILE_ATTRIBUTE_READONLY`](https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants).
+ /// If `FILE_ATTRIBUTE_READONLY` is set then writes to the file will fail
+ /// but the user may still have permission to change this flag. If
+ /// `FILE_ATTRIBUTE_READONLY` is *not* set then the write may still fail if
+ /// the user does not have permission to write to the file.
+ ///
+ /// In Windows 7 and earlier this attribute prevents deleting empty
+ /// directories. It does not prevent modifying the directory contents.
+ /// On later versions of Windows this attribute is ignored for directories.
+ ///
+ /// # Unix (including macOS)
+ ///
+ /// On Unix-based platforms this sets or clears the write access bit for
+ /// the owner, group *and* others, equivalent to `chmod a+w <file>`
+ /// or `chmod a-w <file>` respectively. The latter will grant write access
+ /// to all users! You can use the [`PermissionsExt`] trait on Unix
+ /// to avoid this issue.
+ ///
+ /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt
///
/// # Examples
///
@@ -1376,7 +1465,8 @@ impl Permissions {
///
/// permissions.set_readonly(true);
///
- /// // filesystem doesn't change
+ /// // filesystem doesn't change, only the in memory state of the
+ /// // readonly permission
/// assert_eq!(false, metadata.permissions().readonly());
///
/// // just this particular `permissions`.
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index f7fbaa9c2..4f339a18a 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -2,7 +2,7 @@ mod buffer;
use crate::fmt;
use crate::io::{
- self, BufRead, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
+ self, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
};
use buffer::Buffer;
@@ -224,6 +224,14 @@ impl<R> BufReader<R> {
}
}
+// This is only used by a test which asserts that the initialization-tracking is correct.
+#[cfg(test)]
+impl<R> BufReader<R> {
+ pub fn initialized(&self) -> usize {
+ self.buf.initialized()
+ }
+}
+
impl<R: Seek> BufReader<R> {
/// Seeks relative to the current position. If the new position lies within the buffer,
/// the buffer will not be flushed, allowing for more efficient seeks.
@@ -266,21 +274,21 @@ impl<R: Read> Read for BufReader<R> {
Ok(nread)
}
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
// If we don't have any buffered data and we're doing a massive read
// (larger than our internal buffer), bypass our internal buffer
// entirely.
- if self.buf.pos() == self.buf.filled() && buf.remaining() >= self.capacity() {
+ if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() {
self.discard_buffer();
- return self.inner.read_buf(buf);
+ return self.inner.read_buf(cursor);
}
- let prev = buf.filled_len();
+ let prev = cursor.written();
let mut rem = self.fill_buf()?;
- rem.read_buf(buf)?;
+ rem.read_buf(cursor.reborrow())?;
- self.consume(buf.filled_len() - prev); //slice impl of read_buf known to never unfill buf
+ self.consume(cursor.written() - prev); //slice impl of read_buf known to never unfill buf
Ok(())
}
diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs
index 8ae01f3b0..e9e29d60c 100644
--- a/library/std/src/io/buffered/bufreader/buffer.rs
+++ b/library/std/src/io/buffered/bufreader/buffer.rs
@@ -9,7 +9,7 @@
/// that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so
/// without encountering any runtime bounds checks.
use crate::cmp;
-use crate::io::{self, Read, ReadBuf};
+use crate::io::{self, BorrowedBuf, Read};
use crate::mem::MaybeUninit;
pub struct Buffer {
@@ -20,13 +20,19 @@ pub struct Buffer {
// Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
// initialized with bytes from a read.
filled: usize,
+ // This is the max number of bytes returned across all `fill_buf` calls. We track this so that we
+ // can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its
+ // defensive initialization as possible. Note that while this often the same as `filled`, it
+ // doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and
+ // omitting this is a huge perf regression for `Read` impls that do not.
+ initialized: usize,
}
impl Buffer {
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
let buf = Box::new_uninit_slice(capacity);
- Self { buf, pos: 0, filled: 0 }
+ Self { buf, pos: 0, filled: 0, initialized: 0 }
}
#[inline]
@@ -51,6 +57,12 @@ impl Buffer {
self.pos
}
+ // This is only used by a test which asserts that the initialization-tracking is correct.
+ #[cfg(test)]
+ pub fn initialized(&self) -> usize {
+ self.initialized
+ }
+
#[inline]
pub fn discard_buffer(&mut self) {
self.pos = 0;
@@ -93,12 +105,17 @@ impl Buffer {
if self.pos >= self.filled {
debug_assert!(self.pos == self.filled);
- let mut readbuf = ReadBuf::uninit(&mut self.buf);
+ let mut buf = BorrowedBuf::from(&mut *self.buf);
+ // SAFETY: `self.filled` bytes will always have been initialized.
+ unsafe {
+ buf.set_init(self.initialized);
+ }
- reader.read_buf(&mut readbuf)?;
+ reader.read_buf(buf.unfilled())?;
- self.filled = readbuf.filled_len();
self.pos = 0;
+ self.filled = buf.len();
+ self.initialized = buf.init_len();
}
Ok(self.buffer())
}
diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs
index fe45b1326..f4e688eb9 100644
--- a/library/std/src/io/buffered/tests.rs
+++ b/library/std/src/io/buffered/tests.rs
@@ -1,5 +1,7 @@
use crate::io::prelude::*;
-use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, ReadBuf, SeekFrom};
+use crate::io::{
+ self, BorrowedBuf, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom,
+};
use crate::mem::MaybeUninit;
use crate::panic;
use crate::sync::atomic::{AtomicUsize, Ordering};
@@ -61,48 +63,48 @@ fn test_buffered_reader_read_buf() {
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
let mut reader = BufReader::with_capacity(2, inner);
- let mut buf = [MaybeUninit::uninit(); 3];
- let mut buf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 3];
+ let mut buf: BorrowedBuf<'_> = buf.into();
- reader.read_buf(&mut buf).unwrap();
+ reader.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), [5, 6, 7]);
assert_eq!(reader.buffer(), []);
- let mut buf = [MaybeUninit::uninit(); 2];
- let mut buf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 2];
+ let mut buf: BorrowedBuf<'_> = buf.into();
- reader.read_buf(&mut buf).unwrap();
+ reader.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), [0, 1]);
assert_eq!(reader.buffer(), []);
- let mut buf = [MaybeUninit::uninit(); 1];
- let mut buf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1];
+ let mut buf: BorrowedBuf<'_> = buf.into();
- reader.read_buf(&mut buf).unwrap();
+ reader.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), [2]);
assert_eq!(reader.buffer(), [3]);
- let mut buf = [MaybeUninit::uninit(); 3];
- let mut buf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 3];
+ let mut buf: BorrowedBuf<'_> = buf.into();
- reader.read_buf(&mut buf).unwrap();
+ reader.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), [3]);
assert_eq!(reader.buffer(), []);
- reader.read_buf(&mut buf).unwrap();
+ reader.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), [3, 4]);
assert_eq!(reader.buffer(), []);
buf.clear();
- reader.read_buf(&mut buf).unwrap();
+ reader.read_buf(buf.unfilled()).unwrap();
- assert_eq!(buf.filled_len(), 0);
+ assert!(buf.filled().is_empty());
}
#[test]
@@ -1037,3 +1039,27 @@ fn single_formatted_write() {
writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap();
assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]);
}
+
+#[test]
+fn bufreader_full_initialize() {
+ struct OneByteReader;
+ impl Read for OneByteReader {
+ fn read(&mut self, buf: &mut [u8]) -> crate::io::Result<usize> {
+ if buf.len() > 0 {
+ buf[0] = 0;
+ Ok(1)
+ } else {
+ Ok(0)
+ }
+ }
+ }
+ let mut reader = BufReader::new(OneByteReader);
+ // Nothing is initialized yet.
+ assert_eq!(reader.initialized(), 0);
+
+ let buf = reader.fill_buf().unwrap();
+ // We read one byte...
+ assert_eq!(buf.len(), 1);
+ // But we initialized the whole buffer!
+ assert_eq!(reader.initialized(), reader.capacity());
+}
diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
index 1a10245e4..38b98afff 100644
--- a/library/std/src/io/copy.rs
+++ b/library/std/src/io/copy.rs
@@ -1,4 +1,4 @@
-use super::{BufWriter, ErrorKind, Read, ReadBuf, Result, Write, DEFAULT_BUF_SIZE};
+use super::{BorrowedBuf, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
use crate::mem::MaybeUninit;
/// Copies the entire contents of a reader into a writer.
@@ -97,37 +97,39 @@ impl<I: Write> BufferedCopySpec for BufWriter<I> {
loop {
let buf = writer.buffer_mut();
- let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut());
+ let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
- // SAFETY: init is either 0 or the initialized_len of the previous iteration
unsafe {
- read_buf.assume_init(init);
+ // SAFETY: init is either 0 or the init_len from the previous iteration.
+ read_buf.set_init(init);
}
if read_buf.capacity() >= DEFAULT_BUF_SIZE {
- match reader.read_buf(&mut read_buf) {
+ let mut cursor = read_buf.unfilled();
+ match reader.read_buf(cursor.reborrow()) {
Ok(()) => {
- let bytes_read = read_buf.filled_len();
+ let bytes_read = cursor.written();
if bytes_read == 0 {
return Ok(len);
}
- init = read_buf.initialized_len() - bytes_read;
+ init = read_buf.init_len() - bytes_read;
+ len += bytes_read as u64;
- // SAFETY: ReadBuf guarantees all of its filled bytes are init
+ // SAFETY: BorrowedBuf guarantees all of its filled bytes are init
unsafe { buf.set_len(buf.len() + bytes_read) };
- len += bytes_read as u64;
+
// Read again if the buffer still has enough capacity, as BufWriter itself would do
// This will occur if the reader returns short reads
- continue;
}
- Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
+ } else {
+ writer.flush_buf()?;
+ init = 0;
}
-
- writer.flush_buf()?;
}
}
}
@@ -136,13 +138,13 @@ fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
reader: &mut R,
writer: &mut W,
) -> Result<u64> {
- let mut buf = [MaybeUninit::uninit(); DEFAULT_BUF_SIZE];
- let mut buf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE];
+ let mut buf: BorrowedBuf<'_> = buf.into();
let mut len = 0;
loop {
- match reader.read_buf(&mut buf) {
+ match reader.read_buf(buf.unfilled()) {
Ok(()) => {}
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index f3fbfc447..d98ab021c 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -5,7 +5,7 @@ use crate::io::prelude::*;
use crate::alloc::Allocator;
use crate::cmp;
-use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
/// A `Cursor` wraps an in-memory buffer and provides it with a
/// [`Seek`] implementation.
@@ -323,12 +323,12 @@ where
Ok(n)
}
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- let prev_filled = buf.filled_len();
+ fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ let prev_written = cursor.written();
- Read::read_buf(&mut self.fill_buf()?, buf)?;
+ Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?;
- self.pos += (buf.filled_len() - prev_filled) as u64;
+ self.pos += (cursor.written() - prev_written) as u64;
Ok(())
}
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index ff7fdcae1..3cabf2449 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -76,6 +76,14 @@ impl fmt::Debug for Error {
}
}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<alloc::ffi::NulError> for Error {
+ /// Converts a [`alloc::ffi::NulError`] into a [`Error`].
+ fn from(_: alloc::ffi::NulError) -> Error {
+ const_io_error!(ErrorKind::InvalidInput, "data provided contains a nul byte")
+ }
+}
+
// Only derive debug in tests, to make sure it
// doesn't accidentally get printed.
#[cfg_attr(test, derive(Debug))]
@@ -379,7 +387,7 @@ pub enum ErrorKind {
impl ErrorKind {
pub(crate) fn as_str(&self) -> &'static str {
use ErrorKind::*;
- // Strictly alphabetical, please. (Sadly rustfmt cannot do this yet.)
+ // tidy-alphabetical-start
match *self {
AddrInUse => "address in use",
AddrNotAvailable => "address not available",
@@ -423,6 +431,7 @@ impl ErrorKind {
WouldBlock => "operation would block",
WriteZero => "write zero",
}
+ // tidy-alphabetical-end
}
}
@@ -473,6 +482,7 @@ impl Error {
/// originate from the OS itself. The `error` argument is an arbitrary
/// payload which will be contained in this [`Error`].
///
+ /// Note that this function allocates memory on the heap.
/// If no extra payload is required, use the `From` conversion from
/// `ErrorKind`.
///
@@ -487,7 +497,7 @@ impl Error {
/// // errors can also be created from other errors
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
///
- /// // creating an error without payload
+ /// // creating an error without payload (and without memory allocation)
/// let eof_error = Error::from(ErrorKind::UnexpectedEof);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
@@ -564,6 +574,8 @@ impl Error {
/// println!("last OS error: {os_error:?}");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[doc(alias = "GetLastError")]
+ #[doc(alias = "errno")]
#[must_use]
#[inline]
pub fn last_os_error() -> Error {
diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs
index 292bf4826..781ae03ad 100644
--- a/library/std/src/io/error/repr_bitpacked.rs
+++ b/library/std/src/io/error/repr_bitpacked.rs
@@ -269,10 +269,10 @@ where
}
TAG_SIMPLE_MESSAGE => ErrorData::SimpleMessage(&*ptr.cast::<SimpleMessage>().as_ptr()),
TAG_CUSTOM => {
- // It would be correct for us to use `ptr::sub` here (see the
+ // It would be correct for us to use `ptr::byte_sub` here (see the
// comment above the `wrapping_add` call in `new_custom` for why),
// but it isn't clear that it makes a difference, so we don't.
- let custom = ptr.as_ptr().cast::<u8>().wrapping_sub(TAG_CUSTOM).cast::<Custom>();
+ let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::<Custom>();
ErrorData::Custom(make_custom(custom))
}
_ => {
diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs
index c897a5e87..16c634e9a 100644
--- a/library/std/src/io/error/tests.rs
+++ b/library/std/src/io/error/tests.rs
@@ -86,7 +86,7 @@ fn test_errorkind_packing() {
assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound);
assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied);
assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized);
- // Check that the innards look like like what we want.
+ // Check that the innards look like what we want.
assert_matches!(
Error::from(ErrorKind::OutOfMemory).repr.data(),
ErrorData::Simple(ErrorKind::OutOfMemory),
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index 950725473..e5048dcc8 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -6,7 +6,7 @@ use crate::cmp;
use crate::collections::VecDeque;
use crate::fmt;
use crate::io::{
- self, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write,
+ self, BorrowedCursor, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write,
};
use crate::mem;
@@ -21,8 +21,8 @@ impl<R: Read + ?Sized> Read for &mut R {
}
#[inline]
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- (**self).read_buf(buf)
+ fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ (**self).read_buf(cursor)
}
#[inline]
@@ -125,8 +125,8 @@ impl<R: Read + ?Sized> Read for Box<R> {
}
#[inline]
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- (**self).read_buf(buf)
+ fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ (**self).read_buf(cursor)
}
#[inline]
@@ -249,11 +249,11 @@ impl Read for &[u8] {
}
#[inline]
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- let amt = cmp::min(buf.remaining(), self.len());
+ fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ let amt = cmp::min(cursor.capacity(), self.len());
let (a, b) = self.split_at(amt);
- buf.append(a);
+ cursor.append(a);
*self = b;
Ok(())
@@ -427,10 +427,10 @@ impl<A: Allocator> Read for VecDeque<u8, A> {
}
#[inline]
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
let (ref mut front, _) = self.as_slices();
- let n = cmp::min(buf.remaining(), front.len());
- Read::read_buf(front, buf)?;
+ let n = cmp::min(cursor.capacity(), front.len());
+ Read::read_buf(front, cursor)?;
self.drain(..n);
Ok(())
}
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 96addbd1a..23a13523f 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -262,9 +262,12 @@ use crate::sys_common::memchr;
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub use self::buffered::WriterPanicked;
+pub(crate) use self::stdio::attempt_print_to_stderr;
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
pub use self::stdio::set_output_capture;
+#[unstable(feature = "is_terminal", issue = "98070")]
+pub use self::stdio::IsTerminal;
#[unstable(feature = "print_internals", issue = "none")]
pub use self::stdio::{_eprint, _print};
#[stable(feature = "rust1", since = "1.0.0")]
@@ -278,7 +281,7 @@ pub use self::{
};
#[unstable(feature = "read_buf", issue = "78485")]
-pub use self::readbuf::ReadBuf;
+pub use self::readbuf::{BorrowedBuf, BorrowedCursor};
pub(crate) use error::const_io_error;
mod buffered;
@@ -362,29 +365,30 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>
buf.reserve(32); // buf is full, need more space
}
- let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut());
+ let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
// SAFETY: These bytes were initialized but not filled in the previous loop
unsafe {
- read_buf.assume_init(initialized);
+ read_buf.set_init(initialized);
}
- match r.read_buf(&mut read_buf) {
+ let mut cursor = read_buf.unfilled();
+ match r.read_buf(cursor.reborrow()) {
Ok(()) => {}
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
- if read_buf.filled_len() == 0 {
+ if cursor.written() == 0 {
return Ok(buf.len() - start_len);
}
// store how much was initialized but not filled
- initialized = read_buf.initialized_len() - read_buf.filled_len();
- let new_len = read_buf.filled_len() + buf.len();
+ initialized = cursor.init_ref().len();
- // SAFETY: ReadBuf's invariants mean this much memory is init
+ // SAFETY: BorrowedBuf's invariants mean this much memory is initialized.
unsafe {
+ let new_len = read_buf.filled().len() + buf.len();
buf.set_len(new_len);
}
@@ -461,12 +465,15 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
}
}
-pub(crate) fn default_read_buf<F>(read: F, buf: &mut ReadBuf<'_>) -> Result<()>
+pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()>
where
F: FnOnce(&mut [u8]) -> Result<usize>,
{
- let n = read(buf.initialize_unfilled())?;
- buf.add_filled(n);
+ let n = read(cursor.ensure_init().init_mut())?;
+ unsafe {
+ // SAFETY: we initialised using `ensure_init` so there is no uninit data to advance to.
+ cursor.advance(n);
+ }
Ok(())
}
@@ -576,7 +583,7 @@ pub trait Read {
/// `n > buf.len()`.
///
/// No guarantees are provided about the contents of `buf` when this
- /// function is called, implementations cannot rely on any property of the
+ /// function is called, so implementations cannot rely on any property of the
/// contents of `buf` being true. It is recommended that *implementations*
/// only write data to `buf` instead of reading its contents.
///
@@ -752,7 +759,7 @@ pub trait Read {
/// specified buffer `buf`.
///
/// No guarantees are provided about the contents of `buf` when this
- /// function is called, implementations cannot rely on any property of the
+ /// function is called, so implementations cannot rely on any property of the
/// contents of `buf` being true. It is recommended that implementations
/// only write data to `buf` instead of reading its contents. The
/// documentation on [`read`] has a more detailed explanation on this
@@ -803,30 +810,30 @@ pub trait Read {
/// Pull some bytes from this source into the specified buffer.
///
- /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`ReadBuf`] rather than `[u8]` to allow use
+ /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use
/// with uninitialized buffers. The new data will be appended to any existing contents of `buf`.
///
/// The default implementation delegates to `read`.
#[unstable(feature = "read_buf", issue = "78485")]
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
default_read_buf(|b| self.read(b), buf)
}
- /// Read the exact number of bytes required to fill `buf`.
+ /// Read the exact number of bytes required to fill `cursor`.
///
- /// This is equivalent to the [`read_exact`](Read::read_exact) method, except that it is passed a [`ReadBuf`] rather than `[u8]` to
+ /// This is equivalent to the [`read_exact`](Read::read_exact) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to
/// allow use with uninitialized buffers.
#[unstable(feature = "read_buf", issue = "78485")]
- fn read_buf_exact(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
- while buf.remaining() > 0 {
- let prev_filled = buf.filled().len();
- match self.read_buf(buf) {
+ fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> {
+ while cursor.capacity() > 0 {
+ let prev_written = cursor.written();
+ match self.read_buf(cursor.reborrow()) {
Ok(()) => {}
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
- if buf.filled().len() == prev_filled {
+ if cursor.written() == prev_written {
return Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill buffer"));
}
}
@@ -883,6 +890,10 @@ pub trait Read {
/// The yielded item is [`Ok`] if a byte was successfully read and [`Err`]
/// otherwise. EOF is mapped to returning [`None`] from this iterator.
///
+ /// The default implementation calls `read` for each byte,
+ /// which can be very inefficient for data that's not in memory,
+ /// such as [`File`]. Consider using a [`BufReader`] in such cases.
+ ///
/// # Examples
///
/// [`File`]s implement `Read`:
@@ -895,10 +906,11 @@ pub trait Read {
/// ```no_run
/// use std::io;
/// use std::io::prelude::*;
+ /// use std::io::BufReader;
/// use std::fs::File;
///
/// fn main() -> io::Result<()> {
- /// let f = File::open("foo.txt")?;
+ /// let f = BufReader::new(File::open("foo.txt")?);
///
/// for byte in f.bytes() {
/// println!("{}", byte.unwrap());
@@ -1028,8 +1040,6 @@ pub trait Read {
/// # Examples
///
/// ```no_run
-/// #![feature(io_read_to_string)]
-///
/// # use std::io;
/// fn main() -> io::Result<()> {
/// let stdin = io::read_to_string(io::stdin())?;
@@ -1038,7 +1048,7 @@ pub trait Read {
/// Ok(())
/// }
/// ```
-#[unstable(feature = "io_read_to_string", issue = "80218")]
+#[stable(feature = "io_read_to_string", since = "1.65.0")]
pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> {
let mut buf = String::new();
reader.read_to_string(&mut buf)?;
@@ -2582,50 +2592,48 @@ impl<T: Read> Read for Take<T> {
Ok(n)
}
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
+ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> {
// Don't call into inner reader at all at EOF because it may still block
if self.limit == 0 {
return Ok(());
}
- let prev_filled = buf.filled_len();
-
- if self.limit <= buf.remaining() as u64 {
+ if self.limit <= buf.capacity() as u64 {
// if we just use an as cast to convert, limit may wrap around on a 32 bit target
let limit = cmp::min(self.limit, usize::MAX as u64) as usize;
- let extra_init = cmp::min(limit as usize, buf.initialized_len() - buf.filled_len());
+ let extra_init = cmp::min(limit as usize, buf.init_ref().len());
// SAFETY: no uninit data is written to ibuf
- let ibuf = unsafe { &mut buf.unfilled_mut()[..limit] };
+ let ibuf = unsafe { &mut buf.as_mut()[..limit] };
- let mut sliced_buf = ReadBuf::uninit(ibuf);
+ let mut sliced_buf: BorrowedBuf<'_> = ibuf.into();
// SAFETY: extra_init bytes of ibuf are known to be initialized
unsafe {
- sliced_buf.assume_init(extra_init);
+ sliced_buf.set_init(extra_init);
}
- self.inner.read_buf(&mut sliced_buf)?;
+ let mut cursor = sliced_buf.unfilled();
+ self.inner.read_buf(cursor.reborrow())?;
- let new_init = sliced_buf.initialized_len();
- let filled = sliced_buf.filled_len();
+ let new_init = cursor.init_ref().len();
+ let filled = sliced_buf.len();
- // sliced_buf / ibuf must drop here
+ // cursor / sliced_buf / ibuf must drop here
- // SAFETY: new_init bytes of buf's unfilled buffer have been initialized
unsafe {
- buf.assume_init(new_init);
+ // SAFETY: filled bytes have been filled and therefore initialized
+ buf.advance(filled);
+ // SAFETY: new_init bytes of buf's unfilled buffer have been initialized
+ buf.set_init(new_init);
}
- buf.add_filled(filled);
-
self.limit -= filled as u64;
} else {
- self.inner.read_buf(buf)?;
-
- //inner may unfill
- self.limit -= buf.filled_len().saturating_sub(prev_filled) as u64;
+ let written = buf.written();
+ self.inner.read_buf(buf.reborrow())?;
+ self.limit -= (buf.written() - written) as u64;
}
Ok(())
diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs
index 78d1113f8..4800eeda0 100644
--- a/library/std/src/io/readbuf.rs
+++ b/library/std/src/io/readbuf.rs
@@ -3,11 +3,12 @@
#[cfg(test)]
mod tests;
-use crate::cmp;
use crate::fmt::{self, Debug, Formatter};
-use crate::mem::MaybeUninit;
+use crate::io::{Result, Write};
+use crate::mem::{self, MaybeUninit};
+use crate::{cmp, ptr};
-/// A wrapper around a byte buffer that is incrementally filled and initialized.
+/// A borrowed byte buffer which is incrementally filled and initialized.
///
/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the
/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet
@@ -20,230 +21,289 @@ use crate::mem::MaybeUninit;
/// [ filled | unfilled ]
/// [ initialized | uninitialized ]
/// ```
-pub struct ReadBuf<'a> {
- buf: &'a mut [MaybeUninit<u8>],
+///
+/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference
+/// (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_init`), but cannot be
+/// directly written. To write into the buffer, use `unfilled` to create a `BorrowedCursor`. The cursor
+/// has write-only access to the unfilled portion of the buffer (you can think of it as a
+/// write-only iterator).
+///
+/// The lifetime `'data` is a bound on the lifetime of the underlying data.
+pub struct BorrowedBuf<'data> {
+ /// The buffer's underlying data.
+ buf: &'data mut [MaybeUninit<u8>],
+ /// The length of `self.buf` which is known to be filled.
filled: usize,
- initialized: usize,
+ /// The length of `self.buf` which is known to be initialized.
+ init: usize,
}
-impl Debug for ReadBuf<'_> {
+impl Debug for BorrowedBuf<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.debug_struct("ReadBuf")
- .field("init", &self.initialized())
+ f.debug_struct("BorrowedBuf")
+ .field("init", &self.init)
.field("filled", &self.filled)
.field("capacity", &self.capacity())
.finish()
}
}
-impl<'a> ReadBuf<'a> {
- /// Creates a new `ReadBuf` from a fully initialized buffer.
+/// Create a new `BorrowedBuf` from a fully initialized slice.
+impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> {
#[inline]
- pub fn new(buf: &'a mut [u8]) -> ReadBuf<'a> {
- let len = buf.len();
+ fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> {
+ let len = slice.len();
- ReadBuf {
- //SAFETY: initialized data never becoming uninitialized is an invariant of ReadBuf
- buf: unsafe { (buf as *mut [u8]).as_uninit_slice_mut().unwrap() },
+ BorrowedBuf {
+ // SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf
+ buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() },
filled: 0,
- initialized: len,
+ init: len,
}
}
+}
- /// Creates a new `ReadBuf` from a fully uninitialized buffer.
- ///
- /// Use `assume_init` if part of the buffer is known to be already initialized.
+/// Create a new `BorrowedBuf` from an uninitialized buffer.
+///
+/// Use `set_init` if part of the buffer is known to be already initialized.
+impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuf<'data> {
#[inline]
- pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> {
- ReadBuf { buf, filled: 0, initialized: 0 }
+ fn from(buf: &'data mut [MaybeUninit<u8>]) -> BorrowedBuf<'data> {
+ BorrowedBuf { buf, filled: 0, init: 0 }
}
+}
+impl<'data> BorrowedBuf<'data> {
/// Returns the total capacity of the buffer.
#[inline]
pub fn capacity(&self) -> usize {
self.buf.len()
}
+ /// Returns the length of the filled part of the buffer.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.filled
+ }
+
+ /// Returns the length of the initialized part of the buffer.
+ #[inline]
+ pub fn init_len(&self) -> usize {
+ self.init
+ }
+
/// Returns a shared reference to the filled portion of the buffer.
#[inline]
pub fn filled(&self) -> &[u8] {
- //SAFETY: We only slice the filled part of the buffer, which is always valid
+ // SAFETY: We only slice the filled part of the buffer, which is always valid
unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) }
}
- /// Returns a mutable reference to the filled portion of the buffer.
+ /// Returns a cursor over the unfilled part of the buffer.
#[inline]
- pub fn filled_mut(&mut self) -> &mut [u8] {
- //SAFETY: We only slice the filled part of the buffer, which is always valid
- unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) }
+ pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> {
+ BorrowedCursor {
+ start: self.filled,
+ // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its
+ // lifetime covariantly is safe.
+ buf: unsafe {
+ mem::transmute::<&'this mut BorrowedBuf<'data>, &'this mut BorrowedBuf<'this>>(self)
+ },
+ }
}
- /// Returns a shared reference to the initialized portion of the buffer.
+ /// Clears the buffer, resetting the filled region to empty.
///
- /// This includes the filled portion.
+ /// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
#[inline]
- pub fn initialized(&self) -> &[u8] {
- //SAFETY: We only slice the initialized part of the buffer, which is always valid
- unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.initialized]) }
+ pub fn clear(&mut self) -> &mut Self {
+ self.filled = 0;
+ self
}
- /// Returns a mutable reference to the initialized portion of the buffer.
+ /// Asserts that the first `n` bytes of the buffer are initialized.
///
- /// This includes the filled portion.
- #[inline]
- pub fn initialized_mut(&mut self) -> &mut [u8] {
- //SAFETY: We only slice the initialized part of the buffer, which is always valid
- unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.initialized]) }
- }
-
- /// Returns a mutable reference to the unfilled part of the buffer without ensuring that it has been fully
- /// initialized.
+ /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer
+ /// bytes than are already known to be initialized.
///
/// # Safety
///
- /// The caller must not de-initialize portions of the buffer that have already been initialized.
+ /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized.
#[inline]
- pub unsafe fn unfilled_mut(&mut self) -> &mut [MaybeUninit<u8>] {
- &mut self.buf[self.filled..]
+ pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
+ self.init = cmp::max(self.init, n);
+ self
}
+}
- /// Returns a mutable reference to the uninitialized part of the buffer.
+/// A writeable view of the unfilled portion of a [`BorrowedBuf`](BorrowedBuf).
+///
+/// Provides access to the initialized and uninitialized parts of the underlying `BorrowedBuf`.
+/// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or
+/// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the
+/// indirect case, the caller must call [`advance`](BorrowedCursor::advance) after writing to inform
+/// the cursor how many bytes have been written.
+///
+/// Once data is written to the cursor, it becomes part of the filled portion of the underlying
+/// `BorrowedBuf` and can no longer be accessed or re-written by the cursor. I.e., the cursor tracks
+/// the unfilled part of the underlying `BorrowedBuf`.
+///
+/// The lifetime `'a` is a bound on the lifetime of the underlying buffer (which means it is a bound
+/// on the data in that buffer by transitivity).
+#[derive(Debug)]
+pub struct BorrowedCursor<'a> {
+ /// The underlying buffer.
+ // Safety invariant: we treat the type of buf as covariant in the lifetime of `BorrowedBuf` when
+ // we create a `BorrowedCursor`. This is only safe if we never replace `buf` by assigning into
+ // it, so don't do that!
+ buf: &'a mut BorrowedBuf<'a>,
+ /// The length of the filled portion of the underlying buffer at the time of the cursor's
+ /// creation.
+ start: usize,
+}
+
+impl<'a> BorrowedCursor<'a> {
+ /// Reborrow this cursor by cloning it with a smaller lifetime.
///
- /// It is safe to uninitialize any of these bytes.
+ /// Since a cursor maintains unique access to its underlying buffer, the borrowed cursor is
+ /// not accessible while the new cursor exists.
#[inline]
- pub fn uninitialized_mut(&mut self) -> &mut [MaybeUninit<u8>] {
- &mut self.buf[self.initialized..]
+ pub fn reborrow<'this>(&'this mut self) -> BorrowedCursor<'this> {
+ BorrowedCursor {
+ // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its
+ // lifetime covariantly is safe.
+ buf: unsafe {
+ mem::transmute::<&'this mut BorrowedBuf<'a>, &'this mut BorrowedBuf<'this>>(
+ self.buf,
+ )
+ },
+ start: self.start,
+ }
}
- /// Returns a mutable reference to the unfilled part of the buffer, ensuring it is fully initialized.
- ///
- /// Since `ReadBuf` tracks the region of the buffer that has been initialized, this is effectively "free" after
- /// the first use.
+ /// Returns the available space in the cursor.
#[inline]
- pub fn initialize_unfilled(&mut self) -> &mut [u8] {
- // should optimize out the assertion
- self.initialize_unfilled_to(self.remaining())
+ pub fn capacity(&self) -> usize {
+ self.buf.capacity() - self.buf.filled
}
- /// Returns a mutable reference to the first `n` bytes of the unfilled part of the buffer, ensuring it is
- /// fully initialized.
+ /// Returns the number of bytes written to this cursor since it was created from a `BorrowedBuf`.
///
- /// # Panics
- ///
- /// Panics if `self.remaining()` is less than `n`.
+ /// Note that if this cursor is a reborrowed clone of another, then the count returned is the
+ /// count written via either cursor, not the count since the cursor was reborrowed.
#[inline]
- pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] {
- assert!(self.remaining() >= n);
-
- let extra_init = self.initialized - self.filled;
- // If we don't have enough initialized, do zeroing
- if n > extra_init {
- let uninit = n - extra_init;
- let unfilled = &mut self.uninitialized_mut()[0..uninit];
-
- for byte in unfilled.iter_mut() {
- byte.write(0);
- }
-
- // SAFETY: we just initialized uninit bytes, and the previous bytes were already init
- unsafe {
- self.assume_init(n);
- }
- }
-
- let filled = self.filled;
+ pub fn written(&self) -> usize {
+ self.buf.filled - self.start
+ }
- &mut self.initialized_mut()[filled..filled + n]
+ /// Returns a shared reference to the initialized portion of the cursor.
+ #[inline]
+ pub fn init_ref(&self) -> &[u8] {
+ // SAFETY: We only slice the initialized part of the buffer, which is always valid
+ unsafe { MaybeUninit::slice_assume_init_ref(&self.buf.buf[self.buf.filled..self.buf.init]) }
}
- /// Returns the number of bytes at the end of the slice that have not yet been filled.
+ /// Returns a mutable reference to the initialized portion of the cursor.
#[inline]
- pub fn remaining(&self) -> usize {
- self.capacity() - self.filled
+ pub fn init_mut(&mut self) -> &mut [u8] {
+ // SAFETY: We only slice the initialized part of the buffer, which is always valid
+ unsafe {
+ MaybeUninit::slice_assume_init_mut(&mut self.buf.buf[self.buf.filled..self.buf.init])
+ }
}
- /// Clears the buffer, resetting the filled region to empty.
+ /// Returns a mutable reference to the uninitialized part of the cursor.
///
- /// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
+ /// It is safe to uninitialize any of these bytes.
#[inline]
- pub fn clear(&mut self) -> &mut Self {
- self.set_filled(0) // The assertion in `set_filled` is optimized out
+ pub fn uninit_mut(&mut self) -> &mut [MaybeUninit<u8>] {
+ &mut self.buf.buf[self.buf.init..]
}
- /// Increases the size of the filled region of the buffer.
+ /// Returns a mutable reference to the whole cursor.
///
- /// The number of initialized bytes is not changed.
- ///
- /// # Panics
+ /// # Safety
///
- /// Panics if the filled region of the buffer would become larger than the initialized region.
+ /// The caller must not uninitialize any bytes in the initialized portion of the cursor.
#[inline]
- pub fn add_filled(&mut self, n: usize) -> &mut Self {
- self.set_filled(self.filled + n)
+ pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] {
+ &mut self.buf.buf[self.buf.filled..]
}
- /// Sets the size of the filled region of the buffer.
- ///
- /// The number of initialized bytes is not changed.
+ /// Advance the cursor by asserting that `n` bytes have been filled.
///
- /// Note that this can be used to *shrink* the filled region of the buffer in addition to growing it (for
- /// example, by a `Read` implementation that compresses data in-place).
+ /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be
+ /// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements
+ /// and its unfilled portion (and the capacity of this cursor) shrinks by `n` elements.
///
- /// # Panics
+ /// # Safety
///
- /// Panics if the filled region of the buffer would become larger than the initialized region.
+ /// The caller must ensure that the first `n` bytes of the cursor have been properly
+ /// initialised.
+ #[inline]
+ pub unsafe fn advance(&mut self, n: usize) -> &mut Self {
+ self.buf.filled += n;
+ self.buf.init = cmp::max(self.buf.init, self.buf.filled);
+ self
+ }
+
+ /// Initializes all bytes in the cursor.
#[inline]
- pub fn set_filled(&mut self, n: usize) -> &mut Self {
- assert!(n <= self.initialized);
+ pub fn ensure_init(&mut self) -> &mut Self {
+ let uninit = self.uninit_mut();
+ // SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
+ // since it is comes from a slice reference.
+ unsafe {
+ ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len());
+ }
+ self.buf.init = self.buf.capacity();
- self.filled = n;
self
}
- /// Asserts that the first `n` unfilled bytes of the buffer are initialized.
+ /// Asserts that the first `n` unfilled bytes of the cursor are initialized.
///
- /// `ReadBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer
- /// bytes than are already known to be initialized.
+ /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when
+ /// called with fewer bytes than are already known to be initialized.
///
/// # Safety
///
- /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized.
+ /// The caller must ensure that the first `n` bytes of the buffer have already been initialized.
#[inline]
- pub unsafe fn assume_init(&mut self, n: usize) -> &mut Self {
- self.initialized = cmp::max(self.initialized, self.filled + n);
+ pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
+ self.buf.init = cmp::max(self.buf.init, self.buf.filled + n);
self
}
- /// Appends data to the buffer, advancing the written position and possibly also the initialized position.
+ /// Appends data to the cursor, advancing position within its buffer.
///
/// # Panics
///
- /// Panics if `self.remaining()` is less than `buf.len()`.
+ /// Panics if `self.capacity()` is less than `buf.len()`.
#[inline]
pub fn append(&mut self, buf: &[u8]) {
- assert!(self.remaining() >= buf.len());
+ assert!(self.capacity() >= buf.len());
// SAFETY: we do not de-initialize any of the elements of the slice
unsafe {
- MaybeUninit::write_slice(&mut self.unfilled_mut()[..buf.len()], buf);
+ MaybeUninit::write_slice(&mut self.as_mut()[..buf.len()], buf);
}
// SAFETY: We just added the entire contents of buf to the filled section.
unsafe {
- self.assume_init(buf.len());
+ self.set_init(buf.len());
}
- self.add_filled(buf.len());
+ self.buf.filled += buf.len();
}
+}
- /// Returns the amount of bytes that have been filled.
- #[inline]
- pub fn filled_len(&self) -> usize {
- self.filled
+impl<'a> Write for BorrowedCursor<'a> {
+ fn write(&mut self, buf: &[u8]) -> Result<usize> {
+ self.append(buf);
+ Ok(buf.len())
}
- /// Returns the amount of bytes that have been initialized.
- #[inline]
- pub fn initialized_len(&self) -> usize {
- self.initialized
+ fn flush(&mut self) -> Result<()> {
+ Ok(())
}
}
diff --git a/library/std/src/io/readbuf/tests.rs b/library/std/src/io/readbuf/tests.rs
index 3b7a5a56d..cc1b423f2 100644
--- a/library/std/src/io/readbuf/tests.rs
+++ b/library/std/src/io/readbuf/tests.rs
@@ -1,181 +1,175 @@
-use super::ReadBuf;
+use super::BorrowedBuf;
use crate::mem::MaybeUninit;
-/// Test that ReadBuf has the correct numbers when created with new
+/// Test that BorrowedBuf has the correct numbers when created with new
#[test]
fn new() {
- let mut buf = [0; 16];
- let rbuf = ReadBuf::new(&mut buf);
+ let buf: &mut [_] = &mut [0; 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
- assert_eq!(rbuf.filled_len(), 0);
- assert_eq!(rbuf.initialized_len(), 16);
+ assert_eq!(rbuf.filled().len(), 0);
+ assert_eq!(rbuf.init_len(), 16);
assert_eq!(rbuf.capacity(), 16);
- assert_eq!(rbuf.remaining(), 16);
+ assert_eq!(rbuf.unfilled().capacity(), 16);
}
-/// Test that ReadBuf has the correct numbers when created with uninit
+/// Test that BorrowedBuf has the correct numbers when created with uninit
#[test]
fn uninit() {
- let mut buf = [MaybeUninit::uninit(); 16];
- let rbuf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
- assert_eq!(rbuf.filled_len(), 0);
- assert_eq!(rbuf.initialized_len(), 0);
+ assert_eq!(rbuf.filled().len(), 0);
+ assert_eq!(rbuf.init_len(), 0);
assert_eq!(rbuf.capacity(), 16);
- assert_eq!(rbuf.remaining(), 16);
+ assert_eq!(rbuf.unfilled().capacity(), 16);
}
#[test]
fn initialize_unfilled() {
- let mut buf = [MaybeUninit::uninit(); 16];
- let mut rbuf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
- rbuf.initialize_unfilled();
+ rbuf.unfilled().ensure_init();
- assert_eq!(rbuf.initialized_len(), 16);
+ assert_eq!(rbuf.init_len(), 16);
}
#[test]
-fn initialize_unfilled_to() {
- let mut buf = [MaybeUninit::uninit(); 16];
- let mut rbuf = ReadBuf::uninit(&mut buf);
+fn addvance_filled() {
+ let buf: &mut [_] = &mut [0; 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
- rbuf.initialize_unfilled_to(8);
-
- assert_eq!(rbuf.initialized_len(), 8);
-
- rbuf.initialize_unfilled_to(4);
-
- assert_eq!(rbuf.initialized_len(), 8);
-
- rbuf.set_filled(8);
-
- rbuf.initialize_unfilled_to(6);
-
- assert_eq!(rbuf.initialized_len(), 14);
-
- rbuf.initialize_unfilled_to(8);
-
- assert_eq!(rbuf.initialized_len(), 16);
-}
-
-#[test]
-fn add_filled() {
- let mut buf = [0; 16];
- let mut rbuf = ReadBuf::new(&mut buf);
-
- rbuf.add_filled(1);
-
- assert_eq!(rbuf.filled_len(), 1);
- assert_eq!(rbuf.remaining(), 15);
-}
-
-#[test]
-#[should_panic]
-fn add_filled_panic() {
- let mut buf = [MaybeUninit::uninit(); 16];
- let mut rbuf = ReadBuf::uninit(&mut buf);
-
- rbuf.add_filled(1);
-}
-
-#[test]
-fn set_filled() {
- let mut buf = [0; 16];
- let mut rbuf = ReadBuf::new(&mut buf);
-
- rbuf.set_filled(16);
-
- assert_eq!(rbuf.filled_len(), 16);
- assert_eq!(rbuf.remaining(), 0);
-
- rbuf.set_filled(6);
-
- assert_eq!(rbuf.filled_len(), 6);
- assert_eq!(rbuf.remaining(), 10);
-}
-
-#[test]
-#[should_panic]
-fn set_filled_panic() {
- let mut buf = [MaybeUninit::uninit(); 16];
- let mut rbuf = ReadBuf::uninit(&mut buf);
+ unsafe {
+ rbuf.unfilled().advance(1);
+ }
- rbuf.set_filled(16);
+ assert_eq!(rbuf.filled().len(), 1);
+ assert_eq!(rbuf.unfilled().capacity(), 15);
}
#[test]
fn clear() {
- let mut buf = [255; 16];
- let mut rbuf = ReadBuf::new(&mut buf);
+ let buf: &mut [_] = &mut [255; 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
- rbuf.set_filled(16);
+ unsafe {
+ rbuf.unfilled().advance(16);
+ }
- assert_eq!(rbuf.filled_len(), 16);
- assert_eq!(rbuf.remaining(), 0);
+ assert_eq!(rbuf.filled().len(), 16);
+ assert_eq!(rbuf.unfilled().capacity(), 0);
rbuf.clear();
- assert_eq!(rbuf.filled_len(), 0);
- assert_eq!(rbuf.remaining(), 16);
+ assert_eq!(rbuf.filled().len(), 0);
+ assert_eq!(rbuf.unfilled().capacity(), 16);
- assert_eq!(rbuf.initialized(), [255; 16]);
+ assert_eq!(rbuf.unfilled().init_ref(), [255; 16]);
}
#[test]
-fn assume_init() {
- let mut buf = [MaybeUninit::uninit(); 16];
- let mut rbuf = ReadBuf::uninit(&mut buf);
+fn set_init() {
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
unsafe {
- rbuf.assume_init(8);
+ rbuf.set_init(8);
}
- assert_eq!(rbuf.initialized_len(), 8);
+ assert_eq!(rbuf.init_len(), 8);
- rbuf.add_filled(4);
+ unsafe {
+ rbuf.unfilled().advance(4);
+ }
unsafe {
- rbuf.assume_init(2);
+ rbuf.set_init(2);
}
- assert_eq!(rbuf.initialized_len(), 8);
+ assert_eq!(rbuf.init_len(), 8);
unsafe {
- rbuf.assume_init(8);
+ rbuf.set_init(8);
}
- assert_eq!(rbuf.initialized_len(), 12);
+ assert_eq!(rbuf.init_len(), 8);
}
#[test]
fn append() {
- let mut buf = [MaybeUninit::new(255); 16];
- let mut rbuf = ReadBuf::uninit(&mut buf);
+ let buf: &mut [_] = &mut [MaybeUninit::new(255); 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
- rbuf.append(&[0; 8]);
+ rbuf.unfilled().append(&[0; 8]);
- assert_eq!(rbuf.initialized_len(), 8);
- assert_eq!(rbuf.filled_len(), 8);
+ assert_eq!(rbuf.init_len(), 8);
+ assert_eq!(rbuf.filled().len(), 8);
assert_eq!(rbuf.filled(), [0; 8]);
rbuf.clear();
- rbuf.append(&[1; 16]);
+ rbuf.unfilled().append(&[1; 16]);
- assert_eq!(rbuf.initialized_len(), 16);
- assert_eq!(rbuf.filled_len(), 16);
+ assert_eq!(rbuf.init_len(), 16);
+ assert_eq!(rbuf.filled().len(), 16);
assert_eq!(rbuf.filled(), [1; 16]);
}
#[test]
-fn filled_mut() {
- let mut buf = [0; 16];
- let mut rbuf = ReadBuf::new(&mut buf);
+fn reborrow_written() {
+ let buf: &mut [_] = &mut [MaybeUninit::new(0); 32];
+ let mut buf: BorrowedBuf<'_> = buf.into();
+
+ let mut cursor = buf.unfilled();
+ cursor.append(&[1; 16]);
+
+ let mut cursor2 = cursor.reborrow();
+ cursor2.append(&[2; 16]);
+
+ assert_eq!(cursor2.written(), 32);
+ assert_eq!(cursor.written(), 32);
+
+ assert_eq!(buf.unfilled().written(), 0);
+ assert_eq!(buf.init_len(), 32);
+ assert_eq!(buf.filled().len(), 32);
+ let filled = buf.filled();
+ assert_eq!(&filled[..16], [1; 16]);
+ assert_eq!(&filled[16..], [2; 16]);
+}
+
+#[test]
+fn cursor_set_init() {
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
+ let mut rbuf: BorrowedBuf<'_> = buf.into();
+
+ unsafe {
+ rbuf.unfilled().set_init(8);
+ }
- rbuf.add_filled(8);
+ assert_eq!(rbuf.init_len(), 8);
+ assert_eq!(rbuf.unfilled().init_ref().len(), 8);
+ assert_eq!(rbuf.unfilled().init_mut().len(), 8);
+ assert_eq!(rbuf.unfilled().uninit_mut().len(), 8);
+ assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 16);
+
+ unsafe {
+ rbuf.unfilled().advance(4);
+ }
- let filled = rbuf.filled().to_vec();
+ unsafe {
+ rbuf.unfilled().set_init(2);
+ }
+
+ assert_eq!(rbuf.init_len(), 8);
+
+ unsafe {
+ rbuf.unfilled().set_init(8);
+ }
- assert_eq!(&*filled, &*rbuf.filled_mut());
+ assert_eq!(rbuf.init_len(), 12);
+ assert_eq!(rbuf.unfilled().init_ref().len(), 8);
+ assert_eq!(rbuf.unfilled().init_mut().len(), 8);
+ assert_eq!(rbuf.unfilled().uninit_mut().len(), 4);
+ assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 12);
}
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 4d3736f79..1141a957d 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -7,8 +7,8 @@ use crate::io::prelude::*;
use crate::cell::{Cell, RefCell};
use crate::fmt;
+use crate::fs::File;
use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
-use crate::pin::Pin;
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock};
use crate::sys::stdio;
@@ -526,7 +526,7 @@ pub struct Stdout {
// FIXME: this should be LineWriter or BufWriter depending on the state of
// stdout (tty or not). Note that if this is not line buffered it
// should also flush-on-panic or some form of flush-on-abort.
- inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
+ inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>,
}
/// A locked reference to the [`Stdout`] handle.
@@ -603,22 +603,27 @@ static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLo
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdout() -> Stdout {
Stdout {
- inner: Pin::static_ref(&STDOUT).get_or_init_pin(
- || unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
- |mutex| unsafe { mutex.init() },
- ),
+ inner: STDOUT
+ .get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))),
}
}
+// Flush the data and disable buffering during shutdown
+// by replacing the line writer by one with zero
+// buffering capacity.
pub fn cleanup() {
- if let Some(instance) = STDOUT.get() {
- // Flush the data and disable buffering during shutdown
- // by replacing the line writer by one with zero
- // buffering capacity.
+ let mut initialized = false;
+ let stdout = STDOUT.get_or_init(|| {
+ initialized = true;
+ ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw())))
+ });
+
+ if !initialized {
+ // The buffer was previously initialized, overwrite it here.
// We use try_lock() instead of lock(), because someone
// might have leaked a StdoutLock, which would
// otherwise cause a deadlock here.
- if let Some(lock) = Pin::static_ref(instance).try_lock() {
+ if let Some(lock) = stdout.try_lock() {
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
}
}
@@ -761,7 +766,7 @@ impl fmt::Debug for StdoutLock<'_> {
/// standard library or via raw Windows API calls, will fail.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stderr {
- inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>,
+ inner: &'static ReentrantMutex<RefCell<StderrRaw>>,
}
/// A locked reference to the [`Stderr`] handle.
@@ -834,16 +839,12 @@ pub struct StderrLock<'a> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stderr() -> Stderr {
// Note that unlike `stdout()` we don't use `at_exit` here to register a
- // destructor. Stderr is not buffered , so there's no need to run a
+ // destructor. Stderr is not buffered, so there's no need to run a
// destructor for flushing the buffer
- static INSTANCE: OnceLock<ReentrantMutex<RefCell<StderrRaw>>> = OnceLock::new();
+ static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> =
+ ReentrantMutex::new(RefCell::new(stderr_raw()));
- Stderr {
- inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
- || unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) },
- |mutex| unsafe { mutex.init() },
- ),
- }
+ Stderr { inner: &INSTANCE }
}
impl Stderr {
@@ -986,17 +987,31 @@ pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> {
/// otherwise. `label` identifies the stream in a panic message.
///
/// This function is used to print error messages, so it takes extra
-/// care to avoid causing a panic when `local_s` is unusable.
-/// For instance, if the TLS key for the local stream is
-/// already destroyed, or if the local stream is locked by another
-/// thread, it will just fall back to the global stream.
+/// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable.
+/// For instance, if the TLS key for output capturing is already destroyed, or
+/// if the local stream is in use by another thread, it will just fall back to
+/// the global stream.
///
/// However, if the actual I/O causes an error, this function does panic.
+///
+/// Writing to non-blocking stdout/stderr can cause an error, which will lead
+/// this function to panic.
fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str)
where
T: Write,
{
- if OUTPUT_CAPTURE_USED.load(Ordering::Relaxed)
+ if print_to_buffer_if_capture_used(args) {
+ // Successfully wrote to capture buffer.
+ return;
+ }
+
+ if let Err(e) = global_s().write_fmt(args) {
+ panic!("failed printing to {label}: {e}");
+ }
+}
+
+fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool {
+ OUTPUT_CAPTURE_USED.load(Ordering::Relaxed)
&& OUTPUT_CAPTURE.try_with(|s| {
// Note that we completely remove a local sink to write to in case
// our printing recursively panics/prints, so the recursive
@@ -1006,16 +1021,49 @@ where
s.set(Some(w));
})
}) == Ok(Some(()))
- {
- // Successfully wrote to capture buffer.
+}
+
+/// Used by impl Termination for Result to print error after `main` or a test
+/// has returned. Should avoid panicking, although we can't help it if one of
+/// the Display impls inside args decides to.
+pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) {
+ if print_to_buffer_if_capture_used(args) {
return;
}
- if let Err(e) = global_s().write_fmt(args) {
- panic!("failed printing to {label}: {e}");
- }
+ // Ignore error if the write fails, for example because stderr is already
+ // closed. There is not much point panicking at this point.
+ let _ = stderr().write_fmt(args);
+}
+
+/// Trait to determine if a descriptor/handle refers to a terminal/tty.
+#[unstable(feature = "is_terminal", issue = "98070")]
+pub trait IsTerminal: crate::sealed::Sealed {
+ /// Returns `true` if the descriptor/handle refers to a terminal/tty.
+ ///
+ /// On platforms where Rust does not know how to detect a terminal yet, this will return
+ /// `false`. This will also return `false` if an unexpected error occurred, such as from
+ /// passing an invalid file descriptor.
+ fn is_terminal(&self) -> bool;
+}
+
+macro_rules! impl_is_terminal {
+ ($($t:ty),*$(,)?) => {$(
+ #[unstable(feature = "sealed", issue = "none")]
+ impl crate::sealed::Sealed for $t {}
+
+ #[unstable(feature = "is_terminal", issue = "98070")]
+ impl IsTerminal for $t {
+ #[inline]
+ fn is_terminal(&self) -> bool {
+ crate::sys::io::is_terminal(self)
+ }
+ }
+ )*}
}
+impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>);
+
#[unstable(
feature = "print_internals",
reason = "implementation detail which may disappear or be replaced at any time",
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index f357f33ec..f4a886d88 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -1,4 +1,4 @@
-use super::{repeat, Cursor, ReadBuf, SeekFrom};
+use super::{repeat, BorrowedBuf, Cursor, SeekFrom};
use crate::cmp::{self, min};
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{BufRead, BufReader, Read, Seek, Write};
@@ -94,7 +94,7 @@ fn read_to_end() {
assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
assert_eq!(v, b"1");
- let cap = 1024 * 1024;
+ let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 };
let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
let mut v = Vec::new();
let (a, b) = data.split_at(data.len() / 2);
@@ -159,24 +159,24 @@ fn read_exact_slice() {
#[test]
fn read_buf_exact() {
- let mut buf = [0; 4];
- let mut buf = ReadBuf::new(&mut buf);
+ let buf: &mut [_] = &mut [0; 4];
+ let mut buf: BorrowedBuf<'_> = buf.into();
let mut c = Cursor::new(&b""[..]);
- assert_eq!(c.read_buf_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+ assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
let mut c = Cursor::new(&b"123456789"[..]);
- c.read_buf_exact(&mut buf).unwrap();
+ c.read_buf_exact(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), b"1234");
buf.clear();
- c.read_buf_exact(&mut buf).unwrap();
+ c.read_buf_exact(buf.unfilled()).unwrap();
assert_eq!(buf.filled(), b"5678");
buf.clear();
- assert_eq!(c.read_buf_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+ assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
}
#[test]
@@ -309,6 +309,7 @@ fn chain_zero_length_read_is_not_eof() {
#[bench]
#[cfg_attr(target_os = "emscripten", ignore)]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_read_to_end(b: &mut test::Bencher) {
b.iter(|| {
let mut lr = repeat(1).take(10000000);
@@ -614,10 +615,10 @@ fn bench_take_read(b: &mut test::Bencher) {
#[bench]
fn bench_take_read_buf(b: &mut test::Bencher) {
b.iter(|| {
- let mut buf = [MaybeUninit::uninit(); 64];
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 64];
- let mut rbuf = ReadBuf::uninit(&mut buf);
+ let mut buf: BorrowedBuf<'_> = buf.into();
- [255; 128].take(64).read_buf(&mut rbuf).unwrap();
+ [255; 128].take(64).read_buf(buf.unfilled()).unwrap();
});
}
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index c1300cd67..f076ee092 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -5,7 +5,7 @@ mod tests;
use crate::fmt;
use crate::io::{
- self, BufRead, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, SizeHint, Write,
+ self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
};
/// A reader which is always at EOF.
@@ -47,7 +47,7 @@ impl Read for Empty {
}
#[inline]
- fn read_buf(&mut self, _buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
Ok(())
}
}
@@ -130,21 +130,19 @@ impl Read for Repeat {
Ok(buf.len())
}
- fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
// SAFETY: No uninit bytes are being written
- for slot in unsafe { buf.unfilled_mut() } {
+ for slot in unsafe { buf.as_mut() } {
slot.write(self.byte);
}
- let remaining = buf.remaining();
+ let remaining = buf.capacity();
// SAFETY: the entire unfilled portion of buf has been initialized
unsafe {
- buf.assume_init(remaining);
+ buf.advance(remaining);
}
- buf.add_filled(remaining);
-
Ok(())
}
diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs
index 08972a59a..ce5e2c9da 100644
--- a/library/std/src/io/util/tests.rs
+++ b/library/std/src/io/util/tests.rs
@@ -1,7 +1,7 @@
use crate::cmp::{max, min};
use crate::io::prelude::*;
use crate::io::{
- copy, empty, repeat, sink, BufWriter, Empty, ReadBuf, Repeat, Result, SeekFrom, Sink,
+ copy, empty, repeat, sink, BorrowedBuf, BufWriter, Empty, Repeat, Result, SeekFrom, Sink,
DEFAULT_BUF_SIZE,
};
@@ -79,29 +79,29 @@ fn empty_reads() {
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
- let mut buf = [];
- let mut buf = ReadBuf::uninit(&mut buf);
- e.read_buf(&mut buf).unwrap();
- assert_eq!(buf.filled_len(), 0);
- assert_eq!(buf.initialized_len(), 0);
-
- let mut buf = [MaybeUninit::uninit()];
- let mut buf = ReadBuf::uninit(&mut buf);
- e.read_buf(&mut buf).unwrap();
- assert_eq!(buf.filled_len(), 0);
- assert_eq!(buf.initialized_len(), 0);
-
- let mut buf = [MaybeUninit::uninit(); 1024];
- let mut buf = ReadBuf::uninit(&mut buf);
- e.read_buf(&mut buf).unwrap();
- assert_eq!(buf.filled_len(), 0);
- assert_eq!(buf.initialized_len(), 0);
-
- let mut buf = [MaybeUninit::uninit(); 1024];
- let mut buf = ReadBuf::uninit(&mut buf);
- e.by_ref().read_buf(&mut buf).unwrap();
- assert_eq!(buf.filled_len(), 0);
- assert_eq!(buf.initialized_len(), 0);
+ let buf: &mut [MaybeUninit<_>] = &mut [];
+ let mut buf: BorrowedBuf<'_> = buf.into();
+ e.read_buf(buf.unfilled()).unwrap();
+ assert_eq!(buf.len(), 0);
+ assert_eq!(buf.init_len(), 0);
+
+ let buf: &mut [_] = &mut [MaybeUninit::uninit()];
+ let mut buf: BorrowedBuf<'_> = buf.into();
+ e.read_buf(buf.unfilled()).unwrap();
+ assert_eq!(buf.len(), 0);
+ assert_eq!(buf.init_len(), 0);
+
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
+ let mut buf: BorrowedBuf<'_> = buf.into();
+ e.read_buf(buf.unfilled()).unwrap();
+ assert_eq!(buf.len(), 0);
+ assert_eq!(buf.init_len(), 0);
+
+ let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
+ let mut buf: BorrowedBuf<'_> = buf.into();
+ e.by_ref().read_buf(buf.unfilled()).unwrap();
+ assert_eq!(buf.len(), 0);
+ assert_eq!(buf.init_len(), 0);
}
#[test]
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index 7157b5af0..e35145c4a 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -1867,11 +1867,15 @@ mod type_keyword {}
/// Code or interfaces whose [memory safety] cannot be verified by the type
/// system.
///
-/// The `unsafe` keyword has two uses: to declare the existence of contracts the
-/// compiler can't check (`unsafe fn` and `unsafe trait`), and to declare that a
-/// programmer has checked that these contracts have been upheld (`unsafe {}`
-/// and `unsafe impl`, but also `unsafe fn` -- see below). They are not mutually
-/// exclusive, as can be seen in `unsafe fn`.
+/// The `unsafe` keyword has two uses:
+/// - to declare the existence of contracts the compiler can't check (`unsafe fn` and `unsafe
+/// trait`),
+/// - and to declare that a programmer has checked that these contracts have been upheld (`unsafe
+/// {}` and `unsafe impl`, but also `unsafe fn` -- see below).
+///
+/// They are not mutually exclusive, as can be seen in `unsafe fn`: the body of an `unsafe fn` is,
+/// by default, treated like an unsafe block. The `unsafe_op_in_unsafe_fn` lint can be enabled to
+/// change that.
///
/// # Unsafe abilities
///
@@ -1914,14 +1918,14 @@ mod type_keyword {}
/// - `unsafe impl`: the contract necessary to implement the trait has been
/// checked by the programmer and is guaranteed to be respected.
///
-/// `unsafe fn` also acts like an `unsafe {}` block
+/// By default, `unsafe fn` also acts like an `unsafe {}` block
/// around the code inside the function. This means it is not just a signal to
/// the caller, but also promises that the preconditions for the operations
-/// inside the function are upheld. Mixing these two meanings can be confusing
-/// and [proposal]s exist to use `unsafe {}` blocks inside such functions when
-/// making `unsafe` operations.
+/// inside the function are upheld. Mixing these two meanings can be confusing, so the
+/// `unsafe_op_in_unsafe_fn` lint can be enabled to warn against that and require explicit unsafe
+/// blocks even inside `unsafe fn`.
///
-/// See the [Rustnomicon] and the [Reference] for more informations.
+/// See the [Rustnomicon] and the [Reference] for more information.
///
/// # Examples
///
@@ -1987,13 +1991,16 @@ mod type_keyword {}
///
/// ```rust
/// # #![allow(dead_code)]
+/// #![deny(unsafe_op_in_unsafe_fn)]
+///
/// /// Dereference the given pointer.
/// ///
/// /// # Safety
/// ///
/// /// `ptr` must be aligned and must not be dangling.
/// unsafe fn deref_unchecked(ptr: *const i32) -> i32 {
-/// *ptr
+/// // SAFETY: the caller is required to ensure that `ptr` is aligned and dereferenceable.
+/// unsafe { *ptr }
/// }
///
/// let a = 3;
@@ -2003,35 +2010,118 @@ mod type_keyword {}
/// unsafe { assert_eq!(*b, deref_unchecked(b)); };
/// ```
///
-/// Traits marked as `unsafe` must be [`impl`]emented using `unsafe impl`. This
-/// makes a guarantee to other `unsafe` code that the implementation satisfies
-/// the trait's safety contract. The [Send] and [Sync] traits are examples of
-/// this behaviour in the standard library.
+/// ## `unsafe` and traits
+///
+/// The interactions of `unsafe` and traits can be surprising, so let us contrast the
+/// two combinations of safe `fn` in `unsafe trait` and `unsafe fn` in safe trait using two
+/// examples:
+///
+/// ```rust
+/// /// # Safety
+/// ///
+/// /// `make_even` must return an even number.
+/// unsafe trait MakeEven {
+/// fn make_even(&self) -> i32;
+/// }
+///
+/// // SAFETY: Our `make_even` always returns something even.
+/// unsafe impl MakeEven for i32 {
+/// fn make_even(&self) -> i32 {
+/// self << 1
+/// }
+/// }
+///
+/// fn use_make_even(x: impl MakeEven) {
+/// if x.make_even() % 2 == 1 {
+/// // SAFETY: this can never happen, because all `MakeEven` implementations
+/// // ensure that `make_even` returns something even.
+/// unsafe { std::hint::unreachable_unchecked() };
+/// }
+/// }
+/// ```
+///
+/// Note how the safety contract of the trait is upheld by the implementation, and is itself used to
+/// uphold the safety contract of the unsafe function `unreachable_unchecked` called by
+/// `use_make_even`. `make_even` itself is a safe function because its *callers* do not have to
+/// worry about any contract, only the *implementation* of `MakeEven` is required to uphold a
+/// certain contract. `use_make_even` is safe because it can use the promise made by `MakeEven`
+/// implementations to uphold the safety contract of the `unsafe fn unreachable_unchecked` it calls.
+///
+/// It is also possible to have `unsafe fn` in a regular safe `trait`:
///
/// ```rust
-/// /// Implementors of this trait must guarantee an element is always
-/// /// accessible with index 3.
-/// unsafe trait ThreeIndexable<T> {
-/// /// Returns a reference to the element with index 3 in `&self`.
-/// fn three(&self) -> &T;
+/// # #![feature(never_type)]
+/// #![deny(unsafe_op_in_unsafe_fn)]
+///
+/// trait Indexable {
+/// const LEN: usize;
+///
+/// /// # Safety
+/// ///
+/// /// The caller must ensure that `idx < LEN`.
+/// unsafe fn idx_unchecked(&self, idx: usize) -> i32;
/// }
///
-/// // The implementation of `ThreeIndexable` for `[T; 4]` is `unsafe`
-/// // because the implementor must abide by a contract the compiler cannot
-/// // check but as a programmer we know there will always be a valid element
-/// // at index 3 to access.
-/// unsafe impl<T> ThreeIndexable<T> for [T; 4] {
-/// fn three(&self) -> &T {
-/// // SAFETY: implementing the trait means there always is an element
-/// // with index 3 accessible.
-/// unsafe { self.get_unchecked(3) }
+/// // The implementation for `i32` doesn't need to do any contract reasoning.
+/// impl Indexable for i32 {
+/// const LEN: usize = 1;
+///
+/// unsafe fn idx_unchecked(&self, idx: usize) -> i32 {
+/// debug_assert_eq!(idx, 0);
+/// *self
/// }
/// }
///
-/// let a = [1, 2, 4, 8];
-/// assert_eq!(a.three(), &8);
+/// // The implementation for arrays exploits the function contract to
+/// // make use of `get_unchecked` on slices and avoid a run-time check.
+/// impl Indexable for [i32; 42] {
+/// const LEN: usize = 42;
+///
+/// unsafe fn idx_unchecked(&self, idx: usize) -> i32 {
+/// // SAFETY: As per this trait's documentation, the caller ensures
+/// // that `idx < 42`.
+/// unsafe { *self.get_unchecked(idx) }
+/// }
+/// }
+///
+/// // The implementation for the never type declares a length of 0,
+/// // which means `idx_unchecked` can never be called.
+/// impl Indexable for ! {
+/// const LEN: usize = 0;
+///
+/// unsafe fn idx_unchecked(&self, idx: usize) -> i32 {
+/// // SAFETY: As per this trait's documentation, the caller ensures
+/// // that `idx < 0`, which is impossible, so this is dead code.
+/// unsafe { std::hint::unreachable_unchecked() }
+/// }
+/// }
+///
+/// fn use_indexable<I: Indexable>(x: I, idx: usize) -> i32 {
+/// if idx < I::LEN {
+/// // SAFETY: We have checked that `idx < I::LEN`.
+/// unsafe { x.idx_unchecked(idx) }
+/// } else {
+/// panic!("index out-of-bounds")
+/// }
+/// }
/// ```
///
+/// This time, `use_indexable` is safe because it uses a run-time check to discharge the safety
+/// contract of `idx_unchecked`. Implementing `Indexable` is safe because when writing
+/// `idx_unchecked`, we don't have to worry: our *callers* need to discharge a proof obligation
+/// (like `use_indexable` does), but the *implementation* of `get_unchecked` has no proof obligation
+/// to contend with. Of course, the implementation of `Indexable` may choose to call other unsafe
+/// operations, and then it needs an `unsafe` *block* to indicate it discharged the proof
+/// obligations of its callees. (We enabled `unsafe_op_in_unsafe_fn`, so the body of `idx_unchecked`
+/// is not implicitly an unsafe block.) For that purpose it can make use of the contract that all
+/// its callers must uphold -- the fact that `idx < LEN`.
+///
+/// Formally speaking, an `unsafe fn` in a trait is a function with *preconditions* that go beyond
+/// those encoded by the argument types (such as `idx < LEN`), whereas an `unsafe trait` can declare
+/// that some of its functions have *postconditions* that go beyond those encoded in the return type
+/// (such as returning an even integer). If a trait needs a function with both extra precondition
+/// and extra postcondition, then it needs an `unsafe fn` in an `unsafe trait`.
+///
/// [`extern`]: keyword.extern.html
/// [`trait`]: keyword.trait.html
/// [`static`]: keyword.static.html
@@ -2043,7 +2133,6 @@ mod type_keyword {}
/// [nomicon-soundness]: ../nomicon/safe-unsafe-meaning.html
/// [soundness]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library
/// [Reference]: ../reference/unsafety.html
-/// [proposal]: https://github.com/rust-lang/rfcs/pull/2585
/// [discussion on Rust Internals]: https://internals.rust-lang.org/t/what-does-unsafe-mean/6696
mod unsafe_keyword {}
@@ -2113,7 +2202,7 @@ mod use_keyword {}
/// Add constraints that must be upheld to use an item.
///
/// `where` allows specifying constraints on lifetime and generic parameters.
-/// The [RFC] introducing `where` contains detailed informations about the
+/// The [RFC] introducing `where` contains detailed information about the
/// keyword.
///
/// # Examples
@@ -2355,7 +2444,7 @@ mod dyn_keyword {}
/// println!("f = {f} and i = {i}");
/// ```
///
-/// See the [Reference][union] for more informations on `union`s.
+/// See the [Reference][union] for more information on `union`s.
///
/// [`struct`]: keyword.struct.html
/// [union]: ../reference/items/unions.html
diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs
deleted file mode 100644
index f8c06c3f9..000000000
--- a/library/std/src/lazy.rs
+++ /dev/null
@@ -1 +0,0 @@
-//! Lazy values and one-time initialization of static data.
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 20d25a608..385585dad 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -145,8 +145,8 @@
//! abstracting over differences in common platforms, most notably Windows and
//! Unix derivatives.
//!
-//! Common types of I/O, including [files], [TCP], [UDP], are defined in the
-//! [`io`], [`fs`], and [`net`] modules.
+//! Common types of I/O, including [files], [TCP], and [UDP], are defined in
+//! the [`io`], [`fs`], and [`net`] modules.
//!
//! The [`thread`] module contains Rust's threading abstractions. [`sync`]
//! contains further primitive shared memory types, including [`atomic`] and
@@ -187,6 +187,7 @@
//! [rust-discord]: https://discord.gg/rust-lang
//! [array]: prim@array
//! [slice]: prim@slice
+
#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))]
#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))]
#![doc(
@@ -201,25 +202,35 @@
no_global_oom_handling,
not(no_global_oom_handling)
))]
+// To run libstd tests without x.py without ending up with two copies of libstd, Miri needs to be
+// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
+// rustc itself never sets the feature, so this line has no affect there.
+#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
+// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies.
+#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))]
// Don't link to std. We are std.
#![no_std]
+// Tell the compiler to link to either panic_abort or panic_unwind
+#![needs_panic_runtime]
+//
+// Lints:
#![warn(deprecated_in_future)]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![allow(explicit_outlives_requirements)]
#![allow(unused_lifetimes)]
-// Tell the compiler to link to either panic_abort or panic_unwind
-#![needs_panic_runtime]
+#![deny(rustc::existing_doc_keyword)]
// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind`
-#![cfg_attr(not(bootstrap), deny(ffi_unwind_calls))]
+#![deny(ffi_unwind_calls)]
// std may use features in a platform-specific way
#![allow(unused_features)]
+//
+// Features:
#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))]
#![cfg_attr(
all(target_vendor = "fortanix", target_env = "sgx"),
feature(slice_index_methods, coerce_unsized, sgx_platform)
)]
-#![deny(rustc::existing_doc_keyword)]
//
// Language features:
#![feature(alloc_error_handler)]
@@ -240,12 +251,13 @@
#![feature(doc_notable_trait)]
#![feature(dropck_eyepatch)]
#![feature(exhaustive_patterns)]
+#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
-#![feature(label_break_value)]
+#![feature(is_terminal)]
#![feature(lang_items)]
#![feature(let_chains)]
-#![feature(let_else)]
#![feature(linkage)]
+#![feature(link_cfg)]
#![feature(min_specialization)]
#![feature(must_not_suspend)]
#![feature(needs_panic_runtime)]
@@ -258,6 +270,7 @@
#![feature(staged_api)]
#![feature(thread_local)]
#![feature(try_blocks)]
+#![feature(utf8_chunks)]
//
// Library features (core):
#![feature(array_error_internals)]
@@ -267,23 +280,27 @@
#![feature(core_intrinsics)]
#![feature(cstr_from_bytes_until_nul)]
#![feature(cstr_internals)]
-#![feature(duration_checked_float)]
#![feature(duration_constants)]
+#![feature(error_generic_member_access)]
+#![feature(error_in_core)]
+#![feature(error_iter)]
#![feature(exact_size_is_empty)]
#![feature(exclusive_wrapper)]
#![feature(extend_one)]
#![feature(float_minimum_maximum)]
+#![feature(float_next_up_down)]
#![feature(hasher_prefixfree_extras)]
#![feature(hashmap_internals)]
#![feature(int_error_internals)]
-#![feature(is_some_with)]
+#![feature(is_some_and)]
#![feature(maybe_uninit_slice)]
#![feature(maybe_uninit_write_slice)]
-#![feature(mixed_integer_ops)]
#![feature(nonnull_slice_from_raw_parts)]
#![feature(panic_can_unwind)]
#![feature(panic_info_message)]
#![feature(panic_internals)]
+#![feature(pointer_byte_offsets)]
+#![feature(pointer_is_aligned)]
#![feature(portable_simd)]
#![feature(prelude_2024)]
#![feature(provide_any)]
@@ -294,6 +311,9 @@
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(strict_provenance)]
+#![feature(maybe_uninit_uninit_array)]
+#![feature(const_maybe_uninit_uninit_array)]
+#![feature(const_waker)]
//
// Library features (alloc):
#![feature(alloc_layout_extra)]
@@ -329,9 +349,9 @@
#![feature(trace_macros)]
//
// Only used in tests/benchmarks:
-#![feature(bench_black_box)]
//
// Only for const-ness:
+#![feature(const_collections_with_hasher)]
#![feature(const_io_structs)]
#![feature(const_ip)]
#![feature(const_ipv4)]
@@ -509,9 +529,6 @@ pub mod process;
pub mod sync;
pub mod time;
-#[unstable(feature = "once_cell", issue = "74465")]
-pub mod lazy;
-
// Pull in `std_float` crate into libstd. The contents of
// `std_float` are in a different repository: rust-lang/portable-simd.
#[path = "../../portable-simd/crates/std_float/src/lib.rs"]
@@ -576,6 +593,7 @@ pub mod alloc;
// Private support modules
mod panicking;
+mod personality;
#[path = "../../backtrace/src/lib.rs"]
#[allow(dead_code, unused_attributes)]
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index 0cb21ef53..6e4ba1404 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -27,17 +27,31 @@ macro_rules! panic {
/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted
/// immediately.
///
+/// The `print!` macro will lock the standard output on each call. If you call
+/// `print!` within a hot loop, this behavior may be the bottleneck of the loop.
+/// To avoid this, lock stdout with [`io::stdout().lock()`][lock]:
+/// ```
+/// use std::io::{stdout, Write};
+///
+/// let mut lock = stdout().lock();
+/// write!(lock, "hello world").unwrap();
+/// ```
+///
/// Use `print!` only for the primary output of your program. Use
/// [`eprint!`] instead to print error and progress messages.
///
/// [flush]: crate::io::Write::flush
/// [`println!`]: crate::println
/// [`eprint!`]: crate::eprint
+/// [lock]: crate::io::Stdout
///
/// # Panics
///
/// Panics if writing to `io::stdout()` fails.
///
+/// Writing to non-blocking stdout can cause an error, which will lead
+/// this macro to panic.
+///
/// # Examples
///
/// ```
@@ -75,16 +89,30 @@ macro_rules! print {
/// This macro uses the same syntax as [`format!`], but writes to the standard output instead.
/// See [`std::fmt`] for more information.
///
+/// The `println!` macro will lock the standard output on each call. If you call
+/// `println!` within a hot loop, this behavior may be the bottleneck of the loop.
+/// To avoid this, lock stdout with [`io::stdout().lock()`][lock]:
+/// ```
+/// use std::io::{stdout, Write};
+///
+/// let mut lock = stdout().lock();
+/// writeln!(lock, "hello world").unwrap();
+/// ```
+///
/// Use `println!` only for the primary output of your program. Use
/// [`eprintln!`] instead to print error and progress messages.
///
/// [`std::fmt`]: crate::fmt
/// [`eprintln!`]: crate::eprintln
+/// [lock]: crate::io::Stdout
///
/// # Panics
///
/// Panics if writing to [`io::stdout`] fails.
///
+/// Writing to non-blocking stdout can cause an error, which will lead
+/// this macro to panic.
+///
/// [`io::stdout`]: crate::io::stdout
///
/// # Examples
@@ -93,6 +121,8 @@ macro_rules! print {
/// println!(); // prints just a newline
/// println!("hello there!");
/// println!("format {} arguments", "some");
+/// let local_variable = "some";
+/// println!("format {local_variable} arguments");
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
@@ -123,6 +153,9 @@ macro_rules! println {
///
/// Panics if writing to `io::stderr` fails.
///
+/// Writing to non-blocking stdout can cause an error, which will lead
+/// this macro to panic.
+///
/// # Examples
///
/// ```
@@ -155,6 +188,9 @@ macro_rules! eprint {
///
/// Panics if writing to `io::stderr` fails.
///
+/// Writing to non-blocking stdout can cause an error, which will lead
+/// this macro to panic.
+///
/// # Examples
///
/// ```
diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr.rs
deleted file mode 100644
index 53fee952a..000000000
--- a/library/std/src/net/addr.rs
+++ /dev/null
@@ -1,988 +0,0 @@
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests;
-
-use crate::cmp::Ordering;
-use crate::fmt;
-use crate::hash;
-use crate::io::{self, Write};
-use crate::iter;
-use crate::mem;
-use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-use crate::option;
-use crate::slice;
-use crate::sys::net::netc as c;
-use crate::sys_common::net::LookupHost;
-use crate::sys_common::{FromInner, IntoInner};
-use crate::vec;
-
-/// An internet socket address, either IPv4 or IPv6.
-///
-/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well
-/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and
-/// [`SocketAddrV6`]'s respective documentation for more details.
-///
-/// The size of a `SocketAddr` instance may vary depending on the target operating
-/// system.
-///
-/// [IP address]: IpAddr
-///
-/// # Examples
-///
-/// ```
-/// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
-///
-/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
-///
-/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket));
-/// assert_eq!(socket.port(), 8080);
-/// assert_eq!(socket.is_ipv4(), true);
-/// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub enum SocketAddr {
- /// An IPv4 socket address.
- #[stable(feature = "rust1", since = "1.0.0")]
- V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4),
- /// An IPv6 socket address.
- #[stable(feature = "rust1", since = "1.0.0")]
- V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6),
-}
-
-/// An IPv4 socket address.
-///
-/// IPv4 socket addresses consist of an [`IPv4` address] and a 16-bit port number, as
-/// stated in [IETF RFC 793].
-///
-/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses.
-///
-/// The size of a `SocketAddrV4` struct may vary depending on the target operating
-/// system. Do not assume that this type has the same memory layout as the underlying
-/// system representation.
-///
-/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793
-/// [`IPv4` address]: Ipv4Addr
-///
-/// # Examples
-///
-/// ```
-/// use std::net::{Ipv4Addr, SocketAddrV4};
-///
-/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
-///
-/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket));
-/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1));
-/// assert_eq!(socket.port(), 8080);
-/// ```
-#[derive(Copy, Clone, Eq, PartialEq)]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct SocketAddrV4 {
- ip: Ipv4Addr,
- port: u16,
-}
-
-/// An IPv6 socket address.
-///
-/// IPv6 socket addresses consist of an [`IPv6` address], a 16-bit port number, as well
-/// as fields containing the traffic class, the flow label, and a scope identifier
-/// (see [IETF RFC 2553, Section 3.3] for more details).
-///
-/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses.
-///
-/// The size of a `SocketAddrV6` struct may vary depending on the target operating
-/// system. Do not assume that this type has the same memory layout as the underlying
-/// system representation.
-///
-/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
-/// [`IPv6` address]: Ipv6Addr
-///
-/// # Examples
-///
-/// ```
-/// use std::net::{Ipv6Addr, SocketAddrV6};
-///
-/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
-///
-/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket));
-/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
-/// assert_eq!(socket.port(), 8080);
-/// ```
-#[derive(Copy, Clone, Eq, PartialEq)]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct SocketAddrV6 {
- ip: Ipv6Addr,
- port: u16,
- flowinfo: u32,
- scope_id: u32,
-}
-
-impl SocketAddr {
- /// Creates a new socket address from an [IP address] and a port number.
- ///
- /// [IP address]: IpAddr
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
- ///
- /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
- /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
- /// assert_eq!(socket.port(), 8080);
- /// ```
- #[stable(feature = "ip_addr", since = "1.7.0")]
- #[must_use]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn new(ip: IpAddr, port: u16) -> SocketAddr {
- match ip {
- IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)),
- IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)),
- }
- }
-
- /// Returns the IP address associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
- ///
- /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
- /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
- /// ```
- #[must_use]
- #[stable(feature = "ip_addr", since = "1.7.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn ip(&self) -> IpAddr {
- match *self {
- SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()),
- SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()),
- }
- }
-
- /// Changes the IP address associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
- ///
- /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
- /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1)));
- /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1)));
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_ip(&mut self, new_ip: IpAddr) {
- // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away.
- match (self, new_ip) {
- (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip),
- (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip),
- (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()),
- }
- }
-
- /// Returns the port number associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
- ///
- /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
- /// assert_eq!(socket.port(), 8080);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn port(&self) -> u16 {
- match *self {
- SocketAddr::V4(ref a) => a.port(),
- SocketAddr::V6(ref a) => a.port(),
- }
- }
-
- /// Changes the port number associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
- ///
- /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
- /// socket.set_port(1025);
- /// assert_eq!(socket.port(), 1025);
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_port(&mut self, new_port: u16) {
- match *self {
- SocketAddr::V4(ref mut a) => a.set_port(new_port),
- SocketAddr::V6(ref mut a) => a.set_port(new_port),
- }
- }
-
- /// Returns [`true`] if the [IP address] in this `SocketAddr` is an
- /// [`IPv4` address], and [`false`] otherwise.
- ///
- /// [IP address]: IpAddr
- /// [`IPv4` address]: IpAddr::V4
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
- ///
- /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
- /// assert_eq!(socket.is_ipv4(), true);
- /// assert_eq!(socket.is_ipv6(), false);
- /// ```
- #[must_use]
- #[stable(feature = "sockaddr_checker", since = "1.16.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn is_ipv4(&self) -> bool {
- matches!(*self, SocketAddr::V4(_))
- }
-
- /// Returns [`true`] if the [IP address] in this `SocketAddr` is an
- /// [`IPv6` address], and [`false`] otherwise.
- ///
- /// [IP address]: IpAddr
- /// [`IPv6` address]: IpAddr::V6
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv6Addr, SocketAddr};
- ///
- /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080);
- /// assert_eq!(socket.is_ipv4(), false);
- /// assert_eq!(socket.is_ipv6(), true);
- /// ```
- #[must_use]
- #[stable(feature = "sockaddr_checker", since = "1.16.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn is_ipv6(&self) -> bool {
- matches!(*self, SocketAddr::V6(_))
- }
-}
-
-impl SocketAddrV4 {
- /// Creates a new socket address from an [`IPv4` address] and a port number.
- ///
- /// [`IPv4` address]: Ipv4Addr
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV4, Ipv4Addr};
- ///
- /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 {
- SocketAddrV4 { ip, port }
- }
-
- /// Returns the IP address associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV4, Ipv4Addr};
- ///
- /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
- /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1));
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn ip(&self) -> &Ipv4Addr {
- &self.ip
- }
-
- /// Changes the IP address associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV4, Ipv4Addr};
- ///
- /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
- /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1));
- /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1));
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_ip(&mut self, new_ip: Ipv4Addr) {
- self.ip = new_ip;
- }
-
- /// Returns the port number associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV4, Ipv4Addr};
- ///
- /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
- /// assert_eq!(socket.port(), 8080);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn port(&self) -> u16 {
- self.port
- }
-
- /// Changes the port number associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV4, Ipv4Addr};
- ///
- /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
- /// socket.set_port(4242);
- /// assert_eq!(socket.port(), 4242);
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_port(&mut self, new_port: u16) {
- self.port = new_port;
- }
-}
-
-impl SocketAddrV6 {
- /// Creates a new socket address from an [`IPv6` address], a 16-bit port number,
- /// and the `flowinfo` and `scope_id` fields.
- ///
- /// For more information on the meaning and layout of the `flowinfo` and `scope_id`
- /// parameters, see [IETF RFC 2553, Section 3.3].
- ///
- /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
- /// [`IPv6` address]: Ipv6Addr
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 {
- SocketAddrV6 { ip, port, flowinfo, scope_id }
- }
-
- /// Returns the IP address associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
- /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn ip(&self) -> &Ipv6Addr {
- &self.ip
- }
-
- /// Changes the IP address associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
- /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0));
- /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0));
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_ip(&mut self, new_ip: Ipv6Addr) {
- self.ip = new_ip;
- }
-
- /// Returns the port number associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
- /// assert_eq!(socket.port(), 8080);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn port(&self) -> u16 {
- self.port
- }
-
- /// Changes the port number associated with this socket address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
- /// socket.set_port(4242);
- /// assert_eq!(socket.port(), 4242);
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_port(&mut self, new_port: u16) {
- self.port = new_port;
- }
-
- /// Returns the flow information associated with this address.
- ///
- /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`,
- /// as specified in [IETF RFC 2553, Section 3.3].
- /// It combines information about the flow label and the traffic class as specified
- /// in [IETF RFC 2460], respectively [Section 6] and [Section 7].
- ///
- /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
- /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460
- /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6
- /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0);
- /// assert_eq!(socket.flowinfo(), 10);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn flowinfo(&self) -> u32 {
- self.flowinfo
- }
-
- /// Changes the flow information associated with this socket address.
- ///
- /// See [`SocketAddrV6::flowinfo`]'s documentation for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0);
- /// socket.set_flowinfo(56);
- /// assert_eq!(socket.flowinfo(), 56);
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_flowinfo(&mut self, new_flowinfo: u32) {
- self.flowinfo = new_flowinfo;
- }
-
- /// Returns the scope ID associated with this address.
- ///
- /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`,
- /// as specified in [IETF RFC 2553, Section 3.3].
- ///
- /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78);
- /// assert_eq!(socket.scope_id(), 78);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
- pub const fn scope_id(&self) -> u32 {
- self.scope_id
- }
-
- /// Changes the scope ID associated with this socket address.
- ///
- /// See [`SocketAddrV6::scope_id`]'s documentation for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{SocketAddrV6, Ipv6Addr};
- ///
- /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78);
- /// socket.set_scope_id(42);
- /// assert_eq!(socket.scope_id(), 42);
- /// ```
- #[stable(feature = "sockaddr_setters", since = "1.9.0")]
- pub fn set_scope_id(&mut self, new_scope_id: u32) {
- self.scope_id = new_scope_id;
- }
-}
-
-impl FromInner<c::sockaddr_in> for SocketAddrV4 {
- fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 {
- SocketAddrV4 { ip: Ipv4Addr::from_inner(addr.sin_addr), port: u16::from_be(addr.sin_port) }
- }
-}
-
-impl FromInner<c::sockaddr_in6> for SocketAddrV6 {
- fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 {
- SocketAddrV6 {
- ip: Ipv6Addr::from_inner(addr.sin6_addr),
- port: u16::from_be(addr.sin6_port),
- flowinfo: addr.sin6_flowinfo,
- scope_id: addr.sin6_scope_id,
- }
- }
-}
-
-impl IntoInner<c::sockaddr_in> for SocketAddrV4 {
- fn into_inner(self) -> c::sockaddr_in {
- c::sockaddr_in {
- sin_family: c::AF_INET as c::sa_family_t,
- sin_port: self.port.to_be(),
- sin_addr: self.ip.into_inner(),
- ..unsafe { mem::zeroed() }
- }
- }
-}
-
-impl IntoInner<c::sockaddr_in6> for SocketAddrV6 {
- fn into_inner(self) -> c::sockaddr_in6 {
- c::sockaddr_in6 {
- sin6_family: c::AF_INET6 as c::sa_family_t,
- sin6_port: self.port.to_be(),
- sin6_addr: self.ip.into_inner(),
- sin6_flowinfo: self.flowinfo,
- sin6_scope_id: self.scope_id,
- ..unsafe { mem::zeroed() }
- }
- }
-}
-
-#[stable(feature = "ip_from_ip", since = "1.16.0")]
-impl From<SocketAddrV4> for SocketAddr {
- /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`].
- fn from(sock4: SocketAddrV4) -> SocketAddr {
- SocketAddr::V4(sock4)
- }
-}
-
-#[stable(feature = "ip_from_ip", since = "1.16.0")]
-impl From<SocketAddrV6> for SocketAddr {
- /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`].
- fn from(sock6: SocketAddrV6) -> SocketAddr {
- SocketAddr::V6(sock6)
- }
-}
-
-#[stable(feature = "addr_from_into_ip", since = "1.17.0")]
-impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr {
- /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`].
- ///
- /// This conversion creates a [`SocketAddr::V4`] for an [`IpAddr::V4`]
- /// and creates a [`SocketAddr::V6`] for an [`IpAddr::V6`].
- ///
- /// `u16` is treated as port of the newly created [`SocketAddr`].
- fn from(pieces: (I, u16)) -> SocketAddr {
- SocketAddr::new(pieces.0.into(), pieces.1)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Display for SocketAddr {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- SocketAddr::V4(ref a) => a.fmt(f),
- SocketAddr::V6(ref a) => a.fmt(f),
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for SocketAddr {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, fmt)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Display for SocketAddrV4 {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Fast path: if there's no alignment stuff, write to the output buffer
- // directly
- if f.precision().is_none() && f.width().is_none() {
- write!(f, "{}:{}", self.ip(), self.port())
- } else {
- const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments
- + 3 // the separators
- + 1 + 5; // the port
- let mut buf = [0; IPV4_SOCKET_BUF_LEN];
- let mut buf_slice = &mut buf[..];
-
- // Unwrap is fine because writing to a sufficiently-sized
- // buffer is infallible
- write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
- let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
-
- // This unsafe is OK because we know what is being written to the buffer
- let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
- f.pad(buf)
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for SocketAddrV4 {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, fmt)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Display for SocketAddrV6 {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Fast path: if there's no alignment stuff, write to the output
- // buffer directly
- if f.precision().is_none() && f.width().is_none() {
- match self.scope_id() {
- 0 => write!(f, "[{}]:{}", self.ip(), self.port()),
- scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
- }
- } else {
- const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address
- + 7 // The colon separators
- + 2 // The brackets
- + 1 + 10 // The scope id
- + 1 + 5; // The port
-
- let mut buf = [0; IPV6_SOCKET_BUF_LEN];
- let mut buf_slice = &mut buf[..];
-
- match self.scope_id() {
- 0 => write!(buf_slice, "[{}]:{}", self.ip(), self.port()),
- scope_id => write!(buf_slice, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
- }
- // Unwrap is fine because writing to a sufficiently-sized
- // buffer is infallible
- .unwrap();
- let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
-
- // This unsafe is OK because we know what is being written to the buffer
- let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
- f.pad(buf)
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for SocketAddrV6 {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, fmt)
- }
-}
-
-#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
-impl PartialOrd for SocketAddrV4 {
- fn partial_cmp(&self, other: &SocketAddrV4) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
-impl PartialOrd for SocketAddrV6 {
- fn partial_cmp(&self, other: &SocketAddrV6) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
-impl Ord for SocketAddrV4 {
- fn cmp(&self, other: &SocketAddrV4) -> Ordering {
- self.ip().cmp(other.ip()).then(self.port().cmp(&other.port()))
- }
-}
-
-#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
-impl Ord for SocketAddrV6 {
- fn cmp(&self, other: &SocketAddrV6) -> Ordering {
- self.ip().cmp(other.ip()).then(self.port().cmp(&other.port()))
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl hash::Hash for SocketAddrV4 {
- fn hash<H: hash::Hasher>(&self, s: &mut H) {
- (self.port, self.ip).hash(s)
- }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl hash::Hash for SocketAddrV6 {
- fn hash<H: hash::Hasher>(&self, s: &mut H) {
- (self.port, &self.ip, self.flowinfo, self.scope_id).hash(s)
- }
-}
-
-/// A trait for objects which can be converted or resolved to one or more
-/// [`SocketAddr`] values.
-///
-/// This trait is used for generic address resolution when constructing network
-/// objects. By default it is implemented for the following types:
-///
-/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function.
-///
-/// * [`SocketAddrV4`], [`SocketAddrV6`], <code>([IpAddr], [u16])</code>,
-/// <code>([Ipv4Addr], [u16])</code>, <code>([Ipv6Addr], [u16])</code>:
-/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially.
-///
-/// * <code>(&[str], [u16])</code>: <code>&[str]</code> should be either a string representation
-/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host
-/// name. [`u16`] is the port number.
-///
-/// * <code>&[str]</code>: the string should be either a string representation of a
-/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like
-/// `<host_name>:<port>` pair where `<port>` is a [`u16`] value.
-///
-/// This trait allows constructing network objects like [`TcpStream`] or
-/// [`UdpSocket`] easily with values of various types for the bind/connection
-/// address. It is needed because sometimes one type is more appropriate than
-/// the other: for simple uses a string like `"localhost:12345"` is much nicer
-/// than manual construction of the corresponding [`SocketAddr`], but sometimes
-/// [`SocketAddr`] value is *the* main source of the address, and converting it to
-/// some other type (e.g., a string) just for it to be converted back to
-/// [`SocketAddr`] in constructor methods is pointless.
-///
-/// Addresses returned by the operating system that are not IP addresses are
-/// silently ignored.
-///
-/// [`FromStr`]: crate::str::FromStr "std::str::FromStr"
-/// [`TcpStream`]: crate::net::TcpStream "net::TcpStream"
-/// [`to_socket_addrs`]: ToSocketAddrs::to_socket_addrs
-/// [`UdpSocket`]: crate::net::UdpSocket "net::UdpSocket"
-///
-/// # Examples
-///
-/// Creating a [`SocketAddr`] iterator that yields one item:
-///
-/// ```
-/// use std::net::{ToSocketAddrs, SocketAddr};
-///
-/// let addr = SocketAddr::from(([127, 0, 0, 1], 443));
-/// let mut addrs_iter = addr.to_socket_addrs().unwrap();
-///
-/// assert_eq!(Some(addr), addrs_iter.next());
-/// assert!(addrs_iter.next().is_none());
-/// ```
-///
-/// Creating a [`SocketAddr`] iterator from a hostname:
-///
-/// ```no_run
-/// use std::net::{SocketAddr, ToSocketAddrs};
-///
-/// // assuming 'localhost' resolves to 127.0.0.1
-/// let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap();
-/// assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443))));
-/// assert!(addrs_iter.next().is_none());
-///
-/// // assuming 'foo' does not resolve
-/// assert!("foo:443".to_socket_addrs().is_err());
-/// ```
-///
-/// Creating a [`SocketAddr`] iterator that yields multiple items:
-///
-/// ```
-/// use std::net::{SocketAddr, ToSocketAddrs};
-///
-/// let addr1 = SocketAddr::from(([0, 0, 0, 0], 80));
-/// let addr2 = SocketAddr::from(([127, 0, 0, 1], 443));
-/// let addrs = vec![addr1, addr2];
-///
-/// let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap();
-///
-/// assert_eq!(Some(addr1), addrs_iter.next());
-/// assert_eq!(Some(addr2), addrs_iter.next());
-/// assert!(addrs_iter.next().is_none());
-/// ```
-///
-/// Attempting to create a [`SocketAddr`] iterator from an improperly formatted
-/// socket address `&str` (missing the port):
-///
-/// ```
-/// use std::io;
-/// use std::net::ToSocketAddrs;
-///
-/// let err = "127.0.0.1".to_socket_addrs().unwrap_err();
-/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
-/// ```
-///
-/// [`TcpStream::connect`] is an example of an function that utilizes
-/// `ToSocketAddrs` as a trait bound on its parameter in order to accept
-/// different types:
-///
-/// ```no_run
-/// use std::net::{TcpStream, Ipv4Addr};
-///
-/// let stream = TcpStream::connect(("127.0.0.1", 443));
-/// // or
-/// let stream = TcpStream::connect("127.0.0.1:443");
-/// // or
-/// let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443));
-/// ```
-///
-/// [`TcpStream::connect`]: crate::net::TcpStream::connect
-#[stable(feature = "rust1", since = "1.0.0")]
-pub trait ToSocketAddrs {
- /// Returned iterator over socket addresses which this type may correspond
- /// to.
- #[stable(feature = "rust1", since = "1.0.0")]
- type Iter: Iterator<Item = SocketAddr>;
-
- /// Converts this object to an iterator of resolved [`SocketAddr`]s.
- ///
- /// The returned iterator might not actually yield any values depending on the
- /// outcome of any resolution performed.
- ///
- /// Note that this function may block the current thread while resolution is
- /// performed.
- #[stable(feature = "rust1", since = "1.0.0")]
- fn to_socket_addrs(&self) -> io::Result<Self::Iter>;
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for SocketAddr {
- type Iter = option::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
- Ok(Some(*self).into_iter())
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for SocketAddrV4 {
- type Iter = option::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
- SocketAddr::V4(*self).to_socket_addrs()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for SocketAddrV6 {
- type Iter = option::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
- SocketAddr::V6(*self).to_socket_addrs()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for (IpAddr, u16) {
- type Iter = option::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
- let (ip, port) = *self;
- match ip {
- IpAddr::V4(ref a) => (*a, port).to_socket_addrs(),
- IpAddr::V6(ref a) => (*a, port).to_socket_addrs(),
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for (Ipv4Addr, u16) {
- type Iter = option::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
- let (ip, port) = *self;
- SocketAddrV4::new(ip, port).to_socket_addrs()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for (Ipv6Addr, u16) {
- type Iter = option::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
- let (ip, port) = *self;
- SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs()
- }
-}
-
-fn resolve_socket_addr(lh: LookupHost) -> io::Result<vec::IntoIter<SocketAddr>> {
- let p = lh.port();
- let v: Vec<_> = lh
- .map(|mut a| {
- a.set_port(p);
- a
- })
- .collect();
- Ok(v.into_iter())
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for (&str, u16) {
- type Iter = vec::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
- let (host, port) = *self;
-
- // try to parse the host as a regular IP address first
- if let Ok(addr) = host.parse::<Ipv4Addr>() {
- let addr = SocketAddrV4::new(addr, port);
- return Ok(vec![SocketAddr::V4(addr)].into_iter());
- }
- if let Ok(addr) = host.parse::<Ipv6Addr>() {
- let addr = SocketAddrV6::new(addr, port, 0, 0);
- return Ok(vec![SocketAddr::V6(addr)].into_iter());
- }
-
- resolve_socket_addr((host, port).try_into()?)
- }
-}
-
-#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")]
-impl ToSocketAddrs for (String, u16) {
- type Iter = vec::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
- (&*self.0, self.1).to_socket_addrs()
- }
-}
-
-// accepts strings like 'localhost:12345'
-#[stable(feature = "rust1", since = "1.0.0")]
-impl ToSocketAddrs for str {
- type Iter = vec::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
- // try to parse as a regular SocketAddr first
- if let Ok(addr) = self.parse() {
- return Ok(vec![addr].into_iter());
- }
-
- resolve_socket_addr(self.try_into()?)
- }
-}
-
-#[stable(feature = "slice_to_socket_addrs", since = "1.8.0")]
-impl<'a> ToSocketAddrs for &'a [SocketAddr] {
- type Iter = iter::Cloned<slice::Iter<'a, SocketAddr>>;
-
- fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
- Ok(self.iter().cloned())
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {
- type Iter = T::Iter;
- fn to_socket_addrs(&self) -> io::Result<T::Iter> {
- (**self).to_socket_addrs()
- }
-}
-
-#[stable(feature = "string_to_socket_addrs", since = "1.16.0")]
-impl ToSocketAddrs for String {
- type Iter = vec::IntoIter<SocketAddr>;
- fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
- (&**self).to_socket_addrs()
- }
-}
diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/addr/tests.rs
deleted file mode 100644
index 585a17451..000000000
--- a/library/std/src/net/addr/tests.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-use crate::net::test::{sa4, sa6, tsa};
-use crate::net::*;
-
-#[test]
-fn to_socket_addr_ipaddr_u16() {
- let a = Ipv4Addr::new(77, 88, 21, 11);
- let p = 12345;
- let e = SocketAddr::V4(SocketAddrV4::new(a, p));
- assert_eq!(Ok(vec![e]), tsa((a, p)));
-}
-
-#[test]
-fn to_socket_addr_str_u16() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
- assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352)));
-
- let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
- assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53)));
-
- let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924);
- #[cfg(not(target_env = "sgx"))]
- assert!(tsa(("localhost", 23924)).unwrap().contains(&a));
- #[cfg(target_env = "sgx")]
- let _ = a;
-}
-
-#[test]
-fn to_socket_addr_str() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
- assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352"));
-
- let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
- assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53"));
-
- let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924);
- #[cfg(not(target_env = "sgx"))]
- assert!(tsa("localhost:23924").unwrap().contains(&a));
- #[cfg(target_env = "sgx")]
- let _ = a;
-}
-
-#[test]
-fn to_socket_addr_string() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
- assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352")));
- assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352")));
- assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352")));
-
- let s = format!("{}:{}", "77.88.21.11", "24352");
- assert_eq!(Ok(vec![a]), tsa(s));
- // s has been moved into the tsa call
-}
-
-#[test]
-fn bind_udp_socket_bad() {
- // rust-lang/rust#53957: This is a regression test for a parsing problem
- // discovered as part of issue rust-lang/rust#23076, where we were
- // incorrectly parsing invalid input and then that would result in a
- // successful `UdpSocket` binding when we would expect failure.
- //
- // At one time, this test was written as a call to `tsa` with
- // INPUT_23076. However, that structure yields an unreliable test,
- // because it ends up passing junk input to the DNS server, and some DNS
- // servers will respond with `Ok` to such input, with the ip address of
- // the DNS server itself.
- //
- // This form of the test is more robust: even when the DNS server
- // returns its own address, it is still an error to bind a UDP socket to
- // a non-local address, and so we still get an error here in that case.
-
- const INPUT_23076: &str = "1200::AB00:1234::2552:7777:1313:34300";
-
- assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err())
-}
-
-#[test]
-fn set_ip() {
- fn ip4(low: u8) -> Ipv4Addr {
- Ipv4Addr::new(77, 88, 21, low)
- }
- fn ip6(low: u16) -> Ipv6Addr {
- Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low)
- }
-
- let mut v4 = SocketAddrV4::new(ip4(11), 80);
- assert_eq!(v4.ip(), &ip4(11));
- v4.set_ip(ip4(12));
- assert_eq!(v4.ip(), &ip4(12));
-
- let mut addr = SocketAddr::V4(v4);
- assert_eq!(addr.ip(), IpAddr::V4(ip4(12)));
- addr.set_ip(IpAddr::V4(ip4(13)));
- assert_eq!(addr.ip(), IpAddr::V4(ip4(13)));
- addr.set_ip(IpAddr::V6(ip6(14)));
- assert_eq!(addr.ip(), IpAddr::V6(ip6(14)));
-
- let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0);
- assert_eq!(v6.ip(), &ip6(1));
- v6.set_ip(ip6(2));
- assert_eq!(v6.ip(), &ip6(2));
-
- let mut addr = SocketAddr::V6(v6);
- assert_eq!(addr.ip(), IpAddr::V6(ip6(2)));
- addr.set_ip(IpAddr::V6(ip6(3)));
- assert_eq!(addr.ip(), IpAddr::V6(ip6(3)));
- addr.set_ip(IpAddr::V4(ip4(4)));
- assert_eq!(addr.ip(), IpAddr::V4(ip4(4)));
-}
-
-#[test]
-fn set_port() {
- let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80);
- assert_eq!(v4.port(), 80);
- v4.set_port(443);
- assert_eq!(v4.port(), 443);
-
- let mut addr = SocketAddr::V4(v4);
- assert_eq!(addr.port(), 443);
- addr.set_port(8080);
- assert_eq!(addr.port(), 8080);
-
- let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0);
- assert_eq!(v6.port(), 80);
- v6.set_port(443);
- assert_eq!(v6.port(), 443);
-
- let mut addr = SocketAddr::V6(v6);
- assert_eq!(addr.port(), 443);
- addr.set_port(8080);
- assert_eq!(addr.port(), 8080);
-}
-
-#[test]
-fn set_flowinfo() {
- let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0);
- assert_eq!(v6.flowinfo(), 10);
- v6.set_flowinfo(20);
- assert_eq!(v6.flowinfo(), 20);
-}
-
-#[test]
-fn set_scope_id() {
- let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10);
- assert_eq!(v6.scope_id(), 10);
- v6.set_scope_id(20);
- assert_eq!(v6.scope_id(), 20);
-}
-
-#[test]
-fn is_v4() {
- let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80));
- assert!(v4.is_ipv4());
- assert!(!v4.is_ipv6());
-}
-
-#[test]
-fn is_v6() {
- let v6 = SocketAddr::V6(SocketAddrV6::new(
- Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1),
- 80,
- 10,
- 0,
- ));
- assert!(!v6.is_ipv4());
- assert!(v6.is_ipv6());
-}
-
-#[test]
-fn socket_v4_to_str() {
- let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
-
- assert_eq!(format!("{socket}"), "192.168.0.1:8080");
- assert_eq!(format!("{socket:<20}"), "192.168.0.1:8080 ");
- assert_eq!(format!("{socket:>20}"), " 192.168.0.1:8080");
- assert_eq!(format!("{socket:^20}"), " 192.168.0.1:8080 ");
- assert_eq!(format!("{socket:.10}"), "192.168.0.");
-}
-
-#[test]
-fn socket_v6_to_str() {
- let mut socket = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0);
-
- assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1]:53");
- assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1]:53 ");
- assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1]:53");
- assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1]:53 ");
- assert_eq!(format!("{socket:.15}"), "[2a02:6b8:0:1::");
-
- socket.set_scope_id(5);
-
- assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1%5]:53");
- assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1%5]:53 ");
- assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1%5]:53");
- assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1%5]:53 ");
- assert_eq!(format!("{socket:.18}"), "[2a02:6b8:0:1::1%5");
-}
-
-#[test]
-fn compare() {
- let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();
- let v4_2 = "224.210.103.5:12345".parse::<SocketAddrV4>().unwrap();
- let v4_3 = "224.210.103.5:23456".parse::<SocketAddrV4>().unwrap();
- let v6_1 = "[2001:db8:f00::1002]:23456".parse::<SocketAddrV6>().unwrap();
- let v6_2 = "[2001:db8:f00::2001]:12345".parse::<SocketAddrV6>().unwrap();
- let v6_3 = "[2001:db8:f00::2001]:23456".parse::<SocketAddrV6>().unwrap();
-
- // equality
- assert_eq!(v4_1, v4_1);
- assert_eq!(v6_1, v6_1);
- assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1));
- assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1));
- assert!(v4_1 != v4_2);
- assert!(v6_1 != v6_2);
-
- // compare different addresses
- assert!(v4_1 < v4_2);
- assert!(v6_1 < v6_2);
- assert!(v4_2 > v4_1);
- assert!(v6_2 > v6_1);
-
- // compare the same address with different ports
- assert!(v4_2 < v4_3);
- assert!(v6_2 < v6_3);
- assert!(v4_3 > v4_2);
- assert!(v6_3 > v6_2);
-
- // compare different addresses with the same port
- assert!(v4_1 < v4_3);
- assert!(v6_1 < v6_3);
- assert!(v4_3 > v4_1);
- assert!(v6_3 > v6_1);
-
- // compare with an inferred right-hand side
- assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap());
- assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap());
- assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap());
-}
diff --git a/library/std/src/net/display_buffer.rs b/library/std/src/net/display_buffer.rs
new file mode 100644
index 000000000..7aadf06e9
--- /dev/null
+++ b/library/std/src/net/display_buffer.rs
@@ -0,0 +1,40 @@
+use crate::fmt;
+use crate::mem::MaybeUninit;
+use crate::str;
+
+/// Used for slow path in `Display` implementations when alignment is required.
+pub struct DisplayBuffer<const SIZE: usize> {
+ buf: [MaybeUninit<u8>; SIZE],
+ len: usize,
+}
+
+impl<const SIZE: usize> DisplayBuffer<SIZE> {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { buf: MaybeUninit::uninit_array(), len: 0 }
+ }
+
+ #[inline]
+ pub fn as_str(&self) -> &str {
+ // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation
+ // which writes a valid UTF-8 string to `buf` and correctly sets `len`.
+ unsafe {
+ let s = MaybeUninit::slice_assume_init_ref(&self.buf[..self.len]);
+ str::from_utf8_unchecked(s)
+ }
+ }
+}
+
+impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ let bytes = s.as_bytes();
+
+ if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) {
+ MaybeUninit::write_slice(buf, bytes);
+ self.len += bytes.len();
+ Ok(())
+ } else {
+ Err(fmt::Error)
+ }
+ }
+}
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
deleted file mode 100644
index 41ca9ba84..000000000
--- a/library/std/src/net/ip.rs
+++ /dev/null
@@ -1,2040 +0,0 @@
-// Tests for this module
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests;
-
-use crate::cmp::Ordering;
-use crate::fmt::{self, Write as FmtWrite};
-use crate::io::Write as IoWrite;
-use crate::mem::transmute;
-use crate::sys::net::netc as c;
-use crate::sys_common::{FromInner, IntoInner};
-
-/// An IP address, either IPv4 or IPv6.
-///
-/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their
-/// respective documentation for more details.
-///
-/// # Examples
-///
-/// ```
-/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-///
-/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
-/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
-///
-/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4));
-/// assert_eq!("::1".parse(), Ok(localhost_v6));
-///
-/// assert_eq!(localhost_v4.is_ipv6(), false);
-/// assert_eq!(localhost_v4.is_ipv4(), true);
-/// ```
-#[stable(feature = "ip_addr", since = "1.7.0")]
-#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
-pub enum IpAddr {
- /// An IPv4 address.
- #[stable(feature = "ip_addr", since = "1.7.0")]
- V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr),
- /// An IPv6 address.
- #[stable(feature = "ip_addr", since = "1.7.0")]
- V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr),
-}
-
-/// An IPv4 address.
-///
-/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791].
-/// They are usually represented as four octets.
-///
-/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
-///
-/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791
-///
-/// # Textual representation
-///
-/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
-/// notation, divided by `.` (this is called "dot-decimal notation").
-/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which
-/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943].
-///
-/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1
-/// [`FromStr`]: crate::str::FromStr
-///
-/// # Examples
-///
-/// ```
-/// use std::net::Ipv4Addr;
-///
-/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
-/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
-/// assert_eq!(localhost.is_loopback(), true);
-/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal
-/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal
-/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
-/// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct Ipv4Addr {
- octets: [u8; 4],
-}
-
-/// An IPv6 address.
-///
-/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291].
-/// They are usually represented as eight 16-bit segments.
-///
-/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
-///
-/// # Embedding IPv4 Addresses
-///
-/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
-///
-/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined:
-/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated.
-///
-/// Both types of addresses are not assigned any special meaning by this implementation,
-/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`,
-/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is.
-/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address.
-///
-/// ### IPv4-Compatible IPv6 Addresses
-///
-/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated.
-/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows:
-///
-/// ```text
-/// | 80 bits | 16 | 32 bits |
-/// +--------------------------------------+--------------------------+
-/// |0000..............................0000|0000| IPv4 address |
-/// +--------------------------------------+----+---------------------+
-/// ```
-/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`.
-///
-/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`].
-/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address.
-///
-/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
-///
-/// ### IPv4-Mapped IPv6 Addresses
-///
-/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2].
-/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows:
-///
-/// ```text
-/// | 80 bits | 16 | 32 bits |
-/// +--------------------------------------+--------------------------+
-/// |0000..............................0000|FFFF| IPv4 address |
-/// +--------------------------------------+----+---------------------+
-/// ```
-/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`.
-///
-/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`].
-/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address.
-/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`. Use
-/// [`Ipv6Addr::to_ipv4_mapped`] to avoid this.
-///
-/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
-///
-/// # Textual representation
-///
-/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent
-/// an IPv6 address in text, but in general, each segments is written in hexadecimal
-/// notation, and segments are separated by `:`. For more information, see
-/// [IETF RFC 5952].
-///
-/// [`FromStr`]: crate::str::FromStr
-/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952
-///
-/// # Examples
-///
-/// ```
-/// use std::net::Ipv6Addr;
-///
-/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
-/// assert_eq!("::1".parse(), Ok(localhost));
-/// assert_eq!(localhost.is_loopback(), true);
-/// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct Ipv6Addr {
- octets: [u8; 16],
-}
-
-/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2].
-///
-/// # Stability Guarantees
-///
-/// Not all possible values for a multicast scope have been assigned.
-/// Future RFCs may introduce new scopes, which will be added as variants to this enum;
-/// because of this the enum is marked as `#[non_exhaustive]`.
-///
-/// # Examples
-/// ```
-/// #![feature(ip)]
-///
-/// use std::net::Ipv6Addr;
-/// use std::net::Ipv6MulticastScope::*;
-///
-/// // An IPv6 multicast address with global scope (`ff0e::`).
-/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0);
-///
-/// // Will print "Global scope".
-/// match address.multicast_scope() {
-/// Some(InterfaceLocal) => println!("Interface-Local scope"),
-/// Some(LinkLocal) => println!("Link-Local scope"),
-/// Some(RealmLocal) => println!("Realm-Local scope"),
-/// Some(AdminLocal) => println!("Admin-Local scope"),
-/// Some(SiteLocal) => println!("Site-Local scope"),
-/// Some(OrganizationLocal) => println!("Organization-Local scope"),
-/// Some(Global) => println!("Global scope"),
-/// Some(_) => println!("Unknown scope"),
-/// None => println!("Not a multicast address!")
-/// }
-///
-/// ```
-///
-/// [IPv6 multicast address]: Ipv6Addr
-/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2
-#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
-#[unstable(feature = "ip", issue = "27709")]
-#[non_exhaustive]
-pub enum Ipv6MulticastScope {
- /// Interface-Local scope.
- InterfaceLocal,
- /// Link-Local scope.
- LinkLocal,
- /// Realm-Local scope.
- RealmLocal,
- /// Admin-Local scope.
- AdminLocal,
- /// Site-Local scope.
- SiteLocal,
- /// Organization-Local scope.
- OrganizationLocal,
- /// Global scope.
- Global,
-}
-
-impl IpAddr {
- /// Returns [`true`] for the special 'unspecified' address.
- ///
- /// See the documentation for [`Ipv4Addr::is_unspecified()`] and
- /// [`Ipv6Addr::is_unspecified()`] for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "ip_shared", since = "1.12.0")]
- #[must_use]
- #[inline]
- pub const fn is_unspecified(&self) -> bool {
- match self {
- IpAddr::V4(ip) => ip.is_unspecified(),
- IpAddr::V6(ip) => ip.is_unspecified(),
- }
- }
-
- /// Returns [`true`] if this is a loopback address.
- ///
- /// See the documentation for [`Ipv4Addr::is_loopback()`] and
- /// [`Ipv6Addr::is_loopback()`] for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "ip_shared", since = "1.12.0")]
- #[must_use]
- #[inline]
- pub const fn is_loopback(&self) -> bool {
- match self {
- IpAddr::V4(ip) => ip.is_loopback(),
- IpAddr::V6(ip) => ip.is_loopback(),
- }
- }
-
- /// Returns [`true`] if the address appears to be globally routable.
- ///
- /// See the documentation for [`Ipv4Addr::is_global()`] and
- /// [`Ipv6Addr::is_global()`] for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_global(&self) -> bool {
- match self {
- IpAddr::V4(ip) => ip.is_global(),
- IpAddr::V6(ip) => ip.is_global(),
- }
- }
-
- /// Returns [`true`] if this is a multicast address.
- ///
- /// See the documentation for [`Ipv4Addr::is_multicast()`] and
- /// [`Ipv6Addr::is_multicast()`] for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "ip_shared", since = "1.12.0")]
- #[must_use]
- #[inline]
- pub const fn is_multicast(&self) -> bool {
- match self {
- IpAddr::V4(ip) => ip.is_multicast(),
- IpAddr::V6(ip) => ip.is_multicast(),
- }
- }
-
- /// Returns [`true`] if this address is in a range designated for documentation.
- ///
- /// See the documentation for [`Ipv4Addr::is_documentation()`] and
- /// [`Ipv6Addr::is_documentation()`] for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true);
- /// assert_eq!(
- /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(),
- /// true
- /// );
- /// ```
- #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_documentation(&self) -> bool {
- match self {
- IpAddr::V4(ip) => ip.is_documentation(),
- IpAddr::V6(ip) => ip.is_documentation(),
- }
- }
-
- /// Returns [`true`] if this address is in a range designated for benchmarking.
- ///
- /// See the documentation for [`Ipv4Addr::is_benchmarking()`] and
- /// [`Ipv6Addr::is_benchmarking()`] for more details.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(198, 19, 255, 255)).is_benchmarking(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0)).is_benchmarking(), true);
- /// ```
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_benchmarking(&self) -> bool {
- match self {
- IpAddr::V4(ip) => ip.is_benchmarking(),
- IpAddr::V6(ip) => ip.is_benchmarking(),
- }
- }
-
- /// Returns [`true`] if this address is an [`IPv4` address], and [`false`]
- /// otherwise.
- ///
- /// [`IPv4` address]: IpAddr::V4
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "ipaddr_checker", since = "1.16.0")]
- #[must_use]
- #[inline]
- pub const fn is_ipv4(&self) -> bool {
- matches!(self, IpAddr::V4(_))
- }
-
- /// Returns [`true`] if this address is an [`IPv6` address], and [`false`]
- /// otherwise.
- ///
- /// [`IPv6` address]: IpAddr::V6
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "ipaddr_checker", since = "1.16.0")]
- #[must_use]
- #[inline]
- pub const fn is_ipv6(&self) -> bool {
- matches!(self, IpAddr::V6(_))
- }
-
- /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it
- /// return `self` as-is.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false);
- /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true);
- /// ```
- #[inline]
- #[must_use = "this returns the result of the operation, \
- without modifying the original"]
- #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- pub const fn to_canonical(&self) -> IpAddr {
- match self {
- &v4 @ IpAddr::V4(_) => v4,
- IpAddr::V6(v6) => v6.to_canonical(),
- }
- }
-}
-
-impl Ipv4Addr {
- /// Creates a new IPv4 address from four eight-bit octets.
- ///
- /// The result will represent the IP address `a`.`b`.`c`.`d`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::new(127, 0, 0, 1);
- /// ```
- #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- #[inline]
- pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
- Ipv4Addr { octets: [a, b, c, d] }
- }
-
- /// An IPv4 address with the address pointing to localhost: `127.0.0.1`
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::LOCALHOST;
- /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1));
- /// ```
- #[stable(feature = "ip_constructors", since = "1.30.0")]
- pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1);
-
- /// An IPv4 address representing an unspecified address: `0.0.0.0`
- ///
- /// This corresponds to the constant `INADDR_ANY` in other languages.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::UNSPECIFIED;
- /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0));
- /// ```
- #[doc(alias = "INADDR_ANY")]
- #[stable(feature = "ip_constructors", since = "1.30.0")]
- pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0);
-
- /// An IPv4 address representing the broadcast address: `255.255.255.255`
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::BROADCAST;
- /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255));
- /// ```
- #[stable(feature = "ip_constructors", since = "1.30.0")]
- pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255);
-
- /// Returns the four eight-bit integers that make up this address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::new(127, 0, 0, 1);
- /// assert_eq!(addr.octets(), [127, 0, 0, 1]);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- #[inline]
- pub const fn octets(&self) -> [u8; 4] {
- self.octets
- }
-
- /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`).
- ///
- /// This property is defined in _UNIX Network Programming, Second Edition_,
- /// W. Richard Stevens, p. 891; see also [ip7].
- ///
- /// [ip7]: https://man7.org/linux/man-pages/man7/ip.7.html
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true);
- /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
- #[stable(feature = "ip_shared", since = "1.12.0")]
- #[must_use]
- #[inline]
- pub const fn is_unspecified(&self) -> bool {
- u32::from_be_bytes(self.octets) == 0
- }
-
- /// Returns [`true`] if this is a loopback address (`127.0.0.0/8`).
- ///
- /// This property is defined by [IETF RFC 1122].
- ///
- /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
- /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_loopback(&self) -> bool {
- self.octets()[0] == 127
- }
-
- /// Returns [`true`] if this is a private address.
- ///
- /// The private address ranges are defined in [IETF RFC 1918] and include:
- ///
- /// - `10.0.0.0/8`
- /// - `172.16.0.0/12`
- /// - `192.168.0.0/16`
- ///
- /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true);
- /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true);
- /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true);
- /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true);
- /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false);
- /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
- /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_private(&self) -> bool {
- match self.octets() {
- [10, ..] => true,
- [172, b, ..] if b >= 16 && b <= 31 => true,
- [192, 168, ..] => true,
- _ => false,
- }
- }
-
- /// Returns [`true`] if the address is link-local (`169.254.0.0/16`).
- ///
- /// This property is defined by [IETF RFC 3927].
- ///
- /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true);
- /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true);
- /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_link_local(&self) -> bool {
- matches!(self.octets(), [169, 254, ..])
- }
-
- /// Returns [`true`] if the address appears to be globally routable.
- /// See [iana-ipv4-special-registry][ipv4-sr].
- ///
- /// The following return [`false`]:
- ///
- /// - private addresses (see [`Ipv4Addr::is_private()`])
- /// - the loopback address (see [`Ipv4Addr::is_loopback()`])
- /// - the link-local address (see [`Ipv4Addr::is_link_local()`])
- /// - the broadcast address (see [`Ipv4Addr::is_broadcast()`])
- /// - addresses used for documentation (see [`Ipv4Addr::is_documentation()`])
- /// - the unspecified address (see [`Ipv4Addr::is_unspecified()`]), and the whole
- /// `0.0.0.0/8` block
- /// - addresses reserved for future protocols, except
- /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable
- /// - addresses reserved for future use (see [`Ipv4Addr::is_reserved()`]
- /// - addresses reserved for networking devices benchmarking (see
- /// [`Ipv4Addr::is_benchmarking()`])
- ///
- /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv4Addr;
- ///
- /// // private addresses are not global
- /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false);
- /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false);
- /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false);
- ///
- /// // the 0.0.0.0/8 block is not global
- /// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).is_global(), false);
- /// // in particular, the unspecified address is not global
- /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_global(), false);
- ///
- /// // the loopback address is not global
- /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_global(), false);
- ///
- /// // link local addresses are not global
- /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false);
- ///
- /// // the broadcast address is not global
- /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_global(), false);
- ///
- /// // the address space designated for documentation is not global
- /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false);
- /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false);
- /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false);
- ///
- /// // shared addresses are not global
- /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false);
- ///
- /// // addresses reserved for protocol assignment are not global
- /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_global(), false);
- /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_global(), false);
- ///
- /// // addresses reserved for future use are not global
- /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false);
- ///
- /// // addresses reserved for network devices benchmarking are not global
- /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false);
- ///
- /// // All the other addresses are global
- /// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).is_global(), true);
- /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_global(&self) -> bool {
- // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two
- // globally routable addresses in the 192.0.0.0/24 range.
- if u32::from_be_bytes(self.octets()) == 0xc0000009
- || u32::from_be_bytes(self.octets()) == 0xc000000a
- {
- return true;
- }
- !self.is_private()
- && !self.is_loopback()
- && !self.is_link_local()
- && !self.is_broadcast()
- && !self.is_documentation()
- && !self.is_shared()
- // addresses reserved for future protocols (`192.0.0.0/24`)
- && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0)
- && !self.is_reserved()
- && !self.is_benchmarking()
- // Make sure the address is not in 0.0.0.0/8
- && self.octets()[0] != 0
- }
-
- /// Returns [`true`] if this address is part of the Shared Address Space defined in
- /// [IETF RFC 6598] (`100.64.0.0/10`).
- ///
- /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true);
- /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true);
- /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_shared(&self) -> bool {
- self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
- }
-
- /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
- /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
- /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
- ///
- /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
- /// [errata 423]: https://www.rfc-editor.org/errata/eid423
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false);
- /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true);
- /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true);
- /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_benchmarking(&self) -> bool {
- self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
- }
-
- /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
- /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the
- /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
- /// it is obviously not reserved for future use.
- ///
- /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
- ///
- /// # Warning
- ///
- /// As IANA assigns new addresses, this method will be
- /// updated. This may result in non-reserved addresses being
- /// treated as reserved in code that relies on an outdated version
- /// of this method.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true);
- /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true);
- ///
- /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false);
- /// // The broadcast address is not considered as reserved for future use by this implementation
- /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_reserved(&self) -> bool {
- self.octets()[0] & 240 == 240 && !self.is_broadcast()
- }
-
- /// Returns [`true`] if this is a multicast address (`224.0.0.0/4`).
- ///
- /// Multicast addresses have a most significant octet between `224` and `239`,
- /// and is defined by [IETF RFC 5771].
- ///
- /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true);
- /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true);
- /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_multicast(&self) -> bool {
- self.octets()[0] >= 224 && self.octets()[0] <= 239
- }
-
- /// Returns [`true`] if this is a broadcast address (`255.255.255.255`).
- ///
- /// A broadcast address has all octets set to `255` as defined in [IETF RFC 919].
- ///
- /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true);
- /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_broadcast(&self) -> bool {
- u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets())
- }
-
- /// Returns [`true`] if this address is in a range designated for documentation.
- ///
- /// This is defined in [IETF RFC 5737]:
- ///
- /// - `192.0.2.0/24` (TEST-NET-1)
- /// - `198.51.100.0/24` (TEST-NET-2)
- /// - `203.0.113.0/24` (TEST-NET-3)
- ///
- /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true);
- /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true);
- /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true);
- /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_documentation(&self) -> bool {
- matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _])
- }
-
- /// Converts this address to an [IPv4-compatible] [`IPv6` address].
- ///
- /// `a.b.c.d` becomes `::a.b.c.d`
- ///
- /// Note that IPv4-compatible addresses have been officially deprecated.
- /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead.
- ///
- /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
- /// [`IPv6` address]: Ipv6Addr
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(
- /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(),
- /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff)
- /// );
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use = "this returns the result of the operation, \
- without modifying the original"]
- #[inline]
- pub const fn to_ipv6_compatible(&self) -> Ipv6Addr {
- let [a, b, c, d] = self.octets();
- Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] }
- }
-
- /// Converts this address to an [IPv4-mapped] [`IPv6` address].
- ///
- /// `a.b.c.d` becomes `::ffff:a.b.c.d`
- ///
- /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
- /// [`IPv6` address]: Ipv6Addr
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(),
- /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff));
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use = "this returns the result of the operation, \
- without modifying the original"]
- #[inline]
- pub const fn to_ipv6_mapped(&self) -> Ipv6Addr {
- let [a, b, c, d] = self.octets();
- Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] }
- }
-}
-
-#[stable(feature = "ip_addr", since = "1.7.0")]
-impl fmt::Display for IpAddr {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- IpAddr::V4(ip) => ip.fmt(fmt),
- IpAddr::V6(ip) => ip.fmt(fmt),
- }
- }
-}
-
-#[stable(feature = "ip_addr", since = "1.7.0")]
-impl fmt::Debug for IpAddr {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, fmt)
- }
-}
-
-#[stable(feature = "ip_from_ip", since = "1.16.0")]
-impl From<Ipv4Addr> for IpAddr {
- /// Copies this address to a new `IpAddr::V4`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr};
- ///
- /// let addr = Ipv4Addr::new(127, 0, 0, 1);
- ///
- /// assert_eq!(
- /// IpAddr::V4(addr),
- /// IpAddr::from(addr)
- /// )
- /// ```
- #[inline]
- fn from(ipv4: Ipv4Addr) -> IpAddr {
- IpAddr::V4(ipv4)
- }
-}
-
-#[stable(feature = "ip_from_ip", since = "1.16.0")]
-impl From<Ipv6Addr> for IpAddr {
- /// Copies this address to a new `IpAddr::V6`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv6Addr};
- ///
- /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
- ///
- /// assert_eq!(
- /// IpAddr::V6(addr),
- /// IpAddr::from(addr)
- /// );
- /// ```
- #[inline]
- fn from(ipv6: Ipv6Addr) -> IpAddr {
- IpAddr::V6(ipv6)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Display for Ipv4Addr {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- let octets = self.octets();
- // Fast Path: if there's no alignment stuff, write directly to the buffer
- if fmt.precision().is_none() && fmt.width().is_none() {
- write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3])
- } else {
- const IPV4_BUF_LEN: usize = 15; // Long enough for the longest possible IPv4 address
- let mut buf = [0u8; IPV4_BUF_LEN];
- let mut buf_slice = &mut buf[..];
-
- // Note: The call to write should never fail, hence the unwrap
- write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap();
- let len = IPV4_BUF_LEN - buf_slice.len();
-
- // This unsafe is OK because we know what is being written to the buffer
- let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
- fmt.pad(buf)
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for Ipv4Addr {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, fmt)
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialEq<Ipv4Addr> for IpAddr {
- #[inline]
- fn eq(&self, other: &Ipv4Addr) -> bool {
- match self {
- IpAddr::V4(v4) => v4 == other,
- IpAddr::V6(_) => false,
- }
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialEq<IpAddr> for Ipv4Addr {
- #[inline]
- fn eq(&self, other: &IpAddr) -> bool {
- match other {
- IpAddr::V4(v4) => self == v4,
- IpAddr::V6(_) => false,
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl PartialOrd for Ipv4Addr {
- #[inline]
- fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialOrd<Ipv4Addr> for IpAddr {
- #[inline]
- fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> {
- match self {
- IpAddr::V4(v4) => v4.partial_cmp(other),
- IpAddr::V6(_) => Some(Ordering::Greater),
- }
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialOrd<IpAddr> for Ipv4Addr {
- #[inline]
- fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
- match other {
- IpAddr::V4(v4) => self.partial_cmp(v4),
- IpAddr::V6(_) => Some(Ordering::Less),
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Ord for Ipv4Addr {
- #[inline]
- fn cmp(&self, other: &Ipv4Addr) -> Ordering {
- self.octets.cmp(&other.octets)
- }
-}
-
-impl IntoInner<c::in_addr> for Ipv4Addr {
- #[inline]
- fn into_inner(self) -> c::in_addr {
- // `s_addr` is stored as BE on all machines and the array is in BE order.
- // So the native endian conversion method is used so that it's never swapped.
- c::in_addr { s_addr: u32::from_ne_bytes(self.octets) }
- }
-}
-impl FromInner<c::in_addr> for Ipv4Addr {
- fn from_inner(addr: c::in_addr) -> Ipv4Addr {
- Ipv4Addr { octets: addr.s_addr.to_ne_bytes() }
- }
-}
-
-#[stable(feature = "ip_u32", since = "1.1.0")]
-impl From<Ipv4Addr> for u32 {
- /// Converts an `Ipv4Addr` into a host byte order `u32`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78);
- /// assert_eq!(0x12345678, u32::from(addr));
- /// ```
- #[inline]
- fn from(ip: Ipv4Addr) -> u32 {
- u32::from_be_bytes(ip.octets)
- }
-}
-
-#[stable(feature = "ip_u32", since = "1.1.0")]
-impl From<u32> for Ipv4Addr {
- /// Converts a host byte order `u32` into an `Ipv4Addr`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::from(0x12345678);
- /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr);
- /// ```
- #[inline]
- fn from(ip: u32) -> Ipv4Addr {
- Ipv4Addr { octets: ip.to_be_bytes() }
- }
-}
-
-#[stable(feature = "from_slice_v4", since = "1.9.0")]
-impl From<[u8; 4]> for Ipv4Addr {
- /// Creates an `Ipv4Addr` from a four element byte array.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv4Addr;
- ///
- /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]);
- /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr);
- /// ```
- #[inline]
- fn from(octets: [u8; 4]) -> Ipv4Addr {
- Ipv4Addr { octets }
- }
-}
-
-#[stable(feature = "ip_from_slice", since = "1.17.0")]
-impl From<[u8; 4]> for IpAddr {
- /// Creates an `IpAddr::V4` from a four element byte array.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv4Addr};
- ///
- /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]);
- /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr);
- /// ```
- #[inline]
- fn from(octets: [u8; 4]) -> IpAddr {
- IpAddr::V4(Ipv4Addr::from(octets))
- }
-}
-
-impl Ipv6Addr {
- /// Creates a new IPv6 address from eight 16-bit segments.
- ///
- /// The result will represent the IP address `a:b:c:d:e:f:g:h`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
- /// ```
- #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- #[inline]
- pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
- let addr16 = [
- a.to_be(),
- b.to_be(),
- c.to_be(),
- d.to_be(),
- e.to_be(),
- f.to_be(),
- g.to_be(),
- h.to_be(),
- ];
- Ipv6Addr {
- // All elements in `addr16` are big endian.
- // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`.
- octets: unsafe { transmute::<_, [u8; 16]>(addr16) },
- }
- }
-
- /// An IPv6 address representing localhost: `::1`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::LOCALHOST;
- /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
- /// ```
- #[stable(feature = "ip_constructors", since = "1.30.0")]
- pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
-
- /// An IPv6 address representing the unspecified address: `::`
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::UNSPECIFIED;
- /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
- /// ```
- #[stable(feature = "ip_constructors", since = "1.30.0")]
- pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
-
- /// Returns the eight 16-bit segments that make up this address.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(),
- /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- #[inline]
- pub const fn segments(&self) -> [u16; 8] {
- // All elements in `self.octets` must be big endian.
- // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`.
- let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.octets) };
- // We want native endian u16
- [
- u16::from_be(a),
- u16::from_be(b),
- u16::from_be(c),
- u16::from_be(d),
- u16::from_be(e),
- u16::from_be(f),
- u16::from_be(g),
- u16::from_be(h),
- ]
- }
-
- /// Returns [`true`] for the special 'unspecified' address (`::`).
- ///
- /// This property is defined in [IETF RFC 4291].
- ///
- /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_unspecified(&self) -> bool {
- u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
- }
-
- /// Returns [`true`] if this is the [loopback address] (`::1`),
- /// as defined in [IETF RFC 4291 section 2.5.3].
- ///
- /// Contrary to IPv4, in IPv6 there is only one loopback address.
- ///
- /// [loopback address]: Ipv6Addr::LOCALHOST
- /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_loopback(&self) -> bool {
- u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets())
- }
-
- /// Returns [`true`] if the address appears to be globally routable.
- ///
- /// The following return [`false`]:
- ///
- /// - the loopback address
- /// - link-local and unique local unicast addresses
- /// - interface-, link-, realm-, admin- and site-local multicast addresses
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), true);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_global(), false);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_global(&self) -> bool {
- match self.multicast_scope() {
- Some(Ipv6MulticastScope::Global) => true,
- None => self.is_unicast_global(),
- _ => false,
- }
- }
-
- /// Returns [`true`] if this is a unique local address (`fc00::/7`).
- ///
- /// This property is defined in [IETF RFC 4193].
- ///
- /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false);
- /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_unique_local(&self) -> bool {
- (self.segments()[0] & 0xfe00) == 0xfc00
- }
-
- /// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291].
- /// Any address that is not a [multicast address] (`ff00::/8`) is unicast.
- ///
- /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
- /// [multicast address]: Ipv6Addr::is_multicast
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// // The unspecified and loopback addresses are unicast.
- /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_unicast(), true);
- /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast(), true);
- ///
- /// // Any address that is not a multicast address (`ff00::/8`) is unicast.
- /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true);
- /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_unicast(&self) -> bool {
- !self.is_multicast()
- }
-
- /// Returns `true` if the address is a unicast address with link-local scope,
- /// as defined in [RFC 4291].
- ///
- /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4].
- /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6],
- /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format:
- ///
- /// ```text
- /// | 10 bits | 54 bits | 64 bits |
- /// +----------+-------------------------+----------------------------+
- /// |1111111010| 0 | interface ID |
- /// +----------+-------------------------+----------------------------+
- /// ```
- /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`,
- /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated,
- /// and those addresses will have link-local scope.
- ///
- /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope",
- /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it.
- ///
- /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
- /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
- /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
- /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
- /// [loopback address]: Ipv6Addr::LOCALHOST
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// // The loopback address (`::1`) does not actually have link-local scope.
- /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false);
- ///
- /// // Only addresses in `fe80::/10` have link-local scope.
- /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false);
- /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
- ///
- /// // Addresses outside the stricter `fe80::/64` also have link-local scope.
- /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true);
- /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_unicast_link_local(&self) -> bool {
- (self.segments()[0] & 0xffc0) == 0xfe80
- }
-
- /// Returns [`true`] if this is an address reserved for documentation
- /// (`2001:db8::/32`).
- ///
- /// This property is defined in [IETF RFC 3849].
- ///
- /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false);
- /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_documentation(&self) -> bool {
- (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
- }
-
- /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`).
- ///
- /// This property is defined in [IETF RFC 5180], where it is mistakenly specified as covering the range `2001:0200::/48`.
- /// This is corrected in [IETF RFC Errata 1752] to `2001:0002::/48`.
- ///
- /// [IETF RFC 5180]: https://tools.ietf.org/html/rfc5180
- /// [IETF RFC Errata 1752]: https://www.rfc-editor.org/errata_search.php?eid=1752
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc613, 0x0).is_benchmarking(), false);
- /// assert_eq!(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0).is_benchmarking(), true);
- /// ```
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_benchmarking(&self) -> bool {
- (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0)
- }
-
- /// Returns [`true`] if the address is a globally routable unicast address.
- ///
- /// The following return false:
- ///
- /// - the loopback address
- /// - the link-local addresses
- /// - unique local addresses
- /// - the unspecified address
- /// - the address range reserved for documentation
- ///
- /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7]
- ///
- /// ```no_rust
- /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer
- /// be supported in new implementations (i.e., new implementations must treat this prefix as
- /// Global Unicast).
- /// ```
- ///
- /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn is_unicast_global(&self) -> bool {
- self.is_unicast()
- && !self.is_loopback()
- && !self.is_unicast_link_local()
- && !self.is_unique_local()
- && !self.is_unspecified()
- && !self.is_documentation()
- }
-
- /// Returns the address's multicast scope if the address is multicast.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- ///
- /// use std::net::{Ipv6Addr, Ipv6MulticastScope};
- ///
- /// assert_eq!(
- /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(),
- /// Some(Ipv6MulticastScope::Global)
- /// );
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use]
- #[inline]
- pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
- if self.is_multicast() {
- match self.segments()[0] & 0x000f {
- 1 => Some(Ipv6MulticastScope::InterfaceLocal),
- 2 => Some(Ipv6MulticastScope::LinkLocal),
- 3 => Some(Ipv6MulticastScope::RealmLocal),
- 4 => Some(Ipv6MulticastScope::AdminLocal),
- 5 => Some(Ipv6MulticastScope::SiteLocal),
- 8 => Some(Ipv6MulticastScope::OrganizationLocal),
- 14 => Some(Ipv6MulticastScope::Global),
- _ => None,
- }
- } else {
- None
- }
- }
-
- /// Returns [`true`] if this is a multicast address (`ff00::/8`).
- ///
- /// This property is defined by [IETF RFC 4291].
- ///
- /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false);
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(since = "1.7.0", feature = "ip_17")]
- #[must_use]
- #[inline]
- pub const fn is_multicast(&self) -> bool {
- (self.segments()[0] & 0xff00) == 0xff00
- }
-
- /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address,
- /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
- ///
- /// `::ffff:a.b.c.d` becomes `a.b.c.d`.
- /// All addresses *not* starting with `::ffff` will return `None`.
- ///
- /// [`IPv4` address]: Ipv4Addr
- /// [IPv4-mapped]: Ipv6Addr
- /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(),
- /// Some(Ipv4Addr::new(192, 10, 2, 255)));
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")]
- #[must_use = "this returns the result of the operation, \
- without modifying the original"]
- #[inline]
- pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
- match self.octets() {
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => {
- Some(Ipv4Addr::new(a, b, c, d))
- }
- _ => None,
- }
- }
-
- /// Converts this address to an [`IPv4` address] if it is either
- /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1],
- /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2],
- /// otherwise returns [`None`].
- ///
- /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`. Use
- /// [`Ipv6Addr::to_ipv4_mapped`] to avoid this.
- ///
- /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`.
- /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`.
- ///
- /// [`IPv4` address]: Ipv4Addr
- /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
- /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
- /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1
- /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{Ipv4Addr, Ipv6Addr};
- ///
- /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(),
- /// Some(Ipv4Addr::new(192, 10, 2, 255)));
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(),
- /// Some(Ipv4Addr::new(0, 0, 0, 1)));
- /// ```
- #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use = "this returns the result of the operation, \
- without modifying the original"]
- #[inline]
- pub const fn to_ipv4(&self) -> Option<Ipv4Addr> {
- if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() {
- let [a, b] = ab.to_be_bytes();
- let [c, d] = cd.to_be_bytes();
- Some(Ipv4Addr::new(a, b, c, d))
- } else {
- None
- }
- }
-
- /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it
- /// returns self wrapped in an `IpAddr::V6`.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false);
- /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[must_use = "this returns the result of the operation, \
- without modifying the original"]
- #[inline]
- pub const fn to_canonical(&self) -> IpAddr {
- if let Some(mapped) = self.to_ipv4_mapped() {
- return IpAddr::V4(mapped);
- }
- IpAddr::V6(*self)
- }
-
- /// Returns the sixteen eight-bit integers the IPv6 address consists of.
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(),
- /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
- /// ```
- #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
- #[stable(feature = "ipv6_to_octets", since = "1.12.0")]
- #[must_use]
- #[inline]
- pub const fn octets(&self) -> [u8; 16] {
- self.octets
- }
-}
-
-/// Write an Ipv6Addr, conforming to the canonical style described by
-/// [RFC 5952](https://tools.ietf.org/html/rfc5952).
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Display for Ipv6Addr {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // If there are no alignment requirements, write out the IP address to
- // f. Otherwise, write it to a local buffer, then use f.pad.
- if f.precision().is_none() && f.width().is_none() {
- let segments = self.segments();
-
- // Special case for :: and ::1; otherwise they get written with the
- // IPv4 formatter
- if self.is_unspecified() {
- f.write_str("::")
- } else if self.is_loopback() {
- f.write_str("::1")
- } else if let Some(ipv4) = self.to_ipv4() {
- match segments[5] {
- // IPv4 Compatible address
- 0 => write!(f, "::{}", ipv4),
- // IPv4 Mapped address
- 0xffff => write!(f, "::ffff:{}", ipv4),
- _ => unreachable!(),
- }
- } else {
- #[derive(Copy, Clone, Default)]
- struct Span {
- start: usize,
- len: usize,
- }
-
- // Find the inner 0 span
- let zeroes = {
- let mut longest = Span::default();
- let mut current = Span::default();
-
- for (i, &segment) in segments.iter().enumerate() {
- if segment == 0 {
- if current.len == 0 {
- current.start = i;
- }
-
- current.len += 1;
-
- if current.len > longest.len {
- longest = current;
- }
- } else {
- current = Span::default();
- }
- }
-
- longest
- };
-
- /// Write a colon-separated part of the address
- #[inline]
- fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result {
- if let Some((first, tail)) = chunk.split_first() {
- write!(f, "{:x}", first)?;
- for segment in tail {
- f.write_char(':')?;
- write!(f, "{:x}", segment)?;
- }
- }
- Ok(())
- }
-
- if zeroes.len > 1 {
- fmt_subslice(f, &segments[..zeroes.start])?;
- f.write_str("::")?;
- fmt_subslice(f, &segments[zeroes.start + zeroes.len..])
- } else {
- fmt_subslice(f, &segments)
- }
- }
- } else {
- // Slow path: write the address to a local buffer, then use f.pad.
- // Defined recursively by using the fast path to write to the
- // buffer.
-
- // This is the largest possible size of an IPv6 address
- const IPV6_BUF_LEN: usize = (4 * 8) + 7;
- let mut buf = [0u8; IPV6_BUF_LEN];
- let mut buf_slice = &mut buf[..];
-
- // Note: This call to write should never fail, so unwrap is okay.
- write!(buf_slice, "{}", self).unwrap();
- let len = IPV6_BUF_LEN - buf_slice.len();
-
- // This is safe because we know exactly what can be in this buffer
- let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
- f.pad(buf)
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for Ipv6Addr {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, fmt)
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialEq<IpAddr> for Ipv6Addr {
- #[inline]
- fn eq(&self, other: &IpAddr) -> bool {
- match other {
- IpAddr::V4(_) => false,
- IpAddr::V6(v6) => self == v6,
- }
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialEq<Ipv6Addr> for IpAddr {
- #[inline]
- fn eq(&self, other: &Ipv6Addr) -> bool {
- match self {
- IpAddr::V4(_) => false,
- IpAddr::V6(v6) => v6 == other,
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl PartialOrd for Ipv6Addr {
- #[inline]
- fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialOrd<Ipv6Addr> for IpAddr {
- #[inline]
- fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> {
- match self {
- IpAddr::V4(_) => Some(Ordering::Less),
- IpAddr::V6(v6) => v6.partial_cmp(other),
- }
- }
-}
-
-#[stable(feature = "ip_cmp", since = "1.16.0")]
-impl PartialOrd<IpAddr> for Ipv6Addr {
- #[inline]
- fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
- match other {
- IpAddr::V4(_) => Some(Ordering::Greater),
- IpAddr::V6(v6) => self.partial_cmp(v6),
- }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Ord for Ipv6Addr {
- #[inline]
- fn cmp(&self, other: &Ipv6Addr) -> Ordering {
- self.segments().cmp(&other.segments())
- }
-}
-
-impl IntoInner<c::in6_addr> for Ipv6Addr {
- fn into_inner(self) -> c::in6_addr {
- c::in6_addr { s6_addr: self.octets }
- }
-}
-impl FromInner<c::in6_addr> for Ipv6Addr {
- #[inline]
- fn from_inner(addr: c::in6_addr) -> Ipv6Addr {
- Ipv6Addr { octets: addr.s6_addr }
- }
-}
-
-#[stable(feature = "i128", since = "1.26.0")]
-impl From<Ipv6Addr> for u128 {
- /// Convert an `Ipv6Addr` into a host byte order `u128`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::new(
- /// 0x1020, 0x3040, 0x5060, 0x7080,
- /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
- /// );
- /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr));
- /// ```
- #[inline]
- fn from(ip: Ipv6Addr) -> u128 {
- u128::from_be_bytes(ip.octets)
- }
-}
-#[stable(feature = "i128", since = "1.26.0")]
-impl From<u128> for Ipv6Addr {
- /// Convert a host byte order `u128` into an `Ipv6Addr`.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128);
- /// assert_eq!(
- /// Ipv6Addr::new(
- /// 0x1020, 0x3040, 0x5060, 0x7080,
- /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
- /// ),
- /// addr);
- /// ```
- #[inline]
- fn from(ip: u128) -> Ipv6Addr {
- Ipv6Addr::from(ip.to_be_bytes())
- }
-}
-
-#[stable(feature = "ipv6_from_octets", since = "1.9.0")]
-impl From<[u8; 16]> for Ipv6Addr {
- /// Creates an `Ipv6Addr` from a sixteen element byte array.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::from([
- /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8,
- /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8,
- /// ]);
- /// assert_eq!(
- /// Ipv6Addr::new(
- /// 0x1918, 0x1716,
- /// 0x1514, 0x1312,
- /// 0x1110, 0x0f0e,
- /// 0x0d0c, 0x0b0a
- /// ),
- /// addr
- /// );
- /// ```
- #[inline]
- fn from(octets: [u8; 16]) -> Ipv6Addr {
- Ipv6Addr { octets }
- }
-}
-
-#[stable(feature = "ipv6_from_segments", since = "1.16.0")]
-impl From<[u16; 8]> for Ipv6Addr {
- /// Creates an `Ipv6Addr` from an eight element 16-bit array.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::Ipv6Addr;
- ///
- /// let addr = Ipv6Addr::from([
- /// 525u16, 524u16, 523u16, 522u16,
- /// 521u16, 520u16, 519u16, 518u16,
- /// ]);
- /// assert_eq!(
- /// Ipv6Addr::new(
- /// 0x20d, 0x20c,
- /// 0x20b, 0x20a,
- /// 0x209, 0x208,
- /// 0x207, 0x206
- /// ),
- /// addr
- /// );
- /// ```
- #[inline]
- fn from(segments: [u16; 8]) -> Ipv6Addr {
- let [a, b, c, d, e, f, g, h] = segments;
- Ipv6Addr::new(a, b, c, d, e, f, g, h)
- }
-}
-
-#[stable(feature = "ip_from_slice", since = "1.17.0")]
-impl From<[u8; 16]> for IpAddr {
- /// Creates an `IpAddr::V6` from a sixteen element byte array.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv6Addr};
- ///
- /// let addr = IpAddr::from([
- /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8,
- /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8,
- /// ]);
- /// assert_eq!(
- /// IpAddr::V6(Ipv6Addr::new(
- /// 0x1918, 0x1716,
- /// 0x1514, 0x1312,
- /// 0x1110, 0x0f0e,
- /// 0x0d0c, 0x0b0a
- /// )),
- /// addr
- /// );
- /// ```
- #[inline]
- fn from(octets: [u8; 16]) -> IpAddr {
- IpAddr::V6(Ipv6Addr::from(octets))
- }
-}
-
-#[stable(feature = "ip_from_slice", since = "1.17.0")]
-impl From<[u16; 8]> for IpAddr {
- /// Creates an `IpAddr::V6` from an eight element 16-bit array.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::net::{IpAddr, Ipv6Addr};
- ///
- /// let addr = IpAddr::from([
- /// 525u16, 524u16, 523u16, 522u16,
- /// 521u16, 520u16, 519u16, 518u16,
- /// ]);
- /// assert_eq!(
- /// IpAddr::V6(Ipv6Addr::new(
- /// 0x20d, 0x20c,
- /// 0x20b, 0x20a,
- /// 0x209, 0x208,
- /// 0x207, 0x206
- /// )),
- /// addr
- /// );
- /// ```
- #[inline]
- fn from(segments: [u16; 8]) -> IpAddr {
- IpAddr::V6(Ipv6Addr::from(segments))
- }
-}
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs
deleted file mode 100644
index c29509331..000000000
--- a/library/std/src/net/ip/tests.rs
+++ /dev/null
@@ -1,969 +0,0 @@
-use crate::net::test::{sa4, sa6, tsa};
-use crate::net::*;
-use crate::str::FromStr;
-
-#[test]
-fn test_from_str_ipv4() {
- assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse());
- assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse());
- assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse());
-
- // out of range
- let none: Option<Ipv4Addr> = "256.0.0.1".parse().ok();
- assert_eq!(None, none);
- // too short
- let none: Option<Ipv4Addr> = "255.0.0".parse().ok();
- assert_eq!(None, none);
- // too long
- let none: Option<Ipv4Addr> = "255.0.0.1.2".parse().ok();
- assert_eq!(None, none);
- // no number between dots
- let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
- assert_eq!(None, none);
- // octal
- let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok();
- assert_eq!(None, none);
- // octal zero
- let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok();
- assert_eq!(None, none);
- let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok();
- assert_eq!(None, none);
-}
-
-#[test]
-fn test_from_str_ipv6() {
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse());
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse());
-
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse());
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse());
-
- assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse());
-
- // too long group
- let none: Option<Ipv6Addr> = "::00000".parse().ok();
- assert_eq!(None, none);
- // too short
- let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7".parse().ok();
- assert_eq!(None, none);
- // too long
- let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:8:9".parse().ok();
- assert_eq!(None, none);
- // triple colon
- let none: Option<Ipv6Addr> = "1:2:::6:7:8".parse().ok();
- assert_eq!(None, none);
- // two double colons
- let none: Option<Ipv6Addr> = "1:2::6::8".parse().ok();
- assert_eq!(None, none);
- // `::` indicating zero groups of zeros
- let none: Option<Ipv6Addr> = "1:2:3:4::5:6:7:8".parse().ok();
- assert_eq!(None, none);
-}
-
-#[test]
-fn test_from_str_ipv4_in_ipv6() {
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse());
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse());
- assert_eq!(
- Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
- "64:ff9b::192.0.2.33".parse()
- );
- assert_eq!(
- Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
- "2001:db8:122:c000:2:2100:192.0.2.33".parse()
- );
-
- // colon after v4
- let none: Option<Ipv4Addr> = "::127.0.0.1:".parse().ok();
- assert_eq!(None, none);
- // not enough groups
- let none: Option<Ipv6Addr> = "1:2:3:4:5:127.0.0.1".parse().ok();
- assert_eq!(None, none);
- // too many groups
- let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:127.0.0.1".parse().ok();
- assert_eq!(None, none);
-}
-
-#[test]
-fn test_from_str_socket_addr() {
- assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
- assert_eq!(Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
- assert_eq!(
- Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)),
- "[2a02:6b8:0:1::1]:53".parse()
- );
- assert_eq!(
- Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)),
- "[2a02:6b8:0:1::1]:53".parse()
- );
- assert_eq!(Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), "[::127.0.0.1]:22".parse());
- assert_eq!(
- Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)),
- "[::127.0.0.1]:22".parse()
- );
-
- // without port
- let none: Option<SocketAddr> = "127.0.0.1".parse().ok();
- assert_eq!(None, none);
- // without port
- let none: Option<SocketAddr> = "127.0.0.1:".parse().ok();
- assert_eq!(None, none);
- // wrong brackets around v4
- let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok();
- assert_eq!(None, none);
- // port out of range
- let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok();
- assert_eq!(None, none);
-}
-
-#[test]
-fn ipv4_addr_to_string() {
- assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1");
- // Short address
- assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1");
- // Long address
- assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127");
-
- // Test padding
- assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 ");
- assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1");
-}
-
-#[test]
-fn ipv6_addr_to_string() {
- // ipv4-mapped address
- let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
- assert_eq!(a1.to_string(), "::ffff:192.0.2.128");
-
- // ipv4-compatible address
- let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280);
- assert_eq!(a1.to_string(), "::192.0.2.128");
-
- // v6 address with no zero segments
- assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f");
-
- // longest possible IPv6 length
- assert_eq!(
- Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888).to_string(),
- "1111:2222:3333:4444:5555:6666:7777:8888"
- );
- // padding
- assert_eq!(&format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 ");
- assert_eq!(&format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8");
-
- // reduce a single run of zeros
- assert_eq!(
- "ae::ffff:102:304",
- Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string()
- );
-
- // don't reduce just a single zero segment
- assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string());
-
- // 'any' address
- assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string());
-
- // loopback address
- assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string());
-
- // ends in zeros
- assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string());
-
- // two runs of zeros, second one is longer
- assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string());
-
- // two runs of zeros, equal length
- assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string());
-
- // don't prefix `0x` to each segment in `dbg!`.
- assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8)));
-}
-
-#[test]
-fn ipv4_to_ipv6() {
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678),
- Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped()
- );
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678),
- Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible()
- );
-}
-
-#[test]
-fn ipv6_to_ipv4_mapped() {
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(),
- Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
- );
- assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None);
-}
-
-#[test]
-fn ipv6_to_ipv4() {
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(),
- Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
- );
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(),
- Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
- );
- assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None);
-}
-
-#[test]
-fn ip_properties() {
- macro_rules! ip {
- ($s:expr) => {
- IpAddr::from_str($s).unwrap()
- };
- }
-
- macro_rules! check {
- ($s:expr) => {
- check!($s, 0);
- };
-
- ($s:expr, $mask:expr) => {{
- let unspec: u8 = 1 << 0;
- let loopback: u8 = 1 << 1;
- let global: u8 = 1 << 2;
- let multicast: u8 = 1 << 3;
- let doc: u8 = 1 << 4;
- let benchmarking: u8 = 1 << 5;
-
- if ($mask & unspec) == unspec {
- assert!(ip!($s).is_unspecified());
- } else {
- assert!(!ip!($s).is_unspecified());
- }
-
- if ($mask & loopback) == loopback {
- assert!(ip!($s).is_loopback());
- } else {
- assert!(!ip!($s).is_loopback());
- }
-
- if ($mask & global) == global {
- assert!(ip!($s).is_global());
- } else {
- assert!(!ip!($s).is_global());
- }
-
- if ($mask & multicast) == multicast {
- assert!(ip!($s).is_multicast());
- } else {
- assert!(!ip!($s).is_multicast());
- }
-
- if ($mask & doc) == doc {
- assert!(ip!($s).is_documentation());
- } else {
- assert!(!ip!($s).is_documentation());
- }
-
- if ($mask & benchmarking) == benchmarking {
- assert!(ip!($s).is_benchmarking());
- } else {
- assert!(!ip!($s).is_benchmarking());
- }
- }};
- }
-
- let unspec: u8 = 1 << 0;
- let loopback: u8 = 1 << 1;
- let global: u8 = 1 << 2;
- let multicast: u8 = 1 << 3;
- let doc: u8 = 1 << 4;
- let benchmarking: u8 = 1 << 5;
-
- check!("0.0.0.0", unspec);
- check!("0.0.0.1");
- check!("0.1.0.0");
- check!("10.9.8.7");
- check!("127.1.2.3", loopback);
- check!("172.31.254.253");
- check!("169.254.253.242");
- check!("192.0.2.183", doc);
- check!("192.1.2.183", global);
- check!("192.168.254.253");
- check!("198.51.100.0", doc);
- check!("203.0.113.0", doc);
- check!("203.2.113.0", global);
- check!("224.0.0.0", global | multicast);
- check!("239.255.255.255", global | multicast);
- check!("255.255.255.255");
- // make sure benchmarking addresses are not global
- check!("198.18.0.0", benchmarking);
- check!("198.18.54.2", benchmarking);
- check!("198.19.255.255", benchmarking);
- // make sure addresses reserved for protocol assignment are not global
- check!("192.0.0.0");
- check!("192.0.0.255");
- check!("192.0.0.100");
- // make sure reserved addresses are not global
- check!("240.0.0.0");
- check!("251.54.1.76");
- check!("254.255.255.255");
- // make sure shared addresses are not global
- check!("100.64.0.0");
- check!("100.127.255.255");
- check!("100.100.100.0");
-
- check!("::", unspec);
- check!("::1", loopback);
- check!("::0.0.0.2", global);
- check!("1::", global);
- check!("fc00::");
- check!("fdff:ffff::");
- check!("fe80:ffff::");
- check!("febf:ffff::");
- check!("fec0::", global);
- check!("ff01::", multicast);
- check!("ff02::", multicast);
- check!("ff03::", multicast);
- check!("ff04::", multicast);
- check!("ff05::", multicast);
- check!("ff08::", multicast);
- check!("ff0e::", global | multicast);
- check!("2001:db8:85a3::8a2e:370:7334", doc);
- check!("2001:2::ac32:23ff:21", global | benchmarking);
- check!("102:304:506:708:90a:b0c:d0e:f10", global);
-}
-
-#[test]
-fn ipv4_properties() {
- macro_rules! ip {
- ($s:expr) => {
- Ipv4Addr::from_str($s).unwrap()
- };
- }
-
- macro_rules! check {
- ($s:expr) => {
- check!($s, 0);
- };
-
- ($s:expr, $mask:expr) => {{
- let unspec: u16 = 1 << 0;
- let loopback: u16 = 1 << 1;
- let private: u16 = 1 << 2;
- let link_local: u16 = 1 << 3;
- let global: u16 = 1 << 4;
- let multicast: u16 = 1 << 5;
- let broadcast: u16 = 1 << 6;
- let documentation: u16 = 1 << 7;
- let benchmarking: u16 = 1 << 8;
- let reserved: u16 = 1 << 10;
- let shared: u16 = 1 << 11;
-
- if ($mask & unspec) == unspec {
- assert!(ip!($s).is_unspecified());
- } else {
- assert!(!ip!($s).is_unspecified());
- }
-
- if ($mask & loopback) == loopback {
- assert!(ip!($s).is_loopback());
- } else {
- assert!(!ip!($s).is_loopback());
- }
-
- if ($mask & private) == private {
- assert!(ip!($s).is_private());
- } else {
- assert!(!ip!($s).is_private());
- }
-
- if ($mask & link_local) == link_local {
- assert!(ip!($s).is_link_local());
- } else {
- assert!(!ip!($s).is_link_local());
- }
-
- if ($mask & global) == global {
- assert!(ip!($s).is_global());
- } else {
- assert!(!ip!($s).is_global());
- }
-
- if ($mask & multicast) == multicast {
- assert!(ip!($s).is_multicast());
- } else {
- assert!(!ip!($s).is_multicast());
- }
-
- if ($mask & broadcast) == broadcast {
- assert!(ip!($s).is_broadcast());
- } else {
- assert!(!ip!($s).is_broadcast());
- }
-
- if ($mask & documentation) == documentation {
- assert!(ip!($s).is_documentation());
- } else {
- assert!(!ip!($s).is_documentation());
- }
-
- if ($mask & benchmarking) == benchmarking {
- assert!(ip!($s).is_benchmarking());
- } else {
- assert!(!ip!($s).is_benchmarking());
- }
-
- if ($mask & reserved) == reserved {
- assert!(ip!($s).is_reserved());
- } else {
- assert!(!ip!($s).is_reserved());
- }
-
- if ($mask & shared) == shared {
- assert!(ip!($s).is_shared());
- } else {
- assert!(!ip!($s).is_shared());
- }
- }};
- }
-
- let unspec: u16 = 1 << 0;
- let loopback: u16 = 1 << 1;
- let private: u16 = 1 << 2;
- let link_local: u16 = 1 << 3;
- let global: u16 = 1 << 4;
- let multicast: u16 = 1 << 5;
- let broadcast: u16 = 1 << 6;
- let documentation: u16 = 1 << 7;
- let benchmarking: u16 = 1 << 8;
- let reserved: u16 = 1 << 10;
- let shared: u16 = 1 << 11;
-
- check!("0.0.0.0", unspec);
- check!("0.0.0.1");
- check!("0.1.0.0");
- check!("10.9.8.7", private);
- check!("127.1.2.3", loopback);
- check!("172.31.254.253", private);
- check!("169.254.253.242", link_local);
- check!("192.0.2.183", documentation);
- check!("192.1.2.183", global);
- check!("192.168.254.253", private);
- check!("198.51.100.0", documentation);
- check!("203.0.113.0", documentation);
- check!("203.2.113.0", global);
- check!("224.0.0.0", global | multicast);
- check!("239.255.255.255", global | multicast);
- check!("255.255.255.255", broadcast);
- check!("198.18.0.0", benchmarking);
- check!("198.18.54.2", benchmarking);
- check!("198.19.255.255", benchmarking);
- check!("192.0.0.0");
- check!("192.0.0.255");
- check!("192.0.0.100");
- check!("240.0.0.0", reserved);
- check!("251.54.1.76", reserved);
- check!("254.255.255.255", reserved);
- check!("100.64.0.0", shared);
- check!("100.127.255.255", shared);
- check!("100.100.100.0", shared);
-}
-
-#[test]
-fn ipv6_properties() {
- macro_rules! ip {
- ($s:expr) => {
- Ipv6Addr::from_str($s).unwrap()
- };
- }
-
- macro_rules! check {
- ($s:expr, &[$($octet:expr),*], $mask:expr) => {
- assert_eq!($s, ip!($s).to_string());
- let octets = &[$($octet),*];
- assert_eq!(&ip!($s).octets(), octets);
- assert_eq!(Ipv6Addr::from(*octets), ip!($s));
-
- let unspecified: u32 = 1 << 0;
- let loopback: u32 = 1 << 1;
- let unique_local: u32 = 1 << 2;
- let global: u32 = 1 << 3;
- let unicast_link_local: u32 = 1 << 4;
- let unicast_global: u32 = 1 << 7;
- let documentation: u32 = 1 << 8;
- let benchmarking: u32 = 1 << 16;
- let multicast_interface_local: u32 = 1 << 9;
- let multicast_link_local: u32 = 1 << 10;
- let multicast_realm_local: u32 = 1 << 11;
- let multicast_admin_local: u32 = 1 << 12;
- let multicast_site_local: u32 = 1 << 13;
- let multicast_organization_local: u32 = 1 << 14;
- let multicast_global: u32 = 1 << 15;
- let multicast: u32 = multicast_interface_local
- | multicast_admin_local
- | multicast_global
- | multicast_link_local
- | multicast_realm_local
- | multicast_site_local
- | multicast_organization_local;
-
- if ($mask & unspecified) == unspecified {
- assert!(ip!($s).is_unspecified());
- } else {
- assert!(!ip!($s).is_unspecified());
- }
- if ($mask & loopback) == loopback {
- assert!(ip!($s).is_loopback());
- } else {
- assert!(!ip!($s).is_loopback());
- }
- if ($mask & unique_local) == unique_local {
- assert!(ip!($s).is_unique_local());
- } else {
- assert!(!ip!($s).is_unique_local());
- }
- if ($mask & global) == global {
- assert!(ip!($s).is_global());
- } else {
- assert!(!ip!($s).is_global());
- }
- if ($mask & unicast_link_local) == unicast_link_local {
- assert!(ip!($s).is_unicast_link_local());
- } else {
- assert!(!ip!($s).is_unicast_link_local());
- }
- if ($mask & unicast_global) == unicast_global {
- assert!(ip!($s).is_unicast_global());
- } else {
- assert!(!ip!($s).is_unicast_global());
- }
- if ($mask & documentation) == documentation {
- assert!(ip!($s).is_documentation());
- } else {
- assert!(!ip!($s).is_documentation());
- }
- if ($mask & benchmarking) == benchmarking {
- assert!(ip!($s).is_benchmarking());
- } else {
- assert!(!ip!($s).is_benchmarking());
- }
- if ($mask & multicast) != 0 {
- assert!(ip!($s).multicast_scope().is_some());
- assert!(ip!($s).is_multicast());
- } else {
- assert!(ip!($s).multicast_scope().is_none());
- assert!(!ip!($s).is_multicast());
- }
- if ($mask & multicast_interface_local) == multicast_interface_local {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::InterfaceLocal);
- }
- if ($mask & multicast_link_local) == multicast_link_local {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::LinkLocal);
- }
- if ($mask & multicast_realm_local) == multicast_realm_local {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::RealmLocal);
- }
- if ($mask & multicast_admin_local) == multicast_admin_local {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::AdminLocal);
- }
- if ($mask & multicast_site_local) == multicast_site_local {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::SiteLocal);
- }
- if ($mask & multicast_organization_local) == multicast_organization_local {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::OrganizationLocal);
- }
- if ($mask & multicast_global) == multicast_global {
- assert_eq!(ip!($s).multicast_scope().unwrap(),
- Ipv6MulticastScope::Global);
- }
- }
- }
-
- let unspecified: u32 = 1 << 0;
- let loopback: u32 = 1 << 1;
- let unique_local: u32 = 1 << 2;
- let global: u32 = 1 << 3;
- let unicast_link_local: u32 = 1 << 4;
- let unicast_global: u32 = 1 << 7;
- let documentation: u32 = 1 << 8;
- let benchmarking: u32 = 1 << 16;
- let multicast_interface_local: u32 = 1 << 9;
- let multicast_link_local: u32 = 1 << 10;
- let multicast_realm_local: u32 = 1 << 11;
- let multicast_admin_local: u32 = 1 << 12;
- let multicast_site_local: u32 = 1 << 13;
- let multicast_organization_local: u32 = 1 << 14;
- let multicast_global: u32 = 1 << 15;
-
- check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified);
-
- check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback);
-
- check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global);
-
- check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global);
-
- check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local);
-
- check!(
- "fdff:ffff::",
- &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- unique_local
- );
-
- check!(
- "fe80:ffff::",
- &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- unicast_link_local
- );
-
- check!("fe80::", &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local);
-
- check!(
- "febf:ffff::",
- &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- unicast_link_local
- );
-
- check!("febf::", &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local);
-
- check!(
- "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
- &[
- 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff
- ],
- unicast_link_local
- );
-
- check!(
- "fe80::ffff:ffff:ffff:ffff",
- &[
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff
- ],
- unicast_link_local
- );
-
- check!(
- "fe80:0:0:1::",
- &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
- unicast_link_local
- );
-
- check!(
- "fec0::",
- &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- unicast_global | global
- );
-
- check!(
- "ff01::",
- &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- multicast_interface_local
- );
-
- check!("ff02::", &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_link_local);
-
- check!("ff03::", &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_realm_local);
-
- check!("ff04::", &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_admin_local);
-
- check!("ff05::", &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_site_local);
-
- check!(
- "ff08::",
- &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- multicast_organization_local
- );
-
- check!(
- "ff0e::",
- &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- multicast_global | global
- );
-
- check!(
- "2001:db8:85a3::8a2e:370:7334",
- &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34],
- documentation
- );
-
- check!(
- "2001:2::ac32:23ff:21",
- &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21],
- global | unicast_global | benchmarking
- );
-
- check!(
- "102:304:506:708:90a:b0c:d0e:f10",
- &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
- global | unicast_global
- );
-}
-
-#[test]
-fn to_socket_addr_socketaddr() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345);
- assert_eq!(Ok(vec![a]), tsa(a));
-}
-
-#[test]
-fn test_ipv4_to_int() {
- let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
- assert_eq!(u32::from(a), 0x11223344);
-}
-
-#[test]
-fn test_int_to_ipv4() {
- let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
- assert_eq!(Ipv4Addr::from(0x11223344), a);
-}
-
-#[test]
-fn test_ipv6_to_int() {
- let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
- assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128);
-}
-
-#[test]
-fn test_int_to_ipv6() {
- let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
- assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a);
-}
-
-#[test]
-fn ipv4_from_constructors() {
- assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1));
- assert!(Ipv4Addr::LOCALHOST.is_loopback());
- assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0));
- assert!(Ipv4Addr::UNSPECIFIED.is_unspecified());
- assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255));
- assert!(Ipv4Addr::BROADCAST.is_broadcast());
-}
-
-#[test]
-fn ipv6_from_constructors() {
- assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
- assert!(Ipv6Addr::LOCALHOST.is_loopback());
- assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
- assert!(Ipv6Addr::UNSPECIFIED.is_unspecified());
-}
-
-#[test]
-fn ipv4_from_octets() {
- assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1))
-}
-
-#[test]
-fn ipv6_from_segments() {
- let from_u16s =
- Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
- let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff);
- assert_eq!(new, from_u16s);
-}
-
-#[test]
-fn ipv6_from_octets() {
- let from_u16s =
- Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
- let from_u8s = Ipv6Addr::from([
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
- 0xff,
- ]);
- assert_eq!(from_u16s, from_u8s);
-}
-
-#[test]
-fn cmp() {
- let v41 = Ipv4Addr::new(100, 64, 3, 3);
- let v42 = Ipv4Addr::new(192, 0, 2, 2);
- let v61 = "2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap();
- let v62 = "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap();
- assert!(v41 < v42);
- assert!(v61 < v62);
-
- assert_eq!(v41, IpAddr::V4(v41));
- assert_eq!(v61, IpAddr::V6(v61));
- assert!(v41 != IpAddr::V4(v42));
- assert!(v61 != IpAddr::V6(v62));
-
- assert!(v41 < IpAddr::V4(v42));
- assert!(v61 < IpAddr::V6(v62));
- assert!(IpAddr::V4(v41) < v42);
- assert!(IpAddr::V6(v61) < v62);
-
- assert!(v41 < IpAddr::V6(v61));
- assert!(IpAddr::V4(v41) < v61);
-}
-
-#[test]
-fn is_v4() {
- let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3));
- assert!(ip.is_ipv4());
- assert!(!ip.is_ipv6());
-}
-
-#[test]
-fn is_v6() {
- let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678));
- assert!(!ip.is_ipv4());
- assert!(ip.is_ipv6());
-}
-
-#[test]
-fn ipv4_const() {
- // test that the methods of `Ipv4Addr` are usable in a const context
-
- const IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
- assert_eq!(IP_ADDRESS, Ipv4Addr::LOCALHOST);
-
- const OCTETS: [u8; 4] = IP_ADDRESS.octets();
- assert_eq!(OCTETS, [127, 0, 0, 1]);
-
- const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
- assert!(!IS_UNSPECIFIED);
-
- const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
- assert!(IS_LOOPBACK);
-
- const IS_PRIVATE: bool = IP_ADDRESS.is_private();
- assert!(!IS_PRIVATE);
-
- const IS_LINK_LOCAL: bool = IP_ADDRESS.is_link_local();
- assert!(!IS_LINK_LOCAL);
-
- const IS_GLOBAL: bool = IP_ADDRESS.is_global();
- assert!(!IS_GLOBAL);
-
- const IS_SHARED: bool = IP_ADDRESS.is_shared();
- assert!(!IS_SHARED);
-
- const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
- assert!(!IS_BENCHMARKING);
-
- const IS_RESERVED: bool = IP_ADDRESS.is_reserved();
- assert!(!IS_RESERVED);
-
- const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
- assert!(!IS_MULTICAST);
-
- const IS_BROADCAST: bool = IP_ADDRESS.is_broadcast();
- assert!(!IS_BROADCAST);
-
- const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation();
- assert!(!IS_DOCUMENTATION);
-
- const IP_V6_COMPATIBLE: Ipv6Addr = IP_ADDRESS.to_ipv6_compatible();
- assert_eq!(
- IP_V6_COMPATIBLE,
- Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1])
- );
-
- const IP_V6_MAPPED: Ipv6Addr = IP_ADDRESS.to_ipv6_mapped();
- assert_eq!(
- IP_V6_MAPPED,
- Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1])
- );
-}
-
-#[test]
-fn ipv6_const() {
- // test that the methods of `Ipv6Addr` are usable in a const context
-
- const IP_ADDRESS: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
- assert_eq!(IP_ADDRESS, Ipv6Addr::LOCALHOST);
-
- const SEGMENTS: [u16; 8] = IP_ADDRESS.segments();
- assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]);
-
- const OCTETS: [u8; 16] = IP_ADDRESS.octets();
- assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
-
- const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
- assert!(!IS_UNSPECIFIED);
-
- const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
- assert!(IS_LOOPBACK);
-
- const IS_GLOBAL: bool = IP_ADDRESS.is_global();
- assert!(!IS_GLOBAL);
-
- const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local();
- assert!(!IS_UNIQUE_LOCAL);
-
- const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local();
- assert!(!IS_UNICAST_LINK_LOCAL);
-
- const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation();
- assert!(!IS_DOCUMENTATION);
-
- const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
- assert!(!IS_BENCHMARKING);
-
- const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global();
- assert!(!IS_UNICAST_GLOBAL);
-
- const MULTICAST_SCOPE: Option<Ipv6MulticastScope> = IP_ADDRESS.multicast_scope();
- assert_eq!(MULTICAST_SCOPE, None);
-
- const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
- assert!(!IS_MULTICAST);
-
- const IP_V4: Option<Ipv4Addr> = IP_ADDRESS.to_ipv4();
- assert_eq!(IP_V4.unwrap(), Ipv4Addr::new(0, 0, 0, 1));
-}
-
-#[test]
-fn ip_const() {
- // test that the methods of `IpAddr` are usable in a const context
-
- const IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
-
- const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
- assert!(!IS_UNSPECIFIED);
-
- const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
- assert!(IS_LOOPBACK);
-
- const IS_GLOBAL: bool = IP_ADDRESS.is_global();
- assert!(!IS_GLOBAL);
-
- const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
- assert!(!IS_MULTICAST);
-
- const IS_IP_V4: bool = IP_ADDRESS.is_ipv4();
- assert!(IS_IP_V4);
-
- const IS_IP_V6: bool = IP_ADDRESS.is_ipv6();
- assert!(!IS_IP_V6);
-}
-
-#[test]
-fn structural_match() {
- // test that all IP types can be structurally matched upon
-
- const IPV4: Ipv4Addr = Ipv4Addr::LOCALHOST;
- match IPV4 {
- Ipv4Addr::LOCALHOST => {}
- _ => unreachable!(),
- }
-
- const IPV6: Ipv6Addr = Ipv6Addr::LOCALHOST;
- match IPV6 {
- Ipv6Addr::LOCALHOST => {}
- _ => unreachable!(),
- }
-
- const IP: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
- match IP {
- IpAddr::V4(Ipv4Addr::LOCALHOST) => {}
- _ => unreachable!(),
- }
-}
diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs
new file mode 100644
index 000000000..4f14fc280
--- /dev/null
+++ b/library/std/src/net/ip_addr.rs
@@ -0,0 +1,2095 @@
+// Tests for this module
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+use crate::cmp::Ordering;
+use crate::fmt::{self, Write};
+use crate::mem::transmute;
+use crate::sys::net::netc as c;
+use crate::sys_common::{FromInner, IntoInner};
+
+use super::display_buffer::DisplayBuffer;
+
+/// An IP address, either IPv4 or IPv6.
+///
+/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their
+/// respective documentation for more details.
+///
+/// # Examples
+///
+/// ```
+/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+///
+/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
+/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+///
+/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4));
+/// assert_eq!("::1".parse(), Ok(localhost_v6));
+///
+/// assert_eq!(localhost_v4.is_ipv6(), false);
+/// assert_eq!(localhost_v4.is_ipv4(), true);
+/// ```
+#[cfg_attr(not(test), rustc_diagnostic_item = "IpAddr")]
+#[stable(feature = "ip_addr", since = "1.7.0")]
+#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub enum IpAddr {
+ /// An IPv4 address.
+ #[stable(feature = "ip_addr", since = "1.7.0")]
+ V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr),
+ /// An IPv6 address.
+ #[stable(feature = "ip_addr", since = "1.7.0")]
+ V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr),
+}
+
+/// An IPv4 address.
+///
+/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791].
+/// They are usually represented as four octets.
+///
+/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
+///
+/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791
+///
+/// # Textual representation
+///
+/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
+/// notation, divided by `.` (this is called "dot-decimal notation").
+/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which
+/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943].
+///
+/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1
+/// [`FromStr`]: crate::str::FromStr
+///
+/// # Examples
+///
+/// ```
+/// use std::net::Ipv4Addr;
+///
+/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
+/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
+/// assert_eq!(localhost.is_loopback(), true);
+/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal
+/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal
+/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
+/// ```
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv4Addr")]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct Ipv4Addr {
+ octets: [u8; 4],
+}
+
+/// An IPv6 address.
+///
+/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291].
+/// They are usually represented as eight 16-bit segments.
+///
+/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
+///
+/// # Embedding IPv4 Addresses
+///
+/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
+///
+/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined:
+/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated.
+///
+/// Both types of addresses are not assigned any special meaning by this implementation,
+/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`,
+/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is.
+/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address.
+///
+/// ### IPv4-Compatible IPv6 Addresses
+///
+/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated.
+/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows:
+///
+/// ```text
+/// | 80 bits | 16 | 32 bits |
+/// +--------------------------------------+--------------------------+
+/// |0000..............................0000|0000| IPv4 address |
+/// +--------------------------------------+----+---------------------+
+/// ```
+/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`.
+///
+/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`].
+/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address.
+///
+/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
+///
+/// ### IPv4-Mapped IPv6 Addresses
+///
+/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2].
+/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows:
+///
+/// ```text
+/// | 80 bits | 16 | 32 bits |
+/// +--------------------------------------+--------------------------+
+/// |0000..............................0000|FFFF| IPv4 address |
+/// +--------------------------------------+----+---------------------+
+/// ```
+/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`.
+///
+/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`].
+/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address.
+/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`. Use
+/// [`Ipv6Addr::to_ipv4_mapped`] to avoid this.
+///
+/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
+///
+/// # Textual representation
+///
+/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent
+/// an IPv6 address in text, but in general, each segments is written in hexadecimal
+/// notation, and segments are separated by `:`. For more information, see
+/// [IETF RFC 5952].
+///
+/// [`FromStr`]: crate::str::FromStr
+/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952
+///
+/// # Examples
+///
+/// ```
+/// use std::net::Ipv6Addr;
+///
+/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+/// assert_eq!("::1".parse(), Ok(localhost));
+/// assert_eq!(localhost.is_loopback(), true);
+/// ```
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv6Addr")]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct Ipv6Addr {
+ octets: [u8; 16],
+}
+
+/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2].
+///
+/// # Stability Guarantees
+///
+/// Not all possible values for a multicast scope have been assigned.
+/// Future RFCs may introduce new scopes, which will be added as variants to this enum;
+/// because of this the enum is marked as `#[non_exhaustive]`.
+///
+/// # Examples
+/// ```
+/// #![feature(ip)]
+///
+/// use std::net::Ipv6Addr;
+/// use std::net::Ipv6MulticastScope::*;
+///
+/// // An IPv6 multicast address with global scope (`ff0e::`).
+/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0);
+///
+/// // Will print "Global scope".
+/// match address.multicast_scope() {
+/// Some(InterfaceLocal) => println!("Interface-Local scope"),
+/// Some(LinkLocal) => println!("Link-Local scope"),
+/// Some(RealmLocal) => println!("Realm-Local scope"),
+/// Some(AdminLocal) => println!("Admin-Local scope"),
+/// Some(SiteLocal) => println!("Site-Local scope"),
+/// Some(OrganizationLocal) => println!("Organization-Local scope"),
+/// Some(Global) => println!("Global scope"),
+/// Some(_) => println!("Unknown scope"),
+/// None => println!("Not a multicast address!")
+/// }
+///
+/// ```
+///
+/// [IPv6 multicast address]: Ipv6Addr
+/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2
+#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
+#[unstable(feature = "ip", issue = "27709")]
+#[non_exhaustive]
+pub enum Ipv6MulticastScope {
+ /// Interface-Local scope.
+ InterfaceLocal,
+ /// Link-Local scope.
+ LinkLocal,
+ /// Realm-Local scope.
+ RealmLocal,
+ /// Admin-Local scope.
+ AdminLocal,
+ /// Site-Local scope.
+ SiteLocal,
+ /// Organization-Local scope.
+ OrganizationLocal,
+ /// Global scope.
+ Global,
+}
+
+impl IpAddr {
+ /// Returns [`true`] for the special 'unspecified' address.
+ ///
+ /// See the documentation for [`Ipv4Addr::is_unspecified()`] and
+ /// [`Ipv6Addr::is_unspecified()`] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "ip_shared", since = "1.12.0")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unspecified(&self) -> bool {
+ match self {
+ IpAddr::V4(ip) => ip.is_unspecified(),
+ IpAddr::V6(ip) => ip.is_unspecified(),
+ }
+ }
+
+ /// Returns [`true`] if this is a loopback address.
+ ///
+ /// See the documentation for [`Ipv4Addr::is_loopback()`] and
+ /// [`Ipv6Addr::is_loopback()`] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "ip_shared", since = "1.12.0")]
+ #[must_use]
+ #[inline]
+ pub const fn is_loopback(&self) -> bool {
+ match self {
+ IpAddr::V4(ip) => ip.is_loopback(),
+ IpAddr::V6(ip) => ip.is_loopback(),
+ }
+ }
+
+ /// Returns [`true`] if the address appears to be globally routable.
+ ///
+ /// See the documentation for [`Ipv4Addr::is_global()`] and
+ /// [`Ipv6Addr::is_global()`] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_global(&self) -> bool {
+ match self {
+ IpAddr::V4(ip) => ip.is_global(),
+ IpAddr::V6(ip) => ip.is_global(),
+ }
+ }
+
+ /// Returns [`true`] if this is a multicast address.
+ ///
+ /// See the documentation for [`Ipv4Addr::is_multicast()`] and
+ /// [`Ipv6Addr::is_multicast()`] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "ip_shared", since = "1.12.0")]
+ #[must_use]
+ #[inline]
+ pub const fn is_multicast(&self) -> bool {
+ match self {
+ IpAddr::V4(ip) => ip.is_multicast(),
+ IpAddr::V6(ip) => ip.is_multicast(),
+ }
+ }
+
+ /// Returns [`true`] if this address is in a range designated for documentation.
+ ///
+ /// See the documentation for [`Ipv4Addr::is_documentation()`] and
+ /// [`Ipv6Addr::is_documentation()`] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true);
+ /// assert_eq!(
+ /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(),
+ /// true
+ /// );
+ /// ```
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_documentation(&self) -> bool {
+ match self {
+ IpAddr::V4(ip) => ip.is_documentation(),
+ IpAddr::V6(ip) => ip.is_documentation(),
+ }
+ }
+
+ /// Returns [`true`] if this address is in a range designated for benchmarking.
+ ///
+ /// See the documentation for [`Ipv4Addr::is_benchmarking()`] and
+ /// [`Ipv6Addr::is_benchmarking()`] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(198, 19, 255, 255)).is_benchmarking(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0)).is_benchmarking(), true);
+ /// ```
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_benchmarking(&self) -> bool {
+ match self {
+ IpAddr::V4(ip) => ip.is_benchmarking(),
+ IpAddr::V6(ip) => ip.is_benchmarking(),
+ }
+ }
+
+ /// Returns [`true`] if this address is an [`IPv4` address], and [`false`]
+ /// otherwise.
+ ///
+ /// [`IPv4` address]: IpAddr::V4
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "ipaddr_checker", since = "1.16.0")]
+ #[must_use]
+ #[inline]
+ pub const fn is_ipv4(&self) -> bool {
+ matches!(self, IpAddr::V4(_))
+ }
+
+ /// Returns [`true`] if this address is an [`IPv6` address], and [`false`]
+ /// otherwise.
+ ///
+ /// [`IPv6` address]: IpAddr::V6
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "ipaddr_checker", since = "1.16.0")]
+ #[must_use]
+ #[inline]
+ pub const fn is_ipv6(&self) -> bool {
+ matches!(self, IpAddr::V6(_))
+ }
+
+ /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it
+ /// return `self` as-is.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true);
+ /// ```
+ #[inline]
+ #[must_use = "this returns the result of the operation, \
+ without modifying the original"]
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ pub const fn to_canonical(&self) -> IpAddr {
+ match self {
+ &v4 @ IpAddr::V4(_) => v4,
+ IpAddr::V6(v6) => v6.to_canonical(),
+ }
+ }
+}
+
+impl Ipv4Addr {
+ /// Creates a new IPv4 address from four eight-bit octets.
+ ///
+ /// The result will represent the IP address `a`.`b`.`c`.`d`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::new(127, 0, 0, 1);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ #[inline]
+ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
+ Ipv4Addr { octets: [a, b, c, d] }
+ }
+
+ /// An IPv4 address with the address pointing to localhost: `127.0.0.1`
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::LOCALHOST;
+ /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1));
+ /// ```
+ #[stable(feature = "ip_constructors", since = "1.30.0")]
+ pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1);
+
+ /// An IPv4 address representing an unspecified address: `0.0.0.0`
+ ///
+ /// This corresponds to the constant `INADDR_ANY` in other languages.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::UNSPECIFIED;
+ /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0));
+ /// ```
+ #[doc(alias = "INADDR_ANY")]
+ #[stable(feature = "ip_constructors", since = "1.30.0")]
+ pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0);
+
+ /// An IPv4 address representing the broadcast address: `255.255.255.255`
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::BROADCAST;
+ /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255));
+ /// ```
+ #[stable(feature = "ip_constructors", since = "1.30.0")]
+ pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255);
+
+ /// Returns the four eight-bit integers that make up this address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::new(127, 0, 0, 1);
+ /// assert_eq!(addr.octets(), [127, 0, 0, 1]);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ #[inline]
+ pub const fn octets(&self) -> [u8; 4] {
+ self.octets
+ }
+
+ /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`).
+ ///
+ /// This property is defined in _UNIX Network Programming, Second Edition_,
+ /// W. Richard Stevens, p. 891; see also [ip7].
+ ///
+ /// [ip7]: https://man7.org/linux/man-pages/man7/ip.7.html
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true);
+ /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
+ #[stable(feature = "ip_shared", since = "1.12.0")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unspecified(&self) -> bool {
+ u32::from_be_bytes(self.octets) == 0
+ }
+
+ /// Returns [`true`] if this is a loopback address (`127.0.0.0/8`).
+ ///
+ /// This property is defined by [IETF RFC 1122].
+ ///
+ /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
+ /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_loopback(&self) -> bool {
+ self.octets()[0] == 127
+ }
+
+ /// Returns [`true`] if this is a private address.
+ ///
+ /// The private address ranges are defined in [IETF RFC 1918] and include:
+ ///
+ /// - `10.0.0.0/8`
+ /// - `172.16.0.0/12`
+ /// - `192.168.0.0/16`
+ ///
+ /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true);
+ /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true);
+ /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true);
+ /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true);
+ /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false);
+ /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
+ /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_private(&self) -> bool {
+ match self.octets() {
+ [10, ..] => true,
+ [172, b, ..] if b >= 16 && b <= 31 => true,
+ [192, 168, ..] => true,
+ _ => false,
+ }
+ }
+
+ /// Returns [`true`] if the address is link-local (`169.254.0.0/16`).
+ ///
+ /// This property is defined by [IETF RFC 3927].
+ ///
+ /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true);
+ /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true);
+ /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_link_local(&self) -> bool {
+ matches!(self.octets(), [169, 254, ..])
+ }
+
+ /// Returns [`true`] if the address appears to be globally reachable
+ /// as specified by the [IANA IPv4 Special-Purpose Address Registry].
+ /// Whether or not an address is practically reachable will depend on your network configuration.
+ ///
+ /// Most IPv4 addresses are globally reachable;
+ /// unless they are specifically defined as *not* globally reachable.
+ ///
+ /// Non-exhaustive list of notable addresses that are not globally reachable:
+ ///
+ /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified))
+ /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private))
+ /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared))
+ /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback))
+ /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local))
+ /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation))
+ /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking))
+ /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved))
+ /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast))
+ ///
+ /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry].
+ ///
+ /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
+ /// [unspecified address]: Ipv4Addr::UNSPECIFIED
+ /// [broadcast address]: Ipv4Addr::BROADCAST
+
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv4Addr;
+ ///
+ /// // Most IPv4 addresses are globally reachable:
+ /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true);
+ ///
+ /// // However some addresses have been assigned a special meaning
+ /// // that makes them not globally reachable. Some examples are:
+ ///
+ /// // The unspecified address (`0.0.0.0`)
+ /// assert_eq!(Ipv4Addr::UNSPECIFIED.is_global(), false);
+ ///
+ /// // Addresses reserved for private use (`10.0.0.0/8`, `172.16.0.0/12`, 192.168.0.0/16)
+ /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false);
+ /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false);
+ /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false);
+ ///
+ /// // Addresses in the shared address space (`100.64.0.0/10`)
+ /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false);
+ ///
+ /// // The loopback addresses (`127.0.0.0/8`)
+ /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false);
+ ///
+ /// // Link-local addresses (`169.254.0.0/16`)
+ /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false);
+ ///
+ /// // Addresses reserved for documentation (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`)
+ /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false);
+ /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false);
+ /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false);
+ ///
+ /// // Addresses reserved for benchmarking (`198.18.0.0/15`)
+ /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false);
+ ///
+ /// // Reserved addresses (`240.0.0.0/4`)
+ /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false);
+ ///
+ /// // The broadcast address (`255.255.255.255`)
+ /// assert_eq!(Ipv4Addr::BROADCAST.is_global(), false);
+ ///
+ /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry.
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_global(&self) -> bool {
+ !(self.octets()[0] == 0 // "This network"
+ || self.is_private()
+ || self.is_shared()
+ || self.is_loopback()
+ || self.is_link_local()
+ // addresses reserved for future protocols (`192.0.0.0/24`)
+ ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0)
+ || self.is_documentation()
+ || self.is_benchmarking()
+ || self.is_reserved()
+ || self.is_broadcast())
+ }
+
+ /// Returns [`true`] if this address is part of the Shared Address Space defined in
+ /// [IETF RFC 6598] (`100.64.0.0/10`).
+ ///
+ /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true);
+ /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true);
+ /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_shared(&self) -> bool {
+ self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
+ }
+
+ /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
+ /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
+ /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
+ ///
+ /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
+ /// [errata 423]: https://www.rfc-editor.org/errata/eid423
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false);
+ /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true);
+ /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true);
+ /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_benchmarking(&self) -> bool {
+ self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
+ }
+
+ /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
+ /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the
+ /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
+ /// it is obviously not reserved for future use.
+ ///
+ /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
+ ///
+ /// # Warning
+ ///
+ /// As IANA assigns new addresses, this method will be
+ /// updated. This may result in non-reserved addresses being
+ /// treated as reserved in code that relies on an outdated version
+ /// of this method.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true);
+ /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true);
+ ///
+ /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false);
+ /// // The broadcast address is not considered as reserved for future use by this implementation
+ /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_reserved(&self) -> bool {
+ self.octets()[0] & 240 == 240 && !self.is_broadcast()
+ }
+
+ /// Returns [`true`] if this is a multicast address (`224.0.0.0/4`).
+ ///
+ /// Multicast addresses have a most significant octet between `224` and `239`,
+ /// and is defined by [IETF RFC 5771].
+ ///
+ /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true);
+ /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true);
+ /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_multicast(&self) -> bool {
+ self.octets()[0] >= 224 && self.octets()[0] <= 239
+ }
+
+ /// Returns [`true`] if this is a broadcast address (`255.255.255.255`).
+ ///
+ /// A broadcast address has all octets set to `255` as defined in [IETF RFC 919].
+ ///
+ /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true);
+ /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_broadcast(&self) -> bool {
+ u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets())
+ }
+
+ /// Returns [`true`] if this address is in a range designated for documentation.
+ ///
+ /// This is defined in [IETF RFC 5737]:
+ ///
+ /// - `192.0.2.0/24` (TEST-NET-1)
+ /// - `198.51.100.0/24` (TEST-NET-2)
+ /// - `203.0.113.0/24` (TEST-NET-3)
+ ///
+ /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true);
+ /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true);
+ /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true);
+ /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_documentation(&self) -> bool {
+ matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _])
+ }
+
+ /// Converts this address to an [IPv4-compatible] [`IPv6` address].
+ ///
+ /// `a.b.c.d` becomes `::a.b.c.d`
+ ///
+ /// Note that IPv4-compatible addresses have been officially deprecated.
+ /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead.
+ ///
+ /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
+ /// [`IPv6` address]: Ipv6Addr
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(
+ /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(),
+ /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff)
+ /// );
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use = "this returns the result of the operation, \
+ without modifying the original"]
+ #[inline]
+ pub const fn to_ipv6_compatible(&self) -> Ipv6Addr {
+ let [a, b, c, d] = self.octets();
+ Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] }
+ }
+
+ /// Converts this address to an [IPv4-mapped] [`IPv6` address].
+ ///
+ /// `a.b.c.d` becomes `::ffff:a.b.c.d`
+ ///
+ /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
+ /// [`IPv6` address]: Ipv6Addr
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(),
+ /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff));
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use = "this returns the result of the operation, \
+ without modifying the original"]
+ #[inline]
+ pub const fn to_ipv6_mapped(&self) -> Ipv6Addr {
+ let [a, b, c, d] = self.octets();
+ Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] }
+ }
+}
+
+#[stable(feature = "ip_addr", since = "1.7.0")]
+impl fmt::Display for IpAddr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ IpAddr::V4(ip) => ip.fmt(fmt),
+ IpAddr::V6(ip) => ip.fmt(fmt),
+ }
+ }
+}
+
+#[stable(feature = "ip_addr", since = "1.7.0")]
+impl fmt::Debug for IpAddr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, fmt)
+ }
+}
+
+#[stable(feature = "ip_from_ip", since = "1.16.0")]
+impl From<Ipv4Addr> for IpAddr {
+ /// Copies this address to a new `IpAddr::V4`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr};
+ ///
+ /// let addr = Ipv4Addr::new(127, 0, 0, 1);
+ ///
+ /// assert_eq!(
+ /// IpAddr::V4(addr),
+ /// IpAddr::from(addr)
+ /// )
+ /// ```
+ #[inline]
+ fn from(ipv4: Ipv4Addr) -> IpAddr {
+ IpAddr::V4(ipv4)
+ }
+}
+
+#[stable(feature = "ip_from_ip", since = "1.16.0")]
+impl From<Ipv6Addr> for IpAddr {
+ /// Copies this address to a new `IpAddr::V6`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv6Addr};
+ ///
+ /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
+ ///
+ /// assert_eq!(
+ /// IpAddr::V6(addr),
+ /// IpAddr::from(addr)
+ /// );
+ /// ```
+ #[inline]
+ fn from(ipv6: Ipv6Addr) -> IpAddr {
+ IpAddr::V6(ipv6)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for Ipv4Addr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let octets = self.octets();
+
+ // If there are no alignment requirements, write the IP address directly to `f`.
+ // Otherwise, write it to a local buffer and then use `f.pad`.
+ if fmt.precision().is_none() && fmt.width().is_none() {
+ write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3])
+ } else {
+ const LONGEST_IPV4_ADDR: &str = "255.255.255.255";
+
+ let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new();
+ // Buffer is long enough for the longest possible IPv4 address, so this should never fail.
+ write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap();
+
+ fmt.pad(buf.as_str())
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for Ipv4Addr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, fmt)
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialEq<Ipv4Addr> for IpAddr {
+ #[inline]
+ fn eq(&self, other: &Ipv4Addr) -> bool {
+ match self {
+ IpAddr::V4(v4) => v4 == other,
+ IpAddr::V6(_) => false,
+ }
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialEq<IpAddr> for Ipv4Addr {
+ #[inline]
+ fn eq(&self, other: &IpAddr) -> bool {
+ match other {
+ IpAddr::V4(v4) => self == v4,
+ IpAddr::V6(_) => false,
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl PartialOrd for Ipv4Addr {
+ #[inline]
+ fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialOrd<Ipv4Addr> for IpAddr {
+ #[inline]
+ fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> {
+ match self {
+ IpAddr::V4(v4) => v4.partial_cmp(other),
+ IpAddr::V6(_) => Some(Ordering::Greater),
+ }
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialOrd<IpAddr> for Ipv4Addr {
+ #[inline]
+ fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
+ match other {
+ IpAddr::V4(v4) => self.partial_cmp(v4),
+ IpAddr::V6(_) => Some(Ordering::Less),
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Ord for Ipv4Addr {
+ #[inline]
+ fn cmp(&self, other: &Ipv4Addr) -> Ordering {
+ self.octets.cmp(&other.octets)
+ }
+}
+
+impl IntoInner<c::in_addr> for Ipv4Addr {
+ #[inline]
+ fn into_inner(self) -> c::in_addr {
+ // `s_addr` is stored as BE on all machines and the array is in BE order.
+ // So the native endian conversion method is used so that it's never swapped.
+ c::in_addr { s_addr: u32::from_ne_bytes(self.octets) }
+ }
+}
+impl FromInner<c::in_addr> for Ipv4Addr {
+ fn from_inner(addr: c::in_addr) -> Ipv4Addr {
+ Ipv4Addr { octets: addr.s_addr.to_ne_bytes() }
+ }
+}
+
+#[stable(feature = "ip_u32", since = "1.1.0")]
+impl From<Ipv4Addr> for u32 {
+ /// Converts an `Ipv4Addr` into a host byte order `u32`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78);
+ /// assert_eq!(0x12345678, u32::from(addr));
+ /// ```
+ #[inline]
+ fn from(ip: Ipv4Addr) -> u32 {
+ u32::from_be_bytes(ip.octets)
+ }
+}
+
+#[stable(feature = "ip_u32", since = "1.1.0")]
+impl From<u32> for Ipv4Addr {
+ /// Converts a host byte order `u32` into an `Ipv4Addr`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::from(0x12345678);
+ /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr);
+ /// ```
+ #[inline]
+ fn from(ip: u32) -> Ipv4Addr {
+ Ipv4Addr { octets: ip.to_be_bytes() }
+ }
+}
+
+#[stable(feature = "from_slice_v4", since = "1.9.0")]
+impl From<[u8; 4]> for Ipv4Addr {
+ /// Creates an `Ipv4Addr` from a four element byte array.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]);
+ /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr);
+ /// ```
+ #[inline]
+ fn from(octets: [u8; 4]) -> Ipv4Addr {
+ Ipv4Addr { octets }
+ }
+}
+
+#[stable(feature = "ip_from_slice", since = "1.17.0")]
+impl From<[u8; 4]> for IpAddr {
+ /// Creates an `IpAddr::V4` from a four element byte array.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr};
+ ///
+ /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]);
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr);
+ /// ```
+ #[inline]
+ fn from(octets: [u8; 4]) -> IpAddr {
+ IpAddr::V4(Ipv4Addr::from(octets))
+ }
+}
+
+impl Ipv6Addr {
+ /// Creates a new IPv6 address from eight 16-bit segments.
+ ///
+ /// The result will represent the IP address `a:b:c:d:e:f:g:h`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ #[inline]
+ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
+ let addr16 = [
+ a.to_be(),
+ b.to_be(),
+ c.to_be(),
+ d.to_be(),
+ e.to_be(),
+ f.to_be(),
+ g.to_be(),
+ h.to_be(),
+ ];
+ Ipv6Addr {
+ // All elements in `addr16` are big endian.
+ // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`.
+ octets: unsafe { transmute::<_, [u8; 16]>(addr16) },
+ }
+ }
+
+ /// An IPv6 address representing localhost: `::1`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::LOCALHOST;
+ /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+ /// ```
+ #[stable(feature = "ip_constructors", since = "1.30.0")]
+ pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+
+ /// An IPv6 address representing the unspecified address: `::`
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::UNSPECIFIED;
+ /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
+ /// ```
+ #[stable(feature = "ip_constructors", since = "1.30.0")]
+ pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
+
+ /// Returns the eight 16-bit segments that make up this address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(),
+ /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ #[inline]
+ pub const fn segments(&self) -> [u16; 8] {
+ // All elements in `self.octets` must be big endian.
+ // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`.
+ let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.octets) };
+ // We want native endian u16
+ [
+ u16::from_be(a),
+ u16::from_be(b),
+ u16::from_be(c),
+ u16::from_be(d),
+ u16::from_be(e),
+ u16::from_be(f),
+ u16::from_be(g),
+ u16::from_be(h),
+ ]
+ }
+
+ /// Returns [`true`] for the special 'unspecified' address (`::`).
+ ///
+ /// This property is defined in [IETF RFC 4291].
+ ///
+ /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unspecified(&self) -> bool {
+ u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
+ }
+
+ /// Returns [`true`] if this is the [loopback address] (`::1`),
+ /// as defined in [IETF RFC 4291 section 2.5.3].
+ ///
+ /// Contrary to IPv4, in IPv6 there is only one loopback address.
+ ///
+ /// [loopback address]: Ipv6Addr::LOCALHOST
+ /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_loopback(&self) -> bool {
+ u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets())
+ }
+
+ /// Returns [`true`] if the address appears to be globally reachable
+ /// as specified by the [IANA IPv6 Special-Purpose Address Registry].
+ /// Whether or not an address is practically reachable will depend on your network configuration.
+ ///
+ /// Most IPv6 addresses are globally reachable;
+ /// unless they are specifically defined as *not* globally reachable.
+ ///
+ /// Non-exhaustive list of notable addresses that are not globally reachable:
+ /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified))
+ /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback))
+ /// - IPv4-mapped addresses
+ /// - Addresses reserved for benchmarking
+ /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation))
+ /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local))
+ /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local))
+ ///
+ /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry].
+ ///
+ /// Note that an address having global scope is not the same as being globally reachable,
+ /// and there is no direct relation between the two concepts: There exist addresses with global scope
+ /// that are not globally reachable (for example unique local addresses),
+ /// and addresses that are globally reachable without having global scope
+ /// (multicast addresses with non-global scope).
+ ///
+ /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
+ /// [unspecified address]: Ipv6Addr::UNSPECIFIED
+ /// [loopback address]: Ipv6Addr::LOCALHOST
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// // Most IPv6 addresses are globally reachable:
+ /// assert_eq!(Ipv6Addr::new(0x26, 0, 0x1c9, 0, 0, 0xafc8, 0x10, 0x1).is_global(), true);
+ ///
+ /// // However some addresses have been assigned a special meaning
+ /// // that makes them not globally reachable. Some examples are:
+ ///
+ /// // The unspecified address (`::`)
+ /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_global(), false);
+ ///
+ /// // The loopback address (`::1`)
+ /// assert_eq!(Ipv6Addr::LOCALHOST.is_global(), false);
+ ///
+ /// // IPv4-mapped addresses (`::ffff:0:0/96`)
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), false);
+ ///
+ /// // Addresses reserved for benchmarking (`2001:2::/48`)
+ /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false);
+ ///
+ /// // Addresses reserved for documentation (`2001:db8::/32`)
+ /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false);
+ ///
+ /// // Unique local addresses (`fc00::/7`)
+ /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false);
+ ///
+ /// // Unicast addresses with link-local scope (`fe80::/10`)
+ /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 1).is_global(), false);
+ ///
+ /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry.
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_global(&self) -> bool {
+ !(self.is_unspecified()
+ || self.is_loopback()
+ // IPv4-mapped Address (`::ffff:0:0/96`)
+ || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
+ // IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
+ || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
+ // Discard-Only Address Block (`100::/64`)
+ || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _])
+ // IETF Protocol Assignments (`2001::/23`)
+ || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
+ && !(
+ // Port Control Protocol Anycast (`2001:1::1`)
+ u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
+ // Traversal Using Relays around NAT Anycast (`2001:1::2`)
+ || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
+ // AMT (`2001:3::/32`)
+ || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _])
+ // AS112-v6 (`2001:4:112::/48`)
+ || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
+ // ORCHIDv2 (`2001:20::/28`)
+ || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
+ ))
+ || self.is_documentation()
+ || self.is_unique_local()
+ || self.is_unicast_link_local())
+ }
+
+ /// Returns [`true`] if this is a unique local address (`fc00::/7`).
+ ///
+ /// This property is defined in [IETF RFC 4193].
+ ///
+ /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false);
+ /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unique_local(&self) -> bool {
+ (self.segments()[0] & 0xfe00) == 0xfc00
+ }
+
+ /// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291].
+ /// Any address that is not a [multicast address] (`ff00::/8`) is unicast.
+ ///
+ /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
+ /// [multicast address]: Ipv6Addr::is_multicast
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// // The unspecified and loopback addresses are unicast.
+ /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_unicast(), true);
+ /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast(), true);
+ ///
+ /// // Any address that is not a multicast address (`ff00::/8`) is unicast.
+ /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true);
+ /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unicast(&self) -> bool {
+ !self.is_multicast()
+ }
+
+ /// Returns `true` if the address is a unicast address with link-local scope,
+ /// as defined in [RFC 4291].
+ ///
+ /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4].
+ /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6],
+ /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format:
+ ///
+ /// ```text
+ /// | 10 bits | 54 bits | 64 bits |
+ /// +----------+-------------------------+----------------------------+
+ /// |1111111010| 0 | interface ID |
+ /// +----------+-------------------------+----------------------------+
+ /// ```
+ /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`,
+ /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated,
+ /// and those addresses will have link-local scope.
+ ///
+ /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope",
+ /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it.
+ ///
+ /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
+ /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
+ /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
+ /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
+ /// [loopback address]: Ipv6Addr::LOCALHOST
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// // The loopback address (`::1`) does not actually have link-local scope.
+ /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false);
+ ///
+ /// // Only addresses in `fe80::/10` have link-local scope.
+ /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false);
+ /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
+ ///
+ /// // Addresses outside the stricter `fe80::/64` also have link-local scope.
+ /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true);
+ /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unicast_link_local(&self) -> bool {
+ (self.segments()[0] & 0xffc0) == 0xfe80
+ }
+
+ /// Returns [`true`] if this is an address reserved for documentation
+ /// (`2001:db8::/32`).
+ ///
+ /// This property is defined in [IETF RFC 3849].
+ ///
+ /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false);
+ /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_documentation(&self) -> bool {
+ (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
+ }
+
+ /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`).
+ ///
+ /// This property is defined in [IETF RFC 5180], where it is mistakenly specified as covering the range `2001:0200::/48`.
+ /// This is corrected in [IETF RFC Errata 1752] to `2001:0002::/48`.
+ ///
+ /// [IETF RFC 5180]: https://tools.ietf.org/html/rfc5180
+ /// [IETF RFC Errata 1752]: https://www.rfc-editor.org/errata_search.php?eid=1752
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc613, 0x0).is_benchmarking(), false);
+ /// assert_eq!(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0).is_benchmarking(), true);
+ /// ```
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_benchmarking(&self) -> bool {
+ (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0)
+ }
+
+ /// Returns [`true`] if the address is a globally routable unicast address.
+ ///
+ /// The following return false:
+ ///
+ /// - the loopback address
+ /// - the link-local addresses
+ /// - unique local addresses
+ /// - the unspecified address
+ /// - the address range reserved for documentation
+ ///
+ /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7]
+ ///
+ /// ```no_rust
+ /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer
+ /// be supported in new implementations (i.e., new implementations must treat this prefix as
+ /// Global Unicast).
+ /// ```
+ ///
+ /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn is_unicast_global(&self) -> bool {
+ self.is_unicast()
+ && !self.is_loopback()
+ && !self.is_unicast_link_local()
+ && !self.is_unique_local()
+ && !self.is_unspecified()
+ && !self.is_documentation()
+ && !self.is_benchmarking()
+ }
+
+ /// Returns the address's multicast scope if the address is multicast.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ ///
+ /// use std::net::{Ipv6Addr, Ipv6MulticastScope};
+ ///
+ /// assert_eq!(
+ /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(),
+ /// Some(Ipv6MulticastScope::Global)
+ /// );
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use]
+ #[inline]
+ pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
+ if self.is_multicast() {
+ match self.segments()[0] & 0x000f {
+ 1 => Some(Ipv6MulticastScope::InterfaceLocal),
+ 2 => Some(Ipv6MulticastScope::LinkLocal),
+ 3 => Some(Ipv6MulticastScope::RealmLocal),
+ 4 => Some(Ipv6MulticastScope::AdminLocal),
+ 5 => Some(Ipv6MulticastScope::SiteLocal),
+ 8 => Some(Ipv6MulticastScope::OrganizationLocal),
+ 14 => Some(Ipv6MulticastScope::Global),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+
+ /// Returns [`true`] if this is a multicast address (`ff00::/8`).
+ ///
+ /// This property is defined by [IETF RFC 4291].
+ ///
+ /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(since = "1.7.0", feature = "ip_17")]
+ #[must_use]
+ #[inline]
+ pub const fn is_multicast(&self) -> bool {
+ (self.segments()[0] & 0xff00) == 0xff00
+ }
+
+ /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address,
+ /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
+ ///
+ /// `::ffff:a.b.c.d` becomes `a.b.c.d`.
+ /// All addresses *not* starting with `::ffff` will return `None`.
+ ///
+ /// [`IPv4` address]: Ipv4Addr
+ /// [IPv4-mapped]: Ipv6Addr
+ /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(),
+ /// Some(Ipv4Addr::new(192, 10, 2, 255)));
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")]
+ #[must_use = "this returns the result of the operation, \
+ without modifying the original"]
+ #[inline]
+ pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
+ match self.octets() {
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => {
+ Some(Ipv4Addr::new(a, b, c, d))
+ }
+ _ => None,
+ }
+ }
+
+ /// Converts this address to an [`IPv4` address] if it is either
+ /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1],
+ /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2],
+ /// otherwise returns [`None`].
+ ///
+ /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`. Use
+ /// [`Ipv6Addr::to_ipv4_mapped`] to avoid this.
+ ///
+ /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`.
+ /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`.
+ ///
+ /// [`IPv4` address]: Ipv4Addr
+ /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
+ /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
+ /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1
+ /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(),
+ /// Some(Ipv4Addr::new(192, 10, 2, 255)));
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(),
+ /// Some(Ipv4Addr::new(0, 0, 0, 1)));
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use = "this returns the result of the operation, \
+ without modifying the original"]
+ #[inline]
+ pub const fn to_ipv4(&self) -> Option<Ipv4Addr> {
+ if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() {
+ let [a, b] = ab.to_be_bytes();
+ let [c, d] = cd.to_be_bytes();
+ Some(Ipv4Addr::new(a, b, c, d))
+ } else {
+ None
+ }
+ }
+
+ /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it
+ /// returns self wrapped in an `IpAddr::V6`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true);
+ /// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ #[must_use = "this returns the result of the operation, \
+ without modifying the original"]
+ #[inline]
+ pub const fn to_canonical(&self) -> IpAddr {
+ if let Some(mapped) = self.to_ipv4_mapped() {
+ return IpAddr::V4(mapped);
+ }
+ IpAddr::V6(*self)
+ }
+
+ /// Returns the sixteen eight-bit integers the IPv6 address consists of.
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(),
+ /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+ /// ```
+ #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")]
+ #[stable(feature = "ipv6_to_octets", since = "1.12.0")]
+ #[must_use]
+ #[inline]
+ pub const fn octets(&self) -> [u8; 16] {
+ self.octets
+ }
+}
+
+/// Write an Ipv6Addr, conforming to the canonical style described by
+/// [RFC 5952](https://tools.ietf.org/html/rfc5952).
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for Ipv6Addr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // If there are no alignment requirements, write the IP address directly to `f`.
+ // Otherwise, write it to a local buffer and then use `f.pad`.
+ if f.precision().is_none() && f.width().is_none() {
+ let segments = self.segments();
+
+ // Special case for :: and ::1; otherwise they get written with the
+ // IPv4 formatter
+ if self.is_unspecified() {
+ f.write_str("::")
+ } else if self.is_loopback() {
+ f.write_str("::1")
+ } else if let Some(ipv4) = self.to_ipv4() {
+ match segments[5] {
+ // IPv4 Compatible address
+ 0 => write!(f, "::{}", ipv4),
+ // IPv4 Mapped address
+ 0xffff => write!(f, "::ffff:{}", ipv4),
+ _ => unreachable!(),
+ }
+ } else {
+ #[derive(Copy, Clone, Default)]
+ struct Span {
+ start: usize,
+ len: usize,
+ }
+
+ // Find the inner 0 span
+ let zeroes = {
+ let mut longest = Span::default();
+ let mut current = Span::default();
+
+ for (i, &segment) in segments.iter().enumerate() {
+ if segment == 0 {
+ if current.len == 0 {
+ current.start = i;
+ }
+
+ current.len += 1;
+
+ if current.len > longest.len {
+ longest = current;
+ }
+ } else {
+ current = Span::default();
+ }
+ }
+
+ longest
+ };
+
+ /// Write a colon-separated part of the address
+ #[inline]
+ fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result {
+ if let Some((first, tail)) = chunk.split_first() {
+ write!(f, "{:x}", first)?;
+ for segment in tail {
+ f.write_char(':')?;
+ write!(f, "{:x}", segment)?;
+ }
+ }
+ Ok(())
+ }
+
+ if zeroes.len > 1 {
+ fmt_subslice(f, &segments[..zeroes.start])?;
+ f.write_str("::")?;
+ fmt_subslice(f, &segments[zeroes.start + zeroes.len..])
+ } else {
+ fmt_subslice(f, &segments)
+ }
+ }
+ } else {
+ const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
+
+ let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new();
+ // Buffer is long enough for the longest possible IPv6 address, so this should never fail.
+ write!(buf, "{}", self).unwrap();
+
+ f.pad(buf.as_str())
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for Ipv6Addr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, fmt)
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialEq<IpAddr> for Ipv6Addr {
+ #[inline]
+ fn eq(&self, other: &IpAddr) -> bool {
+ match other {
+ IpAddr::V4(_) => false,
+ IpAddr::V6(v6) => self == v6,
+ }
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialEq<Ipv6Addr> for IpAddr {
+ #[inline]
+ fn eq(&self, other: &Ipv6Addr) -> bool {
+ match self {
+ IpAddr::V4(_) => false,
+ IpAddr::V6(v6) => v6 == other,
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl PartialOrd for Ipv6Addr {
+ #[inline]
+ fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialOrd<Ipv6Addr> for IpAddr {
+ #[inline]
+ fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> {
+ match self {
+ IpAddr::V4(_) => Some(Ordering::Less),
+ IpAddr::V6(v6) => v6.partial_cmp(other),
+ }
+ }
+}
+
+#[stable(feature = "ip_cmp", since = "1.16.0")]
+impl PartialOrd<IpAddr> for Ipv6Addr {
+ #[inline]
+ fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
+ match other {
+ IpAddr::V4(_) => Some(Ordering::Greater),
+ IpAddr::V6(v6) => self.partial_cmp(v6),
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Ord for Ipv6Addr {
+ #[inline]
+ fn cmp(&self, other: &Ipv6Addr) -> Ordering {
+ self.segments().cmp(&other.segments())
+ }
+}
+
+impl IntoInner<c::in6_addr> for Ipv6Addr {
+ fn into_inner(self) -> c::in6_addr {
+ c::in6_addr { s6_addr: self.octets }
+ }
+}
+impl FromInner<c::in6_addr> for Ipv6Addr {
+ #[inline]
+ fn from_inner(addr: c::in6_addr) -> Ipv6Addr {
+ Ipv6Addr { octets: addr.s6_addr }
+ }
+}
+
+#[stable(feature = "i128", since = "1.26.0")]
+impl From<Ipv6Addr> for u128 {
+ /// Convert an `Ipv6Addr` into a host byte order `u128`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::new(
+ /// 0x1020, 0x3040, 0x5060, 0x7080,
+ /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
+ /// );
+ /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr));
+ /// ```
+ #[inline]
+ fn from(ip: Ipv6Addr) -> u128 {
+ u128::from_be_bytes(ip.octets)
+ }
+}
+#[stable(feature = "i128", since = "1.26.0")]
+impl From<u128> for Ipv6Addr {
+ /// Convert a host byte order `u128` into an `Ipv6Addr`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128);
+ /// assert_eq!(
+ /// Ipv6Addr::new(
+ /// 0x1020, 0x3040, 0x5060, 0x7080,
+ /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
+ /// ),
+ /// addr);
+ /// ```
+ #[inline]
+ fn from(ip: u128) -> Ipv6Addr {
+ Ipv6Addr::from(ip.to_be_bytes())
+ }
+}
+
+#[stable(feature = "ipv6_from_octets", since = "1.9.0")]
+impl From<[u8; 16]> for Ipv6Addr {
+ /// Creates an `Ipv6Addr` from a sixteen element byte array.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::from([
+ /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8,
+ /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8,
+ /// ]);
+ /// assert_eq!(
+ /// Ipv6Addr::new(
+ /// 0x1918, 0x1716,
+ /// 0x1514, 0x1312,
+ /// 0x1110, 0x0f0e,
+ /// 0x0d0c, 0x0b0a
+ /// ),
+ /// addr
+ /// );
+ /// ```
+ #[inline]
+ fn from(octets: [u8; 16]) -> Ipv6Addr {
+ Ipv6Addr { octets }
+ }
+}
+
+#[stable(feature = "ipv6_from_segments", since = "1.16.0")]
+impl From<[u16; 8]> for Ipv6Addr {
+ /// Creates an `Ipv6Addr` from an eight element 16-bit array.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let addr = Ipv6Addr::from([
+ /// 525u16, 524u16, 523u16, 522u16,
+ /// 521u16, 520u16, 519u16, 518u16,
+ /// ]);
+ /// assert_eq!(
+ /// Ipv6Addr::new(
+ /// 0x20d, 0x20c,
+ /// 0x20b, 0x20a,
+ /// 0x209, 0x208,
+ /// 0x207, 0x206
+ /// ),
+ /// addr
+ /// );
+ /// ```
+ #[inline]
+ fn from(segments: [u16; 8]) -> Ipv6Addr {
+ let [a, b, c, d, e, f, g, h] = segments;
+ Ipv6Addr::new(a, b, c, d, e, f, g, h)
+ }
+}
+
+#[stable(feature = "ip_from_slice", since = "1.17.0")]
+impl From<[u8; 16]> for IpAddr {
+ /// Creates an `IpAddr::V6` from a sixteen element byte array.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv6Addr};
+ ///
+ /// let addr = IpAddr::from([
+ /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8,
+ /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8,
+ /// ]);
+ /// assert_eq!(
+ /// IpAddr::V6(Ipv6Addr::new(
+ /// 0x1918, 0x1716,
+ /// 0x1514, 0x1312,
+ /// 0x1110, 0x0f0e,
+ /// 0x0d0c, 0x0b0a
+ /// )),
+ /// addr
+ /// );
+ /// ```
+ #[inline]
+ fn from(octets: [u8; 16]) -> IpAddr {
+ IpAddr::V6(Ipv6Addr::from(octets))
+ }
+}
+
+#[stable(feature = "ip_from_slice", since = "1.17.0")]
+impl From<[u16; 8]> for IpAddr {
+ /// Creates an `IpAddr::V6` from an eight element 16-bit array.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv6Addr};
+ ///
+ /// let addr = IpAddr::from([
+ /// 525u16, 524u16, 523u16, 522u16,
+ /// 521u16, 520u16, 519u16, 518u16,
+ /// ]);
+ /// assert_eq!(
+ /// IpAddr::V6(Ipv6Addr::new(
+ /// 0x20d, 0x20c,
+ /// 0x20b, 0x20a,
+ /// 0x209, 0x208,
+ /// 0x207, 0x206
+ /// )),
+ /// addr
+ /// );
+ /// ```
+ #[inline]
+ fn from(segments: [u16; 8]) -> IpAddr {
+ IpAddr::V6(Ipv6Addr::from(segments))
+ }
+}
diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs
new file mode 100644
index 000000000..7c3430b2b
--- /dev/null
+++ b/library/std/src/net/ip_addr/tests.rs
@@ -0,0 +1,1039 @@
+use crate::net::test::{sa4, sa6, tsa};
+use crate::net::*;
+use crate::str::FromStr;
+
+#[test]
+fn test_from_str_ipv4() {
+ assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse());
+ assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse());
+ assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse());
+
+ // out of range
+ let none: Option<Ipv4Addr> = "256.0.0.1".parse().ok();
+ assert_eq!(None, none);
+ // too short
+ let none: Option<Ipv4Addr> = "255.0.0".parse().ok();
+ assert_eq!(None, none);
+ // too long
+ let none: Option<Ipv4Addr> = "255.0.0.1.2".parse().ok();
+ assert_eq!(None, none);
+ // no number between dots
+ let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
+ assert_eq!(None, none);
+ // octal
+ let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok();
+ assert_eq!(None, none);
+ // octal zero
+ let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok();
+ assert_eq!(None, none);
+ let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn test_from_str_ipv6() {
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse());
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse());
+
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse());
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse());
+
+ assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse());
+
+ // too long group
+ let none: Option<Ipv6Addr> = "::00000".parse().ok();
+ assert_eq!(None, none);
+ // too short
+ let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7".parse().ok();
+ assert_eq!(None, none);
+ // too long
+ let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:8:9".parse().ok();
+ assert_eq!(None, none);
+ // triple colon
+ let none: Option<Ipv6Addr> = "1:2:::6:7:8".parse().ok();
+ assert_eq!(None, none);
+ // two double colons
+ let none: Option<Ipv6Addr> = "1:2::6::8".parse().ok();
+ assert_eq!(None, none);
+ // `::` indicating zero groups of zeros
+ let none: Option<Ipv6Addr> = "1:2:3:4::5:6:7:8".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn test_from_str_ipv4_in_ipv6() {
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse());
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse());
+ assert_eq!(
+ Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
+ "64:ff9b::192.0.2.33".parse()
+ );
+ assert_eq!(
+ Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
+ "2001:db8:122:c000:2:2100:192.0.2.33".parse()
+ );
+
+ // colon after v4
+ let none: Option<Ipv4Addr> = "::127.0.0.1:".parse().ok();
+ assert_eq!(None, none);
+ // not enough groups
+ let none: Option<Ipv6Addr> = "1:2:3:4:5:127.0.0.1".parse().ok();
+ assert_eq!(None, none);
+ // too many groups
+ let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:127.0.0.1".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn test_from_str_socket_addr() {
+ assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
+ assert_eq!(Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
+ assert_eq!(
+ Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)),
+ "[2a02:6b8:0:1::1]:53".parse()
+ );
+ assert_eq!(
+ Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)),
+ "[2a02:6b8:0:1::1]:53".parse()
+ );
+ assert_eq!(Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), "[::127.0.0.1]:22".parse());
+ assert_eq!(
+ Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)),
+ "[::127.0.0.1]:22".parse()
+ );
+
+ // without port
+ let none: Option<SocketAddr> = "127.0.0.1".parse().ok();
+ assert_eq!(None, none);
+ // without port
+ let none: Option<SocketAddr> = "127.0.0.1:".parse().ok();
+ assert_eq!(None, none);
+ // wrong brackets around v4
+ let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok();
+ assert_eq!(None, none);
+ // port out of range
+ let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn ipv4_addr_to_string() {
+ assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1");
+ // Short address
+ assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1");
+ // Long address
+ assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127");
+
+ // Test padding
+ assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 ");
+ assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1");
+}
+
+#[test]
+fn ipv6_addr_to_string() {
+ // ipv4-mapped address
+ let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
+ assert_eq!(a1.to_string(), "::ffff:192.0.2.128");
+
+ // ipv4-compatible address
+ let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280);
+ assert_eq!(a1.to_string(), "::192.0.2.128");
+
+ // v6 address with no zero segments
+ assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f");
+
+ // longest possible IPv6 length
+ assert_eq!(
+ Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888).to_string(),
+ "1111:2222:3333:4444:5555:6666:7777:8888"
+ );
+ // padding
+ assert_eq!(&format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 ");
+ assert_eq!(&format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8");
+
+ // reduce a single run of zeros
+ assert_eq!(
+ "ae::ffff:102:304",
+ Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string()
+ );
+
+ // don't reduce just a single zero segment
+ assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string());
+
+ // 'any' address
+ assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string());
+
+ // loopback address
+ assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string());
+
+ // ends in zeros
+ assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string());
+
+ // two runs of zeros, second one is longer
+ assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string());
+
+ // two runs of zeros, equal length
+ assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string());
+
+ // don't prefix `0x` to each segment in `dbg!`.
+ assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8)));
+}
+
+#[test]
+fn ipv4_to_ipv6() {
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678),
+ Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped()
+ );
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678),
+ Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible()
+ );
+}
+
+#[test]
+fn ipv6_to_ipv4_mapped() {
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(),
+ Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
+ );
+ assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None);
+}
+
+#[test]
+fn ipv6_to_ipv4() {
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(),
+ Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
+ );
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(),
+ Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
+ );
+ assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None);
+}
+
+#[test]
+fn ip_properties() {
+ macro_rules! ip {
+ ($s:expr) => {
+ IpAddr::from_str($s).unwrap()
+ };
+ }
+
+ macro_rules! check {
+ ($s:expr) => {
+ check!($s, 0);
+ };
+
+ ($s:expr, $mask:expr) => {{
+ let unspec: u8 = 1 << 0;
+ let loopback: u8 = 1 << 1;
+ let global: u8 = 1 << 2;
+ let multicast: u8 = 1 << 3;
+ let doc: u8 = 1 << 4;
+ let benchmarking: u8 = 1 << 5;
+
+ if ($mask & unspec) == unspec {
+ assert!(ip!($s).is_unspecified());
+ } else {
+ assert!(!ip!($s).is_unspecified());
+ }
+
+ if ($mask & loopback) == loopback {
+ assert!(ip!($s).is_loopback());
+ } else {
+ assert!(!ip!($s).is_loopback());
+ }
+
+ if ($mask & global) == global {
+ assert!(ip!($s).is_global());
+ } else {
+ assert!(!ip!($s).is_global());
+ }
+
+ if ($mask & multicast) == multicast {
+ assert!(ip!($s).is_multicast());
+ } else {
+ assert!(!ip!($s).is_multicast());
+ }
+
+ if ($mask & doc) == doc {
+ assert!(ip!($s).is_documentation());
+ } else {
+ assert!(!ip!($s).is_documentation());
+ }
+
+ if ($mask & benchmarking) == benchmarking {
+ assert!(ip!($s).is_benchmarking());
+ } else {
+ assert!(!ip!($s).is_benchmarking());
+ }
+ }};
+ }
+
+ let unspec: u8 = 1 << 0;
+ let loopback: u8 = 1 << 1;
+ let global: u8 = 1 << 2;
+ let multicast: u8 = 1 << 3;
+ let doc: u8 = 1 << 4;
+ let benchmarking: u8 = 1 << 5;
+
+ check!("0.0.0.0", unspec);
+ check!("0.0.0.1");
+ check!("0.1.0.0");
+ check!("10.9.8.7");
+ check!("127.1.2.3", loopback);
+ check!("172.31.254.253");
+ check!("169.254.253.242");
+ check!("192.0.2.183", doc);
+ check!("192.1.2.183", global);
+ check!("192.168.254.253");
+ check!("198.51.100.0", doc);
+ check!("203.0.113.0", doc);
+ check!("203.2.113.0", global);
+ check!("224.0.0.0", global | multicast);
+ check!("239.255.255.255", global | multicast);
+ check!("255.255.255.255");
+ // make sure benchmarking addresses are not global
+ check!("198.18.0.0", benchmarking);
+ check!("198.18.54.2", benchmarking);
+ check!("198.19.255.255", benchmarking);
+ // make sure addresses reserved for protocol assignment are not global
+ check!("192.0.0.0");
+ check!("192.0.0.255");
+ check!("192.0.0.100");
+ // make sure reserved addresses are not global
+ check!("240.0.0.0");
+ check!("251.54.1.76");
+ check!("254.255.255.255");
+ // make sure shared addresses are not global
+ check!("100.64.0.0");
+ check!("100.127.255.255");
+ check!("100.100.100.0");
+
+ check!("::", unspec);
+ check!("::1", loopback);
+ check!("::0.0.0.2", global);
+ check!("1::", global);
+ check!("fc00::");
+ check!("fdff:ffff::");
+ check!("fe80:ffff::");
+ check!("febf:ffff::");
+ check!("fec0::", global);
+ check!("ff01::", global | multicast);
+ check!("ff02::", global | multicast);
+ check!("ff03::", global | multicast);
+ check!("ff04::", global | multicast);
+ check!("ff05::", global | multicast);
+ check!("ff08::", global | multicast);
+ check!("ff0e::", global | multicast);
+ check!("2001:db8:85a3::8a2e:370:7334", doc);
+ check!("2001:2::ac32:23ff:21", benchmarking);
+ check!("102:304:506:708:90a:b0c:d0e:f10", global);
+}
+
+#[test]
+fn ipv4_properties() {
+ macro_rules! ip {
+ ($s:expr) => {
+ Ipv4Addr::from_str($s).unwrap()
+ };
+ }
+
+ macro_rules! check {
+ ($s:expr) => {
+ check!($s, 0);
+ };
+
+ ($s:expr, $mask:expr) => {{
+ let unspec: u16 = 1 << 0;
+ let loopback: u16 = 1 << 1;
+ let private: u16 = 1 << 2;
+ let link_local: u16 = 1 << 3;
+ let global: u16 = 1 << 4;
+ let multicast: u16 = 1 << 5;
+ let broadcast: u16 = 1 << 6;
+ let documentation: u16 = 1 << 7;
+ let benchmarking: u16 = 1 << 8;
+ let reserved: u16 = 1 << 10;
+ let shared: u16 = 1 << 11;
+
+ if ($mask & unspec) == unspec {
+ assert!(ip!($s).is_unspecified());
+ } else {
+ assert!(!ip!($s).is_unspecified());
+ }
+
+ if ($mask & loopback) == loopback {
+ assert!(ip!($s).is_loopback());
+ } else {
+ assert!(!ip!($s).is_loopback());
+ }
+
+ if ($mask & private) == private {
+ assert!(ip!($s).is_private());
+ } else {
+ assert!(!ip!($s).is_private());
+ }
+
+ if ($mask & link_local) == link_local {
+ assert!(ip!($s).is_link_local());
+ } else {
+ assert!(!ip!($s).is_link_local());
+ }
+
+ if ($mask & global) == global {
+ assert!(ip!($s).is_global());
+ } else {
+ assert!(!ip!($s).is_global());
+ }
+
+ if ($mask & multicast) == multicast {
+ assert!(ip!($s).is_multicast());
+ } else {
+ assert!(!ip!($s).is_multicast());
+ }
+
+ if ($mask & broadcast) == broadcast {
+ assert!(ip!($s).is_broadcast());
+ } else {
+ assert!(!ip!($s).is_broadcast());
+ }
+
+ if ($mask & documentation) == documentation {
+ assert!(ip!($s).is_documentation());
+ } else {
+ assert!(!ip!($s).is_documentation());
+ }
+
+ if ($mask & benchmarking) == benchmarking {
+ assert!(ip!($s).is_benchmarking());
+ } else {
+ assert!(!ip!($s).is_benchmarking());
+ }
+
+ if ($mask & reserved) == reserved {
+ assert!(ip!($s).is_reserved());
+ } else {
+ assert!(!ip!($s).is_reserved());
+ }
+
+ if ($mask & shared) == shared {
+ assert!(ip!($s).is_shared());
+ } else {
+ assert!(!ip!($s).is_shared());
+ }
+ }};
+ }
+
+ let unspec: u16 = 1 << 0;
+ let loopback: u16 = 1 << 1;
+ let private: u16 = 1 << 2;
+ let link_local: u16 = 1 << 3;
+ let global: u16 = 1 << 4;
+ let multicast: u16 = 1 << 5;
+ let broadcast: u16 = 1 << 6;
+ let documentation: u16 = 1 << 7;
+ let benchmarking: u16 = 1 << 8;
+ let reserved: u16 = 1 << 10;
+ let shared: u16 = 1 << 11;
+
+ check!("0.0.0.0", unspec);
+ check!("0.0.0.1");
+ check!("0.1.0.0");
+ check!("10.9.8.7", private);
+ check!("127.1.2.3", loopback);
+ check!("172.31.254.253", private);
+ check!("169.254.253.242", link_local);
+ check!("192.0.2.183", documentation);
+ check!("192.1.2.183", global);
+ check!("192.168.254.253", private);
+ check!("198.51.100.0", documentation);
+ check!("203.0.113.0", documentation);
+ check!("203.2.113.0", global);
+ check!("224.0.0.0", global | multicast);
+ check!("239.255.255.255", global | multicast);
+ check!("255.255.255.255", broadcast);
+ check!("198.18.0.0", benchmarking);
+ check!("198.18.54.2", benchmarking);
+ check!("198.19.255.255", benchmarking);
+ check!("192.0.0.0");
+ check!("192.0.0.255");
+ check!("192.0.0.100");
+ check!("240.0.0.0", reserved);
+ check!("251.54.1.76", reserved);
+ check!("254.255.255.255", reserved);
+ check!("100.64.0.0", shared);
+ check!("100.127.255.255", shared);
+ check!("100.100.100.0", shared);
+}
+
+#[test]
+fn ipv6_properties() {
+ macro_rules! ip {
+ ($s:expr) => {
+ Ipv6Addr::from_str($s).unwrap()
+ };
+ }
+
+ macro_rules! check {
+ ($s:expr, &[$($octet:expr),*], $mask:expr) => {
+ assert_eq!($s, ip!($s).to_string());
+ let octets = &[$($octet),*];
+ assert_eq!(&ip!($s).octets(), octets);
+ assert_eq!(Ipv6Addr::from(*octets), ip!($s));
+
+ let unspecified: u32 = 1 << 0;
+ let loopback: u32 = 1 << 1;
+ let unique_local: u32 = 1 << 2;
+ let global: u32 = 1 << 3;
+ let unicast_link_local: u32 = 1 << 4;
+ let unicast_global: u32 = 1 << 7;
+ let documentation: u32 = 1 << 8;
+ let benchmarking: u32 = 1 << 16;
+ let multicast_interface_local: u32 = 1 << 9;
+ let multicast_link_local: u32 = 1 << 10;
+ let multicast_realm_local: u32 = 1 << 11;
+ let multicast_admin_local: u32 = 1 << 12;
+ let multicast_site_local: u32 = 1 << 13;
+ let multicast_organization_local: u32 = 1 << 14;
+ let multicast_global: u32 = 1 << 15;
+ let multicast: u32 = multicast_interface_local
+ | multicast_admin_local
+ | multicast_global
+ | multicast_link_local
+ | multicast_realm_local
+ | multicast_site_local
+ | multicast_organization_local;
+
+ if ($mask & unspecified) == unspecified {
+ assert!(ip!($s).is_unspecified());
+ } else {
+ assert!(!ip!($s).is_unspecified());
+ }
+ if ($mask & loopback) == loopback {
+ assert!(ip!($s).is_loopback());
+ } else {
+ assert!(!ip!($s).is_loopback());
+ }
+ if ($mask & unique_local) == unique_local {
+ assert!(ip!($s).is_unique_local());
+ } else {
+ assert!(!ip!($s).is_unique_local());
+ }
+ if ($mask & global) == global {
+ assert!(ip!($s).is_global());
+ } else {
+ assert!(!ip!($s).is_global());
+ }
+ if ($mask & unicast_link_local) == unicast_link_local {
+ assert!(ip!($s).is_unicast_link_local());
+ } else {
+ assert!(!ip!($s).is_unicast_link_local());
+ }
+ if ($mask & unicast_global) == unicast_global {
+ assert!(ip!($s).is_unicast_global());
+ } else {
+ assert!(!ip!($s).is_unicast_global());
+ }
+ if ($mask & documentation) == documentation {
+ assert!(ip!($s).is_documentation());
+ } else {
+ assert!(!ip!($s).is_documentation());
+ }
+ if ($mask & benchmarking) == benchmarking {
+ assert!(ip!($s).is_benchmarking());
+ } else {
+ assert!(!ip!($s).is_benchmarking());
+ }
+ if ($mask & multicast) != 0 {
+ assert!(ip!($s).multicast_scope().is_some());
+ assert!(ip!($s).is_multicast());
+ } else {
+ assert!(ip!($s).multicast_scope().is_none());
+ assert!(!ip!($s).is_multicast());
+ }
+ if ($mask & multicast_interface_local) == multicast_interface_local {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::InterfaceLocal);
+ }
+ if ($mask & multicast_link_local) == multicast_link_local {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::LinkLocal);
+ }
+ if ($mask & multicast_realm_local) == multicast_realm_local {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::RealmLocal);
+ }
+ if ($mask & multicast_admin_local) == multicast_admin_local {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::AdminLocal);
+ }
+ if ($mask & multicast_site_local) == multicast_site_local {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::SiteLocal);
+ }
+ if ($mask & multicast_organization_local) == multicast_organization_local {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::OrganizationLocal);
+ }
+ if ($mask & multicast_global) == multicast_global {
+ assert_eq!(ip!($s).multicast_scope().unwrap(),
+ Ipv6MulticastScope::Global);
+ }
+ }
+ }
+
+ let unspecified: u32 = 1 << 0;
+ let loopback: u32 = 1 << 1;
+ let unique_local: u32 = 1 << 2;
+ let global: u32 = 1 << 3;
+ let unicast_link_local: u32 = 1 << 4;
+ let unicast_global: u32 = 1 << 7;
+ let documentation: u32 = 1 << 8;
+ let benchmarking: u32 = 1 << 16;
+ let multicast_interface_local: u32 = 1 << 9;
+ let multicast_link_local: u32 = 1 << 10;
+ let multicast_realm_local: u32 = 1 << 11;
+ let multicast_admin_local: u32 = 1 << 12;
+ let multicast_site_local: u32 = 1 << 13;
+ let multicast_organization_local: u32 = 1 << 14;
+ let multicast_global: u32 = 1 << 15;
+
+ check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified);
+
+ check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback);
+
+ check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global);
+
+ check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global);
+
+ check!(
+ "::ffff:127.0.0.1",
+ &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1],
+ unicast_global
+ );
+
+ check!(
+ "64:ff9b:1::",
+ &[0, 0x64, 0xff, 0x9b, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ unicast_global
+ );
+
+ check!("100::", &[0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global);
+
+ check!("2001::", &[0x20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global);
+
+ check!(
+ "2001:1::1",
+ &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ global | unicast_global
+ );
+
+ check!(
+ "2001:1::2",
+ &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
+ global | unicast_global
+ );
+
+ check!(
+ "2001:3::",
+ &[0x20, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ global | unicast_global
+ );
+
+ check!(
+ "2001:4:112::",
+ &[0x20, 1, 0, 4, 1, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ global | unicast_global
+ );
+
+ check!(
+ "2001:20::",
+ &[0x20, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ global | unicast_global
+ );
+
+ check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global);
+
+ check!(
+ "2001:200::",
+ &[0x20, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ global | unicast_global
+ );
+
+ check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local);
+
+ check!(
+ "fdff:ffff::",
+ &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ unique_local
+ );
+
+ check!(
+ "fe80:ffff::",
+ &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ unicast_link_local
+ );
+
+ check!("fe80::", &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local);
+
+ check!(
+ "febf:ffff::",
+ &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ unicast_link_local
+ );
+
+ check!("febf::", &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local);
+
+ check!(
+ "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ &[
+ 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff
+ ],
+ unicast_link_local
+ );
+
+ check!(
+ "fe80::ffff:ffff:ffff:ffff",
+ &[
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff
+ ],
+ unicast_link_local
+ );
+
+ check!(
+ "fe80:0:0:1::",
+ &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
+ unicast_link_local
+ );
+
+ check!(
+ "fec0::",
+ &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ unicast_global | global
+ );
+
+ check!(
+ "ff01::",
+ &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_interface_local | global
+ );
+
+ check!(
+ "ff02::",
+ &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_link_local | global
+ );
+
+ check!(
+ "ff03::",
+ &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_realm_local | global
+ );
+
+ check!(
+ "ff04::",
+ &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_admin_local | global
+ );
+
+ check!(
+ "ff05::",
+ &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_site_local | global
+ );
+
+ check!(
+ "ff08::",
+ &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_organization_local | global
+ );
+
+ check!(
+ "ff0e::",
+ &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ multicast_global | global
+ );
+
+ check!(
+ "2001:db8:85a3::8a2e:370:7334",
+ &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34],
+ documentation
+ );
+
+ check!(
+ "2001:2::ac32:23ff:21",
+ &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21],
+ benchmarking
+ );
+
+ check!(
+ "102:304:506:708:90a:b0c:d0e:f10",
+ &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ global | unicast_global
+ );
+}
+
+#[test]
+fn to_socket_addr_socketaddr() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345);
+ assert_eq!(Ok(vec![a]), tsa(a));
+}
+
+#[test]
+fn test_ipv4_to_int() {
+ let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
+ assert_eq!(u32::from(a), 0x11223344);
+}
+
+#[test]
+fn test_int_to_ipv4() {
+ let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
+ assert_eq!(Ipv4Addr::from(0x11223344), a);
+}
+
+#[test]
+fn test_ipv6_to_int() {
+ let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
+ assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128);
+}
+
+#[test]
+fn test_int_to_ipv6() {
+ let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
+ assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a);
+}
+
+#[test]
+fn ipv4_from_constructors() {
+ assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1));
+ assert!(Ipv4Addr::LOCALHOST.is_loopback());
+ assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0));
+ assert!(Ipv4Addr::UNSPECIFIED.is_unspecified());
+ assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255));
+ assert!(Ipv4Addr::BROADCAST.is_broadcast());
+}
+
+#[test]
+fn ipv6_from_constructors() {
+ assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+ assert!(Ipv6Addr::LOCALHOST.is_loopback());
+ assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
+ assert!(Ipv6Addr::UNSPECIFIED.is_unspecified());
+}
+
+#[test]
+fn ipv4_from_octets() {
+ assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1))
+}
+
+#[test]
+fn ipv6_from_segments() {
+ let from_u16s =
+ Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
+ let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff);
+ assert_eq!(new, from_u16s);
+}
+
+#[test]
+fn ipv6_from_octets() {
+ let from_u16s =
+ Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
+ let from_u8s = Ipv6Addr::from([
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
+ 0xff,
+ ]);
+ assert_eq!(from_u16s, from_u8s);
+}
+
+#[test]
+fn cmp() {
+ let v41 = Ipv4Addr::new(100, 64, 3, 3);
+ let v42 = Ipv4Addr::new(192, 0, 2, 2);
+ let v61 = "2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap();
+ let v62 = "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap();
+ assert!(v41 < v42);
+ assert!(v61 < v62);
+
+ assert_eq!(v41, IpAddr::V4(v41));
+ assert_eq!(v61, IpAddr::V6(v61));
+ assert!(v41 != IpAddr::V4(v42));
+ assert!(v61 != IpAddr::V6(v62));
+
+ assert!(v41 < IpAddr::V4(v42));
+ assert!(v61 < IpAddr::V6(v62));
+ assert!(IpAddr::V4(v41) < v42);
+ assert!(IpAddr::V6(v61) < v62);
+
+ assert!(v41 < IpAddr::V6(v61));
+ assert!(IpAddr::V4(v41) < v61);
+}
+
+#[test]
+fn is_v4() {
+ let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3));
+ assert!(ip.is_ipv4());
+ assert!(!ip.is_ipv6());
+}
+
+#[test]
+fn is_v6() {
+ let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678));
+ assert!(!ip.is_ipv4());
+ assert!(ip.is_ipv6());
+}
+
+#[test]
+fn ipv4_const() {
+ // test that the methods of `Ipv4Addr` are usable in a const context
+
+ const IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
+ assert_eq!(IP_ADDRESS, Ipv4Addr::LOCALHOST);
+
+ const OCTETS: [u8; 4] = IP_ADDRESS.octets();
+ assert_eq!(OCTETS, [127, 0, 0, 1]);
+
+ const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
+ assert!(!IS_UNSPECIFIED);
+
+ const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
+ assert!(IS_LOOPBACK);
+
+ const IS_PRIVATE: bool = IP_ADDRESS.is_private();
+ assert!(!IS_PRIVATE);
+
+ const IS_LINK_LOCAL: bool = IP_ADDRESS.is_link_local();
+ assert!(!IS_LINK_LOCAL);
+
+ const IS_GLOBAL: bool = IP_ADDRESS.is_global();
+ assert!(!IS_GLOBAL);
+
+ const IS_SHARED: bool = IP_ADDRESS.is_shared();
+ assert!(!IS_SHARED);
+
+ const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
+ assert!(!IS_BENCHMARKING);
+
+ const IS_RESERVED: bool = IP_ADDRESS.is_reserved();
+ assert!(!IS_RESERVED);
+
+ const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
+ assert!(!IS_MULTICAST);
+
+ const IS_BROADCAST: bool = IP_ADDRESS.is_broadcast();
+ assert!(!IS_BROADCAST);
+
+ const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation();
+ assert!(!IS_DOCUMENTATION);
+
+ const IP_V6_COMPATIBLE: Ipv6Addr = IP_ADDRESS.to_ipv6_compatible();
+ assert_eq!(
+ IP_V6_COMPATIBLE,
+ Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1])
+ );
+
+ const IP_V6_MAPPED: Ipv6Addr = IP_ADDRESS.to_ipv6_mapped();
+ assert_eq!(
+ IP_V6_MAPPED,
+ Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1])
+ );
+}
+
+#[test]
+fn ipv6_const() {
+ // test that the methods of `Ipv6Addr` are usable in a const context
+
+ const IP_ADDRESS: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+ assert_eq!(IP_ADDRESS, Ipv6Addr::LOCALHOST);
+
+ const SEGMENTS: [u16; 8] = IP_ADDRESS.segments();
+ assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]);
+
+ const OCTETS: [u8; 16] = IP_ADDRESS.octets();
+ assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+
+ const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
+ assert!(!IS_UNSPECIFIED);
+
+ const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
+ assert!(IS_LOOPBACK);
+
+ const IS_GLOBAL: bool = IP_ADDRESS.is_global();
+ assert!(!IS_GLOBAL);
+
+ const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local();
+ assert!(!IS_UNIQUE_LOCAL);
+
+ const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local();
+ assert!(!IS_UNICAST_LINK_LOCAL);
+
+ const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation();
+ assert!(!IS_DOCUMENTATION);
+
+ const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
+ assert!(!IS_BENCHMARKING);
+
+ const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global();
+ assert!(!IS_UNICAST_GLOBAL);
+
+ const MULTICAST_SCOPE: Option<Ipv6MulticastScope> = IP_ADDRESS.multicast_scope();
+ assert_eq!(MULTICAST_SCOPE, None);
+
+ const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
+ assert!(!IS_MULTICAST);
+
+ const IP_V4: Option<Ipv4Addr> = IP_ADDRESS.to_ipv4();
+ assert_eq!(IP_V4.unwrap(), Ipv4Addr::new(0, 0, 0, 1));
+}
+
+#[test]
+fn ip_const() {
+ // test that the methods of `IpAddr` are usable in a const context
+
+ const IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
+
+ const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
+ assert!(!IS_UNSPECIFIED);
+
+ const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
+ assert!(IS_LOOPBACK);
+
+ const IS_GLOBAL: bool = IP_ADDRESS.is_global();
+ assert!(!IS_GLOBAL);
+
+ const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
+ assert!(!IS_MULTICAST);
+
+ const IS_IP_V4: bool = IP_ADDRESS.is_ipv4();
+ assert!(IS_IP_V4);
+
+ const IS_IP_V6: bool = IP_ADDRESS.is_ipv6();
+ assert!(!IS_IP_V6);
+}
+
+#[test]
+fn structural_match() {
+ // test that all IP types can be structurally matched upon
+
+ const IPV4: Ipv4Addr = Ipv4Addr::LOCALHOST;
+ match IPV4 {
+ Ipv4Addr::LOCALHOST => {}
+ _ => unreachable!(),
+ }
+
+ const IPV6: Ipv6Addr = Ipv6Addr::LOCALHOST;
+ match IPV6 {
+ Ipv6Addr::LOCALHOST => {}
+ _ => unreachable!(),
+ }
+
+ const IP: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
+ match IP {
+ IpAddr::V4(Ipv4Addr::LOCALHOST) => {}
+ _ => unreachable!(),
+ }
+}
diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs
index e7a40bdaf..01e3db9de 100644
--- a/library/std/src/net/mod.rs
+++ b/library/std/src/net/mod.rs
@@ -24,11 +24,11 @@
use crate::io::{self, ErrorKind};
#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
+pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::parser::AddrParseError;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
#[unstable(feature = "tcplistener_into_incoming", issue = "88339")]
pub use self::tcp::IntoIncoming;
#[stable(feature = "rust1", since = "1.0.0")]
@@ -36,12 +36,13 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::udp::UdpSocket;
-mod addr;
-mod ip;
+mod display_buffer;
+mod ip_addr;
mod parser;
+mod socket_addr;
mod tcp;
#[cfg(test)]
-mod test;
+pub(crate) mod test;
mod udp;
/// Possible values which can be passed to the [`TcpStream::shutdown`] method.
diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs
index 069b66099..a38031c48 100644
--- a/library/std/src/net/parser.rs
+++ b/library/std/src/net/parser.rs
@@ -39,8 +39,8 @@ struct Parser<'a> {
}
impl<'a> Parser<'a> {
- fn new(input: &'a str) -> Parser<'a> {
- Parser { state: input.as_bytes() }
+ fn new(input: &'a [u8]) -> Parser<'a> {
+ Parser { state: input }
}
/// Run a parser, and restore the pre-parse state if it fails.
@@ -273,32 +273,106 @@ impl<'a> Parser<'a> {
}
}
+impl IpAddr {
+ /// Parse an IP address from a slice of bytes.
+ ///
+ /// ```
+ /// #![feature(addr_parse_ascii)]
+ ///
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
+ /// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+ ///
+ /// assert_eq!(IpAddr::parse_ascii(b"127.0.0.1"), Ok(localhost_v4));
+ /// assert_eq!(IpAddr::parse_ascii(b"::1"), Ok(localhost_v6));
+ /// ```
+ #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+ pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+ Parser::new(b).parse_with(|p| p.read_ip_addr(), AddrKind::Ip)
+ }
+}
+
#[stable(feature = "ip_addr", since = "1.7.0")]
impl FromStr for IpAddr {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
- Parser::new(s).parse_with(|p| p.read_ip_addr(), AddrKind::Ip)
+ Self::parse_ascii(s.as_bytes())
}
}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl FromStr for Ipv4Addr {
- type Err = AddrParseError;
- fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
+impl Ipv4Addr {
+ /// Parse an IPv4 address from a slice of bytes.
+ ///
+ /// ```
+ /// #![feature(addr_parse_ascii)]
+ ///
+ /// use std::net::Ipv4Addr;
+ ///
+ /// let localhost = Ipv4Addr::new(127, 0, 0, 1);
+ ///
+ /// assert_eq!(Ipv4Addr::parse_ascii(b"127.0.0.1"), Ok(localhost));
+ /// ```
+ #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+ pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
// don't try to parse if too long
- if s.len() > 15 {
+ if b.len() > 15 {
Err(AddrParseError(AddrKind::Ipv4))
} else {
- Parser::new(s).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
+ Parser::new(b).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4)
}
}
}
#[stable(feature = "rust1", since = "1.0.0")]
+impl FromStr for Ipv4Addr {
+ type Err = AddrParseError;
+ fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
+ Self::parse_ascii(s.as_bytes())
+ }
+}
+
+impl Ipv6Addr {
+ /// Parse an IPv6 address from a slice of bytes.
+ ///
+ /// ```
+ /// #![feature(addr_parse_ascii)]
+ ///
+ /// use std::net::Ipv6Addr;
+ ///
+ /// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+ ///
+ /// assert_eq!(Ipv6Addr::parse_ascii(b"::1"), Ok(localhost));
+ /// ```
+ #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+ pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+ Parser::new(b).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for Ipv6Addr {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> {
- Parser::new(s).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6)
+ Self::parse_ascii(s.as_bytes())
+ }
+}
+
+impl SocketAddrV4 {
+ /// Parse an IPv4 socket address from a slice of bytes.
+ ///
+ /// ```
+ /// #![feature(addr_parse_ascii)]
+ ///
+ /// use std::net::{Ipv4Addr, SocketAddrV4};
+ ///
+ /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+ ///
+ /// assert_eq!(SocketAddrV4::parse_ascii(b"127.0.0.1:8080"), Ok(socket));
+ /// ```
+ #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+ pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+ Parser::new(b).parse_with(|p| p.read_socket_addr_v4(), AddrKind::SocketV4)
}
}
@@ -306,7 +380,25 @@ impl FromStr for Ipv6Addr {
impl FromStr for SocketAddrV4 {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> {
- Parser::new(s).parse_with(|p| p.read_socket_addr_v4(), AddrKind::SocketV4)
+ Self::parse_ascii(s.as_bytes())
+ }
+}
+
+impl SocketAddrV6 {
+ /// Parse an IPv6 socket address from a slice of bytes.
+ ///
+ /// ```
+ /// #![feature(addr_parse_ascii)]
+ ///
+ /// use std::net::{Ipv6Addr, SocketAddrV6};
+ ///
+ /// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+ ///
+ /// assert_eq!(SocketAddrV6::parse_ascii(b"[2001:db8::1]:8080"), Ok(socket));
+ /// ```
+ #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+ pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+ Parser::new(b).parse_with(|p| p.read_socket_addr_v6(), AddrKind::SocketV6)
}
}
@@ -314,7 +406,27 @@ impl FromStr for SocketAddrV4 {
impl FromStr for SocketAddrV6 {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> {
- Parser::new(s).parse_with(|p| p.read_socket_addr_v6(), AddrKind::SocketV6)
+ Self::parse_ascii(s.as_bytes())
+ }
+}
+
+impl SocketAddr {
+ /// Parse a socket address from a slice of bytes.
+ ///
+ /// ```
+ /// #![feature(addr_parse_ascii)]
+ ///
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
+ ///
+ /// let socket_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// let socket_v6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
+ ///
+ /// assert_eq!(SocketAddr::parse_ascii(b"127.0.0.1:8080"), Ok(socket_v4));
+ /// assert_eq!(SocketAddr::parse_ascii(b"[::1]:8080"), Ok(socket_v6));
+ /// ```
+ #[unstable(feature = "addr_parse_ascii", issue = "101035")]
+ pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> {
+ Parser::new(b).parse_with(|p| p.read_socket_addr(), AddrKind::Socket)
}
}
@@ -322,7 +434,7 @@ impl FromStr for SocketAddrV6 {
impl FromStr for SocketAddr {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> {
- Parser::new(s).parse_with(|p| p.read_socket_addr(), AddrKind::Socket)
+ Self::parse_ascii(s.as_bytes())
}
}
diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs
new file mode 100644
index 000000000..33b0dfa03
--- /dev/null
+++ b/library/std/src/net/socket_addr.rs
@@ -0,0 +1,974 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+use crate::cmp::Ordering;
+use crate::fmt::{self, Write};
+use crate::hash;
+use crate::io;
+use crate::iter;
+use crate::mem;
+use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+use crate::option;
+use crate::slice;
+use crate::sys::net::netc as c;
+use crate::sys_common::net::LookupHost;
+use crate::sys_common::{FromInner, IntoInner};
+use crate::vec;
+
+use super::display_buffer::DisplayBuffer;
+
+/// An internet socket address, either IPv4 or IPv6.
+///
+/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well
+/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and
+/// [`SocketAddrV6`]'s respective documentation for more details.
+///
+/// The size of a `SocketAddr` instance may vary depending on the target operating
+/// system.
+///
+/// [IP address]: IpAddr
+///
+/// # Examples
+///
+/// ```
+/// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+///
+/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+///
+/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket));
+/// assert_eq!(socket.port(), 8080);
+/// assert_eq!(socket.is_ipv4(), true);
+/// ```
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub enum SocketAddr {
+ /// An IPv4 socket address.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4),
+ /// An IPv6 socket address.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6),
+}
+
+/// An IPv4 socket address.
+///
+/// IPv4 socket addresses consist of an [`IPv4` address] and a 16-bit port number, as
+/// stated in [IETF RFC 793].
+///
+/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses.
+///
+/// The size of a `SocketAddrV4` struct may vary depending on the target operating
+/// system. Do not assume that this type has the same memory layout as the underlying
+/// system representation.
+///
+/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793
+/// [`IPv4` address]: Ipv4Addr
+///
+/// # Examples
+///
+/// ```
+/// use std::net::{Ipv4Addr, SocketAddrV4};
+///
+/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+///
+/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket));
+/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1));
+/// assert_eq!(socket.port(), 8080);
+/// ```
+#[derive(Copy, Clone, Eq, PartialEq)]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct SocketAddrV4 {
+ ip: Ipv4Addr,
+ port: u16,
+}
+
+/// An IPv6 socket address.
+///
+/// IPv6 socket addresses consist of an [`IPv6` address], a 16-bit port number, as well
+/// as fields containing the traffic class, the flow label, and a scope identifier
+/// (see [IETF RFC 2553, Section 3.3] for more details).
+///
+/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses.
+///
+/// The size of a `SocketAddrV6` struct may vary depending on the target operating
+/// system. Do not assume that this type has the same memory layout as the underlying
+/// system representation.
+///
+/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
+/// [`IPv6` address]: Ipv6Addr
+///
+/// # Examples
+///
+/// ```
+/// use std::net::{Ipv6Addr, SocketAddrV6};
+///
+/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+///
+/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket));
+/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
+/// assert_eq!(socket.port(), 8080);
+/// ```
+#[derive(Copy, Clone, Eq, PartialEq)]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct SocketAddrV6 {
+ ip: Ipv6Addr,
+ port: u16,
+ flowinfo: u32,
+ scope_id: u32,
+}
+
+impl SocketAddr {
+ /// Creates a new socket address from an [IP address] and a port number.
+ ///
+ /// [IP address]: IpAddr
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+ ///
+ /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
+ /// assert_eq!(socket.port(), 8080);
+ /// ```
+ #[stable(feature = "ip_addr", since = "1.7.0")]
+ #[must_use]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn new(ip: IpAddr, port: u16) -> SocketAddr {
+ match ip {
+ IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)),
+ IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)),
+ }
+ }
+
+ /// Returns the IP address associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+ ///
+ /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
+ /// ```
+ #[must_use]
+ #[stable(feature = "ip_addr", since = "1.7.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn ip(&self) -> IpAddr {
+ match *self {
+ SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()),
+ SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()),
+ }
+ }
+
+ /// Changes the IP address associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+ ///
+ /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1)));
+ /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1)));
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_ip(&mut self, new_ip: IpAddr) {
+ // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away.
+ match (self, new_ip) {
+ (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip),
+ (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip),
+ (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()),
+ }
+ }
+
+ /// Returns the port number associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+ ///
+ /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// assert_eq!(socket.port(), 8080);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn port(&self) -> u16 {
+ match *self {
+ SocketAddr::V4(ref a) => a.port(),
+ SocketAddr::V6(ref a) => a.port(),
+ }
+ }
+
+ /// Changes the port number associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+ ///
+ /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// socket.set_port(1025);
+ /// assert_eq!(socket.port(), 1025);
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_port(&mut self, new_port: u16) {
+ match *self {
+ SocketAddr::V4(ref mut a) => a.set_port(new_port),
+ SocketAddr::V6(ref mut a) => a.set_port(new_port),
+ }
+ }
+
+ /// Returns [`true`] if the [IP address] in this `SocketAddr` is an
+ /// [`IPv4` address], and [`false`] otherwise.
+ ///
+ /// [IP address]: IpAddr
+ /// [`IPv4` address]: IpAddr::V4
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+ ///
+ /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
+ /// assert_eq!(socket.is_ipv4(), true);
+ /// assert_eq!(socket.is_ipv6(), false);
+ /// ```
+ #[must_use]
+ #[stable(feature = "sockaddr_checker", since = "1.16.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn is_ipv4(&self) -> bool {
+ matches!(*self, SocketAddr::V4(_))
+ }
+
+ /// Returns [`true`] if the [IP address] in this `SocketAddr` is an
+ /// [`IPv6` address], and [`false`] otherwise.
+ ///
+ /// [IP address]: IpAddr
+ /// [`IPv6` address]: IpAddr::V6
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{IpAddr, Ipv6Addr, SocketAddr};
+ ///
+ /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080);
+ /// assert_eq!(socket.is_ipv4(), false);
+ /// assert_eq!(socket.is_ipv6(), true);
+ /// ```
+ #[must_use]
+ #[stable(feature = "sockaddr_checker", since = "1.16.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn is_ipv6(&self) -> bool {
+ matches!(*self, SocketAddr::V6(_))
+ }
+}
+
+impl SocketAddrV4 {
+ /// Creates a new socket address from an [`IPv4` address] and a port number.
+ ///
+ /// [`IPv4` address]: Ipv4Addr
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV4, Ipv4Addr};
+ ///
+ /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 {
+ SocketAddrV4 { ip, port }
+ }
+
+ /// Returns the IP address associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV4, Ipv4Addr};
+ ///
+ /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+ /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1));
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn ip(&self) -> &Ipv4Addr {
+ &self.ip
+ }
+
+ /// Changes the IP address associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV4, Ipv4Addr};
+ ///
+ /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+ /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1));
+ /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1));
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_ip(&mut self, new_ip: Ipv4Addr) {
+ self.ip = new_ip;
+ }
+
+ /// Returns the port number associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV4, Ipv4Addr};
+ ///
+ /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+ /// assert_eq!(socket.port(), 8080);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn port(&self) -> u16 {
+ self.port
+ }
+
+ /// Changes the port number associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV4, Ipv4Addr};
+ ///
+ /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
+ /// socket.set_port(4242);
+ /// assert_eq!(socket.port(), 4242);
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_port(&mut self, new_port: u16) {
+ self.port = new_port;
+ }
+}
+
+impl SocketAddrV6 {
+ /// Creates a new socket address from an [`IPv6` address], a 16-bit port number,
+ /// and the `flowinfo` and `scope_id` fields.
+ ///
+ /// For more information on the meaning and layout of the `flowinfo` and `scope_id`
+ /// parameters, see [IETF RFC 2553, Section 3.3].
+ ///
+ /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
+ /// [`IPv6` address]: Ipv6Addr
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 {
+ SocketAddrV6 { ip, port, flowinfo, scope_id }
+ }
+
+ /// Returns the IP address associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+ /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn ip(&self) -> &Ipv6Addr {
+ &self.ip
+ }
+
+ /// Changes the IP address associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+ /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0));
+ /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0));
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_ip(&mut self, new_ip: Ipv6Addr) {
+ self.ip = new_ip;
+ }
+
+ /// Returns the port number associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+ /// assert_eq!(socket.port(), 8080);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn port(&self) -> u16 {
+ self.port
+ }
+
+ /// Changes the port number associated with this socket address.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
+ /// socket.set_port(4242);
+ /// assert_eq!(socket.port(), 4242);
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_port(&mut self, new_port: u16) {
+ self.port = new_port;
+ }
+
+ /// Returns the flow information associated with this address.
+ ///
+ /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`,
+ /// as specified in [IETF RFC 2553, Section 3.3].
+ /// It combines information about the flow label and the traffic class as specified
+ /// in [IETF RFC 2460], respectively [Section 6] and [Section 7].
+ ///
+ /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
+ /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460
+ /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6
+ /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0);
+ /// assert_eq!(socket.flowinfo(), 10);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn flowinfo(&self) -> u32 {
+ self.flowinfo
+ }
+
+ /// Changes the flow information associated with this socket address.
+ ///
+ /// See [`SocketAddrV6::flowinfo`]'s documentation for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0);
+ /// socket.set_flowinfo(56);
+ /// assert_eq!(socket.flowinfo(), 56);
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_flowinfo(&mut self, new_flowinfo: u32) {
+ self.flowinfo = new_flowinfo;
+ }
+
+ /// Returns the scope ID associated with this address.
+ ///
+ /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`,
+ /// as specified in [IETF RFC 2553, Section 3.3].
+ ///
+ /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78);
+ /// assert_eq!(socket.scope_id(), 78);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ pub const fn scope_id(&self) -> u32 {
+ self.scope_id
+ }
+
+ /// Changes the scope ID associated with this socket address.
+ ///
+ /// See [`SocketAddrV6::scope_id`]'s documentation for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::net::{SocketAddrV6, Ipv6Addr};
+ ///
+ /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78);
+ /// socket.set_scope_id(42);
+ /// assert_eq!(socket.scope_id(), 42);
+ /// ```
+ #[stable(feature = "sockaddr_setters", since = "1.9.0")]
+ pub fn set_scope_id(&mut self, new_scope_id: u32) {
+ self.scope_id = new_scope_id;
+ }
+}
+
+impl FromInner<c::sockaddr_in> for SocketAddrV4 {
+ fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 {
+ SocketAddrV4 { ip: Ipv4Addr::from_inner(addr.sin_addr), port: u16::from_be(addr.sin_port) }
+ }
+}
+
+impl FromInner<c::sockaddr_in6> for SocketAddrV6 {
+ fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 {
+ SocketAddrV6 {
+ ip: Ipv6Addr::from_inner(addr.sin6_addr),
+ port: u16::from_be(addr.sin6_port),
+ flowinfo: addr.sin6_flowinfo,
+ scope_id: addr.sin6_scope_id,
+ }
+ }
+}
+
+impl IntoInner<c::sockaddr_in> for SocketAddrV4 {
+ fn into_inner(self) -> c::sockaddr_in {
+ c::sockaddr_in {
+ sin_family: c::AF_INET as c::sa_family_t,
+ sin_port: self.port.to_be(),
+ sin_addr: self.ip.into_inner(),
+ ..unsafe { mem::zeroed() }
+ }
+ }
+}
+
+impl IntoInner<c::sockaddr_in6> for SocketAddrV6 {
+ fn into_inner(self) -> c::sockaddr_in6 {
+ c::sockaddr_in6 {
+ sin6_family: c::AF_INET6 as c::sa_family_t,
+ sin6_port: self.port.to_be(),
+ sin6_addr: self.ip.into_inner(),
+ sin6_flowinfo: self.flowinfo,
+ sin6_scope_id: self.scope_id,
+ ..unsafe { mem::zeroed() }
+ }
+ }
+}
+
+#[stable(feature = "ip_from_ip", since = "1.16.0")]
+impl From<SocketAddrV4> for SocketAddr {
+ /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`].
+ fn from(sock4: SocketAddrV4) -> SocketAddr {
+ SocketAddr::V4(sock4)
+ }
+}
+
+#[stable(feature = "ip_from_ip", since = "1.16.0")]
+impl From<SocketAddrV6> for SocketAddr {
+ /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`].
+ fn from(sock6: SocketAddrV6) -> SocketAddr {
+ SocketAddr::V6(sock6)
+ }
+}
+
+#[stable(feature = "addr_from_into_ip", since = "1.17.0")]
+impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr {
+ /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`].
+ ///
+ /// This conversion creates a [`SocketAddr::V4`] for an [`IpAddr::V4`]
+ /// and creates a [`SocketAddr::V6`] for an [`IpAddr::V6`].
+ ///
+ /// `u16` is treated as port of the newly created [`SocketAddr`].
+ fn from(pieces: (I, u16)) -> SocketAddr {
+ SocketAddr::new(pieces.0.into(), pieces.1)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for SocketAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ SocketAddr::V4(ref a) => a.fmt(f),
+ SocketAddr::V6(ref a) => a.fmt(f),
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for SocketAddr {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, fmt)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for SocketAddrV4 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // If there are no alignment requirements, write the socket address directly to `f`.
+ // Otherwise, write it to a local buffer and then use `f.pad`.
+ if f.precision().is_none() && f.width().is_none() {
+ write!(f, "{}:{}", self.ip(), self.port())
+ } else {
+ const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536";
+
+ let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new();
+ // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail.
+ write!(buf, "{}:{}", self.ip(), self.port()).unwrap();
+
+ f.pad(buf.as_str())
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for SocketAddrV4 {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, fmt)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for SocketAddrV6 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // If there are no alignment requirements, write the socket address directly to `f`.
+ // Otherwise, write it to a local buffer and then use `f.pad`.
+ if f.precision().is_none() && f.width().is_none() {
+ match self.scope_id() {
+ 0 => write!(f, "[{}]:{}", self.ip(), self.port()),
+ scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
+ }
+ } else {
+ const LONGEST_IPV6_SOCKET_ADDR: &str =
+ "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536";
+
+ let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new();
+ match self.scope_id() {
+ 0 => write!(buf, "[{}]:{}", self.ip(), self.port()),
+ scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
+ }
+ // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail.
+ .unwrap();
+
+ f.pad(buf.as_str())
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for SocketAddrV6 {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, fmt)
+ }
+}
+
+#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
+impl PartialOrd for SocketAddrV4 {
+ fn partial_cmp(&self, other: &SocketAddrV4) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
+impl PartialOrd for SocketAddrV6 {
+ fn partial_cmp(&self, other: &SocketAddrV6) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
+impl Ord for SocketAddrV4 {
+ fn cmp(&self, other: &SocketAddrV4) -> Ordering {
+ self.ip().cmp(other.ip()).then(self.port().cmp(&other.port()))
+ }
+}
+
+#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
+impl Ord for SocketAddrV6 {
+ fn cmp(&self, other: &SocketAddrV6) -> Ordering {
+ self.ip().cmp(other.ip()).then(self.port().cmp(&other.port()))
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl hash::Hash for SocketAddrV4 {
+ fn hash<H: hash::Hasher>(&self, s: &mut H) {
+ (self.port, self.ip).hash(s)
+ }
+}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl hash::Hash for SocketAddrV6 {
+ fn hash<H: hash::Hasher>(&self, s: &mut H) {
+ (self.port, &self.ip, self.flowinfo, self.scope_id).hash(s)
+ }
+}
+
+/// A trait for objects which can be converted or resolved to one or more
+/// [`SocketAddr`] values.
+///
+/// This trait is used for generic address resolution when constructing network
+/// objects. By default it is implemented for the following types:
+///
+/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function.
+///
+/// * [`SocketAddrV4`], [`SocketAddrV6`], <code>([IpAddr], [u16])</code>,
+/// <code>([Ipv4Addr], [u16])</code>, <code>([Ipv6Addr], [u16])</code>:
+/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially.
+///
+/// * <code>(&[str], [u16])</code>: <code>&[str]</code> should be either a string representation
+/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host
+/// name. [`u16`] is the port number.
+///
+/// * <code>&[str]</code>: the string should be either a string representation of a
+/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like
+/// `<host_name>:<port>` pair where `<port>` is a [`u16`] value.
+///
+/// This trait allows constructing network objects like [`TcpStream`] or
+/// [`UdpSocket`] easily with values of various types for the bind/connection
+/// address. It is needed because sometimes one type is more appropriate than
+/// the other: for simple uses a string like `"localhost:12345"` is much nicer
+/// than manual construction of the corresponding [`SocketAddr`], but sometimes
+/// [`SocketAddr`] value is *the* main source of the address, and converting it to
+/// some other type (e.g., a string) just for it to be converted back to
+/// [`SocketAddr`] in constructor methods is pointless.
+///
+/// Addresses returned by the operating system that are not IP addresses are
+/// silently ignored.
+///
+/// [`FromStr`]: crate::str::FromStr "std::str::FromStr"
+/// [`TcpStream`]: crate::net::TcpStream "net::TcpStream"
+/// [`to_socket_addrs`]: ToSocketAddrs::to_socket_addrs
+/// [`UdpSocket`]: crate::net::UdpSocket "net::UdpSocket"
+///
+/// # Examples
+///
+/// Creating a [`SocketAddr`] iterator that yields one item:
+///
+/// ```
+/// use std::net::{ToSocketAddrs, SocketAddr};
+///
+/// let addr = SocketAddr::from(([127, 0, 0, 1], 443));
+/// let mut addrs_iter = addr.to_socket_addrs().unwrap();
+///
+/// assert_eq!(Some(addr), addrs_iter.next());
+/// assert!(addrs_iter.next().is_none());
+/// ```
+///
+/// Creating a [`SocketAddr`] iterator from a hostname:
+///
+/// ```no_run
+/// use std::net::{SocketAddr, ToSocketAddrs};
+///
+/// // assuming 'localhost' resolves to 127.0.0.1
+/// let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap();
+/// assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443))));
+/// assert!(addrs_iter.next().is_none());
+///
+/// // assuming 'foo' does not resolve
+/// assert!("foo:443".to_socket_addrs().is_err());
+/// ```
+///
+/// Creating a [`SocketAddr`] iterator that yields multiple items:
+///
+/// ```
+/// use std::net::{SocketAddr, ToSocketAddrs};
+///
+/// let addr1 = SocketAddr::from(([0, 0, 0, 0], 80));
+/// let addr2 = SocketAddr::from(([127, 0, 0, 1], 443));
+/// let addrs = vec![addr1, addr2];
+///
+/// let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap();
+///
+/// assert_eq!(Some(addr1), addrs_iter.next());
+/// assert_eq!(Some(addr2), addrs_iter.next());
+/// assert!(addrs_iter.next().is_none());
+/// ```
+///
+/// Attempting to create a [`SocketAddr`] iterator from an improperly formatted
+/// socket address `&str` (missing the port):
+///
+/// ```
+/// use std::io;
+/// use std::net::ToSocketAddrs;
+///
+/// let err = "127.0.0.1".to_socket_addrs().unwrap_err();
+/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
+/// ```
+///
+/// [`TcpStream::connect`] is an example of an function that utilizes
+/// `ToSocketAddrs` as a trait bound on its parameter in order to accept
+/// different types:
+///
+/// ```no_run
+/// use std::net::{TcpStream, Ipv4Addr};
+///
+/// let stream = TcpStream::connect(("127.0.0.1", 443));
+/// // or
+/// let stream = TcpStream::connect("127.0.0.1:443");
+/// // or
+/// let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443));
+/// ```
+///
+/// [`TcpStream::connect`]: crate::net::TcpStream::connect
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait ToSocketAddrs {
+ /// Returned iterator over socket addresses which this type may correspond
+ /// to.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ type Iter: Iterator<Item = SocketAddr>;
+
+ /// Converts this object to an iterator of resolved [`SocketAddr`]s.
+ ///
+ /// The returned iterator might not actually yield any values depending on the
+ /// outcome of any resolution performed.
+ ///
+ /// Note that this function may block the current thread while resolution is
+ /// performed.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn to_socket_addrs(&self) -> io::Result<Self::Iter>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for SocketAddr {
+ type Iter = option::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
+ Ok(Some(*self).into_iter())
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for SocketAddrV4 {
+ type Iter = option::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
+ SocketAddr::V4(*self).to_socket_addrs()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for SocketAddrV6 {
+ type Iter = option::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
+ SocketAddr::V6(*self).to_socket_addrs()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for (IpAddr, u16) {
+ type Iter = option::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
+ let (ip, port) = *self;
+ match ip {
+ IpAddr::V4(ref a) => (*a, port).to_socket_addrs(),
+ IpAddr::V6(ref a) => (*a, port).to_socket_addrs(),
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for (Ipv4Addr, u16) {
+ type Iter = option::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
+ let (ip, port) = *self;
+ SocketAddrV4::new(ip, port).to_socket_addrs()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for (Ipv6Addr, u16) {
+ type Iter = option::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
+ let (ip, port) = *self;
+ SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs()
+ }
+}
+
+fn resolve_socket_addr(lh: LookupHost) -> io::Result<vec::IntoIter<SocketAddr>> {
+ let p = lh.port();
+ let v: Vec<_> = lh
+ .map(|mut a| {
+ a.set_port(p);
+ a
+ })
+ .collect();
+ Ok(v.into_iter())
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for (&str, u16) {
+ type Iter = vec::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
+ let (host, port) = *self;
+
+ // try to parse the host as a regular IP address first
+ if let Ok(addr) = host.parse::<Ipv4Addr>() {
+ let addr = SocketAddrV4::new(addr, port);
+ return Ok(vec![SocketAddr::V4(addr)].into_iter());
+ }
+ if let Ok(addr) = host.parse::<Ipv6Addr>() {
+ let addr = SocketAddrV6::new(addr, port, 0, 0);
+ return Ok(vec![SocketAddr::V6(addr)].into_iter());
+ }
+
+ resolve_socket_addr((host, port).try_into()?)
+ }
+}
+
+#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")]
+impl ToSocketAddrs for (String, u16) {
+ type Iter = vec::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
+ (&*self.0, self.1).to_socket_addrs()
+ }
+}
+
+// accepts strings like 'localhost:12345'
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ToSocketAddrs for str {
+ type Iter = vec::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
+ // try to parse as a regular SocketAddr first
+ if let Ok(addr) = self.parse() {
+ return Ok(vec![addr].into_iter());
+ }
+
+ resolve_socket_addr(self.try_into()?)
+ }
+}
+
+#[stable(feature = "slice_to_socket_addrs", since = "1.8.0")]
+impl<'a> ToSocketAddrs for &'a [SocketAddr] {
+ type Iter = iter::Cloned<slice::Iter<'a, SocketAddr>>;
+
+ fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
+ Ok(self.iter().cloned())
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {
+ type Iter = T::Iter;
+ fn to_socket_addrs(&self) -> io::Result<T::Iter> {
+ (**self).to_socket_addrs()
+ }
+}
+
+#[stable(feature = "string_to_socket_addrs", since = "1.16.0")]
+impl ToSocketAddrs for String {
+ type Iter = vec::IntoIter<SocketAddr>;
+ fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
+ (&**self).to_socket_addrs()
+ }
+}
diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs
new file mode 100644
index 000000000..15211f819
--- /dev/null
+++ b/library/std/src/net/socket_addr/tests.rs
@@ -0,0 +1,306 @@
+use crate::net::test::{sa4, sa6, tsa};
+use crate::net::*;
+
+#[test]
+fn to_socket_addr_ipaddr_u16() {
+ let a = Ipv4Addr::new(77, 88, 21, 11);
+ let p = 12345;
+ let e = SocketAddr::V4(SocketAddrV4::new(a, p));
+ assert_eq!(Ok(vec![e]), tsa((a, p)));
+}
+
+#[test]
+fn to_socket_addr_str_u16() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
+ assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352)));
+
+ let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
+ assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53)));
+
+ let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924);
+ #[cfg(not(target_env = "sgx"))]
+ assert!(tsa(("localhost", 23924)).unwrap().contains(&a));
+ #[cfg(target_env = "sgx")]
+ let _ = a;
+}
+
+#[test]
+fn to_socket_addr_str() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
+ assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352"));
+
+ let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
+ assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53"));
+
+ let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924);
+ #[cfg(not(target_env = "sgx"))]
+ assert!(tsa("localhost:23924").unwrap().contains(&a));
+ #[cfg(target_env = "sgx")]
+ let _ = a;
+}
+
+#[test]
+fn to_socket_addr_string() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
+ assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352")));
+ assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352")));
+ assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352")));
+
+ let s = format!("{}:{}", "77.88.21.11", "24352");
+ assert_eq!(Ok(vec![a]), tsa(s));
+ // s has been moved into the tsa call
+}
+
+#[test]
+fn ipv4_socket_addr_to_string() {
+ // Shortest possible IPv4 length.
+ assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0");
+
+ // Longest possible IPv4 length.
+ assert_eq!(
+ SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(),
+ "255.255.255.255:65535"
+ );
+
+ // Test padding.
+ assert_eq!(
+ &format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
+ "1.1.1.1:53 "
+ );
+ assert_eq!(
+ &format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
+ " 1.1.1.1:53"
+ );
+}
+
+#[test]
+fn ipv6_socket_addr_to_string() {
+ // IPv4-mapped address.
+ assert_eq!(
+ SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0)
+ .to_string(),
+ "[::ffff:192.0.2.128]:8080"
+ );
+
+ // IPv4-compatible address.
+ assert_eq!(
+ SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(),
+ "[::192.0.2.128]:8080"
+ );
+
+ // IPv6 address with no zero segments.
+ assert_eq!(
+ SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(),
+ "[8:9:a:b:c:d:e:f]:80"
+ );
+
+ // Shortest possible IPv6 length.
+ assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0");
+
+ // Longest possible IPv6 length.
+ assert_eq!(
+ SocketAddrV6::new(
+ Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888),
+ u16::MAX,
+ u32::MAX,
+ u32::MAX,
+ )
+ .to_string(),
+ "[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535"
+ );
+
+ // Test padding.
+ assert_eq!(
+ &format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
+ "[1:2:3:4:5:6:7:8]:9 "
+ );
+ assert_eq!(
+ &format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
+ " [1:2:3:4:5:6:7:8]:9"
+ );
+}
+
+#[test]
+fn bind_udp_socket_bad() {
+ // rust-lang/rust#53957: This is a regression test for a parsing problem
+ // discovered as part of issue rust-lang/rust#23076, where we were
+ // incorrectly parsing invalid input and then that would result in a
+ // successful `UdpSocket` binding when we would expect failure.
+ //
+ // At one time, this test was written as a call to `tsa` with
+ // INPUT_23076. However, that structure yields an unreliable test,
+ // because it ends up passing junk input to the DNS server, and some DNS
+ // servers will respond with `Ok` to such input, with the ip address of
+ // the DNS server itself.
+ //
+ // This form of the test is more robust: even when the DNS server
+ // returns its own address, it is still an error to bind a UDP socket to
+ // a non-local address, and so we still get an error here in that case.
+
+ const INPUT_23076: &str = "1200::AB00:1234::2552:7777:1313:34300";
+
+ assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err())
+}
+
+#[test]
+fn set_ip() {
+ fn ip4(low: u8) -> Ipv4Addr {
+ Ipv4Addr::new(77, 88, 21, low)
+ }
+ fn ip6(low: u16) -> Ipv6Addr {
+ Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low)
+ }
+
+ let mut v4 = SocketAddrV4::new(ip4(11), 80);
+ assert_eq!(v4.ip(), &ip4(11));
+ v4.set_ip(ip4(12));
+ assert_eq!(v4.ip(), &ip4(12));
+
+ let mut addr = SocketAddr::V4(v4);
+ assert_eq!(addr.ip(), IpAddr::V4(ip4(12)));
+ addr.set_ip(IpAddr::V4(ip4(13)));
+ assert_eq!(addr.ip(), IpAddr::V4(ip4(13)));
+ addr.set_ip(IpAddr::V6(ip6(14)));
+ assert_eq!(addr.ip(), IpAddr::V6(ip6(14)));
+
+ let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0);
+ assert_eq!(v6.ip(), &ip6(1));
+ v6.set_ip(ip6(2));
+ assert_eq!(v6.ip(), &ip6(2));
+
+ let mut addr = SocketAddr::V6(v6);
+ assert_eq!(addr.ip(), IpAddr::V6(ip6(2)));
+ addr.set_ip(IpAddr::V6(ip6(3)));
+ assert_eq!(addr.ip(), IpAddr::V6(ip6(3)));
+ addr.set_ip(IpAddr::V4(ip4(4)));
+ assert_eq!(addr.ip(), IpAddr::V4(ip4(4)));
+}
+
+#[test]
+fn set_port() {
+ let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80);
+ assert_eq!(v4.port(), 80);
+ v4.set_port(443);
+ assert_eq!(v4.port(), 443);
+
+ let mut addr = SocketAddr::V4(v4);
+ assert_eq!(addr.port(), 443);
+ addr.set_port(8080);
+ assert_eq!(addr.port(), 8080);
+
+ let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0);
+ assert_eq!(v6.port(), 80);
+ v6.set_port(443);
+ assert_eq!(v6.port(), 443);
+
+ let mut addr = SocketAddr::V6(v6);
+ assert_eq!(addr.port(), 443);
+ addr.set_port(8080);
+ assert_eq!(addr.port(), 8080);
+}
+
+#[test]
+fn set_flowinfo() {
+ let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0);
+ assert_eq!(v6.flowinfo(), 10);
+ v6.set_flowinfo(20);
+ assert_eq!(v6.flowinfo(), 20);
+}
+
+#[test]
+fn set_scope_id() {
+ let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10);
+ assert_eq!(v6.scope_id(), 10);
+ v6.set_scope_id(20);
+ assert_eq!(v6.scope_id(), 20);
+}
+
+#[test]
+fn is_v4() {
+ let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80));
+ assert!(v4.is_ipv4());
+ assert!(!v4.is_ipv6());
+}
+
+#[test]
+fn is_v6() {
+ let v6 = SocketAddr::V6(SocketAddrV6::new(
+ Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1),
+ 80,
+ 10,
+ 0,
+ ));
+ assert!(!v6.is_ipv4());
+ assert!(v6.is_ipv6());
+}
+
+#[test]
+fn socket_v4_to_str() {
+ let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
+
+ assert_eq!(format!("{socket}"), "192.168.0.1:8080");
+ assert_eq!(format!("{socket:<20}"), "192.168.0.1:8080 ");
+ assert_eq!(format!("{socket:>20}"), " 192.168.0.1:8080");
+ assert_eq!(format!("{socket:^20}"), " 192.168.0.1:8080 ");
+ assert_eq!(format!("{socket:.10}"), "192.168.0.");
+}
+
+#[test]
+fn socket_v6_to_str() {
+ let mut socket = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0);
+
+ assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1]:53");
+ assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1]:53 ");
+ assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1]:53");
+ assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1]:53 ");
+ assert_eq!(format!("{socket:.15}"), "[2a02:6b8:0:1::");
+
+ socket.set_scope_id(5);
+
+ assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1%5]:53");
+ assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1%5]:53 ");
+ assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1%5]:53");
+ assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1%5]:53 ");
+ assert_eq!(format!("{socket:.18}"), "[2a02:6b8:0:1::1%5");
+}
+
+#[test]
+fn compare() {
+ let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();
+ let v4_2 = "224.210.103.5:12345".parse::<SocketAddrV4>().unwrap();
+ let v4_3 = "224.210.103.5:23456".parse::<SocketAddrV4>().unwrap();
+ let v6_1 = "[2001:db8:f00::1002]:23456".parse::<SocketAddrV6>().unwrap();
+ let v6_2 = "[2001:db8:f00::2001]:12345".parse::<SocketAddrV6>().unwrap();
+ let v6_3 = "[2001:db8:f00::2001]:23456".parse::<SocketAddrV6>().unwrap();
+
+ // equality
+ assert_eq!(v4_1, v4_1);
+ assert_eq!(v6_1, v6_1);
+ assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1));
+ assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1));
+ assert!(v4_1 != v4_2);
+ assert!(v6_1 != v6_2);
+
+ // compare different addresses
+ assert!(v4_1 < v4_2);
+ assert!(v6_1 < v6_2);
+ assert!(v4_2 > v4_1);
+ assert!(v6_2 > v6_1);
+
+ // compare the same address with different ports
+ assert!(v4_2 < v4_3);
+ assert!(v6_2 < v6_3);
+ assert!(v4_3 > v4_2);
+ assert!(v6_3 > v6_2);
+
+ // compare different addresses with the same port
+ assert!(v4_1 < v4_3);
+ assert!(v6_1 < v6_3);
+ assert!(v4_3 > v4_1);
+ assert!(v6_3 > v6_1);
+
+ // compare with an inferred right-hand side
+ assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap());
+ assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap());
+ assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap());
+}
diff --git a/library/std/src/os/android/mod.rs b/library/std/src/os/android/mod.rs
index dbb0127f3..5adcb82b6 100644
--- a/library/std/src/os/android/mod.rs
+++ b/library/std/src/os/android/mod.rs
@@ -3,4 +3,5 @@
#![stable(feature = "raw_ext", since = "1.1.0")]
pub mod fs;
+pub mod net;
pub mod raw;
diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs
new file mode 100644
index 000000000..ff96125c3
--- /dev/null
+++ b/library/std/src/os/android/net.rs
@@ -0,0 +1,4 @@
+//! Linux and Android-specific definitions for socket options.
+
+#![unstable(feature = "tcp_quickack", issue = "96256")]
+pub use crate::os::net::tcp::TcpStreamExt;
diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs
index a45694753..c6aa7c77d 100644
--- a/library/std/src/os/fd/mod.rs
+++ b/library/std/src/os/fd/mod.rs
@@ -1,16 +1,25 @@
//! Owned and borrowed Unix-like file descriptors.
+//!
+//! This module is supported on Unix platforms and WASI, which both use a
+//! similar file descriptor system for referencing OS resources.
#![stable(feature = "io_safety", since = "1.63.0")]
#![deny(unsafe_op_in_unsafe_fn)]
// `RawFd`, `AsRawFd`, etc.
-pub mod raw;
+mod raw;
// `OwnedFd`, `AsFd`, etc.
-pub mod owned;
+mod owned;
// Implementations for `AsRawFd` etc. for network types.
mod net;
#[cfg(test)]
mod tests;
+
+// Export the types and traits for the public API.
+#[unstable(feature = "os_fd", issue = "98699")]
+pub use owned::*;
+#[unstable(feature = "os_fd", issue = "98699")]
+pub use raw::*;
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index a463bc41d..c16518577 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -6,6 +6,7 @@
use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::fmt;
use crate::fs;
+use crate::io;
use crate::marker::PhantomData;
use crate::mem::forget;
#[cfg(not(any(target_arch = "wasm32", target_env = "sgx")))]
@@ -104,7 +105,8 @@ impl BorrowedFd<'_> {
#[cfg(target_os = "espidf")]
let cmd = libc::F_DUPFD;
- let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
+ // Avoid using file descriptors below 3 as they are used for stdio
+ let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 3) })?;
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
@@ -191,6 +193,23 @@ impl fmt::Debug for OwnedFd {
}
}
+macro_rules! impl_is_terminal {
+ ($($t:ty),*$(,)?) => {$(
+ #[unstable(feature = "sealed", issue = "none")]
+ impl crate::sealed::Sealed for $t {}
+
+ #[unstable(feature = "is_terminal", issue = "98070")]
+ impl crate::io::IsTerminal for $t {
+ #[inline]
+ fn is_terminal(&self) -> bool {
+ crate::sys::io::is_terminal(self)
+ }
+ }
+ )*}
+}
+
+impl_is_terminal!(BorrowedFd<'_>, OwnedFd);
+
/// A trait to borrow the file descriptor from an underlying object.
///
/// This is only available on unix platforms and must be imported in order to
@@ -205,10 +224,8 @@ pub trait AsFd {
/// ```rust,no_run
/// use std::fs::File;
/// # use std::io;
- /// # #[cfg(target_os = "wasi")]
- /// # use std::os::wasi::io::{AsFd, BorrowedFd};
- /// # #[cfg(unix)]
- /// # use std::os::unix::io::{AsFd, BorrowedFd};
+ /// # #[cfg(any(unix, target_os = "wasi"))]
+ /// # use std::os::fd::{AsFd, BorrowedFd};
///
/// let mut f = File::open("foo.txt")?;
/// # #[cfg(any(unix, target_os = "wasi"))]
@@ -386,3 +403,54 @@ impl<T: AsFd> AsFd for Box<T> {
(**self).as_fd()
}
}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl AsFd for io::Stdin {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(0) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl<'a> AsFd for io::StdinLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ // SAFETY: user code should not close stdin out from under the standard library
+ unsafe { BorrowedFd::borrow_raw(0) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl AsFd for io::Stdout {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(1) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl<'a> AsFd for io::StdoutLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ // SAFETY: user code should not close stdout out from under the standard library
+ unsafe { BorrowedFd::borrow_raw(1) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl AsFd for io::Stderr {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(2) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl<'a> AsFd for io::StderrLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ // SAFETY: user code should not close stderr out from under the standard library
+ unsafe { BorrowedFd::borrow_raw(2) }
+ }
+}
diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs
index 081915ed1..f92a05066 100644
--- a/library/std/src/os/fd/raw.rs
+++ b/library/std/src/os/fd/raw.rs
@@ -14,7 +14,7 @@ use crate::os::wasi::io::OwnedFd;
use crate::sys_common::{AsInner, IntoInner};
/// Raw file descriptors.
-#[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)]
+#[rustc_allowed_through_unstable_modules]
#[stable(feature = "rust1", since = "1.0.0")]
pub type RawFd = raw::c_int;
@@ -23,7 +23,7 @@ pub type RawFd = raw::c_int;
/// This is only available on unix and WASI platforms and must be imported in
/// order to call the method. Windows platforms have a corresponding
/// `AsRawHandle` and `AsRawSocket` set of traits.
-#[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)]
+#[rustc_allowed_through_unstable_modules]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRawFd {
/// Extracts the raw file descriptor.
@@ -42,10 +42,8 @@ pub trait AsRawFd {
/// ```no_run
/// use std::fs::File;
/// # use std::io;
- /// #[cfg(unix)]
- /// use std::os::unix::io::{AsRawFd, RawFd};
- /// #[cfg(target_os = "wasi")]
- /// use std::os::wasi::io::{AsRawFd, RawFd};
+ /// #[cfg(any(unix, target_os = "wasi"))]
+ /// use std::os::fd::{AsRawFd, RawFd};
///
/// let mut f = File::open("foo.txt")?;
/// // Note that `raw_fd` is only valid as long as `f` exists.
@@ -59,7 +57,7 @@ pub trait AsRawFd {
/// A trait to express the ability to construct an object from a raw file
/// descriptor.
-#[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)]
+#[rustc_allowed_through_unstable_modules]
#[stable(feature = "from_raw_os", since = "1.1.0")]
pub trait FromRawFd {
/// Constructs a new instance of `Self` from the given raw file
@@ -83,10 +81,8 @@ pub trait FromRawFd {
/// ```no_run
/// use std::fs::File;
/// # use std::io;
- /// #[cfg(unix)]
- /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
- /// #[cfg(target_os = "wasi")]
- /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd};
+ /// #[cfg(any(unix, target_os = "wasi"))]
+ /// use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
///
/// let f = File::open("foo.txt")?;
/// # #[cfg(any(unix, target_os = "wasi"))]
@@ -103,7 +99,7 @@ pub trait FromRawFd {
/// A trait to express the ability to consume an object and acquire ownership of
/// its raw file descriptor.
-#[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)]
+#[rustc_allowed_through_unstable_modules]
#[stable(feature = "into_raw_os", since = "1.4.0")]
pub trait IntoRawFd {
/// Consumes this object, returning the raw underlying file descriptor.
@@ -121,10 +117,8 @@ pub trait IntoRawFd {
/// ```no_run
/// use std::fs::File;
/// # use std::io;
- /// #[cfg(unix)]
- /// use std::os::unix::io::{IntoRawFd, RawFd};
- /// #[cfg(target_os = "wasi")]
- /// use std::os::wasi::io::{IntoRawFd, RawFd};
+ /// #[cfg(any(unix, target_os = "wasi"))]
+ /// use std::os::fd::{IntoRawFd, RawFd};
///
/// let f = File::open("foo.txt")?;
/// #[cfg(any(unix, target_os = "wasi"))]
diff --git a/library/std/src/os/fortanix_sgx/mod.rs b/library/std/src/os/fortanix_sgx/mod.rs
index a40dabe19..39a42f4e1 100644
--- a/library/std/src/os/fortanix_sgx/mod.rs
+++ b/library/std/src/os/fortanix_sgx/mod.rs
@@ -26,10 +26,13 @@ pub mod usercalls {
free, insecure_time, launch_thread, read, read_alloc, send, wait, write,
};
pub use crate::sys::abi::usercalls::raw::{do_usercall, Usercalls as UsercallNrs};
+ pub use crate::sys::abi::usercalls::raw::{Register, RegisterArgument, ReturnValue};
// fortanix-sgx-abi re-exports
pub use crate::sys::abi::usercalls::raw::Error;
- pub use crate::sys::abi::usercalls::raw::{ByteBuffer, FifoDescriptor, Return, Usercall};
+ pub use crate::sys::abi::usercalls::raw::{
+ ByteBuffer, Cancel, FifoDescriptor, Return, Usercall,
+ };
pub use crate::sys::abi::usercalls::raw::{Fd, Result, Tcs};
pub use crate::sys::abi::usercalls::raw::{
EV_RETURNQ_NOT_EMPTY, EV_UNPARK, EV_USERCALLQ_NOT_FULL, FD_STDERR, FD_STDIN, FD_STDOUT,
diff --git a/library/std/src/os/linux/mod.rs b/library/std/src/os/linux/mod.rs
index 8e7776f66..c17053011 100644
--- a/library/std/src/os/linux/mod.rs
+++ b/library/std/src/os/linux/mod.rs
@@ -4,5 +4,6 @@
#![doc(cfg(target_os = "linux"))]
pub mod fs;
+pub mod net;
pub mod process;
pub mod raw;
diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs
new file mode 100644
index 000000000..ff96125c3
--- /dev/null
+++ b/library/std/src/os/linux/net.rs
@@ -0,0 +1,4 @@
+//! Linux and Android-specific definitions for socket options.
+
+#![unstable(feature = "tcp_quickack", issue = "96256")]
+pub use crate::os::net::tcp::TcpStreamExt;
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 6fbaa42c7..42773805c 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -145,6 +145,11 @@ pub mod solaris;
pub mod solid;
#[cfg(target_os = "vxworks")]
pub mod vxworks;
+#[cfg(target_os = "watchos")]
+pub(crate) mod watchos;
#[cfg(any(unix, target_os = "wasi", doc))]
-mod fd;
+pub mod fd;
+
+#[cfg(any(target_os = "linux", target_os = "android", doc))]
+mod net;
diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs
new file mode 100644
index 000000000..d6d84d24e
--- /dev/null
+++ b/library/std/src/os/net/mod.rs
@@ -0,0 +1,7 @@
+//! Linux and Android-specific definitions for socket options.
+
+#![unstable(feature = "tcp_quickack", issue = "96256")]
+#![doc(cfg(any(target_os = "linux", target_os = "android",)))]
+pub mod tcp;
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/os/net/tcp.rs b/library/std/src/os/net/tcp.rs
new file mode 100644
index 000000000..5e9ee65a4
--- /dev/null
+++ b/library/std/src/os/net/tcp.rs
@@ -0,0 +1,70 @@
+//! Linux and Android-specific tcp extensions to primitives in the [`std::net`] module.
+//!
+//! [`std::net`]: crate::net
+
+use crate::io;
+use crate::net;
+use crate::sealed::Sealed;
+use crate::sys_common::AsInner;
+
+/// Os-specific extensions for [`TcpStream`]
+///
+/// [`TcpStream`]: net::TcpStream
+#[unstable(feature = "tcp_quickack", issue = "96256")]
+pub trait TcpStreamExt: Sealed {
+ /// Enable or disable `TCP_QUICKACK`.
+ ///
+ /// This flag causes Linux to eagerly send ACKs rather than delaying them.
+ /// Linux may reset this flag after further operations on the socket.
+ ///
+ /// See [`man 7 tcp`](https://man7.org/linux/man-pages/man7/tcp.7.html) and
+ /// [TCP delayed acknowledgement](https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment)
+ /// for more information.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(tcp_quickack)]
+ /// use std::net::TcpStream;
+ /// use std::os::linux::net::TcpStreamExt;
+ ///
+ /// let stream = TcpStream::connect("127.0.0.1:8080")
+ /// .expect("Couldn't connect to the server...");
+ /// stream.set_quickack(true).expect("set_quickack call failed");
+ /// ```
+ #[unstable(feature = "tcp_quickack", issue = "96256")]
+ fn set_quickack(&self, quickack: bool) -> io::Result<()>;
+
+ /// Gets the value of the `TCP_QUICKACK` option on this socket.
+ ///
+ /// For more information about this option, see [`TcpStreamExt::set_quickack`].
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(tcp_quickack)]
+ /// use std::net::TcpStream;
+ /// use std::os::linux::net::TcpStreamExt;
+ ///
+ /// let stream = TcpStream::connect("127.0.0.1:8080")
+ /// .expect("Couldn't connect to the server...");
+ /// stream.set_quickack(true).expect("set_quickack call failed");
+ /// assert_eq!(stream.quickack().unwrap_or(false), true);
+ /// ```
+ #[unstable(feature = "tcp_quickack", issue = "96256")]
+ fn quickack(&self) -> io::Result<bool>;
+}
+
+#[unstable(feature = "tcp_quickack", issue = "96256")]
+impl Sealed for net::TcpStream {}
+
+#[unstable(feature = "tcp_quickack", issue = "96256")]
+impl TcpStreamExt for net::TcpStream {
+ fn set_quickack(&self, quickack: bool) -> io::Result<()> {
+ self.as_inner().as_inner().set_quickack(quickack)
+ }
+
+ fn quickack(&self) -> io::Result<bool> {
+ self.as_inner().as_inner().quickack()
+ }
+}
diff --git a/library/std/src/os/net/tests.rs b/library/std/src/os/net/tests.rs
new file mode 100644
index 000000000..4704e3156
--- /dev/null
+++ b/library/std/src/os/net/tests.rs
@@ -0,0 +1,29 @@
+#[cfg(any(target_os = "android", target_os = "linux",))]
+#[test]
+fn quickack() {
+ use crate::{
+ net::{test::next_test_ip4, TcpListener, TcpStream},
+ os::net::tcp::TcpStreamExt,
+ };
+
+ macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ };
+ }
+
+ let addr = next_test_ip4();
+ let _listener = t!(TcpListener::bind(&addr));
+
+ let stream = t!(TcpStream::connect(&("localhost", addr.port())));
+
+ t!(stream.set_quickack(false));
+ assert_eq!(false, t!(stream.quickack()));
+ t!(stream.set_quickack(true));
+ assert_eq!(true, t!(stream.quickack()));
+ t!(stream.set_quickack(false));
+ assert_eq!(false, t!(stream.quickack()));
+}
diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs
deleted file mode 100644
index d4cb69645..000000000
--- a/library/std/src/os/unix/io/fd.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//! Owned and borrowed file descriptors.
-
-// Tests for this module
-#[cfg(test)]
-mod tests;
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-pub use crate::os::fd::owned::*;
diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs
index 3ab5606f8..25b5dbff1 100644
--- a/library/std/src/os/unix/io/mod.rs
+++ b/library/std/src/os/unix/io/mod.rs
@@ -77,10 +77,9 @@
#![stable(feature = "rust1", since = "1.0.0")]
-mod fd;
-mod raw;
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-pub use fd::*;
#[stable(feature = "rust1", since = "1.0.0")]
-pub use raw::*;
+pub use crate::os::fd::*;
+
+// Tests for this module
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs
deleted file mode 100644
index a4d2ba797..000000000
--- a/library/std/src/os/unix/io/raw.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//! Unix-specific extensions to general I/O primitives.
-
-#![stable(feature = "rust1", since = "1.0.0")]
-
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use crate::os::fd::raw::*;
diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/tests.rs
index 84d2a7a1a..84d2a7a1a 100644
--- a/library/std/src/os/unix/io/fd/tests.rs
+++ b/library/std/src/os/unix/io/tests.rs
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 411cc0925..f97fa0fb0 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -73,6 +73,8 @@ mod platform {
pub use crate::os::solaris::*;
#[cfg(target_os = "vxworks")]
pub use crate::os::vxworks::*;
+ #[cfg(target_os = "watchos")]
+ pub use crate::os::watchos::*;
}
pub mod ffi;
diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs
index 9aeae4b2c..094085e19 100644
--- a/library/std/src/os/unix/net/addr.rs
+++ b/library/std/src/os/unix/net/addr.rs
@@ -2,7 +2,7 @@ use crate::ffi::OsStr;
use crate::os::unix::ffi::OsStrExt;
use crate::path::Path;
use crate::sys::cvt;
-use crate::{ascii, fmt, io, mem, ptr};
+use crate::{fmt, io, mem, ptr};
// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
#[cfg(not(unix))]
@@ -64,18 +64,6 @@ enum AddressKind<'a> {
Abstract(&'a [u8]),
}
-struct AsciiEscaped<'a>(&'a [u8]);
-
-impl<'a> fmt::Display for AsciiEscaped<'a> {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(fmt, "\"")?;
- for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
- write!(fmt, "{}", byte as char)?;
- }
- write!(fmt, "\"")
- }
-}
-
/// An address associated with a Unix socket.
///
/// # Examples
@@ -329,7 +317,7 @@ impl SocketAddr {
crate::ptr::copy_nonoverlapping(
namespace.as_ptr(),
- addr.sun_path.as_mut_ptr().offset(1) as *mut u8,
+ addr.sun_path.as_mut_ptr().add(1) as *mut u8,
namespace.len(),
);
let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t;
@@ -343,7 +331,7 @@ impl fmt::Debug for SocketAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.address() {
AddressKind::Unnamed => write!(fmt, "(unnamed)"),
- AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
+ AddressKind::Abstract(name) => write!(fmt, "\"{}\" (abstract)", name.escape_ascii()),
AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
}
}
diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index 8008acfd1..f758f88d0 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -838,6 +838,31 @@ impl UnixDatagram {
self.0.passcred()
}
+ /// Set the id of the socket for network filtering purpose
+ ///
+ #[cfg_attr(
+ any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
+ doc = "```no_run"
+ )]
+ #[cfg_attr(
+ not(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd")),
+ doc = "```ignore"
+ )]
+ /// #![feature(unix_set_mark)]
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.set_mark(32)?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))]
+ #[unstable(feature = "unix_set_mark", issue = "96467")]
+ pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+ self.0.set_mark(mark)
+ }
+
/// Returns the value of the `SO_ERROR` option.
///
/// # Examples
diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs
index 7c0d53950..02090afc8 100644
--- a/library/std/src/os/unix/net/listener.rs
+++ b/library/std/src/os/unix/net/listener.rs
@@ -73,9 +73,11 @@ impl UnixListener {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
let (addr, len) = sockaddr_un(path.as_ref())?;
+ const backlog: libc::c_int =
+ if cfg!(any(target_os = "linux", target_os = "freebsd")) { -1 } else { 128 };
cvt(libc::bind(inner.as_inner().as_raw_fd(), &addr as *const _ as *const _, len as _))?;
- cvt(libc::listen(inner.as_inner().as_raw_fd(), 128))?;
+ cvt(libc::listen(inner.as_inner().as_raw_fd(), backlog))?;
Ok(UnixListener(inner))
}
@@ -109,12 +111,16 @@ impl UnixListener {
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ #[cfg(target_os = "linux")]
+ const backlog: libc::c_int = -1;
+ #[cfg(not(target_os = "linux"))]
+ const backlog: libc::c_int = 128;
cvt(libc::bind(
inner.as_raw_fd(),
&socket_addr.addr as *const _ as *const _,
socket_addr.len as _,
))?;
- cvt(libc::listen(inner.as_raw_fd(), 128))?;
+ cvt(libc::listen(inner.as_raw_fd(), backlog))?;
Ok(UnixListener(inner))
}
}
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index cc3a88587..dff8f6e85 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -427,6 +427,31 @@ impl UnixStream {
self.0.passcred()
}
+ /// Set the id of the socket for network filtering purpose
+ ///
+ #[cfg_attr(
+ any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
+ doc = "```no_run"
+ )]
+ #[cfg_attr(
+ not(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd")),
+ doc = "```ignore"
+ )]
+ /// #![feature(unix_set_mark)]
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixStream::connect("/tmp/sock")?;
+ /// sock.set_mark(32)?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))]
+ #[unstable(feature = "unix_set_mark", issue = "96467")]
+ pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+ self.0.set_mark(mark)
+ }
+
/// Returns the value of the `SO_ERROR` option.
///
/// # Examples
diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs
index 6c884e2ea..57bd842a5 100644
--- a/library/std/src/os/wasi/io/mod.rs
+++ b/library/std/src/os/wasi/io/mod.rs
@@ -1,12 +1,6 @@
//! WASI-specific extensions to general I/O primitives.
-#![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "71213")]
+#![stable(feature = "io_safety", since = "1.63.0")]
-mod fd;
-mod raw;
-
-#[unstable(feature = "wasi_ext", issue = "71213")]
-pub use fd::*;
-#[unstable(feature = "wasi_ext", issue = "71213")]
-pub use raw::*;
+#[stable(feature = "io_safety", since = "1.63.0")]
+pub use crate::os::fd::*;
diff --git a/library/std/src/os/watchos/fs.rs b/library/std/src/os/watchos/fs.rs
new file mode 100644
index 000000000..a14fe35a7
--- /dev/null
+++ b/library/std/src/os/watchos/fs.rs
@@ -0,0 +1,142 @@
+#![stable(feature = "metadata_ext", since = "1.1.0")]
+
+use crate::fs::Metadata;
+use crate::sys_common::AsInner;
+
+#[allow(deprecated)]
+use crate::os::watchos::raw;
+
+/// OS-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: crate::fs::Metadata
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ /// Gain a reference to the underlying `stat` structure which contains
+ /// the raw information returned by the OS.
+ ///
+ /// The contents of the returned `stat` are **not** consistent across
+ /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the
+ /// cross-Unix abstractions contained within the raw stat.
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ #[deprecated(
+ since = "1.8.0",
+ note = "deprecated in favor of the accessor \
+ methods of this trait"
+ )]
+ #[allow(deprecated)]
+ fn as_raw_stat(&self) -> &raw::stat;
+
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_dev(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ino(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mode(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_nlink(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_uid(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_gid(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_rdev(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_size(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_birthtime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_birthtime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blksize(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blocks(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_flags(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_gen(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_lspare(&self) -> u32;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for Metadata {
+ #[allow(deprecated)]
+ fn as_raw_stat(&self) -> &raw::stat {
+ unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) }
+ }
+ fn st_dev(&self) -> u64 {
+ self.as_inner().as_inner().st_dev as u64
+ }
+ fn st_ino(&self) -> u64 {
+ self.as_inner().as_inner().st_ino as u64
+ }
+ fn st_mode(&self) -> u32 {
+ self.as_inner().as_inner().st_mode as u32
+ }
+ fn st_nlink(&self) -> u64 {
+ self.as_inner().as_inner().st_nlink as u64
+ }
+ fn st_uid(&self) -> u32 {
+ self.as_inner().as_inner().st_uid as u32
+ }
+ fn st_gid(&self) -> u32 {
+ self.as_inner().as_inner().st_gid as u32
+ }
+ fn st_rdev(&self) -> u64 {
+ self.as_inner().as_inner().st_rdev as u64
+ }
+ fn st_size(&self) -> u64 {
+ self.as_inner().as_inner().st_size as u64
+ }
+ fn st_atime(&self) -> i64 {
+ self.as_inner().as_inner().st_atime as i64
+ }
+ fn st_atime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_atime_nsec as i64
+ }
+ fn st_mtime(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime as i64
+ }
+ fn st_mtime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime_nsec as i64
+ }
+ fn st_ctime(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime as i64
+ }
+ fn st_ctime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime_nsec as i64
+ }
+ fn st_birthtime(&self) -> i64 {
+ self.as_inner().as_inner().st_birthtime as i64
+ }
+ fn st_birthtime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_birthtime_nsec as i64
+ }
+ fn st_blksize(&self) -> u64 {
+ self.as_inner().as_inner().st_blksize as u64
+ }
+ fn st_blocks(&self) -> u64 {
+ self.as_inner().as_inner().st_blocks as u64
+ }
+ fn st_gen(&self) -> u32 {
+ self.as_inner().as_inner().st_gen as u32
+ }
+ fn st_flags(&self) -> u32 {
+ self.as_inner().as_inner().st_flags as u32
+ }
+ fn st_lspare(&self) -> u32 {
+ self.as_inner().as_inner().st_lspare as u32
+ }
+}
diff --git a/library/std/src/os/watchos/mod.rs b/library/std/src/os/watchos/mod.rs
new file mode 100644
index 000000000..cd6454ebb
--- /dev/null
+++ b/library/std/src/os/watchos/mod.rs
@@ -0,0 +1,6 @@
+//! watchOS-specific definitions
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+
+pub mod fs;
+pub mod raw;
diff --git a/library/std/src/os/watchos/raw.rs b/library/std/src/os/watchos/raw.rs
new file mode 100644
index 000000000..630a533d9
--- /dev/null
+++ b/library/std/src/os/watchos/raw.rs
@@ -0,0 +1,83 @@
+//! watchOS-specific raw type definitions
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+#![deprecated(
+ since = "1.8.0",
+ note = "these type aliases are no longer supported by \
+ the standard library, the `libc` crate on \
+ crates.io should be used instead for the correct \
+ definitions"
+)]
+#![allow(deprecated)]
+
+use crate::os::raw::c_long;
+
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type blkcnt_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type blksize_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type dev_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type ino_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type mode_t = u32;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type nlink_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type off_t = u64;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type time_t = i64;
+
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
+
+#[repr(C)]
+#[derive(Clone)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub struct stat {
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_dev: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mode: u16,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_nlink: u16,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ino: u64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_uid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gid: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_rdev: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_birthtime: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_birthtime_nsec: c_long,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_size: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blocks: i64,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blksize: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_flags: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gen: u32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_lspare: i32,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_qspare: [i64; 2],
+}
diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs
index 16cc8fa27..1dfecc573 100644
--- a/library/std/src/os/windows/io/handle.rs
+++ b/library/std/src/os/windows/io/handle.rs
@@ -384,6 +384,23 @@ impl fmt::Debug for OwnedHandle {
}
}
+macro_rules! impl_is_terminal {
+ ($($t:ty),*$(,)?) => {$(
+ #[unstable(feature = "sealed", issue = "none")]
+ impl crate::sealed::Sealed for $t {}
+
+ #[unstable(feature = "is_terminal", issue = "98070")]
+ impl crate::io::IsTerminal for $t {
+ #[inline]
+ fn is_terminal(&self) -> bool {
+ crate::sys::io::is_terminal(self)
+ }
+ }
+ )*}
+}
+
+impl_is_terminal!(BorrowedHandle<'_>, OwnedHandle);
+
/// A trait to borrow the handle from an underlying object.
#[stable(feature = "io_safety", since = "1.63.0")]
pub trait AsHandle {
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index 45bc56efb..c4f022de0 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -295,23 +295,22 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> {
return Some(style);
}
- // Setting environment variables for Fuchsia components isn't a standard
- // or easily supported workflow. For now, display backtraces by default.
- let format = if cfg!(target_os = "fuchsia") {
- BacktraceStyle::Full
- } else {
- crate::env::var_os("RUST_BACKTRACE")
- .map(|x| {
- if &x == "0" {
- BacktraceStyle::Off
- } else if &x == "full" {
- BacktraceStyle::Full
- } else {
- BacktraceStyle::Short
- }
- })
- .unwrap_or(BacktraceStyle::Off)
- };
+ let format = crate::env::var_os("RUST_BACKTRACE")
+ .map(|x| {
+ if &x == "0" {
+ BacktraceStyle::Off
+ } else if &x == "full" {
+ BacktraceStyle::Full
+ } else {
+ BacktraceStyle::Short
+ }
+ })
+ .unwrap_or(if cfg!(target_os = "fuchsia") {
+ // Fuchsia components default to full backtrace.
+ BacktraceStyle::Full
+ } else {
+ BacktraceStyle::Off
+ });
set_backtrace_style(format);
Some(format)
}
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 25c9201f2..d4976a469 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -18,9 +18,9 @@ use crate::intrinsics;
use crate::mem::{self, ManuallyDrop};
use crate::process;
use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::{PoisonError, RwLock};
use crate::sys::stdio::panic_output;
use crate::sys_common::backtrace;
-use crate::sys_common::rwlock::StaticRwLock;
use crate::sys_common::thread_info;
use crate::thread;
@@ -71,20 +71,29 @@ extern "C" fn __rust_foreign_exception() -> ! {
rtabort!("Rust cannot catch foreign exceptions");
}
-#[derive(Copy, Clone)]
enum Hook {
Default,
- Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)),
+ Custom(Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>),
}
impl Hook {
- fn custom(f: impl Fn(&PanicInfo<'_>) + 'static + Sync + Send) -> Self {
- Self::Custom(Box::into_raw(Box::new(f)))
+ #[inline]
+ fn into_box(self) -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
+ match self {
+ Hook::Default => Box::new(default_hook),
+ Hook::Custom(hook) => hook,
+ }
+ }
+}
+
+impl Default for Hook {
+ #[inline]
+ fn default() -> Hook {
+ Hook::Default
}
}
-static HOOK_LOCK: StaticRwLock = StaticRwLock::new();
-static mut HOOK: Hook = Hook::Default;
+static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
/// Registers a custom panic hook, replacing any that was previously registered.
///
@@ -125,24 +134,13 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
panic!("cannot modify the panic hook from a panicking thread");
}
- // SAFETY:
- //
- // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`.
- // - The argument of `Box::from_raw` is always a valid pointer that was created using
- // `Box::into_raw`.
- unsafe {
- let guard = HOOK_LOCK.write();
- let old_hook = HOOK;
- HOOK = Hook::Custom(Box::into_raw(hook));
- drop(guard);
-
- if let Hook::Custom(ptr) = old_hook {
- #[allow(unused_must_use)]
- {
- Box::from_raw(ptr);
- }
- }
- }
+ let new = Hook::Custom(hook);
+ let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
+ let old = mem::replace(&mut *hook, new);
+ drop(hook);
+ // Only drop the old hook after releasing the lock to avoid deadlocking
+ // if its destructor panics.
+ drop(old);
}
/// Unregisters the current panic hook, returning it.
@@ -179,22 +177,11 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
panic!("cannot modify the panic hook from a panicking thread");
}
- // SAFETY:
- //
- // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`.
- // - The argument of `Box::from_raw` is always a valid pointer that was created using
- // `Box::into_raw`.
- unsafe {
- let guard = HOOK_LOCK.write();
- let hook = HOOK;
- HOOK = Hook::Default;
- drop(guard);
+ let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
+ let old_hook = mem::take(&mut *hook);
+ drop(hook);
- match hook {
- Hook::Default => Box::new(default_hook),
- Hook::Custom(ptr) => Box::from_raw(ptr),
- }
- }
+ old_hook.into_box()
}
/// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with
@@ -240,24 +227,9 @@ where
panic!("cannot modify the panic hook from a panicking thread");
}
- // SAFETY:
- //
- // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`.
- // - The argument of `Box::from_raw` is always a valid pointer that was created using
- // `Box::into_raw`.
- unsafe {
- let guard = HOOK_LOCK.write();
- let old_hook = HOOK;
- HOOK = Hook::Default;
-
- let prev = match old_hook {
- Hook::Default => Box::new(default_hook),
- Hook::Custom(ptr) => Box::from_raw(ptr),
- };
-
- HOOK = Hook::custom(move |info| hook_fn(&prev, info));
- drop(guard);
- }
+ let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
+ let prev = mem::take(&mut *hook).into_box();
+ *hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info)));
}
fn default_hook(info: &PanicInfo<'_>) {
@@ -328,7 +300,7 @@ pub mod panic_count {
thread_local! { static LOCAL_PANIC_COUNT: Cell<usize> = const { Cell::new(0) } }
// Sum of panic counts from all threads. The purpose of this is to have
- // a fast path in `is_zero` (which is used by `panicking`). In any particular
+ // a fast path in `count_is_zero` (which is used by `panicking`). In any particular
// thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero,
// then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before
// and after increase and decrease, but not necessarily during their execution.
@@ -336,6 +308,14 @@ pub mod panic_count {
// Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
// records whether panic::always_abort() has been called. This can only be
// set, never cleared.
+ // panic::always_abort() is usually called to prevent memory allocations done by
+ // the panic handling in the child created by `libc::fork`.
+ // Memory allocations performed in a child created with `libc::fork` are undefined
+ // behavior in most operating systems.
+ // Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory
+ // allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is
+ // sufficient because a child process will always have exactly one thread only.
+ // See also #85261 for details.
//
// This could be viewed as a struct containing a single bit and an n-1-bit
// value, but if we wrote it like that it would be more than a single word,
@@ -346,15 +326,26 @@ pub mod panic_count {
// panicking thread consumes at least 2 bytes of address space.
static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
+ // Return the state of the ALWAYS_ABORT_FLAG and number of panics.
+ //
+ // If ALWAYS_ABORT_FLAG is not set, the number is determined on a per-thread
+ // base (stored in LOCAL_PANIC_COUNT), i.e. it is the amount of recursive calls
+ // of the calling thread.
+ // If ALWAYS_ABORT_FLAG is set, the number equals the *global* number of panic
+ // calls. See above why LOCAL_PANIC_COUNT is not used.
pub fn increase() -> (bool, usize) {
- (
- GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed) & ALWAYS_ABORT_FLAG != 0,
+ let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
+ let must_abort = global_count & ALWAYS_ABORT_FLAG != 0;
+ let panics = if must_abort {
+ global_count & !ALWAYS_ABORT_FLAG
+ } else {
LOCAL_PANIC_COUNT.with(|c| {
let next = c.get() + 1;
c.set(next);
next
- }),
- )
+ })
+ };
+ (must_abort, panics)
}
pub fn decrease() {
@@ -397,7 +388,7 @@ pub mod panic_count {
}
// Slow path is in a separate function to reduce the amount of code
- // inlined from `is_zero`.
+ // inlined from `count_is_zero`.
#[inline(never)]
#[cold]
fn is_zero_slow_path() -> bool {
@@ -682,27 +673,26 @@ fn rust_panic_with_hook(
crate::sys::abort_internal();
}
- unsafe {
- let mut info = PanicInfo::internal_constructor(message, location, can_unwind);
- let _guard = HOOK_LOCK.read();
- match HOOK {
- // Some platforms (like wasm) know that printing to stderr won't ever actually
- // print anything, and if that's the case we can skip the default
- // hook. Since string formatting happens lazily when calling `payload`
- // methods, this means we avoid formatting the string at all!
- // (The panic runtime might still call `payload.take_box()` though and trigger
- // formatting.)
- Hook::Default if panic_output().is_none() => {}
- Hook::Default => {
- info.set_payload(payload.get());
- default_hook(&info);
- }
- Hook::Custom(ptr) => {
- info.set_payload(payload.get());
- (*ptr)(&info);
- }
- };
- }
+ let mut info = PanicInfo::internal_constructor(message, location, can_unwind);
+ let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
+ match *hook {
+ // Some platforms (like wasm) know that printing to stderr won't ever actually
+ // print anything, and if that's the case we can skip the default
+ // hook. Since string formatting happens lazily when calling `payload`
+ // methods, this means we avoid formatting the string at all!
+ // (The panic runtime might still call `payload.take_box()` though and trigger
+ // formatting.)
+ Hook::Default if panic_output().is_none() => {}
+ Hook::Default => {
+ info.set_payload(payload.get());
+ default_hook(&info);
+ }
+ Hook::Custom(ref hook) => {
+ info.set_payload(payload.get());
+ hook(&info);
+ }
+ };
+ drop(hook);
if panics > 1 || !can_unwind {
// If a thread panics while it's already unwinding then we
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 5dfeb517a..9d6328162 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -2158,6 +2158,7 @@ impl Path {
/// assert_eq!(grand_parent.parent(), None);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[doc(alias = "dirname")]
#[must_use]
pub fn parent(&self) -> Option<&Path> {
let mut comps = self.components();
@@ -2225,6 +2226,7 @@ impl Path {
/// assert_eq!(None, Path::new("/").file_name());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[doc(alias = "basename")]
#[must_use]
pub fn file_name(&self) -> Option<&OsStr> {
self.components().next_back().and_then(|p| match p {
@@ -2401,7 +2403,7 @@ impl Path {
self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before))
}
- /// Extracts the extension of [`self.file_name`], if possible.
+ /// Extracts the extension (without the leading dot) of [`self.file_name`], if possible.
///
/// The extension is:
///
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index 351cf6988..dd307022c 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -1768,6 +1768,7 @@ fn test_windows_absolute() {
}
#[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
let prefix = "my/home";
let mut paths: Vec<_> =
@@ -1781,6 +1782,7 @@ fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
}
#[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) {
let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
let paths: Vec<_> =
@@ -1799,6 +1801,7 @@ fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) {
}
#[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) {
let prefix = "my/home";
let paths: Vec<_> =
@@ -1817,6 +1820,7 @@ fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) {
}
#[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
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<_> =
@@ -1835,6 +1839,7 @@ fn bench_path_hashset(b: &mut test::Bencher) {
}
#[bench]
+#[cfg_attr(miri, ignore)] // Miri isn't fast...
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<_> =
diff --git a/library/std/src/personality.rs b/library/std/src/personality.rs
new file mode 100644
index 000000000..63f0ad4f1
--- /dev/null
+++ b/library/std/src/personality.rs
@@ -0,0 +1,46 @@
+//! This module contains the implementation of the `eh_personality` lang item.
+//!
+//! The actual implementation is heavily dependent on the target since Rust
+//! tries to use the native stack unwinding mechanism whenever possible.
+//!
+//! This personality function is still required with `-C panic=abort` because
+//! it is used to catch foreign exceptions from `extern "C-unwind"` and turn
+//! them into aborts.
+//!
+//! Additionally, ARM EHABI uses the personality function when generating
+//! backtraces.
+
+mod dwarf;
+
+#[cfg(not(test))]
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "emscripten")] {
+ mod emcc;
+ } else if #[cfg(target_env = "msvc")] {
+ // This is required by the compiler to exist (e.g., it's a lang item),
+ // but it's never actually called by the compiler because
+ // _CxxFrameHandler3 is the personality function that is always used.
+ // Hence this is just an aborting stub.
+ #[lang = "eh_personality"]
+ fn rust_eh_personality() {
+ core::intrinsics::abort()
+ }
+ } else if #[cfg(any(
+ all(target_family = "windows", target_env = "gnu"),
+ target_os = "psp",
+ target_os = "solid_asp3",
+ all(target_family = "unix", not(target_os = "espidf")),
+ all(target_vendor = "fortanix", target_env = "sgx"),
+ ))] {
+ mod gcc;
+ } else {
+ // Targets that don't support unwinding.
+ // - family=wasm
+ // - os=none ("bare metal" targets)
+ // - os=uefi
+ // - os=espidf
+ // - os=hermit
+ // - nvptx64-nvidia-cuda
+ // - arch=avr
+ }
+}
diff --git a/library/panic_unwind/src/dwarf/eh.rs b/library/std/src/personality/dwarf/eh.rs
index 7394feab8..27b50c13b 100644
--- a/library/panic_unwind/src/dwarf/eh.rs
+++ b/library/std/src/personality/dwarf/eh.rs
@@ -11,7 +11,7 @@
#![allow(non_upper_case_globals)]
#![allow(unused)]
-use crate::dwarf::DwarfReader;
+use super::DwarfReader;
use core::mem;
pub const DW_EH_PE_omit: u8 = 0xFF;
@@ -75,7 +75,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
- let action_table = reader.ptr.offset(call_site_table_length as isize);
+ let action_table = reader.ptr.add(call_site_table_length as usize);
let ip = context.ip;
if !USING_SJLJ_EXCEPTIONS {
@@ -98,9 +98,8 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
}
}
}
- // Ip is not present in the table. This should not happen... but it does: issue #35011.
- // So rather than returning EHAction::Terminate, we do this.
- Ok(EHAction::None)
+ // Ip is not present in the table. This indicates a nounwind call.
+ Ok(EHAction::Terminate)
} else {
// SjLj version:
// The "IP" is an index into the call-site table, with two exceptions:
diff --git a/library/panic_unwind/src/dwarf/mod.rs b/library/std/src/personality/dwarf/mod.rs
index 652fbe95a..652fbe95a 100644
--- a/library/panic_unwind/src/dwarf/mod.rs
+++ b/library/std/src/personality/dwarf/mod.rs
diff --git a/library/panic_unwind/src/dwarf/tests.rs b/library/std/src/personality/dwarf/tests.rs
index 1644f3708..1644f3708 100644
--- a/library/panic_unwind/src/dwarf/tests.rs
+++ b/library/std/src/personality/dwarf/tests.rs
diff --git a/library/std/src/personality/emcc.rs b/library/std/src/personality/emcc.rs
new file mode 100644
index 000000000..f942bdf18
--- /dev/null
+++ b/library/std/src/personality/emcc.rs
@@ -0,0 +1,20 @@
+//! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward
+//! to `__gxx_personality_v0` which is provided by Emscripten.
+
+use libc::c_int;
+use unwind as uw;
+
+// This is required by the compiler to exist (e.g., it's a lang item), but it's
+// never actually called by the compiler. Emscripten EH doesn't use a
+// personality function at all, it instead uses __cxa_find_matching_catch.
+// Wasm error handling would use __gxx_personality_wasm0.
+#[lang = "eh_personality"]
+unsafe extern "C" fn rust_eh_personality(
+ _version: c_int,
+ _actions: uw::_Unwind_Action,
+ _exception_class: uw::_Unwind_Exception_Class,
+ _exception_object: *mut uw::_Unwind_Exception,
+ _context: *mut uw::_Unwind_Context,
+) -> uw::_Unwind_Reason_Code {
+ core::intrinsics::abort()
+}
diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs
new file mode 100644
index 000000000..7f0b0439c
--- /dev/null
+++ b/library/std/src/personality/gcc.rs
@@ -0,0 +1,279 @@
+//! Implementation of panics backed by libgcc/libunwind (in some form).
+//!
+//! For background on exception handling and stack unwinding please see
+//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
+//! documents linked from it.
+//! These are also good reads:
+//! * <https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html>
+//! * <https://monoinfinito.wordpress.com/series/exception-handling-in-c/>
+//! * <https://www.airs.com/blog/index.php?s=exception+frames>
+//!
+//! ## A brief summary
+//!
+//! Exception handling happens in two phases: a search phase and a cleanup
+//! phase.
+//!
+//! In both phases the unwinder walks stack frames from top to bottom using
+//! information from the stack frame unwind sections of the current process's
+//! modules ("module" here refers to an OS module, i.e., an executable or a
+//! dynamic library).
+//!
+//! For each stack frame, it invokes the associated "personality routine", whose
+//! address is also stored in the unwind info section.
+//!
+//! In the search phase, the job of a personality routine is to examine
+//! exception object being thrown, and to decide whether it should be caught at
+//! that stack frame. Once the handler frame has been identified, cleanup phase
+//! begins.
+//!
+//! In the cleanup phase, the unwinder invokes each personality routine again.
+//! This time it decides which (if any) cleanup code needs to be run for
+//! the current stack frame. If so, the control is transferred to a special
+//! branch in the function body, the "landing pad", which invokes destructors,
+//! frees memory, etc. At the end of the landing pad, control is transferred
+//! back to the unwinder and unwinding resumes.
+//!
+//! Once stack has been unwound down to the handler frame level, unwinding stops
+//! and the last personality routine transfers control to the catch block.
+
+use super::dwarf::eh::{self, EHAction, EHContext};
+use libc::{c_int, uintptr_t};
+use unwind as uw;
+
+// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
+// and TargetLowering::getExceptionSelectorRegister() for each architecture,
+// then mapped to DWARF register numbers via register definition tables
+// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
+// See also https://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
+
+#[cfg(target_arch = "x86")]
+const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
+
+#[cfg(target_arch = "x86_64")]
+const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1
+
+#[cfg(target_arch = "m68k")]
+const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1
+
+#[cfg(any(target_arch = "mips", target_arch = "mips64"))]
+const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1
+
+#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
+const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4
+
+#[cfg(target_arch = "s390x")]
+const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7
+
+#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
+const UNWIND_DATA_REG: (i32, i32) = (24, 25); // I0, I1
+
+#[cfg(target_arch = "hexagon")]
+const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
+
+#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
+const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11
+
+// The following code is based on GCC's C and C++ personality routines. For reference, see:
+// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
+// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
+
+cfg_if::cfg_if! {
+ if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "watchos"), not(target_os = "netbsd")))] {
+ // ARM EHABI personality routine.
+ // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
+ //
+ // iOS uses the default routine instead since it uses SjLj unwinding.
+ #[lang = "eh_personality"]
+ unsafe extern "C" fn rust_eh_personality(
+ state: uw::_Unwind_State,
+ exception_object: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context,
+ ) -> uw::_Unwind_Reason_Code {
+ let state = state as c_int;
+ let action = state & uw::_US_ACTION_MASK as c_int;
+ let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
+ // Backtraces on ARM will call the personality routine with
+ // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
+ // we want to continue unwinding the stack, otherwise all our backtraces
+ // would end at __rust_try
+ if state & uw::_US_FORCE_UNWIND as c_int != 0 {
+ return continue_unwind(exception_object, context);
+ }
+ true
+ } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
+ false
+ } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
+ return continue_unwind(exception_object, context);
+ } else {
+ return uw::_URC_FAILURE;
+ };
+
+ // The DWARF unwinder assumes that _Unwind_Context holds things like the function
+ // and LSDA pointers, however ARM EHABI places them into the exception object.
+ // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
+ // take only the context pointer, GCC personality routines stash a pointer to
+ // exception_object in the context, using location reserved for ARM's
+ // "scratch register" (r12).
+ uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr);
+ // ...A more principled approach would be to provide the full definition of ARM's
+ // _Unwind_Context in our libunwind bindings and fetch the required data from there
+ // directly, bypassing DWARF compatibility functions.
+
+ let eh_action = match find_eh_action(context) {
+ Ok(action) => action,
+ Err(_) => return uw::_URC_FAILURE,
+ };
+ if search_phase {
+ match eh_action {
+ EHAction::None | EHAction::Cleanup(_) => {
+ return continue_unwind(exception_object, context);
+ }
+ EHAction::Catch(_) => {
+ // EHABI requires the personality routine to update the
+ // SP value in the barrier cache of the exception object.
+ (*exception_object).private[5] =
+ uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
+ return uw::_URC_HANDLER_FOUND;
+ }
+ EHAction::Terminate => return uw::_URC_FAILURE,
+ }
+ } else {
+ match eh_action {
+ EHAction::None => return continue_unwind(exception_object, context),
+ EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
+ uw::_Unwind_SetGR(
+ context,
+ UNWIND_DATA_REG.0,
+ exception_object as uintptr_t,
+ );
+ uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
+ uw::_Unwind_SetIP(context, lpad);
+ return uw::_URC_INSTALL_CONTEXT;
+ }
+ EHAction::Terminate => return uw::_URC_FAILURE,
+ }
+ }
+
+ // On ARM EHABI the personality routine is responsible for actually
+ // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
+ unsafe fn continue_unwind(
+ exception_object: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context,
+ ) -> uw::_Unwind_Reason_Code {
+ if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
+ uw::_URC_CONTINUE_UNWIND
+ } else {
+ uw::_URC_FAILURE
+ }
+ }
+ // defined in libgcc
+ extern "C" {
+ fn __gnu_unwind_frame(
+ exception_object: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context,
+ ) -> uw::_Unwind_Reason_Code;
+ }
+ }
+ } else {
+ // Default personality routine, which is used directly on most targets
+ // and indirectly on Windows x86_64 via SEH.
+ unsafe extern "C" fn rust_eh_personality_impl(
+ version: c_int,
+ actions: uw::_Unwind_Action,
+ _exception_class: uw::_Unwind_Exception_Class,
+ exception_object: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context,
+ ) -> uw::_Unwind_Reason_Code {
+ if version != 1 {
+ return uw::_URC_FATAL_PHASE1_ERROR;
+ }
+ let eh_action = match find_eh_action(context) {
+ Ok(action) => action,
+ Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
+ };
+ if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
+ match eh_action {
+ EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND,
+ EHAction::Catch(_) => uw::_URC_HANDLER_FOUND,
+ EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR,
+ }
+ } else {
+ match eh_action {
+ EHAction::None => uw::_URC_CONTINUE_UNWIND,
+ EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
+ uw::_Unwind_SetGR(
+ context,
+ UNWIND_DATA_REG.0,
+ exception_object as uintptr_t,
+ );
+ uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
+ uw::_Unwind_SetIP(context, lpad);
+ uw::_URC_INSTALL_CONTEXT
+ }
+ EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR,
+ }
+ }
+ }
+
+ cfg_if::cfg_if! {
+ if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
+ // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind
+ // handler data (aka LSDA) uses GCC-compatible encoding.
+ #[lang = "eh_personality"]
+ #[allow(nonstandard_style)]
+ unsafe extern "C" fn rust_eh_personality(
+ exceptionRecord: *mut uw::EXCEPTION_RECORD,
+ establisherFrame: uw::LPVOID,
+ contextRecord: *mut uw::CONTEXT,
+ dispatcherContext: *mut uw::DISPATCHER_CONTEXT,
+ ) -> uw::EXCEPTION_DISPOSITION {
+ uw::_GCC_specific_handler(
+ exceptionRecord,
+ establisherFrame,
+ contextRecord,
+ dispatcherContext,
+ rust_eh_personality_impl,
+ )
+ }
+ } else {
+ // The personality routine for most of our targets.
+ #[lang = "eh_personality"]
+ unsafe extern "C" fn rust_eh_personality(
+ version: c_int,
+ actions: uw::_Unwind_Action,
+ exception_class: uw::_Unwind_Exception_Class,
+ exception_object: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context,
+ ) -> uw::_Unwind_Reason_Code {
+ rust_eh_personality_impl(
+ version,
+ actions,
+ exception_class,
+ exception_object,
+ context,
+ )
+ }
+ }
+ }
+ }
+}
+
+unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
+ let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
+ let mut ip_before_instr: c_int = 0;
+ let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
+ let eh_context = EHContext {
+ // The return address points 1 byte past the call instruction,
+ // which could be in the next IP range in LSDA range table.
+ //
+ // `ip = -1` has special meaning, so use wrapping sub to allow for that
+ ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) },
+ func_start: uw::_Unwind_GetRegionStart(context),
+ get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
+ get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
+ };
+ eh::find_eh_action(lsda, &eh_context)
+}
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index b8e546164..331714a99 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -611,7 +611,19 @@ mod prim_pointer {}
///
/// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
/// an array. Indeed, this provides most of the API for working with arrays.
-/// Slices have a dynamic size and do not coerce to arrays.
+///
+/// Slices have a dynamic size and do not coerce to arrays. Instead, use
+/// `slice.try_into().unwrap()` or `<ArrayType>::try_from(slice).unwrap()`.
+///
+/// Array's `try_from(slice)` implementations (and the corresponding `slice.try_into()`
+/// array implementations) succeed if the input slice length is the same as the result
+/// array length. They optimize especially well when the optimizer can easily determine
+/// the slice length, e.g. `<[u8; 4]>::try_from(&slice[4..8]).unwrap()`. Array implements
+/// [TryFrom](crate::convert::TryFrom) returning:
+///
+/// - `[T; N]` copies from the slice's elements
+/// - `&[T; N]` references the original slice's elements
+/// - `&mut [T; N]` references the original slice's elements
///
/// You can move elements out of an array with a [slice pattern]. If you want
/// one element, see [`mem::replace`].
@@ -640,6 +652,15 @@ mod prim_pointer {}
/// for x in &array { }
/// ```
///
+/// You can use `<ArrayType>::try_from(slice)` or `slice.try_into()` to get an array from
+/// a slice:
+///
+/// ```
+/// let bytes: [u8; 3] = [1, 0, 2];
+/// assert_eq!(1, u16::from_le_bytes(<[u8; 2]>::try_from(&bytes[0..2]).unwrap()));
+/// assert_eq!(512, u16::from_le_bytes(bytes[1..3].try_into().unwrap()));
+/// ```
+///
/// You can use a [slice pattern] to move elements out of an array:
///
/// ```
@@ -801,11 +822,53 @@ mod prim_array {}
/// assert_eq!(2 * pointer_size, std::mem::size_of::<Box<[u8]>>());
/// assert_eq!(2 * pointer_size, std::mem::size_of::<Rc<[u8]>>());
/// ```
+///
+/// ## Trait Implementations
+///
+/// Some traits are implemented for slices if the element type implements
+/// that trait. This includes [`Eq`], [`Hash`] and [`Ord`].
+///
+/// ## Iteration
+///
+/// The slices implement `IntoIterator`. The iterator yields references to the
+/// slice elements.
+///
+/// ```
+/// let numbers: &[i32] = &[0, 1, 2];
+/// for n in numbers {
+/// println!("{n} is a number!");
+/// }
+/// ```
+///
+/// The mutable slice yields mutable references to the elements:
+///
+/// ```
+/// let mut scores: &mut [i32] = &mut [7, 8, 9];
+/// for score in scores {
+/// *score += 1;
+/// }
+/// ```
+///
+/// This iterator yields mutable references to the slice's elements, so while
+/// the element type of the slice is `i32`, the element type of the iterator is
+/// `&mut i32`.
+///
+/// * [`.iter`] and [`.iter_mut`] are the explicit methods to return the default
+/// iterators.
+/// * Further methods that return iterators are [`.split`], [`.splitn`],
+/// [`.chunks`], [`.windows`] and more.
+///
+/// [`Hash`]: core::hash::Hash
+/// [`.iter`]: slice::iter
+/// [`.iter_mut`]: slice::iter_mut
+/// [`.split`]: slice::split
+/// [`.splitn`]: slice::splitn
+/// [`.chunks`]: slice::chunks
+/// [`.windows`]: slice::windows
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_slice {}
#[doc(primitive = "str")]
-//
/// String slices.
///
/// *[See also the `std::str` module](crate::str).*
@@ -816,19 +879,22 @@ mod prim_slice {}
///
/// String slices are always valid UTF-8.
///
-/// # Examples
+/// # Basic Usage
///
/// String literals are string slices:
///
/// ```
-/// let hello = "Hello, world!";
-///
-/// // with an explicit type annotation
-/// let hello: &'static str = "Hello, world!";
+/// let hello_world = "Hello, World!";
/// ```
///
-/// They are `'static` because they're stored directly in the final binary, and
-/// so will be valid for the `'static` duration.
+/// Here we have declared a string slice initialized with a string literal.
+/// String literals have a static lifetime, which means the string `hello_world`
+/// is guaranteed to be valid for the duration of the entire program.
+/// We can explicitly specify `hello_world`'s lifetime as well:
+///
+/// ```
+/// let hello_world: &'static str = "Hello, world!";
+/// ```
///
/// # Representation
///
@@ -996,7 +1062,7 @@ impl<T> (T,) {}
// Fake impl that's only really used for docs.
#[cfg(doc)]
#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+#[doc(fake_variadic)]
/// This trait is implemented on arbitrary-length tuples.
impl<T: Clone> Clone for (T,) {
fn clone(&self) -> Self {
@@ -1007,7 +1073,7 @@ impl<T: Clone> Clone for (T,) {
// Fake impl that's only really used for docs.
#[cfg(doc)]
#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+#[doc(fake_variadic)]
/// This trait is implemented on arbitrary-length tuples.
impl<T: Copy> Copy for (T,) {
// empty
@@ -1178,7 +1244,7 @@ mod prim_usize {}
#[doc(alias = "&")]
#[doc(alias = "&mut")]
//
-/// References, both shared and mutable.
+/// References, `&T` and `&mut T`.
///
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
@@ -1484,13 +1550,12 @@ mod prim_fn {}
// Required to make auto trait impls render.
// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls
#[doc(hidden)]
-#[cfg(not(bootstrap))]
impl<Ret, T> fn(T) -> Ret {}
// Fake impl that's only really used for docs.
#[cfg(doc)]
#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+#[doc(fake_variadic)]
/// This trait is implemented on function pointers with any number of arguments.
impl<Ret, T> Clone for fn(T) -> Ret {
fn clone(&self) -> Self {
@@ -1501,7 +1566,7 @@ impl<Ret, T> Clone for fn(T) -> Ret {
// Fake impl that's only really used for docs.
#[cfg(doc)]
#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+#[doc(fake_variadic)]
/// This trait is implemented on function pointers with any number of arguments.
impl<Ret, T> Copy for fn(T) -> Ret {
// empty
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index d6cba7e75..400d25beb 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -169,15 +169,15 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
pub struct Child {
pub(crate) handle: imp::Process,
- /// The handle for writing to the child's standard input (stdin), if it has
- /// been captured. To avoid partially moving
- /// the `child` and thus blocking yourself from calling
- /// functions on `child` while using `stdin`,
- /// you might find it helpful:
+ /// The handle for writing to the child's standard input (stdin), if it
+ /// has been captured. You might find it helpful to do
///
/// ```compile_fail,E0425
/// let stdin = child.stdin.take().unwrap();
/// ```
+ ///
+ /// to avoid partially moving the `child` and thus blocking yourself from calling
+ /// functions on `child` while using `stdin`.
#[stable(feature = "process", since = "1.0.0")]
pub stdin: Option<ChildStdin>,
@@ -1629,7 +1629,7 @@ impl ExitStatusError {
///
/// This is exactly like [`code()`](Self::code), except that it returns a `NonZeroI32`.
///
- /// Plain `code`, returning a plain integer, is provided because is is often more convenient.
+ /// Plain `code`, returning a plain integer, is provided because it is often more convenient.
/// The returned value from `code()` is indeed also nonzero; use `code_nonzero()` when you want
/// a type-level guarantee of nonzeroness.
///
@@ -2154,8 +2154,16 @@ pub fn id() -> u32 {
#[cfg_attr(not(test), lang = "termination")]
#[stable(feature = "termination_trait_lib", since = "1.61.0")]
#[rustc_on_unimplemented(
- message = "`main` has invalid return type `{Self}`",
- label = "`main` can only return types that implement `{Termination}`"
+ on(
+ all(not(bootstrap), cause = "MainFunctionType"),
+ message = "`main` has invalid return type `{Self}`",
+ label = "`main` can only return types that implement `{Termination}`"
+ ),
+ on(
+ bootstrap,
+ message = "`main` has invalid return type `{Self}`",
+ label = "`main` can only return types that implement `{Termination}`"
+ )
)]
pub trait Termination {
/// Is called to get the representation of the value as status code.
@@ -2200,9 +2208,7 @@ impl<T: Termination, E: fmt::Debug> Termination for Result<T, E> {
match self {
Ok(val) => val.report(),
Err(err) => {
- // Ignore error if the write fails, for example because stderr is
- // already closed. There is not much point panicking at this point.
- let _ = writeln!(io::stderr(), "Error: {err:?}");
+ io::attempt_print_to_stderr(format_args_nl!("Error: {err:?}"));
ExitCode::FAILURE
}
}
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 663537a05..9c2f0c1dd 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -72,10 +72,29 @@ macro_rules! rtunwrap {
// Runs before `main`.
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+//
+// # The `sigpipe` parameter
+//
+// Since 2014, the Rust runtime on Unix has set the `SIGPIPE` handler to
+// `SIG_IGN`. Applications have good reasons to want a different behavior
+// though, so there is a `#[unix_sigpipe = "..."]` attribute on `fn main()` that
+// can be used to select how `SIGPIPE` shall be setup (if changed at all) before
+// `fn main()` is called. See <https://github.com/rust-lang/rust/issues/97889>
+// for more info.
+//
+// The `sigpipe` parameter to this function gets its value via the code that
+// rustc generates to invoke `fn lang_start()`. The reason we have `sigpipe` for
+// all platforms and not only Unix, is because std is not allowed to have `cfg`
+// directives as this high level. See the module docs in
+// `src/tools/tidy/src/pal.rs` for more info. On all other platforms, `sigpipe`
+// has a value, but its value is ignored.
+//
+// Even though it is an `u8`, it only ever has 4 values. These are documented in
+// `compiler/rustc_session/src/config/sigpipe.rs`.
#[cfg_attr(test, allow(dead_code))]
-unsafe fn init(argc: isize, argv: *const *const u8) {
+unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
unsafe {
- sys::init(argc, argv);
+ sys::init(argc, argv, sigpipe);
let main_guard = sys::thread::guard::init();
// Next, set up the current Thread with the guard information we just
@@ -107,6 +126,7 @@ fn lang_start_internal(
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
argc: isize,
argv: *const *const u8,
+ sigpipe: u8,
) -> Result<isize, !> {
use crate::{mem, panic};
let rt_abort = move |e| {
@@ -124,7 +144,7 @@ fn lang_start_internal(
// prevent libstd from accidentally introducing a panic to these functions. Another is from
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
// SAFETY: Only called once during runtime initialization.
- panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?;
+ panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
@@ -140,11 +160,13 @@ fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
+ sigpipe: u8,
) -> isize {
let Ok(v) = lang_start_internal(
&move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
argc,
argv,
+ sigpipe,
);
v
}
diff --git a/library/std/src/sync/mpsc/mpsc_queue/tests.rs b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
index 9f4f31ed0..34b2a9a98 100644
--- a/library/std/src/sync/mpsc/mpsc_queue/tests.rs
+++ b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
@@ -13,7 +13,7 @@ fn test_full() {
#[test]
fn test() {
let nthreads = 8;
- let nmsgs = 1000;
+ let nmsgs = if cfg!(miri) { 100 } else { 1000 };
let q = Queue::new();
match q.pop() {
Empty => {}
diff --git a/library/std/src/sync/mpsc/spsc_queue/tests.rs b/library/std/src/sync/mpsc/spsc_queue/tests.rs
index 467ef3dbd..eb6d5c2cf 100644
--- a/library/std/src/sync/mpsc/spsc_queue/tests.rs
+++ b/library/std/src/sync/mpsc/spsc_queue/tests.rs
@@ -77,12 +77,13 @@ fn stress() {
}
unsafe fn stress_bound(bound: usize) {
+ let count = if cfg!(miri) { 1000 } else { 100000 };
let q = Arc::new(Queue::with_additions(bound, (), ()));
let (tx, rx) = channel();
let q2 = q.clone();
let _t = thread::spawn(move || {
- for _ in 0..100000 {
+ for _ in 0..count {
loop {
match q2.pop() {
Some(1) => break,
@@ -93,7 +94,7 @@ fn stress() {
}
tx.send(()).unwrap();
});
- for _ in 0..100000 {
+ for _ in 0..count {
q.push(1);
}
rx.recv().unwrap();
diff --git a/library/std/src/sync/mpsc/stream.rs b/library/std/src/sync/mpsc/stream.rs
index 4c3812c79..4592e9141 100644
--- a/library/std/src/sync/mpsc/stream.rs
+++ b/library/std/src/sync/mpsc/stream.rs
@@ -114,7 +114,7 @@ impl<T> Packet<T> {
match self.queue.producer_addition().cnt.fetch_add(1, Ordering::SeqCst) {
// As described in the mod's doc comment, -1 == wakeup
-1 => UpWoke(self.take_to_wake()),
- // As as described before, SPSC queues must be >= -2
+ // As described before, SPSC queues must be >= -2
-2 => UpSuccess,
// Be sure to preserve the disconnected state, and the return value
diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs
index e58649bab..63c794369 100644
--- a/library/std/src/sync/mpsc/sync_tests.rs
+++ b/library/std/src/sync/mpsc/sync_tests.rs
@@ -113,23 +113,25 @@ fn chan_gone_concurrent() {
#[test]
fn stress() {
+ let count = if cfg!(miri) { 100 } else { 10000 };
let (tx, rx) = sync_channel::<i32>(0);
thread::spawn(move || {
- for _ in 0..10000 {
+ for _ in 0..count {
tx.send(1).unwrap();
}
});
- for _ in 0..10000 {
+ for _ in 0..count {
assert_eq!(rx.recv().unwrap(), 1);
}
}
#[test]
fn stress_recv_timeout_two_threads() {
+ let count = if cfg!(miri) { 100 } else { 10000 };
let (tx, rx) = sync_channel::<i32>(0);
thread::spawn(move || {
- for _ in 0..10000 {
+ for _ in 0..count {
tx.send(1).unwrap();
}
});
@@ -146,12 +148,12 @@ fn stress_recv_timeout_two_threads() {
}
}
- assert_eq!(recv_count, 10000);
+ assert_eq!(recv_count, count);
}
#[test]
fn stress_recv_timeout_shared() {
- const AMT: u32 = 1000;
+ const AMT: u32 = if cfg!(miri) { 100 } else { 1000 };
const NTHREADS: u32 = 8;
let (tx, rx) = sync_channel::<i32>(0);
let (dtx, drx) = sync_channel::<()>(0);
@@ -191,7 +193,7 @@ fn stress_recv_timeout_shared() {
#[test]
fn stress_shared() {
- const AMT: u32 = 1000;
+ const AMT: u32 = if cfg!(miri) { 100 } else { 1000 };
const NTHREADS: u32 = 8;
let (tx, rx) = sync_channel::<i32>(0);
let (dtx, drx) = sync_channel::<()>(0);
@@ -438,12 +440,13 @@ fn stream_send_recv_stress() {
#[test]
fn recv_a_lot() {
+ let count = if cfg!(miri) { 1000 } else { 10000 };
// Regression test that we don't run out of stack in scheduler context
- let (tx, rx) = sync_channel(10000);
- for _ in 0..10000 {
+ let (tx, rx) = sync_channel(count);
+ for _ in 0..count {
tx.send(()).unwrap();
}
- for _ in 0..10000 {
+ for _ in 0..count {
rx.recv().unwrap();
}
}
diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs
index 4deb3e596..f6d0796f6 100644
--- a/library/std/src/sync/mpsc/tests.rs
+++ b/library/std/src/sync/mpsc/tests.rs
@@ -120,13 +120,14 @@ fn chan_gone_concurrent() {
#[test]
fn stress() {
+ let count = if cfg!(miri) { 100 } else { 10000 };
let (tx, rx) = channel::<i32>();
let t = thread::spawn(move || {
- for _ in 0..10000 {
+ for _ in 0..count {
tx.send(1).unwrap();
}
});
- for _ in 0..10000 {
+ for _ in 0..count {
assert_eq!(rx.recv().unwrap(), 1);
}
t.join().ok().expect("thread panicked");
@@ -134,7 +135,7 @@ fn stress() {
#[test]
fn stress_shared() {
- const AMT: u32 = 10000;
+ const AMT: u32 = if cfg!(miri) { 100 } else { 10000 };
const NTHREADS: u32 = 8;
let (tx, rx) = channel::<i32>();
@@ -504,12 +505,13 @@ fn very_long_recv_timeout_wont_panic() {
#[test]
fn recv_a_lot() {
+ let count = if cfg!(miri) { 1000 } else { 10000 };
// Regression test that we don't run out of stack in scheduler context
let (tx, rx) = channel();
- for _ in 0..10000 {
+ for _ in 0..count {
tx.send(()).unwrap();
}
- for _ in 0..10000 {
+ for _ in 0..count {
rx.recv().unwrap();
}
}
diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs
index e0d13cd64..de851c8fb 100644
--- a/library/std/src/sync/mutex.rs
+++ b/library/std/src/sync/mutex.rs
@@ -192,6 +192,7 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
and cause Futures to not implement `Send`"]
#[stable(feature = "rust1", since = "1.0.0")]
#[clippy::has_significant_drop]
+#[cfg_attr(not(test), rustc_diagnostic_item = "MutexGuard")]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
poison: poison::Guard,
diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs
index a7feea588..0f25417d6 100644
--- a/library/std/src/sync/once.rs
+++ b/library/std/src/sync/once.rs
@@ -3,99 +3,12 @@
//! This primitive is meant to be used to run one-time initialization. An
//! example use case would be for initializing an FFI library.
-// A "once" is a relatively simple primitive, and it's also typically provided
-// by the OS as well (see `pthread_once` or `InitOnceExecuteOnce`). The OS
-// primitives, however, tend to have surprising restrictions, such as the Unix
-// one doesn't allow an argument to be passed to the function.
-//
-// As a result, we end up implementing it ourselves in the standard library.
-// This also gives us the opportunity to optimize the implementation a bit which
-// should help the fast path on call sites. Consequently, let's explain how this
-// primitive works now!
-//
-// So to recap, the guarantees of a Once are that it will call the
-// initialization closure at most once, and it will never return until the one
-// that's running has finished running. This means that we need some form of
-// blocking here while the custom callback is running at the very least.
-// Additionally, we add on the restriction of **poisoning**. Whenever an
-// initialization closure panics, the Once enters a "poisoned" state which means
-// that all future calls will immediately panic as well.
-//
-// So to implement this, one might first reach for a `Mutex`, but those cannot
-// be put into a `static`. It also gets a lot harder with poisoning to figure
-// out when the mutex needs to be deallocated because it's not after the closure
-// finishes, but after the first successful closure finishes.
-//
-// All in all, this is instead implemented with atomics and lock-free
-// operations! Whee! Each `Once` has one word of atomic state, and this state is
-// CAS'd on to determine what to do. There are four possible state of a `Once`:
-//
-// * Incomplete - no initialization has run yet, and no thread is currently
-// using the Once.
-// * Poisoned - some thread has previously attempted to initialize the Once, but
-// it panicked, so the Once is now poisoned. There are no other
-// threads currently accessing this Once.
-// * Running - some thread is currently attempting to run initialization. It may
-// succeed, so all future threads need to wait for it to finish.
-// Note that this state is accompanied with a payload, described
-// below.
-// * Complete - initialization has completed and all future calls should finish
-// immediately.
-//
-// With 4 states we need 2 bits to encode this, and we use the remaining bits
-// in the word we have allocated as a queue of threads waiting for the thread
-// responsible for entering the RUNNING state. This queue is just a linked list
-// of Waiter nodes which is monotonically increasing in size. Each node is
-// allocated on the stack, and whenever the running closure finishes it will
-// consume the entire queue and notify all waiters they should try again.
-//
-// You'll find a few more details in the implementation, but that's the gist of
-// it!
-//
-// Atomic orderings:
-// When running `Once` we deal with multiple atomics:
-// `Once.state_and_queue` and an unknown number of `Waiter.signaled`.
-// * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the
-// result of the `Once`, and (3) for synchronizing `Waiter` nodes.
-// - At the end of the `call_inner` function we have to make sure the result
-// of the `Once` is acquired. So every load which can be the only one to
-// load COMPLETED must have at least Acquire ordering, which means all
-// three of them.
-// - `WaiterQueue::Drop` is the only place that may store COMPLETED, and
-// must do so with Release ordering to make the result available.
-// - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and
-// needs to make the nodes available with Release ordering. The load in
-// its `compare_exchange` can be Relaxed because it only has to compare
-// the atomic, not to read other data.
-// - `WaiterQueue::Drop` must see the `Waiter` nodes, so it must load
-// `state_and_queue` with Acquire ordering.
-// - There is just one store where `state_and_queue` is used only as a
-// state flag, without having to synchronize data: switching the state
-// from INCOMPLETE to RUNNING in `call_inner`. This store can be Relaxed,
-// but the read has to be Acquire because of the requirements mentioned
-// above.
-// * `Waiter.signaled` is both used as a flag, and to protect a field with
-// interior mutability in `Waiter`. `Waiter.thread` is changed in
-// `WaiterQueue::Drop` which then sets `signaled` with Release ordering.
-// After `wait` loads `signaled` with Acquire and sees it is true, it needs to
-// see the changes to drop the `Waiter` struct correctly.
-// * There is one place where the two atomics `Once.state_and_queue` and
-// `Waiter.signaled` come together, and might be reordered by the compiler or
-// processor. Because both use Acquire ordering such a reordering is not
-// allowed, so no need for SeqCst.
-
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests;
-use crate::cell::Cell;
use crate::fmt;
-use crate::marker;
use crate::panic::{RefUnwindSafe, UnwindSafe};
-use crate::ptr;
-use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
-use crate::thread::{self, Thread};
-
-type Masked = ();
+use crate::sys_common::once as sys;
/// A synchronization primitive which can be used to run a one-time global
/// initialization. Useful for one-time initialization for FFI or related
@@ -114,19 +27,9 @@ type Masked = ();
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Once {
- // `state_and_queue` is actually a pointer to a `Waiter` with extra state
- // bits, so we add the `PhantomData` appropriately.
- state_and_queue: AtomicPtr<Masked>,
- _marker: marker::PhantomData<*const Waiter>,
+ inner: sys::Once,
}
-// The `PhantomData` of a raw pointer removes these two auto traits, but we
-// enforce both below in the implementation so this should be safe to add.
-#[stable(feature = "rust1", since = "1.0.0")]
-unsafe impl Sync for Once {}
-#[stable(feature = "rust1", since = "1.0.0")]
-unsafe impl Send for Once {}
-
#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")]
impl UnwindSafe for Once {}
@@ -136,10 +39,8 @@ impl RefUnwindSafe for Once {}
/// State yielded to [`Once::call_once_force()`]ā€™s closure parameter. The state
/// can be used to query the poison status of the [`Once`].
#[stable(feature = "once_poison", since = "1.51.0")]
-#[derive(Debug)]
pub struct OnceState {
- poisoned: bool,
- set_state_on_drop_to: Cell<*mut Masked>,
+ pub(crate) inner: sys::OnceState,
}
/// Initialization value for static [`Once`] values.
@@ -159,38 +60,6 @@ pub struct OnceState {
)]
pub const ONCE_INIT: Once = Once::new();
-// Four states that a Once can be in, encoded into the lower bits of
-// `state_and_queue` in the Once structure.
-const INCOMPLETE: usize = 0x0;
-const POISONED: usize = 0x1;
-const RUNNING: usize = 0x2;
-const COMPLETE: usize = 0x3;
-
-// Mask to learn about the state. All other bits are the queue of waiters if
-// this is in the RUNNING state.
-const STATE_MASK: usize = 0x3;
-
-// Representation of a node in the linked list of waiters, used while in the
-// RUNNING state.
-// Note: `Waiter` can't hold a mutable pointer to the next thread, because then
-// `wait` would both hand out a mutable reference to its `Waiter` node, and keep
-// a shared reference to check `signaled`. Instead we hold shared references and
-// use interior mutability.
-#[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
-struct Waiter {
- thread: Cell<Option<Thread>>,
- signaled: AtomicBool,
- next: *const Waiter,
-}
-
-// Head of a linked list of waiters.
-// Every node is a struct on the stack of a waiting thread.
-// Will wake up the waiters when it gets dropped, i.e. also on panic.
-struct WaiterQueue<'a> {
- state_and_queue: &'a AtomicPtr<Masked>,
- set_state_on_drop_to: *mut Masked,
-}
-
impl Once {
/// Creates a new `Once` value.
#[inline]
@@ -198,10 +67,7 @@ impl Once {
#[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
#[must_use]
pub const fn new() -> Once {
- Once {
- state_and_queue: AtomicPtr::new(ptr::invalid_mut(INCOMPLETE)),
- _marker: marker::PhantomData,
- }
+ Once { inner: sys::Once::new() }
}
/// Performs an initialization routine once and only once. The given closure
@@ -261,6 +127,7 @@ impl Once {
/// This is similar to [poisoning with mutexes][poison].
///
/// [poison]: struct.Mutex.html#poisoning
+ #[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[track_caller]
pub fn call_once<F>(&self, f: F)
@@ -268,12 +135,12 @@ impl Once {
F: FnOnce(),
{
// Fast path check
- if self.is_completed() {
+ if self.inner.is_completed() {
return;
}
let mut f = Some(f);
- self.call_inner(false, &mut |_| f.take().unwrap()());
+ self.inner.call(false, &mut |_| f.take().unwrap()());
}
/// Performs the same function as [`call_once()`] except ignores poisoning.
@@ -320,18 +187,19 @@ impl Once {
/// // once any success happens, we stop propagating the poison
/// INIT.call_once(|| {});
/// ```
+ #[inline]
#[stable(feature = "once_poison", since = "1.51.0")]
pub fn call_once_force<F>(&self, f: F)
where
F: FnOnce(&OnceState),
{
// Fast path check
- if self.is_completed() {
+ if self.inner.is_completed() {
return;
}
let mut f = Some(f);
- self.call_inner(true, &mut |p| f.take().unwrap()(p));
+ self.inner.call(true, &mut |p| f.take().unwrap()(p));
}
/// Returns `true` if some [`call_once()`] call has completed
@@ -378,119 +246,7 @@ impl Once {
#[stable(feature = "once_is_completed", since = "1.43.0")]
#[inline]
pub fn is_completed(&self) -> bool {
- // An `Acquire` load is enough because that makes all the initialization
- // operations visible to us, and, this being a fast path, weaker
- // ordering helps with performance. This `Acquire` synchronizes with
- // `Release` operations on the slow path.
- self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
- }
-
- // This is a non-generic function to reduce the monomorphization cost of
- // using `call_once` (this isn't exactly a trivial or small implementation).
- //
- // Additionally, this is tagged with `#[cold]` as it should indeed be cold
- // and it helps let LLVM know that calls to this function should be off the
- // fast path. Essentially, this should help generate more straight line code
- // in LLVM.
- //
- // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
- // currently no way to take an `FnOnce` and call it via virtual dispatch
- // without some allocation overhead.
- #[cold]
- #[track_caller]
- fn call_inner(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&OnceState)) {
- let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
- loop {
- match state_and_queue.addr() {
- COMPLETE => break,
- POISONED if !ignore_poisoning => {
- // Panic to propagate the poison.
- panic!("Once instance has previously been poisoned");
- }
- POISONED | INCOMPLETE => {
- // Try to register this thread as the one RUNNING.
- let exchange_result = self.state_and_queue.compare_exchange(
- state_and_queue,
- ptr::invalid_mut(RUNNING),
- Ordering::Acquire,
- Ordering::Acquire,
- );
- if let Err(old) = exchange_result {
- state_and_queue = old;
- continue;
- }
- // `waiter_queue` will manage other waiting threads, and
- // wake them up on drop.
- let mut waiter_queue = WaiterQueue {
- state_and_queue: &self.state_and_queue,
- set_state_on_drop_to: ptr::invalid_mut(POISONED),
- };
- // Run the initialization function, letting it know if we're
- // poisoned or not.
- let init_state = OnceState {
- poisoned: state_and_queue.addr() == POISONED,
- set_state_on_drop_to: Cell::new(ptr::invalid_mut(COMPLETE)),
- };
- init(&init_state);
- waiter_queue.set_state_on_drop_to = init_state.set_state_on_drop_to.get();
- break;
- }
- _ => {
- // All other values must be RUNNING with possibly a
- // pointer to the waiter queue in the more significant bits.
- assert!(state_and_queue.addr() & STATE_MASK == RUNNING);
- wait(&self.state_and_queue, state_and_queue);
- state_and_queue = self.state_and_queue.load(Ordering::Acquire);
- }
- }
- }
- }
-}
-
-fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
- // Note: the following code was carefully written to avoid creating a
- // mutable reference to `node` that gets aliased.
- loop {
- // Don't queue this thread if the status is no longer running,
- // otherwise we will not be woken up.
- if current_state.addr() & STATE_MASK != RUNNING {
- return;
- }
-
- // Create the node for our current thread.
- let node = Waiter {
- thread: Cell::new(Some(thread::current())),
- signaled: AtomicBool::new(false),
- next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter,
- };
- let me = &node as *const Waiter as *const Masked as *mut Masked;
-
- // Try to slide in the node at the head of the linked list, making sure
- // that another thread didn't just replace the head of the linked list.
- let exchange_result = state_and_queue.compare_exchange(
- current_state,
- me.with_addr(me.addr() | RUNNING),
- Ordering::Release,
- Ordering::Relaxed,
- );
- if let Err(old) = exchange_result {
- current_state = old;
- continue;
- }
-
- // We have enqueued ourselves, now lets wait.
- // It is important not to return before being signaled, otherwise we
- // would drop our `Waiter` node and leave a hole in the linked list
- // (and a dangling reference). Guard against spurious wakeups by
- // reparking ourselves until we are signaled.
- while !node.signaled.load(Ordering::Acquire) {
- // If the managing thread happens to signal and unpark us before we
- // can park ourselves, the result could be this thread never gets
- // unparked. Luckily `park` comes with the guarantee that if it got
- // an `unpark` just before on an unparked thread it does not park.
- thread::park();
- }
- break;
+ self.inner.is_completed()
}
}
@@ -501,37 +257,6 @@ impl fmt::Debug for Once {
}
}
-impl Drop for WaiterQueue<'_> {
- fn drop(&mut self) {
- // Swap out our state with however we finished.
- let state_and_queue =
- self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel);
-
- // We should only ever see an old state which was RUNNING.
- assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING);
-
- // Walk the entire linked list of waiters and wake them up (in lifo
- // order, last to register is first to wake up).
- unsafe {
- // Right after setting `node.signaled = true` the other thread may
- // free `node` if there happens to be has a spurious wakeup.
- // So we have to take out the `thread` field and copy the pointer to
- // `next` first.
- let mut queue =
- state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter;
- while !queue.is_null() {
- let next = (*queue).next;
- let thread = (*queue).thread.take().unwrap();
- (*queue).signaled.store(true, Ordering::Release);
- // ^- FIXME (maybe): This is another case of issue #55005
- // `store()` has a potentially dangling ref to `signaled`.
- queue = next;
- thread.unpark();
- }
- }
- }
-}
-
impl OnceState {
/// Returns `true` if the associated [`Once`] was poisoned prior to the
/// invocation of the closure passed to [`Once::call_once_force()`].
@@ -568,13 +293,22 @@ impl OnceState {
/// assert!(!state.is_poisoned());
/// });
#[stable(feature = "once_poison", since = "1.51.0")]
+ #[inline]
pub fn is_poisoned(&self) -> bool {
- self.poisoned
+ self.inner.is_poisoned()
}
/// Poison the associated [`Once`] without explicitly panicking.
- // NOTE: This is currently only exposed for the `lazy` module
+ // NOTE: This is currently only exposed for `OnceLock`.
+ #[inline]
pub(crate) fn poison(&self) {
- self.set_state_on_drop_to.set(ptr::invalid_mut(POISONED));
+ self.inner.poison();
+ }
+}
+
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl fmt::Debug for OnceState {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("OnceState").field("poisoned", &self.is_poisoned()).finish()
}
}
diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs
index 813516040..37413ec62 100644
--- a/library/std/src/sync/once_lock.rs
+++ b/library/std/src/sync/once_lock.rs
@@ -3,7 +3,6 @@ use crate::fmt;
use crate::marker::PhantomData;
use crate::mem::MaybeUninit;
use crate::panic::{RefUnwindSafe, UnwindSafe};
-use crate::pin::Pin;
use crate::sync::Once;
/// A synchronization primitive which can be written to only once.
@@ -223,60 +222,6 @@ impl<T> OnceLock<T> {
Ok(unsafe { self.get_unchecked() })
}
- /// Internal-only API that gets the contents of the cell, initializing it
- /// in two steps with `f` and `g` if the cell was empty.
- ///
- /// `f` is called to construct the value, which is then moved into the cell
- /// and given as a (pinned) mutable reference to `g` to finish
- /// initialization.
- ///
- /// This allows `g` to inspect an manipulate the value after it has been
- /// moved into its final place in the cell, but before the cell is
- /// considered initialized.
- ///
- /// # Panics
- ///
- /// If `f` or `g` panics, the panic is propagated to the caller, and the
- /// cell remains uninitialized.
- ///
- /// With the current implementation, if `g` panics, the value from `f` will
- /// not be dropped. This should probably be fixed if this is ever used for
- /// a type where this matters.
- ///
- /// It is an error to reentrantly initialize the cell from `f`. The exact
- /// outcome is unspecified. Current implementation deadlocks, but this may
- /// be changed to a panic in the future.
- pub(crate) fn get_or_init_pin<F, G>(self: Pin<&Self>, f: F, g: G) -> Pin<&T>
- where
- F: FnOnce() -> T,
- G: FnOnce(Pin<&mut T>),
- {
- if let Some(value) = self.get_ref().get() {
- // SAFETY: The inner value was already initialized, and will not be
- // moved anymore.
- return unsafe { Pin::new_unchecked(value) };
- }
-
- let slot = &self.value;
-
- // Ignore poisoning from other threads
- // If another thread panics, then we'll be able to run our closure
- self.once.call_once_force(|_| {
- let value = f();
- // SAFETY: We use the Once (self.once) to guarantee unique access
- // to the UnsafeCell (slot).
- let value: &mut T = unsafe { (&mut *slot.get()).write(value) };
- // SAFETY: The value has been written to its final place in
- // self.value. We do not to move it anymore, which we promise here
- // with a Pin<&mut T>.
- g(unsafe { Pin::new_unchecked(value) });
- });
-
- // SAFETY: The inner value has been initialized, and will not be moved
- // anymore.
- unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) }
- }
-
/// Consumes the `OnceLock`, returning the wrapped value. Returns
/// `None` if the cell was empty.
///
diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs
index 6e4a2cfc8..8b3877607 100644
--- a/library/std/src/sync/rwlock.rs
+++ b/library/std/src/sync/rwlock.rs
@@ -76,6 +76,7 @@ use crate::sys_common::rwlock as sys;
///
/// [`Mutex`]: super::Mutex
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "RwLock")]
pub struct RwLock<T: ?Sized> {
inner: sys::MovableRwLock,
poison: poison::Flag,
@@ -101,6 +102,7 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
and cause Futures to not implement `Send`"]
#[stable(feature = "rust1", since = "1.0.0")]
#[clippy::has_significant_drop]
+#[cfg_attr(not(test), rustc_diagnostic_item = "RwLockReadGuard")]
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
// NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a
// `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
@@ -130,6 +132,7 @@ unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
and cause Future's to not implement `Send`"]
#[stable(feature = "rust1", since = "1.0.0")]
#[clippy::has_significant_drop]
+#[cfg_attr(not(test), rustc_diagnostic_item = "RwLockWriteGuard")]
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
lock: &'a RwLock<T>,
poison: poison::Guard,
@@ -164,7 +167,7 @@ impl<T> RwLock<T> {
}
impl<T: ?Sized> RwLock<T> {
- /// Locks this rwlock with shared read access, blocking the current thread
+ /// Locks this `RwLock` with shared read access, blocking the current thread
/// until it can be acquired.
///
/// The calling thread will be blocked until there are no more writers which
@@ -178,9 +181,10 @@ impl<T: ?Sized> RwLock<T> {
///
/// # Errors
///
- /// This function will return an error if the RwLock is poisoned. An RwLock
- /// is poisoned whenever a writer panics while holding an exclusive lock.
- /// The failure will occur immediately after the lock has been acquired.
+ /// This function will return an error if the `RwLock` is poisoned. An
+ /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
+ /// lock. The failure will occur immediately after the lock has been
+ /// acquired.
///
/// # Panics
///
@@ -212,7 +216,7 @@ impl<T: ?Sized> RwLock<T> {
}
}
- /// Attempts to acquire this rwlock with shared read access.
+ /// Attempts to acquire this `RwLock` with shared read access.
///
/// If the access could not be granted at this time, then `Err` is returned.
/// Otherwise, an RAII guard is returned which will release the shared access
@@ -225,13 +229,13 @@ impl<T: ?Sized> RwLock<T> {
///
/// # Errors
///
- /// This function will return the [`Poisoned`] error if the RwLock is poisoned.
- /// An RwLock is poisoned whenever a writer panics while holding an exclusive
- /// lock. `Poisoned` will only be returned if the lock would have otherwise been
- /// acquired.
+ /// This function will return the [`Poisoned`] error if the `RwLock` is
+ /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
+ /// an exclusive lock. `Poisoned` will only be returned if the lock would
+ /// have otherwise been acquired.
///
- /// This function will return the [`WouldBlock`] error if the RwLock could not
- /// be acquired because it was already locked exclusively.
+ /// This function will return the [`WouldBlock`] error if the `RwLock` could
+ /// not be acquired because it was already locked exclusively.
///
/// [`Poisoned`]: TryLockError::Poisoned
/// [`WouldBlock`]: TryLockError::WouldBlock
@@ -260,20 +264,20 @@ impl<T: ?Sized> RwLock<T> {
}
}
- /// Locks this rwlock with exclusive write access, blocking the current
+ /// Locks this `RwLock` with exclusive write access, blocking the current
/// thread until it can be acquired.
///
/// This function will not return while other writers or other readers
/// currently have access to the lock.
///
- /// Returns an RAII guard which will drop the write access of this rwlock
+ /// Returns an RAII guard which will drop the write access of this `RwLock`
/// when dropped.
///
/// # Errors
///
- /// This function will return an error if the RwLock is poisoned. An RwLock
- /// is poisoned whenever a writer panics while holding an exclusive lock.
- /// An error will be returned when the lock is acquired.
+ /// This function will return an error if the `RwLock` is poisoned. An
+ /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
+ /// lock. An error will be returned when the lock is acquired.
///
/// # Panics
///
@@ -300,7 +304,7 @@ impl<T: ?Sized> RwLock<T> {
}
}
- /// Attempts to lock this rwlock with exclusive write access.
+ /// Attempts to lock this `RwLock` with exclusive write access.
///
/// If the lock could not be acquired at this time, then `Err` is returned.
/// Otherwise, an RAII guard is returned which will release the lock when
@@ -313,13 +317,13 @@ impl<T: ?Sized> RwLock<T> {
///
/// # Errors
///
- /// This function will return the [`Poisoned`] error if the RwLock is
- /// poisoned. An RwLock is poisoned whenever a writer panics while holding
- /// an exclusive lock. `Poisoned` will only be returned if the lock would have
- /// otherwise been acquired.
+ /// This function will return the [`Poisoned`] error if the `RwLock` is
+ /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
+ /// an exclusive lock. `Poisoned` will only be returned if the lock would
+ /// have otherwise been acquired.
///
- /// This function will return the [`WouldBlock`] error if the RwLock could not
- /// be acquired because it was already locked exclusively.
+ /// This function will return the [`WouldBlock`] error if the `RwLock` could
+ /// not be acquired because it was already locked exclusively.
///
/// [`Poisoned`]: TryLockError::Poisoned
/// [`WouldBlock`]: TryLockError::WouldBlock
@@ -419,10 +423,10 @@ impl<T: ?Sized> RwLock<T> {
///
/// # Errors
///
- /// This function will return an error if the RwLock is poisoned. An RwLock
- /// is poisoned whenever a writer panics while holding an exclusive lock. An
- /// error will only be returned if the lock would have otherwise been
- /// acquired.
+ /// This function will return an error if the `RwLock` is poisoned. An
+ /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
+ /// lock. An error will only be returned if the lock would have otherwise
+ /// been acquired.
///
/// # Examples
///
@@ -452,10 +456,10 @@ impl<T: ?Sized> RwLock<T> {
///
/// # Errors
///
- /// This function will return an error if the RwLock is poisoned. An RwLock
- /// is poisoned whenever a writer panics while holding an exclusive lock. An
- /// error will only be returned if the lock would have otherwise been
- /// acquired.
+ /// This function will return an error if the `RwLock` is poisoned. An
+ /// `RwLock` is poisoned whenever a writer panics while holding an exclusive
+ /// lock. An error will only be returned if the lock would have otherwise
+ /// been acquired.
///
/// # Examples
///
diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs
index 08255c985..b5b3ad989 100644
--- a/library/std/src/sync/rwlock/tests.rs
+++ b/library/std/src/sync/rwlock/tests.rs
@@ -19,7 +19,7 @@ fn smoke() {
#[test]
fn frob() {
const N: u32 = 10;
- const M: usize = 1000;
+ const M: usize = if cfg!(miri) { 100 } else { 1000 };
let r = Arc::new(RwLock::new(()));
diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs
index ff64d2aa8..29fc0835d 100644
--- a/library/std/src/sys/common/mod.rs
+++ b/library/std/src/sys/common/mod.rs
@@ -11,3 +11,7 @@
#![allow(dead_code)]
pub mod alloc;
+pub mod small_c_string;
+
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/sys/common/small_c_string.rs b/library/std/src/sys/common/small_c_string.rs
new file mode 100644
index 000000000..01acd5191
--- /dev/null
+++ b/library/std/src/sys/common/small_c_string.rs
@@ -0,0 +1,58 @@
+use crate::ffi::{CStr, CString};
+use crate::mem::MaybeUninit;
+use crate::path::Path;
+use crate::slice;
+use crate::{io, ptr};
+
+// Make sure to stay under 4096 so the compiler doesn't insert a probe frame:
+// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
+#[cfg(not(target_os = "espidf"))]
+const MAX_STACK_ALLOCATION: usize = 384;
+#[cfg(target_os = "espidf")]
+const MAX_STACK_ALLOCATION: usize = 32;
+
+const NUL_ERR: io::Error =
+ io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte");
+
+#[inline]
+pub fn run_path_with_cstr<T, F>(path: &Path, f: F) -> io::Result<T>
+where
+ F: FnOnce(&CStr) -> io::Result<T>,
+{
+ run_with_cstr(path.as_os_str().bytes(), f)
+}
+
+#[inline]
+pub fn run_with_cstr<T, F>(bytes: &[u8], f: F) -> io::Result<T>
+where
+ F: FnOnce(&CStr) -> io::Result<T>,
+{
+ if bytes.len() >= MAX_STACK_ALLOCATION {
+ return run_with_cstr_allocating(bytes, f);
+ }
+
+ let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
+ let buf_ptr = buf.as_mut_ptr() as *mut u8;
+
+ unsafe {
+ ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
+ buf_ptr.add(bytes.len()).write(0);
+ }
+
+ match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
+ Ok(s) => f(s),
+ Err(_) => Err(NUL_ERR),
+ }
+}
+
+#[cold]
+#[inline(never)]
+fn run_with_cstr_allocating<T, F>(bytes: &[u8], f: F) -> io::Result<T>
+where
+ F: FnOnce(&CStr) -> io::Result<T>,
+{
+ match CString::new(bytes) {
+ Ok(s) => f(&s),
+ Err(_) => Err(NUL_ERR),
+ }
+}
diff --git a/library/std/src/sys/common/tests.rs b/library/std/src/sys/common/tests.rs
new file mode 100644
index 000000000..fb6f5d6af
--- /dev/null
+++ b/library/std/src/sys/common/tests.rs
@@ -0,0 +1,66 @@
+use crate::ffi::CString;
+use crate::hint::black_box;
+use crate::path::Path;
+use crate::sys::common::small_c_string::run_path_with_cstr;
+use core::iter::repeat;
+
+#[test]
+fn stack_allocation_works() {
+ let path = Path::new("abc");
+ let result = run_path_with_cstr(path, |p| {
+ assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap());
+ Ok(42)
+ });
+ assert_eq!(result.unwrap(), 42);
+}
+
+#[test]
+fn stack_allocation_fails() {
+ let path = Path::new("ab\0");
+ assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err());
+}
+
+#[test]
+fn heap_allocation_works() {
+ let path = repeat("a").take(384).collect::<String>();
+ let path = Path::new(&path);
+ let result = run_path_with_cstr(path, |p| {
+ assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap());
+ Ok(42)
+ });
+ assert_eq!(result.unwrap(), 42);
+}
+
+#[test]
+fn heap_allocation_fails() {
+ let mut path = repeat("a").take(384).collect::<String>();
+ path.push('\0');
+ let path = Path::new(&path);
+ assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err());
+}
+
+#[bench]
+fn bench_stack_path_alloc(b: &mut test::Bencher) {
+ let path = repeat("a").take(383).collect::<String>();
+ let p = Path::new(&path);
+ b.iter(|| {
+ run_path_with_cstr(p, |cstr| {
+ black_box(cstr);
+ Ok(())
+ })
+ .unwrap();
+ });
+}
+
+#[bench]
+fn bench_heap_path_alloc(b: &mut test::Bencher) {
+ let path = repeat("a").take(384).collect::<String>();
+ let p = Path::new(&path);
+ b.iter(|| {
+ run_path_with_cstr(p, |cstr| {
+ black_box(cstr);
+ Ok(())
+ })
+ .unwrap();
+ });
+}
diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs
index 1c7e1dd8d..afcae6c90 100644
--- a/library/std/src/sys/hermit/args.rs
+++ b/library/std/src/sys/hermit/args.rs
@@ -1,20 +1,37 @@
-use crate::ffi::OsString;
+use crate::ffi::{c_char, CStr, OsString};
use crate::fmt;
+use crate::os::unix::ffi::OsStringExt;
+use crate::ptr;
+use crate::sync::atomic::{
+ AtomicIsize, AtomicPtr,
+ Ordering::{Acquire, Relaxed, Release},
+};
use crate::vec;
+static ARGC: AtomicIsize = AtomicIsize::new(0);
+static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
+
/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) {
- imp::init(argc, argv)
-}
-
-/// One-time global cleanup.
-pub unsafe fn cleanup() {
- imp::cleanup()
+ ARGC.store(argc, Relaxed);
+ // Use release ordering here to broadcast writes by the OS.
+ ARGV.store(argv as *mut *const u8, Release);
}
/// Returns the command line arguments
pub fn args() -> Args {
- imp::args()
+ // Synchronize with the store above.
+ let argv = ARGV.load(Acquire);
+ // If argv has not been initialized yet, do not return any arguments.
+ let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) };
+ let args: Vec<OsString> = (0..argc)
+ .map(|i| unsafe {
+ let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char);
+ OsStringExt::from_vec(cstr.to_bytes().to_vec())
+ })
+ .collect();
+
+ Args { iter: args.into_iter() }
}
pub struct Args {
@@ -51,44 +68,3 @@ impl DoubleEndedIterator for Args {
self.iter.next_back()
}
}
-
-mod imp {
- use super::Args;
- use crate::ffi::{CStr, OsString};
- use crate::os::unix::ffi::OsStringExt;
- use crate::ptr;
-
- use crate::sys_common::mutex::StaticMutex;
-
- static mut ARGC: isize = 0;
- static mut ARGV: *const *const u8 = ptr::null();
- static LOCK: StaticMutex = StaticMutex::new();
-
- pub unsafe fn init(argc: isize, argv: *const *const u8) {
- let _guard = LOCK.lock();
- ARGC = argc;
- ARGV = argv;
- }
-
- pub unsafe fn cleanup() {
- let _guard = LOCK.lock();
- ARGC = 0;
- ARGV = ptr::null();
- }
-
- pub fn args() -> Args {
- Args { iter: clone().into_iter() }
- }
-
- fn clone() -> Vec<OsString> {
- unsafe {
- let _guard = LOCK.lock();
- (0..ARGC)
- .map(|i| {
- let cstr = CStr::from_ptr(*ARGV.offset(i) as *const i8);
- OsStringExt::from_vec(cstr.to_bytes().to_vec())
- })
- .collect()
- }
- }
-}
diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs
deleted file mode 100644
index 22059ca0d..000000000
--- a/library/std/src/sys/hermit/condvar.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use crate::ffi::c_void;
-use crate::ptr;
-use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use crate::sys::hermit::abi;
-use crate::sys::locks::Mutex;
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
-use crate::time::Duration;
-
-// The implementation is inspired by Andrew D. Birrell's paper
-// "Implementing Condition Variables with Semaphores"
-
-pub struct Condvar {
- counter: AtomicUsize,
- sem1: *const c_void,
- sem2: *const c_void,
-}
-
-pub(crate) type MovableCondvar = LazyBox<Condvar>;
-
-impl LazyInit for Condvar {
- fn init() -> Box<Self> {
- Box::new(Self::new())
- }
-}
-
-unsafe impl Send for Condvar {}
-unsafe impl Sync for Condvar {}
-
-impl Condvar {
- pub fn new() -> Self {
- let mut condvar =
- Self { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() };
- unsafe {
- let _ = abi::sem_init(&mut condvar.sem1, 0);
- let _ = abi::sem_init(&mut condvar.sem2, 0);
- }
- condvar
- }
-
- pub unsafe fn notify_one(&self) {
- if self.counter.load(SeqCst) > 0 {
- self.counter.fetch_sub(1, SeqCst);
- abi::sem_post(self.sem1);
- abi::sem_timedwait(self.sem2, 0);
- }
- }
-
- pub unsafe fn notify_all(&self) {
- let counter = self.counter.swap(0, SeqCst);
- for _ in 0..counter {
- abi::sem_post(self.sem1);
- }
- for _ in 0..counter {
- abi::sem_timedwait(self.sem2, 0);
- }
- }
-
- pub unsafe fn wait(&self, mutex: &Mutex) {
- self.counter.fetch_add(1, SeqCst);
- mutex.unlock();
- abi::sem_timedwait(self.sem1, 0);
- abi::sem_post(self.sem2);
- mutex.lock();
- }
-
- pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
- self.counter.fetch_add(1, SeqCst);
- mutex.unlock();
- let millis = dur.as_millis().min(u32::MAX as u128) as u32;
-
- let res = if millis > 0 {
- abi::sem_timedwait(self.sem1, millis)
- } else {
- abi::sem_trywait(self.sem1)
- };
-
- abi::sem_post(self.sem2);
- mutex.lock();
- res == 0
- }
-}
-
-impl Drop for Condvar {
- fn drop(&mut self) {
- unsafe {
- let _ = abi::sem_destroy(self.sem1);
- let _ = abi::sem_destroy(self.sem2);
- }
- }
-}
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
index fa9a7fb19..af297ff1e 100644
--- a/library/std/src/sys/hermit/fs.rs
+++ b/library/std/src/sys/hermit/fs.rs
@@ -1,10 +1,12 @@
+use crate::convert::TryFrom;
use crate::ffi::{CStr, CString, OsString};
use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::io::{self, Error, ErrorKind};
-use crate::io::{IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::os::unix::ffi::OsStrExt;
use crate::path::{Path, PathBuf};
+use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::cvt;
use crate::sys::hermit::abi;
use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
@@ -15,10 +17,6 @@ use crate::sys::unsupported;
pub use crate::sys_common::fs::{copy, try_exists};
//pub use crate::sys_common::fs::remove_dir_all;
-fn cstr(path: &Path) -> io::Result<CString> {
- Ok(CString::new(path.as_os_str().as_bytes())?)
-}
-
#[derive(Debug)]
pub struct File(FileDesc);
@@ -41,6 +39,9 @@ pub struct OpenOptions {
mode: i32,
}
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
pub struct FilePermissions(!);
pub struct FileType(!);
@@ -110,6 +111,11 @@ impl fmt::Debug for FilePermissions {
}
}
+impl FileTimes {
+ pub fn set_accessed(&mut self, _t: SystemTime) {}
+ pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
impl FileType {
pub fn is_dir(&self) -> bool {
self.0
@@ -264,8 +270,7 @@ impl OpenOptions {
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
- let path = cstr(path)?;
- File::open_c(&path, opts)
+ run_path_with_cstr(path, |path| File::open_c(&path, opts))
}
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
@@ -312,8 +317,8 @@ impl File {
false
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- crate::io::default_read_buf(|buf| self.read(buf), buf)
+ pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|buf| self.read(buf), cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
@@ -344,6 +349,10 @@ impl File {
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
Err(Error::from_raw_os_error(22))
}
+
+ pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
}
impl DirBuilder {
@@ -361,9 +370,7 @@ pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
}
pub fn unlink(path: &Path) -> io::Result<()> {
- let name = cstr(path)?;
- let _ = unsafe { cvt(abi::unlink(name.as_ptr()))? };
- Ok(())
+ run_path_with_cstr(path, |path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ()))
}
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs
new file mode 100644
index 000000000..b64c174b0
--- /dev/null
+++ b/library/std/src/sys/hermit/futex.rs
@@ -0,0 +1,39 @@
+use super::abi;
+use crate::ptr::null;
+use crate::sync::atomic::AtomicU32;
+use crate::time::Duration;
+
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ // Calculate the timeout as a relative timespec.
+ //
+ // Overflows are rounded up to an infinite timeout (None).
+ let timespec = timeout.and_then(|dur| {
+ Some(abi::timespec {
+ tv_sec: dur.as_secs().try_into().ok()?,
+ tv_nsec: dur.subsec_nanos().into(),
+ })
+ });
+
+ let r = unsafe {
+ abi::futex_wait(
+ futex.as_mut_ptr(),
+ expected,
+ timespec.as_ref().map_or(null(), |t| t as *const abi::timespec),
+ abi::FUTEX_RELATIVE_TIMEOUT,
+ )
+ };
+
+ r != -abi::errno::ETIMEDOUT
+}
+
+#[inline]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ unsafe { abi::futex_wake(futex.as_mut_ptr(), 1) > 0 }
+}
+
+#[inline]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ unsafe {
+ abi::futex_wake(futex.as_mut_ptr(), i32::MAX);
+ }
+}
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index 60b7a973c..e6534df89 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -25,6 +25,7 @@ pub mod cmath;
pub mod env;
pub mod fd;
pub mod fs;
+pub mod futex;
#[path = "../unsupported/io.rs"]
pub mod io;
pub mod memchr;
@@ -45,14 +46,14 @@ pub mod thread_local_dtor;
pub mod thread_local_key;
pub mod time;
-mod condvar;
-mod mutex;
-mod rwlock;
-
+#[path = "../unix/locks"]
pub mod locks {
- pub use super::condvar::*;
- pub use super::mutex::*;
- pub use super::rwlock::*;
+ mod futex_condvar;
+ mod futex_mutex;
+ mod futex_rwlock;
+ pub(crate) use futex_condvar::MovableCondvar;
+ pub(crate) use futex_mutex::{MovableMutex, Mutex};
+ pub(crate) use futex_rwlock::{MovableRwLock, RwLock};
}
use crate::io::ErrorKind;
@@ -98,16 +99,14 @@ pub extern "C" fn __rust_abort() {
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-pub unsafe fn init(argc: isize, argv: *const *const u8) {
+pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
let _ = net::init();
args::init(argc, argv);
}
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
-pub unsafe fn cleanup() {
- args::cleanup();
-}
+pub unsafe fn cleanup() {}
#[cfg(not(test))]
#[no_mangle]
diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs
deleted file mode 100644
index eb15a04ff..000000000
--- a/library/std/src/sys/hermit/mutex.rs
+++ /dev/null
@@ -1,216 +0,0 @@
-use crate::cell::UnsafeCell;
-use crate::collections::VecDeque;
-use crate::hint;
-use crate::ops::{Deref, DerefMut, Drop};
-use crate::ptr;
-use crate::sync::atomic::{AtomicUsize, Ordering};
-use crate::sys::hermit::abi;
-
-/// This type provides a lock based on busy waiting to realize mutual exclusion
-///
-/// # Description
-///
-/// This structure behaves a lot like a common mutex. There are some differences:
-///
-/// - By using busy waiting, it can be used outside the runtime.
-/// - It is a so called ticket lock and is completely fair.
-#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
-#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
-struct Spinlock<T: ?Sized> {
- queue: AtomicUsize,
- dequeue: AtomicUsize,
- data: UnsafeCell<T>,
-}
-
-unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
-unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}
-
-/// A guard to which the protected data can be accessed
-///
-/// When the guard falls out of scope it will release the lock.
-struct SpinlockGuard<'a, T: ?Sized + 'a> {
- dequeue: &'a AtomicUsize,
- data: &'a mut T,
-}
-
-impl<T> Spinlock<T> {
- pub const fn new(user_data: T) -> Spinlock<T> {
- Spinlock {
- queue: AtomicUsize::new(0),
- dequeue: AtomicUsize::new(1),
- data: UnsafeCell::new(user_data),
- }
- }
-
- #[inline]
- fn obtain_lock(&self) {
- let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
- let mut counter: u16 = 0;
- while self.dequeue.load(Ordering::SeqCst) != ticket {
- counter += 1;
- if counter < 100 {
- hint::spin_loop();
- } else {
- counter = 0;
- unsafe {
- abi::yield_now();
- }
- }
- }
- }
-
- #[inline]
- pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
- self.obtain_lock();
- SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
- }
-}
-
-impl<T: ?Sized + Default> Default for Spinlock<T> {
- fn default() -> Spinlock<T> {
- Spinlock::new(Default::default())
- }
-}
-
-impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
- type Target = T;
- fn deref(&self) -> &T {
- &*self.data
- }
-}
-
-impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
- fn deref_mut(&mut self) -> &mut T {
- &mut *self.data
- }
-}
-
-impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
- /// The dropping of the SpinlockGuard will release the lock it was created from.
- fn drop(&mut self) {
- self.dequeue.fetch_add(1, Ordering::SeqCst);
- }
-}
-
-/// Realize a priority queue for tasks
-struct PriorityQueue {
- queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
- prio_bitmap: u64,
-}
-
-impl PriorityQueue {
- pub const fn new() -> PriorityQueue {
- PriorityQueue {
- queues: [
- None, None, None, None, None, None, None, None, None, None, None, None, None, None,
- None, None, None, None, None, None, None, None, None, None, None, None, None, None,
- None, None, None,
- ],
- prio_bitmap: 0,
- }
- }
-
- /// Add a task id by its priority to the queue
- pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
- let i: usize = prio.into().into();
- self.prio_bitmap |= (1 << i) as u64;
- if let Some(queue) = &mut self.queues[i] {
- queue.push_back(id);
- } else {
- let mut queue = VecDeque::new();
- queue.push_back(id);
- self.queues[i] = Some(queue);
- }
- }
-
- fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
- if let Some(queue) = &mut self.queues[queue_index] {
- let id = queue.pop_front();
-
- if queue.is_empty() {
- self.prio_bitmap &= !(1 << queue_index as u64);
- }
-
- id
- } else {
- None
- }
- }
-
- /// Pop the task handle with the highest priority from the queue
- pub fn pop(&mut self) -> Option<abi::Tid> {
- for i in 0..abi::NO_PRIORITIES {
- if self.prio_bitmap & (1 << i) != 0 {
- return self.pop_from_queue(i);
- }
- }
-
- None
- }
-}
-
-struct MutexInner {
- locked: bool,
- blocked_task: PriorityQueue,
-}
-
-impl MutexInner {
- pub const fn new() -> MutexInner {
- MutexInner { locked: false, blocked_task: PriorityQueue::new() }
- }
-}
-
-pub struct Mutex {
- inner: Spinlock<MutexInner>,
-}
-
-pub type MovableMutex = Mutex;
-
-unsafe impl Send for Mutex {}
-unsafe impl Sync for Mutex {}
-
-impl Mutex {
- pub const fn new() -> Mutex {
- Mutex { inner: Spinlock::new(MutexInner::new()) }
- }
-
- #[inline]
- pub unsafe fn init(&mut self) {}
-
- #[inline]
- pub unsafe fn lock(&self) {
- loop {
- let mut guard = self.inner.lock();
- if guard.locked == false {
- guard.locked = true;
- return;
- } else {
- let prio = abi::get_priority();
- let id = abi::getpid();
-
- guard.blocked_task.push(prio, id);
- abi::block_current_task();
- drop(guard);
- abi::yield_now();
- }
- }
- }
-
- #[inline]
- pub unsafe fn unlock(&self) {
- let mut guard = self.inner.lock();
- guard.locked = false;
- if let Some(tid) = guard.blocked_task.pop() {
- abi::wakeup_task(tid);
- }
- }
-
- #[inline]
- pub unsafe fn try_lock(&self) -> bool {
- let mut guard = self.inner.lock();
- if guard.locked == false {
- guard.locked = true;
- }
- guard.locked
- }
-}
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
index 745476171..8a13879d8 100644
--- a/library/std/src/sys/hermit/net.rs
+++ b/library/std/src/sys/hermit/net.rs
@@ -487,6 +487,4 @@ pub mod netc {
#[derive(Copy, Clone)]
pub struct sockaddr {}
-
- pub type socklen_t = usize;
}
diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs
deleted file mode 100644
index 9701bab1f..000000000
--- a/library/std/src/sys/hermit/rwlock.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use crate::cell::UnsafeCell;
-use crate::sys::locks::{MovableCondvar, Mutex};
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
-
-pub struct RwLock {
- lock: Mutex,
- cond: MovableCondvar,
- state: UnsafeCell<State>,
-}
-
-pub type MovableRwLock = RwLock;
-
-enum State {
- Unlocked,
- Reading(usize),
- Writing,
-}
-
-unsafe impl Send for RwLock {}
-unsafe impl Sync for RwLock {}
-
-// This rwlock implementation is a relatively simple implementation which has a
-// condition variable for readers/writers as well as a mutex protecting the
-// internal state of the lock. A current downside of the implementation is that
-// unlocking the lock will notify *all* waiters rather than just readers or just
-// writers. This can cause lots of "thundering stampede" problems. While
-// hopefully correct this implementation is very likely to want to be changed in
-// the future.
-
-impl RwLock {
- pub const fn new() -> RwLock {
- RwLock {
- lock: Mutex::new(),
- cond: MovableCondvar::new(),
- state: UnsafeCell::new(State::Unlocked),
- }
- }
-
- #[inline]
- pub unsafe fn read(&self) {
- self.lock.lock();
- while !(*self.state.get()).inc_readers() {
- self.cond.wait(&self.lock);
- }
- self.lock.unlock();
- }
-
- #[inline]
- pub unsafe fn try_read(&self) -> bool {
- self.lock.lock();
- let ok = (*self.state.get()).inc_readers();
- self.lock.unlock();
- return ok;
- }
-
- #[inline]
- pub unsafe fn write(&self) {
- self.lock.lock();
- while !(*self.state.get()).inc_writers() {
- self.cond.wait(&self.lock);
- }
- self.lock.unlock();
- }
-
- #[inline]
- pub unsafe fn try_write(&self) -> bool {
- self.lock.lock();
- let ok = (*self.state.get()).inc_writers();
- self.lock.unlock();
- return ok;
- }
-
- #[inline]
- pub unsafe fn read_unlock(&self) {
- self.lock.lock();
- let notify = (*self.state.get()).dec_readers();
- self.lock.unlock();
- if notify {
- // FIXME: should only wake up one of these some of the time
- self.cond.notify_all();
- }
- }
-
- #[inline]
- pub unsafe fn write_unlock(&self) {
- self.lock.lock();
- (*self.state.get()).dec_writers();
- self.lock.unlock();
- // FIXME: should only wake up one of these some of the time
- self.cond.notify_all();
- }
-}
-
-impl State {
- fn inc_readers(&mut self) -> bool {
- match *self {
- State::Unlocked => {
- *self = State::Reading(1);
- true
- }
- State::Reading(ref mut cnt) => {
- *cnt += 1;
- true
- }
- State::Writing => false,
- }
- }
-
- fn inc_writers(&mut self) -> bool {
- match *self {
- State::Unlocked => {
- *self = State::Writing;
- true
- }
- State::Reading(_) | State::Writing => false,
- }
- }
-
- fn dec_readers(&mut self) -> bool {
- let zero = match *self {
- State::Reading(ref mut cnt) => {
- *cnt -= 1;
- *cnt == 0
- }
- State::Unlocked | State::Writing => invalid(),
- };
- if zero {
- *self = State::Unlocked;
- }
- zero
- }
-
- fn dec_writers(&mut self) {
- match *self {
- State::Writing => {}
- State::Unlocked | State::Reading(_) => invalid(),
- }
- *self = State::Unlocked;
- }
-}
-
-fn invalid() -> ! {
- panic!("inconsistent rwlock");
-}
diff --git a/library/std/src/sys/itron/mutex.rs b/library/std/src/sys/itron/mutex.rs
index 715e94c3b..085662e6d 100644
--- a/library/std/src/sys/itron/mutex.rs
+++ b/library/std/src/sys/itron/mutex.rs
@@ -31,12 +31,6 @@ impl Mutex {
Mutex { mtx: SpinIdOnceCell::new() }
}
- pub unsafe fn init(&mut self) {
- // Initialize `self.mtx` eagerly
- let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx"));
- unsafe { self.mtx.set_unchecked((id, ())) };
- }
-
/// Get the inner mutex's ID, which is lazily created.
fn raw(&self) -> abi::ID {
match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) {
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index 167c918c9..c080c176a 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -22,7 +22,7 @@
#![allow(missing_debug_implementations)]
-mod common;
+pub mod common;
cfg_if::cfg_if! {
if #[cfg(unix)] {
diff --git a/library/std/src/sys/sgx/abi/thread.rs b/library/std/src/sys/sgx/abi/thread.rs
index ef55b821a..2b23e368c 100644
--- a/library/std/src/sys/sgx/abi/thread.rs
+++ b/library/std/src/sys/sgx/abi/thread.rs
@@ -7,7 +7,11 @@ use fortanix_sgx_abi::Tcs;
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn current() -> Tcs {
extern "C" {
- fn get_tcs_addr() -> Tcs;
+ fn get_tcs_addr() -> *mut u8;
+ }
+ let addr = unsafe { get_tcs_addr() };
+ match Tcs::new(addr) {
+ Some(tcs) => tcs,
+ None => rtabort!("TCS must not be placed at address zero (this is a linker error)"),
}
- unsafe { get_tcs_addr() }
}
diff --git a/library/std/src/sys/sgx/abi/tls/mod.rs b/library/std/src/sys/sgx/abi/tls/mod.rs
index 13d96e9a6..09c4ab3d3 100644
--- a/library/std/src/sys/sgx/abi/tls/mod.rs
+++ b/library/std/src/sys/sgx/abi/tls/mod.rs
@@ -111,6 +111,7 @@ impl Tls {
rtabort!("TLS limit exceeded")
};
TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed);
+ unsafe { Self::current() }.data[index].set(ptr::null_mut());
Key::from_index(index)
}
diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
index ea24fedd0..0d934318c 100644
--- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs
+++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
@@ -56,6 +56,8 @@ unsafe impl UserSafeSized for Usercall {}
#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe impl UserSafeSized for Return {}
#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl UserSafeSized for Cancel {}
+#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe impl<T: UserSafeSized> UserSafeSized for [T; 2] {}
/// A type that can be represented in memory as one or more `UserSafeSized`s.
@@ -115,7 +117,7 @@ pub unsafe trait UserSafe {
/// * the pointer is null.
/// * the pointed-to range is not in user memory.
unsafe fn check_ptr(ptr: *const Self) {
- let is_aligned = |p| -> bool { 0 == (p as usize) & (Self::align_of() - 1) };
+ let is_aligned = |p: *const u8| -> bool { p.is_aligned_to(Self::align_of()) };
assert!(is_aligned(ptr as *const u8));
assert!(is_user_range(ptr as _, mem::size_of_val(unsafe { &*ptr })));
@@ -305,6 +307,34 @@ where
}
}
+// Split a memory region ptr..ptr + len into three parts:
+// +--------+
+// | small0 | Chunk smaller than 8 bytes
+// +--------+
+// | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
+// +--------+
+// | small1 | Chunk smaller than 8 bytes
+// +--------+
+fn region_as_aligned_chunks(ptr: *const u8, len: usize) -> (usize, usize, usize) {
+ let small0_size = if ptr.is_aligned_to(8) { 0 } else { 8 - ptr.addr() % 8 };
+ let small1_size = (len - small0_size) % 8;
+ let big_size = len - small0_size - small1_size;
+
+ (small0_size, big_size, small1_size)
+}
+
+unsafe fn copy_quadwords(src: *const u8, dst: *mut u8, len: usize) {
+ unsafe {
+ asm!(
+ "rep movsq (%rsi), (%rdi)",
+ inout("rcx") len / 8 => _,
+ inout("rdi") dst => _,
+ inout("rsi") src => _,
+ options(att_syntax, nostack, preserves_flags)
+ );
+ }
+}
+
/// Copies `len` bytes of data from enclave pointer `src` to userspace `dst`
///
/// This function mitigates stale data vulnerabilities by ensuring all writes to untrusted memory are either:
@@ -334,8 +364,8 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
mfence
lfence
",
- val = in(reg_byte) *src.offset(off as isize),
- dst = in(reg) dst.offset(off as isize),
+ val = in(reg_byte) *src.add(off),
+ dst = in(reg) dst.add(off),
seg_sel = in(reg) &mut seg_sel,
options(nostack, att_syntax)
);
@@ -343,34 +373,23 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
}
}
- unsafe fn copy_aligned_quadwords_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
- unsafe {
- asm!(
- "rep movsq (%rsi), (%rdi)",
- inout("rcx") len / 8 => _,
- inout("rdi") dst => _,
- inout("rsi") src => _,
- options(att_syntax, nostack, preserves_flags)
- );
- }
- }
assert!(!src.is_null());
assert!(!dst.is_null());
assert!(is_enclave_range(src, len));
assert!(is_user_range(dst, len));
assert!(len < isize::MAX as usize);
- assert!(!(src as usize).overflowing_add(len).1);
- assert!(!(dst as usize).overflowing_add(len).1);
+ assert!(!src.addr().overflowing_add(len).1);
+ assert!(!dst.addr().overflowing_add(len).1);
if len < 8 {
// Can't align on 8 byte boundary: copy safely byte per byte
unsafe {
copy_bytewise_to_userspace(src, dst, len);
}
- } else if len % 8 == 0 && dst as usize % 8 == 0 {
+ } else if len % 8 == 0 && dst.is_aligned_to(8) {
// Copying 8-byte aligned quadwords: copy quad word per quad word
unsafe {
- copy_aligned_quadwords_to_userspace(src, dst, len);
+ copy_quadwords(src, dst, len);
}
} else {
// Split copies into three parts:
@@ -381,25 +400,121 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
// +--------+
// | small1 | Chunk smaller than 8 bytes
// +--------+
+ let (small0_size, big_size, small1_size) = region_as_aligned_chunks(dst, len);
unsafe {
// Copy small0
- let small0_size = (8 - dst as usize % 8) as u8;
- let small0_src = src;
- let small0_dst = dst;
- copy_bytewise_to_userspace(small0_src as _, small0_dst, small0_size as _);
+ copy_bytewise_to_userspace(src, dst, small0_size);
// Copy big
- let small1_size = ((len - small0_size as usize) % 8) as u8;
- let big_size = len - small0_size as usize - small1_size as usize;
- let big_src = src.offset(small0_size as _);
- let big_dst = dst.offset(small0_size as _);
- copy_aligned_quadwords_to_userspace(big_src as _, big_dst, big_size);
+ let big_src = src.add(small0_size);
+ let big_dst = dst.add(small0_size);
+ copy_quadwords(big_src, big_dst, big_size);
// Copy small1
- let small1_src = src.offset(big_size as isize + small0_size as isize);
- let small1_dst = dst.offset(big_size as isize + small0_size as isize);
- copy_bytewise_to_userspace(small1_src, small1_dst, small1_size as _);
+ let small1_src = src.add(big_size + small0_size);
+ let small1_dst = dst.add(big_size + small0_size);
+ copy_bytewise_to_userspace(small1_src, small1_dst, small1_size);
+ }
+ }
+}
+
+/// Copies `len` bytes of data from userspace pointer `src` to enclave pointer `dst`
+///
+/// This function mitigates AEPIC leak vulnerabilities by ensuring all reads from untrusted memory are 8-byte aligned
+///
+/// # Panics
+/// This function panics if:
+///
+/// * The `src` pointer is null
+/// * The `dst` pointer is null
+/// * The `src` memory range is not in user memory
+/// * The `dst` memory range is not in enclave memory
+///
+/// # References
+/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00657.html
+/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/stale-data-read-from-xapic.html
+pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ // Copies memory region `src..src + len` to the enclave at `dst`. The source memory region
+ // is:
+ // - strictly less than 8 bytes in size and may be
+ // - located at a misaligned memory location
+ fn copy_misaligned_chunk_to_enclave(src: *const u8, dst: *mut u8, len: usize) {
+ let mut tmp_buff = [0u8; 16];
+
+ unsafe {
+ // Compute an aligned memory region to read from
+ // +--------+ <-- aligned_src + aligned_len (8B-aligned)
+ // | pad1 |
+ // +--------+ <-- src + len (misaligned)
+ // | |
+ // | |
+ // | |
+ // +--------+ <-- src (misaligned)
+ // | pad0 |
+ // +--------+ <-- aligned_src (8B-aligned)
+ let pad0_size = src as usize % 8;
+ let aligned_src = src.sub(pad0_size);
+
+ let pad1_size = 8 - (src.add(len) as usize % 8);
+ let aligned_len = pad0_size + len + pad1_size;
+
+ debug_assert!(len < 8);
+ debug_assert_eq!(aligned_src as usize % 8, 0);
+ debug_assert_eq!(aligned_len % 8, 0);
+ debug_assert!(aligned_len <= 16);
+
+ // Copy the aligned buffer to a temporary buffer
+ // Note: copying from a slightly different memory location is a bit odd. In this case it
+ // can't lead to page faults or inadvertent copying from the enclave as we only ensured
+ // that the `src` pointer is aligned at an 8 byte boundary. As pages are 4096 bytes
+ // aligned, `aligned_src` must be on the same page as `src`. A similar argument can be made
+ // for `src + len`
+ copy_quadwords(aligned_src as _, tmp_buff.as_mut_ptr(), aligned_len);
+
+ // Copy the correct parts of the temporary buffer to the destination
+ ptr::copy(tmp_buff.as_ptr().add(pad0_size), dst, len);
+ }
+ }
+
+ assert!(!src.is_null());
+ assert!(!dst.is_null());
+ assert!(is_user_range(src, len));
+ assert!(is_enclave_range(dst, len));
+ assert!(!(src as usize).overflowing_add(len + 8).1);
+ assert!(!(dst as usize).overflowing_add(len + 8).1);
+
+ if len < 8 {
+ copy_misaligned_chunk_to_enclave(src, dst, len);
+ } else if len % 8 == 0 && src as usize % 8 == 0 {
+ // Copying 8-byte aligned quadwords: copy quad word per quad word
+ unsafe {
+ copy_quadwords(src, dst, len);
+ }
+ } else {
+ // Split copies into three parts:
+ // +--------+
+ // | small0 | Chunk smaller than 8 bytes
+ // +--------+
+ // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
+ // +--------+
+ // | small1 | Chunk smaller than 8 bytes
+ // +--------+
+ let (small0_size, big_size, small1_size) = region_as_aligned_chunks(dst, len);
+
+ unsafe {
+ // Copy small0
+ copy_misaligned_chunk_to_enclave(src, dst, small0_size);
+
+ // Copy big
+ let big_src = src.add(small0_size);
+ let big_dst = dst.add(small0_size);
+ copy_quadwords(big_src, big_dst, big_size);
+
+ // Copy small1
+ let small1_src = src.add(big_size + small0_size);
+ let small1_dst = dst.add(big_size + small0_size);
+ copy_misaligned_chunk_to_enclave(small1_src, small1_dst, small1_size);
}
}
}
@@ -468,7 +583,7 @@ where
pub fn copy_to_enclave(&self, dest: &mut T) {
unsafe {
assert_eq!(mem::size_of_val(dest), mem::size_of_val(&*self.0.get()));
- ptr::copy(
+ copy_from_userspace(
self.0.get() as *const T as *const u8,
dest as *mut T as *mut u8,
mem::size_of_val(dest),
@@ -494,7 +609,11 @@ where
{
/// Copies the value from user memory into enclave memory.
pub fn to_enclave(&self) -> T {
- unsafe { ptr::read(self.0.get()) }
+ unsafe {
+ let mut data: T = mem::MaybeUninit::uninit().assume_init();
+ copy_from_userspace(self.0.get() as _, &mut data as *mut T as _, mem::size_of::<T>());
+ data
+ }
}
}
diff --git a/library/std/src/sys/sgx/abi/usercalls/mod.rs b/library/std/src/sys/sgx/abi/usercalls/mod.rs
index 79d1db5e1..e19e84326 100644
--- a/library/std/src/sys/sgx/abi/usercalls/mod.rs
+++ b/library/std/src/sys/sgx/abi/usercalls/mod.rs
@@ -292,12 +292,17 @@ fn check_os_error(err: Result) -> i32 {
}
}
-trait FromSgxResult {
+/// Translate the raw result of an SGX usercall.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub trait FromSgxResult {
+ /// Return type
type Return;
+ /// Translate the raw result of an SGX usercall.
fn from_sgx_result(self) -> IoResult<Self::Return>;
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl<T> FromSgxResult for (Result, T) {
type Return = T;
@@ -310,6 +315,7 @@ impl<T> FromSgxResult for (Result, T) {
}
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl FromSgxResult for Result {
type Return = ();
diff --git a/library/std/src/sys/sgx/abi/usercalls/raw.rs b/library/std/src/sys/sgx/abi/usercalls/raw.rs
index 4267b96cc..10c1456d4 100644
--- a/library/std/src/sys/sgx/abi/usercalls/raw.rs
+++ b/library/std/src/sys/sgx/abi/usercalls/raw.rs
@@ -37,14 +37,23 @@ pub unsafe fn do_usercall(
(a, b)
}
-type Register = u64;
+/// A value passed or returned in a CPU register.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub type Register = u64;
-trait RegisterArgument {
+/// Translate a type from/to Register to be used as an argument.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub trait RegisterArgument {
+ /// Translate a Register to Self.
fn from_register(_: Register) -> Self;
+ /// Translate self to a Register.
fn into_register(self) -> Register;
}
-trait ReturnValue {
+/// Translate a pair of Registers to the raw usercall return value.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub trait ReturnValue {
+ /// Translate a pair of Registers to the raw usercall return value.
fn from_registers(call: &'static str, regs: (Register, Register)) -> Self;
}
@@ -68,6 +77,7 @@ macro_rules! define_usercalls {
macro_rules! define_ra {
(< $i:ident > $t:ty) => {
+ #[unstable(feature = "sgx_platform", issue = "56975")]
impl<$i> RegisterArgument for $t {
fn from_register(a: Register) -> Self {
a as _
@@ -78,6 +88,7 @@ macro_rules! define_ra {
}
};
($i:ty as $t:ty) => {
+ #[unstable(feature = "sgx_platform", issue = "56975")]
impl RegisterArgument for $t {
fn from_register(a: Register) -> Self {
a as $i as _
@@ -88,6 +99,7 @@ macro_rules! define_ra {
}
};
($t:ty) => {
+ #[unstable(feature = "sgx_platform", issue = "56975")]
impl RegisterArgument for $t {
fn from_register(a: Register) -> Self {
a as _
@@ -112,6 +124,7 @@ define_ra!(usize as isize);
define_ra!(<T> *const T);
define_ra!(<T> *mut T);
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl RegisterArgument for bool {
fn from_register(a: Register) -> bool {
if a != 0 { true } else { false }
@@ -121,6 +134,7 @@ impl RegisterArgument for bool {
}
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> {
fn from_register(a: Register) -> Option<NonNull<T>> {
NonNull::new(a as _)
@@ -130,12 +144,14 @@ impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> {
}
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl ReturnValue for ! {
fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self {
rtabort!("Usercall {call}: did not expect to be re-entered");
}
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl ReturnValue for () {
fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self {
rtassert!(usercall_retval.0 == 0);
@@ -144,6 +160,7 @@ impl ReturnValue for () {
}
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl<T: RegisterArgument> ReturnValue for T {
fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self {
rtassert!(usercall_retval.1 == 0);
@@ -151,6 +168,7 @@ impl<T: RegisterArgument> ReturnValue for T {
}
}
+#[unstable(feature = "sgx_platform", issue = "56975")]
impl<T: RegisterArgument, U: RegisterArgument> ReturnValue for (T, U) {
fn from_registers(_call: &'static str, regs: (Register, Register)) -> Self {
(T::from_register(regs.0), U::from_register(regs.1))
diff --git a/library/std/src/sys/sgx/abi/usercalls/tests.rs b/library/std/src/sys/sgx/abi/usercalls/tests.rs
index cbf7d7d54..58b8eb215 100644
--- a/library/std/src/sys/sgx/abi/usercalls/tests.rs
+++ b/library/std/src/sys/sgx/abi/usercalls/tests.rs
@@ -1,8 +1,8 @@
-use super::alloc::copy_to_userspace;
use super::alloc::User;
+use super::alloc::{copy_from_userspace, copy_to_userspace};
#[test]
-fn test_copy_function() {
+fn test_copy_to_userspace_function() {
let mut src = [0u8; 100];
let mut dst = User::<[u8]>::uninitialized(100);
@@ -17,12 +17,38 @@ fn test_copy_function() {
dst.copy_from_enclave(&[0u8; 100]);
// Copy src[0..size] to dst + offset
- unsafe { copy_to_userspace(src.as_ptr(), dst.as_mut_ptr().offset(offset), size) };
+ unsafe { copy_to_userspace(src.as_ptr(), dst.as_mut_ptr().add(offset), size) };
// Verify copy
for byte in 0..size {
unsafe {
- assert_eq!(*dst.as_ptr().offset(offset + byte as isize), src[byte as usize]);
+ assert_eq!(*dst.as_ptr().add(offset + byte), src[byte as usize]);
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn test_copy_from_userspace_function() {
+ let mut dst = [0u8; 100];
+ let mut src = User::<[u8]>::uninitialized(100);
+
+ src.copy_from_enclave(&[0u8; 100]);
+
+ for size in 0..48 {
+ // For all possible alignment
+ for offset in 0..8 {
+ // overwrite complete dst
+ dst = [0u8; 100];
+
+ // Copy src[0..size] to dst + offset
+ unsafe { copy_from_userspace(src.as_ptr().offset(offset), dst.as_mut_ptr(), size) };
+
+ // Verify copy
+ for byte in 0..size {
+ unsafe {
+ assert_eq!(dst[byte as usize], *src.as_ptr().offset(offset + byte as isize));
}
}
}
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
index 696400670..b1d32929e 100644
--- a/library/std/src/sys/sgx/mod.rs
+++ b/library/std/src/sys/sgx/mod.rs
@@ -47,7 +47,7 @@ pub mod locks {
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-pub unsafe fn init(argc: isize, argv: *const *const u8) {
+pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
unsafe {
args::init(argc, argv);
}
diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs
index 513cd77fd..aa747d56b 100644
--- a/library/std/src/sys/sgx/mutex.rs
+++ b/library/std/src/sys/sgx/mutex.rs
@@ -21,9 +21,6 @@ impl Mutex {
}
#[inline]
- pub unsafe fn init(&mut self) {}
-
- #[inline]
pub unsafe fn lock(&self) {
let mut guard = self.inner.lock();
if *guard.lock_var() {
diff --git a/library/std/src/sys/sgx/thread_local_key.rs b/library/std/src/sys/sgx/thread_local_key.rs
index b21784475..c7a57d3a3 100644
--- a/library/std/src/sys/sgx/thread_local_key.rs
+++ b/library/std/src/sys/sgx/thread_local_key.rs
@@ -21,8 +21,3 @@ pub unsafe fn get(key: Key) -> *mut u8 {
pub unsafe fn destroy(key: Key) {
Tls::destroy(AbiKey::from_usize(key))
}
-
-#[inline]
-pub fn requires_synchronized_create() -> bool {
- false
-}
diff --git a/library/std/src/sys/solid/fs.rs b/library/std/src/sys/solid/fs.rs
index a2cbee4dc..6c66b93a3 100644
--- a/library/std/src/sys/solid/fs.rs
+++ b/library/std/src/sys/solid/fs.rs
@@ -2,7 +2,7 @@ use super::{abi, error};
use crate::{
ffi::{CStr, CString, OsStr, OsString},
fmt,
- io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom},
+ io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom},
mem::MaybeUninit,
os::raw::{c_int, c_short},
os::solid::ffi::OsStrExt,
@@ -77,6 +77,9 @@ pub struct OpenOptions {
custom_flags: i32,
}
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions(c_short);
@@ -126,6 +129,11 @@ impl FilePermissions {
}
}
+impl FileTimes {
+ pub fn set_accessed(&mut self, _t: SystemTime) {}
+ pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
impl FileType {
pub fn is_dir(&self) -> bool {
self.is(abi::S_IFDIR)
@@ -167,15 +175,19 @@ impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
- unsafe {
- let mut out_dirent = MaybeUninit::uninit();
- error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
+ let entry = unsafe {
+ let mut out_entry = MaybeUninit::uninit();
+ match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
self.inner.dirp,
- out_dirent.as_mut_ptr(),
- ))
- .ok()?;
- Some(Ok(DirEntry { entry: out_dirent.assume_init(), inner: Arc::clone(&self.inner) }))
- }
+ out_entry.as_mut_ptr(),
+ )) {
+ Ok(_) => out_entry.assume_init(),
+ Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None,
+ Err(e) => return Some(Err(e.as_io_error())),
+ }
+ };
+
+ (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, inner: Arc::clone(&self.inner) }))
}
}
@@ -358,13 +370,13 @@ impl File {
}
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
unsafe {
- let len = buf.remaining();
+ let len = cursor.capacity();
let mut out_num_bytes = MaybeUninit::uninit();
error::SolidError::err_if_negative(abi::SOLID_FS_Read(
self.fd.raw(),
- buf.unfilled_mut().as_mut_ptr() as *mut u8,
+ cursor.as_mut().as_mut_ptr() as *mut u8,
len,
out_num_bytes.as_mut_ptr(),
))
@@ -376,9 +388,7 @@ impl File {
// Safety: `num_bytes_read` bytes were written to the unfilled
// portion of the buffer
- buf.assume_init(num_bytes_read);
-
- buf.add_filled(num_bytes_read);
+ cursor.advance(num_bytes_read);
Ok(())
}
@@ -452,6 +462,10 @@ impl File {
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
unsupported()
}
+
+ pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+ unsupported()
+ }
}
impl Drop for File {
diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs
index 778a589d1..5867979a2 100644
--- a/library/std/src/sys/solid/mod.rs
+++ b/library/std/src/sys/solid/mod.rs
@@ -56,7 +56,7 @@ pub mod locks {
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
// SAFETY: must be called only once during runtime cleanup.
pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs
index b5649d6e0..4906c6268 100644
--- a/library/std/src/sys/solid/os.rs
+++ b/library/std/src/sys/solid/os.rs
@@ -1,4 +1,5 @@
use super::unsupported;
+use crate::convert::TryFrom;
use crate::error::Error as StdError;
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
@@ -8,7 +9,8 @@ use crate::os::{
solid::ffi::{OsStrExt, OsStringExt},
};
use crate::path::{self, PathBuf};
-use crate::sys_common::rwlock::StaticRwLock;
+use crate::sync::RwLock;
+use crate::sys::common::small_c_string::run_with_cstr;
use crate::vec;
use super::{error, itron, memchr};
@@ -78,7 +80,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
-static ENV_LOCK: StaticRwLock = StaticRwLock::new();
+static ENV_LOCK: RwLock<()> = RwLock::new(());
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
@@ -139,35 +141,33 @@ pub fn env() -> Env {
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
- let k = CString::new(k.as_bytes()).ok()?;
- unsafe {
+ let s = run_with_cstr(k.as_bytes(), |k| {
let _guard = ENV_LOCK.read();
- let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
- if s.is_null() {
- None
- } else {
- Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
- }
+ Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char)
+ })
+ .ok()?;
+
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec()))
}
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
- let k = CString::new(k.as_bytes())?;
- let v = CString::new(v.as_bytes())?;
-
- unsafe {
- let _guard = ENV_LOCK.write();
- cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
- }
+ run_with_cstr(k.as_bytes(), |k| {
+ run_with_cstr(v.as_bytes(), |v| {
+ let _guard = ENV_LOCK.write();
+ cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
+ })
+ })
}
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
- let nbuf = CString::new(n.as_bytes())?;
-
- unsafe {
+ run_with_cstr(n.as_bytes(), |nbuf| {
let _guard = ENV_LOCK.write();
- cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
- }
+ cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
+ })
}
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
diff --git a/library/std/src/sys/solid/thread_local_key.rs b/library/std/src/sys/solid/thread_local_key.rs
index b17521f70..b37bf9996 100644
--- a/library/std/src/sys/solid/thread_local_key.rs
+++ b/library/std/src/sys/solid/thread_local_key.rs
@@ -19,8 +19,3 @@ pub unsafe fn get(_key: Key) -> *mut u8 {
pub unsafe fn destroy(_key: Key) {
panic!("should not be used on the solid target");
}
-
-#[inline]
-pub fn requires_synchronized_create() -> bool {
- panic!("should not be used on the solid target");
-}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 30812dabb..dbaa3c33e 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -4,7 +4,7 @@
mod tests;
use crate::cmp;
-use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -131,20 +131,19 @@ impl FileDesc {
}
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
let ret = cvt(unsafe {
libc::read(
self.as_raw_fd(),
- buf.unfilled_mut().as_mut_ptr() as *mut libc::c_void,
- cmp::min(buf.remaining(), READ_LIMIT),
+ cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
+ cmp::min(cursor.capacity(), READ_LIMIT),
)
})?;
// Safety: `ret` bytes were written to the initialized portion of the buffer
unsafe {
- buf.assume_init(ret as usize);
+ cursor.advance(ret as usize);
}
- buf.add_filled(ret as usize);
Ok(())
}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index b5cc8038c..37a49f2d7 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1,13 +1,26 @@
+// miri has some special hacks here that make things unused.
+#![cfg_attr(miri, allow(unused))]
+
use crate::os::unix::prelude::*;
-use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
-use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "illumos"
+))]
+use crate::mem::MaybeUninit;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
+use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::fd::FileDesc;
use crate::sys::time::SystemTime;
use crate::sys::{cvt, cvt_r};
@@ -260,7 +273,7 @@ pub struct DirEntry {
// We need to store an owned copy of the entry name on platforms that use
// readdir() (not readdir_r()), because a) struct dirent may use a flexible
// array to store the name, b) it lives only until the next readdir() call.
- name: CString,
+ name: crate::ffi::CString,
}
// Define a minimal subset of fields we need from `dirent64`, especially since
@@ -313,8 +326,11 @@ pub struct FilePermissions {
mode: mode_t,
}
-#[derive(Copy, Clone)]
-pub struct FileTimes([libc::timespec; 2]);
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+ accessed: Option<SystemTime>,
+ modified: Option<SystemTime>,
+}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FileType {
@@ -512,45 +528,11 @@ impl FilePermissions {
impl FileTimes {
pub fn set_accessed(&mut self, t: SystemTime) {
- self.0[0] = t.t.to_timespec().expect("Invalid system time");
+ self.accessed = Some(t);
}
pub fn set_modified(&mut self, t: SystemTime) {
- self.0[1] = t.t.to_timespec().expect("Invalid system time");
- }
-}
-
-struct TimespecDebugAdapter<'a>(&'a libc::timespec);
-
-impl fmt::Debug for TimespecDebugAdapter<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("timespec")
- .field("tv_sec", &self.0.tv_sec)
- .field("tv_nsec", &self.0.tv_nsec)
- .finish()
- }
-}
-
-impl fmt::Debug for FileTimes {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("FileTimes")
- .field("accessed", &TimespecDebugAdapter(&self.0[0]))
- .field("modified", &TimespecDebugAdapter(&self.0[1]))
- .finish()
- }
-}
-
-impl Default for FileTimes {
- fn default() -> Self {
- // Redox doesn't appear to support `UTIME_OMIT`, so we stub it out here, and always return
- // an error in `set_times`.
- // ESP-IDF does not support `futimens` at all and the behavior for that OS is therefore
- // the same as for Redox.
- #[cfg(any(target_os = "redox", target_os = "espidf"))]
- let omit = libc::timespec { tv_sec: 0, tv_nsec: 0 };
- #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
- let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ };
- Self([omit; 2])
+ self.modified = Some(t);
}
}
@@ -614,33 +596,69 @@ impl Iterator for ReadDir {
};
}
- // Only d_reclen bytes of *entry_ptr are valid, so we can't just copy the
- // whole thing (#93384). Instead, copy everything except the name.
- let mut copy: dirent64 = mem::zeroed();
- // Can't dereference entry_ptr, so use the local entry to get
- // offsetof(struct dirent, d_name)
- let copy_bytes = &mut copy as *mut _ as *mut u8;
- let copy_name = &mut copy.d_name as *mut _ as *mut u8;
- let name_offset = copy_name.offset_from(copy_bytes) as usize;
- let entry_bytes = entry_ptr as *const u8;
- let entry_name = entry_bytes.add(name_offset);
- ptr::copy_nonoverlapping(entry_bytes, copy_bytes, name_offset);
+ // The dirent64 struct is a weird imaginary thing that isn't ever supposed
+ // to be worked with by value. Its trailing d_name field is declared
+ // variously as [c_char; 256] or [c_char; 1] on different systems but
+ // either way that size is meaningless; only the offset of d_name is
+ // meaningful. The dirent64 pointers that libc returns from readdir64 are
+ // allowed to point to allocations smaller _or_ LARGER than implied by the
+ // definition of the struct.
+ //
+ // As such, we need to be even more careful with dirent64 than if its
+ // contents were "simply" partially initialized data.
+ //
+ // Like for uninitialized contents, converting entry_ptr to `&dirent64`
+ // would not be legal. However, unique to dirent64 is that we don't even
+ // get to use `addr_of!((*entry_ptr).d_name)` because that operation
+ // requires the full extent of *entry_ptr to be in bounds of the same
+ // allocation, which is not necessarily the case here.
+ //
+ // Absent any other way to obtain a pointer to `(*entry_ptr).d_name`
+ // legally in Rust analogously to how it would be done in C, we instead
+ // need to make our own non-libc allocation that conforms to the weird
+ // imaginary definition of dirent64, and use that for a field offset
+ // computation.
+ macro_rules! offset_ptr {
+ ($entry_ptr:expr, $field:ident) => {{
+ const OFFSET: isize = {
+ let delusion = MaybeUninit::<dirent64>::uninit();
+ let entry_ptr = delusion.as_ptr();
+ unsafe {
+ ptr::addr_of!((*entry_ptr).$field)
+ .cast::<u8>()
+ .offset_from(entry_ptr.cast::<u8>())
+ }
+ };
+ if true {
+ // Cast to the same type determined by the else branch.
+ $entry_ptr.byte_offset(OFFSET).cast::<_>()
+ } else {
+ #[allow(deref_nullptr)]
+ {
+ ptr::addr_of!((*ptr::null::<dirent64>()).$field)
+ }
+ }
+ }};
+ }
+
+ // d_name is guaranteed to be null-terminated.
+ let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast());
+ let name_bytes = name.to_bytes();
+ if name_bytes == b"." || name_bytes == b".." {
+ continue;
+ }
let entry = dirent64_min {
- d_ino: copy.d_ino as u64,
+ d_ino: *offset_ptr!(entry_ptr, d_ino) as u64,
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
- d_type: copy.d_type as u8,
+ d_type: *offset_ptr!(entry_ptr, d_type) as u8,
};
- let ret = DirEntry {
+ return Some(Ok(DirEntry {
entry,
- // d_name is guaranteed to be null-terminated.
- name: CStr::from_ptr(entry_name as *const _).to_owned(),
+ name: name.to_owned(),
dir: Arc::clone(&self.inner),
- };
- if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
- return Some(Ok(ret));
- }
+ }));
}
}
}
@@ -687,7 +705,11 @@ impl Iterator for ReadDir {
impl Drop for Dir {
fn drop(&mut self) {
let r = unsafe { libc::closedir(self.0) };
- debug_assert_eq!(r, 0);
+ assert!(
+ r == 0 || crate::io::Error::last_os_error().kind() == crate::io::ErrorKind::Interrupted,
+ "unexpected error during closedir: {:?}",
+ crate::io::Error::last_os_error()
+ );
}
}
@@ -700,7 +722,10 @@ impl DirEntry {
self.file_name_os_str().to_os_string()
}
- #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+ #[cfg(all(
+ any(target_os = "linux", target_os = "emscripten", target_os = "android"),
+ not(miri)
+ ))]
pub fn metadata(&self) -> io::Result<FileAttr> {
let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
let name = self.name_cstr().as_ptr();
@@ -721,7 +746,10 @@ impl DirEntry {
Ok(FileAttr::from_stat64(stat))
}
- #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
+ #[cfg(any(
+ not(any(target_os = "linux", target_os = "emscripten", target_os = "android")),
+ miri
+ ))]
pub fn metadata(&self) -> io::Result<FileAttr> {
lstat(&self.path())
}
@@ -925,8 +953,7 @@ impl OpenOptions {
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
- let path = cstr(path)?;
- File::open_c(&path, opts)
+ run_path_with_cstr(path, |path| File::open_c(path, opts))
}
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
@@ -1031,8 +1058,8 @@ impl File {
self.0.read_at(buf, offset)
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- self.0.read_buf(buf)
+ pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ self.0.read_buf(cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
@@ -1078,10 +1105,21 @@ impl File {
}
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+ #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
+ let to_timespec = |time: Option<SystemTime>| {
+ match time {
+ Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
+ Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")),
+ Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time")),
+ None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
+ }
+ };
+ #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
+ let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
cfg_if::cfg_if! {
- if #[cfg(any(target_os = "redox", target_os = "espidf"))] {
+ if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] {
// Redox doesn't appear to support `UTIME_OMIT`.
- // ESP-IDF does not support `futimens` at all and the behavior for that OS is therefore
+ // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
// the same as for Redox.
drop(times);
Err(io::const_io_error!(
@@ -1093,7 +1131,7 @@ impl File {
cvt(unsafe {
weak!(fn futimens(c_int, *const libc::timespec) -> c_int);
match futimens.get() {
- Some(futimens) => futimens(self.as_raw_fd(), times.0.as_ptr()),
+ Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
#[cfg(target_os = "macos")]
None => {
fn ts_to_tv(ts: &libc::timespec) -> libc::timeval {
@@ -1102,7 +1140,7 @@ impl File {
tv_usec: (ts.tv_nsec / 1000) as _
}
}
- let timevals = [ts_to_tv(&times.0[0]), ts_to_tv(&times.0[1])];
+ let timevals = [ts_to_tv(&times[0]), ts_to_tv(&times[1])];
libc::futimes(self.as_raw_fd(), timevals.as_ptr())
}
// futimes requires even newer Android.
@@ -1115,7 +1153,7 @@ impl File {
})?;
Ok(())
} else {
- cvt(unsafe { libc::futimens(self.as_raw_fd(), times.0.as_ptr()) })?;
+ cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
Ok(())
}
}
@@ -1128,9 +1166,7 @@ impl DirBuilder {
}
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
- let p = cstr(p)?;
- cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
- Ok(())
+ run_path_with_cstr(p, |p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
}
pub fn set_mode(&mut self, mode: u32) {
@@ -1138,10 +1174,6 @@ impl DirBuilder {
}
}
-fn cstr(path: &Path) -> io::Result<CString> {
- Ok(CString::new(path.as_os_str().as_bytes())?)
-}
-
impl AsInner<FileDesc> for File {
fn as_inner(&self) -> &FileDesc {
&self.0
@@ -1192,7 +1224,12 @@ impl FromRawFd for File {
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- #[cfg(any(target_os = "linux", target_os = "netbsd"))]
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "solaris"
+ ))]
fn get_path(fd: c_int) -> Option<PathBuf> {
let mut p = PathBuf::from("/proc/self/fd");
p.push(&fd.to_string());
@@ -1247,14 +1284,23 @@ impl fmt::Debug for File {
target_os = "macos",
target_os = "vxworks",
all(target_os = "freebsd", target_arch = "x86_64"),
- target_os = "netbsd"
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "solaris"
)))]
fn get_path(_fd: c_int) -> Option<PathBuf> {
// FIXME(#24570): implement this for other Unix platforms
None
}
- #[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))]
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "vxworks"
+ ))]
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if mode == -1 {
@@ -1268,7 +1314,14 @@ impl fmt::Debug for File {
}
}
- #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
+ #[cfg(not(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "vxworks"
+ )))]
fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
// FIXME(#24570): implement this for other Unix platforms
None
@@ -1287,173 +1340,170 @@ impl fmt::Debug for File {
}
}
-pub fn readdir(p: &Path) -> io::Result<ReadDir> {
- let root = p.to_path_buf();
- let p = cstr(p)?;
- unsafe {
- let ptr = libc::opendir(p.as_ptr());
- if ptr.is_null() {
- Err(Error::last_os_error())
- } else {
- let inner = InnerReadDir { dirp: Dir(ptr), root };
- Ok(ReadDir {
- inner: Arc::new(inner),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- })
- }
+pub fn readdir(path: &Path) -> io::Result<ReadDir> {
+ let ptr = run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
+ if ptr.is_null() {
+ Err(Error::last_os_error())
+ } else {
+ let root = path.to_path_buf();
+ let inner = InnerReadDir { dirp: Dir(ptr), root };
+ Ok(ReadDir {
+ inner: Arc::new(inner),
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox",
+ )))]
+ end_of_stream: false,
+ })
}
}
pub fn unlink(p: &Path) -> io::Result<()> {
- let p = cstr(p)?;
- cvt(unsafe { libc::unlink(p.as_ptr()) })?;
- Ok(())
+ run_path_with_cstr(p, |p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ()))
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
- let old = cstr(old)?;
- let new = cstr(new)?;
- cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
- Ok(())
+ run_path_with_cstr(old, |old| {
+ run_path_with_cstr(new, |new| {
+ cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
+ })
+ })
}
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
- let p = cstr(p)?;
- cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
- Ok(())
+ run_path_with_cstr(p, |p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()))
}
pub fn rmdir(p: &Path) -> io::Result<()> {
- let p = cstr(p)?;
- cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
- Ok(())
+ run_path_with_cstr(p, |p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()))
}
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
- let c_path = cstr(p)?;
- let p = c_path.as_ptr();
+ run_path_with_cstr(p, |c_path| {
+ let p = c_path.as_ptr();
- let mut buf = Vec::with_capacity(256);
+ let mut buf = Vec::with_capacity(256);
- loop {
- let buf_read =
- cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
+ loop {
+ let buf_read =
+ cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })?
+ as usize;
- unsafe {
- buf.set_len(buf_read);
- }
+ unsafe {
+ buf.set_len(buf_read);
+ }
- if buf_read != buf.capacity() {
- buf.shrink_to_fit();
+ if buf_read != buf.capacity() {
+ buf.shrink_to_fit();
- return Ok(PathBuf::from(OsString::from_vec(buf)));
- }
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ }
- // Trigger the internal buffer resizing logic of `Vec` by requiring
- // more space than the current capacity. The length is guaranteed to be
- // the same as the capacity due to the if statement above.
- buf.reserve(1);
- }
+ // Trigger the internal buffer resizing logic of `Vec` by requiring
+ // more space than the current capacity. The length is guaranteed to be
+ // the same as the capacity due to the if statement above.
+ buf.reserve(1);
+ }
+ })
}
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
- let original = cstr(original)?;
- let link = cstr(link)?;
- cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
- Ok(())
+ run_path_with_cstr(original, |original| {
+ run_path_with_cstr(link, |link| {
+ cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
+ })
+ })
}
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
- let original = cstr(original)?;
- let link = cstr(link)?;
- cfg_if::cfg_if! {
- if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] {
- // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
- // it implementation-defined whether `link` follows symlinks, so rely on the
- // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
- // Android has `linkat` on newer versions, but we happen to know `link`
- // always has the correct behavior, so it's here as well.
- cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
- } else if #[cfg(target_os = "macos")] {
- // On MacOS, older versions (<=10.9) lack support for linkat while newer
- // versions have it. We want to use linkat if it is available, so we use weak!
- // to check. `linkat` is preferable to `link` because it gives us a flag to
- // specify how symlinks should be handled. We pass 0 as the flags argument,
- // meaning it shouldn't follow symlinks.
- weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
-
- if let Some(f) = linkat.get() {
- cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
- } else {
- cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
- };
- } else {
- // Where we can, use `linkat` instead of `link`; see the comment above
- // this one for details on why.
- cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
- }
- }
- Ok(())
+ run_path_with_cstr(original, |original| {
+ run_path_with_cstr(link, |link| {
+ cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] {
+ // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
+ // it implementation-defined whether `link` follows symlinks, so rely on the
+ // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
+ // Android has `linkat` on newer versions, but we happen to know `link`
+ // always has the correct behavior, so it's here as well.
+ cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
+ } else if #[cfg(target_os = "macos")] {
+ // On MacOS, older versions (<=10.9) lack support for linkat while newer
+ // versions have it. We want to use linkat if it is available, so we use weak!
+ // to check. `linkat` is preferable to `link` because it gives us a flag to
+ // specify how symlinks should be handled. We pass 0 as the flags argument,
+ // meaning it shouldn't follow symlinks.
+ weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
+
+ if let Some(f) = linkat.get() {
+ cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
+ } else {
+ cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
+ };
+ } else {
+ // Where we can, use `linkat` instead of `link`; see the comment above
+ // this one for details on why.
+ cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
+ }
+ }
+ Ok(())
+ })
+ })
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
- let p = cstr(p)?;
-
- cfg_has_statx! {
- if let Some(ret) = unsafe { try_statx(
- libc::AT_FDCWD,
- p.as_ptr(),
- libc::AT_STATX_SYNC_AS_STAT,
- libc::STATX_ALL,
- ) } {
- return ret;
+ run_path_with_cstr(p, |p| {
+ cfg_has_statx! {
+ if let Some(ret) = unsafe { try_statx(
+ libc::AT_FDCWD,
+ p.as_ptr(),
+ libc::AT_STATX_SYNC_AS_STAT,
+ libc::STATX_ALL,
+ ) } {
+ return ret;
+ }
}
- }
- let mut stat: stat64 = unsafe { mem::zeroed() };
- cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
- Ok(FileAttr::from_stat64(stat))
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
+ Ok(FileAttr::from_stat64(stat))
+ })
}
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
- let p = cstr(p)?;
-
- cfg_has_statx! {
- if let Some(ret) = unsafe { try_statx(
- libc::AT_FDCWD,
- p.as_ptr(),
- libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
- libc::STATX_ALL,
- ) } {
- return ret;
+ run_path_with_cstr(p, |p| {
+ cfg_has_statx! {
+ if let Some(ret) = unsafe { try_statx(
+ libc::AT_FDCWD,
+ p.as_ptr(),
+ libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+ libc::STATX_ALL,
+ ) } {
+ return ret;
+ }
}
- }
- let mut stat: stat64 = unsafe { mem::zeroed() };
- cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
- Ok(FileAttr::from_stat64(stat))
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
+ Ok(FileAttr::from_stat64(stat))
+ })
}
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
- let path = CString::new(p.as_os_str().as_bytes())?;
- let buf;
- unsafe {
- let r = libc::realpath(path.as_ptr(), ptr::null_mut());
- if r.is_null() {
- return Err(io::Error::last_os_error());
- }
- buf = CStr::from_ptr(r).to_bytes().to_vec();
- libc::free(r as *mut _);
+ let r = run_path_with_cstr(p, |path| unsafe {
+ Ok(libc::realpath(path.as_ptr(), ptr::null_mut()))
+ })?;
+ if r.is_null() {
+ return Err(io::Error::last_os_error());
}
- Ok(PathBuf::from(OsString::from_vec(buf)))
+ Ok(PathBuf::from(OsString::from_vec(unsafe {
+ let buf = CStr::from_ptr(r).to_bytes().to_vec();
+ libc::free(r as *mut _);
+ buf
+ })))
}
fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
@@ -1603,9 +1653,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
// Opportunistically attempt to create a copy-on-write clone of `from`
// using `fclonefileat`.
if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
- let to = cstr(to)?;
- let clonefile_result =
- cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
+ let clonefile_result = run_path_with_cstr(to, |to| {
+ cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) })
+ });
match clonefile_result {
Ok(_) => return Ok(reader_metadata.len()),
Err(err) => match err.raw_os_error() {
@@ -1649,9 +1699,10 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
}
pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
- let path = cstr(path)?;
- cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
- Ok(())
+ run_path_with_cstr(path, |path| {
+ cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
+ .map(|_| ())
+ })
}
pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
@@ -1660,16 +1711,15 @@ pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
}
pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
- let path = cstr(path)?;
- cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
- Ok(())
+ run_path_with_cstr(path, |path| {
+ cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
+ .map(|_| ())
+ })
}
#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
pub fn chroot(dir: &Path) -> io::Result<()> {
- let dir = cstr(dir)?;
- cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
- Ok(())
+ run_path_with_cstr(dir, |dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ()))
}
pub use remove_dir_impl::remove_dir_all;
@@ -1683,13 +1733,14 @@ mod remove_dir_impl {
// Modern implementation using openat(), unlinkat() and fdopendir()
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))]
mod remove_dir_impl {
- use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir};
+ use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir};
use crate::ffi::CStr;
use crate::io;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use crate::os::unix::prelude::{OwnedFd, RawFd};
use crate::path::{Path, PathBuf};
use crate::sync::Arc;
+ use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::{cvt, cvt_r};
#[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))]
@@ -1856,7 +1907,7 @@ mod remove_dir_impl {
if attr.file_type().is_symlink() {
crate::fs::remove_file(p)
} else {
- remove_dir_all_recursive(None, &cstr(p)?)
+ run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &p))
}
}
diff --git a/library/std/src/sys/unix/io.rs b/library/std/src/sys/unix/io.rs
index deb5ee76b..29c340dd3 100644
--- a/library/std/src/sys/unix/io.rs
+++ b/library/std/src/sys/unix/io.rs
@@ -1,4 +1,5 @@
use crate::marker::PhantomData;
+use crate::os::fd::{AsFd, AsRawFd};
use crate::slice;
use libc::{c_void, iovec};
@@ -74,3 +75,8 @@ impl<'a> IoSliceMut<'a> {
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
}
+
+pub fn is_terminal(fd: &impl AsFd) -> bool {
+ let fd = fd.as_fd();
+ unsafe { libc::isatty(fd.as_raw_fd()) != 0 }
+}
diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
index 8f7abb55e..94546ca09 100644
--- a/library/std/src/sys/unix/kernel_copy.rs
+++ b/library/std/src/sys/unix/kernel_copy.rs
@@ -20,7 +20,7 @@
//! Since those syscalls have requirements that cannot be fully checked in advance and
//! gathering additional information about file descriptors would require additional syscalls
//! anyway it simply attempts to use them one after another (guided by inaccurate hints) to
-//! figure out which one works and and falls back to the generic read-write copy loop if none of them
+//! figure out which one works and falls back to the generic read-write copy loop if none of them
//! does.
//! Once a working syscall is found for a pair of file descriptors it will be called in a loop
//! until the copy operation is completed.
diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs
index ce427599c..117611ce4 100644
--- a/library/std/src/sys/unix/locks/fuchsia_mutex.rs
+++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs
@@ -86,9 +86,6 @@ impl Mutex {
}
#[inline]
- pub unsafe fn init(&mut self) {}
-
- #[inline]
pub unsafe fn try_lock(&self) -> bool {
let thread_self = zx_thread_self();
self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok()
@@ -138,7 +135,7 @@ impl Mutex {
}
}
- // The state has changed or a wakeup occured, try to lock the mutex.
+ // The state has changed or a wakeup occurred, try to lock the mutex.
match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) {
Ok(_) => return,
Err(updated) => state = updated,
diff --git a/library/std/src/sys/unix/locks/futex_mutex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs
index 99ba86e5f..33b13dad4 100644
--- a/library/std/src/sys/unix/locks/futex_mutex.rs
+++ b/library/std/src/sys/unix/locks/futex_mutex.rs
@@ -20,9 +20,6 @@ impl Mutex {
}
#[inline]
- pub unsafe fn init(&mut self) {}
-
- #[inline]
pub unsafe fn try_lock(&self) -> bool {
self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok()
}
@@ -53,7 +50,7 @@ impl Mutex {
// We avoid an unnecessary write if it as already set to 2,
// to be friendlier for the caches.
if state != 2 && self.futex.swap(2, Acquire) == 0 {
- // We changed it from 0 to 2, so we just succesfully locked it.
+ // We changed it from 0 to 2, so we just successfully locked it.
return;
}
diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs
index b3bbbf743..0cc92244e 100644
--- a/library/std/src/sys/unix/locks/futex_rwlock.rs
+++ b/library/std/src/sys/unix/locks/futex_rwlock.rs
@@ -54,7 +54,7 @@ fn is_read_lockable(state: u32) -> bool {
// We don't allow read-locking if there's readers waiting, even if the lock is unlocked
// and there's no writers waiting. The only situation when this happens is after unlocking,
// at which point the unlocking thread might be waking up writers, which have priority over readers.
- // The unlocking thread will clear the readers waiting bit and wake up readers, if necssary.
+ // The unlocking thread will clear the readers waiting bit and wake up readers, if necessary.
state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
}
diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs
index f5f92f693..9bb314b70 100644
--- a/library/std/src/sys/unix/locks/mod.rs
+++ b/library/std/src/sys/unix/locks/mod.rs
@@ -11,21 +11,21 @@ cfg_if::cfg_if! {
mod futex_rwlock;
mod futex_condvar;
pub(crate) use futex_mutex::{Mutex, MovableMutex};
- pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use futex_rwlock::MovableRwLock;
pub(crate) use futex_condvar::MovableCondvar;
} else if #[cfg(target_os = "fuchsia")] {
mod fuchsia_mutex;
mod futex_rwlock;
mod futex_condvar;
pub(crate) use fuchsia_mutex::{Mutex, MovableMutex};
- pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use futex_rwlock::MovableRwLock;
pub(crate) use futex_condvar::MovableCondvar;
} else {
mod pthread_mutex;
mod pthread_rwlock;
mod pthread_condvar;
pub(crate) use pthread_mutex::{Mutex, MovableMutex};
- pub(crate) use pthread_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use pthread_rwlock::MovableRwLock;
pub(crate) use pthread_condvar::MovableCondvar;
}
}
diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs
index abf27e7db..4741c0c67 100644
--- a/library/std/src/sys/unix/locks/pthread_condvar.rs
+++ b/library/std/src/sys/unix/locks/pthread_condvar.rs
@@ -172,7 +172,7 @@ impl Condvar {
let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
let stable_now = Instant::now();
let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
- debug_assert_eq!(r, 0);
+ assert_eq!(r, 0, "unexpected error: {:?}", crate::io::Error::last_os_error());
let nsec = dur.subsec_nanos() as libc::c_long + (sys_now.tv_usec * 1000) as libc::c_long;
let extra = (nsec / 1_000_000_000) as libc::time_t;
diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs
index 98afee69b..5964935dd 100644
--- a/library/std/src/sys/unix/locks/pthread_mutex.rs
+++ b/library/std/src/sys/unix/locks/pthread_mutex.rs
@@ -52,7 +52,7 @@ impl Mutex {
Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
}
#[inline]
- pub unsafe fn init(&mut self) {
+ unsafe fn init(&mut self) {
// Issue #33770
//
// A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 3d0d91460..9055a011c 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -44,12 +44,13 @@ pub mod thread_parker;
pub mod time;
#[cfg(target_os = "espidf")]
-pub fn init(argc: isize, argv: *const *const u8) {}
+pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {}
#[cfg(not(target_os = "espidf"))]
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-pub unsafe fn init(argc: isize, argv: *const *const u8) {
+// See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`.
+pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
// The standard streams might be closed on application startup. To prevent
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
// resources opened later, we reopen standards streams when they are closed.
@@ -61,8 +62,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
// want!
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
- // to prevent this problem.
- reset_sigpipe();
+ // to prevent this problem. Add `#[unix_sigpipe = "..."]` above `fn main()` to
+ // alter this behavior.
+ reset_sigpipe(sigpipe);
stack_overflow::init();
args::init(argc, argv);
@@ -151,12 +153,64 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
}
}
- unsafe fn reset_sigpipe() {
+ unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) {
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
- rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
+ {
+ // We don't want to add this as a public type to libstd, nor do we
+ // want to `include!` a file from the compiler (which would break
+ // Miri and xargo for example), so we choose to duplicate these
+ // constants from `compiler/rustc_session/src/config/sigpipe.rs`.
+ // See the other file for docs. NOTE: Make sure to keep them in
+ // sync!
+ mod sigpipe {
+ pub const DEFAULT: u8 = 0;
+ pub const INHERIT: u8 = 1;
+ pub const SIG_IGN: u8 = 2;
+ pub const SIG_DFL: u8 = 3;
+ }
+
+ let (sigpipe_attr_specified, handler) = match sigpipe {
+ sigpipe::DEFAULT => (false, Some(libc::SIG_IGN)),
+ sigpipe::INHERIT => (true, None),
+ sigpipe::SIG_IGN => (true, Some(libc::SIG_IGN)),
+ sigpipe::SIG_DFL => (true, Some(libc::SIG_DFL)),
+ _ => unreachable!(),
+ };
+ // The bootstrap compiler doesn't know about sigpipe::DEFAULT, and always passes in
+ // SIG_IGN. This causes some tests to fail because they expect SIGPIPE to be reset to
+ // default on process spawning (which doesn't happen if #[unix_sigpipe] is specified).
+ // Since we can't differentiate between the cases here, treat SIG_IGN as DEFAULT
+ // unconditionally.
+ if sigpipe_attr_specified && !(cfg!(bootstrap) && sigpipe == sigpipe::SIG_IGN) {
+ UNIX_SIGPIPE_ATTR_SPECIFIED.store(true, crate::sync::atomic::Ordering::Relaxed);
+ }
+ if let Some(handler) = handler {
+ rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR);
+ }
+ }
}
}
+// This is set (up to once) in reset_sigpipe.
+#[cfg(not(any(
+ target_os = "espidf",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "horizon"
+)))]
+static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool =
+ crate::sync::atomic::AtomicBool::new(false);
+
+#[cfg(not(any(
+ target_os = "espidf",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "horizon"
+)))]
+pub(crate) fn unix_sigpipe_attr_specified() -> bool {
+ UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed)
+}
+
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub unsafe fn cleanup() {
@@ -295,8 +349,10 @@ pub fn abort_internal() -> ! {
cfg_if::cfg_if! {
if #[cfg(target_os = "android")] {
- #[link(name = "dl")]
- #[link(name = "log")]
+ #[link(name = "dl", kind = "static", modifiers = "-bundle",
+ cfg(target_feature = "crt-static"))]
+ #[link(name = "dl", cfg(not(target_feature = "crt-static")))]
+ #[link(name = "log", cfg(not(target_feature = "crt-static")))]
extern "C" {}
} else if #[cfg(target_os = "freebsd")] {
#[link(name = "execinfo")]
@@ -326,16 +382,12 @@ cfg_if::cfg_if! {
extern "C" {}
} else if #[cfg(target_os = "macos")] {
#[link(name = "System")]
- // res_init and friends require -lresolv on macOS/iOS.
- // See #41582 and https://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html
- #[link(name = "resolv")]
extern "C" {}
} else if #[cfg(any(target_os = "ios", target_os = "watchos"))] {
#[link(name = "System")]
#[link(name = "objc")]
#[link(name = "Security", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
- #[link(name = "resolv")]
extern "C" {}
} else if #[cfg(target_os = "fuchsia")] {
#[link(name = "zircon")]
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index 462a45b01..b84bf8f92 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -393,6 +393,17 @@ impl Socket {
}
#[cfg(any(target_os = "android", target_os = "linux",))]
+ pub fn set_quickack(&self, quickack: bool) -> io::Result<()> {
+ setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux",))]
+ pub fn quickack(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?;
+ Ok(raw != 0)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux",))]
pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int)
}
@@ -427,6 +438,17 @@ impl Socket {
self.0.set_nonblocking(nonblocking)
}
+ #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
+ pub fn set_mark(&self, mark: u32) -> io::Result<()> {
+ #[cfg(target_os = "linux")]
+ let option = libc::SO_MARK;
+ #[cfg(target_os = "freebsd")]
+ let option = libc::SO_USER_COOKIE;
+ #[cfg(target_os = "openbsd")]
+ let option = libc::SO_RTABLE;
+ setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int)
+ }
+
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 46545a083..2f2663db6 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -7,6 +7,7 @@ mod tests;
use crate::os::unix::prelude::*;
+use crate::convert::TryFrom;
use crate::error::Error as StdError;
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
@@ -17,10 +18,11 @@ use crate::path::{self, PathBuf};
use crate::ptr;
use crate::slice;
use crate::str;
+use crate::sync::{PoisonError, RwLock};
+use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
use crate::sys::cvt;
use crate::sys::fd;
use crate::sys::memchr;
-use crate::sys_common::rwlock::{StaticRwLock, StaticRwLockReadGuard};
use crate::vec;
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
@@ -125,7 +127,9 @@ pub fn error_string(errno: i32) -> String {
}
let p = p as *const _;
- str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
+ // We can't always expect a UTF-8 environment. When we don't get that luxury,
+ // it's better to give a low-quality error message than none at all.
+ String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into()
}
}
@@ -168,12 +172,8 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
#[cfg(not(target_os = "espidf"))]
pub fn chdir(p: &path::Path) -> io::Result<()> {
- let p: &OsStr = p.as_ref();
- let p = CString::new(p.as_bytes())?;
- if unsafe { libc::chdir(p.as_ptr()) } != 0 {
- return Err(io::Error::last_os_error());
- }
- Ok(())
+ let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
+ if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
}
pub struct SplitPaths<'a> {
@@ -501,10 +501,10 @@ pub unsafe fn environ() -> *mut *const *const c_char {
ptr::addr_of_mut!(environ)
}
-static ENV_LOCK: StaticRwLock = StaticRwLock::new();
+static ENV_LOCK: RwLock<()> = RwLock::new(());
-pub fn env_read_lock() -> StaticRwLockReadGuard {
- ENV_LOCK.read()
+pub fn env_read_lock() -> impl Drop {
+ ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
@@ -546,35 +546,32 @@ pub fn env() -> Env {
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
- let k = CString::new(k.as_bytes()).ok()?;
- unsafe {
+ let s = run_with_cstr(k.as_bytes(), |k| {
let _guard = env_read_lock();
- let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
- if s.is_null() {
- None
- } else {
- Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
- }
+ Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char)
+ })
+ .ok()?;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec()))
}
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
- let k = CString::new(k.as_bytes())?;
- let v = CString::new(v.as_bytes())?;
-
- unsafe {
- let _guard = ENV_LOCK.write();
- cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
- }
+ run_with_cstr(k.as_bytes(), |k| {
+ run_with_cstr(v.as_bytes(), |v| {
+ let _guard = ENV_LOCK.write();
+ cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
+ })
+ })
}
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
- let nbuf = CString::new(n.as_bytes())?;
-
- unsafe {
+ run_with_cstr(n.as_bytes(), |nbuf| {
let _guard = ENV_LOCK.write();
- cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
- }
+ cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
+ })
}
#[cfg(not(target_os = "espidf"))]
diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs
index ccbc18224..017e2af29 100644
--- a/library/std/src/sys/unix/os_str.rs
+++ b/library/std/src/sys/unix/os_str.rs
@@ -11,7 +11,7 @@ use crate::str;
use crate::sync::Arc;
use crate::sys_common::{AsInner, IntoInner};
-use core::str::lossy::{Utf8Lossy, Utf8LossyChunk};
+use core::str::Utf8Chunks;
#[cfg(test)]
#[path = "../unix/os_str/tests.rs"]
@@ -29,26 +29,32 @@ pub struct Slice {
}
impl fmt::Debug for Slice {
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Writes out a valid unicode string with the correct escape sequences
-
- formatter.write_str("\"")?;
- for Utf8LossyChunk { valid, broken } in Utf8Lossy::from_bytes(&self.inner).chunks() {
- for c in valid.chars().flat_map(|c| c.escape_debug()) {
- formatter.write_char(c)?
- }
-
- for b in broken {
- write!(formatter, "\\x{:02X}", b)?;
- }
- }
- formatter.write_str("\"")
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&Utf8Chunks::new(&self.inner).debug(), f)
}
}
impl fmt::Display for Slice {
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // If we're the empty string then our iterator won't actually yield
+ // anything, so perform the formatting manually
+ if self.inner.is_empty() {
+ return "".fmt(f);
+ }
+
+ for chunk in Utf8Chunks::new(&self.inner) {
+ let valid = chunk.valid();
+ // If we successfully decoded the whole chunk as a valid string then
+ // we can return a direct formatting of the string which will also
+ // respect various formatting flags if possible.
+ if chunk.invalid().is_empty() {
+ return valid.fmt(f);
+ }
+
+ f.write_str(valid)?;
+ f.write_char(char::REPLACEMENT_CHARACTER)?;
+ }
+ Ok(())
}
}
diff --git a/library/std/src/sys/unix/os_str/tests.rs b/library/std/src/sys/unix/os_str/tests.rs
index 213277f01..22ba0c923 100644
--- a/library/std/src/sys/unix/os_str/tests.rs
+++ b/library/std/src/sys/unix/os_str/tests.rs
@@ -8,3 +8,11 @@ fn slice_debug_output() {
assert_eq!(output, expected);
}
+
+#[test]
+fn display() {
+ assert_eq!(
+ "Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye",
+ Slice::from_u8_slice(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string(),
+ );
+}
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index bca1b65a7..848adca78 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -39,17 +39,39 @@ cfg_if::cfg_if! {
// https://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h
cfg_if::cfg_if! {
if #[cfg(target_os = "android")] {
+ #[allow(dead_code)]
pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
set.write_bytes(0u8, 1);
return 0;
}
+
#[allow(dead_code)]
pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
- use crate::{slice, mem};
+ use crate::{
+ mem::{align_of, size_of},
+ slice,
+ };
+ use libc::{c_ulong, sigset_t};
+
+ // The implementations from bionic (android libc) type pun `sigset_t` as an
+ // array of `c_ulong`. This works, but lets add a smoke check to make sure
+ // that doesn't change.
+ const _: () = assert!(
+ align_of::<c_ulong>() == align_of::<sigset_t>()
+ && (size_of::<sigset_t>() % size_of::<c_ulong>()) == 0
+ );
- let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>());
let bit = (signum - 1) as usize;
- raw[bit / 8] |= 1 << (bit % 8);
+ if set.is_null() || bit >= (8 * size_of::<sigset_t>()) {
+ crate::sys::unix::os::set_errno(libc::EINVAL);
+ return -1;
+ }
+ let raw = slice::from_raw_parts_mut(
+ set as *mut c_ulong,
+ size_of::<sigset_t>() / size_of::<c_ulong>(),
+ );
+ const LONG_BIT: usize = size_of::<c_ulong>() * 8;
+ raw[bit / LONG_BIT] |= 1 << (bit % LONG_BIT);
return 0;
}
} else {
@@ -72,6 +94,7 @@ pub struct Command {
argv: Argv,
env: CommandEnv,
+ program_kind: ProgramKind,
cwd: Option<CString>,
uid: Option<uid_t>,
gid: Option<gid_t>,
@@ -128,15 +151,40 @@ pub enum Stdio {
Fd(FileDesc),
}
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum ProgramKind {
+ /// A program that would be looked up on the PATH (e.g. `ls`)
+ PathLookup,
+ /// A relative path (e.g. `my-dir/foo`, `../foo`, `./foo`)
+ Relative,
+ /// An absolute path.
+ Absolute,
+}
+
+impl ProgramKind {
+ fn new(program: &OsStr) -> Self {
+ if program.bytes().starts_with(b"/") {
+ Self::Absolute
+ } else if program.bytes().contains(&b'/') {
+ // If the program has more than one component in it, it is a relative path.
+ Self::Relative
+ } else {
+ Self::PathLookup
+ }
+ }
+}
+
impl Command {
#[cfg(not(target_os = "linux"))]
pub fn new(program: &OsStr) -> Command {
let mut saw_nul = false;
+ let program_kind = ProgramKind::new(program.as_ref());
let program = os2c(program, &mut saw_nul);
Command {
argv: Argv(vec![program.as_ptr(), ptr::null()]),
args: vec![program.clone()],
program,
+ program_kind,
env: Default::default(),
cwd: None,
uid: None,
@@ -154,11 +202,13 @@ impl Command {
#[cfg(target_os = "linux")]
pub fn new(program: &OsStr) -> Command {
let mut saw_nul = false;
+ let program_kind = ProgramKind::new(program.as_ref());
let program = os2c(program, &mut saw_nul);
Command {
argv: Argv(vec![program.as_ptr(), ptr::null()]),
args: vec![program.clone()],
program,
+ program_kind,
env: Default::default(),
cwd: None,
uid: None,
@@ -234,6 +284,11 @@ impl Command {
OsStr::from_bytes(self.program.as_bytes())
}
+ #[allow(dead_code)]
+ pub fn get_program_kind(&self) -> ProgramKind {
+ self.program_kind
+ }
+
pub fn get_args(&self) -> CommandArgs<'_> {
let mut iter = self.args.iter();
iter.next();
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
index 1956b3692..03631e4e3 100644
--- a/library/std/src/sys/unix/process/process_common/tests.rs
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -31,41 +31,54 @@ macro_rules! t {
ignore
)]
fn test_process_mask() {
- unsafe {
- // Test to make sure that a signal mask does not get inherited.
- let mut cmd = Command::new(OsStr::new("cat"));
-
- let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit();
- let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
- t!(cvt(sigemptyset(set.as_mut_ptr())));
- t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT)));
- t!(cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr())));
-
- cmd.stdin(Stdio::MakePipe);
- cmd.stdout(Stdio::MakePipe);
-
- let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
- let stdin_write = pipes.stdin.take().unwrap();
- let stdout_read = pipes.stdout.take().unwrap();
-
- t!(cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut())));
-
- t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
- // We need to wait until SIGINT is definitely delivered. The
- // easiest way is to write something to cat, and try to read it
- // back: if SIGINT is unmasked, it'll get delivered when cat is
- // next scheduled.
- let _ = stdin_write.write(b"Hello");
- drop(stdin_write);
-
- // Either EOF or failure (EPIPE) is okay.
- let mut buf = [0; 5];
- if let Ok(ret) = stdout_read.read(&mut buf) {
- assert_eq!(ret, 0);
+ // Test to make sure that a signal mask *does* get inherited.
+ fn test_inner(mut cmd: Command) {
+ unsafe {
+ let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit();
+ let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
+ t!(cvt(sigemptyset(set.as_mut_ptr())));
+ t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT)));
+ t!(cvt_nz(libc::pthread_sigmask(
+ libc::SIG_SETMASK,
+ set.as_ptr(),
+ old_set.as_mut_ptr()
+ )));
+
+ cmd.stdin(Stdio::MakePipe);
+ cmd.stdout(Stdio::MakePipe);
+
+ let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
+ let stdin_write = pipes.stdin.take().unwrap();
+ let stdout_read = pipes.stdout.take().unwrap();
+
+ t!(cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut())));
+
+ t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
+ // We need to wait until SIGINT is definitely delivered. The
+ // easiest way is to write something to cat, and try to read it
+ // back: if SIGINT is unmasked, it'll get delivered when cat is
+ // next scheduled.
+ let _ = stdin_write.write(b"Hello");
+ drop(stdin_write);
+
+ // Exactly 5 bytes should be read.
+ let mut buf = [0; 5];
+ let ret = t!(stdout_read.read(&mut buf));
+ assert_eq!(ret, 5);
+ assert_eq!(&buf, b"Hello");
+
+ t!(cat.wait());
}
-
- t!(cat.wait());
}
+
+ // A plain `Command::new` uses the posix_spawn path on many platforms.
+ let cmd = Command::new(OsStr::new("cat"));
+ test_inner(cmd);
+
+ // Specifying `pre_exec` forces the fork/exec path.
+ let mut cmd = Command::new(OsStr::new("cat"));
+ unsafe { cmd.pre_exec(Box::new(|| Ok(()))) };
+ test_inner(cmd);
}
#[test]
@@ -122,3 +135,27 @@ fn test_process_group_no_posix_spawn() {
t!(cat.wait());
}
}
+
+#[test]
+fn test_program_kind() {
+ let vectors = &[
+ ("foo", ProgramKind::PathLookup),
+ ("foo.out", ProgramKind::PathLookup),
+ ("./foo", ProgramKind::Relative),
+ ("../foo", ProgramKind::Relative),
+ ("dir/foo", ProgramKind::Relative),
+ // Note that paths on Unix can't contain / in them, so this is actually the directory "fo\\"
+ // followed by the file "o".
+ ("fo\\/o", ProgramKind::Relative),
+ ("/foo", ProgramKind::Absolute),
+ ("/dir/../foo", ProgramKind::Absolute),
+ ];
+
+ for (program, expected_kind) in vectors {
+ assert_eq!(
+ ProgramKind::new(program.as_ref()),
+ *expected_kind,
+ "actual != expected program kind for input {program}",
+ );
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index 73f5d3a61..66ea3db20 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -287,7 +287,7 @@ impl ExitStatus {
// SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
// necessarily fit.
//
- // It seems to me that that the right answer would be to provide std::os::fuchsia with its
+ // It seems to me that the right answer would be to provide std::os::fuchsia with its
// own ExitStatusExt, rather that trying to provide a not very convincing imitation of
// Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
// fixing this up that is beyond the scope of my efforts now.
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 75bb92437..56a805cef 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -2,7 +2,6 @@ use crate::fmt;
use crate::io::{self, Error, ErrorKind};
use crate::mem;
use crate::num::NonZeroI32;
-use crate::ptr;
use crate::sys;
use crate::sys::cvt;
use crate::sys::process::process_common::*;
@@ -310,7 +309,7 @@ impl Command {
//FIXME: Redox kernel does not support setgroups yet
#[cfg(not(target_os = "redox"))]
if libc::getuid() == 0 && self.get_groups().is_none() {
- cvt(libc::setgroups(0, ptr::null()))?;
+ cvt(libc::setgroups(0, crate::ptr::null()))?;
}
cvt(libc::setuid(u as uid_t))?;
}
@@ -326,30 +325,26 @@ impl Command {
// emscripten has no signal support.
#[cfg(not(target_os = "emscripten"))]
{
- use crate::mem::MaybeUninit;
- use crate::sys::cvt_nz;
- // Reset signal handling so the child process starts in a
- // standardized state. libstd ignores SIGPIPE, and signal-handling
- // libraries often set a mask. Child processes inherit ignored
- // signals and the signal mask from their parent, but most
- // UNIX programs do not reset these things on their own, so we
- // need to clean things up now to avoid confusing the program
- // we're about to run.
- let mut set = MaybeUninit::<libc::sigset_t>::uninit();
- cvt(sigemptyset(set.as_mut_ptr()))?;
- cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?;
-
- #[cfg(target_os = "android")] // see issue #88585
- {
- let mut action: libc::sigaction = mem::zeroed();
- action.sa_sigaction = libc::SIG_DFL;
- cvt(libc::sigaction(libc::SIGPIPE, &action, ptr::null_mut()))?;
- }
- #[cfg(not(target_os = "android"))]
- {
- let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
- if ret == libc::SIG_ERR {
- return Err(io::Error::last_os_error());
+ // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
+ // pthread_sigmask).
+
+ // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
+ // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
+ //
+ // #[unix_sigpipe] is an opportunity to change the default here.
+ if !crate::sys::unix_sigpipe_attr_specified() {
+ #[cfg(target_os = "android")] // see issue #88585
+ {
+ let mut action: libc::sigaction = mem::zeroed();
+ action.sa_sigaction = libc::SIG_DFL;
+ cvt(libc::sigaction(libc::SIGPIPE, &action, crate::ptr::null_mut()))?;
+ }
+ #[cfg(not(target_os = "android"))]
+ {
+ let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
+ if ret == libc::SIG_ERR {
+ return Err(io::Error::last_os_error());
+ }
}
}
}
@@ -411,7 +406,7 @@ impl Command {
envp: Option<&CStringArray>,
) -> io::Result<Option<Process>> {
use crate::mem::MaybeUninit;
- use crate::sys::{self, cvt_nz};
+ use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified};
if self.get_gid().is_some()
|| self.get_uid().is_some()
@@ -453,7 +448,9 @@ impl Command {
// successfully launch the program, but erroneously return
// ENOENT when used with posix_spawn_file_actions_addchdir_np
// which was introduced in macOS 10.15.
- return Ok(None);
+ if self.get_program_kind() == ProgramKind::Relative {
+ return Ok(None);
+ }
}
match posix_spawn_file_actions_addchdir_np.get() {
Some(f) => Some((f, cwd)),
@@ -529,13 +526,24 @@ impl Command {
cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?;
}
- let mut set = MaybeUninit::<libc::sigset_t>::uninit();
- cvt(sigemptyset(set.as_mut_ptr()))?;
- cvt_nz(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?;
- cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?;
- cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?;
+ // Inherit the signal mask from this process rather than resetting it (i.e. do not call
+ // posix_spawnattr_setsigmask).
+
+ // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
+ // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
+ //
+ // #[unix_sigpipe] is an opportunity to change the default here.
+ if !unix_sigpipe_attr_specified() {
+ let mut default_set = MaybeUninit::<libc::sigset_t>::uninit();
+ cvt(sigemptyset(default_set.as_mut_ptr()))?;
+ cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?;
+ cvt_nz(libc::posix_spawnattr_setsigdefault(
+ attrs.0.as_mut_ptr(),
+ default_set.as_ptr(),
+ ))?;
+ flags |= libc::POSIX_SPAWN_SETSIGDEF;
+ }
- flags |= libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK;
cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
// Make sure we synchronize access to the global `environ` resource
@@ -820,14 +828,14 @@ impl crate::os::linux::process::ChildExt for crate::process::Child {
self.handle
.pidfd
.as_ref()
- .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
+ .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
}
fn take_pidfd(&mut self) -> io::Result<PidFd> {
self.handle
.pidfd
.take()
- .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
+ .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
}
}
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index bf4920488..a6fe07873 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -1,13 +1,13 @@
-use crate::mem;
-use crate::slice;
-
pub fn hashmap_random_keys() -> (u64, u64) {
- let mut v = (0, 0);
- unsafe {
- let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, mem::size_of_val(&v));
- imp::fill_bytes(view);
- }
- v
+ const KEY_LEN: usize = core::mem::size_of::<u64>();
+
+ let mut v = [0u8; KEY_LEN * 2];
+ imp::fill_bytes(&mut v);
+
+ let key1 = v[0..KEY_LEN].try_into().unwrap();
+ let key2 = v[KEY_LEN..].try_into().unwrap();
+
+ (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
}
#[cfg(all(
diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs
index 329f9433d..b3626c564 100644
--- a/library/std/src/sys/unix/stdio.rs
+++ b/library/std/src/sys/unix/stdio.rs
@@ -1,6 +1,6 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
-use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd};
+use crate::os::unix::io::FromRawFd;
use crate::sys::fd::FileDesc;
pub struct Stdin(());
@@ -91,51 +91,3 @@ pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
pub fn panic_output() -> Option<impl io::Write> {
Some(Stderr::new())
}
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-impl AsFd for io::Stdin {
- #[inline]
- fn as_fd(&self) -> BorrowedFd<'_> {
- unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) }
- }
-}
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-impl<'a> AsFd for io::StdinLock<'a> {
- #[inline]
- fn as_fd(&self) -> BorrowedFd<'_> {
- unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) }
- }
-}
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-impl AsFd for io::Stdout {
- #[inline]
- fn as_fd(&self) -> BorrowedFd<'_> {
- unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }
- }
-}
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-impl<'a> AsFd for io::StdoutLock<'a> {
- #[inline]
- fn as_fd(&self) -> BorrowedFd<'_> {
- unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }
- }
-}
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-impl AsFd for io::Stderr {
- #[inline]
- fn as_fd(&self) -> BorrowedFd<'_> {
- unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }
- }
-}
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-impl<'a> AsFd for io::StderrLock<'a> {
- #[inline]
- fn as_fd(&self) -> BorrowedFd<'_> {
- unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }
- }
-}
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 36a3fa602..c1d30dd9d 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -116,11 +116,9 @@ impl Thread {
debug_assert_eq!(ret, 0);
}
- #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg(target_os = "android")]
pub fn set_name(name: &CStr) {
const PR_SET_NAME: libc::c_int = 15;
- // pthread wrapper only appeared in glibc 2.12, so we use syscall
- // directly.
unsafe {
libc::prctl(
PR_SET_NAME,
@@ -132,6 +130,19 @@ impl Thread {
}
}
+ #[cfg(target_os = "linux")]
+ pub fn set_name(name: &CStr) {
+ const TASK_COMM_LEN: usize = 16;
+
+ unsafe {
+ // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
+ let name = truncate_cstr(name, TASK_COMM_LEN);
+ let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
+ // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
+ debug_assert_eq!(res, 0);
+ }
+ }
+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
pub fn set_name(name: &CStr) {
unsafe {
@@ -142,20 +153,23 @@ impl Thread {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
pub fn set_name(name: &CStr) {
unsafe {
- libc::pthread_setname_np(name.as_ptr());
+ let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
+ let res = libc::pthread_setname_np(name.as_ptr());
+ // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
+ debug_assert_eq!(res, 0);
}
}
#[cfg(target_os = "netbsd")]
pub fn set_name(name: &CStr) {
- use crate::ffi::CString;
- let cname = CString::new(&b"%s"[..]).unwrap();
unsafe {
- libc::pthread_setname_np(
+ let cname = CStr::from_bytes_with_nul_unchecked(b"%s\0".as_slice());
+ let res = libc::pthread_setname_np(
libc::pthread_self(),
cname.as_ptr(),
name.as_ptr() as *mut libc::c_void,
);
+ debug_assert_eq!(res, 0);
}
}
@@ -168,9 +182,8 @@ impl Thread {
}
if let Some(f) = pthread_setname_np.get() {
- unsafe {
- f(libc::pthread_self(), name.as_ptr());
- }
+ let res = unsafe { f(libc::pthread_self(), name.as_ptr()) };
+ debug_assert_eq!(res, 0);
}
}
@@ -271,6 +284,20 @@ impl Drop for Thread {
}
}
+#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
+fn truncate_cstr(cstr: &CStr, max_with_nul: usize) -> crate::borrow::Cow<'_, CStr> {
+ use crate::{borrow::Cow, ffi::CString};
+
+ if cstr.to_bytes_with_nul().len() > max_with_nul {
+ let bytes = cstr.to_bytes()[..max_with_nul - 1].to_vec();
+ // SAFETY: the non-nul bytes came straight from a CStr.
+ // (CString will add the terminating nul.)
+ Cow::Owned(unsafe { CString::from_vec_unchecked(bytes) })
+ } else {
+ Cow::Borrowed(cstr)
+ }
+}
+
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
cfg_if::cfg_if! {
if #[cfg(any(
@@ -423,7 +450,7 @@ mod cgroups {
Some(b"") => Cgroup::V2,
Some(controllers)
if from_utf8(controllers)
- .is_ok_and(|c| c.split(",").any(|c| c == "cpu")) =>
+ .is_ok_and(|c| c.split(',').any(|c| c == "cpu")) =>
{
Cgroup::V1
}
@@ -761,6 +788,16 @@ pub mod guard {
const GUARD_PAGES: usize = 1;
let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
Some(guard)
+ } else if cfg!(target_os = "openbsd") {
+ // OpenBSD stack already includes a guard page, and stack is
+ // immutable.
+ //
+ // We'll just note where we expect rlimit to start
+ // faulting, so our handler can report "stack overflow", and
+ // trust that the kernel's own stack guard will work.
+ let stackptr = get_stack_start_aligned()?;
+ let stackaddr = stackptr.addr();
+ Some(stackaddr - page_size..stackaddr)
} else {
// Reallocate the last page of the stack.
// This ensures SIGBUS will be raised on
diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs
index 6e8be2a91..d7fd2130f 100644
--- a/library/std/src/sys/unix/thread_local_dtor.rs
+++ b/library/std/src/sys/unix/thread_local_dtor.rs
@@ -17,6 +17,7 @@
target_os = "redox",
target_os = "emscripten"
))]
+#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::mem;
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
diff --git a/library/std/src/sys/unix/thread_local_key.rs b/library/std/src/sys/unix/thread_local_key.rs
index 2c5b94b1e..2b2d079ee 100644
--- a/library/std/src/sys/unix/thread_local_key.rs
+++ b/library/std/src/sys/unix/thread_local_key.rs
@@ -27,8 +27,3 @@ pub unsafe fn destroy(key: Key) {
let r = libc::pthread_key_delete(key);
debug_assert_eq!(r, 0);
}
-
-#[inline]
-pub fn requires_synchronized_create() -> bool {
- false
-}
diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker.rs
deleted file mode 100644
index ca1a7138f..000000000
--- a/library/std/src/sys/unix/thread_parker.rs
+++ /dev/null
@@ -1,281 +0,0 @@
-//! Thread parking without `futex` using the `pthread` synchronization primitives.
-
-#![cfg(not(any(
- target_os = "linux",
- target_os = "android",
- all(target_os = "emscripten", target_feature = "atomics"),
- target_os = "freebsd",
- target_os = "openbsd",
- target_os = "dragonfly",
- target_os = "fuchsia",
-)))]
-
-use crate::cell::UnsafeCell;
-use crate::marker::PhantomPinned;
-use crate::pin::Pin;
-use crate::ptr::addr_of_mut;
-use crate::sync::atomic::AtomicUsize;
-use crate::sync::atomic::Ordering::SeqCst;
-use crate::time::Duration;
-
-const EMPTY: usize = 0;
-const PARKED: usize = 1;
-const NOTIFIED: usize = 2;
-
-unsafe fn lock(lock: *mut libc::pthread_mutex_t) {
- let r = libc::pthread_mutex_lock(lock);
- debug_assert_eq!(r, 0);
-}
-
-unsafe fn unlock(lock: *mut libc::pthread_mutex_t) {
- let r = libc::pthread_mutex_unlock(lock);
- debug_assert_eq!(r, 0);
-}
-
-unsafe fn notify_one(cond: *mut libc::pthread_cond_t) {
- let r = libc::pthread_cond_signal(cond);
- debug_assert_eq!(r, 0);
-}
-
-unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) {
- let r = libc::pthread_cond_wait(cond, lock);
- debug_assert_eq!(r, 0);
-}
-
-const TIMESPEC_MAX: libc::timespec =
- libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
-
-unsafe fn wait_timeout(
- cond: *mut libc::pthread_cond_t,
- lock: *mut libc::pthread_mutex_t,
- dur: Duration,
-) {
- // Use the system clock on systems that do not support pthread_condattr_setclock.
- // This unfortunately results in problems when the system time changes.
- #[cfg(any(
- target_os = "macos",
- target_os = "ios",
- target_os = "watchos",
- target_os = "espidf"
- ))]
- let (now, dur) = {
- use super::time::SystemTime;
- use crate::cmp::min;
-
- // OSX implementation of `pthread_cond_timedwait` is buggy
- // with super long durations. When duration is greater than
- // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
- // in macOS Sierra return error 316.
- //
- // This program demonstrates the issue:
- // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
- //
- // To work around this issue, and possible bugs of other OSes, timeout
- // is clamped to 1000 years, which is allowable per the API of `park_timeout`
- // because of spurious wakeups.
- let dur = min(dur, Duration::from_secs(1000 * 365 * 86400));
- let now = SystemTime::now().t;
- (now, dur)
- };
- // Use the monotonic clock on other systems.
- #[cfg(not(any(
- target_os = "macos",
- target_os = "ios",
- target_os = "watchos",
- target_os = "espidf"
- )))]
- let (now, dur) = {
- use super::time::Timespec;
-
- (Timespec::now(libc::CLOCK_MONOTONIC), dur)
- };
-
- let timeout =
- now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX);
- let r = libc::pthread_cond_timedwait(cond, lock, &timeout);
- debug_assert!(r == libc::ETIMEDOUT || r == 0);
-}
-
-pub struct Parker {
- state: AtomicUsize,
- lock: UnsafeCell<libc::pthread_mutex_t>,
- cvar: UnsafeCell<libc::pthread_cond_t>,
- // The `pthread` primitives require a stable address, so make this struct `!Unpin`.
- _pinned: PhantomPinned,
-}
-
-impl Parker {
- /// Construct the UNIX parker in-place.
- ///
- /// # Safety
- /// The constructed parker must never be moved.
- pub unsafe fn new(parker: *mut Parker) {
- // Use the default mutex implementation to allow for simpler initialization.
- // This could lead to undefined behaviour when deadlocking. This is avoided
- // by not deadlocking. Note in particular the unlocking operation before any
- // panic, as code after the panic could try to park again.
- addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY));
- addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
-
- cfg_if::cfg_if! {
- if #[cfg(any(
- target_os = "macos",
- target_os = "ios",
- target_os = "watchos",
- target_os = "l4re",
- target_os = "android",
- target_os = "redox"
- ))] {
- addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
- } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
- let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
- assert_eq!(r, 0);
- } else {
- use crate::mem::MaybeUninit;
- let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
- let r = libc::pthread_condattr_init(attr.as_mut_ptr());
- assert_eq!(r, 0);
- let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
- assert_eq!(r, 0);
- let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr());
- assert_eq!(r, 0);
- let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
- assert_eq!(r, 0);
- }
- }
- }
-
- // This implementation doesn't require `unsafe`, but other implementations
- // may assume this is only called by the thread that owns the Parker.
- pub unsafe fn park(self: Pin<&Self>) {
- // If we were previously notified then we consume this notification and
- // return quickly.
- if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
- return;
- }
-
- // Otherwise we need to coordinate going to sleep
- lock(self.lock.get());
- match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
- Ok(_) => {}
- Err(NOTIFIED) => {
- // We must read here, even though we know it will be `NOTIFIED`.
- // This is because `unpark` may have been called again since we read
- // `NOTIFIED` in the `compare_exchange` above. We must perform an
- // acquire operation that synchronizes with that `unpark` to observe
- // any writes it made before the call to unpark. To do that we must
- // read from the write it made to `state`.
- let old = self.state.swap(EMPTY, SeqCst);
-
- unlock(self.lock.get());
-
- assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
- return;
- } // should consume this notification, so prohibit spurious wakeups in next park.
- Err(_) => {
- unlock(self.lock.get());
-
- panic!("inconsistent park state")
- }
- }
-
- loop {
- wait(self.cvar.get(), self.lock.get());
-
- match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
- Ok(_) => break, // got a notification
- Err(_) => {} // spurious wakeup, go back to sleep
- }
- }
-
- unlock(self.lock.get());
- }
-
- // This implementation doesn't require `unsafe`, but other implementations
- // may assume this is only called by the thread that owns the Parker. Use
- // `Pin` to guarantee a stable address for the mutex and condition variable.
- pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
- // Like `park` above we have a fast path for an already-notified thread, and
- // afterwards we start coordinating for a sleep.
- // return quickly.
- if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
- return;
- }
-
- lock(self.lock.get());
- match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
- Ok(_) => {}
- Err(NOTIFIED) => {
- // We must read again here, see `park`.
- let old = self.state.swap(EMPTY, SeqCst);
- unlock(self.lock.get());
-
- assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
- return;
- } // should consume this notification, so prohibit spurious wakeups in next park.
- Err(_) => {
- unlock(self.lock.get());
- panic!("inconsistent park_timeout state")
- }
- }
-
- // Wait with a timeout, and if we spuriously wake up or otherwise wake up
- // from a notification we just want to unconditionally set the state back to
- // empty, either consuming a notification or un-flagging ourselves as
- // parked.
- wait_timeout(self.cvar.get(), self.lock.get(), dur);
-
- match self.state.swap(EMPTY, SeqCst) {
- NOTIFIED => unlock(self.lock.get()), // got a notification, hurray!
- PARKED => unlock(self.lock.get()), // no notification, alas
- n => {
- unlock(self.lock.get());
- panic!("inconsistent park_timeout state: {n}")
- }
- }
- }
-
- pub fn unpark(self: Pin<&Self>) {
- // To ensure the unparked thread will observe any writes we made
- // before this call, we must perform a release operation that `park`
- // can synchronize with. To do that we must write `NOTIFIED` even if
- // `state` is already `NOTIFIED`. That is why this must be a swap
- // rather than a compare-and-swap that returns if it reads `NOTIFIED`
- // on failure.
- match self.state.swap(NOTIFIED, SeqCst) {
- EMPTY => return, // no one was waiting
- NOTIFIED => return, // already unparked
- PARKED => {} // gotta go wake someone up
- _ => panic!("inconsistent state in unpark"),
- }
-
- // There is a period between when the parked thread sets `state` to
- // `PARKED` (or last checked `state` in the case of a spurious wake
- // up) and when it actually waits on `cvar`. If we were to notify
- // during this period it would be ignored and then when the parked
- // thread went to sleep it would never wake up. Fortunately, it has
- // `lock` locked at this stage so we can acquire `lock` to wait until
- // it is ready to receive the notification.
- //
- // Releasing `lock` before the call to `notify_one` means that when the
- // parked thread wakes it doesn't get woken only to have to wait for us
- // to release `lock`.
- unsafe {
- lock(self.lock.get());
- unlock(self.lock.get());
- notify_one(self.cvar.get());
- }
- }
-}
-
-impl Drop for Parker {
- fn drop(&mut self) {
- unsafe {
- libc::pthread_cond_destroy(self.cvar.get_mut());
- libc::pthread_mutex_destroy(self.lock.get_mut());
- }
- }
-}
-
-unsafe impl Sync for Parker {}
-unsafe impl Send for Parker {}
diff --git a/library/std/src/sys/unix/thread_parker/darwin.rs b/library/std/src/sys/unix/thread_parker/darwin.rs
new file mode 100644
index 000000000..2f5356fe2
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker/darwin.rs
@@ -0,0 +1,131 @@
+//! Thread parking for Darwin-based systems.
+//!
+//! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they
+//! cannot be used in `std` because they are non-public (their use will lead to
+//! rejection from the App Store) and because they are only available starting
+//! with macOS version 10.12, even though the minimum target version is 10.7.
+//!
+//! Therefore, we need to look for other synchronization primitives. Luckily, Darwin
+//! supports semaphores, which allow us to implement the behaviour we need with
+//! only one primitive (as opposed to a mutex-condvar pair). We use the semaphore
+//! provided by libdispatch, as the underlying Mach semaphore is only dubiously
+//! public.
+
+use crate::pin::Pin;
+use crate::sync::atomic::{
+ AtomicI8,
+ Ordering::{Acquire, Release},
+};
+use crate::time::Duration;
+
+type dispatch_semaphore_t = *mut crate::ffi::c_void;
+type dispatch_time_t = u64;
+
+const DISPATCH_TIME_NOW: dispatch_time_t = 0;
+const DISPATCH_TIME_FOREVER: dispatch_time_t = !0;
+
+// Contained in libSystem.dylib, which is linked by default.
+extern "C" {
+ fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t;
+ fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t;
+ fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize;
+ fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) -> isize;
+ fn dispatch_release(object: *mut crate::ffi::c_void);
+}
+
+const EMPTY: i8 = 0;
+const NOTIFIED: i8 = 1;
+const PARKED: i8 = -1;
+
+pub struct Parker {
+ semaphore: dispatch_semaphore_t,
+ state: AtomicI8,
+}
+
+unsafe impl Sync for Parker {}
+unsafe impl Send for Parker {}
+
+impl Parker {
+ pub unsafe fn new(parker: *mut Parker) {
+ let semaphore = dispatch_semaphore_create(0);
+ assert!(
+ !semaphore.is_null(),
+ "failed to create dispatch semaphore for thread synchronization"
+ );
+ parker.write(Parker { semaphore, state: AtomicI8::new(EMPTY) })
+ }
+
+ // Does not need `Pin`, but other implementation do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // The semaphore counter must be zero at this point, because unparking
+ // threads will not actually increase it until we signalled that we
+ // are waiting.
+
+ // Change NOTIFIED to EMPTY and EMPTY to PARKED.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+
+ // Another thread may increase the semaphore counter from this point on.
+ // If it is faster than us, we will decrement it again immediately below.
+ // If we are faster, we wait.
+
+ // Ensure that the semaphore counter has actually been decremented, even
+ // if the call timed out for some reason.
+ while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {}
+
+ // At this point, the semaphore counter is zero again.
+
+ // We were definitely woken up, so we don't need to check the state.
+ // Still, we need to reset the state using a swap to observe the state
+ // change with acquire ordering.
+ self.state.swap(EMPTY, Acquire);
+ }
+
+ // Does not need `Pin`, but other implementation do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+
+ let nanos = dur.as_nanos().try_into().unwrap_or(i64::MAX);
+ let timeout = dispatch_time(DISPATCH_TIME_NOW, nanos);
+
+ let timeout = dispatch_semaphore_wait(self.semaphore, timeout) != 0;
+
+ let state = self.state.swap(EMPTY, Acquire);
+ if state == NOTIFIED && timeout {
+ // If the state was NOTIFIED but semaphore_wait returned without
+ // decrementing the count because of a timeout, it means another
+ // thread is about to call semaphore_signal. We must wait for that
+ // to happen to ensure the semaphore count is reset.
+ while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {}
+ } else {
+ // Either a timeout occurred and we reset the state before any thread
+ // tried to wake us up, or we were woken up and reset the state,
+ // making sure to observe the state change with acquire ordering.
+ // Either way, the semaphore counter is now zero again.
+ }
+ }
+
+ // Does not need `Pin`, but other implementation do.
+ pub fn unpark(self: Pin<&Self>) {
+ let state = self.state.swap(NOTIFIED, Release);
+ if state == PARKED {
+ unsafe {
+ dispatch_semaphore_signal(self.semaphore);
+ }
+ }
+ }
+}
+
+impl Drop for Parker {
+ fn drop(&mut self) {
+ // SAFETY:
+ // We always ensure that the semaphore count is reset, so this will
+ // never cause an exception.
+ unsafe {
+ dispatch_release(self.semaphore);
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/thread_parker/mod.rs b/library/std/src/sys/unix/thread_parker/mod.rs
new file mode 100644
index 000000000..35f1e68a8
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker/mod.rs
@@ -0,0 +1,32 @@
+//! Thread parking on systems without futex support.
+
+#![cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+)))]
+
+cfg_if::cfg_if! {
+ if #[cfg(all(
+ any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "tvos",
+ ),
+ not(miri),
+ ))] {
+ mod darwin;
+ pub use darwin::Parker;
+ } else if #[cfg(target_os = "netbsd")] {
+ mod netbsd;
+ pub use netbsd::Parker;
+ } else {
+ mod pthread;
+ pub use pthread::Parker;
+ }
+}
diff --git a/library/std/src/sys/unix/thread_parker/netbsd.rs b/library/std/src/sys/unix/thread_parker/netbsd.rs
new file mode 100644
index 000000000..7657605b5
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker/netbsd.rs
@@ -0,0 +1,113 @@
+use crate::ffi::{c_int, c_void};
+use crate::pin::Pin;
+use crate::ptr::{null, null_mut};
+use crate::sync::atomic::{
+ AtomicU64,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::time::Duration;
+use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
+
+extern "C" {
+ fn ___lwp_park60(
+ clock_id: clockid_t,
+ flags: c_int,
+ ts: *mut timespec,
+ unpark: lwpid_t,
+ hint: *const c_void,
+ unparkhint: *const c_void,
+ ) -> c_int;
+ fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
+}
+
+/// The thread is not parked and the token is not available.
+///
+/// Zero cannot be a valid LWP id, since it is used as empty value for the unpark
+/// argument in _lwp_park.
+const EMPTY: u64 = 0;
+/// The token is available. Do not park anymore.
+const NOTIFIED: u64 = u64::MAX;
+
+pub struct Parker {
+ /// The parker state. Contains either one of the two state values above or the LWP
+ /// id of the parked thread.
+ state: AtomicU64,
+}
+
+impl Parker {
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Parker { state: AtomicU64::new(EMPTY) })
+ }
+
+ // Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // If the token has already been made available, we can skip
+ // a bit of work, so check for it here.
+ if self.state.load(Acquire) != NOTIFIED {
+ let parked = _lwp_self() as u64;
+ let hint = self.state.as_mut_ptr().cast();
+ if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
+ // Loop to guard against spurious wakeups.
+ loop {
+ ___lwp_park60(0, 0, null_mut(), 0, hint, null());
+ if self.state.load(Acquire) == NOTIFIED {
+ break;
+ }
+ }
+ }
+ }
+
+ // At this point, the change to NOTIFIED has always been observed with acquire
+ // ordering, so we can just use a relaxed store here (instead of a swap).
+ self.state.store(EMPTY, Relaxed);
+ }
+
+ // Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ if self.state.load(Acquire) != NOTIFIED {
+ let parked = _lwp_self() as u64;
+ let hint = self.state.as_mut_ptr().cast();
+ let mut timeout = timespec {
+ // Saturate so that the operation will definitely time out
+ // (even if it is after the heat death of the universe).
+ tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
+ tv_nsec: dur.subsec_nanos().into(),
+ };
+
+ if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
+ // Timeout needs to be mutable since it is modified on NetBSD 9.0 and
+ // above.
+ ___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, hint, null());
+ // Use a swap to get acquire ordering even if the token was set after
+ // the timeout occurred.
+ self.state.swap(EMPTY, Acquire);
+ return;
+ }
+ }
+
+ self.state.store(EMPTY, Relaxed);
+ }
+
+ // Does not actually need `Pin`, but the pthread implementation does.
+ pub fn unpark(self: Pin<&Self>) {
+ let state = self.state.swap(NOTIFIED, Release);
+ if !matches!(state, EMPTY | NOTIFIED) {
+ let lwp = state as lwpid_t;
+ let hint = self.state.as_mut_ptr().cast();
+
+ // If the parking thread terminated and did not actually park, this will
+ // probably return an error, which is OK. In the worst case, another
+ // thread has received the same LWP id. It will then receive a spurious
+ // wakeup, but those are allowable per the API contract. The same reasoning
+ // applies if a timeout occurred before this call, but the state was not
+ // yet reset.
+
+ // SAFETY:
+ // The syscall has no invariants to hold. Only unsafe because it is an
+ // extern function.
+ unsafe {
+ _lwp_unpark(lwp, hint);
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/thread_parker/pthread.rs b/library/std/src/sys/unix/thread_parker/pthread.rs
new file mode 100644
index 000000000..3dfc0026e
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker/pthread.rs
@@ -0,0 +1,271 @@
+//! Thread parking without `futex` using the `pthread` synchronization primitives.
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomPinned;
+use crate::pin::Pin;
+use crate::ptr::addr_of_mut;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+unsafe fn lock(lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_mutex_lock(lock);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn unlock(lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_mutex_unlock(lock);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn notify_one(cond: *mut libc::pthread_cond_t) {
+ let r = libc::pthread_cond_signal(cond);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_cond_wait(cond, lock);
+ debug_assert_eq!(r, 0);
+}
+
+const TIMESPEC_MAX: libc::timespec =
+ libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
+
+unsafe fn wait_timeout(
+ cond: *mut libc::pthread_cond_t,
+ lock: *mut libc::pthread_mutex_t,
+ dur: Duration,
+) {
+ // Use the system clock on systems that do not support pthread_condattr_setclock.
+ // This unfortunately results in problems when the system time changes.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "espidf"
+ ))]
+ let (now, dur) = {
+ use crate::cmp::min;
+ use crate::sys::time::SystemTime;
+
+ // OSX implementation of `pthread_cond_timedwait` is buggy
+ // with super long durations. When duration is greater than
+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+ // in macOS Sierra return error 316.
+ //
+ // This program demonstrates the issue:
+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+ //
+ // To work around this issue, and possible bugs of other OSes, timeout
+ // is clamped to 1000 years, which is allowable per the API of `park_timeout`
+ // because of spurious wakeups.
+ let dur = min(dur, Duration::from_secs(1000 * 365 * 86400));
+ let now = SystemTime::now().t;
+ (now, dur)
+ };
+ // Use the monotonic clock on other systems.
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "espidf"
+ )))]
+ let (now, dur) = {
+ use crate::sys::time::Timespec;
+
+ (Timespec::now(libc::CLOCK_MONOTONIC), dur)
+ };
+
+ let timeout =
+ now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX);
+ let r = libc::pthread_cond_timedwait(cond, lock, &timeout);
+ debug_assert!(r == libc::ETIMEDOUT || r == 0);
+}
+
+pub struct Parker {
+ state: AtomicUsize,
+ lock: UnsafeCell<libc::pthread_mutex_t>,
+ cvar: UnsafeCell<libc::pthread_cond_t>,
+ // The `pthread` primitives require a stable address, so make this struct `!Unpin`.
+ _pinned: PhantomPinned,
+}
+
+impl Parker {
+ /// Construct the UNIX parker in-place.
+ ///
+ /// # Safety
+ /// The constructed parker must never be moved.
+ pub unsafe fn new(parker: *mut Parker) {
+ // Use the default mutex implementation to allow for simpler initialization.
+ // This could lead to undefined behaviour when deadlocking. This is avoided
+ // by not deadlocking. Note in particular the unlocking operation before any
+ // panic, as code after the panic could try to park again.
+ addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY));
+ addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
+
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "l4re",
+ target_os = "android",
+ target_os = "redox"
+ ))] {
+ addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
+ } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
+ let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
+ assert_eq!(r, 0);
+ } else {
+ use crate::mem::MaybeUninit;
+ let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
+ let r = libc::pthread_condattr_init(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
+ assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ }
+ }
+ }
+
+ // This implementation doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ lock(self.lock.get());
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read here, even though we know it will be `NOTIFIED`.
+ // This is because `unpark` may have been called again since we read
+ // `NOTIFIED` in the `compare_exchange` above. We must perform an
+ // acquire operation that synchronizes with that `unpark` to observe
+ // any writes it made before the call to unpark. To do that we must
+ // read from the write it made to `state`.
+ let old = self.state.swap(EMPTY, SeqCst);
+
+ unlock(self.lock.get());
+
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => {
+ unlock(self.lock.get());
+
+ panic!("inconsistent park state")
+ }
+ }
+
+ loop {
+ wait(self.cvar.get(), self.lock.get());
+
+ match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => break, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+
+ unlock(self.lock.get());
+ }
+
+ // This implementation doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker. Use
+ // `Pin` to guarantee a stable address for the mutex and condition variable.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ lock(self.lock.get());
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read again here, see `park`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ unlock(self.lock.get());
+
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => {
+ unlock(self.lock.get());
+ panic!("inconsistent park_timeout state")
+ }
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ wait_timeout(self.cvar.get(), self.lock.get(), dur);
+
+ match self.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => unlock(self.lock.get()), // got a notification, hurray!
+ PARKED => unlock(self.lock.get()), // no notification, alas
+ n => {
+ unlock(self.lock.get());
+ panic!("inconsistent park_timeout state: {n}")
+ }
+ }
+ }
+
+ pub fn unpark(self: Pin<&Self>) {
+ // To ensure the unparked thread will observe any writes we made
+ // before this call, we must perform a release operation that `park`
+ // can synchronize with. To do that we must write `NOTIFIED` even if
+ // `state` is already `NOTIFIED`. That is why this must be a swap
+ // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+ // on failure.
+ match self.state.swap(NOTIFIED, SeqCst) {
+ EMPTY => return, // no one was waiting
+ NOTIFIED => return, // already unparked
+ PARKED => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // There is a period between when the parked thread sets `state` to
+ // `PARKED` (or last checked `state` in the case of a spurious wake
+ // up) and when it actually waits on `cvar`. If we were to notify
+ // during this period it would be ignored and then when the parked
+ // thread went to sleep it would never wake up. Fortunately, it has
+ // `lock` locked at this stage so we can acquire `lock` to wait until
+ // it is ready to receive the notification.
+ //
+ // Releasing `lock` before the call to `notify_one` means that when the
+ // parked thread wakes it doesn't get woken only to have to wait for us
+ // to release `lock`.
+ unsafe {
+ lock(self.lock.get());
+ unlock(self.lock.get());
+ notify_one(self.cvar.get());
+ }
+ }
+}
+
+impl Drop for Parker {
+ fn drop(&mut self) {
+ unsafe {
+ libc::pthread_cond_destroy(self.cvar.get_mut());
+ libc::pthread_mutex_destroy(self.lock.get_mut());
+ }
+ }
+}
+
+unsafe impl Sync for Parker {}
+unsafe impl Send for Parker {}
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index dff973f59..cca9c6767 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -7,6 +7,12 @@ const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+#[rustc_layout_scalar_valid_range_end(999_999_999)]
+struct Nanoseconds(u32);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
pub(in crate::sys::unix) t: Timespec,
}
@@ -14,7 +20,7 @@ pub struct SystemTime {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(in crate::sys::unix) struct Timespec {
tv_sec: i64,
- tv_nsec: i64,
+ tv_nsec: Nanoseconds,
}
impl SystemTime {
@@ -46,18 +52,20 @@ impl fmt::Debug for SystemTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemTime")
.field("tv_sec", &self.t.tv_sec)
- .field("tv_nsec", &self.t.tv_nsec)
+ .field("tv_nsec", &self.t.tv_nsec.0)
.finish()
}
}
impl Timespec {
pub const fn zero() -> Timespec {
- Timespec { tv_sec: 0, tv_nsec: 0 }
+ Timespec::new(0, 0)
}
- fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
- Timespec { tv_sec, tv_nsec }
+ const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
+ assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
+ // SAFETY: The assert above checks tv_nsec is within the valid range
+ Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
}
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
@@ -75,12 +83,12 @@ impl Timespec {
//
// Ideally this code could be rearranged such that it more
// directly expresses the lower-cost behavior we want from it.
- let (secs, nsec) = if self.tv_nsec >= other.tv_nsec {
- ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32)
+ let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 {
+ ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0)
} else {
(
(self.tv_sec - other.tv_sec - 1) as u64,
- self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32,
+ self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0,
)
};
@@ -102,7 +110,7 @@ impl Timespec {
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
- let mut nsec = other.subsec_nanos() + self.tv_nsec as u32;
+ let mut nsec = other.subsec_nanos() + self.tv_nsec.0;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1)?;
@@ -118,7 +126,7 @@ impl Timespec {
.and_then(|secs| self.tv_sec.checked_sub(secs))?;
// Similar to above, nanos can't overflow.
- let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32;
+ let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
if nsec < 0 {
nsec += NSEC_PER_SEC as i32;
secs = secs.checked_sub(1)?;
@@ -130,7 +138,7 @@ impl Timespec {
pub fn to_timespec(&self) -> Option<libc::timespec> {
Some(libc::timespec {
tv_sec: self.tv_sec.try_into().ok()?,
- tv_nsec: self.tv_nsec.try_into().ok()?,
+ tv_nsec: self.tv_nsec.0.try_into().ok()?,
})
}
}
@@ -293,7 +301,7 @@ mod inner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instant")
.field("tv_sec", &self.t.tv_sec)
- .field("tv_nsec", &self.t.tv_nsec)
+ .field("tv_nsec", &self.t.tv_nsec.0)
.finish()
}
}
@@ -334,7 +342,7 @@ mod inner {
let mut t = MaybeUninit::uninit();
cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
let t = unsafe { t.assume_init() };
- return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 };
+ return Timespec::new(t.tv_sec, t.tv_nsec as i64);
}
}
diff --git a/library/std/src/sys/unsupported/alloc.rs b/library/std/src/sys/unsupported/alloc.rs
index 8d5d0a2f5..d715ae454 100644
--- a/library/std/src/sys/unsupported/alloc.rs
+++ b/library/std/src/sys/unsupported/alloc.rs
@@ -1,15 +1,16 @@
use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr::null_mut;
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
- 0 as *mut u8
+ null_mut()
}
#[inline]
unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 {
- 0 as *mut u8
+ null_mut()
}
#[inline]
@@ -17,6 +18,6 @@ unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn realloc(&self, _ptr: *mut u8, _layout: Layout, _new_size: usize) -> *mut u8 {
- 0 as *mut u8
+ null_mut()
}
}
diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs
index 4c9ade4a8..5cd9e57de 100644
--- a/library/std/src/sys/unsupported/common.rs
+++ b/library/std/src/sys/unsupported/common.rs
@@ -6,7 +6,7 @@ pub mod memchr {
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs
index 0e1a6257e..6ac1b5d2b 100644
--- a/library/std/src/sys/unsupported/fs.rs
+++ b/library/std/src/sys/unsupported/fs.rs
@@ -1,7 +1,7 @@
use crate::ffi::OsString;
use crate::fmt;
use crate::hash::{Hash, Hasher};
-use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::path::{Path, PathBuf};
use crate::sys::time::SystemTime;
use crate::sys::unsupported;
@@ -214,7 +214,7 @@ impl File {
self.0
}
- pub fn read_buf(&self, _buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
self.0
}
diff --git a/library/std/src/sys/unsupported/io.rs b/library/std/src/sys/unsupported/io.rs
index d5f475b43..82610ffab 100644
--- a/library/std/src/sys/unsupported/io.rs
+++ b/library/std/src/sys/unsupported/io.rs
@@ -45,3 +45,7 @@ impl<'a> IoSliceMut<'a> {
self.0
}
}
+
+pub fn is_terminal<T>(_: &T) -> bool {
+ false
+}
diff --git a/library/std/src/sys/unsupported/locks/condvar.rs b/library/std/src/sys/unsupported/locks/condvar.rs
index e703fd0d2..527a26a12 100644
--- a/library/std/src/sys/unsupported/locks/condvar.rs
+++ b/library/std/src/sys/unsupported/locks/condvar.rs
@@ -7,6 +7,7 @@ pub type MovableCondvar = Condvar;
impl Condvar {
#[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> Condvar {
Condvar {}
}
diff --git a/library/std/src/sys/unsupported/locks/mod.rs b/library/std/src/sys/unsupported/locks/mod.rs
index d412ff152..602a2d623 100644
--- a/library/std/src/sys/unsupported/locks/mod.rs
+++ b/library/std/src/sys/unsupported/locks/mod.rs
@@ -3,4 +3,4 @@ mod mutex;
mod rwlock;
pub use condvar::{Condvar, MovableCondvar};
pub use mutex::{MovableMutex, Mutex};
-pub use rwlock::{MovableRwLock, RwLock};
+pub use rwlock::MovableRwLock;
diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs
index d7cb12e0c..87ea475c6 100644
--- a/library/std/src/sys/unsupported/locks/mutex.rs
+++ b/library/std/src/sys/unsupported/locks/mutex.rs
@@ -12,14 +12,12 @@ unsafe impl Sync for Mutex {} // no threads on this platform
impl Mutex {
#[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> Mutex {
Mutex { locked: Cell::new(false) }
}
#[inline]
- pub unsafe fn init(&mut self) {}
-
- #[inline]
pub unsafe fn lock(&self) {
assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex");
}
diff --git a/library/std/src/sys/unsupported/locks/rwlock.rs b/library/std/src/sys/unsupported/locks/rwlock.rs
index aca5fb715..5292691b9 100644
--- a/library/std/src/sys/unsupported/locks/rwlock.rs
+++ b/library/std/src/sys/unsupported/locks/rwlock.rs
@@ -12,6 +12,7 @@ unsafe impl Sync for RwLock {} // no threads on this platform
impl RwLock {
#[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> RwLock {
RwLock { mode: Cell::new(0) }
}
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 42a1ff730..633f17c05 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -200,6 +200,9 @@ impl<'a> Iterator for CommandArgs<'a> {
fn next(&mut self) -> Option<&'a OsStr> {
None
}
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (0, Some(0))
+ }
}
impl<'a> ExactSizeIterator for CommandArgs<'a> {}
diff --git a/library/std/src/sys/unsupported/thread_local_dtor.rs b/library/std/src/sys/unsupported/thread_local_dtor.rs
index 85d660983..84660ea58 100644
--- a/library/std/src/sys/unsupported/thread_local_dtor.rs
+++ b/library/std/src/sys/unsupported/thread_local_dtor.rs
@@ -1,5 +1,6 @@
#![unstable(feature = "thread_local_internals", issue = "none")]
+#[cfg_attr(target_family = "wasm", allow(unused))] // unused on wasm32-unknown-unknown
pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern "C" fn(*mut u8)) {
// FIXME: right now there is no concept of "thread exit", but this is likely
// going to show up at some point in the form of an exported symbol that the
diff --git a/library/std/src/sys/unsupported/thread_local_key.rs b/library/std/src/sys/unsupported/thread_local_key.rs
index c31b61cbf..b6e5e4cd2 100644
--- a/library/std/src/sys/unsupported/thread_local_key.rs
+++ b/library/std/src/sys/unsupported/thread_local_key.rs
@@ -19,8 +19,3 @@ pub unsafe fn get(_key: Key) -> *mut u8 {
pub unsafe fn destroy(_key: Key) {
panic!("should not be used on this target");
}
-
-#[inline]
-pub fn requires_synchronized_create() -> bool {
- panic!("should not be used on this target");
-}
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index 6614ae397..d4866bbc3 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -1,9 +1,9 @@
#![deny(unsafe_op_in_unsafe_fn)]
use super::fd::WasiFd;
-use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::iter;
use crate::mem::{self, ManuallyDrop};
use crate::os::raw::c_int;
@@ -12,6 +12,7 @@ use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
+use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::time::SystemTime;
use crate::sys::unsupported;
use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -65,8 +66,8 @@ pub struct FilePermissions {
#[derive(Copy, Clone, Debug, Default)]
pub struct FileTimes {
- accessed: Option<wasi::Timestamp>,
- modified: Option<wasi::Timestamp>,
+ accessed: Option<SystemTime>,
+ modified: Option<SystemTime>,
}
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
@@ -120,11 +121,11 @@ impl FilePermissions {
impl FileTimes {
pub fn set_accessed(&mut self, t: SystemTime) {
- self.accessed = Some(t.to_wasi_timestamp_or_panic());
+ self.accessed = Some(t);
}
pub fn set_modified(&mut self, t: SystemTime) {
- self.modified = Some(t.to_wasi_timestamp_or_panic());
+ self.modified = Some(t);
}
}
@@ -439,8 +440,8 @@ impl File {
true
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- crate::io::default_read_buf(|buf| self.read(buf), buf)
+ pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|buf| self.read(buf), cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
@@ -476,9 +477,16 @@ impl File {
}
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+ let to_timestamp = |time: Option<SystemTime>| {
+ match time {
+ Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts),
+ Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")),
+ None => Ok(0),
+ }
+ };
self.fd.filestat_set_times(
- times.accessed.unwrap_or(0),
- times.modified.unwrap_or(0),
+ to_timestamp(times.accessed)?,
+ to_timestamp(times.modified)?,
times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM)
| times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM),
)
@@ -687,51 +695,52 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
/// Note that this can fail if `p` doesn't look like it can be opened relative
/// to any pre-opened file descriptor.
fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
- let p = CString::new(p.as_os_str().as_bytes())?;
- let mut buf = Vec::<u8>::with_capacity(512);
- loop {
- unsafe {
- let mut relative_path = buf.as_ptr().cast();
- let mut abs_prefix = ptr::null();
- let fd = __wasilibc_find_relpath(
- p.as_ptr(),
- &mut abs_prefix,
- &mut relative_path,
- buf.capacity(),
- );
- if fd == -1 {
- if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
- // Trigger the internal buffer resizing logic of `Vec` by requiring
- // more space than the current capacity.
- let cap = buf.capacity();
- buf.set_len(cap);
- buf.reserve(1);
- continue;
- }
- let msg = format!(
- "failed to find a pre-opened file descriptor \
- through which {:?} could be opened",
- p
+ run_path_with_cstr(p, |p| {
+ let mut buf = Vec::<u8>::with_capacity(512);
+ loop {
+ unsafe {
+ let mut relative_path = buf.as_ptr().cast();
+ let mut abs_prefix = ptr::null();
+ let fd = __wasilibc_find_relpath(
+ p.as_ptr(),
+ &mut abs_prefix,
+ &mut relative_path,
+ buf.capacity(),
);
- return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
- }
- let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
+ if fd == -1 {
+ if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
+ // Trigger the internal buffer resizing logic of `Vec` by requiring
+ // more space than the current capacity.
+ let cap = buf.capacity();
+ buf.set_len(cap);
+ buf.reserve(1);
+ continue;
+ }
+ let msg = format!(
+ "failed to find a pre-opened file descriptor \
+ through which {:?} could be opened",
+ p
+ );
+ return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
+ }
+ let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
- return Ok((
- ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
- PathBuf::from(OsString::from_vec(relative)),
- ));
+ return Ok((
+ ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
+ PathBuf::from(OsString::from_vec(relative)),
+ ));
+ }
}
- }
- extern "C" {
- pub fn __wasilibc_find_relpath(
- path: *const libc::c_char,
- abs_prefix: *mut *const libc::c_char,
- relative_path: *mut *const libc::c_char,
- relative_path_len: libc::size_t,
- ) -> libc::c_int;
- }
+ extern "C" {
+ pub fn __wasilibc_find_relpath(
+ path: *const libc::c_char,
+ abs_prefix: *mut *const libc::c_char,
+ relative_path: *mut *const libc::c_char,
+ relative_path_len: libc::size_t,
+ ) -> libc::c_int;
+ }
+ })
}
pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
diff --git a/library/std/src/sys/wasi/io.rs b/library/std/src/sys/wasi/io.rs
index ee017d13a..2cd45df88 100644
--- a/library/std/src/sys/wasi/io.rs
+++ b/library/std/src/sys/wasi/io.rs
@@ -1,6 +1,7 @@
#![deny(unsafe_op_in_unsafe_fn)]
use crate::marker::PhantomData;
+use crate::os::fd::{AsFd, AsRawFd};
use crate::slice;
#[derive(Copy, Clone)]
@@ -71,3 +72,8 @@ impl<'a> IoSliceMut<'a> {
unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) }
}
}
+
+pub fn is_terminal(fd: &impl AsFd) -> bool {
+ let fd = fd.as_fd();
+ unsafe { libc::isatty(fd.as_raw_fd()) != 0 }
+}
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index 683a07a34..c8c47763a 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -25,6 +25,9 @@ pub mod cmath;
pub mod env;
pub mod fd;
pub mod fs;
+#[allow(unused)]
+#[path = "../wasm/atomics/futex.rs"]
+pub mod futex;
pub mod io;
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
index c5229a188..f5513e999 100644
--- a/library/std/src/sys/wasi/os.rs
+++ b/library/std/src/sys/wasi/os.rs
@@ -1,14 +1,15 @@
#![deny(unsafe_op_in_unsafe_fn)]
-use crate::any::Any;
use crate::error::Error as StdError;
-use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
+use crate::ops::Drop;
use crate::os::wasi::prelude::*;
use crate::path::{self, PathBuf};
use crate::str;
+use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
use crate::sys::memchr;
use crate::sys::unsupported;
use crate::vec;
@@ -23,10 +24,26 @@ mod libc {
}
}
-#[cfg(not(target_feature = "atomics"))]
-pub unsafe fn env_lock() -> impl Any {
- // No need for a lock if we're single-threaded, but this function will need
- // to get implemented for multi-threaded scenarios
+cfg_if::cfg_if! {
+ if #[cfg(target_feature = "atomics")] {
+ // Access to the environment must be protected by a lock in multi-threaded scenarios.
+ use crate::sync::{PoisonError, RwLock};
+ static ENV_LOCK: RwLock<()> = RwLock::new(());
+ pub fn env_read_lock() -> impl Drop {
+ ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
+ }
+ pub fn env_write_lock() -> impl Drop {
+ ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner)
+ }
+ } else {
+ // No need for a lock if we are single-threaded.
+ pub fn env_read_lock() -> impl Drop {
+ Box::new(())
+ }
+ pub fn env_write_lock() -> impl Drop {
+ Box::new(())
+ }
+ }
}
pub fn errno() -> i32 {
@@ -77,13 +94,10 @@ pub fn getcwd() -> io::Result<PathBuf> {
}
pub fn chdir(p: &path::Path) -> io::Result<()> {
- let p: &OsStr = p.as_ref();
- let p = CString::new(p.as_bytes())?;
- unsafe {
- match libc::chdir(p.as_ptr()) == (0 as libc::c_int) {
- true => Ok(()),
- false => Err(io::Error::last_os_error()),
- }
+ let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
+ match result == (0 as libc::c_int) {
+ true => Ok(()),
+ false => Err(io::Error::last_os_error()),
}
}
@@ -146,7 +160,7 @@ impl Iterator for Env {
pub fn env() -> Env {
unsafe {
- let _guard = env_lock();
+ let _guard = env_read_lock();
let mut environ = libc::environ;
let mut result = Vec::new();
if !environ.is_null() {
@@ -176,35 +190,32 @@ pub fn env() -> Env {
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
- let k = CString::new(k.as_bytes()).ok()?;
- unsafe {
- let _guard = env_lock();
- let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
- if s.is_null() {
- None
- } else {
- Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
- }
+ let s = run_with_cstr(k.as_bytes(), |k| unsafe {
+ let _guard = env_read_lock();
+ Ok(libc::getenv(k.as_ptr()) as *const libc::c_char)
+ })
+ .ok()?;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec()))
}
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
- let k = CString::new(k.as_bytes())?;
- let v = CString::new(v.as_bytes())?;
-
- unsafe {
- let _guard = env_lock();
- cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
- }
+ run_with_cstr(k.as_bytes(), |k| {
+ run_with_cstr(v.as_bytes(), |v| unsafe {
+ let _guard = env_write_lock();
+ cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+ })
+ })
}
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
- let nbuf = CString::new(n.as_bytes())?;
-
- unsafe {
- let _guard = env_lock();
+ run_with_cstr(n.as_bytes(), |nbuf| unsafe {
+ let _guard = env_write_lock();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
- }
+ })
}
pub fn temp_dir() -> PathBuf {
diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs
index 3d326e491..016b06efb 100644
--- a/library/std/src/sys/wasi/time.rs
+++ b/library/std/src/sys/wasi/time.rs
@@ -47,8 +47,8 @@ impl SystemTime {
SystemTime(Duration::from_nanos(ts))
}
- pub fn to_wasi_timestamp_or_panic(&self) -> wasi::Timestamp {
- self.0.as_nanos().try_into().expect("time does not fit in WASI timestamp")
+ pub fn to_wasi_timestamp(&self) -> Option<wasi::Timestamp> {
+ self.0.as_nanos().try_into().ok()
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index 4159efe2a..93838390b 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -57,7 +57,7 @@ cfg_if::cfg_if! {
mod futex_rwlock;
pub(crate) use futex_condvar::{Condvar, MovableCondvar};
pub(crate) use futex_mutex::{Mutex, MovableMutex};
- pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use futex_rwlock::MovableRwLock;
}
#[path = "atomics/futex.rs"]
pub mod futex;
diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs
index fdc81cdea..d53ea1600 100644
--- a/library/std/src/sys/windows/alloc.rs
+++ b/library/std/src/sys/windows/alloc.rs
@@ -16,6 +16,7 @@ mod tests;
// Flag to indicate that the memory returned by `HeapAlloc` should be zeroed.
const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
+#[link(name = "kernel32")]
extern "system" {
// Get a handle to the default heap of the current process, or null if the operation fails.
//
@@ -168,7 +169,7 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
// SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned`
// is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before
// it, it is safe to write a header directly before it.
- unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) };
+ unsafe { ptr::write((aligned as *mut Header).sub(1), Header(ptr)) };
// SAFETY: The returned pointer does not point to the to the start of an allocated block,
// but there is a header readable directly before it containing the location of the start
@@ -213,7 +214,7 @@ unsafe impl GlobalAlloc for System {
// SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null
// and have a header readable directly before it.
- unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 }
+ unsafe { ptr::read((ptr as *mut Header).sub(1)).0 }
}
};
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 478068c73..be6fc2ebb 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -66,10 +66,12 @@ pub type LPSYSTEM_INFO = *mut SYSTEM_INFO;
pub type LPWSABUF = *mut WSABUF;
pub type LPWSAOVERLAPPED = *mut c_void;
pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void;
+pub type BCRYPT_ALG_HANDLE = LPVOID;
pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
pub type PLARGE_INTEGER = *mut c_longlong;
pub type PSRWLOCK = *mut SRWLOCK;
+pub type LPINIT_ONCE = *mut INIT_ONCE;
pub type SOCKET = crate::os::windows::raw::SOCKET;
pub type socklen_t = c_int;
@@ -125,6 +127,10 @@ pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000;
pub const FIONBIO: c_ulong = 0x8004667e;
+pub const MAX_PATH: usize = 260;
+
+pub const FILE_TYPE_PIPE: u32 = 3;
+
#[repr(C)]
#[derive(Copy)]
pub struct WIN32_FIND_DATAW {
@@ -193,6 +199,9 @@ pub const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002;
pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { ptr: ptr::null_mut() };
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: ptr::null_mut() };
+pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { ptr: ptr::null_mut() };
+
+pub const INIT_ONCE_INIT_FAILED: DWORD = 0x00000004;
pub const DETACHED_PROCESS: DWORD = 0x00000008;
pub const CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200;
@@ -285,6 +294,8 @@ pub fn nt_success(status: NTSTATUS) -> bool {
status >= 0
}
+// "RNG\0"
+pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
#[repr(C)]
@@ -455,6 +466,12 @@ pub enum FILE_INFO_BY_HANDLE_CLASS {
}
#[repr(C)]
+pub struct FILE_ATTRIBUTE_TAG_INFO {
+ pub FileAttributes: DWORD,
+ pub ReparseTag: DWORD,
+}
+
+#[repr(C)]
pub struct FILE_DISPOSITION_INFO {
pub DeleteFile: BOOLEAN,
}
@@ -501,6 +518,8 @@ pub struct FILE_END_OF_FILE_INFO {
pub EndOfFile: LARGE_INTEGER,
}
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `rest` field!
#[repr(C)]
pub struct REPARSE_DATA_BUFFER {
pub ReparseTag: c_uint,
@@ -509,6 +528,8 @@ pub struct REPARSE_DATA_BUFFER {
pub rest: (),
}
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
#[repr(C)]
pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
pub SubstituteNameOffset: c_ushort,
@@ -519,6 +540,14 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
pub PathBuffer: WCHAR,
}
+/// NB: Use carefully! In general using this as a reference is likely to get the
+/// provenance wrong for the `PathBuffer` field!
+#[repr(C)]
+pub struct FILE_NAME_INFO {
+ pub FileNameLength: DWORD,
+ pub FileName: [WCHAR; 1],
+}
+
#[repr(C)]
pub struct MOUNT_POINT_REPARSE_BUFFER {
pub SubstituteNameOffset: c_ushort,
@@ -550,6 +579,10 @@ pub struct CONDITION_VARIABLE {
pub struct SRWLOCK {
pub ptr: LPVOID,
}
+#[repr(C)]
+pub struct INIT_ONCE {
+ pub ptr: LPVOID,
+}
#[repr(C)]
pub struct REPARSE_MOUNTPOINT_DATA_BUFFER {
@@ -802,10 +835,6 @@ if #[cfg(not(target_vendor = "uwp"))] {
#[link(name = "advapi32")]
extern "system" {
- // Forbidden when targeting UWP
- #[link_name = "SystemFunction036"]
- pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
-
// Allowed but unused by UWP
pub fn OpenProcessToken(
ProcessHandle: HANDLE,
@@ -944,6 +973,7 @@ extern "system" {
pub fn TlsAlloc() -> DWORD;
pub fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
pub fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
+ pub fn TlsFree(dwTlsIndex: DWORD) -> BOOL;
pub fn GetLastError() -> DWORD;
pub fn QueryPerformanceFrequency(lpFrequency: *mut LARGE_INTEGER) -> BOOL;
pub fn QueryPerformanceCounter(lpPerformanceCount: *mut LARGE_INTEGER) -> BOOL;
@@ -1086,6 +1116,7 @@ extern "system" {
lpFileInformation: LPVOID,
dwBufferSize: DWORD,
) -> BOOL;
+ pub fn GetFileType(hfile: HANDLE) -> DWORD;
pub fn SleepConditionVariableSRW(
ConditionVariable: PCONDITION_VARIABLE,
SRWLock: PSRWLOCK,
@@ -1103,6 +1134,14 @@ extern "system" {
pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN;
pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN;
+ pub fn InitOnceBeginInitialize(
+ lpInitOnce: LPINIT_ONCE,
+ dwFlags: DWORD,
+ fPending: LPBOOL,
+ lpContext: *mut LPVOID,
+ ) -> BOOL;
+ pub fn InitOnceComplete(lpInitOnce: LPINIT_ONCE, dwFlags: DWORD, lpContext: LPVOID) -> BOOL;
+
pub fn CompareStringOrdinal(
lpString1: LPCWSTR,
cchCount1: c_int,
@@ -1217,11 +1256,18 @@ extern "system" {
// >= Vista / Server 2008
// https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
pub fn BCryptGenRandom(
- hAlgorithm: LPVOID,
+ hAlgorithm: BCRYPT_ALG_HANDLE,
pBuffer: *mut u8,
cbBuffer: ULONG,
dwFlags: ULONG,
) -> NTSTATUS;
+ pub fn BCryptOpenAlgorithmProvider(
+ phalgorithm: *mut BCRYPT_ALG_HANDLE,
+ pszAlgId: LPCWSTR,
+ pszimplementation: LPCWSTR,
+ dwflags: ULONG,
+ ) -> NTSTATUS;
+ pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS;
}
// Functions that aren't available on every version of Windows that we support,
@@ -1251,17 +1297,14 @@ compat_fn_with_fallback! {
}
compat_fn_optional! {
- pub static SYNCH_API: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
-
- // >= Windows 8 / Server 2012
- // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress
+ crate::sys::compat::load_synch_functions();
pub fn WaitOnAddress(
Address: LPVOID,
CompareAddress: LPVOID,
AddressSize: SIZE_T,
dwMilliseconds: DWORD
- ) -> BOOL;
- pub fn WakeByAddressSingle(Address: LPVOID) -> ();
+ );
+ pub fn WakeByAddressSingle(Address: LPVOID);
}
compat_fn_with_fallback! {
diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs
index 1a5421fac..43ab8c7ee 100644
--- a/library/std/src/sys/windows/cmath.rs
+++ b/library/std/src/sys/windows/cmath.rs
@@ -44,7 +44,7 @@ mod shims {
}
// On 32-bit x86 MSVC these functions aren't defined, so we just define shims
-// which promote everything fo f64, perform the calculation, and then demote
+// which promote everything to f64, perform the calculation, and then demote
// back to f32. While not precisely correct should be "correct enough" for now.
#[cfg(all(target_env = "msvc", target_arch = "x86"))]
mod shims {
diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs
index ccc90177a..7dff81ecb 100644
--- a/library/std/src/sys/windows/compat.rs
+++ b/library/std/src/sys/windows/compat.rs
@@ -7,52 +7,66 @@
//! `GetModuleHandle` and `GetProcAddress` to look up DLL entry points at
//! runtime.
//!
-//! This implementation uses a static initializer to look up the DLL entry
-//! points. The CRT (C runtime) executes static initializers before `main`
-//! is called (for binaries) and before `DllMain` is called (for DLLs).
-//! This is the ideal time to look up DLL imports, because we are guaranteed
-//! that no other threads will attempt to call these entry points. Thus,
-//! we can look up the imports and store them in `static mut` fields
-//! without any synchronization.
+//! This is implemented simply by storing a function pointer in an atomic.
+//! Loading and calling this function will have little or no overhead
+//! compared with calling any other dynamically imported function.
//!
-//! This has an additional advantage: Because the DLL import lookup happens
-//! at module initialization, the cost of these lookups is deterministic,
-//! and is removed from the code paths that actually call the DLL imports.
-//! That is, there is no unpredictable "cache miss" that occurs when calling
-//! a DLL import. For applications that benefit from predictable delays,
-//! this is a benefit. This also eliminates the comparison-and-branch
-//! from the hot path.
-//!
-//! Currently, the standard library uses only a small number of dynamic
-//! DLL imports. If this number grows substantially, then the cost of
-//! performing all of the lookups at initialization time might become
-//! substantial.
-//!
-//! The mechanism of registering a static initializer with the CRT is
-//! documented in
-//! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160).
-//! It works by contributing a global symbol to the `.CRT$XCU` section.
-//! The linker builds a table of all static initializer functions.
-//! The CRT startup code then iterates that table, calling each
-//! initializer function.
-//!
-//! # **WARNING!!*
-//! The environment that a static initializer function runs in is highly
-//! constrained. There are **many** restrictions on what static initializers
-//! can safely do. Static initializer functions **MUST NOT** do any of the
-//! following (this list is not comprehensive):
-//! * touch any other static field that is used by a different static
-//! initializer, because the order that static initializers run in
-//! is not defined.
-//! * call `LoadLibrary` or any other function that acquires the DLL
-//! loader lock.
-//! * call any Rust function or CRT function that touches any static
-//! (global) state.
+//! The stored function pointer starts out as an importer function which will
+//! swap itself with the real function when it's called for the first time. If
+//! the real function can't be imported then a fallback function is used in its
+//! place. While this is low cost for the happy path (where the function is
+//! already loaded) it does mean there's some overhead the first time the
+//! function is called. In the worst case, multiple threads may all end up
+//! importing the same function unnecessarily.
use crate::ffi::{c_void, CStr};
use crate::ptr::NonNull;
+use crate::sync::atomic::Ordering;
use crate::sys::c;
+// This uses a static initializer to preload some imported functions.
+// The CRT (C runtime) executes static initializers before `main`
+// is called (for binaries) and before `DllMain` is called (for DLLs).
+//
+// It works by contributing a global symbol to the `.CRT$XCT` section.
+// The linker builds a table of all static initializer functions.
+// The CRT startup code then iterates that table, calling each
+// initializer function.
+//
+// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
+// If you're reading this and would like a guarantee here, please
+// file an issue for discussion; currently we don't guarantee any functionality
+// before main.
+// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
+#[used]
+#[link_section = ".CRT$XCT"]
+static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
+
+/// Preload some imported functions.
+///
+/// Note that any functions included here will be unconditionally loaded in
+/// the final binary, regardless of whether or not they're actually used.
+///
+/// Therefore, this should be limited to `compat_fn_optional` functions which
+/// must be preloaded or any functions where lazier loading demonstrates a
+/// negative performance impact in practical situations.
+///
+/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
+unsafe extern "C" fn init() {
+ // In an exe this code is executed before main() so is single threaded.
+ // In a DLL the system's loader lock will be held thereby synchronizing
+ // access. So the same best practices apply here as they do to running in DllMain:
+ // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
+ //
+ // DO NOT do anything interesting or complicated in this function! DO NOT call
+ // any Rust functions or CRT functions if those functions touch any global state,
+ // because this function runs during global initialization. For example, DO NOT
+ // do any dynamic allocation, don't call LoadLibrary, etc.
+
+ // Attempt to preload the synch functions.
+ load_synch_functions();
+}
+
/// Helper macro for creating CStrs from literals and symbol names.
macro_rules! ansi_str {
(sym $ident:ident) => {{
@@ -85,39 +99,6 @@ pub(crate) const fn const_cstr_from_bytes(bytes: &'static [u8]) -> &'static CStr
unsafe { crate::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
}
-#[used]
-#[link_section = ".CRT$XCU"]
-static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
-
-/// This is where the magic preloading of symbols happens.
-///
-/// Note that any functions included here will be unconditionally included in
-/// the final binary, regardless of whether or not they're actually used.
-///
-/// Therefore, this is limited to `compat_fn_optional` functions which must be
-/// preloaded and any functions which may be more time sensitive, even for the first call.
-unsafe extern "C" fn init() {
- // There is no locking here. This code is executed before main() is entered, and
- // is guaranteed to be single-threaded.
- //
- // DO NOT do anything interesting or complicated in this function! DO NOT call
- // any Rust functions or CRT functions if those functions touch any global state,
- // because this function runs during global initialization. For example, DO NOT
- // do any dynamic allocation, don't call LoadLibrary, etc.
-
- if let Some(synch) = Module::new(c::SYNCH_API) {
- // These are optional and so we must manually attempt to load them
- // before they can be used.
- c::WaitOnAddress::preload(synch);
- c::WakeByAddressSingle::preload(synch);
- }
-
- if let Some(kernel32) = Module::new(c::KERNEL32) {
- // Preloading this means getting a precise time will be as fast as possible.
- c::GetSystemTimePreciseAsFileTime::preload(kernel32);
- }
-}
-
/// Represents a loaded module.
///
/// Note that the modules std depends on must not be unloaded.
@@ -151,7 +132,7 @@ impl Module {
macro_rules! compat_fn_with_fallback {
(pub static $module:ident: &CStr = $name:expr; $(
$(#[$meta:meta])*
- pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
+ $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
)*) => (
pub static $module: &CStr = $name;
$(
@@ -196,11 +177,6 @@ macro_rules! compat_fn_with_fallback {
$fallback_body
}
- #[allow(unused)]
- pub(in crate::sys) fn preload(module: Module) {
- load_from_module(Some(module));
- }
-
#[inline(always)]
pub unsafe fn call($($argname: $argtype),*) -> $rettype {
let func: F = mem::transmute(PTR.load(Ordering::Relaxed));
@@ -208,66 +184,60 @@ macro_rules! compat_fn_with_fallback {
}
}
$(#[$meta])*
- pub use $symbol::call as $symbol;
+ $vis use $symbol::call as $symbol;
)*)
}
-/// A function that either exists or doesn't.
+/// Optionally loaded functions.
///
-/// NOTE: Optional functions must be preloaded in the `init` function above, or they will always be None.
+/// Actual loading of the function defers to $load_functions.
macro_rules! compat_fn_optional {
- (pub static $module:ident: &CStr = $name:expr; $(
- $(#[$meta:meta])*
- pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty;
- )*) => (
- pub static $module: &CStr = $name;
+ ($load_functions:expr;
$(
- $(#[$meta])*
- pub mod $symbol {
- #[allow(unused_imports)]
- use super::*;
- use crate::mem;
- use crate::sync::atomic::{AtomicPtr, Ordering};
- use crate::sys::compat::Module;
- use crate::ptr::{self, NonNull};
-
- type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
-
- /// `PTR` will either be `null()` or set to the loaded function.
- static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
-
- /// Only allow access to the function if it has loaded successfully.
- #[inline(always)]
- #[cfg(not(miri))]
- pub fn option() -> Option<F> {
- unsafe {
- NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| mem::transmute(f))
+ $(#[$meta:meta])*
+ $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?;
+ )+) => (
+ $(
+ pub mod $symbol {
+ use super::*;
+ use crate::ffi::c_void;
+ use crate::mem;
+ use crate::ptr::{self, NonNull};
+ use crate::sync::atomic::{AtomicPtr, Ordering};
+
+ pub(in crate::sys) static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
+
+ type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?;
+
+ #[inline(always)]
+ pub fn option() -> Option<F> {
+ // Miri does not understand the way we do preloading
+ // therefore load the function here instead.
+ #[cfg(miri)] $load_functions;
+ NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
}
}
+ )+
+ )
+}
- // Miri does not understand the way we do preloading
- // therefore load the function here instead.
- #[cfg(miri)]
- pub fn option() -> Option<F> {
- let mut func = NonNull::new(PTR.load(Ordering::Relaxed));
- if func.is_none() {
- unsafe { Module::new($module).map(preload) };
- func = NonNull::new(PTR.load(Ordering::Relaxed));
- }
- unsafe {
- func.map(|f| mem::transmute(f))
- }
- }
+/// Load all needed functions from "api-ms-win-core-synch-l1-2-0".
+pub(super) fn load_synch_functions() {
+ fn try_load() -> Option<()> {
+ const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
+ const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress");
+ const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle");
+
+ // Try loading the library and all the required functions.
+ // If any step fails, then they all fail.
+ let library = unsafe { Module::new(MODULE_NAME) }?;
+ let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
+ let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
+
+ c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
+ c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
+ Some(())
+ }
- #[allow(unused)]
- pub(in crate::sys) fn preload(module: Module) {
- unsafe {
- static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol);
- if let Some(f) = module.proc_address(SYMBOL_NAME) {
- PTR.store(f.as_ptr(), Ordering::Relaxed);
- }
- }
- }
- }
- )*)
+ try_load();
}
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index aed082b3e..378098038 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -1,9 +1,10 @@
use crate::os::windows::prelude::*;
+use crate::borrow::Cow;
use crate::ffi::OsString;
use crate::fmt;
-use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
-use crate::mem;
+use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
+use crate::mem::{self, MaybeUninit};
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::path::{Path, PathBuf};
use crate::ptr;
@@ -11,7 +12,7 @@ use crate::slice;
use crate::sync::Arc;
use crate::sys::handle::Handle;
use crate::sys::time::SystemTime;
-use crate::sys::{c, cvt};
+use crate::sys::{c, cvt, Align8};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::thread;
@@ -326,9 +327,15 @@ impl File {
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
let mut reparse_tag = 0;
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- if let Ok((_, buf)) = self.reparse_point(&mut b) {
- reparse_tag = buf.ReparseTag;
+ let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
+ cvt(c::GetFileInformationByHandleEx(
+ self.handle.as_raw_handle(),
+ c::FileAttributeTagInfo,
+ ptr::addr_of_mut!(attr_tag).cast(),
+ mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
+ ))?;
+ if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ reparse_tag = attr_tag.ReparseTag;
}
}
Ok(FileAttr {
@@ -389,9 +396,15 @@ impl File {
attr.file_size = info.AllocationSize as u64;
attr.number_of_links = Some(info.NumberOfLinks);
if attr.file_type().is_reparse_point() {
- let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- if let Ok((_, buf)) = self.reparse_point(&mut b) {
- attr.reparse_tag = buf.ReparseTag;
+ let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
+ cvt(c::GetFileInformationByHandleEx(
+ self.handle.as_raw_handle(),
+ c::FileAttributeTagInfo,
+ ptr::addr_of_mut!(attr_tag).cast(),
+ mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
+ ))?;
+ if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ attr.reparse_tag = attr_tag.ReparseTag;
}
}
Ok(attr)
@@ -415,8 +428,8 @@ impl File {
self.handle.read_at(buf, offset)
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- self.handle.read_buf(buf)
+ pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ self.handle.read_buf(cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
@@ -458,38 +471,46 @@ impl File {
Ok(Self { handle: self.handle.try_clone()? })
}
- fn reparse_point<'a>(
+ // NB: returned pointer is derived from `space`, and has provenance to
+ // match. A raw pointer is returned rather than a reference in order to
+ // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
+ fn reparse_point(
&self,
- space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
- ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
+ space: &mut Align8<[MaybeUninit<u8>]>,
+ ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
cvt({
+ // Grab this in advance to avoid it invalidating the pointer
+ // we get from `space.0.as_mut_ptr()`.
+ let len = space.0.len();
c::DeviceIoControl(
self.handle.as_raw_handle(),
c::FSCTL_GET_REPARSE_POINT,
ptr::null_mut(),
0,
- space.as_mut_ptr() as *mut _,
- space.len() as c::DWORD,
+ space.0.as_mut_ptr().cast(),
+ len as c::DWORD,
&mut bytes,
ptr::null_mut(),
)
})?;
- Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
+ const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
+ Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
}
}
fn readlink(&self) -> io::Result<PathBuf> {
- let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let (_bytes, buf) = self.reparse_point(&mut space)?;
unsafe {
- let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
+ let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
c::IO_REPARSE_TAG_SYMLINK => {
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
- &buf.rest as *const _ as *const _;
+ ptr::addr_of!((*buf).rest).cast();
+ assert!(info.is_aligned());
(
- &(*info).PathBuffer as *const _ as *const u16,
+ ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
@@ -497,9 +518,10 @@ impl File {
}
c::IO_REPARSE_TAG_MOUNT_POINT => {
let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
- &buf.rest as *const _ as *const _;
+ ptr::addr_of!((*buf).rest).cast();
+ assert!(info.is_aligned());
(
- &(*info).PathBuffer as *const _ as *const u16,
+ ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
false,
@@ -512,7 +534,7 @@ impl File {
));
}
};
- let subst_ptr = path_buffer.offset(subst_off as isize);
+ let subst_ptr = path_buffer.add(subst_off.into());
let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
// Absolute paths start with an NT internal namespace prefix `\??\`
// We should not let it leak through.
@@ -551,6 +573,14 @@ impl File {
"Cannot set file timestamp to 0",
));
}
+ let is_max =
+ |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX;
+ if times.accessed.map_or(false, is_max) || times.modified.map_or(false, is_max) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",
+ ));
+ }
cvt(unsafe {
c::SetFileTime(self.as_handle(), None, times.accessed.as_ref(), times.modified.as_ref())
})?;
@@ -649,27 +679,31 @@ impl File {
/// A buffer for holding directory entries.
struct DirBuff {
- buffer: Vec<u8>,
+ buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
}
impl DirBuff {
+ const BUFFER_SIZE: usize = 1024;
fn new() -> Self {
- const BUFFER_SIZE: usize = 1024;
- Self { buffer: vec![0_u8; BUFFER_SIZE] }
+ Self {
+ // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
+ // initialization.
+ buffer: unsafe { Box::new_uninit().assume_init() },
+ }
}
fn capacity(&self) -> usize {
- self.buffer.len()
+ self.buffer.0.len()
}
fn as_mut_ptr(&mut self) -> *mut u8 {
- self.buffer.as_mut_ptr().cast()
+ self.buffer.0.as_mut_ptr().cast()
}
/// Returns a `DirBuffIter`.
fn iter(&self) -> DirBuffIter<'_> {
DirBuffIter::new(self)
}
}
-impl AsRef<[u8]> for DirBuff {
- fn as_ref(&self) -> &[u8] {
- &self.buffer
+impl AsRef<[MaybeUninit<u8>]> for DirBuff {
+ fn as_ref(&self) -> &[MaybeUninit<u8>] {
+ &self.buffer.0
}
}
@@ -677,7 +711,7 @@ impl AsRef<[u8]> for DirBuff {
///
/// Currently only returns file names (UTF-16 encoded).
struct DirBuffIter<'a> {
- buffer: Option<&'a [u8]>,
+ buffer: Option<&'a [MaybeUninit<u8>]>,
cursor: usize,
}
impl<'a> DirBuffIter<'a> {
@@ -686,23 +720,34 @@ impl<'a> DirBuffIter<'a> {
}
}
impl<'a> Iterator for DirBuffIter<'a> {
- type Item = (&'a [u16], bool);
+ type Item = (Cow<'a, [u16]>, bool);
fn next(&mut self) -> Option<Self::Item> {
use crate::mem::size_of;
let buffer = &self.buffer?[self.cursor..];
// Get the name and next entry from the buffer.
- // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
- // last field (the file name) is unsized. So an offset has to be
- // used to get the file name slice.
+ // SAFETY:
+ // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
+ // field (the file name) is unsized. So an offset has to be used to
+ // get the file name slice.
+ // - The OS has guaranteed initialization of the fields of
+ // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
+ // `FileNameLength` bytes)
let (name, is_directory, next_entry) = unsafe {
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
- let next_entry = (*info).NextEntryOffset as usize;
- let name = crate::slice::from_raw_parts(
- (*info).FileName.as_ptr().cast::<u16>(),
- (*info).FileNameLength as usize / size_of::<u16>(),
+ // While this is guaranteed to be aligned in documentation for
+ // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
+ // it does not seem that reality is so kind, and assuming this
+ // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
+ // presumably, this can be blamed on buggy filesystem drivers, but who knows.
+ let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize;
+ let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize;
+ let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned();
+ let name = from_maybe_unaligned(
+ ptr::addr_of!((*info).FileName).cast::<u16>(),
+ length / size_of::<u16>(),
);
- let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
+ let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
(name, is_directory, next_entry)
};
@@ -715,13 +760,21 @@ impl<'a> Iterator for DirBuffIter<'a> {
// Skip `.` and `..` pseudo entries.
const DOT: u16 = b'.' as u16;
- match name {
+ match &name[..] {
[DOT] | [DOT, DOT] => self.next(),
_ => Some((name, is_directory)),
}
}
}
+unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
+ if p.is_aligned() {
+ Cow::Borrowed(crate::slice::from_raw_parts(p, len))
+ } else {
+ Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
+ }
+}
+
/// Open a link relative to the parent directory, ensure no symlinks are followed.
fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
// This is implemented using the lower level `NtCreateFile` function as
@@ -1077,13 +1130,13 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io
if is_directory {
let child_dir = open_link_no_reparse(
&dir,
- name,
+ &name,
c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
)?;
dirlist.push(child_dir);
} else {
for i in 1..=MAX_RETRIES {
- let result = open_link_no_reparse(&dir, name, c::SYNCHRONIZE | c::DELETE);
+ let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
match result {
Ok(f) => delete(&f)?,
// Already deleted, so skip.
@@ -1337,18 +1390,19 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
let h = f.as_inner().as_raw_handle();
unsafe {
- let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
- let buf = &mut (*db).ReparseTarget as *mut c::WCHAR;
+ let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+ let data_ptr = data.0.as_mut_ptr();
+ let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
+ let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
let mut i = 0;
// FIXME: this conversion is very hacky
let v = br"\??\";
let v = v.iter().map(|x| *x as u16);
for c in v.chain(original.as_os_str().encode_wide()) {
- *buf.offset(i) = c;
+ *buf.add(i) = c;
i += 1;
}
- *buf.offset(i) = 0;
+ *buf.add(i) = 0;
i += 1;
(*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
(*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
@@ -1359,7 +1413,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
cvt(c::DeviceIoControl(
h as *mut _,
c::FSCTL_SET_REPARSE_POINT,
- data.as_ptr() as *mut _,
+ data_ptr.cast(),
(*db).ReparseDataLength + 8,
ptr::null_mut(),
0,
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index e24b09cc9..ae33d48c6 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -4,7 +4,7 @@
mod tests;
use crate::cmp;
-use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf};
+use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::os::windows::io::{
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
@@ -112,18 +112,16 @@ impl Handle {
}
}
- pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
- let res = unsafe {
- self.synchronous_read(buf.unfilled_mut().as_mut_ptr(), buf.remaining(), None)
- };
+ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
+ let res =
+ unsafe { self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), None) };
match res {
Ok(read) => {
// Safety: `read` bytes were written to the initialized portion of the buffer
unsafe {
- buf.assume_init(read as usize);
+ cursor.advance(read as usize);
}
- buf.add_filled(read as usize);
Ok(())
}
diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs
index fb06df1f8..2cc34c986 100644
--- a/library/std/src/sys/windows/io.rs
+++ b/library/std/src/sys/windows/io.rs
@@ -1,6 +1,10 @@
use crate::marker::PhantomData;
+use crate::mem::size_of;
+use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle};
use crate::slice;
-use crate::sys::c;
+use crate::sys::{c, Align8};
+use core;
+use libc;
#[derive(Copy, Clone)]
#[repr(transparent)]
@@ -78,3 +82,73 @@ impl<'a> IoSliceMut<'a> {
unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) }
}
}
+
+pub fn is_terminal(h: &impl AsHandle) -> bool {
+ unsafe { handle_is_console(h.as_handle()) }
+}
+
+unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool {
+ let handle = handle.as_raw_handle();
+
+ // A null handle means the process has no console.
+ if handle.is_null() {
+ return false;
+ }
+
+ let mut out = 0;
+ if c::GetConsoleMode(handle, &mut out) != 0 {
+ // False positives aren't possible. If we got a console then we definitely have a console.
+ return true;
+ }
+
+ // At this point, we *could* have a false negative. We can determine that this is a true
+ // negative if we can detect the presence of a console on any of the standard I/O streams. If
+ // another stream has a console, then we know we're in a Windows console and can therefore
+ // trust the negative.
+ for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] {
+ let std_handle = c::GetStdHandle(std_handle);
+ if !std_handle.is_null()
+ && std_handle != handle
+ && c::GetConsoleMode(std_handle, &mut out) != 0
+ {
+ return false;
+ }
+ }
+
+ // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty.
+ msys_tty_on(handle)
+}
+
+unsafe fn msys_tty_on(handle: c::HANDLE) -> bool {
+ // Early return if the handle is not a pipe.
+ if c::GetFileType(handle) != c::FILE_TYPE_PIPE {
+ return false;
+ }
+
+ const SIZE: usize = size_of::<c::FILE_NAME_INFO>() + c::MAX_PATH * size_of::<c::WCHAR>();
+ let mut name_info_bytes = Align8([0u8; SIZE]);
+ let res = c::GetFileInformationByHandleEx(
+ handle,
+ c::FileNameInfo,
+ name_info_bytes.0.as_mut_ptr() as *mut libc::c_void,
+ SIZE as u32,
+ );
+ if res == 0 {
+ return false;
+ }
+ let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.0.as_ptr() as *const c::FILE_NAME_INFO);
+ let name_len = name_info.FileNameLength as usize / 2;
+ // Offset to get the `FileName` field.
+ let name_ptr = name_info_bytes.0.as_ptr().offset(size_of::<c::DWORD>() as isize).cast::<u16>();
+ let s = core::slice::from_raw_parts(name_ptr, name_len);
+ let name = String::from_utf16_lossy(s);
+ // Get the file name only.
+ let name = name.rsplit('\\').next().unwrap_or(&name);
+ // This checks whether 'pty' exists in the file name, which indicates that
+ // a pseudo-terminal is attached. To mitigate against false positives
+ // (e.g., an actual file name that contains 'pty'), we also require that
+ // the file name begins with either the strings 'msys-' or 'cygwin-'.)
+ let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-");
+ let is_pty = name.contains("-pty");
+ is_msys && is_pty
+}
diff --git a/library/std/src/sys/windows/locks/mod.rs b/library/std/src/sys/windows/locks/mod.rs
index d412ff152..602a2d623 100644
--- a/library/std/src/sys/windows/locks/mod.rs
+++ b/library/std/src/sys/windows/locks/mod.rs
@@ -3,4 +3,4 @@ mod mutex;
mod rwlock;
pub use condvar::{Condvar, MovableCondvar};
pub use mutex::{MovableMutex, Mutex};
-pub use rwlock::{MovableRwLock, RwLock};
+pub use rwlock::MovableRwLock;
diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs
index f91e8f9f5..91207f5f4 100644
--- a/library/std/src/sys/windows/locks/mutex.rs
+++ b/library/std/src/sys/windows/locks/mutex.rs
@@ -37,8 +37,6 @@ impl Mutex {
pub const fn new() -> Mutex {
Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
#[inline]
pub unsafe fn lock(&self) {
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index b3f6d2d0a..eab9b9612 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -2,6 +2,7 @@
use crate::ffi::{CStr, OsStr, OsString};
use crate::io::ErrorKind;
+use crate::mem::MaybeUninit;
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::path::PathBuf;
use crate::time::Duration;
@@ -47,7 +48,7 @@ cfg_if::cfg_if! {
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {
stack_overflow::init();
// Normally, `thread::spawn` will call `Thread::set_name` but since this thread already
@@ -204,8 +205,8 @@ where
// This initial size also works around `GetFullPathNameW` returning
// incorrect size hints for some short paths:
// https://github.com/dylni/normpath/issues/5
- let mut stack_buf = [0u16; 512];
- let mut heap_buf = Vec::new();
+ let mut stack_buf: [MaybeUninit<u16>; 512] = MaybeUninit::uninit_array();
+ let mut heap_buf: Vec<MaybeUninit<u16>> = Vec::new();
unsafe {
let mut n = stack_buf.len();
loop {
@@ -214,6 +215,11 @@ where
} else {
let extra = n - heap_buf.len();
heap_buf.reserve(extra);
+ // We used `reserve` and not `reserve_exact`, so in theory we
+ // may have gotten more than requested. If so, we'd like to use
+ // it... so long as we won't cause overflow.
+ n = heap_buf.capacity().min(c::DWORD::MAX as usize);
+ // Safety: MaybeUninit<u16> does not need initialization
heap_buf.set_len(n);
&mut heap_buf[..]
};
@@ -228,13 +234,13 @@ where
// error" is still 0 then we interpret it as a 0 length buffer and
// not an actual error.
c::SetLastError(0);
- let k = match f1(buf.as_mut_ptr(), n as c::DWORD) {
+ let k = match f1(buf.as_mut_ptr().cast::<u16>(), n as c::DWORD) {
0 if c::GetLastError() == 0 => 0,
0 => return Err(crate::io::Error::last_os_error()),
n => n,
} as usize;
if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
- n *= 2;
+ n = n.saturating_mul(2).min(c::DWORD::MAX as usize);
} else if k > n {
n = k;
} else if k == n {
@@ -244,7 +250,9 @@ where
// Therefore k never equals n.
unreachable!();
} else {
- return Ok(f2(&buf[..k]));
+ // Safety: First `k` values are initialized.
+ let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]);
+ return Ok(f2(slice));
}
}
}
@@ -321,3 +329,11 @@ pub fn abort_internal() -> ! {
}
crate::intrinsics::abort();
}
+
+/// Align the inner value to 8 bytes.
+///
+/// This is enough for almost all of the buffers we're likely to work with in
+/// the Windows APIs we use.
+#[repr(C, align(8))]
+#[derive(Copy, Clone)]
+pub(crate) struct Align8<T: ?Sized>(pub T);
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index bcac996c0..352337ba3 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -99,11 +99,11 @@ impl Iterator for Env {
}
let p = self.cur as *const u16;
let mut len = 0;
- while *p.offset(len) != 0 {
+ while *p.add(len) != 0 {
len += 1;
}
- let s = slice::from_raw_parts(p, len as usize);
- self.cur = self.cur.offset(len + 1);
+ let s = slice::from_raw_parts(p, len);
+ self.cur = self.cur.add(len + 1);
// Windows allows environment variables to start with an equals
// symbol (in any other position, this is the separator between
diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs
index 11883f150..4bdd8c505 100644
--- a/library/std/src/sys/windows/os_str.rs
+++ b/library/std/src/sys/windows/os_str.rs
@@ -164,9 +164,7 @@ impl Slice {
}
pub fn to_owned(&self) -> Buf {
- let mut buf = Wtf8Buf::with_capacity(self.inner.len());
- buf.push_wtf8(&self.inner);
- Buf { inner: buf }
+ Buf { inner: self.inner.to_owned() }
}
pub fn clone_into(&self, buf: &mut Buf) {
diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs
index 6eab38cab..623c62361 100644
--- a/library/std/src/sys/windows/path/tests.rs
+++ b/library/std/src/sys/windows/path/tests.rs
@@ -105,7 +105,7 @@ fn test_parse_prefix_verbatim_device() {
assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe"));
}
-// See #93586 for more infomation.
+// See #93586 for more information.
#[test]
fn test_windows_prefix_components() {
use crate::path::Path;
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 02d5af471..9cbb4ef19 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -16,6 +16,7 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle};
use crate::path::{Path, PathBuf};
use crate::ptr;
+use crate::sync::Mutex;
use crate::sys::args::{self, Arg};
use crate::sys::c;
use crate::sys::c::NonZeroDWORD;
@@ -25,7 +26,6 @@ use crate::sys::handle::Handle;
use crate::sys::path;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys::stdio;
-use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys_common::IntoInner;
@@ -301,9 +301,9 @@ impl Command {
//
// For more information, msdn also has an article about this race:
// https://support.microsoft.com/kb/315939
- static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
+ static CREATE_PROCESS_LOCK: Mutex<()> = Mutex::new(());
- let _guard = unsafe { CREATE_PROCESS_LOCK.lock() };
+ let _guard = CREATE_PROCESS_LOCK.lock();
let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None };
let null = Stdio::Null;
diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs
index f8fd93a73..b5a49489d 100644
--- a/library/std/src/sys/windows/rand.rs
+++ b/library/std/src/sys/windows/rand.rs
@@ -1,35 +1,106 @@
-use crate::io;
+//! # Random key generation
+//!
+//! This module wraps the RNG provided by the OS. There are a few different
+//! ways to interface with the OS RNG so it's worth exploring each of the options.
+//! Note that at the time of writing these all go through the (undocumented)
+//! `bcryptPrimitives.dll` but they use different route to get there.
+//!
+//! Originally we were using [`RtlGenRandom`], however that function is
+//! deprecated and warns it "may be altered or unavailable in subsequent versions".
+//!
+//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG`
+//! flag to query and find the system configured RNG. However, this change caused a small
+//! but significant number of users to experience panics caused by a failure of
+//! this function. See [#94098].
+//!
+//! The current version falls back to using `BCryptOpenAlgorithmProvider` if
+//! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason.
+//!
+//! [#94098]: https://github.com/rust-lang/rust/issues/94098
+//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
+//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
use crate::mem;
use crate::ptr;
use crate::sys::c;
+/// Generates high quality secure random keys for use by [`HashMap`].
+///
+/// This is used to seed the default [`RandomState`].
+///
+/// [`HashMap`]: crate::collections::HashMap
+/// [`RandomState`]: crate::collections::hash_map::RandomState
pub fn hashmap_random_keys() -> (u64, u64) {
- let mut v = (0, 0);
- let ret = unsafe {
- c::BCryptGenRandom(
- ptr::null_mut(),
- &mut v as *mut _ as *mut u8,
- mem::size_of_val(&v) as c::ULONG,
- c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
- )
- };
- if ret != 0 { fallback_rng() } else { v }
+ Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng)
}
-/// Generate random numbers using the fallback RNG function (RtlGenRandom)
-#[cfg(not(target_vendor = "uwp"))]
-#[inline(never)]
-fn fallback_rng() -> (u64, u64) {
- let mut v = (0, 0);
- let ret =
- unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
+struct Rng {
+ algorithm: c::BCRYPT_ALG_HANDLE,
+ flags: u32,
+}
+impl Rng {
+ const SYSTEM: Self = unsafe { Self::new(ptr::null_mut(), c::BCRYPT_USE_SYSTEM_PREFERRED_RNG) };
+
+ /// Create the RNG from an existing algorithm handle.
+ ///
+ /// # Safety
+ ///
+ /// The handle must either be null or a valid algorithm handle.
+ const unsafe fn new(algorithm: c::BCRYPT_ALG_HANDLE, flags: u32) -> Self {
+ Self { algorithm, flags }
+ }
+
+ /// Open a handle to the RNG algorithm.
+ fn open() -> Result<Self, c::NTSTATUS> {
+ use crate::sync::atomic::AtomicPtr;
+ use crate::sync::atomic::Ordering::{Acquire, Release};
+
+ // An atomic is used so we don't need to reopen the handle every time.
+ static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut());
+
+ let mut handle = HANDLE.load(Acquire);
+ if handle.is_null() {
+ let status = unsafe {
+ c::BCryptOpenAlgorithmProvider(
+ &mut handle,
+ c::BCRYPT_RNG_ALGORITHM.as_ptr(),
+ ptr::null(),
+ 0,
+ )
+ };
+ if c::nt_success(status) {
+ // If another thread opens a handle first then use that handle instead.
+ let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire);
+ if let Err(previous_handle) = result {
+ // Close our handle and return the previous one.
+ unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) };
+ handle = previous_handle;
+ }
+ Ok(unsafe { Self::new(handle, 0) })
+ } else {
+ Err(status)
+ }
+ } else {
+ Ok(unsafe { Self::new(handle, 0) })
+ }
+ }
- if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
+ fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> {
+ let mut v = (0, 0);
+ let status = unsafe {
+ let size = mem::size_of_val(&v).try_into().unwrap();
+ c::BCryptGenRandom(self.algorithm, ptr::addr_of_mut!(v).cast(), size, self.flags)
+ };
+ if c::nt_success(status) { Ok(v) } else { Err(status) }
+ }
}
-/// We can't use RtlGenRandom with UWP, so there is no fallback
-#[cfg(target_vendor = "uwp")]
+/// Generate random numbers using the fallback RNG function
#[inline(never)]
-fn fallback_rng() -> (u64, u64) {
- panic!("fallback RNG broken: RtlGenRandom() not supported on UWP");
+fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) {
+ match Rng::open().and_then(|rng| rng.gen_random_keys()) {
+ Ok(keys) => keys,
+ Err(status) => {
+ panic!("RNG broken: {rng_status:#x}, fallback RNG broken: {status:#x}")
+ }
+ }
}
diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs
index a001d6b98..70c9b14a0 100644
--- a/library/std/src/sys/windows/stdio.rs
+++ b/library/std/src/sys/windows/stdio.rs
@@ -3,6 +3,7 @@
use crate::char::decode_utf16;
use crate::cmp;
use crate::io;
+use crate::mem::MaybeUninit;
use crate::os::windows::io::{FromRawHandle, IntoRawHandle};
use crate::ptr;
use crate::str;
@@ -169,13 +170,14 @@ fn write(
}
fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> {
- let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2];
+ let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
let mut len_utf16 = 0;
for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) {
- *dest = chr;
+ *dest = MaybeUninit::new(chr);
len_utf16 += 1;
}
- let utf16 = &utf16[..len_utf16];
+ // Safety: We've initialized `len_utf16` values.
+ let utf16: &[u16] = unsafe { MaybeUninit::slice_assume_init_ref(&utf16[..len_utf16]) };
let mut written = write_u16s(handle, &utf16)?;
@@ -250,11 +252,14 @@ impl io::Read for Stdin {
return Ok(bytes_copied);
} else if buf.len() - bytes_copied < 4 {
// Not enough space to get a UTF-8 byte. We will use the incomplete UTF8.
- let mut utf16_buf = [0u16; 1];
+ let mut utf16_buf = [MaybeUninit::new(0); 1];
// Read one u16 character.
let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?;
// Read bytes, using the (now-empty) self.incomplete_utf8 as extra space.
- let read_bytes = utf16_to_utf8(&utf16_buf[..read], &mut self.incomplete_utf8.bytes)?;
+ let read_bytes = utf16_to_utf8(
+ unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) },
+ &mut self.incomplete_utf8.bytes,
+ )?;
// Read in the bytes from incomplete_utf8 until the buffer is full.
self.incomplete_utf8.len = read_bytes as u8;
@@ -262,15 +267,18 @@ impl io::Read for Stdin {
bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]);
Ok(bytes_copied)
} else {
- let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2];
+ let mut utf16_buf = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
+
// In the worst case, a UTF-8 string can take 3 bytes for every `u16` of a UTF-16. So
// we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets
// lost.
let amount = cmp::min(buf.len() / 3, utf16_buf.len());
let read =
read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?;
-
- match utf16_to_utf8(&utf16_buf[..read], buf) {
+ // Safety `read_u16s_fixup_surrogates` returns the number of items
+ // initialized.
+ let utf16s = unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) };
+ match utf16_to_utf8(utf16s, buf) {
Ok(value) => return Ok(bytes_copied + value),
Err(e) => return Err(e),
}
@@ -283,14 +291,14 @@ impl io::Read for Stdin {
// This is a best effort, and might not work if we are not the only reader on Stdin.
fn read_u16s_fixup_surrogates(
handle: c::HANDLE,
- buf: &mut [u16],
+ buf: &mut [MaybeUninit<u16>],
mut amount: usize,
surrogate: &mut u16,
) -> io::Result<usize> {
// Insert possibly remaining unpaired surrogate from last read.
let mut start = 0;
if *surrogate != 0 {
- buf[0] = *surrogate;
+ buf[0] = MaybeUninit::new(*surrogate);
*surrogate = 0;
start = 1;
if amount == 1 {
@@ -303,7 +311,10 @@ fn read_u16s_fixup_surrogates(
let mut amount = read_u16s(handle, &mut buf[start..amount])? + start;
if amount > 0 {
- let last_char = buf[amount - 1];
+ // Safety: The returned `amount` is the number of values initialized,
+ // and it is not 0, so we know that `buf[amount - 1]` have been
+ // initialized.
+ let last_char = unsafe { buf[amount - 1].assume_init() };
if last_char >= 0xD800 && last_char <= 0xDBFF {
// high surrogate
*surrogate = last_char;
@@ -313,7 +324,8 @@ fn read_u16s_fixup_surrogates(
Ok(amount)
}
-fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> {
+// Returns `Ok(n)` if it initialized `n` values in `buf`.
+fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usize> {
// Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the
// traditional DOS method to indicate end of character stream / user input (SUB).
// See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole.
@@ -346,8 +358,9 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> {
}
break;
}
-
- if amount > 0 && buf[amount as usize - 1] == CTRL_Z {
+ // Safety: if `amount > 0`, then that many bytes were written, so
+ // `buf[amount as usize - 1]` has been initialized.
+ if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z {
amount -= 1;
}
Ok(amount as usize)
diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs
index 25d1c6e8e..9707a95df 100644
--- a/library/std/src/sys/windows/thread_local_dtor.rs
+++ b/library/std/src/sys/windows/thread_local_dtor.rs
@@ -8,10 +8,14 @@
#[thread_local]
static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+// Ensure this can never be inlined because otherwise this may break in dylibs.
+// See #44391.
+#[inline(never)]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
DESTRUCTORS.push((t, dtor));
}
+#[inline(never)] // See comment above
/// Runs destructors. This should not be called until thread exit.
pub unsafe fn run_keyless_dtors() {
// Drop all the destructors.
diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs
index ec670238e..17628b757 100644
--- a/library/std/src/sys/windows/thread_local_key.rs
+++ b/library/std/src/sys/windows/thread_local_key.rs
@@ -1,11 +1,16 @@
-use crate::mem::ManuallyDrop;
+use crate::cell::UnsafeCell;
use crate::ptr;
-use crate::sync::atomic::AtomicPtr;
-use crate::sync::atomic::Ordering::SeqCst;
+use crate::sync::atomic::{
+ AtomicPtr, AtomicU32,
+ Ordering::{AcqRel, Acquire, Relaxed, Release},
+};
use crate::sys::c;
-pub type Key = c::DWORD;
-pub type Dtor = unsafe extern "C" fn(*mut u8);
+#[cfg(test)]
+mod tests;
+
+type Key = c::DWORD;
+type Dtor = unsafe extern "C" fn(*mut u8);
// Turns out, like pretty much everything, Windows is pretty close the
// functionality that Unix provides, but slightly different! In the case of
@@ -22,60 +27,109 @@ pub type Dtor = unsafe extern "C" fn(*mut u8);
// To accomplish this feat, we perform a number of threads, all contained
// within this module:
//
-// * All TLS destructors are tracked by *us*, not the windows runtime. This
+// * All TLS destructors are tracked by *us*, not the Windows runtime. This
// means that we have a global list of destructors for each TLS key that
// we know about.
// * When a thread exits, we run over the entire list and run dtors for all
// non-null keys. This attempts to match Unix semantics in this regard.
//
-// This ends up having the overhead of using a global list, having some
-// locks here and there, and in general just adding some more code bloat. We
-// attempt to optimize runtime by forgetting keys that don't have
-// destructors, but this only gets us so far.
-//
// For more details and nitty-gritty, see the code sections below!
//
// [1]: https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
-// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
-// /threading/thread_local_storage_win.cc#L42
+// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42
-// -------------------------------------------------------------------------
-// Native bindings
-//
-// This section is just raw bindings to the native functions that Windows
-// provides, There's a few extra calls to deal with destructors.
+pub struct StaticKey {
+ /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == DWORD::MAX
+ /// is not a valid key value, this allows us to use zero as sentinel value
+ /// without risking overflow.
+ key: AtomicU32,
+ dtor: Option<Dtor>,
+ next: AtomicPtr<StaticKey>,
+ /// Currently, destructors cannot be unregistered, so we cannot use racy
+ /// initialization for keys. Instead, we need synchronize initialization.
+ /// Use the Windows-provided `Once` since it does not require TLS.
+ once: UnsafeCell<c::INIT_ONCE>,
+}
-#[inline]
-pub unsafe fn create(dtor: Option<Dtor>) -> Key {
- let key = c::TlsAlloc();
- assert!(key != c::TLS_OUT_OF_INDEXES);
- if let Some(f) = dtor {
- register_dtor(key, f);
+impl StaticKey {
+ #[inline]
+ pub const fn new(dtor: Option<Dtor>) -> StaticKey {
+ StaticKey {
+ key: AtomicU32::new(0),
+ dtor,
+ next: AtomicPtr::new(ptr::null_mut()),
+ once: UnsafeCell::new(c::INIT_ONCE_STATIC_INIT),
+ }
}
- key
-}
-#[inline]
-pub unsafe fn set(key: Key, value: *mut u8) {
- let r = c::TlsSetValue(key, value as c::LPVOID);
- debug_assert!(r != 0);
-}
+ #[inline]
+ pub unsafe fn set(&'static self, val: *mut u8) {
+ let r = c::TlsSetValue(self.key(), val.cast());
+ debug_assert_eq!(r, c::TRUE);
+ }
-#[inline]
-pub unsafe fn get(key: Key) -> *mut u8 {
- c::TlsGetValue(key) as *mut u8
-}
+ #[inline]
+ pub unsafe fn get(&'static self) -> *mut u8 {
+ c::TlsGetValue(self.key()).cast()
+ }
-#[inline]
-pub unsafe fn destroy(_key: Key) {
- rtabort!("can't destroy tls keys on windows")
-}
+ #[inline]
+ unsafe fn key(&'static self) -> Key {
+ match self.key.load(Acquire) {
+ 0 => self.init(),
+ key => key - 1,
+ }
+ }
+
+ #[cold]
+ unsafe fn init(&'static self) -> Key {
+ if self.dtor.is_some() {
+ let mut pending = c::FALSE;
+ let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut());
+ assert_eq!(r, c::TRUE);
-#[inline]
-pub fn requires_synchronized_create() -> bool {
- true
+ if pending == c::FALSE {
+ // Some other thread initialized the key, load it.
+ self.key.load(Relaxed) - 1
+ } else {
+ let key = c::TlsAlloc();
+ if key == c::TLS_OUT_OF_INDEXES {
+ // Wakeup the waiting threads before panicking to avoid deadlock.
+ c::InitOnceComplete(self.once.get(), c::INIT_ONCE_INIT_FAILED, ptr::null_mut());
+ panic!("out of TLS indexes");
+ }
+
+ self.key.store(key + 1, Release);
+ register_dtor(self);
+
+ let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut());
+ debug_assert_eq!(r, c::TRUE);
+
+ key
+ }
+ } else {
+ // If there is no destructor to clean up, we can use racy initialization.
+
+ let key = c::TlsAlloc();
+ assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes");
+
+ match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) {
+ Ok(_) => key,
+ Err(new) => {
+ // Some other thread completed initialization first, so destroy
+ // our key and use theirs.
+ let r = c::TlsFree(key);
+ debug_assert_eq!(r, c::TRUE);
+ new - 1
+ }
+ }
+ }
+ }
}
+unsafe impl Send for StaticKey {}
+unsafe impl Sync for StaticKey {}
+
// -------------------------------------------------------------------------
// Dtor registration
//
@@ -96,29 +150,21 @@ pub fn requires_synchronized_create() -> bool {
// Typically processes have a statically known set of TLS keys which is pretty
// small, and we'd want to keep this memory alive for the whole process anyway
// really.
-//
-// Perhaps one day we can fold the `Box` here into a static allocation,
-// expanding the `StaticKey` structure to contain not only a slot for the TLS
-// key but also a slot for the destructor queue on windows. An optimization for
-// another day!
-
-static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
-
-struct Node {
- dtor: Dtor,
- key: Key,
- next: *mut Node,
-}
-unsafe fn register_dtor(key: Key, dtor: Dtor) {
- let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() }));
+static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut());
- let mut head = DTORS.load(SeqCst);
+/// Should only be called once per key, otherwise loops or breaks may occur in
+/// the linked list.
+unsafe fn register_dtor(key: &'static StaticKey) {
+ let this = <*const StaticKey>::cast_mut(key);
+ // Use acquire ordering to pass along the changes done by the previously
+ // registered keys when we store the new head with release ordering.
+ let mut head = DTORS.load(Acquire);
loop {
- node.next = head;
- match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) {
- Ok(_) => return, // nothing to drop, we successfully added the node to the list
- Err(cur) => head = cur,
+ key.next.store(head, Relaxed);
+ match DTORS.compare_exchange_weak(head, this, Release, Acquire) {
+ Ok(_) => break,
+ Err(new) => head = new,
}
}
}
@@ -214,25 +260,29 @@ unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv:
unsafe fn reference_tls_used() {}
}
-#[allow(dead_code)] // actually called above
+#[allow(dead_code)] // actually called below
unsafe fn run_dtors() {
- let mut any_run = true;
for _ in 0..5 {
- if !any_run {
- break;
- }
- any_run = false;
- let mut cur = DTORS.load(SeqCst);
+ let mut any_run = false;
+
+ // Use acquire ordering to observe key initialization.
+ let mut cur = DTORS.load(Acquire);
while !cur.is_null() {
- let ptr = c::TlsGetValue((*cur).key);
+ let key = (*cur).key.load(Relaxed) - 1;
+ let dtor = (*cur).dtor.unwrap();
+ let ptr = c::TlsGetValue(key);
if !ptr.is_null() {
- c::TlsSetValue((*cur).key, ptr::null_mut());
- ((*cur).dtor)(ptr as *mut _);
+ c::TlsSetValue(key, ptr::null_mut());
+ dtor(ptr as *mut _);
any_run = true;
}
- cur = (*cur).next;
+ cur = (*cur).next.load(Relaxed);
+ }
+
+ if !any_run {
+ break;
}
}
}
diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs
new file mode 100644
index 000000000..c95f383fb
--- /dev/null
+++ b/library/std/src/sys/windows/thread_local_key/tests.rs
@@ -0,0 +1,53 @@
+use super::StaticKey;
+use crate::ptr;
+
+#[test]
+fn smoke() {
+ static K1: StaticKey = StaticKey::new(None);
+ static K2: StaticKey = StaticKey::new(None);
+
+ unsafe {
+ assert!(K1.get().is_null());
+ assert!(K2.get().is_null());
+ K1.set(ptr::invalid_mut(1));
+ K2.set(ptr::invalid_mut(2));
+ assert_eq!(K1.get() as usize, 1);
+ assert_eq!(K2.get() as usize, 2);
+ }
+}
+
+#[test]
+fn destructors() {
+ use crate::mem::ManuallyDrop;
+ use crate::sync::Arc;
+ use crate::thread;
+
+ unsafe extern "C" fn destruct(ptr: *mut u8) {
+ drop(Arc::from_raw(ptr as *const ()));
+ }
+
+ static KEY: StaticKey = StaticKey::new(Some(destruct));
+
+ let shared1 = Arc::new(());
+ let shared2 = Arc::clone(&shared1);
+
+ unsafe {
+ assert!(KEY.get().is_null());
+ KEY.set(Arc::into_raw(shared1) as *mut u8);
+ }
+
+ thread::spawn(move || unsafe {
+ assert!(KEY.get().is_null());
+ KEY.set(Arc::into_raw(shared2) as *mut u8);
+ })
+ .join()
+ .unwrap();
+
+ // Leak the Arc, let the TLS destructor clean it up.
+ let shared1 = unsafe { ManuallyDrop::new(Arc::from_raw(KEY.get() as *const ())) };
+ assert_eq!(
+ Arc::strong_count(&shared1),
+ 1,
+ "destructor should have dropped the other reference on thread exit"
+ );
+}
diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs
index d876e0f6f..2f7ae863b 100644
--- a/library/std/src/sys/windows/thread_parker.rs
+++ b/library/std/src/sys/windows/thread_parker.rs
@@ -197,19 +197,17 @@ impl Parker {
// purpose, to make sure every unpark() has a release-acquire ordering
// with park().
if self.state.swap(NOTIFIED, Release) == PARKED {
- if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() {
- unsafe {
+ unsafe {
+ if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() {
wake_by_address_single(self.ptr());
- }
- } else {
- // If we run NtReleaseKeyedEvent before the waiting thread runs
- // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
- // If the waiting thread wakes up before we run NtReleaseKeyedEvent
- // (e.g. due to a timeout), this blocks until we do wake up a thread.
- // To prevent this thread from blocking indefinitely in that case,
- // park_impl() will, after seeing the state set to NOTIFIED after
- // waking up, call NtWaitForKeyedEvent again to unblock us.
- unsafe {
+ } else {
+ // If we run NtReleaseKeyedEvent before the waiting thread runs
+ // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
+ // If the waiting thread wakes up before we run NtReleaseKeyedEvent
+ // (e.g. due to a timeout), this blocks until we do wake up a thread.
+ // To prevent this thread from blocking indefinitely in that case,
+ // park_impl() will, after seeing the state set to NOTIFIED after
+ // waking up, call NtWaitForKeyedEvent again to unblock us.
c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
}
}
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index 31164afdc..8807077cb 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -7,15 +7,14 @@ use crate::fmt;
use crate::io;
use crate::io::prelude::*;
use crate::path::{self, Path, PathBuf};
-use crate::sys_common::mutex::StaticMutex;
+use crate::sync::{Mutex, PoisonError};
/// Max number of frames to print.
const MAX_NB_FRAMES: usize = 100;
-// SAFETY: Don't attempt to lock this reentrantly.
-pub unsafe fn lock() -> impl Drop {
- static LOCK: StaticMutex = StaticMutex::new();
- LOCK.lock()
+pub fn lock() -> impl Drop {
+ static LOCK: Mutex<()> = Mutex::new(());
+ LOCK.lock().unwrap_or_else(PoisonError::into_inner)
}
/// Prints the current backtrace.
diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs
index f3ac1061b..8bc5b2411 100644
--- a/library/std/src/sys_common/condvar.rs
+++ b/library/std/src/sys_common/condvar.rs
@@ -15,6 +15,7 @@ pub struct Condvar {
impl Condvar {
/// Creates a new condition variable for use.
#[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> Self {
Self { inner: imp::MovableCondvar::new(), check: CondvarCheck::new() }
}
diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs
index ce8f36704..4ac9e62bf 100644
--- a/library/std/src/sys_common/condvar/check.rs
+++ b/library/std/src/sys_common/condvar/check.rs
@@ -50,6 +50,7 @@ pub struct NoCheck;
#[allow(dead_code)]
impl NoCheck {
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> Self {
Self
}
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 80f56bf75..8c19f9332 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -27,17 +27,25 @@ pub mod io;
pub mod lazy_box;
pub mod memchr;
pub mod mutex;
+pub mod once;
pub mod process;
pub mod remutex;
pub mod rwlock;
pub mod thread;
pub mod thread_info;
pub mod thread_local_dtor;
-pub mod thread_local_key;
pub mod thread_parker;
pub mod wtf8;
cfg_if::cfg_if! {
+ if #[cfg(target_os = "windows")] {
+ pub use crate::sys::thread_local_key;
+ } else {
+ pub mod thread_local_key;
+ }
+}
+
+cfg_if::cfg_if! {
if #[cfg(any(target_os = "l4re",
target_os = "hermit",
feature = "restricted-std",
diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs
index 48479f5bd..98046f20f 100644
--- a/library/std/src/sys_common/mutex.rs
+++ b/library/std/src/sys_common/mutex.rs
@@ -1,49 +1,5 @@
use crate::sys::locks as imp;
-/// An OS-based mutual exclusion lock, meant for use in static variables.
-///
-/// This mutex has a const constructor ([`StaticMutex::new`]), does not
-/// implement `Drop` to cleanup resources, and causes UB when used reentrantly.
-///
-/// This mutex does not implement poisoning.
-///
-/// This is a wrapper around `imp::Mutex` that does *not* call `init()` and
-/// `destroy()`.
-pub struct StaticMutex(imp::Mutex);
-
-unsafe impl Sync for StaticMutex {}
-
-impl StaticMutex {
- /// Creates a new mutex for use.
- #[inline]
- pub const fn new() -> Self {
- Self(imp::Mutex::new())
- }
-
- /// Calls raw_lock() and then returns an RAII guard to guarantee the mutex
- /// will be unlocked.
- ///
- /// It is undefined behaviour to call this function while locked by the
- /// same thread.
- #[inline]
- pub unsafe fn lock(&'static self) -> StaticMutexGuard {
- self.0.lock();
- StaticMutexGuard(&self.0)
- }
-}
-
-#[must_use]
-pub struct StaticMutexGuard(&'static imp::Mutex);
-
-impl Drop for StaticMutexGuard {
- #[inline]
- fn drop(&mut self) {
- unsafe {
- self.0.unlock();
- }
- }
-}
-
/// An OS-based mutual exclusion lock.
///
/// This mutex cleans up its resources in its `Drop` implementation, may safely
@@ -61,6 +17,7 @@ unsafe impl Sync for MovableMutex {}
impl MovableMutex {
/// Creates a new mutex.
#[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> Self {
Self(imp::MovableMutex::new())
}
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index 33d336c43..fad4a6333 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -2,15 +2,16 @@
mod tests;
use crate::cmp;
-use crate::ffi::CString;
+use crate::convert::{TryFrom, TryInto};
use crate::fmt;
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::ptr;
+use crate::sys::common::small_c_string::run_with_cstr;
use crate::sys::net::netc as c;
use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket};
-use crate::sys_common::{FromInner, IntoInner};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
use libc::{c_int, c_void};
@@ -197,14 +198,15 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
init();
- let c_host = CString::new(host)?;
- let mut hints: c::addrinfo = unsafe { mem::zeroed() };
- hints.ai_socktype = c::SOCK_STREAM;
- let mut res = ptr::null_mut();
- unsafe {
- cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res))
- .map(|_| LookupHost { original: res, cur: res, port })
- }
+ run_with_cstr(host.as_bytes(), |c_host| {
+ let mut hints: c::addrinfo = unsafe { mem::zeroed() };
+ hints.ai_socktype = c::SOCK_STREAM;
+ let mut res = ptr::null_mut();
+ unsafe {
+ cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res))
+ .map(|_| LookupHost { original: res, cur: res, port })
+ }
+ })
}
}
@@ -345,6 +347,12 @@ impl TcpStream {
}
}
+impl AsInner<Socket> for TcpStream {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
impl FromInner<Socket> for TcpStream {
fn from_inner(socket: Socket) -> TcpStream {
TcpStream { inner: socket }
diff --git a/library/std/src/sys_common/once/futex.rs b/library/std/src/sys_common/once/futex.rs
new file mode 100644
index 000000000..5c7e6c013
--- /dev/null
+++ b/library/std/src/sys_common/once/futex.rs
@@ -0,0 +1,134 @@
+use crate::cell::Cell;
+use crate::sync as public;
+use crate::sync::atomic::{
+ AtomicU32,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::futex::{futex_wait, futex_wake_all};
+
+// On some platforms, the OS is very nice and handles the waiter queue for us.
+// This means we only need one atomic value with 5 states:
+
+/// No initialization has run yet, and no thread is currently using the Once.
+const INCOMPLETE: u32 = 0;
+/// Some thread has previously attempted to initialize the Once, but it panicked,
+/// so the Once is now poisoned. There are no other threads currently accessing
+/// this Once.
+const POISONED: u32 = 1;
+/// Some thread is currently attempting to run initialization. It may succeed,
+/// so all future threads need to wait for it to finish.
+const RUNNING: u32 = 2;
+/// Some thread is currently attempting to run initialization and there are threads
+/// waiting for it to finish.
+const QUEUED: u32 = 3;
+/// Initialization has completed and all future calls should finish immediately.
+const COMPLETE: u32 = 4;
+
+// Threads wait by setting the state to QUEUED and calling `futex_wait` on the state
+// variable. When the running thread finishes, it will wake all waiting threads using
+// `futex_wake_all`.
+
+pub struct OnceState {
+ poisoned: bool,
+ set_state_to: Cell<u32>,
+}
+
+impl OnceState {
+ #[inline]
+ pub fn is_poisoned(&self) -> bool {
+ self.poisoned
+ }
+
+ #[inline]
+ pub fn poison(&self) {
+ self.set_state_to.set(POISONED);
+ }
+}
+
+struct CompletionGuard<'a> {
+ state: &'a AtomicU32,
+ set_state_on_drop_to: u32,
+}
+
+impl<'a> Drop for CompletionGuard<'a> {
+ fn drop(&mut self) {
+ // Use release ordering to propagate changes to all threads checking
+ // up on the Once. `futex_wake_all` does its own synchronization, hence
+ // we do not need `AcqRel`.
+ if self.state.swap(self.set_state_on_drop_to, Release) == QUEUED {
+ futex_wake_all(&self.state);
+ }
+ }
+}
+
+pub struct Once {
+ state: AtomicU32,
+}
+
+impl Once {
+ #[inline]
+ pub const fn new() -> Once {
+ Once { state: AtomicU32::new(INCOMPLETE) }
+ }
+
+ #[inline]
+ pub fn is_completed(&self) -> bool {
+ // Use acquire ordering to make all initialization changes visible to the
+ // current thread.
+ self.state.load(Acquire) == COMPLETE
+ }
+
+ // This uses FnMut to match the API of the generic implementation. As this
+ // implementation is quite light-weight, it is generic over the closure and
+ // so avoids the cost of dynamic dispatch.
+ #[cold]
+ #[track_caller]
+ pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
+ let mut state = self.state.load(Acquire);
+ loop {
+ match state {
+ POISONED if !ignore_poisoning => {
+ // Panic to propagate the poison.
+ panic!("Once instance has previously been poisoned");
+ }
+ INCOMPLETE | POISONED => {
+ // Try to register the current thread as the one running.
+ if let Err(new) =
+ self.state.compare_exchange_weak(state, RUNNING, Acquire, Acquire)
+ {
+ state = new;
+ continue;
+ }
+ // `waiter_queue` will manage other waiting threads, and
+ // wake them up on drop.
+ let mut waiter_queue =
+ CompletionGuard { state: &self.state, set_state_on_drop_to: POISONED };
+ // Run the function, letting it know if we're poisoned or not.
+ let f_state = public::OnceState {
+ inner: OnceState {
+ poisoned: state == POISONED,
+ set_state_to: Cell::new(COMPLETE),
+ },
+ };
+ f(&f_state);
+ waiter_queue.set_state_on_drop_to = f_state.inner.set_state_to.get();
+ return;
+ }
+ RUNNING | QUEUED => {
+ // Set the state to QUEUED if it is not already.
+ if state == RUNNING
+ && let Err(new) = self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire)
+ {
+ state = new;
+ continue;
+ }
+
+ futex_wait(&self.state, QUEUED, None);
+ state = self.state.load(Acquire);
+ }
+ COMPLETE => return,
+ _ => unreachable!("state is never set to invalid values"),
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys_common/once/generic.rs b/library/std/src/sys_common/once/generic.rs
new file mode 100644
index 000000000..acf5f2471
--- /dev/null
+++ b/library/std/src/sys_common/once/generic.rs
@@ -0,0 +1,282 @@
+// Each `Once` has one word of atomic state, and this state is CAS'd on to
+// determine what to do. There are four possible state of a `Once`:
+//
+// * Incomplete - no initialization has run yet, and no thread is currently
+// using the Once.
+// * Poisoned - some thread has previously attempted to initialize the Once, but
+// it panicked, so the Once is now poisoned. There are no other
+// threads currently accessing this Once.
+// * Running - some thread is currently attempting to run initialization. It may
+// succeed, so all future threads need to wait for it to finish.
+// Note that this state is accompanied with a payload, described
+// below.
+// * Complete - initialization has completed and all future calls should finish
+// immediately.
+//
+// With 4 states we need 2 bits to encode this, and we use the remaining bits
+// in the word we have allocated as a queue of threads waiting for the thread
+// responsible for entering the RUNNING state. This queue is just a linked list
+// of Waiter nodes which is monotonically increasing in size. Each node is
+// allocated on the stack, and whenever the running closure finishes it will
+// consume the entire queue and notify all waiters they should try again.
+//
+// You'll find a few more details in the implementation, but that's the gist of
+// it!
+//
+// Atomic orderings:
+// When running `Once` we deal with multiple atomics:
+// `Once.state_and_queue` and an unknown number of `Waiter.signaled`.
+// * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the
+// result of the `Once`, and (3) for synchronizing `Waiter` nodes.
+// - At the end of the `call` function we have to make sure the result
+// of the `Once` is acquired. So every load which can be the only one to
+// load COMPLETED must have at least acquire ordering, which means all
+// three of them.
+// - `WaiterQueue::drop` is the only place that may store COMPLETED, and
+// must do so with release ordering to make the result available.
+// - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and
+// needs to make the nodes available with release ordering. The load in
+// its `compare_exchange` can be relaxed because it only has to compare
+// the atomic, not to read other data.
+// - `WaiterQueue::drop` must see the `Waiter` nodes, so it must load
+// `state_and_queue` with acquire ordering.
+// - There is just one store where `state_and_queue` is used only as a
+// state flag, without having to synchronize data: switching the state
+// from INCOMPLETE to RUNNING in `call`. This store can be Relaxed,
+// but the read has to be Acquire because of the requirements mentioned
+// above.
+// * `Waiter.signaled` is both used as a flag, and to protect a field with
+// interior mutability in `Waiter`. `Waiter.thread` is changed in
+// `WaiterQueue::drop` which then sets `signaled` with release ordering.
+// After `wait` loads `signaled` with acquire ordering and sees it is true,
+// it needs to see the changes to drop the `Waiter` struct correctly.
+// * There is one place where the two atomics `Once.state_and_queue` and
+// `Waiter.signaled` come together, and might be reordered by the compiler or
+// processor. Because both use acquire ordering such a reordering is not
+// allowed, so no need for `SeqCst`.
+
+use crate::cell::Cell;
+use crate::fmt;
+use crate::ptr;
+use crate::sync as public;
+use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
+use crate::thread::{self, Thread};
+
+type Masked = ();
+
+pub struct Once {
+ state_and_queue: AtomicPtr<Masked>,
+}
+
+pub struct OnceState {
+ poisoned: bool,
+ set_state_on_drop_to: Cell<*mut Masked>,
+}
+
+// Four states that a Once can be in, encoded into the lower bits of
+// `state_and_queue` in the Once structure.
+const INCOMPLETE: usize = 0x0;
+const POISONED: usize = 0x1;
+const RUNNING: usize = 0x2;
+const COMPLETE: usize = 0x3;
+
+// Mask to learn about the state. All other bits are the queue of waiters if
+// this is in the RUNNING state.
+const STATE_MASK: usize = 0x3;
+
+// Representation of a node in the linked list of waiters, used while in the
+// RUNNING state.
+// Note: `Waiter` can't hold a mutable pointer to the next thread, because then
+// `wait` would both hand out a mutable reference to its `Waiter` node, and keep
+// a shared reference to check `signaled`. Instead we hold shared references and
+// use interior mutability.
+#[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
+struct Waiter {
+ thread: Cell<Option<Thread>>,
+ signaled: AtomicBool,
+ next: *const Waiter,
+}
+
+// Head of a linked list of waiters.
+// Every node is a struct on the stack of a waiting thread.
+// Will wake up the waiters when it gets dropped, i.e. also on panic.
+struct WaiterQueue<'a> {
+ state_and_queue: &'a AtomicPtr<Masked>,
+ set_state_on_drop_to: *mut Masked,
+}
+
+impl Once {
+ #[inline]
+ pub const fn new() -> Once {
+ Once { state_and_queue: AtomicPtr::new(ptr::invalid_mut(INCOMPLETE)) }
+ }
+
+ #[inline]
+ pub fn is_completed(&self) -> bool {
+ // An `Acquire` load is enough because that makes all the initialization
+ // operations visible to us, and, this being a fast path, weaker
+ // ordering helps with performance. This `Acquire` synchronizes with
+ // `Release` operations on the slow path.
+ self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
+ }
+
+ // This is a non-generic function to reduce the monomorphization cost of
+ // using `call_once` (this isn't exactly a trivial or small implementation).
+ //
+ // Additionally, this is tagged with `#[cold]` as it should indeed be cold
+ // and it helps let LLVM know that calls to this function should be off the
+ // fast path. Essentially, this should help generate more straight line code
+ // in LLVM.
+ //
+ // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
+ // currently no way to take an `FnOnce` and call it via virtual dispatch
+ // without some allocation overhead.
+ #[cold]
+ #[track_caller]
+ pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) {
+ let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
+ loop {
+ match state_and_queue.addr() {
+ COMPLETE => break,
+ POISONED if !ignore_poisoning => {
+ // Panic to propagate the poison.
+ panic!("Once instance has previously been poisoned");
+ }
+ POISONED | INCOMPLETE => {
+ // Try to register this thread as the one RUNNING.
+ let exchange_result = self.state_and_queue.compare_exchange(
+ state_and_queue,
+ ptr::invalid_mut(RUNNING),
+ Ordering::Acquire,
+ Ordering::Acquire,
+ );
+ if let Err(old) = exchange_result {
+ state_and_queue = old;
+ continue;
+ }
+ // `waiter_queue` will manage other waiting threads, and
+ // wake them up on drop.
+ let mut waiter_queue = WaiterQueue {
+ state_and_queue: &self.state_and_queue,
+ set_state_on_drop_to: ptr::invalid_mut(POISONED),
+ };
+ // Run the initialization function, letting it know if we're
+ // poisoned or not.
+ let init_state = public::OnceState {
+ inner: OnceState {
+ poisoned: state_and_queue.addr() == POISONED,
+ set_state_on_drop_to: Cell::new(ptr::invalid_mut(COMPLETE)),
+ },
+ };
+ init(&init_state);
+ waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get();
+ break;
+ }
+ _ => {
+ // All other values must be RUNNING with possibly a
+ // pointer to the waiter queue in the more significant bits.
+ assert!(state_and_queue.addr() & STATE_MASK == RUNNING);
+ wait(&self.state_and_queue, state_and_queue);
+ state_and_queue = self.state_and_queue.load(Ordering::Acquire);
+ }
+ }
+ }
+ }
+}
+
+fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
+ // Note: the following code was carefully written to avoid creating a
+ // mutable reference to `node` that gets aliased.
+ loop {
+ // Don't queue this thread if the status is no longer running,
+ // otherwise we will not be woken up.
+ if current_state.addr() & STATE_MASK != RUNNING {
+ return;
+ }
+
+ // Create the node for our current thread.
+ let node = Waiter {
+ thread: Cell::new(Some(thread::current())),
+ signaled: AtomicBool::new(false),
+ next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter,
+ };
+ let me = &node as *const Waiter as *const Masked as *mut Masked;
+
+ // Try to slide in the node at the head of the linked list, making sure
+ // that another thread didn't just replace the head of the linked list.
+ let exchange_result = state_and_queue.compare_exchange(
+ current_state,
+ me.with_addr(me.addr() | RUNNING),
+ Ordering::Release,
+ Ordering::Relaxed,
+ );
+ if let Err(old) = exchange_result {
+ current_state = old;
+ continue;
+ }
+
+ // We have enqueued ourselves, now lets wait.
+ // It is important not to return before being signaled, otherwise we
+ // would drop our `Waiter` node and leave a hole in the linked list
+ // (and a dangling reference). Guard against spurious wakeups by
+ // reparking ourselves until we are signaled.
+ while !node.signaled.load(Ordering::Acquire) {
+ // If the managing thread happens to signal and unpark us before we
+ // can park ourselves, the result could be this thread never gets
+ // unparked. Luckily `park` comes with the guarantee that if it got
+ // an `unpark` just before on an unparked thread it does not park.
+ thread::park();
+ }
+ break;
+ }
+}
+
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl fmt::Debug for Once {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Once").finish_non_exhaustive()
+ }
+}
+
+impl Drop for WaiterQueue<'_> {
+ fn drop(&mut self) {
+ // Swap out our state with however we finished.
+ let state_and_queue =
+ self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel);
+
+ // We should only ever see an old state which was RUNNING.
+ assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING);
+
+ // Walk the entire linked list of waiters and wake them up (in lifo
+ // order, last to register is first to wake up).
+ unsafe {
+ // Right after setting `node.signaled = true` the other thread may
+ // free `node` if there happens to be has a spurious wakeup.
+ // So we have to take out the `thread` field and copy the pointer to
+ // `next` first.
+ let mut queue =
+ state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter;
+ while !queue.is_null() {
+ let next = (*queue).next;
+ let thread = (*queue).thread.take().unwrap();
+ (*queue).signaled.store(true, Ordering::Release);
+ // ^- FIXME (maybe): This is another case of issue #55005
+ // `store()` has a potentially dangling ref to `signaled`.
+ queue = next;
+ thread.unpark();
+ }
+ }
+ }
+}
+
+impl OnceState {
+ #[inline]
+ pub fn is_poisoned(&self) -> bool {
+ self.poisoned
+ }
+
+ #[inline]
+ pub fn poison(&self) {
+ self.set_state_on_drop_to.set(ptr::invalid_mut(POISONED));
+ }
+}
diff --git a/library/std/src/sys_common/once/mod.rs b/library/std/src/sys_common/once/mod.rs
new file mode 100644
index 000000000..8742e68cc
--- /dev/null
+++ b/library/std/src/sys_common/once/mod.rs
@@ -0,0 +1,43 @@
+// A "once" is a relatively simple primitive, and it's also typically provided
+// by the OS as well (see `pthread_once` or `InitOnceExecuteOnce`). The OS
+// primitives, however, tend to have surprising restrictions, such as the Unix
+// one doesn't allow an argument to be passed to the function.
+//
+// As a result, we end up implementing it ourselves in the standard library.
+// This also gives us the opportunity to optimize the implementation a bit which
+// should help the fast path on call sites.
+//
+// So to recap, the guarantees of a Once are that it will call the
+// initialization closure at most once, and it will never return until the one
+// that's running has finished running. This means that we need some form of
+// blocking here while the custom callback is running at the very least.
+// Additionally, we add on the restriction of **poisoning**. Whenever an
+// initialization closure panics, the Once enters a "poisoned" state which means
+// that all future calls will immediately panic as well.
+//
+// So to implement this, one might first reach for a `Mutex`, but those cannot
+// be put into a `static`. It also gets a lot harder with poisoning to figure
+// out when the mutex needs to be deallocated because it's not after the closure
+// finishes, but after the first successful closure finishes.
+//
+// All in all, this is instead implemented with atomics and lock-free
+// operations! Whee!
+
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_arch = "wasm32", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+ target_os = "hermit",
+ ))] {
+ mod futex;
+ pub use futex::{Once, OnceState};
+ } else {
+ mod generic;
+ pub use generic::{Once, OnceState};
+ }
+}
diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs
index 8921af311..b448ae3a9 100644
--- a/library/std/src/sys_common/remutex.rs
+++ b/library/std/src/sys_common/remutex.rs
@@ -1,13 +1,11 @@
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests;
+use super::mutex as sys;
use crate::cell::UnsafeCell;
-use crate::marker::PhantomPinned;
use crate::ops::Deref;
use crate::panic::{RefUnwindSafe, UnwindSafe};
-use crate::pin::Pin;
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
-use crate::sys::locks as sys;
/// A re-entrant mutual exclusion
///
@@ -41,11 +39,10 @@ use crate::sys::locks as sys;
/// synchronization is left to the mutex, making relaxed memory ordering for
/// the `owner` field fine in all cases.
pub struct ReentrantMutex<T> {
- mutex: sys::Mutex,
+ mutex: sys::MovableMutex,
owner: AtomicUsize,
lock_count: UnsafeCell<u32>,
data: T,
- _pinned: PhantomPinned,
}
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
@@ -68,39 +65,22 @@ impl<T> RefUnwindSafe for ReentrantMutex<T> {}
/// guarded data.
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
pub struct ReentrantMutexGuard<'a, T: 'a> {
- lock: Pin<&'a ReentrantMutex<T>>,
+ lock: &'a ReentrantMutex<T>,
}
impl<T> !Send for ReentrantMutexGuard<'_, T> {}
impl<T> ReentrantMutex<T> {
/// Creates a new reentrant mutex in an unlocked state.
- ///
- /// # Unsafety
- ///
- /// This function is unsafe because it is required that `init` is called
- /// once this mutex is in its final resting place, and only then are the
- /// lock/unlock methods safe.
- pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
+ pub const fn new(t: T) -> ReentrantMutex<T> {
ReentrantMutex {
- mutex: sys::Mutex::new(),
+ mutex: sys::MovableMutex::new(),
owner: AtomicUsize::new(0),
lock_count: UnsafeCell::new(0),
data: t,
- _pinned: PhantomPinned,
}
}
- /// Initializes this mutex so it's ready for use.
- ///
- /// # Unsafety
- ///
- /// Unsafe to call more than once, and must be called after this will no
- /// longer move in memory.
- pub unsafe fn init(self: Pin<&mut Self>) {
- self.get_unchecked_mut().mutex.init()
- }
-
/// Acquires a mutex, blocking the current thread until it is able to do so.
///
/// This function will block the caller until it is available to acquire the mutex.
@@ -113,15 +93,14 @@ impl<T> ReentrantMutex<T> {
/// If another user of this mutex panicked while holding the mutex, then
/// this call will return failure if the mutex would otherwise be
/// acquired.
- pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> {
+ pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
let this_thread = current_thread_unique_ptr();
- // Safety: We only touch lock_count when we own the lock,
- // and since self is pinned we can safely call the lock() on the mutex.
+ // Safety: We only touch lock_count when we own the lock.
unsafe {
if self.owner.load(Relaxed) == this_thread {
self.increment_lock_count();
} else {
- self.mutex.lock();
+ self.mutex.raw_lock();
self.owner.store(this_thread, Relaxed);
debug_assert_eq!(*self.lock_count.get(), 0);
*self.lock_count.get() = 1;
@@ -142,10 +121,9 @@ impl<T> ReentrantMutex<T> {
/// If another user of this mutex panicked while holding the mutex, then
/// this call will return failure if the mutex would otherwise be
/// acquired.
- pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> {
+ pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
let this_thread = current_thread_unique_ptr();
- // Safety: We only touch lock_count when we own the lock,
- // and since self is pinned we can safely call the try_lock on the mutex.
+ // Safety: We only touch lock_count when we own the lock.
unsafe {
if self.owner.load(Relaxed) == this_thread {
self.increment_lock_count();
@@ -179,12 +157,12 @@ impl<T> Deref for ReentrantMutexGuard<'_, T> {
impl<T> Drop for ReentrantMutexGuard<'_, T> {
#[inline]
fn drop(&mut self) {
- // Safety: We own the lock, and the lock is pinned.
+ // Safety: We own the lock.
unsafe {
*self.lock.lock_count.get() -= 1;
if *self.lock.lock_count.get() == 0 {
self.lock.owner.store(0, Relaxed);
- self.lock.mutex.unlock();
+ self.lock.mutex.raw_unlock();
}
}
}
diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs
index 64873b850..8e97ce11c 100644
--- a/library/std/src/sys_common/remutex/tests.rs
+++ b/library/std/src/sys_common/remutex/tests.rs
@@ -1,18 +1,11 @@
-use crate::boxed::Box;
use crate::cell::RefCell;
-use crate::pin::Pin;
use crate::sync::Arc;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
use crate::thread;
#[test]
fn smoke() {
- let m = unsafe {
- let mut m = Box::pin(ReentrantMutex::new(()));
- m.as_mut().init();
- m
- };
- let m = m.as_ref();
+ let m = ReentrantMutex::new(());
{
let a = m.lock();
{
@@ -29,20 +22,15 @@ fn smoke() {
#[test]
fn is_mutex() {
- let m = unsafe {
- // FIXME: Simplify this if Arc gets an Arc::get_pin_mut.
- let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
- Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
- Pin::new_unchecked(m)
- };
+ let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
let m2 = m.clone();
- let lock = m.as_ref().lock();
+ let lock = m.lock();
let child = thread::spawn(move || {
- let lock = m2.as_ref().lock();
+ let lock = m2.lock();
assert_eq!(*lock.borrow(), 4950);
});
for i in 0..100 {
- let lock = m.as_ref().lock();
+ let lock = m.lock();
*lock.borrow_mut() += i;
}
drop(lock);
@@ -51,22 +39,17 @@ fn is_mutex() {
#[test]
fn trylock_works() {
- let m = unsafe {
- // FIXME: Simplify this if Arc gets an Arc::get_pin_mut.
- let mut m = Arc::new(ReentrantMutex::new(()));
- Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
- Pin::new_unchecked(m)
- };
+ let m = Arc::new(ReentrantMutex::new(()));
let m2 = m.clone();
- let _lock = m.as_ref().try_lock();
- let _lock2 = m.as_ref().try_lock();
+ let _lock = m.try_lock();
+ let _lock2 = m.try_lock();
thread::spawn(move || {
- let lock = m2.as_ref().try_lock();
+ let lock = m2.try_lock();
assert!(lock.is_none());
})
.join()
.unwrap();
- let _lock3 = m.as_ref().try_lock();
+ let _lock3 = m.try_lock();
}
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs
index ba56f3a8f..042981dac 100644
--- a/library/std/src/sys_common/rwlock.rs
+++ b/library/std/src/sys_common/rwlock.rs
@@ -1,65 +1,5 @@
use crate::sys::locks as imp;
-/// An OS-based reader-writer lock, meant for use in static variables.
-///
-/// This rwlock does not implement poisoning.
-///
-/// This rwlock has a const constructor ([`StaticRwLock::new`]), does not
-/// implement `Drop` to cleanup resources.
-pub struct StaticRwLock(imp::RwLock);
-
-impl StaticRwLock {
- /// Creates a new rwlock for use.
- #[inline]
- pub const fn new() -> Self {
- Self(imp::RwLock::new())
- }
-
- /// Acquires shared access to the underlying lock, blocking the current
- /// thread to do so.
- ///
- /// The lock is automatically unlocked when the returned guard is dropped.
- #[inline]
- pub fn read(&'static self) -> StaticRwLockReadGuard {
- unsafe { self.0.read() };
- StaticRwLockReadGuard(&self.0)
- }
-
- /// Acquires write access to the underlying lock, blocking the current thread
- /// to do so.
- ///
- /// The lock is automatically unlocked when the returned guard is dropped.
- #[inline]
- pub fn write(&'static self) -> StaticRwLockWriteGuard {
- unsafe { self.0.write() };
- StaticRwLockWriteGuard(&self.0)
- }
-}
-
-#[must_use]
-pub struct StaticRwLockReadGuard(&'static imp::RwLock);
-
-impl Drop for StaticRwLockReadGuard {
- #[inline]
- fn drop(&mut self) {
- unsafe {
- self.0.read_unlock();
- }
- }
-}
-
-#[must_use]
-pub struct StaticRwLockWriteGuard(&'static imp::RwLock);
-
-impl Drop for StaticRwLockWriteGuard {
- #[inline]
- fn drop(&mut self) {
- unsafe {
- self.0.write_unlock();
- }
- }
-}
-
/// An OS-based reader-writer lock.
///
/// This rwlock cleans up its resources in its `Drop` implementation and may
@@ -75,6 +15,7 @@ pub struct MovableRwLock(imp::MovableRwLock);
impl MovableRwLock {
/// Creates a new reader-writer lock for use.
#[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
pub const fn new() -> Self {
Self(imp::MovableRwLock::new())
}
diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs
index 70beebe86..747579f17 100644
--- a/library/std/src/sys_common/thread_local_key.rs
+++ b/library/std/src/sys_common/thread_local_key.rs
@@ -53,7 +53,6 @@ mod tests;
use crate::sync::atomic::{self, AtomicUsize, Ordering};
use crate::sys::thread_local_key as imp;
-use crate::sys_common::mutex::StaticMutex;
/// A type for TLS keys that are statically allocated.
///
@@ -69,8 +68,10 @@ use crate::sys_common::mutex::StaticMutex;
/// ```ignore (cannot-doctest-private-modules)
/// use tls::os::{StaticKey, INIT};
///
+/// // Use a regular global static to store the key.
/// static KEY: StaticKey = INIT;
///
+/// // The state provided via `get` and `set` is thread-local.
/// unsafe {
/// assert!(KEY.get().is_null());
/// KEY.set(1 as *mut u8);
@@ -149,25 +150,6 @@ impl StaticKey {
}
unsafe fn lazy_init(&self) -> usize {
- // Currently the Windows implementation of TLS is pretty hairy, and
- // it greatly simplifies creation if we just synchronize everything.
- //
- // Additionally a 0-index of a tls key hasn't been seen on windows, so
- // we just simplify the whole branch.
- if imp::requires_synchronized_create() {
- // We never call `INIT_LOCK.init()`, so it is UB to attempt to
- // acquire this mutex reentrantly!
- static INIT_LOCK: StaticMutex = StaticMutex::new();
- let _guard = INIT_LOCK.lock();
- let mut key = self.key.load(Ordering::SeqCst);
- if key == 0 {
- key = imp::create(self.dtor) as usize;
- self.key.store(key, Ordering::SeqCst);
- }
- rtassert!(key != 0);
- return key;
- }
-
// POSIX allows the key created here to be 0, but the compare_exchange
// below relies on using 0 as a sentinel value to check who won the
// race to set the shared TLS key. As far as I know, there is no
@@ -230,8 +212,6 @@ impl Key {
impl Drop for Key {
fn drop(&mut self) {
- // Right now Windows doesn't support TLS key destruction, but this also
- // isn't used anywhere other than tests, so just leak the TLS key.
- // unsafe { imp::destroy(self.key) }
+ unsafe { imp::destroy(self.key) }
}
}
diff --git a/library/std/src/sys_common/thread_local_key/tests.rs b/library/std/src/sys_common/thread_local_key/tests.rs
index 968738a41..6f32b858f 100644
--- a/library/std/src/sys_common/thread_local_key/tests.rs
+++ b/library/std/src/sys_common/thread_local_key/tests.rs
@@ -1,4 +1,5 @@
use super::{Key, StaticKey};
+use core::ptr;
fn assert_sync<T: Sync>() {}
fn assert_send<T: Send>() {}
@@ -12,8 +13,8 @@ fn smoke() {
let k2 = Key::new(None);
assert!(k1.get().is_null());
assert!(k2.get().is_null());
- k1.set(1 as *mut _);
- k2.set(2 as *mut _);
+ k1.set(ptr::invalid_mut(1));
+ k2.set(ptr::invalid_mut(2));
assert_eq!(k1.get() as usize, 1);
assert_eq!(k2.get() as usize, 2);
}
@@ -26,8 +27,8 @@ fn statik() {
unsafe {
assert!(K1.get().is_null());
assert!(K2.get().is_null());
- K1.set(1 as *mut _);
- K2.set(2 as *mut _);
+ K1.set(ptr::invalid_mut(1));
+ K2.set(ptr::invalid_mut(2));
assert_eq!(K1.get() as usize, 1);
assert_eq!(K2.get() as usize, 2);
}
diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs
index cbd7832eb..f86a9a555 100644
--- a/library/std/src/sys_common/thread_parker/mod.rs
+++ b/library/std/src/sys_common/thread_parker/mod.rs
@@ -7,6 +7,7 @@ cfg_if::cfg_if! {
target_os = "openbsd",
target_os = "dragonfly",
target_os = "fuchsia",
+ target_os = "hermit",
))] {
mod futex;
pub use futex::Parker;
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index 57fa49893..dd53767d4 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -89,6 +89,24 @@ impl CodePoint {
self.value
}
+ /// Returns the numeric value of the code point if it is a leading surrogate.
+ #[inline]
+ pub fn to_lead_surrogate(&self) -> Option<u16> {
+ match self.value {
+ lead @ 0xD800..=0xDBFF => Some(lead as u16),
+ _ => None,
+ }
+ }
+
+ /// Returns the numeric value of the code point if it is a trailing surrogate.
+ #[inline]
+ pub fn to_trail_surrogate(&self) -> Option<u16> {
+ match self.value {
+ trail @ 0xDC00..=0xDFFF => Some(trail as u16),
+ _ => None,
+ }
+ }
+
/// Optionally returns a Unicode scalar value for the code point.
///
/// Returns `None` if the code point is a surrogate (from U+D800 to U+DFFF).
@@ -117,6 +135,14 @@ impl CodePoint {
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)]
pub struct Wtf8Buf {
bytes: Vec<u8>,
+
+ /// Do we know that `bytes` holds a valid UTF-8 encoding? We can easily
+ /// know this if we're constructed from a `String` or `&str`.
+ ///
+ /// It is possible for `bytes` to have valid UTF-8 without this being
+ /// set, such as when we're concatenating `&Wtf8`'s and surrogates become
+ /// paired, as we don't bother to rescan the entire string.
+ is_known_utf8: bool,
}
impl ops::Deref for Wtf8Buf {
@@ -147,13 +173,13 @@ impl Wtf8Buf {
/// Creates a new, empty WTF-8 string.
#[inline]
pub fn new() -> Wtf8Buf {
- Wtf8Buf { bytes: Vec::new() }
+ Wtf8Buf { bytes: Vec::new(), is_known_utf8: true }
}
/// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes.
#[inline]
pub fn with_capacity(capacity: usize) -> Wtf8Buf {
- Wtf8Buf { bytes: Vec::with_capacity(capacity) }
+ Wtf8Buf { bytes: Vec::with_capacity(capacity), is_known_utf8: true }
}
/// Creates a WTF-8 string from a UTF-8 `String`.
@@ -163,7 +189,7 @@ impl Wtf8Buf {
/// Since WTF-8 is a superset of UTF-8, this always succeeds.
#[inline]
pub fn from_string(string: String) -> Wtf8Buf {
- Wtf8Buf { bytes: string.into_bytes() }
+ Wtf8Buf { bytes: string.into_bytes(), is_known_utf8: true }
}
/// Creates a WTF-8 string from a UTF-8 `&str` slice.
@@ -173,11 +199,12 @@ impl Wtf8Buf {
/// Since WTF-8 is a superset of UTF-8, this always succeeds.
#[inline]
pub fn from_str(str: &str) -> Wtf8Buf {
- Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()) }
+ Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()), is_known_utf8: true }
}
pub fn clear(&mut self) {
- self.bytes.clear()
+ self.bytes.clear();
+ self.is_known_utf8 = true;
}
/// Creates a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units.
@@ -193,9 +220,11 @@ impl Wtf8Buf {
let surrogate = surrogate.unpaired_surrogate();
// Surrogates are known to be in the code point range.
let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) };
+ // The string will now contain an unpaired surrogate.
+ string.is_known_utf8 = false;
// Skip the WTF-8 concatenation check,
// surrogate pairs are already decoded by decode_utf16
- string.push_code_point_unchecked(code_point)
+ string.push_code_point_unchecked(code_point);
}
}
}
@@ -203,7 +232,7 @@ impl Wtf8Buf {
}
/// Copied from String::push
- /// This does **not** include the WTF-8 concatenation check.
+ /// This does **not** include the WTF-8 concatenation check or `is_known_utf8` check.
fn push_code_point_unchecked(&mut self, code_point: CodePoint) {
let mut bytes = [0; 4];
let bytes = char::encode_utf8_raw(code_point.value, &mut bytes);
@@ -217,6 +246,9 @@ impl Wtf8Buf {
#[inline]
pub fn as_mut_slice(&mut self) -> &mut Wtf8 {
+ // Safety: `Wtf8` doesn't expose any way to mutate the bytes that would
+ // cause them to change from well-formed UTF-8 to ill-formed UTF-8,
+ // which would break the assumptions of the `is_known_utf8` field.
unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) }
}
@@ -236,7 +268,8 @@ impl Wtf8Buf {
/// in the given `Wtf8Buf`. The `Wtf8Buf` may reserve more space to avoid
/// frequent reallocations. After calling `try_reserve`, capacity will be
/// greater than or equal to `self.len() + additional`. Does nothing if
- /// capacity is already sufficient.
+ /// capacity is already sufficient. This method preserves the contents even
+ /// if an error occurs.
///
/// # Errors
///
@@ -313,7 +346,15 @@ impl Wtf8Buf {
self.push_char(decode_surrogate_pair(lead, trail));
self.bytes.extend_from_slice(other_without_trail_surrogate);
}
- _ => self.bytes.extend_from_slice(&other.bytes),
+ _ => {
+ // If we'll be pushing a string containing a surrogate, we may
+ // no longer have UTF-8.
+ if other.next_surrogate(0).is_some() {
+ self.is_known_utf8 = false;
+ }
+
+ self.bytes.extend_from_slice(&other.bytes);
+ }
}
}
@@ -330,13 +371,19 @@ impl Wtf8Buf {
/// like concatenating ill-formed UTF-16 strings effectively would.
#[inline]
pub fn push(&mut self, code_point: CodePoint) {
- if let trail @ 0xDC00..=0xDFFF = code_point.to_u32() {
+ if let Some(trail) = code_point.to_trail_surrogate() {
if let Some(lead) = (&*self).final_lead_surrogate() {
let len_without_lead_surrogate = self.len() - 3;
self.bytes.truncate(len_without_lead_surrogate);
- self.push_char(decode_surrogate_pair(lead, trail as u16));
+ self.push_char(decode_surrogate_pair(lead, trail));
return;
}
+
+ // We're pushing a trailing surrogate.
+ self.is_known_utf8 = false;
+ } else if code_point.to_lead_surrogate().is_some() {
+ // We're pushing a leading surrogate.
+ self.is_known_utf8 = false;
}
// No newly paired surrogates at the boundary.
@@ -363,9 +410,10 @@ impl Wtf8Buf {
/// (that is, if the string contains surrogates),
/// the original WTF-8 string is returned instead.
pub fn into_string(self) -> Result<String, Wtf8Buf> {
- match self.next_surrogate(0) {
- None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }),
- Some(_) => Err(self),
+ if self.is_known_utf8 || self.next_surrogate(0).is_none() {
+ Ok(unsafe { String::from_utf8_unchecked(self.bytes) })
+ } else {
+ Err(self)
}
}
@@ -375,6 +423,11 @@ impl Wtf8Buf {
///
/// Surrogates are replaced with `"\u{FFFD}"` (the replacement character ā€œļæ½ā€)
pub fn into_string_lossy(mut self) -> String {
+ // Fast path: If we already have UTF-8, we can return it immediately.
+ if self.is_known_utf8 {
+ return unsafe { String::from_utf8_unchecked(self.bytes) };
+ }
+
let mut pos = 0;
loop {
match self.next_surrogate(pos) {
@@ -397,7 +450,7 @@ impl Wtf8Buf {
/// Converts a `Box<Wtf8>` into a `Wtf8Buf`.
pub fn from_box(boxed: Box<Wtf8>) -> Wtf8Buf {
let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) };
- Wtf8Buf { bytes: bytes.into_vec() }
+ Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false }
}
}
@@ -575,6 +628,11 @@ impl Wtf8 {
}
}
+ /// Creates an owned `Wtf8Buf` from a borrowed `Wtf8`.
+ pub fn to_owned(&self) -> Wtf8Buf {
+ Wtf8Buf { bytes: self.bytes.to_vec(), is_known_utf8: false }
+ }
+
/// Lossily converts the string to UTF-8.
/// Returns a UTF-8 `&str` slice if the contents are well-formed in UTF-8.
///
@@ -664,7 +722,8 @@ impl Wtf8 {
}
pub fn clone_into(&self, buf: &mut Wtf8Buf) {
- self.bytes.clone_into(&mut buf.bytes)
+ buf.is_known_utf8 = false;
+ self.bytes.clone_into(&mut buf.bytes);
}
/// Boxes this `Wtf8`.
@@ -704,12 +763,12 @@ impl Wtf8 {
#[inline]
pub fn to_ascii_lowercase(&self) -> Wtf8Buf {
- Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() }
+ Wtf8Buf { bytes: self.bytes.to_ascii_lowercase(), is_known_utf8: false }
}
#[inline]
pub fn to_ascii_uppercase(&self) -> Wtf8Buf {
- Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() }
+ Wtf8Buf { bytes: self.bytes.to_ascii_uppercase(), is_known_utf8: false }
}
#[inline]
diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs
index 931996791..1a302d646 100644
--- a/library/std/src/sys_common/wtf8/tests.rs
+++ b/library/std/src/sys_common/wtf8/tests.rs
@@ -20,6 +20,36 @@ fn code_point_to_u32() {
}
#[test]
+fn code_point_to_lead_surrogate() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0).to_lead_surrogate(), None);
+ assert_eq!(c(0xE9).to_lead_surrogate(), None);
+ assert_eq!(c(0xD800).to_lead_surrogate(), Some(0xD800));
+ assert_eq!(c(0xDBFF).to_lead_surrogate(), Some(0xDBFF));
+ assert_eq!(c(0xDC00).to_lead_surrogate(), None);
+ assert_eq!(c(0xDFFF).to_lead_surrogate(), None);
+ assert_eq!(c(0x1F4A9).to_lead_surrogate(), None);
+ assert_eq!(c(0x10FFFF).to_lead_surrogate(), None);
+}
+
+#[test]
+fn code_point_to_trail_surrogate() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0).to_trail_surrogate(), None);
+ assert_eq!(c(0xE9).to_trail_surrogate(), None);
+ assert_eq!(c(0xD800).to_trail_surrogate(), None);
+ assert_eq!(c(0xDBFF).to_trail_surrogate(), None);
+ assert_eq!(c(0xDC00).to_trail_surrogate(), Some(0xDC00));
+ assert_eq!(c(0xDFFF).to_trail_surrogate(), Some(0xDFFF));
+ assert_eq!(c(0x1F4A9).to_trail_surrogate(), None);
+ assert_eq!(c(0x10FFFF).to_trail_surrogate(), None);
+}
+
+#[test]
fn code_point_from_char() {
assert_eq!(CodePoint::from_char('a').to_u32(), 0x61);
assert_eq!(CodePoint::from_char('šŸ’©').to_u32(), 0x1F4A9);
@@ -70,35 +100,66 @@ fn wtf8buf_from_string() {
#[test]
fn wtf8buf_from_wide() {
- assert_eq!(Wtf8Buf::from_wide(&[]).bytes, b"");
- assert_eq!(
- Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes,
- b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"
- );
+ let buf = Wtf8Buf::from_wide(&[]);
+ assert_eq!(buf.bytes, b"");
+ assert!(buf.is_known_utf8);
+
+ let buf = Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xDCA9]);
+ assert_eq!(buf.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert!(buf.is_known_utf8);
+
+ let buf = Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]);
+ assert_eq!(buf.bytes, b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9");
+ assert!(!buf.is_known_utf8);
+
+ let buf = Wtf8Buf::from_wide(&[0xD800]);
+ assert_eq!(buf.bytes, b"\xED\xA0\x80");
+ assert!(!buf.is_known_utf8);
+
+ let buf = Wtf8Buf::from_wide(&[0xDBFF]);
+ assert_eq!(buf.bytes, b"\xED\xAF\xBF");
+ assert!(!buf.is_known_utf8);
+
+ let buf = Wtf8Buf::from_wide(&[0xDC00]);
+ assert_eq!(buf.bytes, b"\xED\xB0\x80");
+ assert!(!buf.is_known_utf8);
+
+ let buf = Wtf8Buf::from_wide(&[0xDFFF]);
+ assert_eq!(buf.bytes, b"\xED\xBF\xBF");
+ assert!(!buf.is_known_utf8);
}
#[test]
fn wtf8buf_push_str() {
let mut string = Wtf8Buf::new();
assert_eq!(string.bytes, b"");
+ assert!(string.is_known_utf8);
+
string.push_str("aĆ© šŸ’©");
assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert!(string.is_known_utf8);
}
#[test]
fn wtf8buf_push_char() {
let mut string = Wtf8Buf::from_str("aƩ ");
assert_eq!(string.bytes, b"a\xC3\xA9 ");
+ assert!(string.is_known_utf8);
+
string.push_char('šŸ’©');
assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert!(string.is_known_utf8);
}
#[test]
fn wtf8buf_push() {
let mut string = Wtf8Buf::from_str("aƩ ");
assert_eq!(string.bytes, b"a\xC3\xA9 ");
+ assert!(string.is_known_utf8);
+
string.push(CodePoint::from_char('šŸ’©'));
assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert!(string.is_known_utf8);
fn c(value: u32) -> CodePoint {
CodePoint::from_u32(value).unwrap()
@@ -106,37 +167,46 @@ fn wtf8buf_push() {
let mut string = Wtf8Buf::new();
string.push(c(0xD83D)); // lead
+ assert!(!string.is_known_utf8);
string.push(c(0xDCA9)); // trail
assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
let mut string = Wtf8Buf::new();
string.push(c(0xD83D)); // lead
+ assert!(!string.is_known_utf8);
string.push(c(0x20)); // not surrogate
string.push(c(0xDCA9)); // trail
assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
let mut string = Wtf8Buf::new();
string.push(c(0xD800)); // lead
+ assert!(!string.is_known_utf8);
string.push(c(0xDBFF)); // lead
assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
let mut string = Wtf8Buf::new();
string.push(c(0xD800)); // lead
+ assert!(!string.is_known_utf8);
string.push(c(0xE000)); // not surrogate
assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
let mut string = Wtf8Buf::new();
string.push(c(0xD7FF)); // not surrogate
+ assert!(string.is_known_utf8);
string.push(c(0xDC00)); // trail
+ assert!(!string.is_known_utf8);
assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
let mut string = Wtf8Buf::new();
string.push(c(0x61)); // not surrogate, < 3 bytes
+ assert!(string.is_known_utf8);
string.push(c(0xDC00)); // trail
+ assert!(!string.is_known_utf8);
assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
let mut string = Wtf8Buf::new();
string.push(c(0xDC00)); // trail
+ assert!(!string.is_known_utf8);
assert_eq!(string.bytes, b"\xED\xB0\x80");
}
@@ -146,6 +216,7 @@ fn wtf8buf_push_wtf8() {
assert_eq!(string.bytes, b"a\xC3\xA9");
string.push_wtf8(Wtf8::from_str(" šŸ’©"));
assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert!(string.is_known_utf8);
fn w(v: &[u8]) -> &Wtf8 {
unsafe { Wtf8::from_bytes_unchecked(v) }
@@ -161,37 +232,68 @@ fn wtf8buf_push_wtf8() {
string.push_wtf8(w(b" ")); // not surrogate
string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+ assert!(!string.is_known_utf8);
let mut string = Wtf8Buf::new();
string.push_wtf8(w(b"\xED\xA0\x80")); // lead
string.push_wtf8(w(b"\xED\xAF\xBF")); // lead
assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+ assert!(!string.is_known_utf8);
let mut string = Wtf8Buf::new();
string.push_wtf8(w(b"\xED\xA0\x80")); // lead
string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate
assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
+ assert!(!string.is_known_utf8);
let mut string = Wtf8Buf::new();
string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate
string.push_wtf8(w(b"\xED\xB0\x80")); // trail
assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+ assert!(!string.is_known_utf8);
let mut string = Wtf8Buf::new();
string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes
string.push_wtf8(w(b"\xED\xB0\x80")); // trail
assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
+ assert!(!string.is_known_utf8);
let mut string = Wtf8Buf::new();
string.push_wtf8(w(b"\xED\xB0\x80")); // trail
assert_eq!(string.bytes, b"\xED\xB0\x80");
+ assert!(!string.is_known_utf8);
}
#[test]
fn wtf8buf_truncate() {
let mut string = Wtf8Buf::from_str("aƩ");
+ assert!(string.is_known_utf8);
+
+ string.truncate(3);
+ assert_eq!(string.bytes, b"a\xC3\xA9");
+ assert!(string.is_known_utf8);
+
string.truncate(1);
assert_eq!(string.bytes, b"a");
+ assert!(string.is_known_utf8);
+
+ string.truncate(0);
+ assert_eq!(string.bytes, b"");
+ assert!(string.is_known_utf8);
+}
+
+#[test]
+fn wtf8buf_truncate_around_non_bmp() {
+ let mut string = Wtf8Buf::from_str("šŸ’©");
+ assert!(string.is_known_utf8);
+
+ string.truncate(4);
+ assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9");
+ assert!(string.is_known_utf8);
+
+ string.truncate(0);
+ assert_eq!(string.bytes, b"");
+ assert!(string.is_known_utf8);
}
#[test]
@@ -209,10 +311,36 @@ fn wtf8buf_truncate_fail_longer() {
}
#[test]
+#[should_panic]
+fn wtf8buf_truncate_splitting_non_bmp3() {
+ let mut string = Wtf8Buf::from_str("šŸ’©");
+ assert!(string.is_known_utf8);
+ string.truncate(3);
+}
+
+#[test]
+#[should_panic]
+fn wtf8buf_truncate_splitting_non_bmp2() {
+ let mut string = Wtf8Buf::from_str("šŸ’©");
+ assert!(string.is_known_utf8);
+ string.truncate(2);
+}
+
+#[test]
+#[should_panic]
+fn wtf8buf_truncate_splitting_non_bmp1() {
+ let mut string = Wtf8Buf::from_str("šŸ’©");
+ assert!(string.is_known_utf8);
+ string.truncate(1);
+}
+
+#[test]
fn wtf8buf_into_string() {
let mut string = Wtf8Buf::from_str("aĆ© šŸ’©");
+ assert!(string.is_known_utf8);
assert_eq!(string.clone().into_string(), Ok(String::from("aĆ© šŸ’©")));
string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert!(!string.is_known_utf8);
assert_eq!(string.clone().into_string(), Err(string));
}
@@ -229,15 +357,33 @@ fn wtf8buf_from_iterator() {
fn f(values: &[u32]) -> Wtf8Buf {
values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>()
}
- assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert_eq!(
+ f(&[0x61, 0xE9, 0x20, 0x1F4A9]),
+ Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true }
+ );
assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
- assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
- assert_eq!(f(&[0xD800, 0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
- assert_eq!(f(&[0xD800, 0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
- assert_eq!(f(&[0xD7FF, 0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
- assert_eq!(f(&[0x61, 0xDC00]).bytes, b"\x61\xED\xB0\x80");
- assert_eq!(f(&[0xDC00]).bytes, b"\xED\xB0\x80");
+ assert_eq!(
+ f(&[0xD83D, 0x20, 0xDCA9]),
+ Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ f(&[0xD800, 0xDBFF]),
+ Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ f(&[0xD800, 0xE000]),
+ Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ f(&[0xD7FF, 0xDC00]),
+ Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ f(&[0x61, 0xDC00]),
+ Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(f(&[0xDC00]), Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false });
}
#[test]
@@ -251,15 +397,36 @@ fn wtf8buf_extend() {
string
}
- assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+ assert_eq!(
+ e(&[0x61, 0xE9], &[0x20, 0x1F4A9]),
+ Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true }
+ );
assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
- assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
- assert_eq!(e(&[0xD800], &[0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
- assert_eq!(e(&[0xD800], &[0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
- assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
- assert_eq!(e(&[0x61], &[0xDC00]).bytes, b"\x61\xED\xB0\x80");
- assert_eq!(e(&[], &[0xDC00]).bytes, b"\xED\xB0\x80");
+ assert_eq!(
+ e(&[0xD83D, 0x20], &[0xDCA9]),
+ Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ e(&[0xD800], &[0xDBFF]),
+ Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ e(&[0xD800], &[0xE000]),
+ Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ e(&[0xD7FF], &[0xDC00]),
+ Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ e(&[0x61], &[0xDC00]),
+ Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false }
+ );
+ assert_eq!(
+ e(&[], &[0xDC00]),
+ Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false }
+ );
}
#[test]
@@ -407,3 +574,93 @@ fn wtf8_encode_wide_size_hint() {
assert_eq!((0, Some(0)), iter.size_hint());
assert!(iter.next().is_none());
}
+
+#[test]
+fn wtf8_clone_into() {
+ let mut string = Wtf8Buf::new();
+ Wtf8::from_str("green").clone_into(&mut string);
+ assert_eq!(string.bytes, b"green");
+
+ let mut string = Wtf8Buf::from_str("green");
+ Wtf8::from_str("").clone_into(&mut string);
+ assert_eq!(string.bytes, b"");
+
+ let mut string = Wtf8Buf::from_str("red");
+ Wtf8::from_str("green").clone_into(&mut string);
+ assert_eq!(string.bytes, b"green");
+
+ let mut string = Wtf8Buf::from_str("green");
+ Wtf8::from_str("red").clone_into(&mut string);
+ assert_eq!(string.bytes, b"red");
+
+ let mut string = Wtf8Buf::from_str("green");
+ assert!(string.is_known_utf8);
+ unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").clone_into(&mut string) };
+ assert_eq!(string.bytes, b"\xED\xA0\x80");
+ assert!(!string.is_known_utf8);
+}
+
+#[test]
+fn wtf8_to_ascii_lowercase() {
+ let lowercase = Wtf8::from_str("").to_ascii_lowercase();
+ assert_eq!(lowercase.bytes, b"");
+
+ let lowercase = Wtf8::from_str("GrEeN gRaPeS! šŸ‡").to_ascii_lowercase();
+ assert_eq!(lowercase.bytes, b"green grapes! \xf0\x9f\x8d\x87");
+
+ let lowercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_ascii_lowercase() };
+ assert_eq!(lowercase.bytes, b"\xED\xA0\x80");
+ assert!(!lowercase.is_known_utf8);
+}
+
+#[test]
+fn wtf8_to_ascii_uppercase() {
+ let uppercase = Wtf8::from_str("").to_ascii_uppercase();
+ assert_eq!(uppercase.bytes, b"");
+
+ let uppercase = Wtf8::from_str("GrEeN gRaPeS! šŸ‡").to_ascii_uppercase();
+ assert_eq!(uppercase.bytes, b"GREEN GRAPES! \xf0\x9f\x8d\x87");
+
+ let uppercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_ascii_uppercase() };
+ assert_eq!(uppercase.bytes, b"\xED\xA0\x80");
+ assert!(!uppercase.is_known_utf8);
+}
+
+#[test]
+fn wtf8_make_ascii_lowercase() {
+ let mut lowercase = Wtf8Buf::from_str("");
+ lowercase.make_ascii_lowercase();
+ assert_eq!(lowercase.bytes, b"");
+
+ let mut lowercase = Wtf8Buf::from_str("GrEeN gRaPeS! šŸ‡");
+ lowercase.make_ascii_lowercase();
+ assert_eq!(lowercase.bytes, b"green grapes! \xf0\x9f\x8d\x87");
+
+ let mut lowercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() };
+ lowercase.make_ascii_lowercase();
+ assert_eq!(lowercase.bytes, b"\xED\xA0\x80");
+ assert!(!lowercase.is_known_utf8);
+}
+
+#[test]
+fn wtf8_make_ascii_uppercase() {
+ let mut uppercase = Wtf8Buf::from_str("");
+ uppercase.make_ascii_uppercase();
+ assert_eq!(uppercase.bytes, b"");
+
+ let mut uppercase = Wtf8Buf::from_str("GrEeN gRaPeS! šŸ‡");
+ uppercase.make_ascii_uppercase();
+ assert_eq!(uppercase.bytes, b"GREEN GRAPES! \xf0\x9f\x8d\x87");
+
+ let mut uppercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() };
+ uppercase.make_ascii_uppercase();
+ assert_eq!(uppercase.bytes, b"\xED\xA0\x80");
+ assert!(!uppercase.is_known_utf8);
+}
+
+#[test]
+fn wtf8_to_owned() {
+ let string = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() };
+ assert_eq!(string.bytes, b"\xED\xA0\x80");
+ assert!(!string.is_known_utf8);
+}
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index f4750cdf7..5d267891b 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -95,6 +95,7 @@ use crate::fmt;
/// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
/// [`JoinHandle::join`]: crate::thread::JoinHandle::join
/// [`with`]: LocalKey::with
+#[cfg_attr(not(test), rustc_diagnostic_item = "LocalKey")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct LocalKey<T: 'static> {
// This outer `LocalKey<T>` type is what's going to be stored in statics,
@@ -900,7 +901,7 @@ pub mod statik {
}
#[doc(hidden)]
-#[cfg(target_thread_local)]
+#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics"))),))]
pub mod fast {
use super::lazy::LazyKeyInner;
use crate::cell::Cell;
@@ -1036,6 +1037,10 @@ pub mod fast {
}
#[doc(hidden)]
+#[cfg(all(
+ not(target_thread_local),
+ not(all(target_family = "wasm", not(target_feature = "atomics"))),
+))]
pub mod os {
use super::lazy::LazyKeyInner;
use crate::cell::Cell;
@@ -1044,6 +1049,8 @@ pub mod os {
use crate::ptr;
use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
+ /// Use a regular global static to store this key; the state provided will then be
+ /// thread-local.
pub struct Key<T> {
// OS-TLS key that we'll use to key off.
os: OsStaticKey,
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 44c8a50fd..05023df1b 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -116,7 +116,7 @@
//! Threads are able to have associated names for identification purposes. By default, spawned
//! threads are unnamed. To specify a name for a thread, build the thread with [`Builder`] and pass
//! the desired thread name to [`Builder::name`]. To retrieve the thread name from within the
-//! thread, use [`Thread::name`]. A couple examples of where the name of a thread gets used:
+//! thread, use [`Thread::name`]. A couple of examples where the name of a thread gets used:
//!
//! * If a panic occurs in a named thread, the thread name will be printed in the panic message.
//! * The thread name is provided to the OS where applicable (e.g., `pthread_setname_np` in
@@ -150,6 +150,8 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
+// Under `test`, `__FastLocalKeyInner` seems unused.
+#![cfg_attr(test, allow(dead_code))]
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests;
@@ -160,7 +162,7 @@ use crate::ffi::{CStr, CString};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
-use crate::mem;
+use crate::mem::{self, forget};
use crate::num::NonZeroU64;
use crate::num::NonZeroUsize;
use crate::panic;
@@ -170,7 +172,6 @@ use crate::ptr::addr_of_mut;
use crate::str;
use crate::sync::Arc;
use crate::sys::thread as imp;
-use crate::sys_common::mutex;
use crate::sys_common::thread;
use crate::sys_common::thread_info;
use crate::sys_common::thread_parker::Parker;
@@ -193,22 +194,40 @@ pub use scoped::{scope, Scope, ScopedJoinHandle};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::local::{AccessError, LocalKey};
-// The types used by the thread_local! macro to access TLS keys. Note that there
-// are two types, the "OS" type and the "fast" type. The OS thread local key
-// type is accessed via platform-specific API calls and is slow, while the fast
+// Provide the type used by the thread_local! macro to access TLS keys. This
+// needs to be kept in sync with the macro itself (in `local.rs`).
+// There are three types: "static", "fast", "OS". The "OS" thread local key
+// type is accessed via platform-specific API calls and is slow, while the "fast"
// key type is accessed via code generated via LLVM, where TLS keys are set up
-// by the elf linker. Note that the OS TLS type is always available: on macOS
-// the standard library is compiled with support for older platform versions
-// where fast TLS was not available; end-user code is compiled with fast TLS
-// where available, but both are needed.
+// by the elf linker. "static" is for single-threaded platforms where a global
+// static is sufficient.
#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(target_thread_local)]
+#[cfg(not(test))]
+#[cfg(all(
+ target_thread_local,
+ not(all(target_family = "wasm", not(target_feature = "atomics"))),
+))]
#[doc(hidden)]
pub use self::local::fast::Key as __FastLocalKeyInner;
+
+// when building for tests, use real std's type
+#[unstable(feature = "libstd_thread_internals", issue = "none")]
+#[cfg(test)]
+#[cfg(all(
+ target_thread_local,
+ not(all(target_family = "wasm", not(target_feature = "atomics"))),
+))]
+pub use realstd::thread::__FastLocalKeyInner;
+
#[unstable(feature = "libstd_thread_internals", issue = "none")]
+#[cfg(all(
+ not(target_thread_local),
+ not(all(target_family = "wasm", not(target_feature = "atomics"))),
+))]
#[doc(hidden)]
pub use self::local::os::Key as __OsLocalKeyInner;
+
#[unstable(feature = "libstd_thread_internals", issue = "none")]
#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
#[doc(hidden)]
@@ -490,6 +509,31 @@ impl Builder {
let output_capture = crate::io::set_output_capture(None);
crate::io::set_output_capture(output_capture.clone());
+ // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*.
+ // See <https://github.com/rust-lang/rust/issues/101983> for more details.
+ // To prevent leaks we use a wrapper that drops its contents.
+ #[repr(transparent)]
+ struct MaybeDangling<T>(mem::MaybeUninit<T>);
+ impl<T> MaybeDangling<T> {
+ fn new(x: T) -> Self {
+ MaybeDangling(mem::MaybeUninit::new(x))
+ }
+ fn into_inner(self) -> T {
+ // SAFETY: we are always initiailized.
+ let ret = unsafe { self.0.assume_init_read() };
+ // Make sure we don't drop.
+ mem::forget(self);
+ ret
+ }
+ }
+ impl<T> Drop for MaybeDangling<T> {
+ fn drop(&mut self) {
+ // SAFETY: we are always initiailized.
+ unsafe { self.0.assume_init_drop() };
+ }
+ }
+
+ let f = MaybeDangling::new(f);
let main = move || {
if let Some(name) = their_thread.cname() {
imp::Thread::set_name(name);
@@ -497,6 +541,8 @@ impl Builder {
crate::io::set_output_capture(output_capture);
+ // SAFETY: we constructed `f` initialized.
+ let f = f.into_inner();
// SAFETY: the stack guard passed is the one for the current thread.
// This means the current thread's stack and the new thread's stack
// are properly set and protected from each other.
@@ -509,6 +555,12 @@ impl Builder {
// same `JoinInner` as this closure meaning the mutation will be
// safe (not modify it and affect a value far away).
unsafe { *their_packet.result.get() = Some(try_result) };
+ // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that
+ // will call `decrement_num_running_threads` and therefore signal that this thread is
+ // done.
+ drop(their_packet);
+ // Here, the lifetime `'a` and even `'scope` can end. `main` keeps running for a bit
+ // after that before returning itself.
};
if let Some(scope_data) = &my_packet.scope {
@@ -770,6 +822,8 @@ pub fn panicking() -> bool {
panicking::panicking()
}
+/// Use [`sleep`].
+///
/// Puts the current thread to sleep for at least the specified amount of time.
///
/// The thread may sleep longer than the duration specified due to scheduling
@@ -840,10 +894,22 @@ pub fn sleep(dur: Duration) {
imp::Thread::sleep(dur)
}
+/// Used to ensure that `park` and `park_timeout` do not unwind, as that can
+/// cause undefined behaviour if not handled correctly (see #102398 for context).
+struct PanicGuard;
+
+impl Drop for PanicGuard {
+ fn drop(&mut self) {
+ rtabort!("an irrecoverable error occurred while synchronizing threads")
+ }
+}
+
/// Blocks unless or until the current thread's token is made available.
///
/// A call to `park` does not guarantee that the thread will remain parked
-/// forever, and callers should be prepared for this possibility.
+/// forever, and callers should be prepared for this possibility. However,
+/// it is guaranteed that this function will not panic (it may abort the
+/// process if the implementation encounters some rare errors).
///
/// # park and unpark
///
@@ -928,10 +994,13 @@ pub fn sleep(dur: Duration) {
/// [`thread::park_timeout`]: park_timeout
#[stable(feature = "rust1", since = "1.0.0")]
pub fn park() {
+ let guard = PanicGuard;
// SAFETY: park_timeout is called on the parker owned by this thread.
unsafe {
current().inner.as_ref().parker().park();
}
+ // No panic occurred, do not abort.
+ forget(guard);
}
/// Use [`park_timeout`].
@@ -992,10 +1061,13 @@ pub fn park_timeout_ms(ms: u32) {
/// ```
#[stable(feature = "park_timeout", since = "1.4.0")]
pub fn park_timeout(dur: Duration) {
+ let guard = PanicGuard;
// SAFETY: park_timeout is called on the parker owned by this thread.
unsafe {
current().inner.as_ref().parker().park_timeout(dur);
}
+ // No panic occurred, do not abort.
+ forget(guard);
}
////////////////////////////////////////////////////////////////////////////////
@@ -1033,24 +1105,45 @@ pub struct ThreadId(NonZeroU64);
impl ThreadId {
// Generate a new unique thread ID.
fn new() -> ThreadId {
- // It is UB to attempt to acquire this mutex reentrantly!
- static GUARD: mutex::StaticMutex = mutex::StaticMutex::new();
- static mut COUNTER: u64 = 1;
-
- unsafe {
- let guard = GUARD.lock();
-
- // If we somehow use up all our bits, panic so that we're not
- // covering up subtle bugs of IDs being reused.
- if COUNTER == u64::MAX {
- drop(guard); // in case the panic handler ends up calling `ThreadId::new()`, avoid reentrant lock acquire.
- panic!("failed to generate unique thread ID: bitspace exhausted");
- }
-
- let id = COUNTER;
- COUNTER += 1;
+ #[cold]
+ fn exhausted() -> ! {
+ panic!("failed to generate unique thread ID: bitspace exhausted")
+ }
- ThreadId(NonZeroU64::new(id).unwrap())
+ cfg_if::cfg_if! {
+ if #[cfg(target_has_atomic = "64")] {
+ use crate::sync::atomic::{AtomicU64, Ordering::Relaxed};
+
+ static COUNTER: AtomicU64 = AtomicU64::new(0);
+
+ let mut last = COUNTER.load(Relaxed);
+ loop {
+ let Some(id) = last.checked_add(1) else {
+ exhausted();
+ };
+
+ match COUNTER.compare_exchange_weak(last, id, Relaxed, Relaxed) {
+ Ok(_) => return ThreadId(NonZeroU64::new(id).unwrap()),
+ Err(id) => last = id,
+ }
+ }
+ } else {
+ use crate::sync::{Mutex, PoisonError};
+
+ static COUNTER: Mutex<u64> = Mutex::new(0);
+
+ let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner);
+ let Some(id) = counter.checked_add(1) else {
+ // in case the panic handler ends up calling `ThreadId::new()`,
+ // avoid reentrant lock acquire.
+ drop(counter);
+ exhausted();
+ };
+
+ *counter = id;
+ drop(counter);
+ ThreadId(NonZeroU64::new(id).unwrap())
+ }
}
}
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
index ec68b5291..6c9ce6fa0 100644
--- a/library/std/src/thread/tests.rs
+++ b/library/std/src/thread/tests.rs
@@ -37,6 +37,37 @@ fn test_named_thread() {
.unwrap();
}
+#[cfg(any(
+ // Note: musl didn't add pthread_getname_np until 1.2.3
+ all(target_os = "linux", target_env = "gnu"),
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos"
+))]
+#[test]
+fn test_named_thread_truncation() {
+ use crate::ffi::CStr;
+
+ let long_name = crate::iter::once("test_named_thread_truncation")
+ .chain(crate::iter::repeat(" yada").take(100))
+ .collect::<String>();
+
+ let result = Builder::new().name(long_name.clone()).spawn(move || {
+ // Rust remembers the full thread name itself.
+ assert_eq!(thread::current().name(), Some(long_name.as_str()));
+
+ // But the system is limited -- make sure we successfully set a truncation.
+ let mut buf = vec![0u8; long_name.len() + 1];
+ unsafe {
+ libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len());
+ }
+ let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+ assert!(cstr.to_bytes().len() > 0);
+ assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
+ });
+ result.unwrap().join().unwrap();
+}
+
#[test]
#[should_panic]
fn test_invalid_named_thread() {
@@ -245,6 +276,28 @@ fn test_try_panic_any_message_unit_struct() {
}
#[test]
+fn test_park_unpark_before() {
+ for _ in 0..10 {
+ thread::current().unpark();
+ thread::park();
+ }
+}
+
+#[test]
+fn test_park_unpark_called_other_thread() {
+ for _ in 0..10 {
+ let th = thread::current();
+
+ let _guard = thread::spawn(move || {
+ super::sleep(Duration::from_millis(50));
+ th.unpark();
+ });
+
+ thread::park();
+ }
+}
+
+#[test]
fn test_park_timeout_unpark_before() {
for _ in 0..10 {
thread::current().unpark();
@@ -329,3 +382,22 @@ fn test_scoped_threads_nll() {
let x = 42_u8;
foo(&x);
}
+
+// Regression test for https://github.com/rust-lang/rust/issues/98498.
+#[test]
+#[cfg(miri)] // relies on Miri's data race detector
+fn scope_join_race() {
+ for _ in 0..100 {
+ let a_bool = AtomicBool::new(false);
+
+ thread::scope(|s| {
+ for _ in 0..5 {
+ s.spawn(|| a_bool.load(Ordering::Relaxed));
+ }
+
+ for _ in 0..5 {
+ s.spawn(|| a_bool.load(Ordering::Relaxed));
+ }
+ });
+ }
+}
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 759a59e1f..ecd06ebf7 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -43,8 +43,8 @@ use crate::sys_common::{FromInner, IntoInner};
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;
-#[unstable(feature = "duration_checked_float", issue = "83400")]
-pub use core::time::FromFloatSecsError;
+#[stable(feature = "duration_checked_float", since = "1.66.0")]
+pub use core::time::TryFromFloatSecsError;
/// A measurement of a monotonically nondecreasing clock.
/// Opaque and useful only with [`Duration`].
@@ -356,7 +356,7 @@ impl Instant {
///
/// # Panics
///
- /// Previous rust versions panicked when self was earlier than the current time. Currently this
+ /// Previous rust versions panicked when the current time was earlier than self. Currently this
/// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
/// See [Monotonicity].
///
diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs
index d710a5744..6229556c8 100644
--- a/library/std/src/time/tests.rs
+++ b/library/std/src/time/tests.rs
@@ -31,7 +31,8 @@ fn instant_monotonic_concurrent() -> crate::thread::Result<()> {
.map(|_| {
crate::thread::spawn(|| {
let mut old = Instant::now();
- for _ in 0..5_000_000 {
+ let count = if cfg!(miri) { 1_000 } else { 5_000_000 };
+ for _ in 0..count {
let new = Instant::now();
assert!(new >= old);
old = new;