summaryrefslogtreecommitdiffstats
path: root/library/std
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:06:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:06:37 +0000
commit246f239d9f40f633160f0c18f87a20922d4e77bb (patch)
tree5a88572663584b3d4d28e5a20e10abab1be40884 /library/std
parentReleasing progress-linux version 1.64.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-246f239d9f40f633160f0c18f87a20922d4e77bb.tar.xz
rustc-246f239d9f40f633160f0c18f87a20922d4e77bb.zip
Merging debian version 1.65.0+dfsg1-2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std')
-rw-r--r--library/std/Cargo.toml4
-rw-r--r--library/std/src/alloc.rs5
-rw-r--r--library/std/src/backtrace.rs21
-rw-r--r--library/std/src/collections/hash/map.rs11
-rw-r--r--library/std/src/collections/hash/map/tests.rs24
-rw-r--r--library/std/src/collections/hash/set.rs2
-rw-r--r--library/std/src/error.rs379
-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.rs41
-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.rs11
-rw-r--r--library/std/src/io/error/repr_bitpacked.rs4
-rw-r--r--library/std/src/io/impls.rs22
-rw-r--r--library/std/src/io/mod.rs93
-rw-r--r--library/std/src/io/readbuf.rs307
-rw-r--r--library/std/src/io/readbuf/tests.rs220
-rw-r--r--library/std/src/io/stdio.rs51
-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.rs6
-rw-r--r--library/std/src/lib.rs34
-rw-r--r--library/std/src/macros.rs36
-rw-r--r--library/std/src/net/display_buffer.rs40
-rw-r--r--library/std/src/net/ip_addr.rs (renamed from library/std/src/net/ip.rs)257
-rw-r--r--library/std/src/net/ip_addr/tests.rs (renamed from library/std/src/net/ip/tests.rs)98
-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.rs (renamed from library/std/src/net/addr.rs)58
-rw-r--r--library/std/src/net/socket_addr/tests.rs (renamed from library/std/src/net/addr/tests.rs)69
-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/owned.rs3
-rw-r--r--library/std/src/os/fd/raw.rs8
-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.rs3
-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/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/fd.rs3
-rw-r--r--library/std/src/os/wasi/io/mod.rs6
-rw-r--r--library/std/src/os/wasi/io/raw.rs18
-rw-r--r--library/std/src/panic.rs33
-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.rs192
-rw-r--r--library/std/src/personality/dwarf/mod.rs73
-rw-r--r--library/std/src/personality/dwarf/tests.rs19
-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.rs72
-rw-r--r--library/std/src/process.rs10
-rw-r--r--library/std/src/rt.rs31
-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/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_lock.rs55
-rw-r--r--library/std/src/sync/rwlock.rs2
-rw-r--r--library/std/src/sync/rwlock/tests.rs2
-rw-r--r--library/std/src/sys/hermit/condvar.rs90
-rw-r--r--library/std/src/sys/hermit/fs.rs18
-rw-r--r--library/std/src/sys/hermit/futex.rs39
-rw-r--r--library/std/src/sys/hermit/mod.rs17
-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/sgx/abi/thread.rs8
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/alloc.rs165
-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/solid/fs.rs24
-rw-r--r--library/std/src/sys/solid/mod.rs2
-rw-r--r--library/std/src/sys/unix/fd.rs11
-rw-r--r--library/std/src/sys/unix/fs.rs24
-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/pthread_condvar.rs2
-rw-r--r--library/std/src/sys/unix/locks/pthread_mutex.rs2
-rw-r--r--library/std/src/sys/unix/mod.rs42
-rw-r--r--library/std/src/sys/unix/net.rs22
-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.rs59
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs24
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs4
-rw-r--r--library/std/src/sys/unix/rand.rs18
-rw-r--r--library/std/src/sys/unix/thread.rs32
-rw-r--r--library/std/src/sys/unix/thread_parker/mod.rs21
-rw-r--r--library/std/src/sys/unix/thread_parker/netbsd.rs113
-rw-r--r--library/std/src/sys/unix/thread_parker/pthread.rs (renamed from library/std/src/sys/unix/thread_parker.rs)14
-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/locks/mutex.rs3
-rw-r--r--library/std/src/sys/unsupported/process.rs3
-rw-r--r--library/std/src/sys/wasi/fs.rs6
-rw-r--r--library/std/src/sys/wasi/stdio.rs23
-rw-r--r--library/std/src/sys/windows/alloc.rs5
-rw-r--r--library/std/src/sys/windows/c.rs35
-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.rs119
-rw-r--r--library/std/src/sys/windows/handle.rs12
-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/rand.rs121
-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_parker.rs22
-rw-r--r--library/std/src/sys_common/net.rs8
-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/thread_local_key.rs2
-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.rs3
-rw-r--r--library/std/src/thread/mod.rs85
-rw-r--r--library/std/src/thread/tests.rs50
-rw-r--r--library/std/src/time/tests.rs3
145 files changed, 4005 insertions, 2023 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 229e546e0..324ecc804 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -39,10 +39,10 @@ rand = "0.7"
dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] }
[target.x86_64-fortanix-unknown-sgx.dependencies]
-fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] }
+fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'] }
[target.'cfg(target_os = "hermit")'.dependencies]
-hermit-abi = { version = "0.2.0", features = ['rustc-dep-of-std'] }
+hermit-abi = { version = "0.2.6", features = ['rustc-dep-of-std'] }
[target.wasm32-wasi.dependencies]
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
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..5cf6ec817 100644
--- a/library/std/src/backtrace.rs
+++ b/library/std/src/backtrace.rs
@@ -9,12 +9,6 @@
//! 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
@@ -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 }
}
@@ -356,6 +361,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 +381,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 {
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index db811343f..9845d1faf 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -9,6 +9,8 @@ use crate::borrow::Borrow;
use crate::cell::Cell;
use crate::collections::TryReserveError;
use crate::collections::TryReserveErrorKind;
+#[cfg(not(bootstrap))]
+use crate::error::Error;
use crate::fmt::{self, Debug};
#[allow(deprecated)]
use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
@@ -2158,6 +2160,15 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> {
}
}
+#[cfg(not(bootstrap))]
+#[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..cb3032719 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();
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index abff82788..5b6a415fa 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
///
diff --git a/library/std/src/error.rs b/library/std/src/error.rs
index 722df119d..e45059595 100644
--- a/library/std/src/error.rs
+++ b/library/std/src/error.rs
@@ -1,175 +1,51 @@
-//! 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;
+#[cfg(bootstrap)]
use core::array;
+#[cfg(bootstrap)]
use core::convert::Infallible;
+#[cfg(bootstrap)]
use crate::alloc::{AllocError, LayoutError};
-use crate::any::{Demand, Provider, TypeId};
+#[cfg(bootstrap)]
+use crate::any::Demand;
+#[cfg(bootstrap)]
+use crate::any::{Provider, TypeId};
use crate::backtrace::Backtrace;
+#[cfg(bootstrap)]
use crate::borrow::Cow;
+#[cfg(bootstrap)]
use crate::cell;
+#[cfg(bootstrap)]
use crate::char;
-use crate::fmt::{self, Debug, Display, Write};
+#[cfg(bootstrap)]
+use crate::fmt::Debug;
+#[cfg(bootstrap)]
+use crate::fmt::Display;
+use crate::fmt::{self, Write};
+#[cfg(bootstrap)]
use crate::io;
+#[cfg(bootstrap)]
use crate::mem::transmute;
+#[cfg(bootstrap)]
use crate::num;
+#[cfg(bootstrap)]
use crate::str;
+#[cfg(bootstrap)]
use crate::string;
+#[cfg(bootstrap)]
use crate::sync::Arc;
+#[cfg(bootstrap)]
use crate::time;
+#[cfg(not(bootstrap))]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use core::error::Error;
+
/// `Error` is a trait representing the basic expectations for error values,
/// i.e., values of type `E` in [`Result<T, E>`].
///
@@ -182,14 +58,15 @@ use crate::time;
/// assert_eq!(err.to_string(), "invalid digit found in string");
/// ```
///
-/// Errors may provide cause chain information. [`Error::source()`] is generally
+/// Errors may provide cause 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.
+/// implementation for debugging.
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
+#[cfg(bootstrap)]
pub trait Error: Debug + Display {
/// The lower-level source of this error, if any.
///
@@ -333,8 +210,8 @@ pub trait Error: Debug + Display {
/// }
///
/// impl std::error::Error for Error {
- /// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
- /// req
+ /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ /// demand
/// .provide_ref::<MyBacktrace>(&self.backtrace)
/// .provide_ref::<dyn std::error::Error + 'static>(&self.source);
/// }
@@ -352,13 +229,14 @@ pub trait Error: Debug + Display {
/// ```
#[unstable(feature = "error_generic_member_access", issue = "99301")]
#[allow(unused_variables)]
- fn provide<'a>(&'a self, req: &mut Demand<'a>) {}
+ fn provide<'a>(&'a self, demand: &mut Demand<'a>) {}
}
+#[cfg(bootstrap)]
#[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)
+ fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ self.provide(demand)
}
}
@@ -370,6 +248,7 @@ mod private {
pub struct Internal;
}
+#[cfg(bootstrap)]
#[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`].
@@ -402,6 +281,7 @@ impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
}
}
+#[cfg(bootstrap)]
#[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
@@ -440,6 +320,7 @@ impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync +
}
}
+#[cfg(bootstrap)]
#[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`].
@@ -483,6 +364,7 @@ impl From<String> for Box<dyn Error + Send + Sync> {
}
}
+#[cfg(bootstrap)]
#[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`].
@@ -504,6 +386,7 @@ impl From<String> for Box<dyn Error> {
}
}
+#[cfg(bootstrap)]
#[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`].
@@ -527,6 +410,7 @@ impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
}
}
+#[cfg(bootstrap)]
#[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`].
@@ -548,6 +432,7 @@ impl From<&str> for Box<dyn Error> {
}
}
+#[cfg(bootstrap)]
#[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`].
@@ -569,6 +454,7 @@ impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> {
}
}
+#[cfg(bootstrap)]
#[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`].
@@ -589,9 +475,11 @@ impl<'a> From<Cow<'a, str>> for Box<dyn Error> {
}
}
+#[cfg(bootstrap)]
#[unstable(feature = "never_type", issue = "35121")]
impl Error for ! {}
+#[cfg(bootstrap)]
#[unstable(
feature = "allocator_api",
reason = "the precise API and guarantees it provides may be tweaked.",
@@ -599,9 +487,11 @@ impl Error for ! {}
)]
impl Error for AllocError {}
+#[cfg(bootstrap)]
#[stable(feature = "alloc_layout", since = "1.28.0")]
impl Error for LayoutError {}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for str::ParseBoolError {
#[allow(deprecated)]
@@ -610,6 +500,7 @@ impl Error for str::ParseBoolError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for str::Utf8Error {
#[allow(deprecated)]
@@ -618,6 +509,7 @@ impl Error for str::Utf8Error {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for num::ParseIntError {
#[allow(deprecated)]
@@ -626,6 +518,7 @@ impl Error for num::ParseIntError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for num::TryFromIntError {
#[allow(deprecated)]
@@ -634,6 +527,7 @@ impl Error for num::TryFromIntError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for array::TryFromSliceError {
#[allow(deprecated)]
@@ -642,6 +536,7 @@ impl Error for array::TryFromSliceError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for num::ParseFloatError {
#[allow(deprecated)]
@@ -650,6 +545,7 @@ impl Error for num::ParseFloatError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for string::FromUtf8Error {
#[allow(deprecated)]
@@ -658,6 +554,7 @@ impl Error for string::FromUtf8Error {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for string::FromUtf16Error {
#[allow(deprecated)]
@@ -666,6 +563,7 @@ impl Error for string::FromUtf16Error {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "str_parse_error2", since = "1.8.0")]
impl Error for Infallible {
fn description(&self) -> &str {
@@ -673,6 +571,7 @@ impl Error for Infallible {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "decode_utf16", since = "1.9.0")]
impl Error for char::DecodeUtf16Error {
#[allow(deprecated)]
@@ -681,9 +580,11 @@ impl Error for char::DecodeUtf16Error {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "u8_from_char", since = "1.59.0")]
impl Error for char::TryFromCharError {}
+#[cfg(bootstrap)]
#[unstable(feature = "map_try_insert", issue = "82766")]
impl<'a, K: Debug + Ord, V: Debug> Error
for crate::collections::btree_map::OccupiedError<'a, K, V>
@@ -694,6 +595,7 @@ impl<'a, K: Debug + Ord, V: Debug> Error
}
}
+#[cfg(bootstrap)]
#[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)]
@@ -702,6 +604,7 @@ impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedErr
}
}
+#[cfg(bootstrap)]
#[stable(feature = "box_error", since = "1.8.0")]
impl<T: Error> Error for Box<T> {
#[allow(deprecated, deprecated_in_future)]
@@ -719,6 +622,7 @@ impl<T: Error> Error for Box<T> {
}
}
+#[cfg(bootstrap)]
#[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)> {
@@ -727,6 +631,7 @@ impl<T: ?Sized + crate::error::Error> crate::error::Error for crate::boxed::Thin
}
}
+#[cfg(bootstrap)]
#[stable(feature = "error_by_ref", since = "1.51.0")]
impl<'a, T: Error + ?Sized> Error for &'a T {
#[allow(deprecated, deprecated_in_future)]
@@ -743,11 +648,12 @@ impl<'a, T: Error + ?Sized> Error for &'a T {
Error::source(&**self)
}
- fn provide<'b>(&'b self, req: &mut Demand<'b>) {
- Error::provide(&**self, req);
+ fn provide<'b>(&'b self, demand: &mut Demand<'b>) {
+ Error::provide(&**self, demand);
}
}
+#[cfg(bootstrap)]
#[stable(feature = "arc_error", since = "1.52.0")]
impl<T: Error + ?Sized> Error for Arc<T> {
#[allow(deprecated, deprecated_in_future)]
@@ -764,11 +670,12 @@ impl<T: Error + ?Sized> Error for Arc<T> {
Error::source(&**self)
}
- fn provide<'a>(&'a self, req: &mut Demand<'a>) {
- Error::provide(&**self, req);
+ fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+ Error::provide(&**self, demand);
}
}
+#[cfg(bootstrap)]
#[stable(feature = "fmt_error", since = "1.11.0")]
impl Error for fmt::Error {
#[allow(deprecated)]
@@ -777,6 +684,7 @@ impl Error for fmt::Error {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "try_borrow", since = "1.13.0")]
impl Error for cell::BorrowError {
#[allow(deprecated)]
@@ -785,6 +693,7 @@ impl Error for cell::BorrowError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "try_borrow", since = "1.13.0")]
impl Error for cell::BorrowMutError {
#[allow(deprecated)]
@@ -793,6 +702,7 @@ impl Error for cell::BorrowMutError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for char::CharTryFromError {
#[allow(deprecated)]
@@ -801,6 +711,7 @@ impl Error for char::CharTryFromError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "char_from_str", since = "1.20.0")]
impl Error for char::ParseCharError {
#[allow(deprecated)]
@@ -809,12 +720,15 @@ impl Error for char::ParseCharError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "try_reserve", since = "1.57.0")]
impl Error for alloc::collections::TryReserveError {}
+#[cfg(bootstrap)]
#[unstable(feature = "duration_checked_float", issue = "83400")]
impl Error for time::FromFloatSecsError {}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for alloc::ffi::NulError {
#[allow(deprecated)]
@@ -823,6 +737,7 @@ impl Error for alloc::ffi::NulError {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl From<alloc::ffi::NulError> for io::Error {
/// Converts a [`alloc::ffi::NulError`] into a [`io::Error`].
@@ -831,6 +746,7 @@ impl From<alloc::ffi::NulError> for io::Error {
}
}
+#[cfg(bootstrap)]
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
impl Error for core::ffi::FromBytesWithNulError {
#[allow(deprecated)]
@@ -839,12 +755,15 @@ impl Error for core::ffi::FromBytesWithNulError {
}
}
+#[cfg(bootstrap)]
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
impl Error for core::ffi::FromBytesUntilNulError {}
+#[cfg(bootstrap)]
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
impl Error for alloc::ffi::FromVecWithNulError {}
+#[cfg(bootstrap)]
#[stable(feature = "cstring_into", since = "1.7.0")]
impl Error for alloc::ffi::IntoStringError {
#[allow(deprecated)]
@@ -857,6 +776,7 @@ impl Error for alloc::ffi::IntoStringError {
}
}
+#[cfg(bootstrap)]
impl<'a> dyn Error + 'a {
/// Request a reference of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
@@ -872,6 +792,7 @@ impl<'a> dyn Error + 'a {
}
// Copied from `any.rs`.
+#[cfg(bootstrap)]
impl dyn Error + 'static {
/// Returns `true` if the inner type is the same as `T`.
#[stable(feature = "error_downcast", since = "1.3.0")]
@@ -912,6 +833,7 @@ impl dyn Error + 'static {
}
}
+#[cfg(bootstrap)]
impl dyn Error + 'static + Send {
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
@@ -947,6 +869,7 @@ impl dyn Error + 'static + Send {
}
}
+#[cfg(bootstrap)]
impl dyn Error + 'static + Send + Sync {
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
@@ -982,6 +905,7 @@ impl dyn Error + 'static + Send + Sync {
}
}
+#[cfg(bootstrap)]
impl dyn Error {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
@@ -1041,7 +965,7 @@ impl dyn Error {
/// // let err : Box<Error> = b.into(); // or
/// let err = &b as &(dyn Error);
///
- /// let mut iter = err.chain();
+ /// let mut iter = err.sources();
///
/// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
/// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
@@ -1050,8 +974,19 @@ impl dyn Error {
/// ```
#[unstable(feature = "error_iter", issue = "58520")]
#[inline]
- pub fn chain(&self) -> Chain<'_> {
- Chain { current: Some(self) }
+ pub fn sources(&self) -> Sources<'_> {
+ // You may think this method would be better in the Error trait, and you'd be right.
+ // Unfortunately that doesn't work, not because of the object safety rules but because we
+ // save a reference to self in Sources below as a trait object. If this method was
+ // declared in Error, then self would have the type &T where T is some concrete type which
+ // implements Error. We would need to coerce self to have type &dyn Error, but that requires
+ // that Self has a known size (i.e., Self: Sized). We can't put that bound on Error
+ // since that would forbid Error trait objects, and we can't put that bound on the method
+ // because that means the method can't be called on trait objects (we'd also need the
+ // 'static bound, but that isn't allowed because methods with bounds on Self other than
+ // Sized are not object-safe). Requiring an Unsize bound is not backwards compatible.
+
+ Sources { current: Some(self) }
}
}
@@ -1061,12 +996,14 @@ impl dyn Error {
/// its sources, use `skip(1)`.
#[unstable(feature = "error_iter", issue = "58520")]
#[derive(Clone, Debug)]
-pub struct Chain<'a> {
+#[cfg(bootstrap)]
+pub struct Sources<'a> {
current: Option<&'a (dyn Error + 'static)>,
}
+#[cfg(bootstrap)]
#[unstable(feature = "error_iter", issue = "58520")]
-impl<'a> Iterator for Chain<'a> {
+impl<'a> Iterator for Sources<'a> {
type Item = &'a (dyn Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
@@ -1076,6 +1013,7 @@ impl<'a> Iterator for Chain<'a> {
}
}
+#[cfg(bootstrap)]
impl dyn Error + Send {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
@@ -1089,6 +1027,7 @@ impl dyn Error + Send {
}
}
+#[cfg(bootstrap)]
impl dyn Error + Send + Sync {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
@@ -1104,8 +1043,8 @@ impl dyn Error + Send + Sync {
/// 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 +1185,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 +1232,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 +1389,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 +1427,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,7 +1485,7 @@ where
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
@@ -1559,7 +1496,7 @@ where
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}")?;
@@ -1580,73 +1517,7 @@ where
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()))
- .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() {
+ for (ind, error) in cause.sources().enumerate() {
writeln!(f)?;
let mut indented = Indented { inner: f };
if multiple {
@@ -1683,17 +1554,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 +1563,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..c6c78dc39 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> {
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..29b09fcc5 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -76,6 +76,15 @@ impl fmt::Debug for Error {
}
}
+#[cfg(not(bootstrap))]
+#[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))]
@@ -564,6 +573,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/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..eeace2c43 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -278,7 +278,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 +362,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 +462,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(())
}
@@ -803,30 +807,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 +887,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 +903,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 +1037,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 +1045,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 +2589,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..b1a84095f 100644
--- a/library/std/src/io/readbuf.rs
+++ b/library/std/src/io/readbuf.rs
@@ -5,9 +5,10 @@ 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};
-/// 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,286 @@ 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.
- ///
- /// # Panics
+ /// Returns the number of bytes written to this cursor since it was created from a `BorrowedBuf`.
///
- /// 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.
- ///
- /// The number of initialized bytes is not changed.
+ /// Returns a mutable reference to the whole cursor.
///
- /// # 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.
+ /// Advance the cursor by asserting that `n` bytes have been filled.
///
- /// The number of initialized bytes is not changed.
+ /// 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.
///
- /// 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).
- ///
- /// # 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 {
+ for byte in self.uninit_mut() {
+ byte.write(0);
+ }
+ 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..2dc12a18a 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -8,7 +8,6 @@ use crate::io::prelude::*;
use crate::cell::{Cell, RefCell};
use crate::fmt;
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 +525,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 +602,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 +765,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 +838,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,12 +986,15 @@ 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,
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..a4b0522b0 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -1921,7 +1921,7 @@ mod type_keyword {}
/// and [proposal]s exist to use `unsafe {}` blocks inside such functions when
/// making `unsafe` operations.
///
-/// See the [Rustnomicon] and the [Reference] for more informations.
+/// See the [Rustnomicon] and the [Reference] for more information.
///
/// # Examples
///
@@ -2113,7 +2113,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 +2355,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/lib.rs b/library/std/src/lib.rs
index 20d25a608..bc4f1b27c 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -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)]
@@ -241,11 +252,12 @@
#![feature(dropck_eyepatch)]
#![feature(exhaustive_patterns)]
#![feature(intra_doc_pointers)]
-#![feature(label_break_value)]
+#![cfg_attr(bootstrap, feature(label_break_value))]
#![feature(lang_items)]
#![feature(let_chains)]
-#![feature(let_else)]
+#![cfg_attr(bootstrap, 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)]
@@ -269,10 +282,14 @@
#![feature(cstr_internals)]
#![feature(duration_checked_float)]
#![feature(duration_constants)]
+#![cfg_attr(not(bootstrap), feature(error_generic_member_access))]
+#![cfg_attr(not(bootstrap), feature(error_in_core))]
+#![cfg_attr(not(bootstrap), 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)]
@@ -284,6 +301,8 @@
#![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 +313,8 @@
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(strict_provenance)]
+#![feature(maybe_uninit_uninit_array)]
+#![feature(const_maybe_uninit_uninit_array)]
//
// Library features (alloc):
#![feature(alloc_layout_extra)]
@@ -576,6 +597,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/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_addr.rs
index 41ca9ba84..4f14fc280 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip_addr.rs
@@ -3,12 +3,13 @@
mod tests;
use crate::cmp::Ordering;
-use crate::fmt::{self, Write as FmtWrite};
-use crate::io::Write as IoWrite;
+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
@@ -28,6 +29,7 @@ use crate::sys_common::{FromInner, IntoInner};
/// 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 {
@@ -71,6 +73,7 @@ pub enum IpAddr {
/// 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],
@@ -153,6 +156,7 @@ pub struct Ipv4Addr {
/// 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],
@@ -618,25 +622,31 @@ impl Ipv4Addr {
matches!(self.octets(), [169, 254, ..])
}
- /// Returns [`true`] if the address appears to be globally routable.
- /// See [iana-ipv4-special-registry][ipv4-sr].
+ /// 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.
///
- /// The following return [`false`]:
+ /// Non-exhaustive list of notable addresses that are not globally reachable:
///
- /// - 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()`])
+ /// - 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))
///
- /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
+ /// 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
///
@@ -645,71 +655,61 @@ impl Ipv4Addr {
///
/// use std::net::Ipv4Addr;
///
- /// // private addresses are not global
+ /// // 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);
///
- /// // 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);
+ /// // Addresses in the shared address space (`100.64.0.0/10`)
+ /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false);
///
- /// // the loopback address is not global
- /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_global(), false);
+ /// // The loopback addresses (`127.0.0.0/8`)
+ /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false);
///
- /// // link local addresses are not global
+ /// // Link-local addresses (`169.254.0.0/16`)
/// 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
+ /// // 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);
///
- /// // 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 benchmarking (`198.18.0.0/15`)
+ /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false);
///
- /// // addresses reserved for future use are not global
+ /// // Reserved addresses (`240.0.0.0/4`)
/// 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);
+ /// // The broadcast address (`255.255.255.255`)
+ /// assert_eq!(Ipv4Addr::BROADCAST.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);
+ /// // 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 {
- // 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()
+ !(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_reserved()
- && !self.is_benchmarking()
- // Make sure the address is not in 0.0.0.0/8
- && self.octets()[0] != 0
+ ||(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
@@ -991,21 +991,19 @@ impl From<Ipv6Addr> for IpAddr {
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 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 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[..];
+ const LONGEST_IPV4_ADDR: &str = "255.255.255.255";
- // 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();
+ 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();
- // 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)
+ fmt.pad(buf.as_str())
}
}
}
@@ -1300,13 +1298,33 @@ impl Ipv6Addr {
u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets())
}
- /// Returns [`true`] if the address appears to be globally routable.
+ /// 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.
///
- /// The following return [`false`]:
+ /// Most IPv6 addresses are globally reachable;
+ /// unless they are specifically defined as *not* globally reachable.
///
- /// - the loopback address
- /// - link-local and unique local unicast addresses
- /// - interface-, link-, realm-, admin- and site-local multicast addresses
+ /// 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
///
@@ -1315,20 +1333,65 @@ impl Ipv6Addr {
///
/// 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);
+ /// // 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 {
- match self.multicast_scope() {
- Some(Ipv6MulticastScope::Global) => true,
- None => self.is_unicast_global(),
- _ => false,
- }
+ !(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`).
@@ -1525,6 +1588,7 @@ impl Ipv6Addr {
&& !self.is_unique_local()
&& !self.is_unspecified()
&& !self.is_documentation()
+ && !self.is_benchmarking()
}
/// Returns the address's multicast scope if the address is multicast.
@@ -1708,8 +1772,8 @@ impl Ipv6Addr {
#[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 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();
@@ -1780,22 +1844,13 @@ impl fmt::Display for Ipv6Addr {
}
}
} 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)
+ 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())
}
}
}
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip_addr/tests.rs
index c29509331..7c3430b2b 100644
--- a/library/std/src/net/ip/tests.rs
+++ b/library/std/src/net/ip_addr/tests.rs
@@ -321,15 +321,15 @@ fn ip_properties() {
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!("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", global | benchmarking);
+ check!("2001:2::ac32:23ff:21", benchmarking);
check!("102:304:506:708:90a:b0c:d0e:f10", global);
}
@@ -609,6 +609,60 @@ fn ipv6_properties() {
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!(
@@ -666,21 +720,37 @@ fn ipv6_properties() {
check!(
"ff01::",
&[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- multicast_interface_local
+ 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);
+ 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);
+ 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);
+ 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);
+ 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
+ multicast_organization_local | global
);
check!(
@@ -698,7 +768,7 @@ fn ipv6_properties() {
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
+ benchmarking
);
check!(
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/addr.rs b/library/std/src/net/socket_addr.rs
index 53fee952a..33b0dfa03 100644
--- a/library/std/src/net/addr.rs
+++ b/library/std/src/net/socket_addr.rs
@@ -2,9 +2,9 @@
mod tests;
use crate::cmp::Ordering;
-use crate::fmt;
+use crate::fmt::{self, Write};
use crate::hash;
-use crate::io::{self, Write};
+use crate::io;
use crate::iter;
use crate::mem;
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
@@ -15,6 +15,8 @@ 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
@@ -616,25 +618,18 @@ impl fmt::Debug for SocketAddr {
#[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 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 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)
+ 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())
}
}
}
@@ -649,35 +644,26 @@ impl fmt::Debug for SocketAddrV4 {
#[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 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 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[..];
+ 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_slice, "[{}]:{}", self.ip(), self.port()),
- scope_id => write!(buf_slice, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
+ 0 => write!(buf, "[{}]:{}", self.ip(), self.port()),
+ scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
}
- // Unwrap is fine because writing to a sufficiently-sized
- // buffer is infallible
+ // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail.
.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)
+ f.pad(buf.as_str())
}
}
}
diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/socket_addr/tests.rs
index 585a17451..15211f819 100644
--- a/library/std/src/net/addr/tests.rs
+++ b/library/std/src/net/socket_addr/tests.rs
@@ -52,6 +52,75 @@ fn to_socket_addr_string() {
}
#[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
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/owned.rs b/library/std/src/os/fd/owned.rs
index a463bc41d..71e33fb9e 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -104,7 +104,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) })
}
diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs
index 081915ed1..1b3d11042 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.
@@ -59,7 +59,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
@@ -103,7 +103,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.
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..18c64b510 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -148,3 +148,6 @@ pub mod vxworks;
#[cfg(any(unix, target_os = "wasi", doc))]
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/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/fd.rs b/library/std/src/os/wasi/io/fd.rs
index 930aca887..75703af6a 100644
--- a/library/std/src/os/wasi/io/fd.rs
+++ b/library/std/src/os/wasi/io/fd.rs
@@ -1,9 +1,10 @@
//! Owned and borrowed file descriptors.
-#![unstable(feature = "wasi_ext", issue = "71213")]
+#![stable(feature = "io_safety_wasi", since = "1.65.0")]
// Tests for this module
#[cfg(test)]
mod tests;
+#[stable(feature = "io_safety_wasi", since = "1.65.0")]
pub use crate::os::fd::owned::*;
diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs
index 6c884e2ea..4f5cfbf9a 100644
--- a/library/std/src/os/wasi/io/mod.rs
+++ b/library/std/src/os/wasi/io/mod.rs
@@ -1,12 +1,12 @@
//! WASI-specific extensions to general I/O primitives.
#![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "71213")]
+#![stable(feature = "io_safety_wasi", since = "1.65.0")]
mod fd;
mod raw;
-#[unstable(feature = "wasi_ext", issue = "71213")]
+#[stable(feature = "io_safety_wasi", since = "1.65.0")]
pub use fd::*;
-#[unstable(feature = "wasi_ext", issue = "71213")]
+#[stable(feature = "io_safety_wasi", since = "1.65.0")]
pub use raw::*;
diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs
index da3b36ada..4ac792ee8 100644
--- a/library/std/src/os/wasi/io/raw.rs
+++ b/library/std/src/os/wasi/io/raw.rs
@@ -1,20 +1,6 @@
//! WASI-specific extensions to general I/O primitives.
-#![unstable(feature = "wasi_ext", issue = "71213")]
+#![stable(feature = "io_safety_wasi", since = "1.65.0")]
-// NOTE: despite the fact that this module is unstable,
-// stable Rust had the capability to access the stable
-// re-exported items from os::fd::raw through this
-// unstable module.
-// In PR #95956 the stability checker was changed to check
-// all path segments of an item rather than just the last,
-// which caused the aforementioned stable usage to regress
-// (see issue #99502).
-// As a result, the items in os::fd::raw were given the
-// rustc_allowed_through_unstable_modules attribute.
-// No regression tests were added to ensure this property,
-// as CI is not configured to test wasm32-wasi.
-// If this module is stabilized,
-// you may want to remove those attributes
-// (assuming no other unstable modules need them).
+#[stable(feature = "io_safety_wasi", since = "1.65.0")]
pub use crate::os::fd::raw::*;
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/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/std/src/personality/dwarf/eh.rs b/library/std/src/personality/dwarf/eh.rs
new file mode 100644
index 000000000..8799137b7
--- /dev/null
+++ b/library/std/src/personality/dwarf/eh.rs
@@ -0,0 +1,192 @@
+//! Parsing of GCC-style Language-Specific Data Area (LSDA)
+//! For details see:
+//! * <https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html>
+//! * <https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf>
+//! * <https://www.airs.com/blog/archives/460>
+//! * <https://www.airs.com/blog/archives/464>
+//!
+//! A reference implementation may be found in the GCC source tree
+//! (`<root>/libgcc/unwind-c.c` as of this writing).
+
+#![allow(non_upper_case_globals)]
+#![allow(unused)]
+
+use super::DwarfReader;
+use core::mem;
+
+pub const DW_EH_PE_omit: u8 = 0xFF;
+pub const DW_EH_PE_absptr: u8 = 0x00;
+
+pub const DW_EH_PE_uleb128: u8 = 0x01;
+pub const DW_EH_PE_udata2: u8 = 0x02;
+pub const DW_EH_PE_udata4: u8 = 0x03;
+pub const DW_EH_PE_udata8: u8 = 0x04;
+pub const DW_EH_PE_sleb128: u8 = 0x09;
+pub const DW_EH_PE_sdata2: u8 = 0x0A;
+pub const DW_EH_PE_sdata4: u8 = 0x0B;
+pub const DW_EH_PE_sdata8: u8 = 0x0C;
+
+pub const DW_EH_PE_pcrel: u8 = 0x10;
+pub const DW_EH_PE_textrel: u8 = 0x20;
+pub const DW_EH_PE_datarel: u8 = 0x30;
+pub const DW_EH_PE_funcrel: u8 = 0x40;
+pub const DW_EH_PE_aligned: u8 = 0x50;
+
+pub const DW_EH_PE_indirect: u8 = 0x80;
+
+#[derive(Copy, Clone)]
+pub struct EHContext<'a> {
+ pub ip: usize, // Current instruction pointer
+ pub func_start: usize, // Address of the current function
+ pub get_text_start: &'a dyn Fn() -> usize, // Get address of the code section
+ pub get_data_start: &'a dyn Fn() -> usize, // Get address of the data section
+}
+
+pub enum EHAction {
+ None,
+ Cleanup(usize),
+ Catch(usize),
+ Terminate,
+}
+
+pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
+
+pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
+ if lsda.is_null() {
+ return Ok(EHAction::None);
+ }
+
+ let func_start = context.func_start;
+ let mut reader = DwarfReader::new(lsda);
+
+ let start_encoding = reader.read::<u8>();
+ // base address for landing pad offsets
+ let lpad_base = if start_encoding != DW_EH_PE_omit {
+ read_encoded_pointer(&mut reader, context, start_encoding)?
+ } else {
+ func_start
+ };
+
+ let ttype_encoding = reader.read::<u8>();
+ if ttype_encoding != DW_EH_PE_omit {
+ // Rust doesn't analyze exception types, so we don't care about the type table
+ reader.read_uleb128();
+ }
+
+ let call_site_encoding = reader.read::<u8>();
+ let call_site_table_length = reader.read_uleb128();
+ let action_table = reader.ptr.add(call_site_table_length as usize);
+ let ip = context.ip;
+
+ if !USING_SJLJ_EXCEPTIONS {
+ while reader.ptr < action_table {
+ let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
+ let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
+ let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
+ let cs_action = reader.read_uleb128();
+ // Callsite table is sorted by cs_start, so if we've passed the ip, we
+ // may stop searching.
+ if ip < func_start + cs_start {
+ break;
+ }
+ if ip < func_start + cs_start + cs_len {
+ if cs_lpad == 0 {
+ return Ok(EHAction::None);
+ } else {
+ let lpad = lpad_base + cs_lpad;
+ return Ok(interpret_cs_action(cs_action, lpad));
+ }
+ }
+ }
+ // 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)
+ } else {
+ // SjLj version:
+ // The "IP" is an index into the call-site table, with two exceptions:
+ // -1 means 'no-action', and 0 means 'terminate'.
+ match ip as isize {
+ -1 => return Ok(EHAction::None),
+ 0 => return Ok(EHAction::Terminate),
+ _ => (),
+ }
+ let mut idx = ip;
+ loop {
+ let cs_lpad = reader.read_uleb128();
+ let cs_action = reader.read_uleb128();
+ idx -= 1;
+ if idx == 0 {
+ // Can never have null landing pad for sjlj -- that would have
+ // been indicated by a -1 call site index.
+ let lpad = (cs_lpad + 1) as usize;
+ return Ok(interpret_cs_action(cs_action, lpad));
+ }
+ }
+ }
+}
+
+fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
+ if cs_action == 0 {
+ // If cs_action is 0 then this is a cleanup (Drop::drop). We run these
+ // for both Rust panics and foreign exceptions.
+ EHAction::Cleanup(lpad)
+ } else {
+ // Stop unwinding Rust panics at catch_unwind.
+ EHAction::Catch(lpad)
+ }
+}
+
+#[inline]
+fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
+ if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
+}
+
+unsafe fn read_encoded_pointer(
+ reader: &mut DwarfReader,
+ context: &EHContext<'_>,
+ encoding: u8,
+) -> Result<usize, ()> {
+ if encoding == DW_EH_PE_omit {
+ return Err(());
+ }
+
+ // DW_EH_PE_aligned implies it's an absolute pointer value
+ if encoding == DW_EH_PE_aligned {
+ reader.ptr = round_up(reader.ptr as usize, mem::size_of::<usize>())? as *const u8;
+ return Ok(reader.read::<usize>());
+ }
+
+ let mut result = match encoding & 0x0F {
+ DW_EH_PE_absptr => reader.read::<usize>(),
+ DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
+ DW_EH_PE_udata2 => reader.read::<u16>() as usize,
+ DW_EH_PE_udata4 => reader.read::<u32>() as usize,
+ DW_EH_PE_udata8 => reader.read::<u64>() as usize,
+ DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
+ DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
+ DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
+ DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
+ _ => return Err(()),
+ };
+
+ result += match encoding & 0x70 {
+ DW_EH_PE_absptr => 0,
+ // relative to address of the encoded value, despite the name
+ DW_EH_PE_pcrel => reader.ptr as usize,
+ DW_EH_PE_funcrel => {
+ if context.func_start == 0 {
+ return Err(());
+ }
+ context.func_start
+ }
+ DW_EH_PE_textrel => (*context.get_text_start)(),
+ DW_EH_PE_datarel => (*context.get_data_start)(),
+ _ => return Err(()),
+ };
+
+ if encoding & DW_EH_PE_indirect != 0 {
+ result = *(result as *const usize);
+ }
+
+ Ok(result)
+}
diff --git a/library/std/src/personality/dwarf/mod.rs b/library/std/src/personality/dwarf/mod.rs
new file mode 100644
index 000000000..652fbe95a
--- /dev/null
+++ b/library/std/src/personality/dwarf/mod.rs
@@ -0,0 +1,73 @@
+//! Utilities for parsing DWARF-encoded data streams.
+//! See <http://www.dwarfstd.org>,
+//! DWARF-4 standard, Section 7 - "Data Representation"
+
+// This module is used only by x86_64-pc-windows-gnu for now, but we
+// are compiling it everywhere to avoid regressions.
+#![allow(unused)]
+
+#[cfg(test)]
+mod tests;
+
+pub mod eh;
+
+use core::mem;
+
+pub struct DwarfReader {
+ pub ptr: *const u8,
+}
+
+#[repr(C, packed)]
+struct Unaligned<T>(T);
+
+impl DwarfReader {
+ pub fn new(ptr: *const u8) -> DwarfReader {
+ DwarfReader { ptr }
+ }
+
+ // DWARF streams are packed, so e.g., a u32 would not necessarily be aligned
+ // on a 4-byte boundary. This may cause problems on platforms with strict
+ // alignment requirements. By wrapping data in a "packed" struct, we are
+ // telling the backend to generate "misalignment-safe" code.
+ pub unsafe fn read<T: Copy>(&mut self) -> T {
+ let Unaligned(result) = *(self.ptr as *const Unaligned<T>);
+ self.ptr = self.ptr.add(mem::size_of::<T>());
+ result
+ }
+
+ // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
+ // Length Data".
+ pub unsafe fn read_uleb128(&mut self) -> u64 {
+ let mut shift: usize = 0;
+ let mut result: u64 = 0;
+ let mut byte: u8;
+ loop {
+ byte = self.read::<u8>();
+ result |= ((byte & 0x7F) as u64) << shift;
+ shift += 7;
+ if byte & 0x80 == 0 {
+ break;
+ }
+ }
+ result
+ }
+
+ pub unsafe fn read_sleb128(&mut self) -> i64 {
+ let mut shift: u32 = 0;
+ let mut result: u64 = 0;
+ let mut byte: u8;
+ loop {
+ byte = self.read::<u8>();
+ result |= ((byte & 0x7F) as u64) << shift;
+ shift += 7;
+ if byte & 0x80 == 0 {
+ break;
+ }
+ }
+ // sign-extend
+ if shift < u64::BITS && (byte & 0x40) != 0 {
+ result |= (!0 as u64) << shift;
+ }
+ result as i64
+ }
+}
diff --git a/library/std/src/personality/dwarf/tests.rs b/library/std/src/personality/dwarf/tests.rs
new file mode 100644
index 000000000..1644f3708
--- /dev/null
+++ b/library/std/src/personality/dwarf/tests.rs
@@ -0,0 +1,19 @@
+use super::*;
+
+#[test]
+fn dwarf_reader() {
+ let encoded: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 0xE5, 0x8E, 0x26, 0x9B, 0xF1, 0x59, 0xFF, 0xFF];
+
+ let mut reader = DwarfReader::new(encoded.as_ptr());
+
+ unsafe {
+ assert!(reader.read::<u8>() == u8::to_be(1u8));
+ assert!(reader.read::<u16>() == u16::to_be(0x0203));
+ assert!(reader.read::<u32>() == u32::to_be(0x04050607));
+
+ assert!(reader.read_uleb128() == 624485);
+ assert!(reader.read_sleb128() == -624485);
+
+ assert!(reader.read::<i8>() == i8::to_be(-1));
+ }
+}
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..242f44ade 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -801,11 +801,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 +858,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 +1041,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 +1052,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 +1223,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 +1529,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 +1545,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..d91d4fa64 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>,
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 663537a05..98f6cc7aa 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 3 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,16 @@ fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
+ #[cfg(not(bootstrap))] sigpipe: u8,
) -> isize {
let Ok(v) = lang_start_internal(
&move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
argc,
argv,
+ #[cfg(bootstrap)]
+ 2, // Temporary inlining of sigpipe::DEFAULT until bootstrap stops being special
+ #[cfg(not(bootstrap))]
+ 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/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_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..9ab781561 100644
--- a/library/std/src/sync/rwlock.rs
+++ b/library/std/src/sync/rwlock.rs
@@ -101,6 +101,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 +131,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,
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/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..f921839cf 100644
--- a/library/std/src/sys/hermit/fs.rs
+++ b/library/std/src/sys/hermit/fs.rs
@@ -2,7 +2,7 @@ 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::cvt;
@@ -41,6 +41,9 @@ pub struct OpenOptions {
mode: i32,
}
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
pub struct FilePermissions(!);
pub struct FileType(!);
@@ -110,6 +113,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
@@ -312,8 +320,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 +352,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 {
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..827d82900 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,7 +99,7 @@ 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);
}
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/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/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
index ea24fedd0..5409bd177 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 as usize % 8 == 0 { 0 } else { 8 - ptr as usize % 8 };
+ let small1_size = (len - small0_size as usize) % 8;
+ let big_size = len - small0_size as usize - small1_size as usize;
+
+ (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:
@@ -343,17 +373,6 @@ 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));
@@ -367,10 +386,10 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
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,20 +400,16 @@ 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 as _);
// 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);
+ copy_quadwords(big_src as _, big_dst, big_size);
// Copy small1
let small1_src = src.offset(big_size as isize + small0_size as isize);
@@ -404,6 +419,106 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
}
}
+/// 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);
+ }
+ }
+}
+
#[unstable(feature = "sgx_platform", issue = "56975")]
impl<T: ?Sized> UserRef<T>
where
@@ -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/solid/fs.rs b/library/std/src/sys/solid/fs.rs
index a2cbee4dc..969222253 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)
@@ -358,13 +366,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 +384,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 +458,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/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..cc347e358 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -2,7 +2,7 @@ use crate::os::unix::prelude::*;
use crate::ffi::{CStr, CString, 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;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use crate::path::{Path, PathBuf};
@@ -544,11 +544,11 @@ 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
+ // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
// the same as for Redox.
- #[cfg(any(target_os = "redox", target_os = "espidf"))]
+ #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))]
let omit = libc::timespec { tv_sec: 0, tv_nsec: 0 };
- #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
+ #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ };
Self([omit; 2])
}
@@ -687,7 +687,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()
+ );
}
}
@@ -825,6 +829,7 @@ impl DirEntry {
target_os = "fuchsia",
target_os = "redox"
)))]
+ #[cfg_attr(miri, allow(unused))]
fn name_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
}
@@ -836,6 +841,7 @@ impl DirEntry {
target_os = "fuchsia",
target_os = "redox"
))]
+ #[cfg_attr(miri, allow(unused))]
fn name_cstr(&self) -> &CStr {
&self.name
}
@@ -1031,8 +1037,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> {
@@ -1079,9 +1085,9 @@ impl File {
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
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!(
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/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..c84e292ea 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,9 +153,31 @@ 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 INHERIT: u8 = 1;
+ pub const SIG_IGN: u8 = 2;
+ pub const SIG_DFL: u8 = 3;
+ }
+
+ let handler = match sigpipe {
+ sigpipe::INHERIT => None,
+ sigpipe::SIG_IGN => Some(libc::SIG_IGN),
+ sigpipe::SIG_DFL => Some(libc::SIG_DFL),
+ _ => unreachable!(),
+ };
+ if let Some(handler) = handler {
+ rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR);
+ }
+ }
}
}
@@ -295,8 +319,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")]
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_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..2834ee0ac 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -45,11 +45,31 @@ cfg_if::cfg_if! {
}
#[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 +92,7 @@ pub struct Command {
argv: Argv,
env: CommandEnv,
+ program_kind: ProgramKind,
cwd: Option<CString>,
uid: Option<uid_t>,
gid: Option<gid_t>,
@@ -128,15 +149,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 +200,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 +282,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..d176b3401 100644
--- a/library/std/src/sys/unix/process/process_common/tests.rs
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -122,3 +122,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_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 75bb92437..26ae62817 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -453,7 +453,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)),
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/thread.rs b/library/std/src/sys/unix/thread.rs
index 36a3fa602..f6b627afc 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,17 @@ 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);
+ libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
+ }
+ }
+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
pub fn set_name(name: &CStr) {
unsafe {
@@ -142,6 +151,7 @@ impl Thread {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
pub fn set_name(name: &CStr) {
unsafe {
+ let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
libc::pthread_setname_np(name.as_ptr());
}
}
@@ -271,6 +281,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 +447,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
}
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..e2453580d
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker/mod.rs
@@ -0,0 +1,21 @@
+//! 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(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.rs b/library/std/src/sys/unix/thread_parker/pthread.rs
index ca1a7138f..3dfc0026e 100644
--- a/library/std/src/sys/unix/thread_parker.rs
+++ b/library/std/src/sys/unix/thread_parker/pthread.rs
@@ -1,15 +1,5 @@
//! 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;
@@ -59,8 +49,8 @@ unsafe fn wait_timeout(
target_os = "espidf"
))]
let (now, dur) = {
- use super::time::SystemTime;
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
@@ -85,7 +75,7 @@ unsafe fn wait_timeout(
target_os = "espidf"
)))]
let (now, dur) = {
- use super::time::Timespec;
+ use crate::sys::time::Timespec;
(Timespec::now(libc::CLOCK_MONOTONIC), dur)
};
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/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs
index d7cb12e0c..2be0b34b9 100644
--- a/library/std/src/sys/unsupported/locks/mutex.rs
+++ b/library/std/src/sys/unsupported/locks/mutex.rs
@@ -17,9 +17,6 @@ impl Mutex {
}
#[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/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/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index 6614ae397..510cf36b1 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -3,7 +3,7 @@
use super::fd::WasiFd;
use crate::ffi::{CStr, CString, 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;
@@ -439,8 +439,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> {
diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs
index 4cc0e4ed5..d2081771b 100644
--- a/library/std/src/sys/wasi/stdio.rs
+++ b/library/std/src/sys/wasi/stdio.rs
@@ -4,7 +4,7 @@ use super::fd::WasiFd;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
use crate::os::raw;
-use crate::os::wasi::io::{AsRawFd, FromRawFd};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd};
pub struct Stdin;
pub struct Stdout;
@@ -23,6 +23,13 @@ impl AsRawFd for Stdin {
}
}
+impl AsFd for Stdin {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(0) }
+ }
+}
+
impl io::Read for Stdin {
fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
self.read_vectored(&mut [IoSliceMut::new(data)])
@@ -51,6 +58,13 @@ impl AsRawFd for Stdout {
}
}
+impl AsFd for Stdout {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(1) }
+ }
+}
+
impl io::Write for Stdout {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.write_vectored(&[IoSlice::new(data)])
@@ -82,6 +96,13 @@ impl AsRawFd for Stderr {
}
}
+impl AsFd for Stderr {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(2) }
+ }
+}
+
impl io::Write for Stderr {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.write_vectored(&[IoSlice::new(data)])
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..89d0ab59b 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -66,6 +66,7 @@ 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;
@@ -278,6 +279,7 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
+pub const STATUS_NOT_SUPPORTED: NTSTATUS = 0xC00000BB_u32 as _;
// Equivalent to the `NT_SUCCESS` C preprocessor macro.
// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
@@ -285,7 +287,8 @@ pub fn nt_success(status: NTSTATUS) -> bool {
status >= 0
}
-pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
+// "RNG\0"
+pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
#[repr(C)]
pub struct UNICODE_STRING {
@@ -455,6 +458,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 +510,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 +520,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 +532,8 @@ 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 MOUNT_POINT_REPARSE_BUFFER {
pub SubstituteNameOffset: c_ushort,
@@ -1217,11 +1232,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 +1273,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..155d0297e 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -2,8 +2,8 @@ use crate::os::windows::prelude::*;
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 +11,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 +326,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 +395,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 +427,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 +470,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 +517,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 +533,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.
@@ -649,27 +670,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 +702,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> {
@@ -692,14 +717,21 @@ impl<'a> Iterator for DirBuffIter<'a> {
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>();
+ // Guaranteed to be aligned in documentation for
+ // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
+ assert!(info.is_aligned());
let next_entry = (*info).NextEntryOffset as usize;
let name = crate::slice::from_raw_parts(
- (*info).FileName.as_ptr().cast::<u16>(),
+ ptr::addr_of!((*info).FileName).cast::<u16>(),
(*info).FileNameLength as usize / size_of::<u16>(),
);
let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
@@ -1337,18 +1369,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 +1392,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/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/rand.rs b/library/std/src/sys/windows/rand.rs
index f8fd93a73..d6cd8f802 100644
--- a/library/std/src/sys/windows/rand.rs
+++ b/library/std/src/sys/windows/rand.rs
@@ -1,35 +1,126 @@
-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 changes this to use the `BCRYPT_RNG_ALG_HANDLE`
+//! [Pseudo-handle], which gets the default RNG algorithm without querying the
+//! system preference thus hopefully avoiding the previous issue.
+//! This is only supported on Windows 10+ so a fallback is used for older versions.
+//!
+//! [#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
+//! [Pseudo-handle]: https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-pseudo-handles
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::open().and_then(|rng| rng.gen_random_keys()).unwrap_or_else(fallback_rng)
+}
+
+struct Rng(c::BCRYPT_ALG_HANDLE);
+impl Rng {
+ #[cfg(miri)]
+ fn open() -> Result<Self, c::NTSTATUS> {
+ const BCRYPT_RNG_ALG_HANDLE: c::BCRYPT_ALG_HANDLE = ptr::invalid_mut(0x81);
+ let _ = (
+ c::BCryptOpenAlgorithmProvider,
+ c::BCryptCloseAlgorithmProvider,
+ c::BCRYPT_RNG_ALGORITHM,
+ c::STATUS_NOT_SUPPORTED,
+ );
+ Ok(Self(BCRYPT_RNG_ALG_HANDLE))
+ }
+ #[cfg(not(miri))]
+ // 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};
+ const ERROR_VALUE: c::LPVOID = ptr::invalid_mut(usize::MAX);
+
+ // 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);
+ // We use a sentinel value to designate an error occurred last time.
+ if handle == ERROR_VALUE {
+ Err(c::STATUS_NOT_SUPPORTED)
+ } else 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(Self(handle))
+ } else {
+ HANDLE.store(ERROR_VALUE, Release);
+ Err(status)
+ }
+ } else {
+ Ok(Self(handle))
+ }
+ }
+
+ 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.0, ptr::addr_of_mut!(v).cast(), size, 0)
+ };
+ if c::nt_success(status) { Ok(v) } else { Err(status) }
+ }
}
/// Generate random numbers using the fallback RNG function (RtlGenRandom)
#[cfg(not(target_vendor = "uwp"))]
#[inline(never)]
-fn fallback_rng() -> (u64, u64) {
+fn fallback_rng(rng_status: c::NTSTATUS) -> (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) };
- if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
+ if ret != 0 {
+ v
+ } else {
+ panic!(
+ "RNG broken: {rng_status:#x}, fallback RNG broken: {}",
+ crate::io::Error::last_os_error()
+ )
+ }
}
/// We can't use RtlGenRandom with UWP, so there is no fallback
#[cfg(target_vendor = "uwp")]
#[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) {
+ panic!("RNG broken: {rng_status:#x} fallback RNG broken: RtlGenRandom() not supported on UWP");
}
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_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/net.rs b/library/std/src/sys_common/net.rs
index 33d336c43..3ad802afa 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -10,7 +10,7 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::ptr;
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};
@@ -345,6 +345,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/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/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs
index 70beebe86..032bf604d 100644
--- a/library/std/src/sys_common/thread_local_key.rs
+++ b/library/std/src/sys_common/thread_local_key.rs
@@ -69,8 +69,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);
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..8aedfc4a6 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -1036,6 +1036,7 @@ pub mod fast {
}
#[doc(hidden)]
+#[cfg(not(target_thread_local))]
pub mod os {
use super::lazy::LazyKeyInner;
use crate::cell::Cell;
@@ -1044,6 +1045,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..ceea6986e 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
@@ -170,7 +170,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,21 +192,31 @@ 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
+// Select the type used by the thread_local! macro to access TLS keys. 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))]
#[doc(hidden)]
pub use self::local::fast::Key as __FastLocalKeyInner;
#[unstable(feature = "libstd_thread_internals", issue = "none")]
+#[cfg(target_thread_local)]
+#[cfg(test)] // when building for tests, use real std's key
+pub use realstd::thread::__FastLocalKeyInner;
+
+#[unstable(feature = "libstd_thread_internals", issue = "none")]
+#[cfg(target_thread_local)]
+#[cfg(test)]
+pub use self::local::fast::Key as __FastLocalKeyInnerUnused; // we import this anyway to silence 'unused' warnings
+
+#[unstable(feature = "libstd_thread_internals", issue = "none")]
#[doc(hidden)]
+#[cfg(not(target_thread_local))]
pub use self::local::os::Key as __OsLocalKeyInner;
#[unstable(feature = "libstd_thread_internals", issue = "none")]
#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
@@ -1033,24 +1042,48 @@ 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::sys_common::mutex::StaticMutex;
+
+ // It is UB to attempt to acquire this mutex reentrantly!
+ static GUARD: StaticMutex = StaticMutex::new();
+ static mut COUNTER: u64 = 0;
+
+ unsafe {
+ let guard = GUARD.lock();
+
+ let Some(id) = COUNTER.checked_add(1) else {
+ drop(guard); // in case the panic handler ends up calling `ThreadId::new()`, avoid reentrant lock acquire.
+ exhausted();
+ };
+
+ COUNTER = id;
+ drop(guard);
+ ThreadId(NonZeroU64::new(id).unwrap())
+ }
+ }
}
}
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
index ec68b5291..777964f04 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() {
@@ -329,3 +360,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/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;