Importing rustc-1.56.0
Change-Id: I98941481270706fa55f8fb2cb91686ae3bd30f38
diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs
index f888452..9ace3e1 100644
--- a/library/std/src/backtrace.rs
+++ b/library/std/src/backtrace.rs
@@ -35,13 +35,13 @@
//! `BacktraceStatus` enum as a result of `Backtrace::status`.
//!
//! Like above with accuracy platform support is done on a best effort basis.
-//! Sometimes libraries may not be available at runtime or something may go
+//! Sometimes libraries might not be available at runtime or something may go
//! wrong which would cause a backtrace to not be captured. Please feel free to
//! report issues with platforms where a backtrace cannot be captured though!
//!
//! ## Environment Variables
//!
-//! The `Backtrace::capture` function may not actually capture a backtrace by
+//! The `Backtrace::capture` function might not actually capture a backtrace by
//! default. Its behavior is governed by two environment variables:
//!
//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
@@ -61,7 +61,7 @@
//! Note that the `Backtrace::force_capture` function can be used to ignore
//! these environment variables. Also note that the state of environment
//! variables is cached once the first backtrace is created, so altering
-//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime may not actually change
+//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime might not actually change
//! how backtraces are captured.
#![unstable(feature = "backtrace", issue = "53487")]
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 4a5efab..4dff4fa 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -1,5 +1,3 @@
-// ignore-tidy-filelength
-
#[cfg(test)]
mod tests;
@@ -10,6 +8,7 @@
use crate::borrow::Borrow;
use crate::cell::Cell;
use crate::collections::TryReserveError;
+use crate::collections::TryReserveErrorKind;
use crate::fmt::{self, Debug};
#[allow(deprecated)]
use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
@@ -124,8 +123,21 @@
/// }
/// ```
///
-/// `HashMap` also implements an [`Entry API`](#method.entry), which allows
-/// for more complex methods of getting, setting, updating and removing keys and
+/// A `HashMap` with a known list of items can be initialized from an array:
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let solar_distance = HashMap::from([
+/// ("Mercury", 0.4),
+/// ("Venus", 0.7),
+/// ("Earth", 1.0),
+/// ("Mars", 1.5),
+/// ]);
+/// ```
+///
+/// `HashMap` implements an [`Entry API`](#method.entry), which allows
+/// for complex methods of getting, setting, updating and removing keys and
/// their values:
///
/// ```
@@ -179,30 +191,21 @@
/// }
///
/// // Use a HashMap to store the vikings' health points.
-/// let mut vikings = HashMap::new();
-///
-/// vikings.insert(Viking::new("Einar", "Norway"), 25);
-/// vikings.insert(Viking::new("Olaf", "Denmark"), 24);
-/// vikings.insert(Viking::new("Harald", "Iceland"), 12);
+/// let vikings = HashMap::from([
+/// (Viking::new("Einar", "Norway"), 25),
+/// (Viking::new("Olaf", "Denmark"), 24),
+/// (Viking::new("Harald", "Iceland"), 12),
+/// ]);
///
/// // Use derived implementation to print the status of the vikings.
/// for (viking, health) in &vikings {
/// println!("{:?} has {} hp", viking, health);
/// }
/// ```
-///
-/// A `HashMap` with fixed list of elements can be initialized from an array:
-///
-/// ```
-/// use std::collections::HashMap;
-///
-/// let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)]
-/// .iter().cloned().collect();
-/// // use the values stored in map
-/// ```
#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")]
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), rustc_insignificant_dtor)]
pub struct HashMap<K, V, S = RandomState> {
base: base::HashMap<K, V, S>,
}
@@ -665,7 +668,6 @@
/// # Examples
///
/// ```
- /// #![feature(shrink_to)]
/// use std::collections::HashMap;
///
/// let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
@@ -678,7 +680,7 @@
/// assert!(map.capacity() >= 2);
/// ```
#[inline]
- #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")]
+ #[stable(feature = "shrink_to", since = "1.56.0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.base.shrink_to(min_capacity);
}
@@ -968,7 +970,11 @@
/// map.insert("b", 2);
/// map.insert("c", 3);
///
- /// let vec: Vec<&str> = map.into_keys().collect();
+ /// let mut vec: Vec<&str> = map.into_keys().collect();
+ /// // The `IntoKeys` iterator produces keys in arbitrary order, so the
+ /// // keys must be sorted to test them against a sorted array.
+ /// vec.sort_unstable();
+ /// assert_eq!(vec, ["a", "b", "c"]);
/// ```
#[inline]
#[stable(feature = "map_into_keys_values", since = "1.54.0")]
@@ -990,7 +996,11 @@
/// map.insert("b", 2);
/// map.insert("c", 3);
///
- /// let vec: Vec<i32> = map.into_values().collect();
+ /// let mut vec: Vec<i32> = map.into_values().collect();
+ /// // The `IntoValues` iterator produces values in arbitrary order, so
+ /// // the values must be sorted to test them against a sorted array.
+ /// vec.sort_unstable();
+ /// assert_eq!(vec, [1, 2, 3]);
/// ```
#[inline]
#[stable(feature = "map_into_keys_values", since = "1.54.0")]
@@ -1148,6 +1158,37 @@
}
}
+#[stable(feature = "std_collections_from_array", since = "1.56.0")]
+// Note: as what is currently the most convenient built-in way to construct
+// a HashMap, a simple usage of this function must not *require* the user
+// to provide a type annotation in order to infer the third type parameter
+// (the hasher parameter, conventionally "S").
+// To that end, this impl is defined using RandomState as the concrete
+// type of S, rather than being generic over `S: BuildHasher + Default`.
+// It is expected that users who want to specify a hasher will manually use
+// `with_capacity_and_hasher`.
+// If type parameter defaults worked on impls, and if type parameter
+// defaults could be mixed with const generics, then perhaps
+// this could be generalized.
+// See also the equivalent impl on HashSet.
+impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V, RandomState>
+where
+ K: Eq + Hash,
+{
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ ///
+ /// let map1 = HashMap::from([(1, 2), (3, 4)]);
+ /// let map2: HashMap<_, _> = [(1, 2), (3, 4)].into();
+ /// assert_eq!(map1, map2);
+ /// ```
+ fn from(arr: [(K, V); N]) -> Self {
+ crate::array::IntoIter::new(arr).collect()
+ }
+}
+
/// An iterator over the entries of a `HashMap`.
///
/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its
@@ -1207,7 +1248,7 @@
}
impl<'a, K, V> IterMut<'a, K, V> {
- /// Returns a iterator of references over the remaining items.
+ /// Returns an iterator of references over the remaining items.
#[inline]
pub(super) fn iter(&self) -> Iter<'_, K, V> {
Iter { base: self.base.rustc_iter() }
@@ -1236,7 +1277,7 @@
}
impl<K, V> IntoIter<K, V> {
- /// Returns a iterator of references over the remaining items.
+ /// Returns an iterator of references over the remaining items.
#[inline]
pub(super) fn iter(&self) -> Iter<'_, K, V> {
Iter { base: self.base.rustc_iter() }
@@ -1339,7 +1380,7 @@
}
impl<'a, K, V> Drain<'a, K, V> {
- /// Returns a iterator of references over the remaining items.
+ /// Returns an iterator of references over the remaining items.
#[inline]
pub(super) fn iter(&self) -> Iter<'_, K, V> {
Iter { base: self.base.rustc_iter() }
@@ -2787,15 +2828,7 @@
#[inline]
fn extend_reserve(&mut self, additional: usize) {
- // self.base.extend_reserve(additional);
- // FIXME: hashbrown should implement this method.
- // But until then, use the same reservation logic:
-
- // Reserve the entire hint lower bound if the map is empty.
- // Otherwise reserve half the hint (rounded up), so the map
- // will only resize twice in the worst case.
- let reserve = if self.is_empty() { additional } else { (additional + 1) / 2 };
- self.base.reserve(reserve);
+ self.base.extend_reserve(additional);
}
}
@@ -2966,9 +2999,11 @@
#[inline]
pub(super) fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError {
match err {
- hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow,
+ hashbrown::TryReserveError::CapacityOverflow => {
+ TryReserveErrorKind::CapacityOverflow.into()
+ }
hashbrown::TryReserveError::AllocError { layout } => {
- TryReserveError::AllocError { layout, non_exhaustive: () }
+ TryReserveErrorKind::AllocError { layout, non_exhaustive: () }.into()
}
}
}
diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs
index 819be14..d9b20ae 100644
--- a/library/std/src/collections/hash/map/tests.rs
+++ b/library/std/src/collections/hash/map/tests.rs
@@ -1,9 +1,10 @@
use super::Entry::{Occupied, Vacant};
use super::HashMap;
use super::RandomState;
+use crate::assert_matches::assert_matches;
use crate::cell::RefCell;
use rand::{thread_rng, Rng};
-use realstd::collections::TryReserveError::*;
+use realstd::collections::TryReserveErrorKind::*;
// https://github.com/rust-lang/rust/issues/62301
fn _assert_hashmap_is_unwind_safe() {
@@ -821,15 +822,17 @@
const MAX_USIZE: usize = usize::MAX;
- if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
- } else {
- panic!("usize::MAX should trigger an overflow!");
- }
+ assert_matches!(
+ empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
+ Err(CapacityOverflow),
+ "usize::MAX should trigger an overflow!"
+ );
- if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 8) {
- } else {
- panic!("usize::MAX / 8 should trigger an OOM!")
- }
+ assert_matches!(
+ empty_bytes.try_reserve(MAX_USIZE / 8).map_err(|e| e.kind()),
+ Err(AllocError { .. }),
+ "usize::MAX / 8 should trigger an OOM!"
+ );
}
#[test]
@@ -1085,3 +1088,15 @@
assert_eq!(map.len(), 2);
}
}
+
+#[test]
+fn from_array() {
+ let map = HashMap::from([(1, 2), (3, 4)]);
+ let unordered_duplicates = HashMap::from([(3, 4), (1, 2), (1, 2)]);
+ assert_eq!(map, unordered_duplicates);
+
+ // This next line must infer the hasher type parameter.
+ // If you make a change that causes this line to no longer infer,
+ // that's a problem!
+ let _must_not_require_type_annotation = HashMap::from([(1, 2)]);
+}
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index 1a2a8af..3b61acd 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -95,14 +95,12 @@
/// }
/// ```
///
-/// A `HashSet` with fixed list of elements can be initialized from an array:
+/// A `HashSet` with a known list of items can be initialized from an array:
///
/// ```
/// use std::collections::HashSet;
///
-/// let viking_names: HashSet<&'static str> =
-/// [ "Einar", "Olaf", "Harald" ].iter().cloned().collect();
-/// // use the values stored in the set
+/// let viking_names = HashSet::from(["Einar", "Olaf", "Harald"]);
/// ```
///
/// [hash set]: crate::collections#use-the-set-variant-of-any-of-these-maps-when
@@ -466,7 +464,6 @@
/// # Examples
///
/// ```
- /// #![feature(shrink_to)]
/// use std::collections::HashSet;
///
/// let mut set = HashSet::with_capacity(100);
@@ -479,7 +476,7 @@
/// assert!(set.capacity() >= 2);
/// ```
#[inline]
- #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")]
+ #[stable(feature = "shrink_to", since = "1.56.0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.base.shrink_to(min_capacity)
}
@@ -997,6 +994,37 @@
}
}
+#[stable(feature = "std_collections_from_array", since = "1.56.0")]
+// Note: as what is currently the most convenient built-in way to construct
+// a HashSet, a simple usage of this function must not *require* the user
+// to provide a type annotation in order to infer the third type parameter
+// (the hasher parameter, conventionally "S").
+// To that end, this impl is defined using RandomState as the concrete
+// type of S, rather than being generic over `S: BuildHasher + Default`.
+// It is expected that users who want to specify a hasher will manually use
+// `with_capacity_and_hasher`.
+// If type parameter defaults worked on impls, and if type parameter
+// defaults could be mixed with const generics, then perhaps
+// this could be generalized.
+// See also the equivalent impl on HashMap.
+impl<T, const N: usize> From<[T; N]> for HashSet<T, RandomState>
+where
+ T: Eq + Hash,
+{
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::HashSet;
+ ///
+ /// let set1 = HashSet::from([1, 2, 3, 4]);
+ /// let set2: HashSet<_> = [1, 2, 3, 4].into();
+ /// assert_eq!(set1, set2);
+ /// ```
+ fn from(arr: [T; N]) -> Self {
+ crate::array::IntoIter::new(arr).collect()
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, S> Extend<T> for HashSet<T, S>
where
diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs
index 40f8467..6a625e6 100644
--- a/library/std/src/collections/hash/set/tests.rs
+++ b/library/std/src/collections/hash/set/tests.rs
@@ -484,3 +484,15 @@
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
assert_eq!(set.len(), 0);
}
+
+#[test]
+fn from_array() {
+ let set = HashSet::from([1, 2, 3, 4]);
+ let unordered_duplicates = HashSet::from([4, 1, 4, 3, 2]);
+ assert_eq!(set, unordered_duplicates);
+
+ // This next line must infer the hasher type parameter.
+ // If you make a change that causes this line to no longer infer,
+ // that's a problem!
+ let _must_not_require_type_annotation = HashSet::from([1, 2]);
+}
diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs
index 8cda601..130bb5c 100644
--- a/library/std/src/collections/mod.rs
+++ b/library/std/src/collections/mod.rs
@@ -422,6 +422,12 @@
#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
pub use alloc_crate::collections::TryReserveError;
+#[unstable(
+ feature = "try_reserve_kind",
+ reason = "Uncertain how much info should be exposed",
+ issue = "48043"
+)]
+pub use alloc_crate::collections::TryReserveErrorKind;
mod hash;
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 64f88c1..e343073 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -185,15 +185,13 @@
///
/// # Errors
///
-/// Errors if the environment variable is not present.
-/// Errors if the environment variable is not valid Unicode. If this is not desired, consider using
-/// [`var_os`].
+/// This function will return an error if the environment variable isn't set.
///
-/// # Panics
+/// This function may return an error if the environment variable's name contains
+/// the equal sign character (`=`) or the NUL character.
///
-/// This function may panic if `key` is empty, contains an ASCII equals sign
-/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
-/// character.
+/// This function will return an error if the environment variable's value is
+/// not valid Unicode. If this is not desired, consider using [`var_os`].
///
/// # Examples
///
@@ -219,18 +217,22 @@
}
/// Fetches the environment variable `key` from the current process, returning
-/// [`None`] if the variable isn't set.
-///
-/// # Panics
-///
-/// This function may panic if `key` is empty, contains an ASCII equals sign
-/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
-/// character.
+/// [`None`] if the variable isn't set or there's another error.
///
/// Note that the method will not check if the environment variable
/// is valid Unicode. If you want to have an error on invalid UTF-8,
/// use the [`var`] function instead.
///
+/// # Errors
+///
+/// This function returns an error if the environment variable isn't set.
+///
+/// This function may return an error if the environment variable's name contains
+/// the equal sign character (`=`) or the NUL character.
+///
+/// This function may return an error if the environment variable's value contains
+/// the NUL character.
+///
/// # Examples
///
/// ```
@@ -249,7 +251,6 @@
fn _var_os(key: &OsStr) -> Option<OsString> {
os_imp::getenv(key)
- .unwrap_or_else(|e| panic!("failed to get environment variable `{:?}`: {}", key, e))
}
/// The error type for operations interacting with environment variables.
@@ -294,7 +295,7 @@
}
}
-/// Sets the environment variable `k` to the value `v` for the currently running
+/// Sets the environment variable `key` to the value `value` for the currently running
/// process.
///
/// Note that while concurrent access to environment variables is safe in Rust,
@@ -310,9 +311,8 @@
///
/// # Panics
///
-/// This function may panic if `key` is empty, contains an ASCII equals sign
-/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
-/// character.
+/// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
+/// or the NUL character `'\0'`, or when `value` contains the NUL character.
///
/// # Examples
///
@@ -616,6 +616,9 @@
/// return the path of the symbolic link and other platforms will return the
/// path of the symbolic link’s target.
///
+/// If the executable is renamed while it is running, platforms may return the
+/// path at the time it was loaded instead of the new path.
+///
/// # Errors
///
/// Acquiring the path of the current executable is a platform-specific operation
@@ -683,7 +686,7 @@
/// for more.
///
/// The first element is traditionally the path of the executable, but it can be
-/// set to arbitrary text, and may not even exist. This means this property
+/// set to arbitrary text, and might not even exist. This means this property
/// should not be relied upon for security purposes.
///
/// [`env::args()`]: args
@@ -699,7 +702,7 @@
/// for more.
///
/// The first element is traditionally the path of the executable, but it can be
-/// set to arbitrary text, and may not even exist. This means this property
+/// set to arbitrary text, and might not even exist. This means this property
/// should not be relied upon for security purposes.
///
/// [`env::args_os()`]: args_os
@@ -712,7 +715,7 @@
/// via the command line).
///
/// The first element is traditionally the path of the executable, but it can be
-/// set to arbitrary text, and may not even exist. This means this property should
+/// set to arbitrary text, and might not even exist. This means this property should
/// not be relied upon for security purposes.
///
/// On Unix systems the shell usually expands unquoted arguments with glob patterns
@@ -749,7 +752,7 @@
/// via the command line).
///
/// The first element is traditionally the path of the executable, but it can be
-/// set to arbitrary text, and may not even exist. This means this property should
+/// set to arbitrary text, and might not even exist. This means this property should
/// not be relied upon for security purposes.
///
/// On Unix systems the shell usually expands unquoted arguments with glob patterns
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index e0cc6ad..0b39289 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -409,7 +409,7 @@
/// Returns the logarithm of the number with respect to an arbitrary base.
///
- /// The result may not be correctly rounded owing to implementation details;
+ /// The result might not be correctly rounded owing to implementation details;
/// `self.log2()` can produce more accurate results for base 2, and
/// `self.log10()` can produce more accurate results for base 10.
///
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index 7ed65b7..602cceb 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -409,7 +409,7 @@
/// Returns the logarithm of the number with respect to an arbitrary base.
///
- /// The result may not be correctly rounded owing to implementation details;
+ /// The result might not be correctly rounded owing to implementation details;
/// `self.log2()` can produce more accurate results for base 2, and
/// `self.log10()` can produce more accurate results for base 10.
///
diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs
index be7e099..de05c37 100644
--- a/library/std/src/ffi/c_str.rs
+++ b/library/std/src/ffi/c_str.rs
@@ -46,7 +46,7 @@
///
/// # Extracting a raw pointer to the whole C string
///
-/// `CString` implements a [`as_ptr`][`CStr::as_ptr`] method through the [`Deref`]
+/// `CString` implements an [`as_ptr`][`CStr::as_ptr`] method through the [`Deref`]
/// trait. This method will give you a `*const c_char` which you can
/// feed directly to extern functions that expect a nul-terminated
/// string, like C's `strdup()`. Notice that [`as_ptr`][`CStr::as_ptr`] returns a
@@ -730,7 +730,7 @@
/// );
/// ```
///
- /// A incorrectly formatted [`Vec`] will produce an error.
+ /// An incorrectly formatted [`Vec`] will produce an error.
///
/// ```
/// #![feature(cstring_from_vec_with_nul)]
@@ -939,7 +939,7 @@
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<CString> for Arc<CStr> {
- /// Converts a [`CString`] into a [`Arc`]`<CStr>` without copying or allocating.
+ /// Converts a [`CString`] into an [`Arc`]`<CStr>` without copying or allocating.
#[inline]
fn from(s: CString) -> Arc<CStr> {
let arc: Arc<[u8]> = Arc::from(s.into_inner());
@@ -958,7 +958,7 @@
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<CString> for Rc<CStr> {
- /// Converts a [`CString`] into a [`Rc`]`<CStr>` without copying or allocating.
+ /// Converts a [`CString`] into an [`Rc`]`<CStr>` without copying or allocating.
#[inline]
fn from(s: CString) -> Rc<CStr> {
let rc: Rc<[u8]> = Rc::from(s.into_inner());
diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs
index 0b7dc25..fe4e3af 100644
--- a/library/std/src/ffi/mod.rs
+++ b/library/std/src/ffi/mod.rs
@@ -94,7 +94,7 @@
//!
//! * [`OsStr`] represents a borrowed reference to a string in a
//! format that can be passed to the operating system. It can be
-//! converted into an UTF-8 Rust string slice in a similar way to
+//! converted into a UTF-8 Rust string slice in a similar way to
//! [`OsString`].
//!
//! # Conversions
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 2a85f37..21f354c 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -271,7 +271,9 @@
///
/// Note that the allocator may give the collection more space than it
/// requests. Therefore, capacity can not be relied upon to be precisely
- /// minimal. Prefer reserve if future insertions are expected.
+ /// minimal. Prefer [`reserve`] if future insertions are expected.
+ ///
+ /// [`reserve`]: OsString::reserve
///
/// # Examples
///
@@ -319,7 +321,6 @@
/// # Examples
///
/// ```
- /// #![feature(shrink_to)]
/// use std::ffi::OsString;
///
/// let mut s = OsString::from("foo");
@@ -333,7 +334,7 @@
/// assert!(s.capacity() >= 3);
/// ```
#[inline]
- #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")]
+ #[stable(feature = "shrink_to", since = "1.56.0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}
@@ -358,7 +359,7 @@
#[stable(feature = "rust1", since = "1.0.0")]
impl From<String> for OsString {
- /// Converts a [`String`] into a [`OsString`].
+ /// Converts a [`String`] into an [`OsString`].
///
/// This conversion does not allocate or copy memory.
#[inline]
@@ -879,7 +880,7 @@
#[stable(feature = "box_from_os_string", since = "1.20.0")]
impl From<OsString> for Box<OsStr> {
- /// Converts a [`OsString`] into a [`Box`]`<OsStr>` without copying or allocating.
+ /// Converts an [`OsString`] into a [`Box`]`<OsStr>` without copying or allocating.
#[inline]
fn from(s: OsString) -> Box<OsStr> {
s.into_boxed_os_str()
@@ -896,7 +897,7 @@
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<OsString> for Arc<OsStr> {
- /// Converts a [`OsString`] into a [`Arc`]`<OsStr>` without copying or allocating.
+ /// Converts an [`OsString`] into an [`Arc`]`<OsStr>` without copying or allocating.
#[inline]
fn from(s: OsString) -> Arc<OsStr> {
let arc = s.inner.into_arc();
@@ -915,7 +916,7 @@
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<OsString> for Rc<OsStr> {
- /// Converts a [`OsString`] into a [`Rc`]`<OsStr>` without copying or allocating.
+ /// Converts an [`OsString`] into an [`Rc`]`<OsStr>` without copying or allocating.
#[inline]
fn from(s: OsString) -> Rc<OsStr> {
let rc = s.inner.into_rc();
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index bbe1ab4..bdb1729 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -419,7 +419,7 @@
self.inner.fsync()
}
- /// This function is similar to [`sync_all`], except that it may not
+ /// This function is similar to [`sync_all`], except that it might not
/// synchronize file metadata to the filesystem.
///
/// This is intended for use cases that must synchronize content, but don't
@@ -587,6 +587,12 @@
}
}
+// In addition to the `impl`s here, `File` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and
+// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows.
+
impl AsInner<fs_imp::File> for File {
fn as_inner(&self) -> &fs_imp::File {
&self.inner
@@ -1081,7 +1087,7 @@
///
/// # Errors
///
- /// This field may not be available on all platforms, and will return an
+ /// This field might not be available on all platforms, and will return an
/// `Err` on platforms where it is not available.
///
/// # Examples
@@ -1116,7 +1122,7 @@
///
/// # Errors
///
- /// This field may not be available on all platforms, and will return an
+ /// This field might not be available on all platforms, and will return an
/// `Err` on platforms where it is not available.
///
/// # Examples
@@ -1148,7 +1154,7 @@
///
/// # Errors
///
- /// This field may not be available on all platforms, and will return an
+ /// This field might not be available on all platforms, and will return an
/// `Err` on platforms or filesystems where it is not available.
///
/// # Examples
@@ -1912,6 +1918,7 @@
/// Ok(())
/// }
/// ```
+#[doc(alias = "mkdir")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
DirBuilder::new().create(path.as_ref())
@@ -1991,6 +1998,7 @@
/// Ok(())
/// }
/// ```
+#[doc(alias = "rmdir")]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
fs_imp::rmdir(path.as_ref())
@@ -2037,6 +2045,8 @@
///
/// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`.
/// New errors may be encountered after an iterator is initially constructed.
+/// Entries for the current and parent directories (typically `.` and `..`) are
+/// skipped.
///
/// # Platform-specific behavior
///
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 080b4b5..13dbae3 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -824,7 +824,7 @@
};
// Use a relative path for testing. Symlinks get normalized by Windows,
- // so we may not get the same path back for absolute paths
+ // so we might not get the same path back for absolute paths
check!(symlink_file(&"foo", &tmpdir.join("bar")));
assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
}
diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs
index c982441..df60af7 100644
--- a/library/std/src/io/buffered/bufwriter.rs
+++ b/library/std/src/io/buffered/bufwriter.rs
@@ -307,7 +307,7 @@
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
match self.flush_buf() {
Err(e) => Err(IntoInnerError::new(self, e)),
- Ok(()) => Ok(self.into_raw_parts().0),
+ Ok(()) => Ok(self.into_parts().0),
}
}
@@ -318,24 +318,23 @@
/// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
/// contents can still be recovered).
///
- /// `into_raw_parts` makes no attempt to flush data and cannot fail.
+ /// `into_parts` makes no attempt to flush data and cannot fail.
///
/// # Examples
///
/// ```
- /// #![feature(bufwriter_into_raw_parts)]
/// use std::io::{BufWriter, Write};
///
/// let mut buffer = [0u8; 10];
/// let mut stream = BufWriter::new(buffer.as_mut());
/// write!(stream, "too much data").unwrap();
/// stream.flush().expect_err("it doesn't fit");
- /// let (recovered_writer, buffered_data) = stream.into_raw_parts();
+ /// let (recovered_writer, buffered_data) = stream.into_parts();
/// assert_eq!(recovered_writer.len(), 0);
/// assert_eq!(&buffered_data.unwrap(), b"ata");
/// ```
- #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
- pub fn into_raw_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
+ #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
+ pub fn into_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
let buf = mem::take(&mut self.buf);
let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
@@ -444,14 +443,13 @@
}
}
-#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
-/// Error returned for the buffered data from `BufWriter::into_raw_parts`, when the underlying
+#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
+/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying
/// writer has previously panicked. Contains the (possibly partly written) buffered data.
///
/// # Example
///
/// ```
-/// #![feature(bufwriter_into_raw_parts)]
/// use std::io::{self, BufWriter, Write};
/// use std::panic::{catch_unwind, AssertUnwindSafe};
///
@@ -467,7 +465,7 @@
/// stream.flush().unwrap()
/// }));
/// assert!(result.is_err());
-/// let (recovered_writer, buffered_data) = stream.into_raw_parts();
+/// let (recovered_writer, buffered_data) = stream.into_parts();
/// assert!(matches!(recovered_writer, PanickingWriter));
/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data");
/// ```
@@ -478,7 +476,7 @@
impl WriterPanicked {
/// Returns the perhaps-unwritten data. Some of this data may have been written by the
/// panicking call(s) to the underlying writer, so simply writing it again is not a good idea.
- #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
+ #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub fn into_inner(self) -> Vec<u8> {
self.buf
}
@@ -487,7 +485,7 @@
"BufWriter inner writer panicked, what data remains unwritten is not known";
}
-#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
+#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
impl error::Error for WriterPanicked {
#[allow(deprecated, deprecated_in_future)]
fn description(&self) -> &str {
@@ -495,14 +493,14 @@
}
}
-#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
+#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
impl fmt::Display for WriterPanicked {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Self::DESCRIPTION)
}
}
-#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
+#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
impl fmt::Debug for WriterPanicked {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WriterPanicked")
diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs
index 38076ab..179bdf7 100644
--- a/library/std/src/io/buffered/mod.rs
+++ b/library/std/src/io/buffered/mod.rs
@@ -14,6 +14,8 @@
pub use bufreader::BufReader;
pub use bufwriter::BufWriter;
+#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
+pub use bufwriter::WriterPanicked;
pub use linewriter::LineWriter;
use linewritershim::LineWriterShim;
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index 829ef3d..51666c0 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -261,6 +261,33 @@
#[stable(feature = "rust1", since = "1.0.0")]
Interrupted,
+ /// This operation is unsupported on this platform.
+ ///
+ /// This means that the operation can never succeed.
+ #[stable(feature = "unsupported_error", since = "1.53.0")]
+ Unsupported,
+
+ // ErrorKinds which are primarily categorisations for OS error
+ // codes should be added above.
+ //
+ /// An error returned when an operation could not be completed because an
+ /// "end of file" was reached prematurely.
+ ///
+ /// This typically means that an operation could only succeed if it read a
+ /// particular number of bytes but only a smaller number of bytes could be
+ /// read.
+ #[stable(feature = "read_exact", since = "1.6.0")]
+ UnexpectedEof,
+
+ /// An operation could not be completed, because it failed
+ /// to allocate enough memory.
+ #[stable(feature = "out_of_memory_error", since = "1.54.0")]
+ OutOfMemory,
+
+ // "Unusual" error kinds which do not correspond simply to (sets
+ // of) OS error codes, should be added just above this comment.
+ // `Other` and `Uncategorised` should remain at the end:
+ //
/// A custom error that does not fall under any other I/O error kind.
///
/// This can be used to construct your own [`Error`]s that do not match any
@@ -274,26 +301,6 @@
#[stable(feature = "rust1", since = "1.0.0")]
Other,
- /// An error returned when an operation could not be completed because an
- /// "end of file" was reached prematurely.
- ///
- /// This typically means that an operation could only succeed if it read a
- /// particular number of bytes but only a smaller number of bytes could be
- /// read.
- #[stable(feature = "read_exact", since = "1.6.0")]
- UnexpectedEof,
-
- /// This operation is unsupported on this platform.
- ///
- /// This means that the operation can never succeed.
- #[stable(feature = "unsupported_error", since = "1.53.0")]
- Unsupported,
-
- /// An operation could not be completed, because it failed
- /// to allocate enough memory.
- #[stable(feature = "out_of_memory_error", since = "1.54.0")]
- OutOfMemory,
-
/// Any I/O error from the standard library that's not part of this list.
///
/// Errors that are `Uncategorized` now may move to a different or a new
@@ -307,13 +314,13 @@
impl ErrorKind {
pub(crate) fn as_str(&self) -> &'static str {
use ErrorKind::*;
+ // Strictly alphabetical, please. (Sadly rustfmt cannot do this yet.)
match *self {
AddrInUse => "address in use",
AddrNotAvailable => "address not available",
AlreadyExists => "entity already exists",
ArgumentListTooLong => "argument list too long",
BrokenPipe => "broken pipe",
- ResourceBusy => "resource busy",
ConnectionAborted => "connection aborted",
ConnectionRefused => "connection refused",
ConnectionReset => "connection reset",
@@ -321,9 +328,10 @@
Deadlock => "deadlock",
DirectoryNotEmpty => "directory not empty",
ExecutableFileBusy => "executable file busy",
- FilenameTooLong => "filename too long",
- FilesystemQuotaExceeded => "filesystem quota exceeded",
FileTooLarge => "file too large",
+ FilenameTooLong => "filename too long",
+ FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)",
+ FilesystemQuotaExceeded => "filesystem quota exceeded",
HostUnreachable => "host unreachable",
Interrupted => "operation interrupted",
InvalidData => "invalid data",
@@ -332,16 +340,16 @@
NetworkDown => "network down",
NetworkUnreachable => "network unreachable",
NotADirectory => "not a directory",
- StorageFull => "no storage space",
NotConnected => "not connected",
NotFound => "entity not found",
+ NotSeekable => "seek on unseekable file",
Other => "other error",
OutOfMemory => "out of memory",
PermissionDenied => "permission denied",
ReadOnlyFilesystem => "read-only filesystem or storage medium",
+ ResourceBusy => "resource busy",
StaleNetworkFileHandle => "stale network file handle",
- FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)",
- NotSeekable => "seek on unseekable file",
+ StorageFull => "no storage space",
TimedOut => "timed out",
TooManyLinks => "too many links",
Uncategorized => "uncategorized error",
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index cc615b9..e8466fa 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -264,6 +264,8 @@
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::buffered::IntoInnerError;
+#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
+pub use self::buffered::WriterPanicked;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::buffered::{BufReader, BufWriter, LineWriter};
#[stable(feature = "rust1", since = "1.0.0")]
@@ -553,7 +555,7 @@
/// contents of `buf` being true. It is recommended that *implementations*
/// only write data to `buf` instead of reading its contents.
///
- /// Correspondingly, however, *callers* of this method may not assume any guarantees
+ /// Correspondingly, however, *callers* of this method must not assume any guarantees
/// about how the implementation uses `buf`. The trait is safe to implement,
/// so it is possible that the code that's supposed to write to the buffer might also read
/// from it. It is your responsibility to make sure that `buf` is initialized
@@ -808,9 +810,9 @@
default_read_exact(self, buf)
}
- /// Creates a "by reference" adaptor for this instance of `Read`.
+ /// Creates a "by reference" adapter for this instance of `Read`.
///
- /// The returned adaptor also implements `Read` and will simply borrow this
+ /// The returned adapter also implements `Read` and will simply borrow this
/// current reader.
///
/// # Examples
@@ -887,7 +889,7 @@
Bytes { inner: self }
}
- /// Creates an adaptor which will chain this stream with another.
+ /// Creates an adapter which will chain this stream with another.
///
/// The returned `Read` instance will first read all bytes from this object
/// until EOF is encountered. Afterwards the output is equivalent to the
@@ -925,7 +927,7 @@
Chain { first: self, second: next, done_first: false }
}
- /// Creates an adaptor which will read at most `limit` bytes from it.
+ /// Creates an adapter which will read at most `limit` bytes from it.
///
/// This function returns a new instance of `Read` which will read at most
/// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
@@ -1148,7 +1150,7 @@
/// A buffer type used with `Write::write_vectored`.
///
-/// It is semantically a wrapper around an `&[u8]`, but is guaranteed to be
+/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be
/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on
/// Windows.
#[stable(feature = "iovec", since = "1.36.0")]
@@ -1324,7 +1326,7 @@
/// * The [`write`] method will attempt to write some data into the object,
/// returning how many bytes were successfully written.
///
-/// * The [`flush`] method is useful for adaptors and explicit buffers
+/// * The [`flush`] method is useful for adapters and explicit buffers
/// themselves for ensuring that all buffered data has been pushed out to the
/// 'true sink'.
///
@@ -1367,7 +1369,7 @@
/// Write a buffer into this writer, returning how many bytes were written.
///
/// This function will attempt to write the entire contents of `buf`, but
- /// the entire write may not succeed, or the write may also generate an
+ /// the entire write might not succeed, or the write may also generate an
/// error. A call to `write` represents *at most one* attempt to write to
/// any wrapped object.
///
@@ -1644,12 +1646,12 @@
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> {
// Create a shim which translates a Write to a fmt::Write and saves
// off I/O errors. instead of discarding them
- struct Adaptor<'a, T: ?Sized + 'a> {
+ struct Adapter<'a, T: ?Sized + 'a> {
inner: &'a mut T,
error: Result<()>,
}
- impl<T: Write + ?Sized> fmt::Write for Adaptor<'_, T> {
+ impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
match self.inner.write_all(s.as_bytes()) {
Ok(()) => Ok(()),
@@ -1661,7 +1663,7 @@
}
}
- let mut output = Adaptor { inner: self, error: Ok(()) };
+ let mut output = Adapter { inner: self, error: Ok(()) };
match fmt::write(&mut output, fmt) {
Ok(()) => Ok(()),
Err(..) => {
@@ -1675,9 +1677,9 @@
}
}
- /// Creates a "by reference" adaptor for this instance of `Write`.
+ /// Creates a "by reference" adapter for this instance of `Write`.
///
- /// The returned adaptor also implements `Write` and will simply borrow this
+ /// The returned adapter also implements `Write` and will simply borrow this
/// current writer.
///
/// # Examples
@@ -2261,7 +2263,7 @@
}
}
-/// Adaptor to chain together two readers.
+/// Adapter to chain together two readers.
///
/// This struct is generally created by calling [`chain`] on a reader.
/// Please see the documentation of [`chain`] for more details.
@@ -2412,7 +2414,7 @@
}
}
-/// Reader adaptor which limits the bytes read from an underlying reader.
+/// Reader adapter which limits the bytes read from an underlying reader.
///
/// This struct is generally created by calling [`take`] on a reader.
/// Please see the documentation of [`take`] for more details.
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 65ad7d8..14a6330 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -216,12 +216,12 @@
/// # Examples
///
/// ```no_run
-/// use std::io::{self, Read};
+/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
/// let mut stdin = io::stdin(); // We get `Stdin` here.
-/// stdin.read_to_string(&mut buffer)?;
+/// stdin.read_line(&mut buffer)?;
/// Ok(())
/// }
/// ```
@@ -244,14 +244,14 @@
/// # Examples
///
/// ```no_run
-/// use std::io::{self, Read};
+/// use std::io::{self, BufRead};
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
/// let stdin = io::stdin(); // We get `Stdin` here.
/// {
/// let mut handle = stdin.lock(); // We get `StdinLock` here.
-/// handle.read_to_string(&mut buffer)?;
+/// handle.read_line(&mut buffer)?;
/// } // `StdinLock` is dropped here.
/// Ok(())
/// }
@@ -277,11 +277,11 @@
/// Using implicit synchronization:
///
/// ```no_run
-/// use std::io::{self, Read};
+/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
-/// io::stdin().read_to_string(&mut buffer)?;
+/// io::stdin().read_line(&mut buffer)?;
/// Ok(())
/// }
/// ```
@@ -289,14 +289,14 @@
/// Using explicit synchronization:
///
/// ```no_run
-/// use std::io::{self, Read};
+/// use std::io::{self, BufRead};
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
/// let stdin = io::stdin();
/// let mut handle = stdin.lock();
///
-/// handle.read_to_string(&mut buffer)?;
+/// handle.read_line(&mut buffer)?;
/// Ok(())
/// }
/// ```
@@ -337,13 +337,13 @@
///
/// ```no_run
/// #![feature(stdio_locked)]
-/// use std::io::{self, Read};
+/// use std::io::{self, BufRead};
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
/// let mut handle = io::stdin_locked();
///
-/// handle.read_to_string(&mut buffer)?;
+/// handle.read_line(&mut buffer)?;
/// Ok(())
/// }
/// ```
@@ -363,14 +363,14 @@
/// # Examples
///
/// ```no_run
- /// use std::io::{self, Read};
+ /// use std::io::{self, BufRead};
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
/// let stdin = io::stdin();
/// let mut handle = stdin.lock();
///
- /// handle.read_to_string(&mut buffer)?;
+ /// handle.read_line(&mut buffer)?;
/// Ok(())
/// }
/// ```
@@ -432,13 +432,13 @@
///
/// ```no_run
/// #![feature(stdio_locked)]
- /// use std::io::{self, Read};
+ /// use std::io::{self, BufRead};
///
/// fn main() -> io::Result<()> {
/// let mut buffer = String::new();
/// let mut handle = io::stdin().into_locked();
///
- /// handle.read_to_string(&mut buffer)?;
+ /// handle.read_line(&mut buffer)?;
/// Ok(())
/// }
/// ```
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index fd52de7..a8812f1 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -14,6 +14,7 @@
/// the documentation of [`empty()`] for more details.
#[stable(feature = "rust1", since = "1.0.0")]
#[non_exhaustive]
+#[derive(Copy, Clone, Default)]
pub struct Empty;
/// Constructs a new handle to an empty reader.
@@ -172,6 +173,7 @@
/// see the documentation of [`sink()`] for more details.
#[stable(feature = "rust1", since = "1.0.0")]
#[non_exhaustive]
+#[derive(Copy, Clone, Default)]
pub struct Sink;
/// Creates an instance of a writer which will successfully consume all data.
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index 605bd33..749a441 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -2289,7 +2289,7 @@
/// }
///
/// let mut u = IntOrFloat { f: 1.0 };
-/// // Reading the fields of an union is always unsafe
+/// // Reading the fields of a union is always unsafe
/// assert_eq!(unsafe { u.i }, 1065353216);
/// // Updating through any of the field will modify all of them
/// u.i = 1073741824;
diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs
index ca86e56..5afdb79 100644
--- a/library/std/src/lazy.rs
+++ b/library/std/src/lazy.rs
@@ -86,7 +86,21 @@
impl<T: UnwindSafe> UnwindSafe for SyncOnceCell<T> {}
#[unstable(feature = "once_cell", issue = "74465")]
-impl<T> Default for SyncOnceCell<T> {
+#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
+impl<T> const Default for SyncOnceCell<T> {
+ /// Creates a new empty cell.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// fn main() {
+ /// assert_eq!(SyncOnceCell::<()>::new(), SyncOnceCell::default());
+ /// }
+ /// ```
fn default() -> SyncOnceCell<T> {
SyncOnceCell::new()
}
@@ -118,6 +132,23 @@
#[unstable(feature = "once_cell", issue = "74465")]
impl<T> From<T> for SyncOnceCell<T> {
+ /// Create a new cell with its contents set to `value`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// # fn main() -> Result<(), i32> {
+ /// let a = SyncOnceCell::from(3);
+ /// let b = SyncOnceCell::new();
+ /// b.set(3)?;
+ /// assert_eq!(a, b);
+ /// Ok(())
+ /// # }
+ /// ```
fn from(value: T) -> Self {
let cell = Self::new();
match cell.set(value) {
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index cfbfe7c..3a1eb62 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -235,6 +235,7 @@
#![feature(auto_traits)]
#![feature(bench_black_box)]
#![feature(box_syntax)]
+#![feature(c_unwind)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#![feature(cfg_eval)]
@@ -246,13 +247,16 @@
#![feature(const_cstr_unchecked)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_fn_fn_ptr_basics)]
-#![feature(const_fn_transmute)]
+#![cfg_attr(bootstrap, feature(const_fn_transmute))]
+#![feature(const_format_args)]
#![feature(const_io_structs)]
#![feature(const_ip)]
#![feature(const_ipv4)]
#![feature(const_ipv6)]
+#![feature(const_option)]
#![feature(const_raw_ptr_deref)]
#![feature(const_socketaddr)]
+#![feature(const_trait_impl)]
#![feature(container_error_extra)]
#![feature(core_intrinsics)]
#![feature(custom_test_frameworks)]
@@ -261,6 +265,7 @@
#![feature(doc_keyword)]
#![feature(doc_masked)]
#![feature(doc_notable_trait)]
+#![cfg_attr(not(bootstrap), feature(doc_primitive))]
#![feature(dropck_eyepatch)]
#![feature(duration_checked_float)]
#![feature(duration_constants)]
@@ -304,10 +309,10 @@
#![feature(pin_static_ref)]
#![feature(prelude_import)]
#![feature(ptr_internals)]
-#![feature(ready_macro)]
#![feature(rustc_attrs)]
#![feature(rustc_private)]
-#![feature(shrink_to)]
+#![feature(saturating_div)]
+#![feature(saturating_int_impl)]
#![feature(slice_concat_ext)]
#![feature(slice_internals)]
#![feature(slice_ptr_get)]
@@ -325,9 +330,8 @@
#![feature(trace_macros)]
#![feature(try_blocks)]
#![feature(try_reserve)]
+#![feature(try_reserve_kind)]
#![feature(unboxed_closures)]
-#![feature(unsafe_cell_raw_get)]
-#![feature(unwind_attributes)]
#![feature(unwrap_infallible)]
#![feature(vec_into_raw_parts)]
#![feature(vec_spare_capacity)]
@@ -556,9 +560,9 @@
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[allow(deprecated)]
pub use core::{
- assert, assert_matches, cfg, column, compile_error, concat, concat_idents, env, file,
- format_args, format_args_nl, include, include_bytes, include_str, line, llvm_asm, log_syntax,
- module_path, option_env, stringify, trace_macros,
+ assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args,
+ env, file, format_args, format_args_nl, include, include_bytes, include_str, line, llvm_asm,
+ log_syntax, module_path, option_env, stringify, trace_macros,
};
#[stable(feature = "core_primitive", since = "1.43.0")]
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index 7afe52a..5dc75d3 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -6,8 +6,7 @@
#[doc = include_str!("../../core/src/macros/panic.md")]
#[macro_export]
-#[cfg_attr(bootstrap, rustc_builtin_macro = "std_panic")]
-#[cfg_attr(not(bootstrap), rustc_builtin_macro(std_panic))]
+#[rustc_builtin_macro(std_panic)]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(edition_panic)]
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
@@ -290,7 +289,7 @@
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
// will be malformed.
() => {
- $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
+ $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!())
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr.rs
index 70376d5..43d9306 100644
--- a/library/std/src/net/addr.rs
+++ b/library/std/src/net/addr.rs
@@ -561,8 +561,8 @@
impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr {
/// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`].
///
- /// This conversion creates a [`SocketAddr::V4`] for a [`IpAddr::V4`]
- /// and creates a [`SocketAddr::V6`] for a [`IpAddr::V6`].
+ /// This conversion creates a [`SocketAddr::V4`] for an [`IpAddr::V4`]
+ /// and creates a [`SocketAddr::V6`] for an [`IpAddr::V6`].
///
/// `u16` is treated as port of the newly created [`SocketAddr`].
fn from(pieces: (I, u16)) -> SocketAddr {
@@ -874,7 +874,7 @@
/// Converts this object to an iterator of resolved `SocketAddr`s.
///
- /// The returned iterator may not actually yield any values depending on the
+ /// The returned iterator might not actually yield any values depending on the
/// outcome of any resolution performed.
///
/// Note that this function may block the current thread while resolution is
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
index 4b6d60d..4165a7b 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip.rs
@@ -84,13 +84,59 @@
/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291].
/// They are usually represented as eight 16-bit segments.
///
-/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
-///
/// The size of an `Ipv6Addr` struct may vary depending on the target operating
/// system.
///
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
///
+/// # Embedding IPv4 Addresses
+///
+/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
+///
+/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined:
+/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated.
+///
+/// Both types of addresses are not assigned any special meaning by this implementation,
+/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`,
+/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is.
+/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address.
+///
+/// ### IPv4-Compatible IPv6 Addresses
+///
+/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated.
+/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows:
+///
+/// ```text
+/// | 80 bits | 16 | 32 bits |
+/// +--------------------------------------+--------------------------+
+/// |0000..............................0000|0000| IPv4 address |
+/// +--------------------------------------+----+---------------------+
+/// ```
+/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`.
+///
+/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`].
+/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address.
+///
+/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
+///
+/// ### IPv4-Mapped IPv6 Addresses
+///
+/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2].
+/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows:
+///
+/// ```text
+/// | 80 bits | 16 | 32 bits |
+/// +--------------------------------------+--------------------------+
+/// |0000..............................0000|FFFF| IPv4 address |
+/// +--------------------------------------+----+---------------------+
+/// ```
+/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`.
+///
+/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`].
+/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address.
+///
+/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
+///
/// # Textual representation
///
/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent
@@ -116,16 +162,58 @@
inner: c::in6_addr,
}
-#[allow(missing_docs)]
+/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2].
+///
+/// # Stability Guarantees
+///
+/// Not all possible values for a multicast scope have been assigned.
+/// Future RFCs may introduce new scopes, which will be added as variants to this enum;
+/// because of this the enum is marked as `#[non_exhaustive]`.
+///
+/// # Examples
+/// ```
+/// #![feature(ip)]
+///
+/// use std::net::Ipv6Addr;
+/// use std::net::Ipv6MulticastScope::*;
+///
+/// // An IPv6 multicast address with global scope (`ff0e::`).
+/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0);
+///
+/// // Will print "Global scope".
+/// match address.multicast_scope() {
+/// Some(InterfaceLocal) => println!("Interface-Local scope"),
+/// Some(LinkLocal) => println!("Link-Local scope"),
+/// Some(RealmLocal) => println!("Realm-Local scope"),
+/// Some(AdminLocal) => println!("Admin-Local scope"),
+/// Some(SiteLocal) => println!("Site-Local scope"),
+/// Some(OrganizationLocal) => println!("Organization-Local scope"),
+/// Some(Global) => println!("Global scope"),
+/// Some(_) => println!("Unknown scope"),
+/// None => println!("Not a multicast address!")
+/// }
+///
+/// ```
+///
+/// [IPv6 multicast address]: Ipv6Addr
+/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
#[unstable(feature = "ip", issue = "27709")]
+#[non_exhaustive]
pub enum Ipv6MulticastScope {
+ /// Interface-Local scope.
InterfaceLocal,
+ /// Link-Local scope.
LinkLocal,
+ /// Realm-Local scope.
RealmLocal,
+ /// Admin-Local scope.
AdminLocal,
+ /// Site-Local scope.
SiteLocal,
+ /// Organization-Local scope.
OrganizationLocal,
+ /// Global scope.
Global,
}
@@ -291,6 +379,29 @@
pub const fn is_ipv6(&self) -> bool {
matches!(self, IpAddr::V6(_))
}
+
+ /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it
+ /// return `self` as-is.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ ///
+ /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false);
+ /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true);
+ /// ```
+ #[inline]
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ pub const fn to_canonical(&self) -> IpAddr {
+ match self {
+ &v4 @ IpAddr::V4(_) => v4,
+ IpAddr::V6(v6) => v6.to_canonical(),
+ }
+ }
}
impl Ipv4Addr {
@@ -486,8 +597,7 @@
/// - 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 (see
- /// [`Ipv4Addr::is_ietf_protocol_assignment()`], except
+ /// - 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
@@ -560,7 +670,8 @@
&& !self.is_broadcast()
&& !self.is_documentation()
&& !self.is_shared()
- && !self.is_ietf_protocol_assignment()
+ // 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
@@ -589,40 +700,6 @@
self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
}
- /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to
- /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890].
- ///
- /// Note that parts of this block are in use:
- ///
- /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600])
- /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723])
- /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155])
- ///
- /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890
- /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600
- /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723
- /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(ip)]
- /// use std::net::Ipv4Addr;
- ///
- /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_ietf_protocol_assignment(), true);
- /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).is_ietf_protocol_assignment(), true);
- /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).is_ietf_protocol_assignment(), true);
- /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_ietf_protocol_assignment(), true);
- /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).is_ietf_protocol_assignment(), false);
- /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false);
- /// ```
- #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
- #[unstable(feature = "ip", issue = "27709")]
- #[inline]
- pub const fn is_ietf_protocol_assignment(&self) -> bool {
- self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
- }
-
/// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
/// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
/// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
@@ -758,13 +835,14 @@
}
}
- /// Converts this address to an IPv4-compatible [`IPv6` address].
+ /// Converts this address to an [IPv4-compatible] [`IPv6` address].
///
/// `a.b.c.d` becomes `::a.b.c.d`
///
- /// This isn't typically the method you want; these addresses don't typically
- /// function on modern systems. Use `to_ipv6_mapped` instead.
+ /// Note that IPv4-compatible addresses have been officially deprecated.
+ /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead.
///
+ /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
/// [`IPv6` address]: Ipv6Addr
///
/// # Examples
@@ -787,10 +865,11 @@
}
}
- /// Converts this address to an IPv4-mapped [`IPv6` address].
+ /// Converts this address to an [IPv4-mapped] [`IPv6` address].
///
/// `a.b.c.d` becomes `::ffff:a.b.c.d`
///
+ /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
/// [`IPv6` address]: Ipv6Addr
///
/// # Examples
@@ -1087,7 +1166,7 @@
///
/// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
/// ```
- #[rustc_allow_const_fn_unstable(const_fn_transmute)]
+ #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_fn_transmute))]
#[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
@@ -1149,7 +1228,7 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(),
/// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]);
/// ```
- #[rustc_allow_const_fn_unstable(const_fn_transmute)]
+ #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_fn_transmute))]
#[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
@@ -1193,11 +1272,13 @@
u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
}
- /// Returns [`true`] if this is a loopback address (::1).
+ /// Returns [`true`] if this is the [loopback address] (`::1`),
+ /// as defined in [IETF RFC 4291 section 2.5.3].
///
- /// This property is defined in [IETF RFC 4291].
+ /// Contrary to IPv4, in IPv6 there is only one loopback address.
///
- /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
+ /// [loopback address]: Ipv6Addr::LOCALHOST
+ /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
///
/// # Examples
///
@@ -1468,13 +1549,14 @@
(self.segments()[0] & 0xff00) == 0xff00
}
- /// Converts this address to an [`IPv4` address] if it's an "IPv4-mapped IPv6 address"
- /// defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
+ /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address,
+ /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
///
/// `::ffff:a.b.c.d` becomes `a.b.c.d`.
/// All addresses *not* starting with `::ffff` will return `None`.
///
/// [`IPv4` address]: Ipv4Addr
+ /// [IPv4-mapped]: Ipv6Addr
/// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
///
/// # Examples
@@ -1501,12 +1583,19 @@
}
}
- /// Converts this address to an [`IPv4` address]. Returns [`None`] if this address is
- /// neither IPv4-compatible or IPv4-mapped.
+ /// Converts this address to an [`IPv4` address] if it is either
+ /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1],
+ /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2],
+ /// otherwise returns [`None`].
///
/// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`
+ /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`.
///
- /// [`IPv4` address]: Ipv4Addr
+ /// [IPv4 address]: Ipv4Addr
+ /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses
+ /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses
+ /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1
+ /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
///
/// # Examples
///
@@ -1532,6 +1621,28 @@
}
}
+ /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it
+ /// returns self wrapped in an `IpAddr::V6`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(ip)]
+ /// use std::net::Ipv6Addr;
+ ///
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false);
+ /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true);
+ /// ```
+ #[inline]
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ #[unstable(feature = "ip", issue = "27709")]
+ pub const fn to_canonical(&self) -> IpAddr {
+ if let Some(mapped) = self.to_ipv4_mapped() {
+ return IpAddr::V4(mapped);
+ }
+ IpAddr::V6(*self)
+ }
+
/// Returns the sixteen eight-bit integers the IPv6 address consists of.
///
/// ```
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs
index 2109980..dbfab9d 100644
--- a/library/std/src/net/ip/tests.rs
+++ b/library/std/src/net/ip/tests.rs
@@ -339,7 +339,6 @@
let broadcast: u16 = 1 << 6;
let documentation: u16 = 1 << 7;
let benchmarking: u16 = 1 << 8;
- let ietf_protocol_assignment: u16 = 1 << 9;
let reserved: u16 = 1 << 10;
let shared: u16 = 1 << 11;
@@ -397,12 +396,6 @@
assert!(!ip!($s).is_benchmarking());
}
- if ($mask & ietf_protocol_assignment) == ietf_protocol_assignment {
- assert!(ip!($s).is_ietf_protocol_assignment());
- } else {
- assert!(!ip!($s).is_ietf_protocol_assignment());
- }
-
if ($mask & reserved) == reserved {
assert!(ip!($s).is_reserved());
} else {
@@ -426,7 +419,6 @@
let broadcast: u16 = 1 << 6;
let documentation: u16 = 1 << 7;
let benchmarking: u16 = 1 << 8;
- let ietf_protocol_assignment: u16 = 1 << 9;
let reserved: u16 = 1 << 10;
let shared: u16 = 1 << 11;
@@ -449,9 +441,9 @@
check!("198.18.0.0", benchmarking);
check!("198.18.54.2", benchmarking);
check!("198.19.255.255", benchmarking);
- check!("192.0.0.0", ietf_protocol_assignment);
- check!("192.0.0.255", ietf_protocol_assignment);
- check!("192.0.0.100", ietf_protocol_assignment);
+ check!("192.0.0.0");
+ check!("192.0.0.255");
+ check!("192.0.0.100");
check!("240.0.0.0", reserved);
check!("251.54.1.76", reserved);
check!("254.255.255.255", reserved);
@@ -823,9 +815,6 @@
const IS_SHARED: bool = IP_ADDRESS.is_shared();
assert!(!IS_SHARED);
- const IS_IETF_PROTOCOL_ASSIGNMENT: bool = IP_ADDRESS.is_ietf_protocol_assignment();
- assert!(!IS_IETF_PROTOCOL_ASSIGNMENT);
-
const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
assert!(!IS_BENCHMARKING);
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index 325acf0..5b4a9fa 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -401,6 +401,53 @@
self.0.peek(buf)
}
+ /// Sets the value of the `SO_LINGER` option on this socket.
+ ///
+ /// This value controls how the socket is closed when data remains
+ /// to be sent. If `SO_LINGER` is set, the socket will remain open
+ /// for the specified duration as the system attempts to send pending data.
+ /// Otherwise, the system may close the socket immediately, or wait for a
+ /// default timeout.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(tcp_linger)]
+ ///
+ /// use std::net::TcpStream;
+ /// use std::time::Duration;
+ ///
+ /// let stream = TcpStream::connect("127.0.0.1:8080")
+ /// .expect("Couldn't connect to the server...");
+ /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed");
+ /// ```
+ #[unstable(feature = "tcp_linger", issue = "88494")]
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ self.0.set_linger(linger)
+ }
+
+ /// Gets the value of the `SO_LINGER` option on this socket.
+ ///
+ /// For more information about this option, see [`TcpStream::set_linger`].
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(tcp_linger)]
+ ///
+ /// use std::net::TcpStream;
+ /// use std::time::Duration;
+ ///
+ /// let stream = TcpStream::connect("127.0.0.1:8080")
+ /// .expect("Couldn't connect to the server...");
+ /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed");
+ /// assert_eq!(stream.linger().unwrap(), Some(Duration::from_secs(0)));
+ /// ```
+ #[unstable(feature = "tcp_linger", issue = "88494")]
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ self.0.linger()
+ }
+
/// Sets the value of the `TCP_NODELAY` option on this socket.
///
/// If set, this option disables the Nagle algorithm. This means that
@@ -546,6 +593,12 @@
}
}
+// In addition to the `impl`s here, `TcpStream` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and
+// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows.
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Read for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -767,17 +820,24 @@
/// # Examples
///
/// ```no_run
- /// use std::net::TcpListener;
+ /// use std::net::{TcpListener, TcpStream};
///
- /// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
+ /// fn handle_connection(stream: TcpStream) {
+ /// //...
+ /// }
///
- /// for stream in listener.incoming() {
- /// match stream {
- /// Ok(stream) => {
- /// println!("new client!");
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
+ ///
+ /// for stream in listener.incoming() {
+ /// match stream {
+ /// Ok(stream) => {
+ /// handle_connection(stream);
+ /// }
+ /// Err(e) => { /* connection failed */ }
/// }
- /// Err(e) => { /* connection failed */ }
/// }
+ /// Ok(())
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
@@ -908,6 +968,12 @@
}
}
+// In addition to the `impl`s here, `TcpListener` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and
+// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows.
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Iterator for Incoming<'a> {
type Item = io::Result<TcpStream>;
diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs
index 387a361..c2061c1 100644
--- a/library/std/src/net/tcp/tests.rs
+++ b/library/std/src/net/tcp/tests.rs
@@ -769,6 +769,21 @@
#[test]
#[cfg_attr(target_env = "sgx", ignore)]
+fn linger() {
+ let addr = next_test_ip4();
+ let _listener = t!(TcpListener::bind(&addr));
+
+ let stream = t!(TcpStream::connect(&("localhost", addr.port())));
+
+ assert_eq!(None, t!(stream.linger()));
+ t!(stream.set_linger(Some(Duration::from_secs(1))));
+ assert_eq!(Some(Duration::from_secs(1)), t!(stream.linger()));
+ t!(stream.set_linger(None));
+ assert_eq!(None, t!(stream.linger()));
+}
+
+#[test]
+#[cfg_attr(target_env = "sgx", ignore)]
fn nodelay() {
let addr = next_test_ip4();
let _listener = t!(TcpListener::bind(&addr));
diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs
index 1829713..6354752 100644
--- a/library/std/src/net/udp.rs
+++ b/library/std/src/net/udp.rs
@@ -39,7 +39,7 @@
///
/// fn main() -> std::io::Result<()> {
/// {
-/// let mut socket = UdpSocket::bind("127.0.0.1:34254")?;
+/// let socket = UdpSocket::bind("127.0.0.1:34254")?;
///
/// // Receives a single datagram message on the socket. If `buf` is too small to hold
/// // the message, it will be cut off.
@@ -408,7 +408,7 @@
/// Sets the value of the `IP_MULTICAST_LOOP` option for this socket.
///
/// If enabled, multicast packets will be looped back to the local socket.
- /// Note that this may not have any effect on IPv6 sockets.
+ /// Note that this might not have any effect on IPv6 sockets.
///
/// # Examples
///
@@ -447,7 +447,7 @@
/// this socket. The default value is 1 which means that multicast packets
/// don't leave the local network unless explicitly requested.
///
- /// Note that this may not have any effect on IPv6 sockets.
+ /// Note that this might not have any effect on IPv6 sockets.
///
/// # Examples
///
@@ -483,7 +483,7 @@
/// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
///
/// Controls whether this socket sees the multicast packets it sends itself.
- /// Note that this may not have any affect on IPv4 sockets.
+ /// Note that this might not have any affect on IPv4 sockets.
///
/// # Examples
///
@@ -779,6 +779,12 @@
}
}
+// In addition to the `impl`s here, `UdpSocket` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and
+// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows.
+
impl AsInner<net_imp::UdpSocket> for UdpSocket {
fn as_inner(&self) -> &net_imp::UdpSocket {
&self.0
diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs
index fbed3d3..a51113d 100644
--- a/library/std/src/net/udp/tests.rs
+++ b/library/std/src/net/udp/tests.rs
@@ -2,7 +2,6 @@
use crate::net::test::{next_test_ip4, next_test_ip6};
use crate::net::*;
use crate::sync::mpsc::channel;
-use crate::sys_common::AsInner;
use crate::thread;
use crate::time::{Duration, Instant};
@@ -173,7 +172,7 @@
let socket_addr = next_test_ip4();
let udpsock = t!(UdpSocket::bind(&socket_addr));
- let udpsock_inner = udpsock.0.socket().as_inner();
+ let udpsock_inner = udpsock.0.socket().as_raw();
let compare = format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner);
assert_eq!(format!("{:?}", udpsock), compare);
}
diff --git a/library/std/src/num.rs b/library/std/src/num.rs
index e7051f0..46064bd 100644
--- a/library/std/src/num.rs
+++ b/library/std/src/num.rs
@@ -12,6 +12,8 @@
#[cfg(test)]
mod benches;
+#[unstable(feature = "saturating_int_impl", issue = "87920")]
+pub use core::num::Saturating;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::num::Wrapping;
#[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/os/espidf/fs.rs b/library/std/src/os/espidf/fs.rs
new file mode 100644
index 0000000..93dc2c0
--- /dev/null
+++ b/library/std/src/os/espidf/fs.rs
@@ -0,0 +1,117 @@
+#![stable(feature = "metadata_ext", since = "1.1.0")]
+
+use crate::fs::Metadata;
+use crate::sys_common::AsInner;
+
+#[allow(deprecated)]
+use crate::os::espidf::raw;
+
+/// OS-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: crate::fs::Metadata
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ #[rustc_deprecated(
+ since = "1.8.0",
+ reason = "deprecated in favor of the accessor \
+ methods of this trait"
+ )]
+ #[allow(deprecated)]
+ fn as_raw_stat(&self) -> &raw::stat;
+
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_dev(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ino(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mode(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_nlink(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_uid(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_gid(&self) -> u32;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_rdev(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_size(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime_nsec(&self) -> i64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blksize(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blocks(&self) -> u64;
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_spare4(&self) -> [u32; 2];
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for Metadata {
+ #[allow(deprecated)]
+ fn as_raw_stat(&self) -> &raw::stat {
+ unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) }
+ }
+ fn st_dev(&self) -> u64 {
+ self.as_inner().as_inner().st_dev as u64
+ }
+ fn st_ino(&self) -> u64 {
+ self.as_inner().as_inner().st_ino as u64
+ }
+ fn st_mode(&self) -> u32 {
+ self.as_inner().as_inner().st_mode as u32
+ }
+ fn st_nlink(&self) -> u64 {
+ self.as_inner().as_inner().st_nlink as u64
+ }
+ fn st_uid(&self) -> u32 {
+ self.as_inner().as_inner().st_uid as u32
+ }
+ fn st_gid(&self) -> u32 {
+ self.as_inner().as_inner().st_gid as u32
+ }
+ fn st_rdev(&self) -> u64 {
+ self.as_inner().as_inner().st_rdev as u64
+ }
+ fn st_size(&self) -> u64 {
+ self.as_inner().as_inner().st_size as u64
+ }
+ fn st_atime(&self) -> i64 {
+ self.as_inner().as_inner().st_atime as i64
+ }
+ fn st_atime_nsec(&self) -> i64 {
+ 0
+ }
+ fn st_mtime(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime as i64
+ }
+ fn st_mtime_nsec(&self) -> i64 {
+ 0
+ }
+ fn st_ctime(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime as i64
+ }
+ fn st_ctime_nsec(&self) -> i64 {
+ 0
+ }
+ fn st_blksize(&self) -> u64 {
+ self.as_inner().as_inner().st_blksize as u64
+ }
+ fn st_blocks(&self) -> u64 {
+ self.as_inner().as_inner().st_blocks as u64
+ }
+ fn st_spare4(&self) -> [u32; 2] {
+ let spare4 = self.as_inner().as_inner().st_spare4;
+ [spare4[0] as u32, spare4[1] as u32]
+ }
+}
diff --git a/library/std/src/os/espidf/mod.rs b/library/std/src/os/espidf/mod.rs
new file mode 100644
index 0000000..a9cef97
--- /dev/null
+++ b/library/std/src/os/espidf/mod.rs
@@ -0,0 +1,6 @@
+//! Definitions for the ESP-IDF framework.
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+
+pub mod fs;
+pub mod raw;
diff --git a/library/std/src/os/espidf/raw.rs b/library/std/src/os/espidf/raw.rs
new file mode 100644
index 0000000..fb18ec6
--- /dev/null
+++ b/library/std/src/os/espidf/raw.rs
@@ -0,0 +1,69 @@
+//! Raw type definitions for the ESP-IDF framework.
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+#![rustc_deprecated(
+ since = "1.8.0",
+ reason = "these type aliases are no longer supported by \
+ the standard library, the `libc` crate on \
+ crates.io should be used instead for the correct \
+ definitions"
+)]
+
+use crate::os::raw::c_long;
+use crate::os::unix::raw::{gid_t, uid_t};
+
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = libc::pthread_t;
+
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type blkcnt_t = libc::blkcnt_t;
+
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type blksize_t = libc::blksize_t;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type dev_t = libc::dev_t;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type ino_t = libc::ino_t;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type mode_t = libc::mode_t;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type nlink_t = libc::nlink_t;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type off_t = libc::off_t;
+
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type time_t = libc::time_t;
+
+#[repr(C)]
+#[derive(Clone)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub struct stat {
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_dev: dev_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ino: ino_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mode: mode_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_nlink: nlink_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_uid: uid_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_gid: gid_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_rdev: dev_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_size: off_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_atime: time_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_mtime: time_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_ctime: time_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blksize: blksize_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_blocks: blkcnt_t,
+ #[stable(feature = "raw_ext", since = "1.1.0")]
+ pub st_spare4: [c_long; 2usize],
+}
diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs
new file mode 100644
index 0000000..df11dc2
--- /dev/null
+++ b/library/std/src/os/fd/mod.rs
@@ -0,0 +1,13 @@
+//! Owned and borrowed Unix-like file descriptors.
+
+#![unstable(feature = "io_safety", issue = "87074")]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+// `RawFd`, `AsRawFd`, etc.
+pub mod raw;
+
+// `OwnedFd`, `AsFd`, etc.
+pub mod owned;
+
+// Implementations for `AsRawFd` etc. for network types.
+mod net;
diff --git a/library/std/src/os/unix/net/raw_fd.rs b/library/std/src/os/fd/net.rs
similarity index 67%
rename from library/std/src/os/unix/net/raw_fd.rs
rename to library/std/src/os/fd/net.rs
index b3f1284..843f45f 100644
--- a/library/std/src/os/unix/net/raw_fd.rs
+++ b/library/std/src/os/fd/net.rs
@@ -1,4 +1,5 @@
-use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::os::fd::owned::OwnedFd;
+use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
use crate::{net, sys};
@@ -8,7 +9,7 @@
impl AsRawFd for net::$t {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- *self.as_inner().socket().as_inner()
+ self.as_inner().socket().as_raw_fd()
}
}
)*};
@@ -21,8 +22,10 @@
impl FromRawFd for net::$t {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
- let socket = sys::net::Socket::from_inner(fd);
- net::$t::from_inner(sys_common::net::$t::from_inner(socket))
+ unsafe {
+ let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)));
+ net::$t::from_inner(sys_common::net::$t::from_inner(socket))
+ }
}
}
)*};
@@ -35,7 +38,7 @@
impl IntoRawFd for net::$t {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_socket().into_inner()
+ self.into_inner().into_socket().into_inner().into_inner().into_raw_fd()
}
}
)*};
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
new file mode 100644
index 0000000..52d7d46
--- /dev/null
+++ b/library/std/src/os/fd/owned.rs
@@ -0,0 +1,289 @@
+//! Owned and borrowed Unix-like file descriptors.
+
+#![unstable(feature = "io_safety", issue = "87074")]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::fmt;
+use crate::fs;
+use crate::marker::PhantomData;
+use crate::mem::forget;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+/// A borrowed file descriptor.
+///
+/// This has a lifetime parameter to tie it to the lifetime of something that
+/// owns the file descriptor.
+///
+/// This uses `repr(transparent)` and has the representation of a host file
+/// descriptor, so it can be used in FFI in places where a file descriptor is
+/// passed as an argument, it is not captured or consumed, and it never has the
+/// value `-1`.
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+#[unstable(feature = "io_safety", issue = "87074")]
+pub struct BorrowedFd<'fd> {
+ fd: RawFd,
+ _phantom: PhantomData<&'fd OwnedFd>,
+}
+
+/// An owned file descriptor.
+///
+/// This closes the file descriptor on drop.
+///
+/// This uses `repr(transparent)` and has the representation of a host file
+/// descriptor, so it can be used in FFI in places where a file descriptor is
+/// passed as a consumed argument or returned as an owned value, and it never
+/// has the value `-1`.
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+#[unstable(feature = "io_safety", issue = "87074")]
+pub struct OwnedFd {
+ fd: RawFd,
+}
+
+impl BorrowedFd<'_> {
+ /// Return a `BorrowedFd` holding the given raw file descriptor.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `fd` must remain open for the duration of
+ /// the returned `BorrowedFd`, and it must not have the value `-1`.
+ #[inline]
+ #[unstable(feature = "io_safety", issue = "87074")]
+ pub unsafe fn borrow_raw_fd(fd: RawFd) -> Self {
+ assert_ne!(fd, u32::MAX as RawFd);
+ // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+ unsafe { Self { fd, _phantom: PhantomData } }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsRawFd for BorrowedFd<'_> {
+ #[inline]
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsRawFd for OwnedFd {
+ #[inline]
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl IntoRawFd for OwnedFd {
+ #[inline]
+ fn into_raw_fd(self) -> RawFd {
+ let fd = self.fd;
+ forget(self);
+ fd
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl FromRawFd for OwnedFd {
+ /// Constructs a new instance of `Self` from the given raw file descriptor.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `fd` must be open and suitable for assuming
+ /// ownership. The resource must not require any cleanup other than `close`.
+ #[inline]
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ assert_ne!(fd, u32::MAX as RawFd);
+ // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+ unsafe { Self { fd } }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl Drop for OwnedFd {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ // Note that errors are ignored when closing a file descriptor. The
+ // reason for this is that if an error occurs we don't actually know if
+ // the file descriptor was closed or not, and if we retried (for
+ // something like EINTR), we might close another valid file descriptor
+ // opened after we closed ours.
+ let _ = libc::close(self.fd);
+ }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl fmt::Debug for BorrowedFd<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BorrowedFd").field("fd", &self.fd).finish()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl fmt::Debug for OwnedFd {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("OwnedFd").field("fd", &self.fd).finish()
+ }
+}
+
+/// A trait to borrow the file descriptor from an underlying object.
+///
+/// This is only available on unix platforms and must be imported in order to
+/// call the method. Windows platforms have a corresponding `AsHandle` and
+/// `AsSocket` set of traits.
+#[unstable(feature = "io_safety", issue = "87074")]
+pub trait AsFd {
+ /// Borrows the file descriptor.
+ ///
+ /// # Example
+ ///
+ /// ```rust,no_run
+ /// # #![feature(io_safety)]
+ /// use std::fs::File;
+ /// # use std::io;
+ /// # #[cfg(target_os = "wasi")]
+ /// # use std::os::wasi::io::{AsFd, BorrowedFd};
+ /// # #[cfg(unix)]
+ /// # use std::os::unix::io::{AsFd, BorrowedFd};
+ ///
+ /// let mut f = File::open("foo.txt")?;
+ /// # #[cfg(any(unix, target_os = "wasi"))]
+ /// let borrowed_fd: BorrowedFd<'_> = f.as_fd();
+ /// # Ok::<(), io::Error>(())
+ /// ```
+ #[unstable(feature = "io_safety", issue = "87074")]
+ fn as_fd(&self) -> BorrowedFd<'_>;
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for BorrowedFd<'_> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ *self
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for OwnedFd {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ // Safety: `OwnedFd` and `BorrowedFd` have the same validity
+ // invariants, and the `BorrowdFd` is bounded by the lifetime
+ // of `&self`.
+ unsafe { BorrowedFd::borrow_raw_fd(self.as_raw_fd()) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for fs::File {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<fs::File> for OwnedFd {
+ #[inline]
+ fn from(file: fs::File) -> OwnedFd {
+ file.into_inner().into_inner().into_inner()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for fs::File {
+ #[inline]
+ fn from(owned_fd: OwnedFd) -> Self {
+ Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd)))
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for crate::net::TcpStream {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().socket().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<crate::net::TcpStream> for OwnedFd {
+ #[inline]
+ fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd {
+ tcp_stream.into_inner().into_socket().into_inner().into_inner().into()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for crate::net::TcpStream {
+ #[inline]
+ fn from(owned_fd: OwnedFd) -> Self {
+ Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
+ owned_fd,
+ ))))
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for crate::net::TcpListener {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().socket().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<crate::net::TcpListener> for OwnedFd {
+ #[inline]
+ fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd {
+ tcp_listener.into_inner().into_socket().into_inner().into_inner().into()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for crate::net::TcpListener {
+ #[inline]
+ fn from(owned_fd: OwnedFd) -> Self {
+ Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
+ owned_fd,
+ ))))
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for crate::net::UdpSocket {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().socket().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<crate::net::UdpSocket> for OwnedFd {
+ #[inline]
+ fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd {
+ udp_socket.into_inner().into_socket().into_inner().into_inner().into()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for crate::net::UdpSocket {
+ #[inline]
+ fn from(owned_fd: OwnedFd) -> Self {
+ Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
+ owned_fd,
+ ))))
+ }
+}
diff --git a/library/std/src/os/unix/io.rs b/library/std/src/os/fd/raw.rs
similarity index 81%
rename from library/std/src/os/unix/io.rs
rename to library/std/src/os/fd/raw.rs
index 07c30bf..f874cf0 100644
--- a/library/std/src/os/unix/io.rs
+++ b/library/std/src/os/fd/raw.rs
@@ -1,23 +1,25 @@
-//! Unix-specific extensions to general I/O primitives.
+//! Raw Unix-like file descriptors.
#![stable(feature = "rust1", since = "1.0.0")]
use crate::fs;
use crate::io;
use crate::os::raw;
-use crate::sys;
-use crate::sys_common::{AsInner, FromInner, IntoInner};
+#[cfg(unix)]
+use crate::os::unix::io::OwnedFd;
+#[cfg(target_os = "wasi")]
+use crate::os::wasi::io::OwnedFd;
+use crate::sys_common::{AsInner, IntoInner};
/// Raw file descriptors.
#[stable(feature = "rust1", since = "1.0.0")]
pub type RawFd = raw::c_int;
-/// A trait to extract the raw unix file descriptor from an underlying
-/// object.
+/// A trait to extract the raw file descriptor from an underlying object.
///
-/// This is only available on unix platforms and must be imported in order
-/// to call the method. Windows platforms have a corresponding `AsRawHandle`
-/// and `AsRawSocket` set of traits.
+/// 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.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRawFd {
/// Extracts the raw file descriptor.
@@ -31,10 +33,14 @@
/// ```no_run
/// use std::fs::File;
/// # use std::io;
+ /// #[cfg(unix)]
/// use std::os::unix::io::{AsRawFd, RawFd};
+ /// #[cfg(target_os = "wasi")]
+ /// use std::os::wasi::io::{AsRawFd, RawFd};
///
/// let mut f = File::open("foo.txt")?;
/// // Note that `raw_fd` is only valid as long as `f` exists.
+ /// #[cfg(any(unix, target_os = "wasi"))]
/// let raw_fd: RawFd = f.as_raw_fd();
/// # Ok::<(), io::Error>(())
/// ```
@@ -64,12 +70,17 @@
/// ```no_run
/// use std::fs::File;
/// # use std::io;
+ /// #[cfg(unix)]
/// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
+ /// #[cfg(target_os = "wasi")]
+ /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd};
///
/// let f = File::open("foo.txt")?;
+ /// # #[cfg(any(unix, target_os = "wasi"))]
/// let raw_fd: RawFd = f.into_raw_fd();
/// // SAFETY: no other functions should call `from_raw_fd`, so there
/// // is only one owner for the file descriptor.
+ /// # #[cfg(any(unix, target_os = "wasi"))]
/// let f = unsafe { File::from_raw_fd(raw_fd) };
/// # Ok::<(), io::Error>(())
/// ```
@@ -92,9 +103,13 @@
/// ```no_run
/// use std::fs::File;
/// # use std::io;
+ /// #[cfg(unix)]
/// use std::os::unix::io::{IntoRawFd, RawFd};
+ /// #[cfg(target_os = "wasi")]
+ /// use std::os::wasi::io::{IntoRawFd, RawFd};
///
/// let f = File::open("foo.txt")?;
+ /// #[cfg(any(unix, target_os = "wasi"))]
/// let raw_fd: RawFd = f.into_raw_fd();
/// # Ok::<(), io::Error>(())
/// ```
@@ -128,21 +143,21 @@
impl AsRawFd for fs::File {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().raw()
+ self.as_inner().as_raw_fd()
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawFd for fs::File {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
- fs::File::from_inner(sys::fs::File::from_inner(fd))
+ unsafe { fs::File::from(OwnedFd::from_raw_fd(fd)) }
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for fs::File {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
+ self.into_inner().into_inner().into_raw_fd()
}
}
diff --git a/library/std/src/os/linux/mod.rs b/library/std/src/os/linux/mod.rs
index 94438de..8e7776f 100644
--- a/library/std/src/os/linux/mod.rs
+++ b/library/std/src/os/linux/mod.rs
@@ -4,4 +4,5 @@
#![doc(cfg(target_os = "linux"))]
pub mod fs;
+pub mod process;
pub mod raw;
diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs
new file mode 100644
index 0000000..e3e7143
--- /dev/null
+++ b/library/std/src/os/linux/process.rs
@@ -0,0 +1,163 @@
+//! Linux-specific extensions to primitives in the `std::process` module.
+
+#![unstable(feature = "linux_pidfd", issue = "82971")]
+
+use crate::io::Result;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::process;
+use crate::sealed::Sealed;
+#[cfg(not(doc))]
+use crate::sys::fd::FileDesc;
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+
+#[cfg(doc)]
+struct FileDesc;
+
+/// This type represents a file descriptor that refers to a process.
+///
+/// A `PidFd` can be obtained by setting the corresponding option on [`Command`]
+/// with [`create_pidfd`]. Subsequently, the created pidfd can be retrieved
+/// from the [`Child`] by calling [`pidfd`] or [`take_pidfd`].
+///
+/// Example:
+/// ```no_run
+/// #![feature(linux_pidfd)]
+/// use std::os::linux::process::{CommandExt, ChildExt};
+/// use std::process::Command;
+///
+/// let mut child = Command::new("echo")
+/// .create_pidfd(true)
+/// .spawn()
+/// .expect("Failed to spawn child");
+///
+/// let pidfd = child
+/// .take_pidfd()
+/// .expect("Failed to retrieve pidfd");
+///
+/// // The file descriptor will be closed when `pidfd` is dropped.
+/// ```
+/// Refer to the man page of [`pidfd_open(2)`] for further details.
+///
+/// [`Command`]: process::Command
+/// [`create_pidfd`]: CommandExt::create_pidfd
+/// [`Child`]: process::Child
+/// [`pidfd`]: fn@ChildExt::pidfd
+/// [`take_pidfd`]: ChildExt::take_pidfd
+/// [`pidfd_open(2)`]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html
+#[derive(Debug)]
+pub struct PidFd {
+ inner: FileDesc,
+}
+
+impl AsInner<FileDesc> for PidFd {
+ fn as_inner(&self) -> &FileDesc {
+ &self.inner
+ }
+}
+
+impl FromInner<FileDesc> for PidFd {
+ fn from_inner(inner: FileDesc) -> PidFd {
+ PidFd { inner }
+ }
+}
+
+impl IntoInner<FileDesc> for PidFd {
+ fn into_inner(self) -> FileDesc {
+ self.inner
+ }
+}
+
+impl AsRawFd for PidFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().as_raw_fd()
+ }
+}
+
+impl FromRawFd for PidFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Self::from_inner(FileDesc::from_raw_fd(fd))
+ }
+}
+
+impl IntoRawFd for PidFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_raw_fd()
+ }
+}
+
+impl AsFd for PidFd {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().as_fd()
+ }
+}
+
+impl From<OwnedFd> for PidFd {
+ fn from(fd: OwnedFd) -> Self {
+ Self::from_inner(FileDesc::from_inner(fd))
+ }
+}
+
+impl From<PidFd> for OwnedFd {
+ fn from(pid_fd: PidFd) -> Self {
+ pid_fd.into_inner().into_inner()
+ }
+}
+
+/// Os-specific extensions for [`Child`]
+///
+/// [`Child`]: process::Child
+pub trait ChildExt: Sealed {
+ /// Obtains a reference to the [`PidFd`] created for this [`Child`], if available.
+ ///
+ /// A pidfd will only be available if its creation was requested with
+ /// [`create_pidfd`] when the corresponding [`Command`] was created.
+ ///
+ /// Even if requested, a pidfd may not be available due to an older
+ /// version of Linux being in use, or if some other error occurred.
+ ///
+ /// [`Command`]: process::Command
+ /// [`create_pidfd`]: CommandExt::create_pidfd
+ /// [`Child`]: process::Child
+ fn pidfd(&self) -> Result<&PidFd>;
+
+ /// Takes ownership of the [`PidFd`] created for this [`Child`], if available.
+ ///
+ /// A pidfd will only be available if its creation was requested with
+ /// [`create_pidfd`] when the corresponding [`Command`] was created.
+ ///
+ /// Even if requested, a pidfd may not be available due to an older
+ /// version of Linux being in use, or if some other error occurred.
+ ///
+ /// [`Command`]: process::Command
+ /// [`create_pidfd`]: CommandExt::create_pidfd
+ /// [`Child`]: process::Child
+ fn take_pidfd(&mut self) -> Result<PidFd>;
+}
+
+/// Os-specific extensions for [`Command`]
+///
+/// [`Command`]: process::Command
+pub trait CommandExt: Sealed {
+ /// Sets whether a [`PidFd`](struct@PidFd) should be created for the [`Child`]
+ /// spawned by this [`Command`].
+ /// By default, no pidfd will be created.
+ ///
+ /// The pidfd can be retrieved from the child with [`pidfd`] or [`take_pidfd`].
+ ///
+ /// A pidfd will only be created if it is possible to do so
+ /// in a guaranteed race-free manner (e.g. if the `clone3` system call
+ /// is supported). Otherwise, [`pidfd`] will return an error.
+ ///
+ /// [`Command`]: process::Command
+ /// [`Child`]: process::Child
+ /// [`pidfd`]: fn@ChildExt::pidfd
+ /// [`take_pidfd`]: ChildExt::take_pidfd
+ fn create_pidfd(&mut self, val: bool) -> &mut process::Command;
+}
+
+impl CommandExt for process::Command {
+ fn create_pidfd(&mut self, val: bool) -> &mut process::Command {
+ self.as_inner_mut().create_pidfd(val);
+ self
+ }
+}
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 07e29eb..79e6967 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -80,6 +80,8 @@
pub mod dragonfly;
#[cfg(target_os = "emscripten")]
pub mod emscripten;
+ #[cfg(target_os = "espidf")]
+ pub mod espidf;
#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "fuchsia")]
@@ -119,3 +121,6 @@
#[cfg(not(doc))]
#[stable(feature = "os", since = "1.0.0")]
pub use imp::*;
+
+#[cfg(any(unix, target_os = "wasi", doc))]
+mod fd;
diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs
index 50464a0..1e220ea 100644
--- a/library/std/src/os/raw/mod.rs
+++ b/library/std/src/os/raw/mod.rs
@@ -151,3 +151,17 @@
#[stable(feature = "raw_os", since = "1.1.0")]
#[doc(no_inline)]
pub use core::ffi::c_void;
+
+/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++).
+///
+/// This type is currently always [`usize`], however in the future there may be
+/// platforms where this is not the case.
+#[unstable(feature = "c_size_t", issue = "88345")]
+pub type c_size_t = usize;
+
+/// Equivalent to C's `ssize_t` type, from `stddef.h` (or `cstddef` for C++).
+///
+/// This type is currently always [`isize`], however in the future there may be
+/// platforms where this is not the case.
+#[unstable(feature = "c_size_t", issue = "88345")]
+pub type c_ssize_t = isize;
diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs
index e4ce788..6cf37f2 100644
--- a/library/std/src/os/unix/fs.rs
+++ b/library/std/src/os/unix/fs.rs
@@ -934,7 +934,6 @@
/// # Examples
///
/// ```no_run
-/// #![feature(unix_chroot)]
/// use std::os::unix::fs;
///
/// fn main() -> std::io::Result<()> {
@@ -944,7 +943,7 @@
/// Ok(())
/// }
/// ```
-#[unstable(feature = "unix_chroot", issue = "84715")]
+#[stable(feature = "unix_chroot", since = "1.56.0")]
#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
pub fn chroot<P: AsRef<Path>>(dir: P) -> io::Result<()> {
sys::fs::chroot(dir.as_ref())
diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs
new file mode 100644
index 0000000..7795db7
--- /dev/null
+++ b/library/std/src/os/unix/io/fd.rs
@@ -0,0 +1,9 @@
+//! Owned and borrowed file descriptors.
+
+#![unstable(feature = "io_safety", issue = "87074")]
+
+// Tests for this module
+#[cfg(test)]
+mod tests;
+
+pub use crate::os::fd::owned::*;
diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/fd/tests.rs
new file mode 100644
index 0000000..84d2a7a
--- /dev/null
+++ b/library/std/src/os/unix/io/fd/tests.rs
@@ -0,0 +1,11 @@
+use crate::mem::size_of;
+use crate::os::unix::io::RawFd;
+
+#[test]
+fn test_raw_fd_layout() {
+ // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start`
+ // and `rustc_layout_scalar_valid_range_end`, with values that depend on
+ // the bit width of `RawFd`. If this ever changes, those values will need
+ // to be updated.
+ assert_eq!(size_of::<RawFd>(), 4);
+}
diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs
new file mode 100644
index 0000000..0fd9591
--- /dev/null
+++ b/library/std/src/os/unix/io/mod.rs
@@ -0,0 +1,57 @@
+//! Unix-specific extensions to general I/O primitives.
+//!
+//! Just like raw pointers, raw file descriptors point to resources with
+//! dynamic lifetimes, and they can dangle if they outlive their resources
+//! or be forged if they're created from invalid values.
+//!
+//! This module provides three types for representing file descriptors,
+//! with different ownership properties: raw, borrowed, and owned, which are
+//! analogous to types used for representing pointers:
+//!
+//! | Type | Analogous to |
+//! | ------------------ | ------------ |
+//! | [`RawFd`] | `*const _` |
+//! | [`BorrowedFd<'a>`] | `&'a _` |
+//! | [`OwnedFd`] | `Box<_>` |
+//!
+//! Like raw pointers, `RawFd` values are primitive values. And in new code,
+//! they should be considered unsafe to do I/O on (analogous to dereferencing
+//! them). Rust did not always provide this guidance, so existing code in the
+//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the
+//! `io_safety` feature is stable, libraries will be encouraged to migrate,
+//! either by adding `unsafe` to APIs that dereference `RawFd` values, or by
+//! using to `BorrowedFd` or `OwnedFd` instead.
+//!
+//! Like references, `BorrowedFd` values are tied to a lifetime, to ensure
+//! that they don't outlive the resource they point to. These are safe to
+//! use. `BorrowedFd` values may be used in APIs which provide safe access to
+//! any system call except for:
+//! - `close`, because that would end the dynamic lifetime of the resource
+//! without ending the lifetime of the file descriptor.
+//! - `dup2`/`dup3`, in the second argument, because this argument is
+//! closed and assigned a new resource, which may break the assumptions
+//! other code using that file descriptor.
+//! This list doesn't include `mmap`, since `mmap` does do a proper borrow of
+//! its file descriptor argument. That said, `mmap` is unsafe for other
+//! reasons: it operates on raw pointers, and it can have undefined behavior if
+//! the underlying storage is mutated. Mutations may come from other processes,
+//! or from the same process if the API provides `BorrowedFd` access, since as
+//! mentioned earlier, `BorrowedFd` values may be used in APIs which provide
+//! safe access to any system call. Consequently, code using `mmap` and
+//! presenting a safe API must take full responsibility for ensuring that safe
+//! Rust code cannot evoke undefined behavior through it.
+//!
+//! Like boxes, `OwnedFd` values conceptually own the resource they point to,
+//! and free (close) it when they are dropped.
+//!
+//! [`BorrowedFd<'a>`]: crate::os::unix::io::BorrowedFd
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+mod fd;
+mod raw;
+
+#[unstable(feature = "io_safety", issue = "87074")]
+pub use fd::*;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use raw::*;
diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs
new file mode 100644
index 0000000..6317e31
--- /dev/null
+++ b/library/std/src/os/unix/io/raw.rs
@@ -0,0 +1,5 @@
+//! Unix-specific extensions to general I/O primitives.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+pub use crate::os::fd::raw::*;
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 6fc1c89..17a0259 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -40,6 +40,8 @@
pub use crate::os::dragonfly::*;
#[cfg(target_os = "emscripten")]
pub use crate::os::emscripten::*;
+ #[cfg(target_os = "espidf")]
+ pub use crate::os::espidf::*;
#[cfg(target_os = "freebsd")]
pub use crate::os::freebsd::*;
#[cfg(target_os = "fuchsia")]
@@ -82,6 +84,7 @@
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "netbsd",
target_os = "openbsd"
))]
pub mod ucred;
@@ -105,7 +108,7 @@
pub use super::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt};
#[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]
- pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+ pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
#[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use super::process::{CommandExt, ExitStatusExt};
diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs
index 459f359..62bfde8 100644
--- a/library/std/src/os/unix/net/addr.rs
+++ b/library/std/src/os/unix/net/addr.rs
@@ -31,7 +31,7 @@
if bytes.contains(&0) {
return Err(io::Error::new_const(
io::ErrorKind::InvalidInput,
- &"paths may not contain interior null bytes",
+ &"paths must not contain interior null bytes",
));
}
diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs
index cd429d1..1f9d428 100644
--- a/library/std/src/os/unix/net/ancillary.rs
+++ b/library/std/src/os/unix/net/ancillary.rs
@@ -279,7 +279,7 @@
}
impl<'a> AncillaryData<'a> {
- /// Create a `AncillaryData::ScmRights` variant.
+ /// Create an `AncillaryData::ScmRights` variant.
///
/// # Safety
///
@@ -291,7 +291,7 @@
AncillaryData::ScmRights(scm_rights)
}
- /// Create a `AncillaryData::ScmCredentials` variant.
+ /// Create an `AncillaryData::ScmCredentials` variant.
///
/// # Safety
///
diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index 9e39f70..f11eec1 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -21,7 +21,7 @@
))]
use crate::io::{IoSlice, IoSliceMut};
use crate::net::Shutdown;
-use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::path::Path;
use crate::sys::cvt;
use crate::sys::net::Socket;
@@ -106,7 +106,7 @@
let socket = UnixDatagram::unbound()?;
let (addr, len) = sockaddr_un(path.as_ref())?;
- cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?;
+ cvt(libc::bind(socket.as_raw_fd(), &addr as *const _ as *const _, len as _))?;
Ok(socket)
}
@@ -187,7 +187,7 @@
unsafe {
let (addr, len) = sockaddr_un(path.as_ref())?;
- cvt(libc::connect(*self.0.as_inner(), &addr as *const _ as *const _, len))?;
+ cvt(libc::connect(self.as_raw_fd(), &addr as *const _ as *const _, len))?;
}
Ok(())
}
@@ -229,7 +229,7 @@
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
- SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) })
}
/// Returns the address of this socket's peer.
@@ -253,7 +253,7 @@
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
+ SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) })
}
fn recv_from_flags(
@@ -264,7 +264,7 @@
let mut count = 0;
let addr = SocketAddr::new(|addr, len| unsafe {
count = libc::recvfrom(
- *self.0.as_inner(),
+ self.as_raw_fd(),
buf.as_mut_ptr() as *mut _,
buf.len(),
flags,
@@ -462,7 +462,7 @@
let (addr, len) = sockaddr_un(path.as_ref())?;
let count = cvt(libc::sendto(
- *self.0.as_inner(),
+ self.as_raw_fd(),
buf.as_ptr() as *const _,
buf.len(),
MSG_NOSIGNAL,
@@ -881,7 +881,7 @@
impl AsRawFd for UnixDatagram {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- *self.0.as_inner()
+ self.0.as_inner().as_raw_fd()
}
}
@@ -889,7 +889,7 @@
impl FromRawFd for UnixDatagram {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
- UnixDatagram(Socket::from_inner(fd))
+ UnixDatagram(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))))
}
}
@@ -897,6 +897,30 @@
impl IntoRawFd for UnixDatagram {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.0.into_inner()
+ self.0.into_inner().into_inner().into_raw_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for UnixDatagram {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_inner().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<UnixDatagram> for OwnedFd {
+ #[inline]
+ fn from(unix_datagram: UnixDatagram) -> OwnedFd {
+ unsafe { OwnedFd::from_raw_fd(unix_datagram.into_raw_fd()) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for UnixDatagram {
+ #[inline]
+ fn from(owned: OwnedFd) -> Self {
+ unsafe { Self::from_raw_fd(owned.into_raw_fd()) }
}
}
diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs
index bdd08fe..f08bd25 100644
--- a/library/std/src/os/unix/net/listener.rs
+++ b/library/std/src/os/unix/net/listener.rs
@@ -1,5 +1,5 @@
use super::{sockaddr_un, SocketAddr, UnixStream};
-use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::path::Path;
use crate::sys::cvt;
use crate::sys::net::Socket;
@@ -74,8 +74,8 @@
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
let (addr, len) = sockaddr_un(path.as_ref())?;
- cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?;
- cvt(libc::listen(*inner.as_inner(), 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))?;
Ok(UnixListener(inner))
}
@@ -150,7 +150,7 @@
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
- SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) })
}
/// Moves the socket into or out of nonblocking mode.
@@ -242,7 +242,7 @@
impl AsRawFd for UnixListener {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- *self.0.as_inner()
+ self.0.as_inner().as_raw_fd()
}
}
@@ -250,7 +250,7 @@
impl FromRawFd for UnixListener {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
- UnixListener(Socket::from_inner(fd))
+ UnixListener(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))))
}
}
@@ -258,7 +258,31 @@
impl IntoRawFd for UnixListener {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.0.into_inner()
+ self.0.into_inner().into_inner().into_raw_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for UnixListener {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_inner().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for UnixListener {
+ #[inline]
+ fn from(fd: OwnedFd) -> UnixListener {
+ UnixListener(Socket::from_inner(FromInner::from_inner(fd)))
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<UnixListener> for OwnedFd {
+ #[inline]
+ fn from(listener: UnixListener) -> OwnedFd {
+ listener.0.into_inner().into_inner()
}
}
diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs
index 3088ffb..d462bd4 100644
--- a/library/std/src/os/unix/net/mod.rs
+++ b/library/std/src/os/unix/net/mod.rs
@@ -25,7 +25,6 @@
mod ancillary;
mod datagram;
mod listener;
-mod raw_fd;
mod stream;
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests;
@@ -48,7 +47,5 @@
pub use self::datagram::*;
#[stable(feature = "unix_socket", since = "1.10.0")]
pub use self::listener::*;
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::raw_fd::*;
#[stable(feature = "unix_socket", since = "1.10.0")]
pub use self::stream::*;
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index a6f6e09..4119de3 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -13,7 +13,7 @@
use crate::fmt;
use crate::io::{self, Initializer, IoSlice, IoSliceMut};
use crate::net::Shutdown;
-use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
#[cfg(any(
target_os = "android",
target_os = "linux",
@@ -21,13 +21,14 @@
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "netbsd",
target_os = "openbsd"
))]
use crate::os::unix::ucred;
use crate::path::Path;
use crate::sys::cvt;
use crate::sys::net::Socket;
-use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::sys_common::{AsInner, FromInner};
use crate::time::Duration;
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
@@ -38,6 +39,7 @@
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "netbsd",
target_os = "openbsd"
))]
pub use ucred::UCred;
@@ -99,7 +101,7 @@
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
let (addr, len) = sockaddr_un(path.as_ref())?;
- cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?;
+ cvt(libc::connect(inner.as_raw_fd(), &addr as *const _ as *const _, len))?;
Ok(UnixStream(inner))
}
}
@@ -165,7 +167,7 @@
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
- SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) })
}
/// Returns the socket address of the remote half of this connection.
@@ -183,7 +185,7 @@
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
+ SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) })
}
/// Gets the peer credentials for this Unix domain socket.
@@ -208,6 +210,7 @@
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "netbsd",
target_os = "openbsd"
))]
pub fn peer_cred(&self) -> io::Result<UCred> {
@@ -656,7 +659,7 @@
impl AsRawFd for UnixStream {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- *self.0.as_inner()
+ self.0.as_raw_fd()
}
}
@@ -664,7 +667,7 @@
impl FromRawFd for UnixStream {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
- UnixStream(Socket::from_inner(fd))
+ UnixStream(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))))
}
}
@@ -672,6 +675,30 @@
impl IntoRawFd for UnixStream {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.0.into_inner()
+ self.0.into_raw_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for UnixStream {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<UnixStream> for OwnedFd {
+ #[inline]
+ fn from(unix_stream: UnixStream) -> OwnedFd {
+ unsafe { OwnedFd::from_raw_fd(unix_stream.into_raw_fd()) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for UnixStream {
+ #[inline]
+ fn from(owned: OwnedFd) -> Self {
+ unsafe { Self::from_raw_fd(owned.into_raw_fd()) }
}
}
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index f3b5227..650dcba 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -4,7 +4,7 @@
use crate::ffi::OsStr;
use crate::io;
-use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::process;
use crate::sealed::Sealed;
use crate::sys;
@@ -83,7 +83,7 @@
///
/// When this closure is run, aspects such as the stdio file descriptors and
/// working directory have successfully been changed, so output to these
- /// locations may not appear where intended.
+ /// locations might not appear where intended.
///
/// [POSIX fork() specification]:
/// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
@@ -321,7 +321,17 @@
impl FromRawFd for process::Stdio {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
- let fd = sys::fd::FileDesc::new(fd);
+ let fd = sys::fd::FileDesc::from_raw_fd(fd);
+ let io = sys::process::Stdio::Fd(fd);
+ process::Stdio::from_inner(io)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedFd> for process::Stdio {
+ #[inline]
+ fn from(fd: OwnedFd) -> process::Stdio {
+ let fd = sys::fd::FileDesc::from_inner(fd);
let io = sys::process::Stdio::Fd(fd);
process::Stdio::from_inner(io)
}
@@ -331,7 +341,7 @@
impl AsRawFd for process::ChildStdin {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().raw()
+ self.as_inner().as_raw_fd()
}
}
@@ -339,7 +349,7 @@
impl AsRawFd for process::ChildStdout {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().raw()
+ self.as_inner().as_raw_fd()
}
}
@@ -347,7 +357,7 @@
impl AsRawFd for process::ChildStderr {
#[inline]
fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().raw()
+ self.as_inner().as_raw_fd()
}
}
@@ -355,7 +365,7 @@
impl IntoRawFd for process::ChildStdin {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
+ self.into_inner().into_inner().into_raw_fd()
}
}
@@ -363,7 +373,7 @@
impl IntoRawFd for process::ChildStdout {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
+ self.into_inner().into_inner().into_raw_fd()
}
}
@@ -371,7 +381,55 @@
impl IntoRawFd for process::ChildStderr {
#[inline]
fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
+ self.into_inner().into_inner().into_raw_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for crate::process::ChildStdin {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<crate::process::ChildStdin> for OwnedFd {
+ #[inline]
+ fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd {
+ child_stdin.into_inner().into_inner().into_inner()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for crate::process::ChildStdout {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<crate::process::ChildStdout> for OwnedFd {
+ #[inline]
+ fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd {
+ child_stdout.into_inner().into_inner().into_inner()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for crate::process::ChildStderr {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.as_inner().as_fd()
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<crate::process::ChildStderr> for OwnedFd {
+ #[inline]
+ fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd {
+ child_stderr.into_inner().into_inner().into_inner()
}
}
diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs
index 1b4c18d..32e6430 100644
--- a/library/std/src/os/unix/ucred.rs
+++ b/library/std/src/os/unix/ucred.rs
@@ -28,7 +28,12 @@
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use self::impl_linux::peer_cred;
-#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd"
+))]
pub use self::impl_bsd::peer_cred;
#[cfg(any(target_os = "macos", target_os = "ios",))]
@@ -70,7 +75,12 @@
}
}
-#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd"
+))]
pub mod impl_bsd {
use super::UCred;
use crate::io;
diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs
index bd30d6a..3df2756 100644
--- a/library/std/src/os/wasi/fs.rs
+++ b/library/std/src/os/wasi/fs.rs
@@ -228,35 +228,35 @@
impl FileExt for fs::File {
fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
- self.as_inner().fd().pread(bufs, offset)
+ self.as_inner().as_inner().pread(bufs, offset)
}
fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
- self.as_inner().fd().pwrite(bufs, offset)
+ self.as_inner().as_inner().pwrite(bufs, offset)
}
fn tell(&self) -> io::Result<u64> {
- self.as_inner().fd().tell()
+ self.as_inner().as_inner().tell()
}
fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
- self.as_inner().fd().set_flags(flags)
+ self.as_inner().as_inner().set_flags(flags)
}
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
- self.as_inner().fd().set_rights(rights, inheriting)
+ self.as_inner().as_inner().set_rights(rights, inheriting)
}
fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
- self.as_inner().fd().advise(offset, len, advice)
+ self.as_inner().as_inner().advise(offset, len, advice)
}
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
- self.as_inner().fd().allocate(offset, len)
+ self.as_inner().as_inner().allocate(offset, len)
}
fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> {
- self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?)
+ self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?)
}
fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
@@ -269,11 +269,11 @@
}
fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
- self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?)
+ self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?)
}
fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
- self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?)
+ self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?)
}
}
@@ -486,10 +486,10 @@
new_fd: &File,
new_path: U,
) -> io::Result<()> {
- old_fd.as_inner().fd().link(
+ old_fd.as_inner().as_inner().link(
old_flags,
osstr2str(old_path.as_ref().as_ref())?,
- new_fd.as_inner().fd(),
+ new_fd.as_inner().as_inner(),
osstr2str(new_path.as_ref().as_ref())?,
)
}
@@ -503,9 +503,9 @@
new_fd: &File,
new_path: U,
) -> io::Result<()> {
- old_fd.as_inner().fd().rename(
+ old_fd.as_inner().as_inner().rename(
osstr2str(old_path.as_ref().as_ref())?,
- new_fd.as_inner().fd(),
+ new_fd.as_inner().as_inner(),
osstr2str(new_path.as_ref().as_ref())?,
)
}
@@ -519,7 +519,7 @@
new_path: U,
) -> io::Result<()> {
fd.as_inner()
- .fd()
+ .as_inner()
.symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?)
}
diff --git a/library/std/src/os/wasi/io.rs b/library/std/src/os/wasi/io.rs
deleted file mode 100644
index cf4501b..0000000
--- a/library/std/src/os/wasi/io.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-//! WASI-specific extensions to general I/O primitives
-
-#![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "71213")]
-
-use crate::fs;
-use crate::io;
-use crate::net;
-use crate::sys;
-use crate::sys_common::{AsInner, FromInner, IntoInner};
-
-/// Raw file descriptors.
-pub type RawFd = u32;
-
-/// A trait to extract the raw WASI file descriptor from an underlying
-/// object.
-pub trait AsRawFd {
- /// Extracts the raw file descriptor.
- ///
- /// This method does **not** pass ownership of the raw file descriptor
- /// to the caller. The descriptor is only guaranteed to be valid while
- /// the original object has not yet been destroyed.
- fn as_raw_fd(&self) -> RawFd;
-}
-
-/// A trait to express the ability to construct an object from a raw file
-/// descriptor.
-pub trait FromRawFd {
- /// Constructs a new instance of `Self` from the given raw file
- /// descriptor.
- ///
- /// This function **consumes ownership** of the specified file
- /// descriptor. The returned object will take responsibility for closing
- /// it when the object goes out of scope.
- ///
- /// This function is also unsafe as the primitives currently returned
- /// have the contract that they are the sole owner of the file
- /// descriptor they are wrapping. Usage of this function could
- /// accidentally allow violating this contract which can cause memory
- /// unsafety in code that relies on it being true.
- unsafe fn from_raw_fd(fd: RawFd) -> Self;
-}
-
-/// A trait to express the ability to consume an object and acquire ownership of
-/// its raw file descriptor.
-pub trait IntoRawFd {
- /// Consumes this object, returning the raw underlying file descriptor.
- ///
- /// This function **transfers ownership** of the underlying file descriptor
- /// to the caller. Callers are then the unique owners of the file descriptor
- /// and must close the descriptor once it's no longer needed.
- fn into_raw_fd(self) -> RawFd;
-}
-
-#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
-impl AsRawFd for RawFd {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- *self
- }
-}
-#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
-impl IntoRawFd for RawFd {
- #[inline]
- fn into_raw_fd(self) -> RawFd {
- self
- }
-}
-#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
-impl FromRawFd for RawFd {
- #[inline]
- unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
- fd
- }
-}
-
-impl AsRawFd for net::TcpStream {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().as_raw()
- }
-}
-
-impl FromRawFd for net::TcpStream {
- #[inline]
- unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
- net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd))
- }
-}
-
-impl IntoRawFd for net::TcpStream {
- #[inline]
- fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
- }
-}
-
-impl AsRawFd for net::TcpListener {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().as_raw()
- }
-}
-
-impl FromRawFd for net::TcpListener {
- #[inline]
- unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
- net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd))
- }
-}
-
-impl IntoRawFd for net::TcpListener {
- #[inline]
- fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
- }
-}
-
-impl AsRawFd for net::UdpSocket {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().as_raw()
- }
-}
-
-impl FromRawFd for net::UdpSocket {
- #[inline]
- unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket {
- net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd))
- }
-}
-
-impl IntoRawFd for net::UdpSocket {
- #[inline]
- fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
- }
-}
-
-impl AsRawFd for fs::File {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- self.as_inner().fd().as_raw()
- }
-}
-
-impl FromRawFd for fs::File {
- #[inline]
- unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
- fs::File::from_inner(sys::fs::File::from_inner(fd))
- }
-}
-
-impl IntoRawFd for fs::File {
- #[inline]
- fn into_raw_fd(self) -> RawFd {
- self.into_inner().into_fd().into_raw()
- }
-}
-
-impl AsRawFd for io::Stdin {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- libc::STDIN_FILENO as RawFd
- }
-}
-
-impl AsRawFd for io::Stdout {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- libc::STDOUT_FILENO as RawFd
- }
-}
-
-impl AsRawFd for io::Stderr {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- libc::STDERR_FILENO as RawFd
- }
-}
-
-impl<'a> AsRawFd for io::StdinLock<'a> {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- libc::STDIN_FILENO as RawFd
- }
-}
-
-impl<'a> AsRawFd for io::StdoutLock<'a> {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- libc::STDOUT_FILENO as RawFd
- }
-}
-
-impl<'a> AsRawFd for io::StderrLock<'a> {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- libc::STDERR_FILENO as RawFd
- }
-}
diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs
new file mode 100644
index 0000000..930aca8
--- /dev/null
+++ b/library/std/src/os/wasi/io/fd.rs
@@ -0,0 +1,9 @@
+//! Owned and borrowed file descriptors.
+
+#![unstable(feature = "wasi_ext", issue = "71213")]
+
+// Tests for this module
+#[cfg(test)]
+mod tests;
+
+pub use crate::os::fd::owned::*;
diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/fd/tests.rs
new file mode 100644
index 0000000..4182747
--- /dev/null
+++ b/library/std/src/os/wasi/io/fd/tests.rs
@@ -0,0 +1,11 @@
+use crate::mem::size_of;
+use crate::os::wasi::io::RawFd;
+
+#[test]
+fn test_raw_fd_layout() {
+ // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start`
+ // and `rustc_layout_scalar_valid_range_end`, with values that depend on
+ // the bit width of `RawFd`. If this ever changes, those values will need
+ // to be updated.
+ assert_eq!(size_of::<RawFd>(), 4);
+}
diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs
new file mode 100644
index 0000000..6c884e2
--- /dev/null
+++ b/library/std/src/os/wasi/io/mod.rs
@@ -0,0 +1,12 @@
+//! WASI-specific extensions to general I/O primitives.
+
+#![deny(unsafe_op_in_unsafe_fn)]
+#![unstable(feature = "wasi_ext", issue = "71213")]
+
+mod fd;
+mod raw;
+
+#[unstable(feature = "wasi_ext", issue = "71213")]
+pub use fd::*;
+#[unstable(feature = "wasi_ext", issue = "71213")]
+pub use raw::*;
diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs
new file mode 100644
index 0000000..0e0c582
--- /dev/null
+++ b/library/std/src/os/wasi/io/raw.rs
@@ -0,0 +1,5 @@
+//! WASI-specific extensions to general I/O primitives.
+
+#![unstable(feature = "wasi_ext", issue = "71213")]
+
+pub use crate::os::fd::raw::*;
diff --git a/library/std/src/os/wasi/mod.rs b/library/std/src/os/wasi/mod.rs
index 44b7c32..d767c14 100644
--- a/library/std/src/os/wasi/mod.rs
+++ b/library/std/src/os/wasi/mod.rs
@@ -32,6 +32,7 @@
pub mod ffi;
pub mod fs;
pub mod io;
+pub mod net;
/// A prelude for conveniently writing platform-specific code.
///
@@ -49,5 +50,5 @@
pub use super::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt};
#[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]
- pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+ pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
}
diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs
new file mode 100644
index 0000000..e6bcf87
--- /dev/null
+++ b/library/std/src/os/wasi/net/mod.rs
@@ -0,0 +1,3 @@
+//! WASI-specific networking functionality
+
+#![unstable(feature = "wasi_ext", issue = "71213")]
diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs
index c89b9ff..8d29fa7 100644
--- a/library/std/src/os/windows/ffi.rs
+++ b/library/std/src/os/windows/ffi.rs
@@ -30,7 +30,7 @@
//! [`OsString`] is the Rust wrapper for owned strings in the
//! preferred representation of the operating system. On Windows,
//! this struct gets augmented with an implementation of the
-//! [`OsStringExt`] trait, which has a [`OsStringExt::from_wide`] method. This
+//! [`OsStringExt`] trait, which has an [`OsStringExt::from_wide`] method. This
//! lets you create an [`OsString`] from a `&[u16]` slice; presumably
//! you get such a slice out of a `WCHAR` Windows API.
//!
diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs
new file mode 100644
index 0000000..72a17ad
--- /dev/null
+++ b/library/std/src/os/windows/io/handle.rs
@@ -0,0 +1,386 @@
+//! Owned and borrowed OS handles.
+
+#![unstable(feature = "io_safety", issue = "87074")]
+
+use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+use crate::convert::TryFrom;
+use crate::ffi::c_void;
+use crate::fmt;
+use crate::fs;
+use crate::marker::PhantomData;
+use crate::mem::forget;
+use crate::ptr::NonNull;
+use crate::sys::c;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+/// A borrowed handle.
+///
+/// This has a lifetime parameter to tie it to the lifetime of something that
+/// owns the handle.
+///
+/// This uses `repr(transparent)` and has the representation of a host handle,
+/// so it can be used in FFI in places where a handle is passed as an argument,
+/// it is not captured or consumed, and it is never null.
+///
+/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
+/// sometimes a valid handle value. See [here] for the full story.
+///
+/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+#[unstable(feature = "io_safety", issue = "87074")]
+pub struct BorrowedHandle<'handle> {
+ handle: NonNull<c_void>,
+ _phantom: PhantomData<&'handle OwnedHandle>,
+}
+
+/// An owned handle.
+///
+/// This closes the handle on drop.
+///
+/// This uses `repr(transparent)` and has the representation of a host handle,
+/// so it can be used in FFI in places where a handle is passed as a consumed
+/// argument or returned as an owned value, and is never null.
+///
+/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
+/// sometimes a valid handle value. See [here] for the full story. For APIs
+/// like `CreateFileW` which report errors with `INVALID_HANDLE_VALUE` instead
+/// of null, use [`HandleOrInvalid`] instead of `Option<OwnedHandle>`.
+///
+/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such,
+/// it must not be used with handles to open registry keys which need to be
+/// closed with [`RegCloseKey`] instead.
+///
+/// [`CloseHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
+/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
+///
+/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+#[repr(transparent)]
+#[unstable(feature = "io_safety", issue = "87074")]
+pub struct OwnedHandle {
+ handle: NonNull<c_void>,
+}
+
+/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used
+/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses
+/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
+/// FFI declarations.
+///
+/// The only thing you can usefully do with a `HandleOrInvalid` is to convert it into an
+/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for
+/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
+/// checking for `INVALID_HANDLE_VALUE` first.
+///
+/// If this holds a valid handle, it will close the handle on drop.
+#[repr(transparent)]
+#[unstable(feature = "io_safety", issue = "87074")]
+#[derive(Debug)]
+pub struct HandleOrInvalid(Option<OwnedHandle>);
+
+// The Windows [`HANDLE`] type may be transferred across and shared between
+// thread boundaries (despite containing a `*mut void`, which in general isn't
+// `Send` or `Sync`).
+//
+// [`HANDLE`]: std::os::windows::raw::HANDLE
+unsafe impl Send for OwnedHandle {}
+unsafe impl Send for HandleOrInvalid {}
+unsafe impl Send for BorrowedHandle<'_> {}
+unsafe impl Sync for OwnedHandle {}
+unsafe impl Sync for HandleOrInvalid {}
+unsafe impl Sync for BorrowedHandle<'_> {}
+
+impl BorrowedHandle<'_> {
+ /// Return a `BorrowedHandle` holding the given raw handle.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `handle` must be a valid open handle, it
+ /// must remain open for the duration of the returned `BorrowedHandle`, and
+ /// it must not be null.
+ ///
+ /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
+ /// sometimes a valid handle value. See [here] for the full story.
+ ///
+ /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+ #[inline]
+ #[unstable(feature = "io_safety", issue = "87074")]
+ pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self {
+ assert!(!handle.is_null());
+ Self { handle: NonNull::new_unchecked(handle), _phantom: PhantomData }
+ }
+}
+
+impl TryFrom<HandleOrInvalid> for OwnedHandle {
+ type Error = ();
+
+ #[inline]
+ fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> {
+ // In theory, we ought to be able to assume that the pointer here is
+ // never null, use `OwnedHandle` rather than `Option<OwnedHandle>`, and
+ // obviate the the panic path here. Unfortunately, Win32 documentation
+ // doesn't explicitly guarantee this anywhere.
+ //
+ // APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
+ // null handle indicates an absent value, which wouldn't work if null
+ // were a valid handle value, so it seems very unlikely that it could
+ // ever return null. But who knows?
+ //
+ // [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
+ let owned_handle = handle_or_invalid.0.expect("A `HandleOrInvalid` was null!");
+ if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE {
+ Err(())
+ } else {
+ Ok(owned_handle)
+ }
+ }
+}
+
+impl AsRawHandle for BorrowedHandle<'_> {
+ #[inline]
+ fn as_raw_handle(&self) -> RawHandle {
+ self.handle.as_ptr()
+ }
+}
+
+impl AsRawHandle for OwnedHandle {
+ #[inline]
+ fn as_raw_handle(&self) -> RawHandle {
+ self.handle.as_ptr()
+ }
+}
+
+impl IntoRawHandle for OwnedHandle {
+ #[inline]
+ fn into_raw_handle(self) -> RawHandle {
+ let handle = self.handle.as_ptr();
+ forget(self);
+ handle
+ }
+}
+
+impl FromRawHandle for OwnedHandle {
+ /// Constructs a new instance of `Self` from the given raw handle.
+ ///
+ /// Use `HandleOrInvalid` instead of `Option<OwnedHandle>` for APIs that
+ /// use `INVALID_HANDLE_VALUE` to indicate failure.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `handle` must be open and suitable for
+ /// assuming ownership. The resource must not require any cleanup other
+ /// than `CloseHandle`.
+ ///
+ /// In particular, it must not be used with handles to open registry
+ /// keys which need to be closed with [`RegCloseKey`] instead.
+ ///
+ /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
+ /// sometimes a valid handle value. See [here] for the full story.
+ ///
+ /// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
+ /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+ #[inline]
+ unsafe fn from_raw_handle(handle: RawHandle) -> Self {
+ assert!(!handle.is_null());
+ Self { handle: NonNull::new_unchecked(handle) }
+ }
+}
+
+impl FromRawHandle for HandleOrInvalid {
+ /// Constructs a new instance of `Self` from the given `RawHandle` returned
+ /// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate
+ /// failure, such as `CreateFileW`.
+ ///
+ /// Use `Option<OwnedHandle>` instead of `HandleOrInvalid` for APIs that
+ /// use null to indicate failure.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `handle` must be either open and otherwise
+ /// unowned, or equal to `INVALID_HANDLE_VALUE` (-1). It must not be null.
+ /// Note that not all Windows APIs use `INVALID_HANDLE_VALUE` for errors;
+ /// see [here] for the full story.
+ ///
+ /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+ #[inline]
+ unsafe fn from_raw_handle(handle: RawHandle) -> Self {
+ // We require non-null here to catch errors earlier.
+ Self(Some(OwnedHandle::from_raw_handle(handle)))
+ }
+}
+
+impl Drop for OwnedHandle {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ let _ = c::CloseHandle(self.handle.as_ptr());
+ }
+ }
+}
+
+impl fmt::Debug for BorrowedHandle<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BorrowedHandle").field("handle", &self.handle).finish()
+ }
+}
+
+impl fmt::Debug for OwnedHandle {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("OwnedHandle").field("handle", &self.handle).finish()
+ }
+}
+
+/// A trait to borrow the handle from an underlying object.
+#[unstable(feature = "io_safety", issue = "87074")]
+pub trait AsHandle {
+ /// Borrows the handle.
+ ///
+ /// # Example
+ ///
+ /// ```rust,no_run
+ /// # #![feature(io_safety)]
+ /// use std::fs::File;
+ /// # use std::io;
+ /// use std::os::windows::io::{AsHandle, BorrowedHandle};
+ ///
+ /// let mut f = File::open("foo.txt")?;
+ /// let borrowed_handle: BorrowedHandle<'_> = f.as_handle();
+ /// # Ok::<(), io::Error>(())
+ /// ```
+ fn as_handle(&self) -> BorrowedHandle<'_>;
+}
+
+impl AsHandle for BorrowedHandle<'_> {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ *self
+ }
+}
+
+impl AsHandle for OwnedHandle {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ // Safety: `OwnedHandle` and `BorrowedHandle` have the same validity
+ // invariants, and the `BorrowdHandle` is bounded by the lifetime
+ // of `&self`.
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl AsHandle for fs::File {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ self.as_inner().as_handle()
+ }
+}
+
+impl From<fs::File> for OwnedHandle {
+ #[inline]
+ fn from(file: fs::File) -> OwnedHandle {
+ file.into_inner().into_inner().into_inner().into()
+ }
+}
+
+impl From<OwnedHandle> for fs::File {
+ #[inline]
+ fn from(owned: OwnedHandle) -> Self {
+ Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned)))
+ }
+}
+
+impl AsHandle for crate::io::Stdin {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl<'a> AsHandle for crate::io::StdinLock<'a> {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl AsHandle for crate::io::Stdout {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl<'a> AsHandle for crate::io::StdoutLock<'a> {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl AsHandle for crate::io::Stderr {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl<'a> AsHandle for crate::io::StderrLock<'a> {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl AsHandle for crate::process::ChildStdin {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl From<crate::process::ChildStdin> for OwnedHandle {
+ #[inline]
+ fn from(child_stdin: crate::process::ChildStdin) -> OwnedHandle {
+ unsafe { OwnedHandle::from_raw_handle(child_stdin.into_raw_handle()) }
+ }
+}
+
+impl AsHandle for crate::process::ChildStdout {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl From<crate::process::ChildStdout> for OwnedHandle {
+ #[inline]
+ fn from(child_stdout: crate::process::ChildStdout) -> OwnedHandle {
+ unsafe { OwnedHandle::from_raw_handle(child_stdout.into_raw_handle()) }
+ }
+}
+
+impl AsHandle for crate::process::ChildStderr {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl From<crate::process::ChildStderr> for OwnedHandle {
+ #[inline]
+ fn from(child_stderr: crate::process::ChildStderr) -> OwnedHandle {
+ unsafe { OwnedHandle::from_raw_handle(child_stderr.into_raw_handle()) }
+ }
+}
+
+impl<T> AsHandle for crate::thread::JoinHandle<T> {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
+ }
+}
+
+impl<T> From<crate::thread::JoinHandle<T>> for OwnedHandle {
+ #[inline]
+ fn from(join_handle: crate::thread::JoinHandle<T>) -> OwnedHandle {
+ join_handle.into_inner().into_handle().into_inner()
+ }
+}
diff --git a/library/std/src/os/windows/io/mod.rs b/library/std/src/os/windows/io/mod.rs
new file mode 100644
index 0000000..2f6f076
--- /dev/null
+++ b/library/std/src/os/windows/io/mod.rs
@@ -0,0 +1,56 @@
+//! Windows-specific extensions to general I/O primitives.
+//!
+//! Just like raw pointers, raw Windows handles and sockets point to resources
+//! with dynamic lifetimes, and they can dangle if they outlive their resources
+//! or be forged if they're created from invalid values.
+//!
+//! This module provides three types for representing raw handles and sockets
+//! with different ownership properties: raw, borrowed, and owned, which are
+//! analogous to types used for representing pointers:
+//!
+//! | Type | Analogous to |
+//! | ---------------------- | ------------ |
+//! | [`RawHandle`] | `*const _` |
+//! | [`RawSocket`] | `*const _` |
+//! | | |
+//! | [`BorrowedHandle<'a>`] | `&'a _` |
+//! | [`BorrowedSocket<'a>`] | `&'a _` |
+//! | | |
+//! | [`OwnedHandle`] | `Box<_>` |
+//! | [`OwnedSocket`] | `Box<_>` |
+//!
+//! Like raw pointers, `RawHandle` and `RawSocket` values are primitive values.
+//! And in new code, they should be considered unsafe to do I/O on (analogous
+//! to dereferencing them). Rust did not always provide this guidance, so
+//! existing code in the Rust ecosystem often doesn't mark `RawHandle` and
+//! `RawSocket` usage as unsafe. Once the `io_safety` feature is stable,
+//! libraries will be encouraged to migrate, either by adding `unsafe` to APIs
+//! that dereference `RawHandle` and `RawSocket` values, or by using to
+//! `BorrowedHandle`, `BorrowedSocket`, `OwnedHandle`, or `OwnedSocket`.
+//!
+//! Like references, `BorrowedHandle` and `BorrowedSocket` values are tied to a
+//! lifetime, to ensure that they don't outlive the resource they point to.
+//! These are safe to use. `BorrowedHandle` and `BorrowedSocket` values may be
+//! used in APIs which provide safe access to any system call except for
+//! `CloseHandle`, `closesocket`, or any other call that would end the
+//! dynamic lifetime of the resource without ending the lifetime of the
+//! handle or socket.
+//!
+//! Like boxes, `OwnedHandle` and `OwnedSocket` values conceptually own the
+//! resource they point to, and free (close) it when they are dropped.
+//!
+//! [`BorrowedHandle<'a>`]: crate::os::windows::io::BorrowedHandle
+//! [`BorrowedSocket<'a>`]: crate::os::windows::io::BorrowedSocket
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+mod handle;
+mod raw;
+mod socket;
+
+#[unstable(feature = "io_safety", issue = "87074")]
+pub use handle::*;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use raw::*;
+#[unstable(feature = "io_safety", issue = "87074")]
+pub use socket::*;
diff --git a/library/std/src/os/windows/io.rs b/library/std/src/os/windows/io/raw.rs
similarity index 88%
rename from library/std/src/os/windows/io.rs
rename to library/std/src/os/windows/io/raw.rs
index 31b5d01..c7f1220 100644
--- a/library/std/src/os/windows/io.rs
+++ b/library/std/src/os/windows/io/raw.rs
@@ -5,6 +5,7 @@
use crate::fs;
use crate::io;
use crate::net;
+use crate::os::windows::io::{OwnedHandle, OwnedSocket};
use crate::os::windows::raw;
use crate::sys;
use crate::sys::c;
@@ -61,7 +62,7 @@
impl AsRawHandle for fs::File {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.as_inner().handle().raw() as RawHandle
+ self.as_inner().as_raw_handle() as RawHandle
}
}
@@ -112,7 +113,9 @@
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> fs::File {
let handle = handle as c::HANDLE;
- fs::File::from_inner(sys::fs::File::from_inner(handle))
+ fs::File::from_inner(sys::fs::File::from_inner(FromInner::from_inner(
+ OwnedHandle::from_raw_handle(handle),
+ )))
}
}
@@ -120,7 +123,7 @@
impl IntoRawHandle for fs::File {
#[inline]
fn into_raw_handle(self) -> RawHandle {
- self.into_inner().into_handle().into_raw() as *mut _
+ self.into_inner().into_raw_handle() as *mut _
}
}
@@ -166,21 +169,21 @@
impl AsRawSocket for net::TcpStream {
#[inline]
fn as_raw_socket(&self) -> RawSocket {
- *self.as_inner().socket().as_inner()
+ self.as_inner().socket().as_raw_socket()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawSocket for net::TcpListener {
#[inline]
fn as_raw_socket(&self) -> RawSocket {
- *self.as_inner().socket().as_inner()
+ self.as_inner().socket().as_raw_socket()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawSocket for net::UdpSocket {
#[inline]
fn as_raw_socket(&self) -> RawSocket {
- *self.as_inner().socket().as_inner()
+ self.as_inner().socket().as_raw_socket()
}
}
@@ -188,7 +191,7 @@
impl FromRawSocket for net::TcpStream {
#[inline]
unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream {
- let sock = sys::net::Socket::from_inner(sock);
+ let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock));
net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock))
}
}
@@ -196,7 +199,7 @@
impl FromRawSocket for net::TcpListener {
#[inline]
unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener {
- let sock = sys::net::Socket::from_inner(sock);
+ let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock));
net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock))
}
}
@@ -204,7 +207,7 @@
impl FromRawSocket for net::UdpSocket {
#[inline]
unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket {
- let sock = sys::net::Socket::from_inner(sock);
+ let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock));
net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock))
}
}
@@ -213,7 +216,7 @@
impl IntoRawSocket for net::TcpStream {
#[inline]
fn into_raw_socket(self) -> RawSocket {
- self.into_inner().into_socket().into_inner()
+ self.into_inner().into_socket().into_inner().into_raw_socket()
}
}
@@ -221,7 +224,7 @@
impl IntoRawSocket for net::TcpListener {
#[inline]
fn into_raw_socket(self) -> RawSocket {
- self.into_inner().into_socket().into_inner()
+ self.into_inner().into_socket().into_inner().into_raw_socket()
}
}
@@ -229,6 +232,6 @@
impl IntoRawSocket for net::UdpSocket {
#[inline]
fn into_raw_socket(self) -> RawSocket {
- self.into_inner().into_socket().into_inner()
+ self.into_inner().into_socket().into_inner().into_raw_socket()
}
}
diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs
new file mode 100644
index 0000000..23db66d
--- /dev/null
+++ b/library/std/src/os/windows/io/socket.rs
@@ -0,0 +1,216 @@
+//! Owned and borrowed OS sockets.
+
+#![unstable(feature = "io_safety", issue = "87074")]
+
+use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
+use crate::fmt;
+use crate::marker::PhantomData;
+use crate::mem::forget;
+use crate::sys::c;
+
+/// A borrowed socket.
+///
+/// This has a lifetime parameter to tie it to the lifetime of something that
+/// owns the socket.
+///
+/// This uses `repr(transparent)` and has the representation of a host socket,
+/// so it can be used in FFI in places where a socket is passed as an argument,
+/// it is not captured or consumed, and it never has the value
+/// `INVALID_SOCKET`.
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// This is -2, in two's complement. -1 is `INVALID_SOCKET`.
+#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
+#[cfg_attr(
+ target_pointer_width = "64",
+ rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
+)]
+#[unstable(feature = "io_safety", issue = "87074")]
+pub struct BorrowedSocket<'socket> {
+ socket: RawSocket,
+ _phantom: PhantomData<&'socket OwnedSocket>,
+}
+
+/// An owned socket.
+///
+/// This closes the socket on drop.
+///
+/// This uses `repr(transparent)` and has the representation of a host socket,
+/// so it can be used in FFI in places where a socket is passed as a consumed
+/// argument or returned as an owned value, and it never has the value
+/// `INVALID_SOCKET`.
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// This is -2, in two's complement. -1 is `INVALID_SOCKET`.
+#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
+#[cfg_attr(
+ target_pointer_width = "64",
+ rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
+)]
+#[unstable(feature = "io_safety", issue = "87074")]
+pub struct OwnedSocket {
+ socket: RawSocket,
+}
+
+impl BorrowedSocket<'_> {
+ /// Return a `BorrowedSocket` holding the given raw socket.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `raw` must remain open for the duration of
+ /// the returned `BorrowedSocket`, and it must not have the value
+ /// `INVALID_SOCKET`.
+ #[inline]
+ #[unstable(feature = "io_safety", issue = "87074")]
+ pub unsafe fn borrow_raw_socket(socket: RawSocket) -> Self {
+ debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket);
+ Self { socket, _phantom: PhantomData }
+ }
+}
+
+impl AsRawSocket for BorrowedSocket<'_> {
+ #[inline]
+ fn as_raw_socket(&self) -> RawSocket {
+ self.socket
+ }
+}
+
+impl AsRawSocket for OwnedSocket {
+ #[inline]
+ fn as_raw_socket(&self) -> RawSocket {
+ self.socket
+ }
+}
+
+impl IntoRawSocket for OwnedSocket {
+ #[inline]
+ fn into_raw_socket(self) -> RawSocket {
+ let socket = self.socket;
+ forget(self);
+ socket
+ }
+}
+
+impl FromRawSocket for OwnedSocket {
+ /// Constructs a new instance of `Self` from the given raw socket.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `socket` must be open and suitable for
+ /// assuming ownership. The resource must not require cleanup other than
+ /// `closesocket`.
+ #[inline]
+ unsafe fn from_raw_socket(socket: RawSocket) -> Self {
+ debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket);
+ Self { socket }
+ }
+}
+
+impl Drop for OwnedSocket {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ let _ = c::closesocket(self.socket);
+ }
+ }
+}
+
+impl fmt::Debug for BorrowedSocket<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BorrowedSocket").field("socket", &self.socket).finish()
+ }
+}
+
+impl fmt::Debug for OwnedSocket {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("OwnedSocket").field("socket", &self.socket).finish()
+ }
+}
+
+/// A trait to borrow the socket from an underlying object.
+#[unstable(feature = "io_safety", issue = "87074")]
+pub trait AsSocket {
+ /// Borrows the socket.
+ fn as_socket(&self) -> BorrowedSocket<'_>;
+}
+
+impl AsSocket for BorrowedSocket<'_> {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ *self
+ }
+}
+
+impl AsSocket for OwnedSocket {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ // Safety: `OwnedSocket` and `BorrowedSocket` have the same validity
+ // invariants, and the `BorrowdSocket` is bounded by the lifetime
+ // of `&self`.
+ unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) }
+ }
+}
+
+impl AsSocket for crate::net::TcpStream {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) }
+ }
+}
+
+impl From<crate::net::TcpStream> for OwnedSocket {
+ #[inline]
+ fn from(tcp_stream: crate::net::TcpStream) -> OwnedSocket {
+ unsafe { OwnedSocket::from_raw_socket(tcp_stream.into_raw_socket()) }
+ }
+}
+
+impl From<OwnedSocket> for crate::net::TcpStream {
+ #[inline]
+ fn from(owned: OwnedSocket) -> Self {
+ unsafe { Self::from_raw_socket(owned.into_raw_socket()) }
+ }
+}
+
+impl AsSocket for crate::net::TcpListener {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) }
+ }
+}
+
+impl From<crate::net::TcpListener> for OwnedSocket {
+ #[inline]
+ fn from(tcp_listener: crate::net::TcpListener) -> OwnedSocket {
+ unsafe { OwnedSocket::from_raw_socket(tcp_listener.into_raw_socket()) }
+ }
+}
+
+impl From<OwnedSocket> for crate::net::TcpListener {
+ #[inline]
+ fn from(owned: OwnedSocket) -> Self {
+ unsafe { Self::from_raw_socket(owned.into_raw_socket()) }
+ }
+}
+
+impl AsSocket for crate::net::UdpSocket {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) }
+ }
+}
+
+impl From<crate::net::UdpSocket> for OwnedSocket {
+ #[inline]
+ fn from(udp_socket: crate::net::UdpSocket) -> OwnedSocket {
+ unsafe { OwnedSocket::from_raw_socket(udp_socket.into_raw_socket()) }
+ }
+}
+
+impl From<OwnedSocket> for crate::net::UdpSocket {
+ #[inline]
+ fn from(owned: OwnedSocket) -> Self {
+ unsafe { Self::from_raw_socket(owned.into_raw_socket()) }
+ }
+}
diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs
index 52ac508..969054d 100644
--- a/library/std/src/os/windows/mod.rs
+++ b/library/std/src/os/windows/mod.rs
@@ -32,8 +32,11 @@
pub use super::fs::{MetadataExt, OpenOptionsExt};
#[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]
- pub use super::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket};
+ pub use super::io::{
+ AsHandle, AsSocket, BorrowedHandle, BorrowedSocket, FromRawHandle, FromRawSocket,
+ HandleOrInvalid, IntoRawHandle, IntoRawSocket, OwnedHandle, OwnedSocket,
+ };
#[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]
- pub use super::io::{FromRawHandle, FromRawSocket, IntoRawHandle, IntoRawSocket};
+ pub use super::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket};
}
diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs
index 9e7ccd0..b246599 100644
--- a/library/std/src/os/windows/process.rs
+++ b/library/std/src/os/windows/process.rs
@@ -3,7 +3,9 @@
#![stable(feature = "process_extensions", since = "1.2.0")]
use crate::ffi::OsStr;
-use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+use crate::os::windows::io::{
+ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
+};
use crate::process;
use crate::sealed::Sealed;
use crate::sys;
@@ -12,7 +14,16 @@
#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawHandle for process::Stdio {
unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio {
- let handle = sys::handle::Handle::new(handle as *mut _);
+ let handle = sys::handle::Handle::from_raw_handle(handle as *mut _);
+ let io = sys::process::Stdio::Handle(handle);
+ process::Stdio::from_inner(io)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<OwnedHandle> for process::Stdio {
+ fn from(handle: OwnedHandle) -> process::Stdio {
+ let handle = sys::handle::Handle::from_inner(handle);
let io = sys::process::Stdio::Handle(handle);
process::Stdio::from_inner(io)
}
@@ -22,14 +33,29 @@
impl AsRawHandle for process::Child {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.as_inner().handle().raw() as *mut _
+ self.as_inner().handle().as_raw_handle() as *mut _
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsHandle for process::Child {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ self.as_inner().handle().as_handle()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawHandle for process::Child {
fn into_raw_handle(self) -> RawHandle {
- self.into_inner().into_handle().into_raw() as *mut _
+ self.into_inner().into_handle().into_raw_handle() as *mut _
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl From<process::Child> for OwnedHandle {
+ fn from(child: process::Child) -> OwnedHandle {
+ child.into_inner().into_handle().into_inner()
}
}
@@ -37,7 +63,7 @@
impl AsRawHandle for process::ChildStdin {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.as_inner().handle().raw() as *mut _
+ self.as_inner().handle().as_raw_handle() as *mut _
}
}
@@ -45,7 +71,7 @@
impl AsRawHandle for process::ChildStdout {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.as_inner().handle().raw() as *mut _
+ self.as_inner().handle().as_raw_handle() as *mut _
}
}
@@ -53,28 +79,28 @@
impl AsRawHandle for process::ChildStderr {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.as_inner().handle().raw() as *mut _
+ self.as_inner().handle().as_raw_handle() as *mut _
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawHandle for process::ChildStdin {
fn into_raw_handle(self) -> RawHandle {
- self.into_inner().into_handle().into_raw() as *mut _
+ self.into_inner().into_handle().into_raw_handle() as *mut _
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawHandle for process::ChildStdout {
fn into_raw_handle(self) -> RawHandle {
- self.into_inner().into_handle().into_raw() as *mut _
+ self.into_inner().into_handle().into_raw_handle() as *mut _
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawHandle for process::ChildStderr {
fn into_raw_handle(self) -> RawHandle {
- self.into_inner().into_handle().into_raw() as *mut _
+ self.into_inner().into_handle().into_raw_handle() as *mut _
}
}
diff --git a/library/std/src/os/windows/thread.rs b/library/std/src/os/windows/thread.rs
index 6bd0205..fb1bf66 100644
--- a/library/std/src/os/windows/thread.rs
+++ b/library/std/src/os/windows/thread.rs
@@ -10,7 +10,7 @@
impl<T> AsRawHandle for thread::JoinHandle<T> {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.as_inner().handle().raw() as *mut _
+ self.as_inner().handle().as_raw_handle() as *mut _
}
}
@@ -18,6 +18,6 @@
impl<T> IntoRawHandle for thread::JoinHandle<T> {
#[inline]
fn into_raw_handle(self) -> RawHandle {
- self.into_inner().into_handle().into_raw() as *mut _
+ self.into_inner().into_handle().into_raw_handle() as *mut _
}
}
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index 7bc987d..c1c0395 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -3,24 +3,14 @@
#![stable(feature = "std_panic", since = "1.9.0")]
use crate::any::Any;
-use crate::cell::UnsafeCell;
use crate::collections;
-use crate::fmt;
-use crate::future::Future;
-use crate::ops::{Deref, DerefMut};
use crate::panicking;
-use crate::pin::Pin;
-use crate::ptr::{NonNull, Unique};
-use crate::rc::Rc;
-use crate::stream::Stream;
-use crate::sync::atomic;
-use crate::sync::{Arc, Mutex, RwLock};
-use crate::task::{Context, Poll};
+use crate::sync::{Mutex, RwLock};
use crate::thread::Result;
#[doc(hidden)]
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
-#[allow_internal_unstable(libstd_sys_internals)]
+#[allow_internal_unstable(libstd_sys_internals, const_format_args)]
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")]
#[rustc_macro_transparency = "semitransparent"]
pub macro panic_2015 {
@@ -31,7 +21,7 @@
$crate::rt::begin_panic($msg)
}),
($fmt:expr, $($arg:tt)+) => ({
- $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+))
+ $crate::rt::begin_panic_fmt(&$crate::const_format_args!($fmt, $($arg)+))
}),
}
@@ -45,6 +35,9 @@
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub use core::panic::{Location, PanicInfo};
+#[stable(feature = "catch_unwind", since = "1.9.0")]
+pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
+
/// Panic the current thread with the given message as the panic payload.
///
/// The message can be of any (`Any + Send`) type, not just strings.
@@ -60,259 +53,16 @@
crate::panicking::begin_panic(msg);
}
-/// A marker trait which represents "panic safe" types in Rust.
-///
-/// This trait is implemented by default for many types and behaves similarly in
-/// terms of inference of implementation to the [`Send`] and [`Sync`] traits. The
-/// purpose of this trait is to encode what types are safe to cross a [`catch_unwind`]
-/// boundary with no fear of unwind safety.
-///
-/// ## What is unwind safety?
-///
-/// In Rust a function can "return" early if it either panics or calls a
-/// function which transitively panics. This sort of control flow is not always
-/// anticipated, and has the possibility of causing subtle bugs through a
-/// combination of two critical components:
-///
-/// 1. A data structure is in a temporarily invalid state when the thread
-/// panics.
-/// 2. This broken invariant is then later observed.
-///
-/// Typically in Rust, it is difficult to perform step (2) because catching a
-/// panic involves either spawning a thread (which in turns makes it difficult
-/// to later witness broken invariants) or using the `catch_unwind` function in this
-/// module. Additionally, even if an invariant is witnessed, it typically isn't a
-/// problem in Rust because there are no uninitialized values (like in C or C++).
-///
-/// It is possible, however, for **logical** invariants to be broken in Rust,
-/// which can end up causing behavioral bugs. Another key aspect of unwind safety
-/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
-/// memory unsafety.
-///
-/// That was a bit of a whirlwind tour of unwind safety, but for more information
-/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc].
-///
-/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
-///
-/// ## What is `UnwindSafe`?
-///
-/// Now that we've got an idea of what unwind safety is in Rust, it's also
-/// important to understand what this trait represents. As mentioned above, one
-/// way to witness broken invariants is through the `catch_unwind` function in this
-/// module as it allows catching a panic and then re-using the environment of
-/// the closure.
-///
-/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
-/// witnessing a broken invariant through the use of `catch_unwind` (catching a
-/// panic). This trait is an auto trait, so it is automatically implemented for
-/// many types, and it is also structurally composed (e.g., a struct is unwind
-/// safe if all of its components are unwind safe).
-///
-/// Note, however, that this is not an unsafe trait, so there is not a succinct
-/// contract that this trait is providing. Instead it is intended as more of a
-/// "speed bump" to alert users of `catch_unwind` that broken invariants may be
-/// witnessed and may need to be accounted for.
-///
-/// ## Who implements `UnwindSafe`?
-///
-/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
-/// unwind safe. The general idea is that any mutable state which can be shared
-/// across `catch_unwind` is not unwind safe by default. This is because it is very
-/// easy to witness a broken invariant outside of `catch_unwind` as the data is
-/// simply accessed as usual.
-///
-/// Types like `&Mutex<T>`, however, are unwind safe because they implement
-/// poisoning by default. They still allow witnessing a broken invariant, but
-/// they already provide their own "speed bumps" to do so.
-///
-/// ## When should `UnwindSafe` be used?
-///
-/// It is not intended that most types or functions need to worry about this trait.
-/// It is only used as a bound on the `catch_unwind` function and as mentioned
-/// above, the lack of `unsafe` means it is mostly an advisory. The
-/// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be
-/// implemented for any closed over variables passed to `catch_unwind`.
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "unwind_safe_trait")]
-#[rustc_on_unimplemented(
- message = "the type `{Self}` may not be safely transferred across an unwind boundary",
- label = "`{Self}` may not be safely transferred across an unwind boundary"
-)]
-pub auto trait UnwindSafe {}
-
-/// A marker trait representing types where a shared reference is considered
-/// unwind safe.
-///
-/// This trait is namely not implemented by [`UnsafeCell`], the root of all
-/// interior mutability.
-///
-/// This is a "helper marker trait" used to provide impl blocks for the
-/// [`UnwindSafe`] trait, for more information see that documentation.
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "ref_unwind_safe_trait")]
-#[rustc_on_unimplemented(
- message = "the type `{Self}` may contain interior mutability and a reference may not be safely \
- transferrable across a catch_unwind boundary",
- label = "`{Self}` may contain interior mutability and a reference may not be safely \
- transferrable across a catch_unwind boundary"
-)]
-pub auto trait RefUnwindSafe {}
-
-/// A simple wrapper around a type to assert that it is unwind safe.
-///
-/// When using [`catch_unwind`] it may be the case that some of the closed over
-/// variables are not unwind safe. For example if `&mut T` is captured the
-/// compiler will generate a warning indicating that it is not unwind safe. It
-/// may not be the case, however, that this is actually a problem due to the
-/// specific usage of [`catch_unwind`] if unwind safety is specifically taken into
-/// account. This wrapper struct is useful for a quick and lightweight
-/// annotation that a variable is indeed unwind safe.
-///
-/// # Examples
-///
-/// One way to use `AssertUnwindSafe` is to assert that the entire closure
-/// itself is unwind safe, bypassing all checks for all variables:
-///
-/// ```
-/// use std::panic::{self, AssertUnwindSafe};
-///
-/// let mut variable = 4;
-///
-/// // This code will not compile because the closure captures `&mut variable`
-/// // which is not considered unwind safe by default.
-///
-/// // panic::catch_unwind(|| {
-/// // variable += 3;
-/// // });
-///
-/// // This, however, will compile due to the `AssertUnwindSafe` wrapper
-/// let result = panic::catch_unwind(AssertUnwindSafe(|| {
-/// variable += 3;
-/// }));
-/// // ...
-/// ```
-///
-/// Wrapping the entire closure amounts to a blanket assertion that all captured
-/// variables are unwind safe. This has the downside that if new captures are
-/// added in the future, they will also be considered unwind safe. Therefore,
-/// you may prefer to just wrap individual captures, as shown below. This is
-/// more annotation, but it ensures that if a new capture is added which is not
-/// unwind safe, you will get a compilation error at that time, which will
-/// allow you to consider whether that new capture in fact represent a bug or
-/// not.
-///
-/// ```
-/// use std::panic::{self, AssertUnwindSafe};
-///
-/// let mut variable = 4;
-/// let other_capture = 3;
-///
-/// let result = {
-/// let mut wrapper = AssertUnwindSafe(&mut variable);
-/// panic::catch_unwind(move || {
-/// **wrapper += other_capture;
-/// })
-/// };
-/// // ...
-/// ```
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-pub struct AssertUnwindSafe<T>(#[stable(feature = "catch_unwind", since = "1.9.0")] pub T);
-
-// Implementations of the `UnwindSafe` trait:
-//
-// * By default everything is unwind safe
-// * pointers T contains mutability of some form are not unwind safe
-// * Unique, an owning pointer, lifts an implementation
-// * Types like Mutex/RwLock which are explicitly poisoned are unwind safe
-// * Our custom AssertUnwindSafe wrapper is indeed unwind safe
-
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: ?Sized> !UnwindSafe for &mut T {}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: RefUnwindSafe + ?Sized> UnwindSafe for &T {}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *const T {}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *mut T {}
-#[unstable(feature = "ptr_internals", issue = "none")]
-impl<T: UnwindSafe + ?Sized> UnwindSafe for Unique<T> {}
-#[stable(feature = "nonnull", since = "1.25.0")]
-impl<T: RefUnwindSafe + ?Sized> UnwindSafe for NonNull<T> {}
#[stable(feature = "catch_unwind", since = "1.9.0")]
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
#[stable(feature = "catch_unwind", since = "1.9.0")]
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T> UnwindSafe for AssertUnwindSafe<T> {}
-
-// not covered via the Shared impl above b/c the inner contents use
-// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the
-// impl up one level to Arc/Rc itself
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {}
-
-// Pretty simple implementations for the `RefUnwindSafe` marker trait,
-// basically just saying that `UnsafeCell` is the
-// only thing which doesn't implement it (which then transitively applies to
-// everything else).
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T: ?Sized> !RefUnwindSafe for UnsafeCell<T> {}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T> RefUnwindSafe for AssertUnwindSafe<T> {}
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
-#[cfg(target_has_atomic_load_store = "ptr")]
-#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
-impl RefUnwindSafe for atomic::AtomicIsize {}
-#[cfg(target_has_atomic_load_store = "8")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicI8 {}
-#[cfg(target_has_atomic_load_store = "16")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicI16 {}
-#[cfg(target_has_atomic_load_store = "32")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicI32 {}
-#[cfg(target_has_atomic_load_store = "64")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicI64 {}
-#[cfg(target_has_atomic_load_store = "128")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
-impl RefUnwindSafe for atomic::AtomicI128 {}
-
-#[cfg(target_has_atomic_load_store = "ptr")]
-#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
-impl RefUnwindSafe for atomic::AtomicUsize {}
-#[cfg(target_has_atomic_load_store = "8")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicU8 {}
-#[cfg(target_has_atomic_load_store = "16")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicU16 {}
-#[cfg(target_has_atomic_load_store = "32")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicU32 {}
-#[cfg(target_has_atomic_load_store = "64")]
-#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
-impl RefUnwindSafe for atomic::AtomicU64 {}
-#[cfg(target_has_atomic_load_store = "128")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
-impl RefUnwindSafe for atomic::AtomicU128 {}
-
-#[cfg(target_has_atomic_load_store = "8")]
-#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
-impl RefUnwindSafe for atomic::AtomicBool {}
-
-#[cfg(target_has_atomic_load_store = "ptr")]
-#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
-impl<T> RefUnwindSafe for atomic::AtomicPtr<T> {}
-
// https://github.com/rust-lang/rust/issues/62301
#[stable(feature = "hashbrown", since = "1.36.0")]
impl<K, V, S> UnwindSafe for collections::HashMap<K, V, S>
@@ -323,61 +73,6 @@
{
}
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T> Deref for AssertUnwindSafe<T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- &self.0
- }
-}
-
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<T> DerefMut for AssertUnwindSafe<T> {
- fn deref_mut(&mut self) -> &mut T {
- &mut self.0
- }
-}
-
-#[stable(feature = "catch_unwind", since = "1.9.0")]
-impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> {
- type Output = R;
-
- extern "rust-call" fn call_once(self, _args: ()) -> R {
- (self.0)()
- }
-}
-
-#[stable(feature = "std_debug", since = "1.16.0")]
-impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("AssertUnwindSafe").field(&self.0).finish()
- }
-}
-
-#[stable(feature = "futures_api", since = "1.36.0")]
-impl<F: Future> Future for AssertUnwindSafe<F> {
- type Output = F::Output;
-
- fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
- let pinned_field = unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) };
- F::poll(pinned_field, cx)
- }
-}
-
-#[unstable(feature = "async_stream", issue = "79024")]
-impl<S: Stream> Stream for AssertUnwindSafe<S> {
- type Item = S::Item;
-
- fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
- unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx)
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.0.size_hint()
- }
-}
-
/// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
///
/// This function will return `Ok` with the closure's result if the closure
@@ -406,7 +101,7 @@
///
/// # Notes
///
-/// Note that this function **may not catch all panics** in Rust. A panic in
+/// Note that this function **might not catch all panics** in Rust. A panic in
/// Rust is not always implemented via unwinding, but can be implemented by
/// aborting the process as well. This function *only* catches unwinding panics,
/// not those that abort the process.
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 0b9c9fb..7de7009 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -43,11 +43,13 @@
#[allow(improper_ctypes)]
extern "C" {
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
+}
+#[allow(improper_ctypes)]
+extern "C-unwind" {
/// `payload` is passed through another layer of raw pointers as `&mut dyn Trait` is not
/// FFI-safe. `BoxMeUp` lazily performs allocation only when needed (this avoids allocations
/// when using the "abort" panic runtime).
- #[unwind(allowed)]
fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32;
}
@@ -448,6 +450,7 @@
#[cfg_attr(not(feature = "panic_immediate_abort"), track_caller)]
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
+#[cfg_attr(all(not(bootstrap), not(test)), lang = "begin_panic_fmt")]
pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>) -> ! {
if cfg!(feature = "panic_immediate_abort") {
intrinsics::abort()
@@ -459,7 +462,6 @@
/// Entry point of panics from the libcore crate (`panic_impl` lang item).
#[cfg_attr(not(test), panic_handler)]
-#[unwind(allowed)]
pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
struct PanicPayload<'a> {
inner: &'a fmt::Arguments<'a>,
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index c71751e..2a9c361 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -315,7 +315,7 @@
}
// basic workhorse for splitting stem and extension
-fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
+fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
if os_str_as_u8_slice(file) == b".." {
return (Some(file), None);
}
@@ -334,6 +334,25 @@
}
}
+fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) {
+ let slice = os_str_as_u8_slice(file);
+ if slice == b".." {
+ return (file, None);
+ }
+
+ // The unsafety here stems from converting between &OsStr and &[u8]
+ // and back. This is safe to do because (1) we only look at ASCII
+ // contents of the encoding and (2) new &OsStr values are produced
+ // only from ASCII-bounded slices of existing &OsStr values.
+ let i = match slice[1..].iter().position(|b| *b == b'.') {
+ Some(i) => i + 1,
+ None => return (file, None),
+ };
+ let before = &slice[..i];
+ let after = &slice[i + 1..];
+ unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) }
+}
+
////////////////////////////////////////////////////////////////////////////////
// The core iterators
////////////////////////////////////////////////////////////////////////////////
@@ -962,7 +981,7 @@
impl<'a> cmp::PartialOrd for Components<'a> {
#[inline]
fn partial_cmp(&self, other: &Components<'a>) -> Option<cmp::Ordering> {
- Iterator::partial_cmp(self.clone(), other.clone())
+ Some(compare_components(self.clone(), other.clone()))
}
}
@@ -970,10 +989,43 @@
impl cmp::Ord for Components<'_> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
- Iterator::cmp(self.clone(), other.clone())
+ compare_components(self.clone(), other.clone())
}
}
+fn compare_components(mut left: Components<'_>, mut right: Components<'_>) -> cmp::Ordering {
+ // Fast path for long shared prefixes
+ //
+ // - compare raw bytes to find first mismatch
+ // - backtrack to find separator before mismatch to avoid ambiguous parsings of '.' or '..' characters
+ // - if found update state to only do a component-wise comparison on the remainder,
+ // otherwise do it on the full path
+ //
+ // The fast path isn't taken for paths with a PrefixComponent to avoid backtracking into
+ // the middle of one
+ if left.prefix.is_none() && right.prefix.is_none() && left.front == right.front {
+ // this might benefit from a [u8]::first_mismatch simd implementation, if it existed
+ let first_difference =
+ match left.path.iter().zip(right.path.iter()).position(|(&a, &b)| a != b) {
+ None if left.path.len() == right.path.len() => return cmp::Ordering::Equal,
+ None => left.path.len().min(right.path.len()),
+ Some(diff) => diff,
+ };
+
+ if let Some(previous_sep) =
+ left.path[..first_difference].iter().rposition(|&b| left.is_sep_byte(b))
+ {
+ let mismatched_component_start = previous_sep + 1;
+ left.path = &left.path[mismatched_component_start..];
+ left.front = State::Body;
+ right.path = &right.path[mismatched_component_start..];
+ right.front = State::Body;
+ }
+ }
+
+ Iterator::cmp(left, right)
+}
+
/// An iterator over [`Path`] and its ancestors.
///
/// This `struct` is created by the [`ancestors`] method on [`Path`].
@@ -1398,7 +1450,7 @@
/// Invokes [`shrink_to`] on the underlying instance of [`OsString`].
///
/// [`shrink_to`]: OsString::shrink_to
- #[unstable(feature = "shrink_to", issue = "56431")]
+ #[stable(feature = "shrink_to", since = "1.56.0")]
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
@@ -1704,7 +1756,7 @@
impl cmp::PartialOrd for PathBuf {
#[inline]
fn partial_cmp(&self, other: &PathBuf) -> Option<cmp::Ordering> {
- self.components().partial_cmp(other.components())
+ Some(compare_components(self.components(), other.components()))
}
}
@@ -1712,7 +1764,7 @@
impl cmp::Ord for PathBuf {
#[inline]
fn cmp(&self, other: &PathBuf) -> cmp::Ordering {
- self.components().cmp(other.components())
+ compare_components(self.components(), other.components())
}
}
@@ -2180,9 +2232,49 @@
/// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap());
/// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap());
/// ```
+ ///
+ /// # See Also
+ /// This method is similar to [`Path::file_prefix`], which extracts the portion of the file name
+ /// before the *first* `.`
+ ///
+ /// [`Path::file_prefix`]: Path::file_prefix
+ ///
#[stable(feature = "rust1", since = "1.0.0")]
pub fn file_stem(&self) -> Option<&OsStr> {
- self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after))
+ self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.or(after))
+ }
+
+ /// Extracts the prefix of [`self.file_name`].
+ ///
+ /// The prefix is:
+ ///
+ /// * [`None`], if there is no file name;
+ /// * The entire file name if there is no embedded `.`;
+ /// * The portion of the file name before the first non-beginning `.`;
+ /// * The entire file name if the file name begins with `.` and has no other `.`s within;
+ /// * The portion of the file name before the second `.` if the file name begins with `.`
+ ///
+ /// [`self.file_name`]: Path::file_name
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![feature(path_file_prefix)]
+ /// use std::path::Path;
+ ///
+ /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap());
+ /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap());
+ /// ```
+ ///
+ /// # See Also
+ /// This method is similar to [`Path::file_stem`], which extracts the portion of the file name
+ /// before the *last* `.`
+ ///
+ /// [`Path::file_stem`]: Path::file_stem
+ ///
+ #[unstable(feature = "path_file_prefix", issue = "86319")]
+ pub fn file_prefix(&self) -> Option<&OsStr> {
+ self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before))
}
/// Extracts the extension of [`self.file_name`], if possible.
@@ -2206,7 +2298,7 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn extension(&self) -> Option<&OsStr> {
- self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after))
+ self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after))
}
/// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
@@ -2686,7 +2778,7 @@
impl cmp::PartialEq for Path {
#[inline]
fn eq(&self, other: &Path) -> bool {
- self.components().eq(other.components())
+ self.components() == other.components()
}
}
@@ -2706,7 +2798,7 @@
impl cmp::PartialOrd for Path {
#[inline]
fn partial_cmp(&self, other: &Path) -> Option<cmp::Ordering> {
- self.components().partial_cmp(other.components())
+ Some(compare_components(self.components(), other.components()))
}
}
@@ -2714,7 +2806,7 @@
impl cmp::Ord for Path {
#[inline]
fn cmp(&self, other: &Path) -> cmp::Ordering {
- self.components().cmp(other.components())
+ compare_components(self.components(), other.components())
}
}
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index 896d6c2..ce23cf6 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -1,9 +1,11 @@
use super::*;
+use crate::collections::BTreeSet;
use crate::rc::Rc;
use crate::sync::Arc;
+use core::hint::black_box;
-macro_rules! t(
+macro_rules! t (
($path:expr, iter: $iter:expr) => (
{
let path = Path::new($path);
@@ -73,15 +75,33 @@
}
);
+ ($path:expr, file_prefix: $file_prefix:expr, extension: $extension:expr) => (
+ {
+ let path = Path::new($path);
+
+ let prefix = path.file_prefix().map(|p| p.to_str().unwrap());
+ let exp_prefix: Option<&str> = $file_prefix;
+ assert!(prefix == exp_prefix, "file_prefix: Expected {:?}, found {:?}",
+ exp_prefix, prefix);
+
+ let ext = path.extension().map(|p| p.to_str().unwrap());
+ let exp_ext: Option<&str> = $extension;
+ assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}",
+ exp_ext, ext);
+ }
+ );
+
($path:expr, iter: $iter:expr,
has_root: $has_root:expr, is_absolute: $is_absolute:expr,
parent: $parent:expr, file_name: $file:expr,
- file_stem: $file_stem:expr, extension: $extension:expr) => (
+ file_stem: $file_stem:expr, extension: $extension:expr,
+ file_prefix: $file_prefix:expr) => (
{
t!($path, iter: $iter);
t!($path, has_root: $has_root, is_absolute: $is_absolute);
t!($path, parent: $parent, file_name: $file);
t!($path, file_stem: $file_stem, extension: $extension);
+ t!($path, file_prefix: $file_prefix, extension: $extension);
}
);
);
@@ -116,7 +136,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo",
@@ -126,7 +147,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("/",
@@ -136,7 +158,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("/foo",
@@ -146,7 +169,8 @@
parent: Some("/"),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/",
@@ -156,7 +180,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("/foo/",
@@ -166,7 +191,8 @@
parent: Some("/"),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/bar",
@@ -176,7 +202,8 @@
parent: Some("foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("/foo/bar",
@@ -186,7 +213,8 @@
parent: Some("/foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("///foo///",
@@ -196,7 +224,8 @@
parent: Some("/"),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("///foo///bar",
@@ -206,7 +235,8 @@
parent: Some("///foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("./.",
@@ -216,7 +246,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("/..",
@@ -226,7 +257,8 @@
parent: Some("/"),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("../",
@@ -236,7 +268,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo/.",
@@ -246,7 +279,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/..",
@@ -256,7 +290,8 @@
parent: Some("foo"),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo/./",
@@ -266,7 +301,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/./bar",
@@ -276,7 +312,8 @@
parent: Some("foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("foo/../",
@@ -286,7 +323,8 @@
parent: Some("foo"),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo/../bar",
@@ -296,7 +334,8 @@
parent: Some("foo/.."),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("./a",
@@ -306,7 +345,8 @@
parent: Some("."),
file_name: Some("a"),
file_stem: Some("a"),
- extension: None
+ extension: None,
+ file_prefix: Some("a")
);
t!(".",
@@ -316,7 +356,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("./",
@@ -326,7 +367,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("a/b",
@@ -336,7 +378,8 @@
parent: Some("a"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
);
t!("a//b",
@@ -346,7 +389,8 @@
parent: Some("a"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
);
t!("a/./b",
@@ -356,7 +400,8 @@
parent: Some("a"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
);
t!("a/b/c",
@@ -366,7 +411,8 @@
parent: Some("a/b"),
file_name: Some("c"),
file_stem: Some("c"),
- extension: None
+ extension: None,
+ file_prefix: Some("c")
);
t!(".foo",
@@ -376,7 +422,41 @@
parent: Some(""),
file_name: Some(".foo"),
file_stem: Some(".foo"),
- extension: None
+ extension: None,
+ file_prefix: Some(".foo")
+ );
+
+ t!("a/.foo",
+ iter: ["a", ".foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some(".foo"),
+ file_stem: Some(".foo"),
+ extension: None,
+ file_prefix: Some(".foo")
+ );
+
+ t!("a/.rustfmt.toml",
+ iter: ["a", ".rustfmt.toml"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some(".rustfmt.toml"),
+ file_stem: Some(".rustfmt"),
+ extension: Some("toml"),
+ file_prefix: Some(".rustfmt")
+ );
+
+ t!("a/.x.y.z",
+ iter: ["a", ".x.y.z"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some(".x.y.z"),
+ file_stem: Some(".x.y"),
+ extension: Some("z"),
+ file_prefix: Some(".x")
);
}
@@ -390,7 +470,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo",
@@ -400,7 +481,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("/",
@@ -410,7 +492,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\",
@@ -420,7 +503,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("c:",
@@ -430,7 +514,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("c:\\",
@@ -440,7 +525,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("c:/",
@@ -450,7 +536,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("/foo",
@@ -460,7 +547,8 @@
parent: Some("/"),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/",
@@ -470,7 +558,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("/foo/",
@@ -480,7 +569,8 @@
parent: Some("/"),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/bar",
@@ -490,7 +580,8 @@
parent: Some("foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("/foo/bar",
@@ -500,7 +591,8 @@
parent: Some("/foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("///foo///",
@@ -510,7 +602,8 @@
parent: Some("/"),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("///foo///bar",
@@ -520,7 +613,8 @@
parent: Some("///foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("./.",
@@ -530,7 +624,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("/..",
@@ -540,7 +635,8 @@
parent: Some("/"),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("../",
@@ -550,7 +646,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo/.",
@@ -560,7 +657,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/..",
@@ -570,7 +668,8 @@
parent: Some("foo"),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo/./",
@@ -580,7 +679,8 @@
parent: Some(""),
file_name: Some("foo"),
file_stem: Some("foo"),
- extension: None
+ extension: None,
+ file_prefix: Some("foo")
);
t!("foo/./bar",
@@ -590,7 +690,8 @@
parent: Some("foo"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("foo/../",
@@ -600,7 +701,8 @@
parent: Some("foo"),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("foo/../bar",
@@ -610,7 +712,8 @@
parent: Some("foo/.."),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("./a",
@@ -620,7 +723,8 @@
parent: Some("."),
file_name: Some("a"),
file_stem: Some("a"),
- extension: None
+ extension: None,
+ file_prefix: Some("a")
);
t!(".",
@@ -630,7 +734,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("./",
@@ -640,7 +745,8 @@
parent: Some(""),
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("a/b",
@@ -650,7 +756,8 @@
parent: Some("a"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
);
t!("a//b",
@@ -660,7 +767,8 @@
parent: Some("a"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
);
t!("a/./b",
@@ -670,7 +778,8 @@
parent: Some("a"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
);
t!("a/b/c",
@@ -680,7 +789,9 @@
parent: Some("a/b"),
file_name: Some("c"),
file_stem: Some("c"),
- extension: None);
+ extension: None,
+ file_prefix: Some("c")
+ );
t!("a\\b\\c",
iter: ["a", "b", "c"],
@@ -689,7 +800,8 @@
parent: Some("a\\b"),
file_name: Some("c"),
file_stem: Some("c"),
- extension: None
+ extension: None,
+ file_prefix: Some("c")
);
t!("\\a",
@@ -699,7 +811,8 @@
parent: Some("\\"),
file_name: Some("a"),
file_stem: Some("a"),
- extension: None
+ extension: None,
+ file_prefix: Some("a")
);
t!("c:\\foo.txt",
@@ -709,7 +822,8 @@
parent: Some("c:\\"),
file_name: Some("foo.txt"),
file_stem: Some("foo"),
- extension: Some("txt")
+ extension: Some("txt"),
+ file_prefix: Some("foo")
);
t!("\\\\server\\share\\foo.txt",
@@ -719,7 +833,8 @@
parent: Some("\\\\server\\share\\"),
file_name: Some("foo.txt"),
file_stem: Some("foo"),
- extension: Some("txt")
+ extension: Some("txt"),
+ file_prefix: Some("foo")
);
t!("\\\\server\\share",
@@ -729,7 +844,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\server",
@@ -739,7 +855,8 @@
parent: Some("\\"),
file_name: Some("server"),
file_stem: Some("server"),
- extension: None
+ extension: None,
+ file_prefix: Some("server")
);
t!("\\\\?\\bar\\foo.txt",
@@ -749,7 +866,8 @@
parent: Some("\\\\?\\bar\\"),
file_name: Some("foo.txt"),
file_stem: Some("foo"),
- extension: Some("txt")
+ extension: Some("txt"),
+ file_prefix: Some("foo")
);
t!("\\\\?\\bar",
@@ -759,7 +877,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\",
@@ -769,7 +888,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\UNC\\server\\share\\foo.txt",
@@ -779,7 +899,8 @@
parent: Some("\\\\?\\UNC\\server\\share\\"),
file_name: Some("foo.txt"),
file_stem: Some("foo"),
- extension: Some("txt")
+ extension: Some("txt"),
+ file_prefix: Some("foo")
);
t!("\\\\?\\UNC\\server",
@@ -789,7 +910,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\UNC\\",
@@ -799,7 +921,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\C:\\foo.txt",
@@ -809,7 +932,8 @@
parent: Some("\\\\?\\C:\\"),
file_name: Some("foo.txt"),
file_stem: Some("foo"),
- extension: Some("txt")
+ extension: Some("txt"),
+ file_prefix: Some("foo")
);
t!("\\\\?\\C:\\",
@@ -819,7 +943,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\C:",
@@ -829,7 +954,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\foo/bar",
@@ -839,7 +965,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\C:/foo",
@@ -849,7 +976,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\.\\foo\\bar",
@@ -859,7 +987,8 @@
parent: Some("\\\\.\\foo\\"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("\\\\.\\foo",
@@ -869,7 +998,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\.\\foo/bar",
@@ -879,7 +1009,8 @@
parent: Some("\\\\.\\foo/"),
file_name: Some("bar"),
file_stem: Some("bar"),
- extension: None
+ extension: None,
+ file_prefix: Some("bar")
);
t!("\\\\.\\foo\\bar/baz",
@@ -889,7 +1020,8 @@
parent: Some("\\\\.\\foo\\bar"),
file_name: Some("baz"),
file_stem: Some("baz"),
- extension: None
+ extension: None,
+ file_prefix: Some("baz")
);
t!("\\\\.\\",
@@ -899,7 +1031,8 @@
parent: None,
file_name: None,
file_stem: None,
- extension: None
+ extension: None,
+ file_prefix: None
);
t!("\\\\?\\a\\b\\",
@@ -909,7 +1042,52 @@
parent: Some("\\\\?\\a\\"),
file_name: Some("b"),
file_stem: Some("b"),
- extension: None
+ extension: None,
+ file_prefix: Some("b")
+ );
+
+ t!("\\\\?\\C:\\foo.txt.zip",
+ iter: ["\\\\?\\C:", "\\", "foo.txt.zip"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\C:\\"),
+ file_name: Some("foo.txt.zip"),
+ file_stem: Some("foo.txt"),
+ extension: Some("zip"),
+ file_prefix: Some("foo")
+ );
+
+ t!("\\\\?\\C:\\.foo.txt.zip",
+ iter: ["\\\\?\\C:", "\\", ".foo.txt.zip"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\C:\\"),
+ file_name: Some(".foo.txt.zip"),
+ file_stem: Some(".foo.txt"),
+ extension: Some("zip"),
+ file_prefix: Some(".foo")
+ );
+
+ t!("\\\\?\\C:\\.foo",
+ iter: ["\\\\?\\C:", "\\", ".foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\C:\\"),
+ file_name: Some(".foo"),
+ file_stem: Some(".foo"),
+ extension: None,
+ file_prefix: Some(".foo")
+ );
+
+ t!("a/.x.y.z",
+ iter: ["a", ".x.y.z"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some(".x.y.z"),
+ file_stem: Some(".x.y"),
+ extension: Some("z"),
+ file_prefix: Some(".x")
);
}
@@ -949,12 +1127,59 @@
t!("..", file_stem: None, extension: None);
+ t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z"));
+
+ t!("..x.y.z", file_stem: Some("..x.y"), extension: Some("z"));
+
t!("", file_stem: None, extension: None);
}
#[test]
+pub fn test_prefix_ext() {
+ t!("foo",
+ file_prefix: Some("foo"),
+ extension: None
+ );
+
+ t!("foo.",
+ file_prefix: Some("foo"),
+ extension: Some("")
+ );
+
+ t!(".foo",
+ file_prefix: Some(".foo"),
+ extension: None
+ );
+
+ t!("foo.txt",
+ file_prefix: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("foo.bar.txt",
+ file_prefix: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("foo.bar.",
+ file_prefix: Some("foo"),
+ extension: Some("")
+ );
+
+ t!(".", file_prefix: None, extension: None);
+
+ t!("..", file_prefix: None, extension: None);
+
+ t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z"));
+
+ t!("..x.y.z", file_prefix: Some("."), extension: Some("z"));
+
+ t!("", file_prefix: None, extension: None);
+}
+
+#[test]
pub fn test_push() {
- macro_rules! tp(
+ macro_rules! tp (
($path:expr, $push:expr, $expected:expr) => ( {
let mut actual = PathBuf::from($path);
actual.push($push);
@@ -1042,7 +1267,7 @@
#[test]
pub fn test_pop() {
- macro_rules! tp(
+ macro_rules! tp (
($path:expr, $expected:expr, $output:expr) => ( {
let mut actual = PathBuf::from($path);
let output = actual.pop();
@@ -1096,7 +1321,7 @@
#[test]
pub fn test_set_file_name() {
- macro_rules! tfn(
+ macro_rules! tfn (
($path:expr, $file:expr, $expected:expr) => ( {
let mut p = PathBuf::from($path);
p.set_file_name($file);
@@ -1130,7 +1355,7 @@
#[test]
pub fn test_set_extension() {
- macro_rules! tfe(
+ macro_rules! tfe (
($path:expr, $ext:expr, $expected:expr, $output:expr) => ( {
let mut p = PathBuf::from($path);
let output = p.set_extension($ext);
@@ -1192,7 +1417,7 @@
s.finish()
}
- macro_rules! tc(
+ macro_rules! tc (
($path1:expr, $path2:expr, eq: $eq:expr,
starts_with: $starts_with:expr, ends_with: $ends_with:expr,
relative_from: $relative_from:expr) => ({
@@ -1392,3 +1617,69 @@
assert_eq!(&*rc2, path);
assert_eq!(&*arc2, path);
}
+
+#[test]
+fn test_ord() {
+ macro_rules! ord(
+ ($ord:ident, $left:expr, $right:expr) => ( {
+ assert_eq!(Path::new($left).cmp(&Path::new($right)), core::cmp::Ordering::$ord);
+ });
+ );
+
+ ord!(Less, "1", "2");
+ ord!(Less, "/foo/bar", "/foo./bar");
+ ord!(Less, "foo/bar", "foo/bar.");
+ ord!(Equal, "foo/./bar", "foo/bar/");
+ ord!(Equal, "foo/bar", "foo/bar/");
+ ord!(Equal, "foo/bar", "foo/bar/.");
+ ord!(Equal, "foo/bar", "foo/bar//");
+}
+
+#[bench]
+fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
+ let prefix = "my/home";
+ let mut paths: Vec<_> =
+ (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect();
+
+ paths.sort();
+
+ b.iter(|| {
+ black_box(paths.as_mut_slice()).sort_unstable();
+ });
+}
+
+#[bench]
+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<_> =
+ (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect();
+
+ let mut set = BTreeSet::new();
+
+ paths.iter().for_each(|p| {
+ set.insert(p.as_path());
+ });
+
+ b.iter(|| {
+ set.remove(paths[500].as_path());
+ set.insert(paths[500].as_path());
+ });
+}
+
+#[bench]
+fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) {
+ let prefix = "my/home";
+ let paths: Vec<_> =
+ (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect();
+
+ let mut set = BTreeSet::new();
+
+ paths.iter().for_each(|p| {
+ set.insert(p.as_path());
+ });
+
+ b.iter(|| {
+ set.remove(paths[500].as_path());
+ set.insert(paths[500].as_path());
+ });
+}
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 7bc1f5e..261d0e6 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -303,7 +303,7 @@
///
/// [`String`]: string/struct.String.html
///
-/// As always, remember that a human intuition for 'character' may not map to
+/// As always, remember that a human intuition for 'character' might not map to
/// Unicode's definitions. For example, despite looking similar, the 'é'
/// character is one Unicode code point while 'é' is two Unicode code points:
///
@@ -581,6 +581,8 @@
/// might be made consistent to the behavior of later editions.
///
/// ```rust,edition2018
+/// // Rust 2015 and 2018:
+///
/// # #![allow(array_into_iter)] // override our `deny(warnings)`
/// let array: [i32; 3] = [0; 3];
///
@@ -604,11 +606,13 @@
/// }
/// ```
///
-/// Starting in the 2021 edition, `array.into_iter()` will use `IntoIterator` normally to iterate
+/// Starting in the 2021 edition, `array.into_iter()` uses `IntoIterator` normally to iterate
/// by value, and `iter()` should be used to iterate by reference like previous editions.
///
-/// ```rust,edition2021,ignore
-/// # // FIXME: ignored because 2021 testing is still unstable
+#[cfg_attr(bootstrap, doc = "```rust,edition2021,ignore")]
+#[cfg_attr(not(bootstrap), doc = "```rust,edition2021")]
+/// // Rust 2021:
+///
/// let array: [i32; 3] = [0; 3];
///
/// // This iterates by reference:
@@ -631,12 +635,12 @@
/// avoid the `into_iter` syntax on those editions. If an edition update is not
/// viable/desired, there are multiple alternatives:
/// * use `iter`, equivalent to the old behavior, creating references
-/// * use [`array::IntoIter`], equivalent to the post-2021 behavior (Rust 1.51+)
+/// * use [`IntoIterator::into_iter`], equivalent to the post-2021 behavior (Rust 1.53+)
/// * replace `for ... in array.into_iter() {` with `for ... in array {`,
/// equivalent to the post-2021 behavior (Rust 1.53+)
///
/// ```rust,edition2018
-/// use std::array::IntoIter;
+/// // Rust 2015 and 2018:
///
/// let array: [i32; 3] = [0; 3];
///
@@ -647,7 +651,7 @@
/// }
///
/// // This iterates by value:
-/// for item in IntoIter::new(array) {
+/// for item in IntoIterator::into_iter(array) {
/// let x: i32 = item;
/// println!("{}", x);
/// }
@@ -660,7 +664,7 @@
///
/// // IntoIter can also start a chain.
/// // This iterates by value:
-/// for item in IntoIter::new(array).enumerate() {
+/// for item in IntoIterator::into_iter(array).enumerate() {
/// let (i, x): (usize, i32) = item;
/// println!("array[{}] = {}", i, x);
/// }
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 11a0432..c9b21fc 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -166,7 +166,7 @@
/// [`wait`]: Child::wait
#[stable(feature = "process", since = "1.0.0")]
pub struct Child {
- handle: imp::Process,
+ 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
@@ -205,6 +205,10 @@
pub stderr: Option<ChildStderr>,
}
+/// Allows extension traits within `std`.
+#[unstable(feature = "sealed", issue = "none")]
+impl crate::sealed::Sealed for Child {}
+
impl AsInner<imp::Process> for Child {
fn as_inner(&self) -> &imp::Process {
&self.handle
@@ -254,6 +258,12 @@
inner: AnonPipe,
}
+// In addition to the `impl`s here, `ChildStdin` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and
+// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows.
+
#[stable(feature = "process", since = "1.0.0")]
impl Write for ChildStdin {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -331,6 +341,12 @@
inner: AnonPipe,
}
+// In addition to the `impl`s here, `ChildStdout` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and
+// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows.
+
#[stable(feature = "process", since = "1.0.0")]
impl Read for ChildStdout {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -392,6 +408,12 @@
inner: AnonPipe,
}
+// In addition to the `impl`s here, `ChildStderr` also has `impl`s for
+// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
+// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
+// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and
+// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows.
+
#[stable(feature = "process", since = "1.0.0")]
impl Read for ChildStderr {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -1453,7 +1475,7 @@
///
/// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the
/// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8
- /// bits, and that values that didn't come from a program's call to `exit` may be invented the
+ /// bits, and that values that didn't come from a program's call to `exit` may be invented by the
/// runtime system (often, for example, 255, 254, 127 or 126).
///
/// On Unix, this will return `None` if the process was terminated by a signal.
diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs
index 2f0b32c..00a4afc 100644
--- a/library/std/src/sync/condvar.rs
+++ b/library/std/src/sync/condvar.rs
@@ -254,7 +254,7 @@
/// except that the thread will be blocked for roughly no longer
/// than `ms` milliseconds. This method should not be used for
/// precise timing due to anomalies such as preemption or platform
- /// differences that may not cause the maximum amount of time
+ /// differences that might not cause the maximum amount of time
/// waited to be precisely `ms`.
///
/// Note that the best effort is made to ensure that the time waited is
@@ -317,7 +317,7 @@
/// The semantics of this function are equivalent to [`wait`] except that
/// the thread will be blocked for roughly no longer than `dur`. This
/// method should not be used for precise timing due to anomalies such as
- /// preemption or platform differences that may not cause the maximum
+ /// preemption or platform differences that might not cause the maximum
/// amount of time waited to be precisely `dur`.
///
/// Note that the best effort is made to ensure that the time waited is
@@ -392,7 +392,7 @@
/// The semantics of this function are equivalent to [`wait_while`] except
/// that the thread will be blocked for roughly no longer than `dur`. This
/// method should not be used for precise timing due to anomalies such as
- /// preemption or platform differences that may not cause the maximum
+ /// preemption or platform differences that might not cause the maximum
/// amount of time waited to be precisely `dur`.
///
/// Note that the best effort is made to ensure that the time waited is
diff --git a/library/std/src/sync/mpsc/mpsc_queue.rs b/library/std/src/sync/mpsc/mpsc_queue.rs
index b93eb05..cdd64a5 100644
--- a/library/std/src/sync/mpsc/mpsc_queue.rs
+++ b/library/std/src/sync/mpsc/mpsc_queue.rs
@@ -6,7 +6,7 @@
//!
//! Note that the current implementation of this queue has a caveat of the `pop`
//! method, and see the method for more information about it. Due to this
-//! caveat, this queue may not be appropriate for all use-cases.
+//! caveat, this queue might not be appropriate for all use-cases.
// https://www.1024cores.net/home/lock-free-algorithms
// /queues/non-intrusive-mpsc-node-based-queue
diff --git a/library/std/src/sync/mpsc/stream.rs b/library/std/src/sync/mpsc/stream.rs
index a652f24..2a1d3f8 100644
--- a/library/std/src/sync/mpsc/stream.rs
+++ b/library/std/src/sync/mpsc/stream.rs
@@ -339,7 +339,7 @@
// At this point in time, we have gated all future senders from sending,
// and we have flagged the channel as being disconnected. The senders
- // still have some responsibility, however, because some sends may not
+ // still have some responsibility, however, because some sends might not
// complete until after we flag the disconnection. There are more
// details in the sending methods that see DISCONNECTED
}
@@ -370,7 +370,7 @@
// at all.
//
// Hence, because of these invariants, we immediately return `Ok(true)`.
- // Note that the data may not actually be sent on the channel just yet.
+ // Note that the data might not actually be sent on the channel just yet.
// The other end could have flagged the upgrade but not sent data to
// this end. This is fine because we know it's a small bounded windows
// of time until the data is actually sent.
diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs
index 6da6c18..a2e935a 100644
--- a/library/std/src/sync/once.rs
+++ b/library/std/src/sync/once.rs
@@ -198,7 +198,7 @@
/// routine is currently running.
///
/// When this function returns, it is guaranteed that some initialization
- /// has run and completed (it may not be the closure specified). It is also
+ /// has run and completed (it might not be the closure specified). It is also
/// guaranteed that any memory writes performed by the executed closure can
/// be reliably observed by other threads at this point (there is a
/// happens-before relation between the closure and code executing after the
diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs
index 05e1833..fa95033 100644
--- a/library/std/src/sync/poison.rs
+++ b/library/std/src/sync/poison.rs
@@ -120,7 +120,7 @@
/// A type alias for the result of a nonblocking locking method.
///
/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
-/// necessarily hold the associated guard in the [`Err`] type as the lock may not
+/// necessarily hold the associated guard in the [`Err`] type as the lock might not
/// have been acquired for other reasons.
#[stable(feature = "rust1", since = "1.0.0")]
pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs
index 2a54e99..576667c 100644
--- a/library/std/src/sys/common/alloc.rs
+++ b/library/std/src/sys/common/alloc.rs
@@ -14,7 +14,8 @@
target_arch = "asmjs",
target_arch = "wasm32",
target_arch = "hexagon",
- target_arch = "riscv32"
+ target_arch = "riscv32",
+ target_arch = "xtensa"
)))]
pub const MIN_ALIGN: usize = 8;
#[cfg(all(any(
diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs
index b45e871..fa8ef8f 100644
--- a/library/std/src/sys/hermit/condvar.rs
+++ b/library/std/src/sys/hermit/condvar.rs
@@ -14,7 +14,7 @@
sem2: *const c_void,
}
-pub type MovableCondvar = Box<Condvar>;
+pub type MovableCondvar = Condvar;
unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index 10c1942..185b68c 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -32,6 +32,8 @@
pub mod mutex;
pub mod net;
pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
@@ -47,7 +49,6 @@
pub mod time;
use crate::io::ErrorKind;
-pub use crate::sys_common::os_str_bytes as os_str;
#[allow(unused_extern_crates)]
pub extern crate hermit_abi as abi;
diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs
index 4221799..691e7e0 100644
--- a/library/std/src/sys/hermit/mutex.rs
+++ b/library/std/src/sys/hermit/mutex.rs
@@ -156,7 +156,7 @@
inner: Spinlock<MutexInner>,
}
-pub type MovableMutex = Box<Mutex>;
+pub type MovableMutex = Mutex;
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
index 3f0c99c..880ef67 100644
--- a/library/std/src/sys/hermit/net.rs
+++ b/library/std/src/sys/hermit/net.rs
@@ -182,6 +182,14 @@
Ok(self.clone())
}
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"set_nodelay failed"))
diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs
index eeb30a5..8f927df 100644
--- a/library/std/src/sys/hermit/os.rs
+++ b/library/std/src/sys/hermit/os.rs
@@ -140,13 +140,8 @@
}
}
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
- unsafe {
- match ENV.as_ref().unwrap().lock().unwrap().get_mut(k) {
- Some(value) => Ok(Some(value.clone())),
- None => Ok(None),
- }
- }
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs
index d205818..64eaa2f 100644
--- a/library/std/src/sys/hermit/rwlock.rs
+++ b/library/std/src/sys/hermit/rwlock.rs
@@ -8,7 +8,7 @@
state: UnsafeCell<State>,
}
-pub type MovableRWLock = Box<RWLock>;
+pub type MovableRWLock = RWLock;
enum State {
Unlocked,
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
index fce6b42..a2a763c 100644
--- a/library/std/src/sys/sgx/mod.rs
+++ b/library/std/src/sys/sgx/mod.rs
@@ -26,6 +26,8 @@
pub mod mutex;
pub mod net;
pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
pub mod path;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
@@ -37,8 +39,6 @@
pub mod thread_local_key;
pub mod time;
-pub use crate::sys_common::os_str_bytes as os_str;
-
// 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) {
diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs
index 1b5ced4..0b2d1f4 100644
--- a/library/std/src/sys/sgx/mutex.rs
+++ b/library/std/src/sys/sgx/mutex.rs
@@ -8,7 +8,8 @@
inner: SpinMutex<WaitVariable<bool>>,
}
-pub type MovableMutex = Mutex;
+// not movable: see UnsafeList implementation
+pub type MovableMutex = Box<Mutex>;
// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
impl Mutex {
diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs
index 3a69aa0..89c5af6 100644
--- a/library/std/src/sys/sgx/net.rs
+++ b/library/std/src/sys/sgx/net.rs
@@ -183,6 +183,14 @@
Ok(self.clone())
}
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ sgx_ineffective(None)
+ }
+
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
sgx_ineffective(())
}
diff --git a/library/std/src/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs
index 144248d..5f8b8de 100644
--- a/library/std/src/sys/sgx/os.rs
+++ b/library/std/src/sys/sgx/os.rs
@@ -106,8 +106,8 @@
get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default().into_iter()
}
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
- Ok(get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()))
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
index cf2f088..c736cab 100644
--- a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
@@ -23,6 +23,7 @@
}
}
+// WARNING: self-referential struct!
pub struct UnsafeList<T> {
head_tail: NonNull<UnsafeListEntry<T>>,
head_tail_entry: Option<UnsafeListEntry<T>>,
diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs
index 1b71905..7c3d957 100644
--- a/library/std/src/sys/unix/alloc.rs
+++ b/library/std/src/sys/unix/alloc.rs
@@ -57,7 +57,8 @@
target_os = "android",
target_os = "illumos",
target_os = "redox",
- target_os = "solaris"
+ target_os = "solaris",
+ target_os = "espidf"
))] {
#[inline]
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index 0bd1ea6..ee5e398 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -246,3 +246,15 @@
Args { iter: res.into_iter() }
}
}
+
+#[cfg(target_os = "espidf")]
+mod imp {
+ use super::Args;
+
+ #[inline(always)]
+ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+ pub fn args() -> Args {
+ Args { iter: Vec::new().into_iter() }
+ }
+}
diff --git a/library/std/src/sys/unix/condvar.rs b/library/std/src/sys/unix/condvar.rs
index e38f91a..61261c0 100644
--- a/library/std/src/sys/unix/condvar.rs
+++ b/library/std/src/sys/unix/condvar.rs
@@ -34,12 +34,23 @@
))]
pub unsafe fn init(&mut self) {}
+ // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet
+ // So on that platform, init() should always be called
+ // Moreover, that platform does not have pthread_condattr_setclock support,
+ // hence that initialization should be skipped as well
+ #[cfg(target_os = "espidf")]
+ pub unsafe fn init(&mut self) {
+ let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null());
+ assert_eq!(r, 0);
+ }
+
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "l4re",
target_os = "android",
- target_os = "redox"
+ target_os = "redox",
+ target_os = "espidf"
)))]
pub unsafe fn init(&mut self) {
use crate::mem::MaybeUninit;
@@ -76,7 +87,12 @@
// where we configure condition variable to use monotonic clock (instead of
// default system clock). This approach avoids all problems that result
// from changes made to the system time.
- #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "espidf"
+ )))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
use crate::mem;
@@ -103,7 +119,12 @@
// This implementation is modeled after libcxx's condition_variable
// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
// https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
- #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "espidf"
+ ))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
use crate::ptr;
use crate::time::Instant;
diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs
index 3a88dc0..60551ae 100644
--- a/library/std/src/sys/unix/env.rs
+++ b/library/std/src/sys/unix/env.rs
@@ -184,3 +184,14 @@
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
+
+#[cfg(target_os = "espidf")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "espidf";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 821851a..0956726 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -5,21 +5,14 @@
use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
-use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::sys::cvt;
-use crate::sys_common::AsInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
use libc::{c_int, c_void};
#[derive(Debug)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
-// 32-bit c_int. Below is -2, in two's complement, but that only works out
-// because c_int is 32 bits.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
-pub struct FileDesc {
- fd: c_int,
-}
+pub struct FileDesc(OwnedFd);
// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
@@ -67,34 +60,22 @@
}
impl FileDesc {
- pub fn new(fd: c_int) -> FileDesc {
- assert_ne!(fd, -1i32);
- // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
- unsafe { FileDesc { fd } }
- }
-
- pub fn raw(&self) -> c_int {
- self.fd
- }
-
- /// Extracts the actual file descriptor without closing it.
- pub fn into_raw(self) -> c_int {
- let fd = self.fd;
- mem::forget(self);
- fd
- }
-
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
- libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
+ libc::read(
+ self.as_raw_fd(),
+ buf.as_mut_ptr() as *mut c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ )
})?;
Ok(ret as usize)
}
+ #[cfg(not(target_os = "espidf"))]
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::readv(
- self.fd,
+ self.as_raw_fd(),
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), max_iov()) as c_int,
)
@@ -102,9 +83,14 @@
Ok(ret as usize)
}
+ #[cfg(target_os = "espidf")]
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ return crate::io::default_read_vectored(|b| self.read(b), bufs);
+ }
+
#[inline]
pub fn is_read_vectored(&self) -> bool {
- true
+ cfg!(not(target_os = "espidf"))
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
@@ -132,7 +118,7 @@
unsafe {
cvt_pread64(
- self.fd,
+ self.as_raw_fd(),
buf.as_mut_ptr() as *mut c_void,
cmp::min(buf.len(), READ_LIMIT),
offset as i64,
@@ -143,15 +129,20 @@
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
- libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
+ libc::write(
+ self.as_raw_fd(),
+ buf.as_ptr() as *const c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ )
})?;
Ok(ret as usize)
}
+ #[cfg(not(target_os = "espidf"))]
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::writev(
- self.fd,
+ self.as_raw_fd(),
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), max_iov()) as c_int,
)
@@ -159,9 +150,14 @@
Ok(ret as usize)
}
+ #[cfg(target_os = "espidf")]
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ return crate::io::default_write_vectored(|b| self.write(b), bufs);
+ }
+
#[inline]
pub fn is_write_vectored(&self) -> bool {
- true
+ cfg!(not(target_os = "espidf"))
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
@@ -184,7 +180,7 @@
unsafe {
cvt_pwrite64(
- self.fd,
+ self.as_raw_fd(),
buf.as_ptr() as *const c_void,
cmp::min(buf.len(), READ_LIMIT),
offset as i64,
@@ -195,7 +191,7 @@
#[cfg(target_os = "linux")]
pub fn get_cloexec(&self) -> io::Result<bool> {
- unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
+ unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
}
#[cfg(not(any(
@@ -212,12 +208,12 @@
)))]
pub fn set_cloexec(&self) -> io::Result<()> {
unsafe {
- cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
+ cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
Ok(())
}
}
#[cfg(any(
- target_env = "newlib",
+ all(target_env = "newlib", not(target_os = "espidf")),
target_os = "solaris",
target_os = "illumos",
target_os = "emscripten",
@@ -230,20 +226,26 @@
))]
pub fn set_cloexec(&self) -> io::Result<()> {
unsafe {
- let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
+ let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
let new = previous | libc::FD_CLOEXEC;
if new != previous {
- cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
+ cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
}
Ok(())
}
}
+ #[cfg(target_os = "espidf")]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ // FD_CLOEXEC is not supported in ESP-IDF but there's no need to,
+ // because ESP-IDF does not support spawning processes either.
+ Ok(())
+ }
#[cfg(target_os = "linux")]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
let v = nonblocking as c_int;
- cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
+ cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
Ok(())
}
}
@@ -251,14 +253,14 @@
#[cfg(not(target_os = "linux"))]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
- let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
+ let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
let new = if nonblocking {
previous | libc::O_NONBLOCK
} else {
previous & !libc::O_NONBLOCK
};
if new != previous {
- cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
+ cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
}
Ok(())
}
@@ -268,8 +270,18 @@
// We want to atomically duplicate this file descriptor and set the
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
// is a POSIX flag that was added to Linux in 2.6.24.
- let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
- Ok(FileDesc::new(fd))
+ #[cfg(not(target_os = "espidf"))]
+ let cmd = libc::F_DUPFD_CLOEXEC;
+
+ // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
+ // will never be supported, as this is a bare metal framework with
+ // no capabilities for multi-process execution. While F_DUPFD is also
+ // not supported yet, it might be (currently it returns ENOSYS).
+ #[cfg(target_os = "espidf")]
+ let cmd = libc::F_DUPFD;
+
+ let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
+ Ok(unsafe { FileDesc::from_raw_fd(fd) })
}
}
@@ -284,19 +296,44 @@
}
}
-impl AsInner<c_int> for FileDesc {
- fn as_inner(&self) -> &c_int {
- &self.fd
+impl AsInner<OwnedFd> for FileDesc {
+ fn as_inner(&self) -> &OwnedFd {
+ &self.0
}
}
-impl Drop for FileDesc {
- fn drop(&mut self) {
- // Note that errors are ignored when closing a file descriptor. The
- // reason for this is that if an error occurs we don't actually know if
- // the file descriptor was closed or not, and if we retried (for
- // something like EINTR), we might close another valid file descriptor
- // opened after we closed ours.
- let _ = unsafe { libc::close(self.fd) };
+impl IntoInner<OwnedFd> for FileDesc {
+ fn into_inner(self) -> OwnedFd {
+ self.0
+ }
+}
+
+impl FromInner<OwnedFd> for FileDesc {
+ fn from_inner(owned_fd: OwnedFd) -> Self {
+ Self(owned_fd)
+ }
+}
+
+impl AsFd for FileDesc {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for FileDesc {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for FileDesc {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for FileDesc {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
}
}
diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs
index c952048..5d17e46 100644
--- a/library/std/src/sys/unix/fd/tests.rs
+++ b/library/std/src/sys/unix/fd/tests.rs
@@ -1,9 +1,10 @@
use super::{FileDesc, IoSlice};
+use crate::os::unix::io::FromRawFd;
use core::mem::ManuallyDrop;
#[test]
fn limit_vector_count() {
- let stdout = ManuallyDrop::new(unsafe { FileDesc { fd: 1 } });
+ let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) });
let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
assert!(stdout.write_vectored(&bufs).is_ok());
}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 5c8c949..6d7524a 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -4,13 +4,14 @@
use crate::fmt;
use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
use crate::sys::fd::FileDesc;
use crate::sys::time::SystemTime;
use crate::sys::{cvt, cvt_r};
-use crate::sys_common::{AsInner, FromInner};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
#[cfg(any(
all(target_os = "linux", target_env = "gnu"),
@@ -312,7 +313,7 @@
#[cfg(not(target_os = "netbsd"))]
impl FileAttr {
- #[cfg(not(target_os = "vxworks"))]
+ #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
pub fn modified(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(libc::timespec {
tv_sec: self.stat.st_mtime as libc::time_t,
@@ -320,7 +321,7 @@
}))
}
- #[cfg(target_os = "vxworks")]
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
pub fn modified(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(libc::timespec {
tv_sec: self.stat.st_mtime as libc::time_t,
@@ -328,7 +329,7 @@
}))
}
- #[cfg(not(target_os = "vxworks"))]
+ #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(libc::timespec {
tv_sec: self.stat.st_atime as libc::time_t,
@@ -336,7 +337,7 @@
}))
}
- #[cfg(target_os = "vxworks")]
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(libc::timespec {
tv_sec: self.stat.st_atime as libc::time_t,
@@ -505,7 +506,8 @@
let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
let mut entry_ptr = ptr::null_mut();
loop {
- if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
+ let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
+ if err != 0 {
if entry_ptr.is_null() {
// We encountered an error (which will be returned in this iteration), but
// we also reached the end of the directory stream. The `end_of_stream`
@@ -513,7 +515,7 @@
// (instead of looping forever)
self.end_of_stream = true;
}
- return Some(Err(Error::last_os_error()));
+ return Some(Err(Error::from_raw_os_error(err)));
}
if entry_ptr.is_null() {
return None;
@@ -609,7 +611,8 @@
target_os = "l4re",
target_os = "fuchsia",
target_os = "redox",
- target_os = "vxworks"
+ target_os = "vxworks",
+ target_os = "espidf"
))]
pub fn ino(&self) -> u64 {
self.entry.d_ino as u64
@@ -648,7 +651,8 @@
target_os = "emscripten",
target_os = "l4re",
target_os = "haiku",
- target_os = "vxworks"
+ target_os = "vxworks",
+ target_os = "espidf"
))]
fn name_bytes(&self) -> &[u8] {
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
@@ -762,11 +766,11 @@
// However, since this is a variadic function, C integer promotion rules mean that on
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
- Ok(File(FileDesc::new(fd)))
+ Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
- let fd = self.0.raw();
+ let fd = self.as_raw_fd();
cfg_has_statx! {
if let Some(ret) = unsafe { try_statx(
@@ -785,7 +789,7 @@
}
pub fn fsync(&self) -> io::Result<()> {
- cvt_r(|| unsafe { os_fsync(self.0.raw()) })?;
+ cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
return Ok(());
#[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -799,7 +803,7 @@
}
pub fn datasync(&self) -> io::Result<()> {
- cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
+ cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
return Ok(());
#[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -832,14 +836,14 @@
pub fn truncate(&self, size: u64) -> io::Result<()> {
#[cfg(target_os = "android")]
- return crate::sys::android::ftruncate64(self.0.raw(), size);
+ return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
#[cfg(not(target_os = "android"))]
{
use crate::convert::TryInto;
let size: off64_t =
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
- cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop)
+ cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
}
}
@@ -889,7 +893,7 @@
SeekFrom::End(off) => (libc::SEEK_END, off),
SeekFrom::Current(off) => (libc::SEEK_CUR, off),
};
- let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?;
+ let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
Ok(n as u64)
}
@@ -897,16 +901,8 @@
self.0.duplicate().map(File)
}
- pub fn fd(&self) -> &FileDesc {
- &self.0
- }
-
- pub fn into_fd(self) -> FileDesc {
- self.0
- }
-
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
- cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
+ cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
Ok(())
}
}
@@ -931,15 +927,57 @@
Ok(CString::new(path.as_os_str().as_bytes())?)
}
-impl FromInner<c_int> for File {
- fn from_inner(fd: c_int) -> File {
- File(FileDesc::new(fd))
+impl AsInner<FileDesc> for File {
+ fn as_inner(&self) -> &FileDesc {
+ &self.0
+ }
+}
+
+impl AsInnerMut<FileDesc> for File {
+ fn as_inner_mut(&mut self) -> &mut FileDesc {
+ &mut self.0
+ }
+}
+
+impl IntoInner<FileDesc> for File {
+ fn into_inner(self) -> FileDesc {
+ self.0
+ }
+}
+
+impl FromInner<FileDesc> for File {
+ fn from_inner(file_desc: FileDesc) -> Self {
+ Self(file_desc)
+ }
+}
+
+impl AsFd for File {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for File {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for File {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- #[cfg(target_os = "linux")]
+ #[cfg(any(target_os = "linux", target_os = "netbsd"))]
fn get_path(fd: c_int) -> Option<PathBuf> {
let mut p = PathBuf::from("/proc/self/fd");
p.push(&fd.to_string());
@@ -976,7 +1014,12 @@
Some(PathBuf::from(OsString::from_vec(buf)))
}
- #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
+ #[cfg(not(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "vxworks",
+ target_os = "netbsd"
+ )))]
fn get_path(_fd: c_int) -> Option<PathBuf> {
// FIXME(#24570): implement this for other Unix platforms
None
@@ -1002,7 +1045,7 @@
None
}
- let fd = self.0.raw();
+ let fd = self.as_raw_fd();
let mut b = f.debug_struct("File");
b.field("fd", &fd);
if let Some(path) = get_path(fd) {
@@ -1101,8 +1144,8 @@
let original = cstr(original)?;
let link = cstr(link)?;
cfg_if::cfg_if! {
- if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] {
- // VxWorks and Redox lack `linkat`, so use `link` instead. POSIX leaves
+ if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] {
+ // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
// it implementation-defined whether `link` follows symlinks, so rely on the
// `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
// Android has `linkat` on newer versions, but we happen to know `link`
@@ -1194,6 +1237,18 @@
Ok((reader, metadata))
}
+#[cfg(target_os = "espidf")]
+fn open_to_and_set_permissions(
+ to: &Path,
+ reader_metadata: crate::fs::Metadata,
+) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+ use crate::fs::OpenOptions;
+ let writer = OpenOptions::new().open(to)?;
+ let writer_metadata = writer.metadata()?;
+ Ok((writer, writer_metadata))
+}
+
+#[cfg(not(target_os = "espidf"))]
fn open_to_and_set_permissions(
to: &Path,
reader_metadata: crate::fs::Metadata,
diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs
index 3cf637c..ba63b41 100644
--- a/library/std/src/sys/unix/l4re.rs
+++ b/library/std/src/sys/unix/l4re.rs
@@ -98,6 +98,14 @@
unimpl!();
}
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unimpl!();
}
@@ -214,6 +222,14 @@
unimpl!();
}
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unimpl!();
}
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 9e553ec..f5424e3 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -5,6 +5,7 @@
pub use self::rand::hashmap_random_keys;
pub use libc::strlen;
+#[cfg(not(target_os = "espidf"))]
#[macro_use]
pub mod weak;
@@ -30,6 +31,7 @@
#[cfg(target_os = "l4re")]
pub use self::l4re::net;
pub mod os;
+pub mod os_str;
pub mod path;
pub mod pipe;
pub mod process;
@@ -42,8 +44,10 @@
pub mod thread_local_key;
pub mod time;
-pub use crate::sys_common::os_str_bytes as os_str;
+#[cfg(target_os = "espidf")]
+pub fn init(argc: isize, argv: *const *const 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) {
@@ -305,3 +309,19 @@
extern "C" {}
}
}
+
+#[cfg(target_os = "espidf")]
+mod unsupported {
+ use crate::io;
+
+ pub fn unsupported<T>() -> io::Result<T> {
+ Err(unsupported_err())
+ }
+
+ pub fn unsupported_err() -> io::Error {
+ io::Error::new_const(
+ io::ErrorKind::Unsupported,
+ &"operation not supported on this platform",
+ )
+ }
+}
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index 753cad5..9ae6d12d 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -3,13 +3,22 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::str;
use crate::sys::fd::FileDesc;
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::{Duration, Instant};
-use libc::{c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
+use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK};
+
+cfg_if::cfg_if! {
+ if #[cfg(target_vendor = "apple")] {
+ use libc::SO_LINGER_SEC as SO_LINGER;
+ } else {
+ use libc::SO_LINGER;
+ }
+}
pub use crate::sys::{cvt, cvt_r};
@@ -30,13 +39,19 @@
// We may need to trigger a glibc workaround. See on_resolver_failure() for details.
on_resolver_failure();
- if err == EAI_SYSTEM {
+ #[cfg(not(target_os = "espidf"))]
+ if err == libc::EAI_SYSTEM {
return Err(io::Error::last_os_error());
}
+ #[cfg(not(target_os = "espidf"))]
let detail = unsafe {
str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned()
};
+
+ #[cfg(target_os = "espidf")]
+ let detail = "";
+
Err(io::Error::new(
io::ErrorKind::Uncategorized,
&format!("failed to lookup address information: {}", detail)[..],
@@ -68,10 +83,10 @@
// flag to atomically create the socket and set it as
// CLOEXEC. On Linux this was added in 2.6.27.
let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
- Ok(Socket(FileDesc::new(fd)))
+ Ok(Socket(FileDesc::from_raw_fd(fd)))
} else {
let fd = cvt(libc::socket(fam, ty, 0))?;
- let fd = FileDesc::new(fd);
+ let fd = FileDesc::from_raw_fd(fd);
fd.set_cloexec()?;
let socket = Socket(fd);
@@ -103,11 +118,11 @@
))] {
// Like above, set cloexec atomically
cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
- Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))))
+ Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1]))))
} else {
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
- let a = FileDesc::new(fds[0]);
- let b = FileDesc::new(fds[1]);
+ let a = FileDesc::from_raw_fd(fds[0]);
+ let b = FileDesc::from_raw_fd(fds[1]);
a.set_cloexec()?;
b.set_cloexec()?;
Ok((Socket(a), Socket(b)))
@@ -125,7 +140,7 @@
self.set_nonblocking(true)?;
let r = unsafe {
let (addrp, len) = addr.into_inner();
- cvt(libc::connect(self.0.raw(), addrp, len))
+ cvt(libc::connect(self.as_raw_fd(), addrp, len))
};
self.set_nonblocking(false)?;
@@ -136,7 +151,7 @@
Err(e) => return Err(e),
}
- let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 };
+ let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 };
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
return Err(io::Error::new_const(
@@ -206,15 +221,17 @@
target_os = "netbsd",
target_os = "openbsd",
))] {
- let fd = cvt_r(|| unsafe {
- libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
- })?;
- Ok(Socket(FileDesc::new(fd)))
+ unsafe {
+ let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?;
+ Ok(Socket(FileDesc::from_raw_fd(fd)))
+ }
} else {
- let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
- let fd = FileDesc::new(fd);
- fd.set_cloexec()?;
- Ok(Socket(fd))
+ unsafe {
+ let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?;
+ let fd = FileDesc::from_raw_fd(fd);
+ fd.set_cloexec()?;
+ Ok(Socket(fd))
+ }
}
}
}
@@ -225,7 +242,7 @@
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
let ret = cvt(unsafe {
- libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
+ libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
})?;
Ok(ret as usize)
}
@@ -257,7 +274,7 @@
let n = cvt(unsafe {
libc::recvfrom(
- self.0.raw(),
+ self.as_raw_fd(),
buf.as_mut_ptr() as *mut c_void,
buf.len(),
flags,
@@ -282,7 +299,7 @@
target_os = "openbsd",
))]
pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
- let n = cvt(unsafe { libc::recvmsg(self.0.raw(), msg, libc::MSG_CMSG_CLOEXEC) })?;
+ let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?;
Ok(n as usize)
}
@@ -313,7 +330,7 @@
target_os = "openbsd",
))]
pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
- let n = cvt(unsafe { libc::sendmsg(self.0.raw(), msg, 0) })?;
+ let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?;
Ok(n as usize)
}
@@ -363,10 +380,25 @@
Shutdown::Read => libc::SHUT_RD,
Shutdown::Both => libc::SHUT_RDWR,
};
- cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
+ cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?;
Ok(())
}
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = libc::linger {
+ l_onoff: linger.is_some() as libc::c_int,
+ l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
+ };
+
+ setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?;
+
+ Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+ }
+
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int)
}
@@ -390,7 +422,7 @@
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
let mut nonblocking = nonblocking as libc::c_int;
- cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop)
+ cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop)
}
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
@@ -404,23 +436,52 @@
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))) }
}
-}
-impl AsInner<c_int> for Socket {
- fn as_inner(&self) -> &c_int {
- self.0.as_inner()
+ // This is used by sys_common code to abstract over Windows and Unix.
+ pub fn as_raw(&self) -> RawFd {
+ self.as_raw_fd()
}
}
-impl FromInner<c_int> for Socket {
- fn from_inner(fd: c_int) -> Socket {
- Socket(FileDesc::new(fd))
+impl AsInner<FileDesc> for Socket {
+ fn as_inner(&self) -> &FileDesc {
+ &self.0
}
}
-impl IntoInner<c_int> for Socket {
- fn into_inner(self) -> c_int {
- self.0.into_raw()
+impl IntoInner<FileDesc> for Socket {
+ fn into_inner(self) -> FileDesc {
+ self.0
+ }
+}
+
+impl FromInner<FileDesc> for Socket {
+ fn from_inner(file_desc: FileDesc) -> Self {
+ Self(file_desc)
+ }
+}
+
+impl AsFd for Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
}
}
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index d3c874e..1d5ffb0 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -128,6 +128,12 @@
}
}
+#[cfg(target_os = "espidf")]
+pub fn getcwd() -> io::Result<PathBuf> {
+ Ok(PathBuf::from("/"))
+}
+
+#[cfg(not(target_os = "espidf"))]
pub fn getcwd() -> io::Result<PathBuf> {
let mut buf = Vec::with_capacity(512);
loop {
@@ -154,6 +160,12 @@
}
}
+#[cfg(target_os = "espidf")]
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+ super::unsupported::unsupported()
+}
+
+#[cfg(not(target_os = "espidf"))]
pub fn chdir(p: &path::Path) -> io::Result<()> {
let p: &OsStr = p.as_ref();
let p = CString::new(p.as_bytes())?;
@@ -350,17 +362,14 @@
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn current_exe() -> io::Result<PathBuf> {
- extern "C" {
- fn _NSGetExecutablePath(buf: *mut libc::c_char, bufsize: *mut u32) -> libc::c_int;
- }
unsafe {
let mut sz: u32 = 0;
- _NSGetExecutablePath(ptr::null_mut(), &mut sz);
+ libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz);
if sz == 0 {
return Err(io::Error::last_os_error());
}
let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
- let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
+ let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
if err != 0 {
return Err(io::Error::last_os_error());
}
@@ -391,46 +400,21 @@
#[cfg(target_os = "haiku")]
pub fn current_exe() -> io::Result<PathBuf> {
- // Use Haiku's image info functions
- #[repr(C)]
- struct image_info {
- id: i32,
- type_: i32,
- sequence: i32,
- init_order: i32,
- init_routine: *mut libc::c_void, // function pointer
- term_routine: *mut libc::c_void, // function pointer
- device: libc::dev_t,
- node: libc::ino_t,
- name: [libc::c_char; 1024], // MAXPATHLEN
- text: *mut libc::c_void,
- data: *mut libc::c_void,
- text_size: i32,
- data_size: i32,
- api_version: i32,
- abi: i32,
- }
-
unsafe {
- extern "C" {
- fn _get_next_image_info(
- team_id: i32,
- cookie: *mut i32,
- info: *mut image_info,
- size: i32,
- ) -> i32;
- }
-
- let mut info: image_info = mem::zeroed();
+ let mut info: mem::MaybeUninit<libc::image_info> = mem::MaybeUninit::uninit();
let mut cookie: i32 = 0;
// the executable can be found at team id 0
- let result =
- _get_next_image_info(0, &mut cookie, &mut info, mem::size_of::<image_info>() as i32);
+ let result = libc::_get_next_image_info(
+ 0,
+ &mut cookie,
+ info.as_mut_ptr(),
+ mem::size_of::<libc::image_info>(),
+ );
if result != 0 {
use crate::io::ErrorKind;
Err(io::Error::new_const(ErrorKind::Uncategorized, &"Error getting executable path"))
} else {
- let name = CStr::from_ptr(info.name.as_ptr()).to_bytes();
+ let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes();
Ok(PathBuf::from(OsStr::from_bytes(name)))
}
}
@@ -460,6 +444,11 @@
path.canonicalize()
}
+#[cfg(target_os = "espidf")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ super::unsupported::unsupported()
+}
+
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
@@ -535,19 +524,18 @@
}
}
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
- let k = CString::new(k.as_bytes())?;
+ let k = CString::new(k.as_bytes()).ok()?;
unsafe {
let _guard = env_read_lock();
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
- let ret = if s.is_null() {
+ if s.is_null() {
None
} else {
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
- };
- Ok(ret)
+ }
}
}
@@ -570,6 +558,7 @@
}
}
+#[cfg(not(target_os = "espidf"))]
pub fn page_size() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
}
@@ -592,7 +581,8 @@
target_os = "ios",
target_os = "emscripten",
target_os = "redox",
- target_os = "vxworks"
+ target_os = "vxworks",
+ target_os = "espidf"
))]
unsafe fn fallback() -> Option<OsString> {
None
@@ -602,7 +592,8 @@
target_os = "ios",
target_os = "emscripten",
target_os = "redox",
- target_os = "vxworks"
+ target_os = "vxworks",
+ target_os = "espidf"
)))]
unsafe fn fallback() -> Option<OsString> {
let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
diff --git a/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs
index 0e1dcb3..c445acf 100644
--- a/library/std/src/sys/unix/os/tests.rs
+++ b/library/std/src/sys/unix/os/tests.rs
@@ -1,12 +1,14 @@
use super::*;
#[test]
+#[cfg(not(target_os = "vxworks"))]
fn test_glibc_version() {
// This mostly just tests that the weak linkage doesn't panic wildly...
glibc_version();
}
#[test]
+#[cfg(not(target_os = "vxworks"))]
fn test_parse_glibc_version() {
let cases = [
("0.0", Some((0, 0))),
diff --git a/library/std/src/sys_common/os_str_bytes.rs b/library/std/src/sys/unix/os_str.rs
similarity index 98%
rename from library/std/src/sys_common/os_str_bytes.rs
rename to library/std/src/sys/unix/os_str.rs
index 5696004..ae96d6b 100644
--- a/library/std/src/sys_common/os_str_bytes.rs
+++ b/library/std/src/sys/unix/os_str.rs
@@ -13,6 +13,7 @@
use core::str::lossy::{Utf8Lossy, Utf8LossyChunk};
#[cfg(test)]
+#[path = "../unix/os_str/tests.rs"]
mod tests;
#[derive(Hash)]
diff --git a/library/std/src/sys_common/os_str_bytes/tests.rs b/library/std/src/sys/unix/os_str/tests.rs
similarity index 100%
rename from library/std/src/sys_common/os_str_bytes/tests.rs
rename to library/std/src/sys/unix/os_str/tests.rs
diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs
index 7ae37bd..a56c275 100644
--- a/library/std/src/sys/unix/pipe.rs
+++ b/library/std/src/sys/unix/pipe.rs
@@ -1,7 +1,9 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::{cvt, cvt_r};
+use crate::sys_common::IntoInner;
////////////////////////////////////////////////////////////////////////////////
// Anonymous pipes
@@ -24,16 +26,20 @@
target_os = "openbsd",
target_os = "redox"
))] {
- cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
- Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
+ unsafe {
+ cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?;
+ Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1]))))
+ }
} else {
- cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
+ unsafe {
+ cvt(libc::pipe(fds.as_mut_ptr()))?;
- let fd0 = FileDesc::new(fds[0]);
- let fd1 = FileDesc::new(fds[1]);
- fd0.set_cloexec()?;
- fd1.set_cloexec()?;
- Ok((AnonPipe(fd0), AnonPipe(fd1)))
+ let fd0 = FileDesc::from_raw_fd(fds[0]);
+ let fd1 = FileDesc::from_raw_fd(fds[1]);
+ fd0.set_cloexec()?;
+ fd1.set_cloexec()?;
+ Ok((AnonPipe(fd0), AnonPipe(fd1)))
+ }
}
}
}
@@ -64,11 +70,10 @@
pub fn is_write_vectored(&self) -> bool {
self.0.is_write_vectored()
}
+}
- pub fn fd(&self) -> &FileDesc {
- &self.0
- }
- pub fn into_fd(self) -> FileDesc {
+impl IntoInner<FileDesc> for AnonPipe {
+ fn into_inner(self) -> FileDesc {
self.0
}
}
@@ -76,15 +81,15 @@
pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> {
// Set both pipes into nonblocking mode as we're gonna be reading from both
// in the `select` loop below, and we wouldn't want one to block the other!
- let p1 = p1.into_fd();
- let p2 = p2.into_fd();
+ let p1 = p1.into_inner();
+ let p2 = p2.into_inner();
p1.set_nonblocking(true)?;
p2.set_nonblocking(true)?;
let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
- fds[0].fd = p1.raw();
+ fds[0].fd = p1.as_raw_fd();
fds[0].events = libc::POLLIN;
- fds[1].fd = p2.raw();
+ fds[1].fd = p2.as_raw_fd();
fds[1].events = libc::POLLIN;
loop {
// wait for either pipe to become readable using `poll`
@@ -120,3 +125,27 @@
}
}
}
+
+impl AsRawFd for AnonPipe {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl AsFd for AnonPipe {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl IntoRawFd for AnonPipe {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for AnonPipe {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+}
diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
index b5a19ed..0165ece 100644
--- a/library/std/src/sys/unix/process/mod.rs
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -13,6 +13,9 @@
} else if #[cfg(target_os = "vxworks")] {
#[path = "process_vxworks.rs"]
mod process_inner;
+ } else if #[cfg(target_os = "espidf")] {
+ #[path = "process_unsupported.rs"]
+ mod process_inner;
} else {
#[path = "process_unix.rs"]
mod process_inner;
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index c5bdd1b..7b261a3 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -13,6 +13,7 @@
use crate::sys::fs::File;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys_common::process::{CommandEnv, CommandEnvs};
+use crate::sys_common::IntoInner;
#[cfg(not(target_os = "fuchsia"))]
use crate::sys::fs::OpenOptions;
@@ -50,7 +51,7 @@
raw[bit / 8] |= 1 << (bit % 8);
return 0;
}
- } else if #[cfg(not(target_os = "vxworks"))] {
+ } else {
pub use libc::{sigemptyset, sigaddset};
}
}
@@ -79,6 +80,8 @@
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
+ #[cfg(target_os = "linux")]
+ create_pidfd: bool,
}
// Create a new type for argv, so that we can make it `Send` and `Sync`
@@ -124,6 +127,7 @@
}
impl Command {
+ #[cfg(not(target_os = "linux"))]
pub fn new(program: &OsStr) -> Command {
let mut saw_nul = false;
let program = os2c(program, &mut saw_nul);
@@ -144,6 +148,28 @@
}
}
+ #[cfg(target_os = "linux")]
+ pub fn new(program: &OsStr) -> Command {
+ let mut saw_nul = false;
+ let program = os2c(program, &mut saw_nul);
+ Command {
+ argv: Argv(vec![program.as_ptr(), ptr::null()]),
+ args: vec![program.clone()],
+ program,
+ env: Default::default(),
+ cwd: None,
+ uid: None,
+ gid: None,
+ saw_nul,
+ closures: Vec::new(),
+ groups: None,
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ create_pidfd: false,
+ }
+ }
+
pub fn set_arg_0(&mut self, arg: &OsStr) {
// Set a new arg0
let arg = os2c(arg, &mut self.saw_nul);
@@ -177,6 +203,22 @@
self.groups = Some(Box::from(groups));
}
+ #[cfg(target_os = "linux")]
+ pub fn create_pidfd(&mut self, val: bool) {
+ self.create_pidfd = val;
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ #[allow(dead_code)]
+ pub fn get_create_pidfd(&self) -> bool {
+ false
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn get_create_pidfd(&self) -> bool {
+ self.create_pidfd
+ }
+
pub fn saw_nul(&self) -> bool {
self.saw_nul
}
@@ -347,17 +389,17 @@
// stderr. No matter which we dup first, the second will get
// overwritten prematurely.
Stdio::Fd(ref fd) => {
- if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
+ if fd.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO {
Ok((ChildStdio::Owned(fd.duplicate()?), None))
} else {
- Ok((ChildStdio::Explicit(fd.raw()), None))
+ Ok((ChildStdio::Explicit(fd.as_raw_fd()), None))
}
}
Stdio::MakePipe => {
let (reader, writer) = pipe::anon_pipe()?;
let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) };
- Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
+ Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours)))
}
#[cfg(not(target_os = "fuchsia"))]
@@ -367,7 +409,7 @@
opts.write(!readable);
let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) };
let fd = File::open_c(&path, &opts)?;
- Ok((ChildStdio::Owned(fd.into_fd()), None))
+ Ok((ChildStdio::Owned(fd.into_inner()), None))
}
#[cfg(target_os = "fuchsia")]
@@ -378,13 +420,13 @@
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
- Stdio::Fd(pipe.into_fd())
+ Stdio::Fd(pipe.into_inner())
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
- Stdio::Fd(file.into_fd())
+ Stdio::Fd(file.into_inner())
}
}
@@ -393,7 +435,7 @@
match *self {
ChildStdio::Inherit => None,
ChildStdio::Explicit(fd) => Some(fd),
- ChildStdio::Owned(ref fd) => Some(fd.raw()),
+ ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()),
#[cfg(target_os = "fuchsia")]
ChildStdio::Null => None,
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index c888dd0..11d23a3 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -9,6 +9,12 @@
use crate::sys::cvt;
use crate::sys::process::process_common::*;
+#[cfg(target_os = "linux")]
+use crate::os::linux::process::PidFd;
+
+#[cfg(target_os = "linux")]
+use crate::sys::weak::syscall;
+
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
@@ -61,7 +67,8 @@
// a lock any more because the parent won't do anything and the child is
// in its own process. Thus the parent drops the lock guard while the child
// forgets it to avoid unlocking it on a new thread, which would be invalid.
- let (env_lock, pid) = unsafe { (sys::os::env_read_lock(), cvt(libc::fork())?) };
+ let env_lock = sys::os::env_read_lock();
+ let (pid, pidfd) = unsafe { self.do_fork()? };
if pid == 0 {
crate::panic::always_abort();
@@ -90,7 +97,9 @@
drop(env_lock);
drop(output);
- let mut p = Process { pid, status: None };
+ // Safety: We obtained the pidfd from calling `clone3` with
+ // `CLONE_PIDFD` so it's valid an otherwise unowned.
+ let mut p = unsafe { Process::new(pid, pidfd) };
let mut bytes = [0; 8];
// loop to handle EINTR
@@ -122,6 +131,98 @@
}
}
+ // Attempts to fork the process. If successful, returns Ok((0, -1))
+ // in the child, and Ok((child_pid, -1)) in the parent.
+ #[cfg(not(target_os = "linux"))]
+ unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
+ cvt(libc::fork()).map(|res| (res, -1))
+ }
+
+ // Attempts to fork the process. If successful, returns Ok((0, -1))
+ // in the child, and Ok((child_pid, child_pidfd)) in the parent.
+ #[cfg(target_os = "linux")]
+ unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
+ use crate::sync::atomic::{AtomicBool, Ordering};
+
+ static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
+ const CLONE_PIDFD: u64 = 0x00001000;
+
+ #[repr(C)]
+ struct clone_args {
+ flags: u64,
+ pidfd: u64,
+ child_tid: u64,
+ parent_tid: u64,
+ exit_signal: u64,
+ stack: u64,
+ stack_size: u64,
+ tls: u64,
+ set_tid: u64,
+ set_tid_size: u64,
+ cgroup: u64,
+ }
+
+ syscall! {
+ fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
+ }
+
+ // Bypassing libc for `clone3` can make further libc calls unsafe,
+ // so we use it sparingly for now. See #89522 for details.
+ // Some tools (e.g. sandboxing tools) may also expect `fork`
+ // rather than `clone3`.
+ let want_clone3 = self.get_create_pidfd();
+
+ // If we fail to create a pidfd for any reason, this will
+ // stay as -1, which indicates an error.
+ let mut pidfd: pid_t = -1;
+
+ // Attempt to use the `clone3` syscall, which supports more arguments
+ // (in particular, the ability to create a pidfd). If this fails,
+ // we will fall through this block to a call to `fork()`
+ if want_clone3 && HAS_CLONE3.load(Ordering::Relaxed) {
+ let mut flags = 0;
+ if self.get_create_pidfd() {
+ flags |= CLONE_PIDFD;
+ }
+
+ let mut args = clone_args {
+ flags,
+ pidfd: &mut pidfd as *mut pid_t as u64,
+ child_tid: 0,
+ parent_tid: 0,
+ exit_signal: libc::SIGCHLD as u64,
+ stack: 0,
+ stack_size: 0,
+ tls: 0,
+ set_tid: 0,
+ set_tid_size: 0,
+ cgroup: 0,
+ };
+
+ let args_ptr = &mut args as *mut clone_args;
+ let args_size = crate::mem::size_of::<clone_args>();
+
+ let res = cvt(clone3(args_ptr, args_size));
+ match res {
+ Ok(n) => return Ok((n as pid_t, pidfd)),
+ Err(e) => match e.raw_os_error() {
+ // Multiple threads can race to execute this store,
+ // but that's fine - that just means that multiple threads
+ // will have tried and failed to execute the same syscall,
+ // with no other side effects.
+ Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed),
+ // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
+ Some(libc::EPERM) => {}
+ _ => return Err(e),
+ },
+ }
+ }
+
+ // Generally, we just call `fork`. If we get here after wanting `clone3`,
+ // then the syscall does not exist or we do not have permission to call it.
+ cvt(libc::fork()).map(|res| (res, pidfd))
+ }
+
pub fn exec(&mut self, default: Stdio) -> io::Error {
let envp = self.capture_env();
@@ -308,6 +409,7 @@
|| (self.env_saw_path() && !self.program_is_path())
|| !self.get_closures().is_empty()
|| self.get_groups().is_some()
+ || self.get_create_pidfd()
{
return Ok(None);
}
@@ -352,7 +454,8 @@
None => None,
};
- let mut p = Process { pid: 0, status: None };
+ // Safety: -1 indicates we don't have a pidfd.
+ let mut p = unsafe { Process::new(0, -1) };
struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
@@ -441,9 +544,30 @@
pub struct Process {
pid: pid_t,
status: Option<ExitStatus>,
+ // On Linux, stores the pidfd created for this child.
+ // This is None if the user did not request pidfd creation,
+ // or if the pidfd could not be created for some reason
+ // (e.g. the `clone3` syscall was not available).
+ #[cfg(target_os = "linux")]
+ pidfd: Option<PidFd>,
}
impl Process {
+ #[cfg(target_os = "linux")]
+ unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
+ use crate::os::unix::io::FromRawFd;
+ use crate::sys_common::FromInner;
+ // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
+ let pidfd = (pidfd >= 0)
+ .then(|| PidFd::from_inner(unsafe { sys::fd::FileDesc::from_raw_fd(pidfd) }));
+ Process { pid, status: None, pidfd }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
+ Process { pid, status: None }
+ }
+
pub fn id(&self) -> u32 {
self.pid as u32
}
@@ -580,6 +704,24 @@
}
}
+#[cfg(target_os = "linux")]
+#[unstable(feature = "linux_pidfd", issue = "82971")]
+impl crate::os::linux::process::ChildExt for crate::process::Child {
+ fn pidfd(&self) -> io::Result<&PidFd> {
+ self.handle
+ .pidfd
+ .as_ref()
+ .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
+ }
+
+ fn take_pidfd(&mut self) -> io::Result<PidFd> {
+ self.handle
+ .pidfd
+ .take()
+ .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
+ }
+}
+
#[cfg(test)]
#[path = "process_unix/tests.rs"]
mod tests;
diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs
new file mode 100644
index 0000000..7d549d0
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unsupported.rs
@@ -0,0 +1,122 @@
+use crate::convert::{TryFrom, TryInto};
+use crate::fmt;
+use crate::io;
+use crate::io::ErrorKind;
+use crate::num::NonZeroI32;
+use crate::os::raw::NonZero_c_int;
+use crate::sys;
+use crate::sys::cvt;
+use crate::sys::pipe::AnonPipe;
+use crate::sys::process::process_common::*;
+use crate::sys::unix::unsupported::*;
+
+use libc::{c_int, pid_t};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(
+ &mut self,
+ default: Stdio,
+ needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ unsupported()
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ unsupported_err()
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Process {
+ handle: pid_t,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ 0
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ unsupported()
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ unsupported()
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(c_int);
+
+impl ExitStatus {
+ pub fn success(&self) -> bool {
+ self.code() == Some(0)
+ }
+
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ Err(ExitStatusError(1.try_into().unwrap()))
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ None
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ None
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ false
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ None
+ }
+
+ pub fn continued(&self) -> bool {
+ false
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ 0
+ }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a as i32)
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "exit code: {}", self.0)
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(NonZero_c_int);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ ExitStatus(self.0.into())
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ }
+}
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index 3289500..7a3f6b0 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -44,12 +44,17 @@
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
}
- #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ #[cfg(target_os = "espidf")]
+ fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
+ unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "espidf")))]
fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool {
false
}
- #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg(any(target_os = "linux", target_os = "android", target_os = "espidf"))]
fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sys::os::errno;
diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs
index 81f47a7..e8747e3 100644
--- a/library/std/src/sys/unix/stack_overflow.rs
+++ b/library/std/src/sys/unix/stack_overflow.rs
@@ -161,24 +161,10 @@
stackp.add(page_size())
}
- #[cfg(any(
- target_os = "linux",
- target_os = "macos",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd",
- target_os = "solaris",
- target_os = "illumos"
- ))]
unsafe fn get_stack() -> libc::stack_t {
libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
}
- #[cfg(target_os = "dragonfly")]
- unsafe fn get_stack() -> libc::stack_t {
- libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
- }
-
pub unsafe fn make_handler() -> Handler {
if !NEED_ALTSTACK.load(Ordering::Relaxed) {
return Handler::null();
diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs
index a05fe81..b359987 100644
--- a/library/std/src/sys/unix/stdio.rs
+++ b/library/std/src/sys/unix/stdio.rs
@@ -1,5 +1,6 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
+use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd};
use crate::sys::fd::FileDesc;
pub struct Stdin(());
@@ -14,11 +15,11 @@
impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf)
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) }
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs)
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) }
}
#[inline]
@@ -35,11 +36,13 @@
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf)
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) }
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs)
+ unsafe {
+ ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs)
+ }
}
#[inline]
@@ -60,11 +63,13 @@
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf)
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) }
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs)
+ unsafe {
+ ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs)
+ }
}
#[inline]
@@ -86,3 +91,51 @@
pub fn panic_output() -> Option<impl io::Write> {
Some(Stderr::new())
}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for io::Stdin {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<'a> AsFd for io::StdinLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for io::Stdout {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<'a> AsFd for io::StdoutLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for io::Stderr {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) }
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<'a> AsFd for io::StderrLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) }
+ }
+}
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 879d716..133ad3e 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -9,12 +9,31 @@
#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))]
use crate::sys::weak::weak;
-#[cfg(not(any(target_os = "l4re", target_os = "vxworks")))]
+#[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))]
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
#[cfg(target_os = "l4re")]
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
#[cfg(target_os = "vxworks")]
pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
+#[cfg(target_os = "espidf")]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF menuconfig system should be used
+
+#[cfg(target_os = "fuchsia")]
+mod zircon {
+ type zx_handle_t = u32;
+ type zx_status_t = i32;
+ pub const ZX_PROP_NAME: u32 = 3;
+
+ extern "C" {
+ pub fn zx_object_set_property(
+ handle: zx_handle_t,
+ property: u32,
+ value: *const libc::c_void,
+ value_size: libc::size_t,
+ ) -> zx_status_t;
+ pub fn zx_thread_self() -> zx_handle_t;
+ }
+}
pub struct Thread {
id: libc::pthread_t,
@@ -33,22 +52,35 @@
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
- let stack_size = cmp::max(stack, min_stack_size(&attr));
+ #[cfg(target_os = "espidf")]
+ if stack > 0 {
+ // Only set the stack if a non-zero value is passed
+ // 0 is used as an indication that the default stack size configured in the ESP-IDF menuconfig system should be used
+ assert_eq!(
+ libc::pthread_attr_setstacksize(&mut attr, cmp::max(stack, min_stack_size(&attr))),
+ 0
+ );
+ }
- match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
- 0 => {}
- n => {
- assert_eq!(n, libc::EINVAL);
- // EINVAL means |stack_size| is either too small or not a
- // multiple of the system page size. Because it's definitely
- // >= PTHREAD_STACK_MIN, it must be an alignment issue.
- // Round up to the nearest page and try again.
- let page_size = os::page_size();
- let stack_size =
- (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
- assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
- }
- };
+ #[cfg(not(target_os = "espidf"))]
+ {
+ let stack_size = cmp::max(stack, min_stack_size(&attr));
+
+ match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
+ 0 => {}
+ n => {
+ assert_eq!(n, libc::EINVAL);
+ // EINVAL means |stack_size| is either too small or not a
+ // multiple of the system page size. Because it's definitely
+ // >= PTHREAD_STACK_MIN, it must be an alignment issue.
+ // Round up to the nearest page and try again.
+ let page_size = os::page_size();
+ let stack_size =
+ (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
+ assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
+ }
+ };
+ }
let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
// Note: if the thread creation fails and this assert fails, then p will
@@ -134,22 +166,39 @@
}
}
+ #[cfg(target_os = "fuchsia")]
+ pub fn set_name(name: &CStr) {
+ use self::zircon::*;
+ unsafe {
+ zx_object_set_property(
+ zx_thread_self(),
+ ZX_PROP_NAME,
+ name.as_ptr() as *const libc::c_void,
+ name.to_bytes().len(),
+ );
+ }
+ }
+
+ #[cfg(target_os = "haiku")]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ let thread_self = libc::find_thread(ptr::null_mut());
+ libc::rename_thread(thread_self, name.as_ptr());
+ }
+ }
+
#[cfg(any(
target_env = "newlib",
- target_os = "haiku",
target_os = "l4re",
target_os = "emscripten",
target_os = "redox",
target_os = "vxworks"
))]
pub fn set_name(_name: &CStr) {
- // Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name.
- }
- #[cfg(target_os = "fuchsia")]
- pub fn set_name(_name: &CStr) {
- // FIXME: determine whether Fuchsia has a way to set a thread name.
+ // Newlib, Emscripten, and VxWorks have no way to set a thread name.
}
+ #[cfg(not(target_os = "espidf"))]
pub fn sleep(dur: Duration) {
let mut secs = dur.as_secs();
let mut nsecs = dur.subsec_nanos() as _;
@@ -175,6 +224,19 @@
}
}
+ #[cfg(target_os = "espidf")]
+ pub fn sleep(dur: Duration) {
+ let mut micros = dur.as_micros();
+ unsafe {
+ while micros > 0 {
+ let st = if micros > u32::MAX as u128 { u32::MAX } else { micros as u32 };
+ libc::usleep(st);
+
+ micros -= st as u128;
+ }
+ }
+ }
+
pub fn join(self) {
unsafe {
let ret = libc::pthread_join(self.id, ptr::null_mut());
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 23a5c81..7dc09ad 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -361,9 +361,9 @@
}
}
- #[cfg(not(target_os = "dragonfly"))]
+ #[cfg(not(any(target_os = "dragonfly", target_os = "espidf")))]
pub type clock_t = libc::c_int;
- #[cfg(target_os = "dragonfly")]
+ #[cfg(any(target_os = "dragonfly", target_os = "espidf"))]
pub type clock_t = libc::c_ulong;
fn now(clock: clock_t) -> Timespec {
diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs
index cad8be6..ba432ec 100644
--- a/library/std/src/sys/unix/weak.rs
+++ b/library/std/src/sys/unix/weak.rs
@@ -26,8 +26,6 @@
use crate::mem;
use crate::sync::atomic::{self, AtomicUsize, Ordering};
-// Temporary null documentation to work around #57569 until the fix is beta
-#[cfg_attr(bootstrap, doc = "")]
pub(crate) macro weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(non_upper_case_globals)]
@@ -103,8 +101,6 @@
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
}
-// Temporary null documentation to work around #57569 until the fix is beta
-#[cfg_attr(bootstrap, doc = "")]
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub(crate) macro syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
@@ -123,7 +119,6 @@
)
}
-#[cfg_attr(bootstrap, doc = "")]
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) macro syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs
index 4e6c301..a06b44e 100644
--- a/library/std/src/sys/unsupported/common.rs
+++ b/library/std/src/sys/unsupported/common.rs
@@ -4,8 +4,6 @@
pub use core::slice::memchr::{memchr, memrchr};
}
-pub use crate::sys_common::os_str_bytes as os_str;
-
// This is not necessarily correct. May want to consider making it part of the
// spec definition?
use crate::os::raw::c_char;
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
index 3ef4c6b..a127619 100644
--- a/library/std/src/sys/unsupported/mod.rs
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -11,6 +11,8 @@
pub mod mutex;
pub mod net;
pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
#[path = "../unix/path.rs"]
pub mod path;
pub mod pipe;
diff --git a/library/std/src/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs
index 96203c7..dbb6ce2 100644
--- a/library/std/src/sys/unsupported/net.rs
+++ b/library/std/src/sys/unsupported/net.rs
@@ -76,6 +76,14 @@
self.0
}
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
self.0
}
diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs
index e30395a..2886ec1 100644
--- a/library/std/src/sys/unsupported/os.rs
+++ b/library/std/src/sys/unsupported/os.rs
@@ -76,8 +76,8 @@
panic!("not supported on this platform")
}
-pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
- Ok(None)
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+ None
}
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs
index ba66eba..e4f4456 100644
--- a/library/std/src/sys/wasi/fd.rs
+++ b/library/std/src/sys/wasi/fd.rs
@@ -5,10 +5,12 @@
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::net::Shutdown;
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
#[derive(Debug)]
pub struct WasiFd {
- fd: wasi::Fd,
+ fd: OwnedFd,
}
fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] {
@@ -26,38 +28,26 @@
}
impl WasiFd {
- pub unsafe fn from_raw(fd: wasi::Fd) -> WasiFd {
- WasiFd { fd }
- }
-
- pub fn into_raw(self) -> wasi::Fd {
- let ret = self.fd;
- mem::forget(self);
- ret
- }
-
- pub fn as_raw(&self) -> wasi::Fd {
- self.fd
- }
-
pub fn datasync(&self) -> io::Result<()> {
- unsafe { wasi::fd_datasync(self.fd).map_err(err2io) }
+ unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
}
pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
- unsafe { wasi::fd_pread(self.fd, iovec(bufs), offset).map_err(err2io) }
+ unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) }
}
pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
- unsafe { wasi::fd_pwrite(self.fd, ciovec(bufs), offset).map_err(err2io) }
+ unsafe {
+ wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io)
+ }
}
pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- unsafe { wasi::fd_read(self.fd, iovec(bufs)).map_err(err2io) }
+ unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) }
}
pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- unsafe { wasi::fd_write(self.fd, ciovec(bufs)).map_err(err2io) }
+ unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) }
}
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
@@ -66,37 +56,42 @@
SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
};
- unsafe { wasi::fd_seek(self.fd, offset, whence).map_err(err2io) }
+ unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) }
}
pub fn tell(&self) -> io::Result<u64> {
- unsafe { wasi::fd_tell(self.fd).map_err(err2io) }
+ unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
}
// FIXME: __wasi_fd_fdstat_get
pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> {
- unsafe { wasi::fd_fdstat_set_flags(self.fd, flags).map_err(err2io) }
+ unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
}
pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
- unsafe { wasi::fd_fdstat_set_rights(self.fd, base, inheriting).map_err(err2io) }
+ unsafe {
+ wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, base, inheriting)
+ .map_err(err2io)
+ }
}
pub fn sync(&self) -> io::Result<()> {
- unsafe { wasi::fd_sync(self.fd).map_err(err2io) }
+ unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
}
pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
- unsafe { wasi::fd_advise(self.fd, offset, len, advice).map_err(err2io) }
+ unsafe {
+ wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io)
+ }
}
pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
- unsafe { wasi::fd_allocate(self.fd, offset, len).map_err(err2io) }
+ unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) }
}
pub fn create_directory(&self, path: &str) -> io::Result<()> {
- unsafe { wasi::path_create_directory(self.fd, path).map_err(err2io) }
+ unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
}
pub fn link(
@@ -107,7 +102,14 @@
new_path: &str,
) -> io::Result<()> {
unsafe {
- wasi::path_link(self.fd, old_flags, old_path, new_fd.fd, new_path).map_err(err2io)
+ wasi::path_link(
+ self.as_raw_fd() as wasi::Fd,
+ old_flags,
+ old_path,
+ new_fd.as_raw_fd() as wasi::Fd,
+ new_path,
+ )
+ .map_err(err2io)
}
}
@@ -122,7 +124,7 @@
) -> io::Result<WasiFd> {
unsafe {
wasi::path_open(
- self.fd,
+ self.as_raw_fd() as wasi::Fd,
dirflags,
path,
oflags,
@@ -130,25 +132,39 @@
fs_rights_inheriting,
fs_flags,
)
- .map(|fd| WasiFd::from_raw(fd))
+ .map(|fd| WasiFd::from_raw_fd(fd as RawFd))
.map_err(err2io)
}
}
pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> {
- unsafe { wasi::fd_readdir(self.fd, buf.as_mut_ptr(), buf.len(), cookie).map_err(err2io) }
+ unsafe {
+ wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie)
+ .map_err(err2io)
+ }
}
pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> {
- unsafe { wasi::path_readlink(self.fd, path, buf.as_mut_ptr(), buf.len()).map_err(err2io) }
+ unsafe {
+ wasi::path_readlink(self.as_raw_fd() as wasi::Fd, path, buf.as_mut_ptr(), buf.len())
+ .map_err(err2io)
+ }
}
pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> {
- unsafe { wasi::path_rename(self.fd, old_path, new_fd.fd, new_path).map_err(err2io) }
+ unsafe {
+ wasi::path_rename(
+ self.as_raw_fd() as wasi::Fd,
+ old_path,
+ new_fd.as_raw_fd() as wasi::Fd,
+ new_path,
+ )
+ .map_err(err2io)
+ }
}
pub fn filestat_get(&self) -> io::Result<wasi::Filestat> {
- unsafe { wasi::fd_filestat_get(self.fd).map_err(err2io) }
+ unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
}
pub fn filestat_set_times(
@@ -157,11 +173,14 @@
mtim: wasi::Timestamp,
fstflags: wasi::Fstflags,
) -> io::Result<()> {
- unsafe { wasi::fd_filestat_set_times(self.fd, atim, mtim, fstflags).map_err(err2io) }
+ unsafe {
+ wasi::fd_filestat_set_times(self.as_raw_fd() as wasi::Fd, atim, mtim, fstflags)
+ .map_err(err2io)
+ }
}
pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
- unsafe { wasi::fd_filestat_set_size(self.fd, size).map_err(err2io) }
+ unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) }
}
pub fn path_filestat_get(
@@ -169,7 +188,9 @@
flags: wasi::Lookupflags,
path: &str,
) -> io::Result<wasi::Filestat> {
- unsafe { wasi::path_filestat_get(self.fd, flags, path).map_err(err2io) }
+ unsafe {
+ wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io)
+ }
}
pub fn path_filestat_set_times(
@@ -181,21 +202,30 @@
fstflags: wasi::Fstflags,
) -> io::Result<()> {
unsafe {
- wasi::path_filestat_set_times(self.fd, flags, path, atim, mtim, fstflags)
- .map_err(err2io)
+ wasi::path_filestat_set_times(
+ self.as_raw_fd() as wasi::Fd,
+ flags,
+ path,
+ atim,
+ mtim,
+ fstflags,
+ )
+ .map_err(err2io)
}
}
pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> {
- unsafe { wasi::path_symlink(old_path, self.fd, new_path).map_err(err2io) }
+ unsafe {
+ wasi::path_symlink(old_path, self.as_raw_fd() as wasi::Fd, new_path).map_err(err2io)
+ }
}
pub fn unlink_file(&self, path: &str) -> io::Result<()> {
- unsafe { wasi::path_unlink_file(self.fd, path).map_err(err2io) }
+ unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
}
pub fn remove_directory(&self, path: &str) -> io::Result<()> {
- unsafe { wasi::path_remove_directory(self.fd, path).map_err(err2io) }
+ unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
}
pub fn sock_recv(
@@ -203,11 +233,15 @@
ri_data: &mut [IoSliceMut<'_>],
ri_flags: wasi::Riflags,
) -> io::Result<(usize, wasi::Roflags)> {
- unsafe { wasi::sock_recv(self.fd, iovec(ri_data), ri_flags).map_err(err2io) }
+ unsafe {
+ wasi::sock_recv(self.as_raw_fd() as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io)
+ }
}
pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> {
- unsafe { wasi::sock_send(self.fd, ciovec(si_data), si_flags).map_err(err2io) }
+ unsafe {
+ wasi::sock_send(self.as_raw_fd() as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io)
+ }
}
pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
@@ -216,14 +250,54 @@
Shutdown::Write => wasi::SDFLAGS_WR,
Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD,
};
- unsafe { wasi::sock_shutdown(self.fd, how).map_err(err2io) }
+ unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) }
}
}
-impl Drop for WasiFd {
- fn drop(&mut self) {
- // FIXME: can we handle the return code here even though we can't on
- // unix?
- let _ = unsafe { wasi::fd_close(self.fd) };
+impl AsInner<OwnedFd> for WasiFd {
+ fn as_inner(&self) -> &OwnedFd {
+ &self.fd
+ }
+}
+
+impl AsInnerMut<OwnedFd> for WasiFd {
+ fn as_inner_mut(&mut self) -> &mut OwnedFd {
+ &mut self.fd
+ }
+}
+
+impl IntoInner<OwnedFd> for WasiFd {
+ fn into_inner(self) -> OwnedFd {
+ self.fd
+ }
+}
+
+impl FromInner<OwnedFd> for WasiFd {
+ fn from_inner(owned_fd: OwnedFd) -> Self {
+ Self { fd: owned_fd }
+ }
+}
+
+impl AsFd for WasiFd {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl AsRawFd for WasiFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for WasiFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+impl FromRawFd for WasiFd {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
}
}
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index 8ffa1c8..984dda8 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -6,13 +6,15 @@
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::iter;
use crate::mem::{self, ManuallyDrop};
+use crate::os::raw::c_int;
use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
use crate::sys::time::SystemTime;
use crate::sys::unsupported;
-use crate::sys_common::FromInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
pub use crate::sys_common::fs::{remove_dir_all, try_exists};
@@ -441,22 +443,50 @@
unsupported()
}
- pub fn fd(&self) -> &WasiFd {
- &self.fd
- }
-
- pub fn into_fd(self) -> WasiFd {
- self.fd
- }
-
pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
read_link(&self.fd, file)
}
}
-impl FromInner<u32> for File {
- fn from_inner(fd: u32) -> File {
- unsafe { File { fd: WasiFd::from_raw(fd) } }
+impl AsInner<WasiFd> for File {
+ fn as_inner(&self) -> &WasiFd {
+ &self.fd
+ }
+}
+
+impl IntoInner<WasiFd> for File {
+ fn into_inner(self) -> WasiFd {
+ self.fd
+ }
+}
+
+impl FromInner<WasiFd> for File {
+ fn from_inner(fd: WasiFd) -> File {
+ File { fd }
+ }
+}
+
+impl AsFd for File {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl AsRawFd for File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for File {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+impl FromRawFd for File {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
}
}
@@ -473,7 +503,7 @@
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("File").field("fd", &self.fd.as_raw()).finish()
+ f.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
}
}
@@ -653,7 +683,7 @@
let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
return Ok((
- ManuallyDrop::new(WasiFd::from_raw(fd as u32)),
+ ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
PathBuf::from(OsString::from_vec(relative)),
));
}
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index 4af99bf..8d62335 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -32,7 +32,8 @@
pub mod mutex;
pub mod net;
pub mod os;
-pub use crate::sys_common::os_str_bytes as os_str;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
index 0686067..a4dbb22 100644
--- a/library/std/src/sys/wasi/net.rs
+++ b/library/std/src/sys/wasi/net.rs
@@ -5,12 +5,57 @@
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::unsupported;
-use crate::sys_common::FromInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
+pub struct Socket(WasiFd);
+
pub struct TcpStream {
- fd: WasiFd,
+ inner: Socket,
+}
+
+impl AsInner<WasiFd> for Socket {
+ fn as_inner(&self) -> &WasiFd {
+ &self.0
+ }
+}
+
+impl IntoInner<WasiFd> for Socket {
+ fn into_inner(self) -> WasiFd {
+ self.0
+ }
+}
+
+impl FromInner<WasiFd> for Socket {
+ fn from_inner(inner: WasiFd) -> Socket {
+ Socket(inner)
+ }
+}
+
+impl AsFd for Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
+ }
}
impl TcpStream {
@@ -82,6 +127,14 @@
unsupported()
}
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unsupported()
}
@@ -106,29 +159,29 @@
unsupported()
}
- pub fn fd(&self) -> &WasiFd {
- &self.fd
+ pub fn socket(&self) -> &Socket {
+ &self.inner
}
- pub fn into_fd(self) -> WasiFd {
- self.fd
+ pub fn into_socket(self) -> Socket {
+ self.inner
}
}
-impl FromInner<u32> for TcpStream {
- fn from_inner(fd: u32) -> TcpStream {
- unsafe { TcpStream { fd: WasiFd::from_raw(fd) } }
+impl FromInner<Socket> for TcpStream {
+ fn from_inner(socket: Socket) -> TcpStream {
+ TcpStream { inner: socket }
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("TcpStream").field("fd", &self.fd.as_raw()).finish()
+ f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish()
}
}
pub struct TcpListener {
- fd: WasiFd,
+ inner: Socket,
}
impl TcpListener {
@@ -172,29 +225,41 @@
unsupported()
}
- pub fn fd(&self) -> &WasiFd {
- &self.fd
+ pub fn socket(&self) -> &Socket {
+ &self.inner
}
- pub fn into_fd(self) -> WasiFd {
- self.fd
+ pub fn into_socket(self) -> Socket {
+ self.inner
}
}
-impl FromInner<u32> for TcpListener {
- fn from_inner(fd: u32) -> TcpListener {
- unsafe { TcpListener { fd: WasiFd::from_raw(fd) } }
+impl AsInner<Socket> for TcpListener {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for TcpListener {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for TcpListener {
+ fn from_inner(inner: Socket) -> TcpListener {
+ TcpListener { inner }
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("TcpListener").field("fd", &self.fd.as_raw()).finish()
+ f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish()
}
}
pub struct UdpSocket {
- fd: WasiFd,
+ inner: Socket,
}
impl UdpSocket {
@@ -322,24 +387,36 @@
unsupported()
}
- pub fn fd(&self) -> &WasiFd {
- &self.fd
+ pub fn socket(&self) -> &Socket {
+ &self.inner
}
- pub fn into_fd(self) -> WasiFd {
- self.fd
+ pub fn into_socket(self) -> Socket {
+ self.inner
}
}
-impl FromInner<u32> for UdpSocket {
- fn from_inner(fd: u32) -> UdpSocket {
- unsafe { UdpSocket { fd: WasiFd::from_raw(fd) } }
+impl AsInner<Socket> for UdpSocket {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for UdpSocket {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for UdpSocket {
+ fn from_inner(inner: Socket) -> UdpSocket {
+ UdpSocket { inner }
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("UdpSocket").field("fd", &self.fd.as_raw()).finish()
+ f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish()
}
}
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
index f129ee5..c5229a1 100644
--- a/library/std/src/sys/wasi/os.rs
+++ b/library/std/src/sys/wasi/os.rs
@@ -175,17 +175,16 @@
}
}
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
- let k = CString::new(k.as_bytes())?;
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ let k = CString::new(k.as_bytes()).ok()?;
unsafe {
let _guard = env_lock();
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
- let ret = if s.is_null() {
+ if s.is_null() {
None
} else {
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
- };
- Ok(ret)
+ }
}
}
diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs
index 209d5b9..2c8f394 100644
--- a/library/std/src/sys/wasi/stdio.rs
+++ b/library/std/src/sys/wasi/stdio.rs
@@ -3,6 +3,8 @@
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};
pub struct Stdin;
pub struct Stdout;
@@ -12,9 +14,11 @@
pub const fn new() -> Stdin {
Stdin
}
+}
+impl AsRawFd for Stdin {
#[inline]
- pub fn as_raw_fd(&self) -> u32 {
+ fn as_raw_fd(&self) -> raw::c_int {
0
}
}
@@ -25,7 +29,7 @@
}
fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data)
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data)
}
#[inline]
@@ -38,9 +42,11 @@
pub const fn new() -> Stdout {
Stdout
}
+}
+impl AsRawFd for Stdout {
#[inline]
- pub fn as_raw_fd(&self) -> u32 {
+ fn as_raw_fd(&self) -> raw::c_int {
1
}
}
@@ -51,7 +57,7 @@
}
fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
- ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data)
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
}
#[inline]
@@ -67,9 +73,11 @@
pub const fn new() -> Stderr {
Stderr
}
+}
+impl AsRawFd for Stderr {
#[inline]
- pub fn as_raw_fd(&self) -> u32 {
+ fn as_raw_fd(&self) -> raw::c_int {
2
}
}
@@ -80,7 +88,7 @@
}
fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
- ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data)
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
}
#[inline]
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index cd701a3..c81d653 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -30,6 +30,8 @@
pub mod net;
#[path = "../unsupported/os.rs"]
pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
@@ -45,8 +47,6 @@
#[path = "../unsupported/time.rs"]
pub mod time;
-pub use crate::sys_common::os_str_bytes as os_str;
-
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
#[path = "atomics/condvar.rs"]
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index f126413..3919025 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -1,13 +1,18 @@
-#![allow(dead_code)] // runtime init functions not used during testing
+//! The Windows command line is just a string
+//! <https://docs.microsoft.com/en-us/archive/blogs/larryosterman/the-windows-command-line-is-just-a-string>
+//!
+//! This module implements the parsing necessary to turn that string into a list of arguments.
#[cfg(test)]
mod tests;
use crate::ffi::OsString;
use crate::fmt;
+use crate::marker::PhantomData;
+use crate::num::NonZeroU16;
use crate::os::windows::prelude::*;
use crate::path::PathBuf;
-use crate::slice;
+use crate::ptr::NonNull;
use crate::sys::c;
use crate::sys::windows::os::current_exe;
use crate::vec;
@@ -15,9 +20,11 @@
use core::iter;
pub fn args() -> Args {
+ // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16
+ // string so it's safe for `WStrUnits` to use.
unsafe {
let lp_cmd_line = c::GetCommandLineW();
- let parsed_args_list = parse_lp_cmd_line(lp_cmd_line as *const u16, || {
+ let parsed_args_list = parse_lp_cmd_line(WStrUnits::new(lp_cmd_line), || {
current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new())
});
@@ -28,129 +35,120 @@
/// Implements the Windows command-line argument parsing algorithm.
///
/// Microsoft's documentation for the Windows CLI argument format can be found at
-/// <https://docs.microsoft.com/en-us/previous-versions//17w5ykft(v=vs.85)>.
+/// <https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160#parsing-c-command-line-arguments>
///
-/// Windows includes a function to do this in shell32.dll,
-/// but linking with that DLL causes the process to be registered as a GUI application.
+/// A more in-depth explanation is here:
+/// <https://daviddeley.com/autohotkey/parameters/parameters.htm#WIN>
+///
+/// Windows includes a function to do command line parsing in shell32.dll.
+/// However, this is not used for two reasons:
+///
+/// 1. Linking with that DLL causes the process to be registered as a GUI application.
/// GUI applications add a bunch of overhead, even if no windows are drawn. See
/// <https://randomascii.wordpress.com/2018/12/03/a-not-called-function-can-cause-a-5x-slowdown/>.
///
-/// This function was tested for equivalence to the shell32.dll implementation in
-/// Windows 10 Pro v1803, using an exhaustive test suite available at
-/// <https://gist.github.com/notriddle/dde431930c392e428055b2dc22e638f5> or
-/// <https://paste.gg/p/anonymous/47d6ed5f5bd549168b1c69c799825223>.
-unsafe fn parse_lp_cmd_line<F: Fn() -> OsString>(
- lp_cmd_line: *const u16,
+/// 2. It does not follow the modern C/C++ argv rules outlined in the first two links above.
+///
+/// This function was tested for equivalence to the C/C++ parsing rules using an
+/// extensive test suite available at
+/// <https://github.com/ChrisDenton/winarg/tree/std>.
+fn parse_lp_cmd_line<'a, F: Fn() -> OsString>(
+ lp_cmd_line: Option<WStrUnits<'a>>,
exe_name: F,
) -> Vec<OsString> {
- const BACKSLASH: u16 = '\\' as u16;
- const QUOTE: u16 = '"' as u16;
- const TAB: u16 = '\t' as u16;
- const SPACE: u16 = ' ' as u16;
+ const BACKSLASH: NonZeroU16 = NonZeroU16::new(b'\\' as u16).unwrap();
+ const QUOTE: NonZeroU16 = NonZeroU16::new(b'"' as u16).unwrap();
+ const TAB: NonZeroU16 = NonZeroU16::new(b'\t' as u16).unwrap();
+ const SPACE: NonZeroU16 = NonZeroU16::new(b' ' as u16).unwrap();
+
let mut ret_val = Vec::new();
- if lp_cmd_line.is_null() || *lp_cmd_line == 0 {
+ // If the cmd line pointer is null or it points to an empty string then
+ // return the name of the executable as argv[0].
+ if lp_cmd_line.as_ref().and_then(|cmd| cmd.peek()).is_none() {
ret_val.push(exe_name());
return ret_val;
}
- let mut cmd_line = {
- let mut end = 0;
- while *lp_cmd_line.offset(end) != 0 {
- end += 1;
- }
- slice::from_raw_parts(lp_cmd_line, end as usize)
- };
+ let mut code_units = lp_cmd_line.unwrap();
+
// The executable name at the beginning is special.
- cmd_line = match cmd_line[0] {
- // The executable name ends at the next quote mark,
- // no matter what.
- QUOTE => {
- let args = {
- let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE);
- if let Some(exe) = cut.next() {
- ret_val.push(OsString::from_wide(exe));
- }
- cut.next()
- };
- if let Some(args) = args {
- args
- } else {
- return ret_val;
- }
- }
- // Implement quirk: when they say whitespace here,
- // they include the entire ASCII control plane:
- // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW
- // will consider the first argument to be an empty string. Excess whitespace at the
- // end of lpCmdLine is ignored."
- 0..=SPACE => {
- ret_val.push(OsString::new());
- &cmd_line[1..]
- }
- // The executable name ends at the next whitespace,
- // no matter what.
- _ => {
- let args = {
- let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE);
- if let Some(exe) = cut.next() {
- ret_val.push(OsString::from_wide(exe));
- }
- cut.next()
- };
- if let Some(args) = args {
- args
- } else {
- return ret_val;
- }
- }
- };
- let mut cur = Vec::new();
let mut in_quotes = false;
- let mut was_in_quotes = false;
- let mut backslash_count: usize = 0;
- for &c in cmd_line {
- match c {
- // backslash
- BACKSLASH => {
- backslash_count += 1;
- was_in_quotes = false;
- }
- QUOTE if backslash_count % 2 == 0 => {
- cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2));
- backslash_count = 0;
- if was_in_quotes {
- cur.push('"' as u16);
- was_in_quotes = false;
- } else {
- was_in_quotes = in_quotes;
- in_quotes = !in_quotes;
- }
- }
- QUOTE if backslash_count % 2 != 0 => {
- cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2));
- backslash_count = 0;
- was_in_quotes = false;
- cur.push(b'"' as u16);
- }
- SPACE | TAB if !in_quotes => {
- cur.extend(iter::repeat(b'\\' as u16).take(backslash_count));
- if !cur.is_empty() || was_in_quotes {
- ret_val.push(OsString::from_wide(&cur[..]));
- cur.truncate(0);
- }
- backslash_count = 0;
- was_in_quotes = false;
- }
- _ => {
- cur.extend(iter::repeat(b'\\' as u16).take(backslash_count));
- backslash_count = 0;
- was_in_quotes = false;
- cur.push(c);
- }
+ let mut cur = Vec::new();
+ for w in &mut code_units {
+ match w {
+ // A quote mark always toggles `in_quotes` no matter what because
+ // there are no escape characters when parsing the executable name.
+ QUOTE => in_quotes = !in_quotes,
+ // If not `in_quotes` then whitespace ends argv[0].
+ SPACE | TAB if !in_quotes => break,
+ // In all other cases the code unit is taken literally.
+ _ => cur.push(w.get()),
}
}
- cur.extend(iter::repeat(b'\\' as u16).take(backslash_count));
- // include empty quoted strings at the end of the arguments list
- if !cur.is_empty() || was_in_quotes || in_quotes {
+ // Skip whitespace.
+ code_units.advance_while(|w| w == SPACE || w == TAB);
+ ret_val.push(OsString::from_wide(&cur));
+
+ // Parse the arguments according to these rules:
+ // * All code units are taken literally except space, tab, quote and backslash.
+ // * When not `in_quotes`, space and tab separate arguments. Consecutive spaces and tabs are
+ // treated as a single separator.
+ // * A space or tab `in_quotes` is taken literally.
+ // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
+ // * A quote can be escaped if preceded by an odd number of backslashes.
+ // * If any number of backslashes is immediately followed by a quote then the number of
+ // backslashes is halved (rounding down).
+ // * Backslashes not followed by a quote are all taken literally.
+ // * If `in_quotes` then a quote can also be escaped using another quote
+ // (i.e. two consecutive quotes become one literal quote).
+ let mut cur = Vec::new();
+ let mut in_quotes = false;
+ while let Some(w) = code_units.next() {
+ match w {
+ // If not `in_quotes`, a space or tab ends the argument.
+ SPACE | TAB if !in_quotes => {
+ ret_val.push(OsString::from_wide(&cur[..]));
+ cur.truncate(0);
+
+ // Skip whitespace.
+ code_units.advance_while(|w| w == SPACE || w == TAB);
+ }
+ // Backslashes can escape quotes or backslashes but only if consecutive backslashes are followed by a quote.
+ BACKSLASH => {
+ let backslash_count = code_units.advance_while(|w| w == BACKSLASH) + 1;
+ if code_units.peek() == Some(QUOTE) {
+ cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count / 2));
+ // The quote is escaped if there are an odd number of backslashes.
+ if backslash_count % 2 == 1 {
+ code_units.next();
+ cur.push(QUOTE.get());
+ }
+ } else {
+ // If there is no quote on the end then there is no escaping.
+ cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count));
+ }
+ }
+ // If `in_quotes` and not backslash escaped (see above) then a quote either
+ // unsets `in_quote` or is escaped by another quote.
+ QUOTE if in_quotes => match code_units.peek() {
+ // Two consecutive quotes when `in_quotes` produces one literal quote.
+ Some(QUOTE) => {
+ cur.push(QUOTE.get());
+ code_units.next();
+ }
+ // Otherwise set `in_quotes`.
+ Some(_) => in_quotes = false,
+ // The end of the command line.
+ // Push `cur` even if empty, which we do by breaking while `in_quotes` is still set.
+ None => break,
+ },
+ // If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`.
+ QUOTE => in_quotes = true,
+ // Everything else is always taken literally.
+ _ => cur.push(w.get()),
+ }
+ }
+ // Push the final argument, if any.
+ if !cur.is_empty() || in_quotes {
ret_val.push(OsString::from_wide(&cur[..]));
}
ret_val
@@ -187,3 +185,52 @@
self.parsed_args_list.len()
}
}
+
+/// A safe iterator over a LPWSTR
+/// (aka a pointer to a series of UTF-16 code units terminated by a NULL).
+struct WStrUnits<'a> {
+ // The pointer must never be null...
+ lpwstr: NonNull<u16>,
+ // ...and the memory it points to must be valid for this lifetime.
+ lifetime: PhantomData<&'a [u16]>,
+}
+impl WStrUnits<'_> {
+ /// Create the iterator. Returns `None` if `lpwstr` is null.
+ ///
+ /// SAFETY: `lpwstr` must point to a null-terminated wide string that lives
+ /// at least as long as the lifetime of this struct.
+ unsafe fn new(lpwstr: *const u16) -> Option<Self> {
+ Some(Self { lpwstr: NonNull::new(lpwstr as _)?, lifetime: PhantomData })
+ }
+ fn peek(&self) -> Option<NonZeroU16> {
+ // SAFETY: It's always safe to read the current item because we don't
+ // ever move out of the array's bounds.
+ unsafe { NonZeroU16::new(*self.lpwstr.as_ptr()) }
+ }
+ /// Advance the iterator while `predicate` returns true.
+ /// Returns the number of items it advanced by.
+ fn advance_while<P: FnMut(NonZeroU16) -> bool>(&mut self, mut predicate: P) -> usize {
+ let mut counter = 0;
+ while let Some(w) = self.peek() {
+ if !predicate(w) {
+ break;
+ }
+ counter += 1;
+ self.next();
+ }
+ counter
+ }
+}
+impl Iterator for WStrUnits<'_> {
+ // This can never return zero as that marks the end of the string.
+ type Item = NonZeroU16;
+ fn next(&mut self) -> Option<NonZeroU16> {
+ // SAFETY: If NULL is reached we immediately return.
+ // Therefore it's safe to advance the pointer after that.
+ unsafe {
+ let next = self.peek()?;
+ self.lpwstr = NonNull::new_unchecked(self.lpwstr.as_ptr().add(1));
+ Some(next)
+ }
+ }
+}
diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs
index 756a436..82c32d0 100644
--- a/library/std/src/sys/windows/args/tests.rs
+++ b/library/std/src/sys/windows/args/tests.rs
@@ -5,9 +5,9 @@
let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
wide.push(0);
let parsed =
- unsafe { parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) };
+ unsafe { parse_lp_cmd_line(WStrUnits::new(wide.as_ptr()), || OsString::from("TEST.EXE")) };
let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
- assert_eq!(parsed.as_slice(), expected.as_slice());
+ assert_eq!(parsed.as_slice(), expected.as_slice(), "{:?}", string);
}
#[test]
@@ -27,35 +27,65 @@
#[test]
fn official_examples() {
chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
- chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]);
+ chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r"a\\\b", "de fg", "h"]);
chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
- chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]);
+ chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r"a\\b c", "d", "e"]);
}
#[test]
fn whitespace_behavior() {
- chk(r#" test"#, &["", "test"]);
- chk(r#" test"#, &["", "test"]);
- chk(r#" test test2"#, &["", "test", "test2"]);
- chk(r#" test test2"#, &["", "test", "test2"]);
- chk(r#"test test2 "#, &["test", "test2"]);
- chk(r#"test test2 "#, &["test", "test2"]);
- chk(r#"test "#, &["test"]);
+ chk(" test", &["", "test"]);
+ chk(" test", &["", "test"]);
+ chk(" test test2", &["", "test", "test2"]);
+ chk(" test test2", &["", "test", "test2"]);
+ chk("test test2 ", &["test", "test2"]);
+ chk("test test2 ", &["test", "test2"]);
+ chk("test ", &["test"]);
}
#[test]
fn genius_quotes() {
chk(r#"EXE "" """#, &["EXE", "", ""]);
- chk(r#"EXE "" """"#, &["EXE", "", "\""]);
+ chk(r#"EXE "" """"#, &["EXE", "", r#"""#]);
chk(
r#"EXE "this is """all""" in the same argument""#,
- &["EXE", "this is \"all\" in the same argument"],
+ &["EXE", r#"this is "all" in the same argument"#],
);
- chk(r#"EXE "a"""#, &["EXE", "a\""]);
- chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]);
+ chk(r#"EXE "a"""#, &["EXE", r#"a""#]);
+ chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]);
// quotes cannot be escaped in command names
chk(r#""EXE" check"#, &["EXE", "check"]);
chk(r#""EXE check""#, &["EXE check"]);
- chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]);
- chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]);
+ chk(r#""EXE """for""" check"#, &["EXE for check"]);
+ chk(r#""EXE \"for\" check"#, &[r"EXE \for\ check"]);
+ chk(r#""EXE \" for \" check"#, &[r"EXE \", "for", r#"""#, "check"]);
+ chk(r#"E"X"E test"#, &["EXE", "test"]);
+ chk(r#"EX""E test"#, &["EXE", "test"]);
+}
+
+// from https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX
+#[test]
+fn post_2008() {
+ chk("EXE CallMeIshmael", &["EXE", "CallMeIshmael"]);
+ chk(r#"EXE "Call Me Ishmael""#, &["EXE", "Call Me Ishmael"]);
+ chk(r#"EXE Cal"l Me I"shmael"#, &["EXE", "Call Me Ishmael"]);
+ chk(r#"EXE CallMe\"Ishmael"#, &["EXE", r#"CallMe"Ishmael"#]);
+ chk(r#"EXE "CallMe\"Ishmael""#, &["EXE", r#"CallMe"Ishmael"#]);
+ chk(r#"EXE "Call Me Ishmael\\""#, &["EXE", r"Call Me Ishmael\"]);
+ chk(r#"EXE "CallMe\\\"Ishmael""#, &["EXE", r#"CallMe\"Ishmael"#]);
+ chk(r#"EXE a\\\b"#, &["EXE", r"a\\\b"]);
+ chk(r#"EXE "a\\\b""#, &["EXE", r"a\\\b"]);
+ chk(r#"EXE "\"Call Me Ishmael\"""#, &["EXE", r#""Call Me Ishmael""#]);
+ chk(r#"EXE "C:\TEST A\\""#, &["EXE", r"C:\TEST A\"]);
+ chk(r#"EXE "\"C:\TEST A\\\"""#, &["EXE", r#""C:\TEST A\""#]);
+ chk(r#"EXE "a b c" d e"#, &["EXE", "a b c", "d", "e"]);
+ chk(r#"EXE "ab\"c" "\\" d"#, &["EXE", r#"ab"c"#, r"\", "d"]);
+ chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r"a\\\b", "de fg", "h"]);
+ chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
+ chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r"a\\b c", "d", "e"]);
+ // Double Double Quotes
+ chk(r#"EXE "a b c"""#, &["EXE", r#"a b c""#]);
+ chk(r#"EXE """CallMeIshmael""" b c"#, &["EXE", r#""CallMeIshmael""#, "b", "c"]);
+ chk(r#"EXE """Call Me Ishmael""""#, &["EXE", r#""Call Me Ishmael""#]);
+ chk(r#"EXE """"Call Me Ishmael"" b c"#, &["EXE", r#""Call"#, "Me", "Ishmael", "b", "c"]);
}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 63f9be7..6fb850d 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -197,6 +197,7 @@
pub const SOCK_STREAM: c_int = 1;
pub const SOCKET_ERROR: c_int = -1;
pub const SOL_SOCKET: c_int = 0xffff;
+pub const SO_LINGER: c_int = 0x0080;
pub const SO_RCVTIMEO: c_int = 0x1006;
pub const SO_SNDTIMEO: c_int = 0x1005;
pub const IPPROTO_IP: c_int = 0;
@@ -217,6 +218,13 @@
pub const MSG_PEEK: c_int = 0x2;
#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct linger {
+ pub l_onoff: c_ushort,
+ pub l_linger: c_ushort,
+}
+
+#[repr(C)]
pub struct ip_mreq {
pub imr_multiaddr: in_addr,
pub imr_interface: in_addr,
@@ -781,7 +789,7 @@
pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL;
pub fn SetFileAttributesW(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL;
pub fn SetLastError(dwErrCode: DWORD);
- pub fn GetCommandLineW() -> *mut LPCWSTR;
+ pub fn GetCommandLineW() -> LPWSTR;
pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD;
pub fn GetCurrentProcess() -> HANDLE;
pub fn GetCurrentThread() -> HANDLE;
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index c677ada..cc13777 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -4,6 +4,7 @@
use crate::fmt;
use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
+use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::slice;
@@ -11,7 +12,7 @@
use crate::sys::handle::Handle;
use crate::sys::time::SystemTime;
use crate::sys::{c, cvt};
-use crate::sys_common::FromInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
use super::to_u16s;
@@ -295,12 +296,12 @@
if handle == c::INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
} else {
- Ok(File { handle: Handle::new(handle) })
+ unsafe { Ok(File { handle: Handle::from_raw_handle(handle) }) }
}
}
pub fn fsync(&self) -> io::Result<()> {
- cvt(unsafe { c::FlushFileBuffers(self.handle.raw()) })?;
+ cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
Ok(())
}
@@ -313,7 +314,7 @@
let size = mem::size_of_val(&info);
cvt(unsafe {
c::SetFileInformationByHandle(
- self.handle.raw(),
+ self.handle.as_raw_handle(),
c::FileEndOfFileInfo,
&mut info as *mut _ as *mut _,
size as c::DWORD,
@@ -326,7 +327,7 @@
pub fn file_attr(&self) -> io::Result<FileAttr> {
unsafe {
let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
- cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))?;
+ 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];
@@ -356,7 +357,7 @@
let mut info: c::FILE_BASIC_INFO = mem::zeroed();
let size = mem::size_of_val(&info);
cvt(c::GetFileInformationByHandleEx(
- self.handle.raw(),
+ self.handle.as_raw_handle(),
c::FileBasicInfo,
&mut info as *mut _ as *mut libc::c_void,
size as c::DWORD,
@@ -384,7 +385,7 @@
let mut info: c::FILE_STANDARD_INFO = mem::zeroed();
let size = mem::size_of_val(&info);
cvt(c::GetFileInformationByHandleEx(
- self.handle.raw(),
+ self.handle.as_raw_handle(),
c::FileStandardInfo,
&mut info as *mut _ as *mut libc::c_void,
size as c::DWORD,
@@ -449,7 +450,7 @@
};
let pos = pos as c::LARGE_INTEGER;
let mut newpos = 0;
- cvt(unsafe { c::SetFilePointerEx(self.handle.raw(), pos, &mut newpos, whence) })?;
+ cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
Ok(newpos as u64)
}
@@ -457,14 +458,6 @@
Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? })
}
- pub fn handle(&self) -> &Handle {
- &self.handle
- }
-
- pub fn into_handle(self) -> Handle {
- self.handle
- }
-
fn reparse_point<'a>(
&self,
space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
@@ -473,7 +466,7 @@
let mut bytes = 0;
cvt({
c::DeviceIoControl(
- self.handle.raw(),
+ self.handle.as_raw_handle(),
c::FSCTL_GET_REPARSE_POINT,
ptr::null_mut(),
0,
@@ -541,7 +534,7 @@
let size = mem::size_of_val(&info);
cvt(unsafe {
c::SetFileInformationByHandle(
- self.handle.raw(),
+ self.handle.as_raw_handle(),
c::FileBasicInfo,
&mut info as *mut _ as *mut _,
size as c::DWORD,
@@ -551,9 +544,45 @@
}
}
-impl FromInner<c::HANDLE> for File {
- fn from_inner(handle: c::HANDLE) -> File {
- File { handle: Handle::new(handle) }
+impl AsInner<Handle> for File {
+ fn as_inner(&self) -> &Handle {
+ &self.handle
+ }
+}
+
+impl IntoInner<Handle> for File {
+ fn into_inner(self) -> Handle {
+ self.handle
+ }
+}
+
+impl FromInner<Handle> for File {
+ fn from_inner(handle: Handle) -> File {
+ File { handle: handle }
+ }
+}
+
+impl AsHandle for File {
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ self.as_inner().as_handle()
+ }
+}
+
+impl AsRawHandle for File {
+ fn as_raw_handle(&self) -> RawHandle {
+ self.as_inner().as_raw_handle()
+ }
+}
+
+impl IntoRawHandle for File {
+ fn into_raw_handle(self) -> RawHandle {
+ self.into_inner().into_raw_handle()
+ }
+}
+
+impl FromRawHandle for File {
+ unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
+ Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) }
}
}
@@ -561,7 +590,7 @@
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// FIXME(#24570): add more info here (e.g., mode)
let mut b = f.debug_struct("File");
- b.field("handle", &self.handle.raw());
+ b.field("handle", &self.handle.as_raw_handle());
if let Ok(path) = get_path(&self) {
b.field("path", &path);
}
@@ -838,7 +867,7 @@
fn get_path(f: &File) -> io::Result<PathBuf> {
super::fill_utf16_buf(
|buf, sz| unsafe {
- c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, c::VOLUME_NAME_DOS)
+ c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
},
|buf| PathBuf::from(OsString::from_wide(buf)),
)
@@ -909,7 +938,7 @@
opts.write(true);
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
let f = File::open(junction, &opts)?;
- let h = f.handle().raw();
+ let h = f.as_inner().as_raw_handle();
unsafe {
let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index 0d4baa3..21d86b0 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -3,76 +3,87 @@
use crate::cmp;
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read};
use crate::mem;
-use crate::ops::Deref;
+use crate::os::windows::io::{
+ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
+};
use crate::ptr;
use crate::sys::c;
use crate::sys::cvt;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
/// An owned container for `HANDLE` object, closing them on Drop.
///
/// All methods are inherited through a `Deref` impl to `RawHandle`
-pub struct Handle(RawHandle);
-
-/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference
-/// as well as Rust-y methods.
-///
-/// This does **not** drop the handle when it goes out of scope, use `Handle`
-/// instead for that.
-#[derive(Copy, Clone)]
-pub struct RawHandle(c::HANDLE);
-
-unsafe impl Send for RawHandle {}
-unsafe impl Sync for RawHandle {}
+pub struct Handle(OwnedHandle);
impl Handle {
- pub fn new(handle: c::HANDLE) -> Handle {
- Handle(RawHandle::new(handle))
- }
-
pub fn new_event(manual: bool, init: bool) -> io::Result<Handle> {
unsafe {
let event =
c::CreateEventW(ptr::null_mut(), manual as c::BOOL, init as c::BOOL, ptr::null());
- if event.is_null() { Err(io::Error::last_os_error()) } else { Ok(Handle::new(event)) }
+ if event.is_null() {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(Handle::from_raw_handle(event))
+ }
}
}
-
- pub fn into_raw(self) -> c::HANDLE {
- let ret = self.raw();
- mem::forget(self);
- ret
- }
}
-impl Deref for Handle {
- type Target = RawHandle;
- fn deref(&self) -> &RawHandle {
+impl AsInner<OwnedHandle> for Handle {
+ fn as_inner(&self) -> &OwnedHandle {
&self.0
}
}
-impl Drop for Handle {
- fn drop(&mut self) {
- unsafe {
- let _ = c::CloseHandle(self.raw());
- }
+impl IntoInner<OwnedHandle> for Handle {
+ fn into_inner(self) -> OwnedHandle {
+ self.0
}
}
-impl RawHandle {
- pub fn new(handle: c::HANDLE) -> RawHandle {
- RawHandle(handle)
+impl FromInner<OwnedHandle> for Handle {
+ fn from_inner(file_desc: OwnedHandle) -> Self {
+ Self(file_desc)
}
+}
- pub fn raw(&self) -> c::HANDLE {
- self.0
+impl AsHandle for Handle {
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ self.0.as_handle()
}
+}
+impl AsRawHandle for Handle {
+ fn as_raw_handle(&self) -> RawHandle {
+ self.0.as_raw_handle()
+ }
+}
+
+impl IntoRawHandle for Handle {
+ fn into_raw_handle(self) -> RawHandle {
+ self.0.into_raw_handle()
+ }
+}
+
+impl FromRawHandle for Handle {
+ unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
+ Self(FromRawHandle::from_raw_handle(raw_handle))
+ }
+}
+
+impl Handle {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let mut read = 0;
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
let res = cvt(unsafe {
- c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, ptr::null_mut())
+ c::ReadFile(
+ self.as_raw_handle(),
+ buf.as_mut_ptr() as c::LPVOID,
+ len,
+ &mut read,
+ ptr::null_mut(),
+ )
});
match res {
@@ -104,7 +115,13 @@
let mut overlapped: c::OVERLAPPED = mem::zeroed();
overlapped.Offset = offset as u32;
overlapped.OffsetHigh = (offset >> 32) as u32;
- cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, &mut overlapped))
+ cvt(c::ReadFile(
+ self.as_raw_handle(),
+ buf.as_mut_ptr() as c::LPVOID,
+ len,
+ &mut read,
+ &mut overlapped,
+ ))
};
match res {
Ok(_) => Ok(read as usize),
@@ -120,7 +137,13 @@
) -> io::Result<Option<usize>> {
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
let mut amt = 0;
- let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped));
+ let res = cvt(c::ReadFile(
+ self.as_raw_handle(),
+ buf.as_ptr() as c::LPVOID,
+ len,
+ &mut amt,
+ overlapped,
+ ));
match res {
Ok(_) => Ok(Some(amt as usize)),
Err(e) => {
@@ -143,7 +166,8 @@
unsafe {
let mut bytes = 0;
let wait = if wait { c::TRUE } else { c::FALSE };
- let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait));
+ let res =
+ cvt(c::GetOverlappedResult(self.as_raw_handle(), overlapped, &mut bytes, wait));
match res {
Ok(_) => Ok(bytes as usize),
Err(e) => {
@@ -160,14 +184,20 @@
}
pub fn cancel_io(&self) -> io::Result<()> {
- unsafe { cvt(c::CancelIo(self.raw())).map(drop) }
+ unsafe { cvt(c::CancelIo(self.as_raw_handle())).map(drop) }
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let mut amt = 0;
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
cvt(unsafe {
- c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, ptr::null_mut())
+ c::WriteFile(
+ self.as_raw_handle(),
+ buf.as_ptr() as c::LPVOID,
+ len,
+ &mut amt,
+ ptr::null_mut(),
+ )
})?;
Ok(amt as usize)
}
@@ -189,7 +219,7 @@
overlapped.Offset = offset as u32;
overlapped.OffsetHigh = (offset >> 32) as u32;
cvt(c::WriteFile(
- self.0,
+ self.as_raw_handle(),
buf.as_ptr() as c::LPVOID,
len,
&mut written,
@@ -210,7 +240,7 @@
let cur_proc = c::GetCurrentProcess();
c::DuplicateHandle(
cur_proc,
- self.0,
+ self.as_raw_handle(),
cur_proc,
&mut ret,
access,
@@ -218,11 +248,11 @@
options,
)
})?;
- Ok(Handle::new(ret))
+ unsafe { Ok(Handle::from_raw_handle(ret)) }
}
}
-impl<'a> Read for &'a RawHandle {
+impl<'a> Read for &'a Handle {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs
index 12c5ea7..56f91eb 100644
--- a/library/std/src/sys/windows/mutex.rs
+++ b/library/std/src/sys/windows/mutex.rs
@@ -1,6 +1,6 @@
//! System Mutexes
//!
-//! The Windows implementation of mutexes is a little odd and it may not be
+//! The Windows implementation of mutexes is a little odd and it might not be
//! immediately obvious what's going on. The primary oddness is that SRWLock is
//! used instead of CriticalSection, and this is done because:
//!
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index 9cea5c5..33152cc 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -4,6 +4,9 @@
use crate::io::{self, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
+use crate::os::windows::io::{
+ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
+};
use crate::ptr;
use crate::sync::Once;
use crate::sys;
@@ -12,7 +15,7 @@
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
-use libc::{c_int, c_long, c_ulong};
+use libc::{c_int, c_long, c_ulong, c_ushort};
pub type wrlen_t = i32;
@@ -24,7 +27,7 @@
pub use crate::sys::c::*;
}
-pub struct Socket(c::SOCKET);
+pub struct Socket(OwnedSocket);
static INIT: Once = Once::new();
@@ -109,7 +112,7 @@
};
if socket != c::INVALID_SOCKET {
- Ok(Self(socket))
+ unsafe { Ok(Self::from_raw_socket(socket)) }
} else {
let error = unsafe { c::WSAGetLastError() };
@@ -124,9 +127,11 @@
return Err(last_error());
}
- let socket = Self(socket);
- socket.set_no_inherit()?;
- Ok(socket)
+ unsafe {
+ let socket = Self::from_raw_socket(socket);
+ socket.set_no_inherit()?;
+ Ok(socket)
+ }
}
}
@@ -134,7 +139,7 @@
self.set_nonblocking(true)?;
let result = {
let (addrp, len) = addr.into_inner();
- let result = unsafe { c::connect(self.0, addrp, len) };
+ let result = unsafe { c::connect(self.as_raw_socket(), addrp, len) };
cvt(result).map(drop)
};
self.set_nonblocking(false)?;
@@ -160,7 +165,7 @@
let fds = {
let mut fds = unsafe { mem::zeroed::<c::fd_set>() };
fds.fd_count = 1;
- fds.fd_array[0] = self.0;
+ fds.fd_array[0] = self.as_raw_socket();
fds
};
@@ -194,17 +199,19 @@
}
pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result<Socket> {
- let socket = unsafe { c::accept(self.0, storage, len) };
+ let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) };
match socket {
c::INVALID_SOCKET => Err(last_error()),
- _ => Ok(Self(socket)),
+ _ => unsafe { Ok(Self::from_raw_socket(socket)) },
}
}
pub fn duplicate(&self) -> io::Result<Socket> {
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
- let result = unsafe { c::WSADuplicateSocketW(self.0, c::GetCurrentProcessId(), &mut info) };
+ let result = unsafe {
+ c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
+ };
cvt(result)?;
let socket = unsafe {
c::WSASocketW(
@@ -218,7 +225,7 @@
};
if socket != c::INVALID_SOCKET {
- Ok(Self(socket))
+ unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) }
} else {
let error = unsafe { c::WSAGetLastError() };
@@ -241,9 +248,11 @@
return Err(last_error());
}
- let socket = Self(socket);
- socket.set_no_inherit()?;
- Ok(socket)
+ unsafe {
+ let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket));
+ socket.set_no_inherit()?;
+ Ok(socket)
+ }
}
}
@@ -251,7 +260,8 @@
// On unix when a socket is shut down all further reads return 0, so we
// do the same on windows to map a shut down socket to returning EOF.
let length = cmp::min(buf.len(), i32::MAX as usize) as i32;
- let result = unsafe { c::recv(self.0, buf.as_mut_ptr() as *mut _, length, flags) };
+ let result =
+ unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) };
match result {
c::SOCKET_ERROR => {
@@ -279,7 +289,7 @@
let mut flags = 0;
let result = unsafe {
c::WSARecv(
- self.0,
+ self.as_raw_socket(),
bufs.as_mut_ptr() as *mut c::WSABUF,
length,
&mut nread,
@@ -325,7 +335,7 @@
// do the same on windows to map a shut down socket to returning EOF.
let result = unsafe {
c::recvfrom(
- self.0,
+ self.as_raw_socket(),
buf.as_mut_ptr() as *mut _,
length,
flags,
@@ -361,7 +371,7 @@
let mut nwritten = 0;
let result = unsafe {
c::WSASend(
- self.0,
+ self.as_raw_socket(),
bufs.as_ptr() as *const c::WSABUF as *mut _,
length,
&mut nwritten,
@@ -408,8 +418,10 @@
#[cfg(not(target_vendor = "uwp"))]
fn set_no_inherit(&self) -> io::Result<()> {
- sys::cvt(unsafe { c::SetHandleInformation(self.0 as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) })
- .map(drop)
+ sys::cvt(unsafe {
+ c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
+ })
+ .map(drop)
}
#[cfg(target_vendor = "uwp")]
@@ -423,16 +435,32 @@
Shutdown::Read => c::SD_RECEIVE,
Shutdown::Both => c::SD_BOTH,
};
- let result = unsafe { c::shutdown(self.0, how) };
+ let result = unsafe { c::shutdown(self.as_raw_socket(), how) };
cvt(result).map(drop)
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
let mut nonblocking = nonblocking as c_ulong;
- let result = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) };
+ let result =
+ unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) };
cvt(result).map(drop)
}
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = c::linger {
+ l_onoff: linger.is_some() as c_ushort,
+ l_linger: linger.unwrap_or_default().as_secs() as c_ushort,
+ };
+
+ net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ let val: c::linger = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?;
+
+ Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+ }
+
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE)
}
@@ -446,6 +474,11 @@
let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?;
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
}
+
+ // This is used by sys_common code to abstract over Windows and Unix.
+ pub fn as_raw(&self) -> RawSocket {
+ self.as_inner().as_raw_socket()
+ }
}
#[unstable(reason = "not public", issue = "none", feature = "fd_read")]
@@ -455,28 +488,44 @@
}
}
-impl Drop for Socket {
- fn drop(&mut self) {
- let _ = unsafe { c::closesocket(self.0) };
- }
-}
-
-impl AsInner<c::SOCKET> for Socket {
- fn as_inner(&self) -> &c::SOCKET {
+impl AsInner<OwnedSocket> for Socket {
+ fn as_inner(&self) -> &OwnedSocket {
&self.0
}
}
-impl FromInner<c::SOCKET> for Socket {
- fn from_inner(sock: c::SOCKET) -> Socket {
+impl FromInner<OwnedSocket> for Socket {
+ fn from_inner(sock: OwnedSocket) -> Socket {
Socket(sock)
}
}
-impl IntoInner<c::SOCKET> for Socket {
- fn into_inner(self) -> c::SOCKET {
- let ret = self.0;
- mem::forget(self);
- ret
+impl IntoInner<OwnedSocket> for Socket {
+ fn into_inner(self) -> OwnedSocket {
+ self.0
+ }
+}
+
+impl AsSocket for Socket {
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ self.0.as_socket()
+ }
+}
+
+impl AsRawSocket for Socket {
+ fn as_raw_socket(&self) -> RawSocket {
+ self.0.as_raw_socket()
+ }
+}
+
+impl IntoRawSocket for Socket {
+ fn into_raw_socket(self) -> RawSocket {
+ self.0.into_raw_socket()
+ }
+}
+
+impl FromRawSocket for Socket {
+ unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self {
+ Self(FromRawSocket::from_raw_socket(raw_socket))
}
}
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index 77c378a..883690c 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -253,22 +253,13 @@
cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
}
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
- let k = to_u16s(k)?;
- let res = super::fill_utf16_buf(
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ let k = to_u16s(k).ok()?;
+ super::fill_utf16_buf(
|buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
|buf| OsStringExt::from_wide(buf),
- );
- match res {
- Ok(value) => Ok(Some(value)),
- Err(e) => {
- if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
- Ok(None)
- } else {
- Err(e)
- }
- }
- }
+ )
+ .ok()
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
@@ -297,7 +288,7 @@
if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
return None;
}
- let _handle = Handle::new(token);
+ let _handle = Handle::from_raw_handle(token);
super::fill_utf16_buf(
|buf, mut sz| {
match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index 104a8db..63d3d6c 100644
--- a/library/std/src/sys/windows/pipe.rs
+++ b/library/std/src/sys/windows/pipe.rs
@@ -12,6 +12,7 @@
use crate::sys::fs::{File, OpenOptions};
use crate::sys::handle::Handle;
use crate::sys::hashmap_random_keys;
+use crate::sys_common::IntoInner;
////////////////////////////////////////////////////////////////////////////////
// Anonymous pipes
@@ -21,6 +22,12 @@
inner: Handle,
}
+impl IntoInner<Handle> for AnonPipe {
+ fn into_inner(self) -> Handle {
+ self.inner
+ }
+}
+
pub struct Pipes {
pub ours: AnonPipe,
pub theirs: AnonPipe,
@@ -123,7 +130,7 @@
}
return Err(err);
}
- ours = Handle::new(handle);
+ ours = Handle::from_raw_handle(handle);
break;
}
@@ -146,11 +153,11 @@
};
opts.security_attributes(&mut sa);
let theirs = File::open(Path::new(&name), &opts)?;
- let theirs = AnonPipe { inner: theirs.into_handle() };
+ let theirs = AnonPipe { inner: theirs.into_inner() };
Ok(Pipes {
ours: AnonPipe { inner: ours },
- theirs: AnonPipe { inner: theirs.into_handle() },
+ theirs: AnonPipe { inner: theirs.into_inner() },
})
}
}
@@ -207,7 +214,7 @@
let mut p1 = AsyncPipe::new(p1, v1)?;
let mut p2 = AsyncPipe::new(p2, v2)?;
- let objs = [p1.event.raw(), p2.event.raw()];
+ let objs = [p1.event.as_raw_handle(), p2.event.as_raw_handle()];
// In a loop we wait for either pipe's scheduled read operation to complete.
// If the operation completes with 0 bytes, that means EOF was reached, in
@@ -262,7 +269,7 @@
// I/O operation is successfully scheduled (what we want).
let event = Handle::new_event(true, true)?;
let mut overlapped: Box<c::OVERLAPPED> = unsafe { Box::new(mem::zeroed()) };
- overlapped.hEvent = event.raw();
+ overlapped.hEvent = event.as_raw_handle();
Ok(AsyncPipe { pipe, overlapped, event, dst, state: State::NotReading })
}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index ae193b8..ccff906 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -15,6 +15,7 @@
use crate::mem;
use crate::num::NonZeroI32;
use crate::os::windows::ffi::OsStrExt;
+use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
use crate::path::Path;
use crate::ptr;
use crate::sys::c;
@@ -26,7 +27,7 @@
use crate::sys::stdio;
use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
-use crate::sys_common::AsInner;
+use crate::sys_common::{AsInner, IntoInner};
use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
@@ -316,9 +317,9 @@
let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?;
let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?;
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
- si.hStdInput = stdin.raw();
- si.hStdOutput = stdout.raw();
- si.hStdError = stderr.raw();
+ si.hStdInput = stdin.as_raw_handle();
+ si.hStdOutput = stdout.as_raw_handle();
+ si.hStdError = stderr.as_raw_handle();
unsafe {
cvt(c::CreateProcessW(
@@ -338,9 +339,11 @@
// We close the thread handle because we don't care about keeping
// the thread id valid, and we aren't keeping the thread handle
// around to be able to close it later.
- drop(Handle::new(pi.hThread));
+ unsafe {
+ drop(Handle::from_raw_handle(pi.hThread));
- Ok((Process { handle: Handle::new(pi.hProcess) }, pipes))
+ Ok((Process { handle: Handle::from_raw_handle(pi.hProcess) }, pipes))
+ }
}
}
@@ -365,13 +368,13 @@
// should still be unavailable so propagate the
// INVALID_HANDLE_VALUE.
Stdio::Inherit => match stdio::get_handle(stdio_id) {
- Ok(io) => {
- let io = Handle::new(io);
+ Ok(io) => unsafe {
+ let io = Handle::from_raw_handle(io);
let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
- io.into_raw();
+ io.into_raw_handle();
ret
- }
- Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)),
+ },
+ Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) },
},
Stdio::MakePipe => {
@@ -397,7 +400,7 @@
opts.read(stdio_id == c::STD_INPUT_HANDLE);
opts.write(stdio_id != c::STD_INPUT_HANDLE);
opts.security_attributes(&mut sa);
- File::open(Path::new("NUL"), &opts).map(|file| file.into_handle())
+ File::open(Path::new("NUL"), &opts).map(|file| file.into_inner())
}
}
}
@@ -411,7 +414,7 @@
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
- Stdio::Handle(file.into_handle())
+ Stdio::Handle(file.into_inner())
}
}
@@ -430,29 +433,29 @@
impl Process {
pub fn kill(&mut self) -> io::Result<()> {
- cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?;
+ cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?;
Ok(())
}
pub fn id(&self) -> u32 {
- unsafe { c::GetProcessId(self.handle.raw()) as u32 }
+ unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 }
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
unsafe {
- let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE);
+ let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE);
if res != c::WAIT_OBJECT_0 {
return Err(Error::last_os_error());
}
let mut status = 0;
- cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?;
+ cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
Ok(ExitStatus(status))
}
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
unsafe {
- match c::WaitForSingleObject(self.handle.raw(), 0) {
+ match c::WaitForSingleObject(self.handle.as_raw_handle(), 0) {
c::WAIT_OBJECT_0 => {}
c::WAIT_TIMEOUT => {
return Ok(None);
@@ -460,7 +463,7 @@
_ => return Err(io::Error::last_os_error()),
}
let mut status = 0;
- cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?;
+ cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
Ok(Some(ExitStatus(status)))
}
}
diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs
index be3141e..2719a53 100644
--- a/library/std/src/sys/windows/stdio.rs
+++ b/library/std/src/sys/windows/stdio.rs
@@ -3,19 +3,31 @@
use crate::char::decode_utf16;
use crate::cmp;
use crate::io;
+use crate::os::windows::io::{FromRawHandle, IntoRawHandle};
use crate::ptr;
use crate::str;
use crate::sys::c;
use crate::sys::cvt;
use crate::sys::handle::Handle;
+use core::str::utf8_char_width;
// Don't cache handles but get them fresh for every read/write. This allows us to track changes to
// the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490.
pub struct Stdin {
surrogate: u16,
}
-pub struct Stdout;
-pub struct Stderr;
+pub struct Stdout {
+ incomplete_utf8: IncompleteUtf8,
+}
+
+pub struct Stderr {
+ incomplete_utf8: IncompleteUtf8,
+}
+
+struct IncompleteUtf8 {
+ bytes: [u8; 4],
+ len: u8,
+}
// Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see
// #13304 for details).
@@ -50,31 +62,92 @@
unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
}
-fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> {
- let handle = get_handle(handle_id)?;
- if !is_console(handle) {
- let handle = Handle::new(handle);
- let ret = handle.write(data);
- handle.into_raw(); // Don't close the handle
- return ret;
+fn write(
+ handle_id: c::DWORD,
+ data: &[u8],
+ incomplete_utf8: &mut IncompleteUtf8,
+) -> io::Result<usize> {
+ if data.is_empty() {
+ return Ok(0);
}
- // As the console is meant for presenting text, we assume bytes of `data` come from a string
- // and are encoded as UTF-8, which needs to be encoded as UTF-16.
- //
- // If the data is not valid UTF-8 we write out as many bytes as are valid.
- // Only when there are no valid bytes (which will happen on the next call), return an error.
- let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2);
- let utf8 = match str::from_utf8(&data[..len]) {
- Ok(s) => s,
- Err(ref e) if e.valid_up_to() == 0 => {
+ let handle = get_handle(handle_id)?;
+ if !is_console(handle) {
+ unsafe {
+ let handle = Handle::from_raw_handle(handle);
+ let ret = handle.write(data);
+ handle.into_raw_handle(); // Don't close the handle
+ return ret;
+ }
+ }
+
+ if incomplete_utf8.len > 0 {
+ assert!(
+ incomplete_utf8.len < 4,
+ "Unexpected number of bytes for incomplete UTF-8 codepoint."
+ );
+ if data[0] >> 6 != 0b10 {
+ // not a continuation byte - reject
+ incomplete_utf8.len = 0;
return Err(io::Error::new_const(
io::ErrorKind::InvalidData,
&"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
));
}
+ incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0];
+ incomplete_utf8.len += 1;
+ let char_width = utf8_char_width(incomplete_utf8.bytes[0]);
+ if (incomplete_utf8.len as usize) < char_width {
+ // more bytes needed
+ return Ok(1);
+ }
+ let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]);
+ incomplete_utf8.len = 0;
+ match s {
+ Ok(s) => {
+ assert_eq!(char_width, s.len());
+ let written = write_valid_utf8_to_console(handle, s)?;
+ assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes
+ return Ok(1);
+ }
+ Err(_) => {
+ return Err(io::Error::new_const(
+ io::ErrorKind::InvalidData,
+ &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ ));
+ }
+ }
+ }
+
+ // As the console is meant for presenting text, we assume bytes of `data` are encoded as UTF-8,
+ // which needs to be encoded as UTF-16.
+ //
+ // If the data is not valid UTF-8 we write out as many bytes as are valid.
+ // If the first byte is invalid it is either first byte of a multi-byte sequence but the
+ // provided byte slice is too short or it is the first byte of an invalide multi-byte sequence.
+ let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2);
+ let utf8 = match str::from_utf8(&data[..len]) {
+ Ok(s) => s,
+ Err(ref e) if e.valid_up_to() == 0 => {
+ let first_byte_char_width = utf8_char_width(data[0]);
+ if first_byte_char_width > 1 && data.len() < first_byte_char_width {
+ incomplete_utf8.bytes[0] = data[0];
+ incomplete_utf8.len = 1;
+ return Ok(1);
+ } else {
+ return Err(io::Error::new_const(
+ io::ErrorKind::InvalidData,
+ &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ ));
+ }
+ }
Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
};
+
+ write_valid_utf8_to_console(handle, utf8)
+}
+
+fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> {
let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2];
let mut len_utf16 = 0;
for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) {
@@ -140,10 +213,12 @@
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let handle = get_handle(c::STD_INPUT_HANDLE)?;
if !is_console(handle) {
- let handle = Handle::new(handle);
- let ret = handle.read(buf);
- handle.into_raw(); // Don't close the handle
- return ret;
+ unsafe {
+ let handle = Handle::from_raw_handle(handle);
+ let ret = handle.read(buf);
+ handle.into_raw_handle(); // Don't close the handle
+ return ret;
+ }
}
if buf.len() == 0 {
@@ -157,7 +232,7 @@
}
let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2];
- // In the worst case, an UTF-8 string can take 3 bytes for every `u16` of an UTF-16. So
+ // 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());
@@ -169,7 +244,7 @@
// We assume that if the last `u16` is an unpaired surrogate they got sliced apart by our
// buffer size, and keep it around for the next read hoping to put them together.
-// This is a best effort, and may not work if we are not the only reader on 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],
@@ -254,15 +329,21 @@
Ok(written)
}
+impl IncompleteUtf8 {
+ pub const fn new() -> IncompleteUtf8 {
+ IncompleteUtf8 { bytes: [0; 4], len: 0 }
+ }
+}
+
impl Stdout {
pub const fn new() -> Stdout {
- Stdout
+ Stdout { incomplete_utf8: IncompleteUtf8::new() }
}
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- write(c::STD_OUTPUT_HANDLE, buf)
+ write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8)
}
fn flush(&mut self) -> io::Result<()> {
@@ -272,13 +353,13 @@
impl Stderr {
pub const fn new() -> Stderr {
- Stderr
+ Stderr { incomplete_utf8: IncompleteUtf8::new() }
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- write(c::STD_ERROR_HANDLE, buf)
+ write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8)
}
fn flush(&mut self) -> io::Result<()> {
diff --git a/library/std/src/sys/windows/stdio_uwp.rs b/library/std/src/sys/windows/stdio_uwp.rs
index 872511a..32550f7 100644
--- a/library/std/src/sys/windows/stdio_uwp.rs
+++ b/library/std/src/sys/windows/stdio_uwp.rs
@@ -2,6 +2,7 @@
use crate::io;
use crate::mem::ManuallyDrop;
+use crate::os::windows::io::FromRawHandle;
use crate::sys::c;
use crate::sys::handle::Handle;
@@ -25,7 +26,8 @@
fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> {
let handle = get_handle(handle_id)?;
- let handle = Handle::new(handle);
+ // SAFETY: The handle returned from `get_handle` must be valid and non-null.
+ let handle = unsafe { Handle::from_raw_handle(handle) };
ManuallyDrop::new(handle).write(data)
}
@@ -38,7 +40,8 @@
impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let handle = get_handle(c::STD_INPUT_HANDLE)?;
- let handle = Handle::new(handle);
+ // SAFETY: The handle returned from `get_handle` must be valid and non-null.
+ let handle = unsafe { Handle::from_raw_handle(handle) };
ManuallyDrop::new(handle).read(buf)
}
}
diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs
index ef7a973..a529313 100644
--- a/library/std/src/sys/windows/thread.rs
+++ b/library/std/src/sys/windows/thread.rs
@@ -1,6 +1,7 @@
use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
+use crate::os::windows::io::{AsRawHandle, FromRawHandle};
use crate::ptr;
use crate::sys::c;
use crate::sys::handle::Handle;
@@ -45,7 +46,7 @@
drop(Box::from_raw(p));
Err(io::Error::last_os_error())
} else {
- Ok(Thread { handle: Handle::new(ret) })
+ Ok(Thread { handle: Handle::from_raw_handle(ret) })
};
extern "system" fn thread_start(main: *mut c_void) -> c::DWORD {
@@ -71,7 +72,7 @@
}
pub fn join(self) {
- let rc = unsafe { c::WaitForSingleObject(self.handle.raw(), c::INFINITE) };
+ let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
if rc == c::WAIT_FAILED {
panic!("failed to join on thread: {}", io::Error::last_os_error());
}
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index a549770..e6a099f 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -75,7 +75,7 @@
hit = true;
if print_fmt == PrintFmt::Short {
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
- if sym.contains("__rust_begin_short_backtrace") {
+ if start && sym.contains("__rust_begin_short_backtrace") {
stop = true;
return;
}
diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs
index 7c1d98a..ea9108f 100644
--- a/library/std/src/sys_common/io.rs
+++ b/library/std/src/sys_common/io.rs
@@ -1,4 +1,6 @@
-pub const DEFAULT_BUF_SIZE: usize = 8 * 1024;
+// Bare metal platforms usually have very small amounts of RAM
+// (in the order of hundreds of KB)
+pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 };
#[cfg(test)]
#[allow(dead_code)] // not used on emscripten
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index db83bad..8944405 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -26,10 +26,6 @@
pub mod io;
pub mod memchr;
pub mod mutex;
-// `doc` is required because `sys/mod.rs` imports `unix/ext/mod.rs` on Windows
-// when generating documentation.
-#[cfg(any(doc, not(windows)))]
-pub mod os_str_bytes;
pub mod process;
pub mod remutex;
#[macro_use]
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index d5f29c4..c5c3df3 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -61,13 +61,7 @@
pub fn setsockopt<T>(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> {
unsafe {
let payload = &payload as *const T as *const c_void;
- cvt(c::setsockopt(
- *sock.as_inner(),
- opt,
- val,
- payload,
- mem::size_of::<T>() as c::socklen_t,
- ))?;
+ cvt(c::setsockopt(sock.as_raw(), opt, val, payload, mem::size_of::<T>() as c::socklen_t))?;
Ok(())
}
}
@@ -76,7 +70,7 @@
unsafe {
let mut slot: T = mem::zeroed();
let mut len = mem::size_of::<T>() as c::socklen_t;
- cvt(c::getsockopt(*sock.as_inner(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?;
+ cvt(c::getsockopt(sock.as_raw(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?;
assert_eq!(len as usize, mem::size_of::<T>());
Ok(slot)
}
@@ -217,7 +211,7 @@
let sock = Socket::new(addr, c::SOCK_STREAM)?;
let (addrp, len) = addr.into_inner();
- cvt_r(|| unsafe { c::connect(*sock.as_inner(), addrp, len) })?;
+ cvt_r(|| unsafe { c::connect(sock.as_raw(), addrp, len) })?;
Ok(TcpStream { inner: sock })
}
@@ -273,7 +267,7 @@
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
let ret = cvt(unsafe {
- c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL)
+ c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL)
})?;
Ok(ret as usize)
}
@@ -288,11 +282,11 @@
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) })
+ sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) })
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) })
+ sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) })
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
@@ -303,6 +297,14 @@
self.inner.duplicate().map(|s| TcpStream { inner: s })
}
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ self.inner.set_linger(linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ self.inner.linger()
+ }
+
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
@@ -348,7 +350,7 @@
}
let name = if cfg!(windows) { "socket" } else { "fd" };
- res.field(name, &self.inner.as_inner()).finish()
+ res.field(name, &self.inner.as_raw()).finish()
}
}
@@ -380,10 +382,10 @@
// Bind our new socket
let (addrp, len) = addr.into_inner();
- cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?;
+ cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?;
// Start listening
- cvt(unsafe { c::listen(*sock.as_inner(), 128) })?;
+ cvt(unsafe { c::listen(sock.as_raw(), 128) })?;
Ok(TcpListener { inner: sock })
}
@@ -396,7 +398,7 @@
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) })
+ sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) })
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
@@ -453,7 +455,7 @@
}
let name = if cfg!(windows) { "socket" } else { "fd" };
- res.field(name, &self.inner.as_inner()).finish()
+ res.field(name, &self.inner.as_raw()).finish()
}
}
@@ -473,7 +475,7 @@
let sock = Socket::new(addr, c::SOCK_DGRAM)?;
let (addrp, len) = addr.into_inner();
- cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?;
+ cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?;
Ok(UdpSocket { inner: sock })
}
@@ -486,11 +488,11 @@
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
- sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) })
+ sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) })
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
- sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) })
+ sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) })
}
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
@@ -506,7 +508,7 @@
let (dstp, dstlen) = dst.into_inner();
let ret = cvt(unsafe {
c::sendto(
- *self.inner.as_inner(),
+ self.inner.as_raw(),
buf.as_ptr() as *const c_void,
len,
MSG_NOSIGNAL,
@@ -643,14 +645,14 @@
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
let ret = cvt(unsafe {
- c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL)
+ c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL)
})?;
Ok(ret as usize)
}
pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> {
let (addrp, len) = addr?.into_inner();
- cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(drop)
+ cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addrp, len) }).map(drop)
}
}
@@ -669,6 +671,6 @@
}
let name = if cfg!(windows) { "socket" } else { "fd" };
- res.field(name, &self.inner.as_inner()).finish()
+ res.field(name, &self.inner.as_raw()).finish()
}
}
diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs
index 88453de..64873b8 100644
--- a/library/std/src/sys_common/remutex/tests.rs
+++ b/library/std/src/sys_common/remutex/tests.rs
@@ -30,7 +30,7 @@
#[test]
fn is_mutex() {
let m = unsafe {
- // FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
+ // 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)
@@ -52,7 +52,7 @@
#[test]
fn trylock_works() {
let m = unsafe {
- // FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
+ // 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)
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index 1bd3cfd..9508bd7 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -785,7 +785,7 @@
/// Copied from core::str::raw::slice_unchecked
#[inline]
pub unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 {
- // memory layout of an &[u8] and &Wtf8 are the same
+ // memory layout of a &[u8] and &Wtf8 are the same
Wtf8::from_bytes_unchecked(slice::from_raw_parts(s.bytes.as_ptr().add(begin), end - begin))
}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 9f7e6b9..f44df84 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -28,7 +28,7 @@
//! When the main thread of a Rust program terminates, the entire program shuts
//! down, even if other threads are still running. However, this module provides
//! convenient facilities for automatically waiting for the termination of a
-//! child thread (i.e., join).
+//! thread (i.e., join).
//!
//! ## Spawning a thread
//!
@@ -42,38 +42,43 @@
//! });
//! ```
//!
-//! In this example, the spawned thread is "detached" from the current
-//! thread. This means that it can outlive its parent (the thread that spawned
-//! it), unless this parent is the main thread.
+//! In this example, the spawned thread is "detached," which means that there is
+//! no way for the program to learn when the spawned thread completes or otherwise
+//! terminates.
//!
-//! The parent thread can also wait on the completion of the child
-//! thread; a call to [`spawn`] produces a [`JoinHandle`], which provides
-//! a `join` method for waiting:
+//! To learn when a thread completes, it is necessary to capture the [`JoinHandle`]
+//! object that is returned by the call to [`spawn`], which provides
+//! a `join` method that allows the caller to wait for the completion of the
+//! spawned thread:
//!
//! ```rust
//! use std::thread;
//!
-//! let child = thread::spawn(move || {
+//! let thread_join_handle = thread::spawn(move || {
//! // some work here
//! });
//! // some work here
-//! let res = child.join();
+//! let res = thread_join_handle.join();
//! ```
//!
//! The [`join`] method returns a [`thread::Result`] containing [`Ok`] of the final
-//! value produced by the child thread, or [`Err`] of the value given to
-//! a call to [`panic!`] if the child panicked.
+//! value produced by the spawned thread, or [`Err`] of the value given to
+//! a call to [`panic!`] if the thread panicked.
+//!
+//! Note that there is no parent/child relationship between a thread that spawns a
+//! new thread and the thread being spawned. In particular, the spawned thread may or
+//! may not outlive the spawning thread, unless the spawning thread is the main thread.
//!
//! ## Configuring threads
//!
//! A new thread can be configured before it is spawned via the [`Builder`] type,
-//! which currently allows you to set the name and stack size for the child thread:
+//! which currently allows you to set the name and stack size for the thread:
//!
//! ```rust
//! # #![allow(unused_must_use)]
//! use std::thread;
//!
-//! thread::Builder::new().name("child1".to_string()).spawn(move || {
+//! thread::Builder::new().name("thread1".to_string()).spawn(move || {
//! println!("Hello, world!");
//! });
//! ```
@@ -344,7 +349,7 @@
/// The spawned thread may outlive the caller (unless the caller thread
/// is the main thread; the whole process is terminated when the main
/// thread finishes). The join handle can be used to block on
- /// termination of the child thread, including recovering its panics.
+ /// termination of the spawned thread, including recovering its panics.
///
/// For a more complete documentation see [`thread::spawn`][`spawn`].
///
@@ -389,7 +394,7 @@
/// The spawned thread may outlive the caller (unless the caller thread
/// is the main thread; the whole process is terminated when the main
/// thread finishes). The join handle can be used to block on
- /// termination of the child thread, including recovering its panics.
+ /// termination of the spawned thread, including recovering its panics.
///
/// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`],
/// except for the relaxed lifetime bounds, which render it unsafe.
@@ -516,15 +521,16 @@
/// Spawns a new thread, returning a [`JoinHandle`] for it.
///
-/// The join handle will implicitly *detach* the child thread upon being
-/// dropped. In this case, the child thread may outlive the parent (unless
-/// the parent thread is the main thread; the whole process is terminated when
-/// the main thread finishes). Additionally, the join handle provides a [`join`]
-/// method that can be used to join the child thread. If the child thread
-/// panics, [`join`] will return an [`Err`] containing the argument given to
-/// [`panic!`].
+/// The join handle provides a [`join`] method that can be used to join the spawned
+/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
+/// the argument given to [`panic!`].
///
-/// This will create a thread using default parameters of [`Builder`], if you
+/// If the join handle is dropped, the spawned thread will implicitly be *detached*.
+/// In this case, the spawned thread may no longer be joined.
+/// (It is the responsibility of the program to either eventually join threads it
+/// creates or detach them; otherwise, a resource leak will result.)
+///
+/// This call will create a thread using default parameters of [`Builder`], if you
/// want to specify the stack size or the name of the thread, use this API
/// instead.
///
@@ -533,8 +539,8 @@
///
/// - The `'static` constraint means that the closure and its return value
/// must have a lifetime of the whole program execution. The reason for this
-/// is that threads can `detach` and outlive the lifetime they have been
-/// created in.
+/// is that threads can outlive the lifetime they have been created in.
+///
/// Indeed if the thread, and by extension its return value, can outlive their
/// caller, we need to make sure that they will be valid afterwards, and since
/// we *can't* know when it will return we need to have them valid as long as
@@ -906,7 +912,7 @@
/// The semantics of this function are equivalent to [`park`] except
/// that the thread will be blocked for roughly no longer than `dur`. This
/// method should not be used for precise timing due to anomalies such as
-/// preemption or platform differences that may not cause the maximum
+/// preemption or platform differences that might not cause the maximum
/// amount of time waited to be precisely `ms` long.
///
/// See the [park documentation][`park`] for more detail.
@@ -922,7 +928,7 @@
/// The semantics of this function are equivalent to [`park`][park] except
/// that the thread will be blocked for roughly no longer than `dur`. This
/// method should not be used for precise timing due to anomalies such as
-/// preemption or platform differences that may not cause the maximum
+/// preemption or platform differences that might not cause the maximum
/// amount of time waited to be precisely `dur` long.
///
/// See the [park documentation][park] for more details.
@@ -1236,10 +1242,10 @@
#[stable(feature = "rust1", since = "1.0.0")]
pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
-// This packet is used to communicate the return value between the child thread
-// and the parent thread. Memory is shared through the `Arc` within and there's
+// This packet is used to communicate the return value between the spawned thread
+// and the rest of the program. Memory is shared through the `Arc` within and there's
// no need for a mutex here because synchronization happens with `join()` (the
-// parent thread never reads this packet until the child has exited).
+// caller will never read this packet until the thread has exited).
//
// This packet itself is then stored into a `JoinInner` which in turns is placed
// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to
@@ -1303,7 +1309,7 @@
/// }).unwrap();
/// ```
///
-/// Child being detached and outliving its parent:
+/// A thread being detached and outliving the thread that spawned it:
///
/// ```no_run
/// use std::thread;
@@ -1361,12 +1367,15 @@
/// Waits for the associated thread to finish.
///
+ /// This function will return immediately if the associated thread has already finished.
+ ///
/// In terms of [atomic memory orderings], the completion of the associated
/// thread synchronizes with this function returning. In other words, all
- /// operations performed by that thread are ordered before all
+ /// operations performed by that thread [happen
+ /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all
/// operations that happen after `join` returns.
///
- /// If the child thread panics, [`Err`] is returned with the parameter given
+ /// If the associated thread panics, [`Err`] is returned with the parameter given
/// to [`panic!`].
///
/// [`Err`]: crate::result::Result::Err
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 003069b..e9207ee 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -1,40 +1,58 @@
//! Temporal quantification.
//!
-//! Example:
+//! # Examples:
+//!
+//! There are multiple ways to create a new [`Duration`]:
//!
//! ```
-//! use std::time::Duration;
+//! # use std::time::Duration;
+//! let five_seconds = Duration::from_secs(5);
+//! assert_eq!(five_seconds, Duration::from_millis(5_000));
+//! assert_eq!(five_seconds, Duration::from_micros(5_000_000));
+//! assert_eq!(five_seconds, Duration::from_nanos(5_000_000_000));
//!
-//! let five_seconds = Duration::new(5, 0);
-//! // both declarations are equivalent
-//! assert_eq!(Duration::new(5, 0), Duration::from_secs(5));
+//! let ten_seconds = Duration::from_secs(10);
+//! let seven_nanos = Duration::from_nanos(7);
+//! let total = ten_seconds + seven_nanos;
+//! assert_eq!(total, Duration::new(10, 7));
+//! ```
+//!
+//! Using [`Instant`] to calculate how long a function took to run:
+//!
+//! ```ignore (incomplete)
+//! let now = Instant::now();
+//!
+//! // Calling a slow function, it may take a while
+//! slow_function();
+//!
+//! let elapsed_time = now.elapsed();
+//! println!("Running slow_function() took {} seconds.", elapsed_time.as_secs());
//! ```
#![stable(feature = "time", since = "1.3.0")]
+mod monotonic;
#[cfg(test)]
mod tests;
-use crate::cmp;
use crate::error::Error;
use crate::fmt;
use crate::ops::{Add, AddAssign, Sub, SubAssign};
use crate::sys::time;
-use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::FromInner;
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;
/// A measurement of a monotonically nondecreasing clock.
-/// Opaque and useful only with `Duration`.
+/// Opaque and useful only with [`Duration`].
///
/// Instants are always guaranteed to be no less than any previously measured
/// instant when created, and are often useful for tasks such as measuring
/// benchmarks or timing how long an operation takes.
///
/// Note, however, that instants are not guaranteed to be **steady**. In other
-/// words, each tick of the underlying clock may not be the same length (e.g.
+/// words, each tick of the underlying clock might not be the same length (e.g.
/// some seconds may be longer than others). An instant may jump forwards or
/// experience time dilation (slow down or speed up), but it will never go
/// backwards.
@@ -249,14 +267,7 @@
return Instant(os_now);
}
- static LOCK: StaticMutex = StaticMutex::new();
- static mut LAST_NOW: time::Instant = time::Instant::zero();
- unsafe {
- let _lock = LOCK.lock();
- let now = cmp::max(LAST_NOW, os_now);
- LAST_NOW = now;
- Instant(now)
- }
+ Instant(monotonic::monotonize(os_now))
}
/// Returns the amount of time elapsed from another instant to this one.
@@ -485,7 +496,7 @@
///
/// This function may fail as the underlying system clock is susceptible to
/// drift and updates (e.g., the system clock could go backwards), so this
- /// function may not always succeed. If successful, [`Ok`]`(`[`Duration`]`)` is
+ /// function might not always succeed. If successful, [`Ok`]`(`[`Duration`]`)` is
/// returned where the duration represents the amount of time elapsed from
/// this time measurement to the current time.
///
diff --git a/library/std/src/time/monotonic.rs b/library/std/src/time/monotonic.rs
new file mode 100644
index 0000000..fa96b7a
--- /dev/null
+++ b/library/std/src/time/monotonic.rs
@@ -0,0 +1,115 @@
+use crate::sys::time;
+
+#[inline]
+pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
+ inner::monotonize(raw)
+}
+
+#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))]
+pub mod inner {
+ use crate::sync::atomic::AtomicU64;
+ use crate::sync::atomic::Ordering::*;
+ use crate::sys::time;
+ use crate::time::Duration;
+
+ pub(in crate::time) const ZERO: time::Instant = time::Instant::zero();
+
+ // bits 30 and 31 are never used since the nanoseconds part never exceeds 10^9
+ const UNINITIALIZED: u64 = 0b11 << 30;
+ static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED);
+
+ #[inline]
+ pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
+ monotonize_impl(&MONO, raw)
+ }
+
+ #[inline]
+ pub(in crate::time) fn monotonize_impl(mono: &AtomicU64, raw: time::Instant) -> time::Instant {
+ let delta = raw.checked_sub_instant(&ZERO).unwrap();
+ let secs = delta.as_secs();
+ // occupies no more than 30 bits (10^9 seconds)
+ let nanos = delta.subsec_nanos() as u64;
+
+ // This wraps around every 136 years (2^32 seconds).
+ // To detect backsliding we use wrapping arithmetic and declare forward steps smaller
+ // than 2^31 seconds as expected and everything else as a backslide which will be
+ // monotonized.
+ // This could be a problem for programs that call instants at intervals greater
+ // than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true.
+ let packed = (secs << 32) | nanos;
+ let old = mono.load(Relaxed);
+
+ if old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 {
+ mono.store(packed, Relaxed);
+ raw
+ } else {
+ // Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
+ // passed in value and the 64bits loaded from the atomic
+ let seconds_lower = old >> 32;
+ let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
+ if secs & 0xffff_ffff > seconds_lower {
+ // Backslide caused the lower 32bit of the seconds part to wrap.
+ // This must be the case because the seconds part is larger even though
+ // we are in the backslide branch, i.e. the seconds count should be smaller or equal.
+ //
+ // We assume that backslides are smaller than 2^32 seconds
+ // which means we need to add 1 to the upper half to restore it.
+ //
+ // Example:
+ // most recent observed time: 0xA1_0000_0000_0000_0000u128
+ // bits stored in AtomicU64: 0x0000_0000_0000_0000u64
+ // backslide by 1s
+ // caller time is 0xA0_ffff_ffff_0000_0000u128
+ // -> we can fix up the upper half time by adding 1 << 32
+ seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
+ }
+ let secs = seconds_upper | seconds_lower;
+ let nanos = old as u32;
+ ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
+ }
+ }
+}
+
+#[cfg(target_has_atomic = "128")]
+pub mod inner {
+ use crate::sync::atomic::AtomicU128;
+ use crate::sync::atomic::Ordering::*;
+ use crate::sys::time;
+ use crate::time::Duration;
+
+ const ZERO: time::Instant = time::Instant::zero();
+ static MONO: AtomicU128 = AtomicU128::new(0);
+
+ #[inline]
+ pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
+ let delta = raw.checked_sub_instant(&ZERO).unwrap();
+ // Split into seconds and nanos since Duration doesn't have a
+ // constructor that takes a u128
+ let secs = delta.as_secs() as u128;
+ let nanos = delta.subsec_nanos() as u128;
+ let timestamp: u128 = secs << 64 | nanos;
+ let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp);
+ let secs = (timestamp >> 64) as u64;
+ let nanos = timestamp as u32;
+ ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
+ }
+}
+
+#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))]
+pub mod inner {
+ use crate::cmp;
+ use crate::sys::time;
+ use crate::sys_common::mutex::StaticMutex;
+
+ #[inline]
+ pub(super) fn monotonize(os_now: time::Instant) -> time::Instant {
+ static LOCK: StaticMutex = StaticMutex::new();
+ static mut LAST_NOW: time::Instant = time::Instant::zero();
+ unsafe {
+ let _lock = LOCK.lock();
+ let now = cmp::max(LAST_NOW, os_now);
+ LAST_NOW = now;
+ now
+ }
+ }
+}
diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs
index 20c813f..dc44c93 100644
--- a/library/std/src/time/tests.rs
+++ b/library/std/src/time/tests.rs
@@ -1,4 +1,6 @@
use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
+#[cfg(not(target_arch = "wasm32"))]
+use test::{black_box, Bencher};
macro_rules! assert_almost_eq {
($a:expr, $b:expr) => {{
@@ -13,8 +15,34 @@
#[test]
fn instant_monotonic() {
let a = Instant::now();
- let b = Instant::now();
- assert!(b >= a);
+ loop {
+ let b = Instant::now();
+ assert!(b >= a);
+ if b > a {
+ break;
+ }
+ }
+}
+
+#[test]
+#[cfg(not(target_arch = "wasm32"))]
+fn instant_monotonic_concurrent() -> crate::thread::Result<()> {
+ let threads: Vec<_> = (0..8)
+ .map(|_| {
+ crate::thread::spawn(|| {
+ let mut old = Instant::now();
+ for _ in 0..5_000_000 {
+ let new = Instant::now();
+ assert!(new >= old);
+ old = new;
+ }
+ })
+ })
+ .collect();
+ for t in threads {
+ t.join()?;
+ }
+ Ok(())
}
#[test]
@@ -163,3 +191,71 @@
let hundred_twenty_years = thirty_years * 4;
assert!(a < hundred_twenty_years);
}
+
+#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))]
+#[test]
+fn monotonizer_wrapping_backslide() {
+ use super::monotonic::inner::{monotonize_impl, ZERO};
+ use core::sync::atomic::AtomicU64;
+
+ let reference = AtomicU64::new(0);
+
+ let time = match ZERO.checked_add_duration(&Duration::from_secs(0xffff_ffff)) {
+ Some(time) => time,
+ None => {
+ // platform cannot represent u32::MAX seconds so it won't have to deal with this kind
+ // of overflow either
+ return;
+ }
+ };
+
+ let monotonized = monotonize_impl(&reference, time);
+ let expected = ZERO.checked_add_duration(&Duration::from_secs(1 << 32)).unwrap();
+ assert_eq!(
+ monotonized, expected,
+ "64bit monotonizer should handle overflows in the seconds part"
+ );
+}
+
+macro_rules! bench_instant_threaded {
+ ($bench_name:ident, $thread_count:expr) => {
+ #[bench]
+ #[cfg(not(target_arch = "wasm32"))]
+ fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> {
+ use crate::sync::atomic::{AtomicBool, Ordering};
+ use crate::sync::Arc;
+
+ let running = Arc::new(AtomicBool::new(true));
+
+ let threads: Vec<_> = (0..$thread_count)
+ .map(|_| {
+ let flag = Arc::clone(&running);
+ crate::thread::spawn(move || {
+ while flag.load(Ordering::Relaxed) {
+ black_box(Instant::now());
+ }
+ })
+ })
+ .collect();
+
+ b.iter(|| {
+ let a = Instant::now();
+ let b = Instant::now();
+ assert!(b >= a);
+ });
+
+ running.store(false, Ordering::Relaxed);
+
+ for t in threads {
+ t.join()?;
+ }
+ Ok(())
+ }
+ };
+}
+
+bench_instant_threaded!(instant_contention_01_threads, 0);
+bench_instant_threaded!(instant_contention_02_threads, 1);
+bench_instant_threaded!(instant_contention_04_threads, 3);
+bench_instant_threaded!(instant_contention_08_threads, 7);
+bench_instant_threaded!(instant_contention_16_threads, 15);