diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
commit | dc0db358abe19481e475e10c32149b53370f1a1c (patch) | |
tree | ab8ce99c4b255ce46f99ef402c27916055b899ee /library/std/src | |
parent | Releasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src')
83 files changed, 1251 insertions, 553 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index c722bad2e..a083b6560 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -623,28 +623,27 @@ impl<K, V, S> HashMap<K, V, S> { /// If the closure returns false, or panics, the element remains in the map and will not be /// yielded. /// - /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// Note that `extract_if` lets you mutate every value in the filter closure, regardless of /// whether you choose to keep or remove it. /// - /// If the iterator is only partially consumed or not consumed at all, each of the remaining - /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating + /// or the iteration short-circuits, then the remaining elements will be retained. + /// Use [`retain`] with a negated predicate if you do not need the returned iterator. /// - /// It is unspecified how many more elements will be subjected to the closure - /// if a panic occurs in the closure, or a panic occurs while dropping an element, - /// or if the `DrainFilter` value is leaked. + /// [`retain`]: HashMap::retain /// /// # Examples /// /// Splitting a map into even and odd keys, reusing the original map: /// /// ``` - /// #![feature(hash_drain_filter)] + /// #![feature(hash_extract_if)] /// use std::collections::HashMap; /// /// let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect(); - /// let drained: HashMap<i32, i32> = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// let extracted: HashMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect(); /// - /// let mut evens = drained.keys().copied().collect::<Vec<_>>(); + /// let mut evens = extracted.keys().copied().collect::<Vec<_>>(); /// let mut odds = map.keys().copied().collect::<Vec<_>>(); /// evens.sort(); /// odds.sort(); @@ -654,12 +653,12 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` #[inline] #[rustc_lint_query_instability] - #[unstable(feature = "hash_drain_filter", issue = "59618")] - pub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + #[unstable(feature = "hash_extract_if", issue = "59618")] + pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, { - DrainFilter { base: self.base.drain_filter(pred) } + ExtractIf { base: self.base.extract_if(pred) } } /// Retains only the elements specified by the predicate. @@ -1578,28 +1577,29 @@ impl<'a, K, V> Drain<'a, K, V> { /// A draining, filtering iterator over the entries of a `HashMap`. /// -/// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. +/// This `struct` is created by the [`extract_if`] method on [`HashMap`]. /// -/// [`drain_filter`]: HashMap::drain_filter +/// [`extract_if`]: HashMap::extract_if /// /// # Example /// /// ``` -/// #![feature(hash_drain_filter)] +/// #![feature(hash_extract_if)] /// /// use std::collections::HashMap; /// /// let mut map = HashMap::from([ /// ("a", 1), /// ]); -/// let iter = map.drain_filter(|_k, v| *v % 2 == 0); +/// let iter = map.extract_if(|_k, v| *v % 2 == 0); /// ``` -#[unstable(feature = "hash_drain_filter", issue = "59618")] -pub struct DrainFilter<'a, K, V, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf<'a, K, V, F> where F: FnMut(&K, &mut V) -> bool, { - base: base::DrainFilter<'a, K, V, F>, + base: base::ExtractIf<'a, K, V, F>, } /// A mutable iterator over the values of a `HashMap`. @@ -2479,8 +2479,8 @@ where } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<K, V, F> Iterator for DrainFilter<'_, K, V, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<K, V, F> Iterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, { @@ -2496,16 +2496,16 @@ where } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<K, V, F> FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<K, V, F> FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<'a, K, V, F> fmt::Debug for ExtractIf<'a, K, V, F> where F: FnMut(&K, &mut V) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DrainFilter").finish_non_exhaustive() + f.debug_struct("ExtractIf").finish_non_exhaustive() } } @@ -2543,12 +2543,12 @@ impl<'a, K, V> Entry<'a, K, V> { /// ``` /// use std::collections::HashMap; /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let s = "hoho".to_string(); + /// let mut map = HashMap::new(); + /// let value = "hoho"; /// - /// map.entry("poneyland").or_insert_with(|| s); + /// map.entry("poneyland").or_insert_with(|| value); /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// assert_eq!(map["poneyland"], "hoho"); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 6b89518e2..91a3776e7 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -944,7 +944,7 @@ fn test_raw_entry() { } } -mod test_drain_filter { +mod test_extract_if { use super::*; use crate::panic::{catch_unwind, AssertUnwindSafe}; @@ -968,7 +968,7 @@ mod test_drain_filter { #[test] fn empty() { let mut map: HashMap<i32, i32> = HashMap::new(); - map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop); assert!(map.is_empty()); } @@ -976,7 +976,7 @@ mod test_drain_filter { fn consuming_nothing() { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.collect(); - assert!(map.drain_filter(|_, _| false).eq_sorted(crate::iter::empty())); + assert!(map.extract_if(|_, _| false).eq_sorted(crate::iter::empty())); assert_eq!(map.len(), 3); } @@ -984,7 +984,7 @@ mod test_drain_filter { fn consuming_all() { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.clone().collect(); - assert!(map.drain_filter(|_, _| true).eq_sorted(pairs)); + assert!(map.extract_if(|_, _| true).eq_sorted(pairs)); assert!(map.is_empty()); } @@ -993,7 +993,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.collect(); assert!( - map.drain_filter(|_, v| { + map.extract_if(|_, v| { *v += 6; false }) @@ -1008,7 +1008,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.collect(); assert!( - map.drain_filter(|_, v| { + map.extract_if(|_, v| { *v += 6; true }) @@ -1034,14 +1034,15 @@ mod test_drain_filter { let mut map = (0..3).map(|i| (i, D)).collect::<HashMap<_, _>>(); catch_unwind(move || { - drop(map.drain_filter(|_, _| { + map.extract_if(|_, _| { PREDS.fetch_add(1, Ordering::SeqCst); true - })) + }) + .for_each(drop) }) .unwrap_err(); - assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(PREDS.load(Ordering::SeqCst), 2); assert_eq!(DROPS.load(Ordering::SeqCst), 3); } @@ -1060,10 +1061,11 @@ mod test_drain_filter { let mut map = (0..3).map(|i| (i, D)).collect::<HashMap<_, _>>(); catch_unwind(AssertUnwindSafe(|| { - drop(map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + map.extract_if(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { 0 => true, _ => panic!(), - })) + }) + .for_each(drop) })) .unwrap_err(); @@ -1088,7 +1090,7 @@ mod test_drain_filter { let mut map = (0..3).map(|i| (i, D)).collect::<HashMap<_, _>>(); { - let mut it = map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + let mut it = map.extract_if(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { 0 => true, _ => panic!(), }); diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index ac906e682..ec59634df 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -262,25 +262,24 @@ impl<T, S> HashSet<T, S> { /// If the closure returns false, the value will remain in the list and will not be yielded /// by the iterator. /// - /// If the iterator is only partially consumed or not consumed at all, each of the remaining - /// values will still be subjected to the closure and removed and dropped if it returns true. + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating + /// or the iteration short-circuits, then the remaining elements will be retained. + /// Use [`retain`] with a negated predicate if you do not need the returned iterator. /// - /// It is unspecified how many more values will be subjected to the closure - /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the - /// `DrainFilter` itself is leaked. + /// [`retain`]: HashSet::retain /// /// # Examples /// /// Splitting a set into even and odd values, reusing the original set: /// /// ``` - /// #![feature(hash_drain_filter)] + /// #![feature(hash_extract_if)] /// use std::collections::HashSet; /// /// let mut set: HashSet<i32> = (0..8).collect(); - /// let drained: HashSet<i32> = set.drain_filter(|v| v % 2 == 0).collect(); + /// let extracted: HashSet<i32> = set.extract_if(|v| v % 2 == 0).collect(); /// - /// let mut evens = drained.into_iter().collect::<Vec<_>>(); + /// let mut evens = extracted.into_iter().collect::<Vec<_>>(); /// let mut odds = set.into_iter().collect::<Vec<_>>(); /// evens.sort(); /// odds.sort(); @@ -290,12 +289,12 @@ impl<T, S> HashSet<T, S> { /// ``` #[inline] #[rustc_lint_query_instability] - #[unstable(feature = "hash_drain_filter", issue = "59618")] - pub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, T, F> + #[unstable(feature = "hash_extract_if", issue = "59618")] + pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, T, F> where F: FnMut(&T) -> bool, { - DrainFilter { base: self.base.drain_filter(pred) } + ExtractIf { base: self.base.extract_if(pred) } } /// Retains only the elements specified by the predicate. @@ -868,7 +867,9 @@ where /// Returns whether the value was newly inserted. That is: /// /// - If the set did not previously contain this value, `true` is returned. - /// - If the set already contained this value, `false` is returned. + /// - If the set already contained this value, `false` is returned, + /// and the set is not modified: original value is not replaced, + /// and the value passed as argument is dropped. /// /// # Examples /// @@ -1310,27 +1311,27 @@ pub struct Drain<'a, K: 'a> { /// A draining, filtering iterator over the items of a `HashSet`. /// -/// This `struct` is created by the [`drain_filter`] method on [`HashSet`]. +/// This `struct` is created by the [`extract_if`] method on [`HashSet`]. /// -/// [`drain_filter`]: HashSet::drain_filter +/// [`extract_if`]: HashSet::extract_if /// /// # Examples /// /// ``` -/// #![feature(hash_drain_filter)] +/// #![feature(hash_extract_if)] /// /// use std::collections::HashSet; /// /// let mut a = HashSet::from([1, 2, 3]); /// -/// let mut drain_filtered = a.drain_filter(|v| v % 2 == 0); +/// let mut extract_ifed = a.extract_if(|v| v % 2 == 0); /// ``` -#[unstable(feature = "hash_drain_filter", issue = "59618")] -pub struct DrainFilter<'a, K, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +pub struct ExtractIf<'a, K, F> where F: FnMut(&K) -> bool, { - base: base::DrainFilter<'a, K, F>, + base: base::ExtractIf<'a, K, F>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1576,8 +1577,8 @@ impl<K: fmt::Debug> fmt::Debug for Drain<'_, K> { } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<K, F> Iterator for DrainFilter<'_, K, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<K, F> Iterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool, { @@ -1593,16 +1594,16 @@ where } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<K, F> FusedIterator for DrainFilter<'_, K, F> where F: FnMut(&K) -> bool {} +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<K, F> FusedIterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool {} -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<'a, K, F> fmt::Debug for DrainFilter<'a, K, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<'a, K, F> fmt::Debug for ExtractIf<'a, K, F> where F: FnMut(&K) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DrainFilter").finish_non_exhaustive() + f.debug_struct("ExtractIf").finish_non_exhaustive() } } diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index 941a0450c..e0cd80b44 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -3,6 +3,7 @@ use super::HashSet; use crate::panic::{catch_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicU32, Ordering}; +use crate::sync::Arc; #[test] fn test_zero_capacities() { @@ -418,18 +419,18 @@ fn test_retain() { } #[test] -fn test_drain_filter() { +fn test_extract_if() { let mut x: HashSet<_> = [1].iter().copied().collect(); let mut y: HashSet<_> = [1].iter().copied().collect(); - x.drain_filter(|_| true); - y.drain_filter(|_| false); + x.extract_if(|_| true).for_each(drop); + y.extract_if(|_| false).for_each(drop); assert_eq!(x.len(), 0); assert_eq!(y.len(), 1); } #[test] -fn test_drain_filter_drop_panic_leak() { +fn test_extract_if_drop_panic_leak() { static PREDS: AtomicU32 = AtomicU32::new(0); static DROPS: AtomicU32 = AtomicU32::new(0); @@ -446,19 +447,20 @@ fn test_drain_filter_drop_panic_leak() { let mut set = (0..3).map(|i| D(i)).collect::<HashSet<_>>(); catch_unwind(move || { - drop(set.drain_filter(|_| { + set.extract_if(|_| { PREDS.fetch_add(1, Ordering::SeqCst); true - })) + }) + .for_each(drop) }) .ok(); - assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(PREDS.load(Ordering::SeqCst), 2); assert_eq!(DROPS.load(Ordering::SeqCst), 3); } #[test] -fn test_drain_filter_pred_panic_leak() { +fn test_extract_if_pred_panic_leak() { static PREDS: AtomicU32 = AtomicU32::new(0); static DROPS: AtomicU32 = AtomicU32::new(0); @@ -473,10 +475,11 @@ fn test_drain_filter_pred_panic_leak() { let mut set: HashSet<_> = (0..3).map(|_| D).collect(); catch_unwind(AssertUnwindSafe(|| { - drop(set.drain_filter(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { + set.extract_if(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { 0 => true, _ => panic!(), - })) + }) + .for_each(drop) })) .ok(); @@ -502,3 +505,22 @@ fn const_with_hasher() { const X: HashSet<(), ()> = HashSet::with_hasher(()); assert_eq!(X.len(), 0); } + +#[test] +fn test_insert_does_not_overwrite_the_value() { + let first_value = Arc::new(17); + let second_value = Arc::new(17); + + let mut set = HashSet::new(); + let inserted = set.insert(first_value.clone()); + assert!(inserted); + + let inserted = set.insert(second_value); + assert!(!inserted); + + assert!( + Arc::ptr_eq(set.iter().next().unwrap(), &first_value), + "Insert must not overwrite the value, so the contained value pointer \ + must be the same as first value pointer we inserted" + ); +} diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 408244b2c..bed90418b 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -528,7 +528,7 @@ impl f32 { /// The positive difference of two numbers. /// - /// * If `self <= other`: `0:0` + /// * If `self <= other`: `0.0` /// * Else: `self - other` /// /// # Examples diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 6782b861f..e72de05ca 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -530,7 +530,7 @@ impl f64 { /// The positive difference of two numbers. /// - /// * If `self <= other`: `0:0` + /// * If `self <= other`: `0.0` /// * Else: `self - other` /// /// # Examples diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index d987bf69b..3ddb87487 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -127,6 +127,14 @@ //! trait, which provides a [`from_wide`] method to convert a native Windows //! string (without the terminating nul character) to an [`OsString`]. //! +//! ## On all platforms +//! +//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of +//! UTF-8; see [`OsString`] for more details on its encoding on different platforms. +//! +//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_os_str_bytes`] and +//! [`OsStr::from_os_str_bytes_unchecked`]. +//! //! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value //! [Unicode code point]: https://www.unicode.org/glossary/#code_point //! [`env::set_var()`]: crate::env::set_var "env::set_var" diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 5c0541d3c..e7bad9d54 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -667,6 +667,51 @@ impl OsStr { s.as_ref() } + /// Converts a slice of bytes to an OS string slice without checking that the string contains + /// valid `OsStr`-encoded data. + /// + /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. + /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit + /// ASCII. + /// + /// See the [module's toplevel documentation about conversions][conversions] for safe, + /// cross-platform [conversions] from/to native representations. + /// + /// # Safety + /// + /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of + /// validated UTF-8 and bytes from [`OsStr::as_os_str_bytes`] from within the same rust version + /// built for the same target platform. For example, reconstructing an `OsStr` from bytes sent + /// over the network or stored in a file will likely violate these safety rules. + /// + /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_os_str_bytes`] can be + /// split either immediately before or immediately after any valid non-empty UTF-8 substring. + /// + /// # Example + /// + /// ``` + /// #![feature(os_str_bytes)] + /// + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("Mary had a little lamb"); + /// let bytes = os_str.as_os_str_bytes(); + /// let words = bytes.split(|b| *b == b' '); + /// let words: Vec<&OsStr> = words.map(|word| { + /// // SAFETY: + /// // - Each `word` only contains content that originated from `OsStr::as_os_str_bytes` + /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring + /// unsafe { OsStr::from_os_str_bytes_unchecked(word) } + /// }).collect(); + /// ``` + /// + /// [conversions]: super#conversions + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub unsafe fn from_os_str_bytes_unchecked(bytes: &[u8]) -> &Self { + Self::from_inner(Slice::from_os_str_bytes_unchecked(bytes)) + } + #[inline] fn from_inner(inner: &Slice) -> &OsStr { // SAFETY: OsStr is just a wrapper of Slice, @@ -700,7 +745,7 @@ impl OsStr { without modifying the original"] #[inline] pub fn to_str(&self) -> Option<&str> { - self.inner.to_str() + self.inner.to_str().ok() } /// Converts an `OsStr` to a <code>[Cow]<[str]></code>. @@ -837,13 +882,24 @@ impl OsStr { OsString { inner: Buf::from_box(boxed) } } - /// Gets the underlying byte representation. + /// Converts an OS string slice to a byte slice. To convert the byte slice back into an OS + /// string slice, use the [`OsStr::from_os_str_bytes_unchecked`] function. + /// + /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. + /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit + /// ASCII. + /// + /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should + /// be treated as opaque and only comparable within the same rust version built for the same + /// target platform. For example, sending the slice over the network or storing it in a file + /// will likely result in incompatible byte slices. See [`OsString`] for more encoding details + /// and [`std::ffi`] for platform-specific, specified conversions. /// - /// Note: it is *crucial* that this API is not externally public, to avoid - /// revealing the internal, platform-specific encodings. + /// [`std::ffi`]: crate::ffi #[inline] - pub(crate) fn bytes(&self) -> &[u8] { - unsafe { &*(&self.inner as *const _ as *const [u8]) } + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub fn as_os_str_bytes(&self) -> &[u8] { + self.inner.as_os_str_bytes() } /// Converts this string to its ASCII lower case equivalent in-place. @@ -1109,6 +1165,24 @@ impl<'a> From<Cow<'a, OsStr>> for OsString { } } +#[stable(feature = "str_tryfrom_osstr_impl", since = "1.72.0")] +impl<'a> TryFrom<&'a OsStr> for &'a str { + type Error = crate::str::Utf8Error; + + /// Tries to convert an `&OsStr` to a `&str`. + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("foo"); + /// let as_str = <&str>::try_from(os_str).unwrap(); + /// assert_eq!(as_str, "foo"); + /// ``` + fn try_from(value: &'a OsStr) -> Result<Self, Self::Error> { + value.inner.to_str() + } +} + #[stable(feature = "box_default_extra", since = "1.17.0")] impl Default for Box<OsStr> { #[inline] @@ -1131,7 +1205,7 @@ impl Default for &OsStr { impl PartialEq for OsStr { #[inline] fn eq(&self, other: &OsStr) -> bool { - self.bytes().eq(other.bytes()) + self.as_os_str_bytes().eq(other.as_os_str_bytes()) } } @@ -1158,23 +1232,23 @@ impl Eq for OsStr {} impl PartialOrd for OsStr { #[inline] fn partial_cmp(&self, other: &OsStr) -> Option<cmp::Ordering> { - self.bytes().partial_cmp(other.bytes()) + self.as_os_str_bytes().partial_cmp(other.as_os_str_bytes()) } #[inline] fn lt(&self, other: &OsStr) -> bool { - self.bytes().lt(other.bytes()) + self.as_os_str_bytes().lt(other.as_os_str_bytes()) } #[inline] fn le(&self, other: &OsStr) -> bool { - self.bytes().le(other.bytes()) + self.as_os_str_bytes().le(other.as_os_str_bytes()) } #[inline] fn gt(&self, other: &OsStr) -> bool { - self.bytes().gt(other.bytes()) + self.as_os_str_bytes().gt(other.as_os_str_bytes()) } #[inline] fn ge(&self, other: &OsStr) -> bool { - self.bytes().ge(other.bytes()) + self.as_os_str_bytes().ge(other.as_os_str_bytes()) } } @@ -1193,7 +1267,7 @@ impl PartialOrd<str> for OsStr { impl Ord for OsStr { #[inline] fn cmp(&self, other: &OsStr) -> cmp::Ordering { - self.bytes().cmp(other.bytes()) + self.as_os_str_bytes().cmp(other.as_os_str_bytes()) } } @@ -1243,7 +1317,7 @@ impl_cmp!(Cow<'a, OsStr>, OsString); impl Hash for OsStr { #[inline] fn hash<H: Hasher>(&self, state: &mut H) { - self.bytes().hash(state) + self.as_os_str_bytes().hash(state) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 89dfdfafd..c2d82169d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1755,8 +1755,14 @@ impl DirEntry { self.0.file_type().map(FileType) } - /// Returns the bare file name of this directory entry without any other - /// leading path component. + /// Returns the file name of this directory entry without any + /// leading path component(s). + /// + /// As an example, + /// the output of the function will result in "foo" for all the following paths: + /// - "./foo" + /// - "/the/foo" + /// - "../../foo" /// /// # Examples /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index e2480bcbb..9ff01b9c3 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1640,6 +1640,10 @@ fn test_file_times() { use crate::os::ios::fs::FileTimesExt; #[cfg(target_os = "macos")] use crate::os::macos::fs::FileTimesExt; + #[cfg(target_os = "tvos")] + use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "tvos")] + use crate::os::tvos::fs::FileTimesExt; #[cfg(target_os = "watchos")] use crate::os::watchos::fs::FileTimesExt; #[cfg(windows)] @@ -1651,9 +1655,21 @@ fn test_file_times() { let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); times = times.set_accessed(accessed).set_modified(modified); - #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + windows, + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + windows, + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] { times = times.set_created(created); } @@ -1678,7 +1694,13 @@ fn test_file_times() { let metadata = file.metadata().unwrap(); assert_eq!(metadata.accessed().unwrap(), accessed); assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + windows, + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] { assert_eq!(metadata.created().unwrap(), created); } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 4f339a18a..7097dfef8 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -47,13 +47,13 @@ use buffer::Buffer; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct BufReader<R> { - inner: R, +pub struct BufReader<R: ?Sized> { buf: Buffer, + inner: R, } impl<R: Read> BufReader<R> { - /// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KB, + /// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KiB, /// but may change in the future. /// /// # Examples @@ -95,7 +95,7 @@ impl<R: Read> BufReader<R> { } } -impl<R> BufReader<R> { +impl<R: ?Sized> BufReader<R> { /// Gets a reference to the underlying reader. /// /// It is inadvisable to directly read from the underlying reader. @@ -213,26 +213,29 @@ impl<R> BufReader<R> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> R { + pub fn into_inner(self) -> R + where + R: Sized, + { self.inner } /// Invalidates all data in the internal buffer. #[inline] - fn discard_buffer(&mut self) { + pub(in crate::io) fn discard_buffer(&mut self) { self.buf.discard_buffer() } } // This is only used by a test which asserts that the initialization-tracking is correct. #[cfg(test)] -impl<R> BufReader<R> { +impl<R: ?Sized> BufReader<R> { pub fn initialized(&self) -> usize { self.buf.initialized() } } -impl<R: Seek> BufReader<R> { +impl<R: ?Sized + Seek> BufReader<R> { /// Seeks relative to the current position. If the new position lies within the buffer, /// the buffer will not be flushed, allowing for more efficient seeks. /// This method does not return the location of the underlying reader, so the caller @@ -257,7 +260,7 @@ impl<R: Seek> BufReader<R> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<R: Read> Read for BufReader<R> { +impl<R: ?Sized + Read> Read for BufReader<R> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { // If we don't have any buffered data and we're doing a massive read // (larger than our internal buffer), bypass our internal buffer @@ -371,7 +374,7 @@ impl<R: Read> Read for BufReader<R> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<R: Read> BufRead for BufReader<R> { +impl<R: ?Sized + Read> BufRead for BufReader<R> { fn fill_buf(&mut self) -> io::Result<&[u8]> { self.buf.fill_buf(&mut self.inner) } @@ -384,11 +387,11 @@ impl<R: Read> BufRead for BufReader<R> { #[stable(feature = "rust1", since = "1.0.0")] impl<R> fmt::Debug for BufReader<R> where - R: fmt::Debug, + R: ?Sized + fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("BufReader") - .field("reader", &self.inner) + .field("reader", &&self.inner) .field( "buffer", &format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()), @@ -398,7 +401,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<R: Seek> Seek for BufReader<R> { +impl<R: ?Sized + Seek> Seek for BufReader<R> { /// Seek to an offset, in bytes, in the underlying reader. /// /// The position used for seeking with <code>[SeekFrom::Current]\(_)</code> is the @@ -491,7 +494,7 @@ impl<R: Seek> Seek for BufReader<R> { } } -impl<T> SizeHint for BufReader<T> { +impl<T: ?Sized> SizeHint for BufReader<T> { #[inline] fn lower_bound(&self) -> usize { SizeHint::lower_bound(self.get_ref()) + self.buffer().len() diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 14c455d4f..0f04f2911 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -67,8 +67,7 @@ use crate::ptr; /// [`TcpStream`]: crate::net::TcpStream /// [`flush`]: BufWriter::flush #[stable(feature = "rust1", since = "1.0.0")] -pub struct BufWriter<W: Write> { - inner: W, +pub struct BufWriter<W: ?Sized + Write> { // The buffer. Avoid using this like a normal `Vec` in common code paths. // That is, don't use `buf.push`, `buf.extend_from_slice`, or any other // methods that require bounds checking or the like. This makes an enormous @@ -78,10 +77,11 @@ pub struct BufWriter<W: Write> { // write the buffered data a second time in BufWriter's destructor. This // flag tells the Drop impl if it should skip the flush. panicked: bool, + inner: W, } impl<W: Write> BufWriter<W> { - /// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KB, + /// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KiB, /// but may change in the future. /// /// # Examples @@ -115,6 +115,69 @@ impl<W: Write> BufWriter<W> { BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false } } + /// Unwraps this `BufWriter<W>`, returning the underlying writer. + /// + /// The buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // unwrap the TcpStream and flush the buffer + /// let stream = buffer.into_inner().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + 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_parts().0), + } + } + + /// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but + /// unwritten data. + /// + /// If the underlying writer panicked, it is not known what portion of the data was written. + /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer + /// contents can still be recovered). + /// + /// `into_parts` makes no attempt to flush data and cannot fail. + /// + /// # Examples + /// + /// ``` + /// 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_parts(); + /// assert_eq!(recovered_writer.len(), 0); + /// assert_eq!(&buffered_data.unwrap(), b"ata"); + /// ``` + #[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 }) }; + + // SAFETY: forget(self) prevents double dropping inner + let inner = unsafe { ptr::read(&self.inner) }; + mem::forget(self); + + (inner, buf) + } +} + +impl<W: ?Sized + Write> BufWriter<W> { /// Send data in our local buffer into the inner writer, looping as /// necessary until either it's all been sent or an error occurs. /// @@ -284,67 +347,6 @@ impl<W: Write> BufWriter<W> { self.buf.capacity() } - /// Unwraps this `BufWriter<W>`, returning the underlying writer. - /// - /// The buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An [`Err`] will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // unwrap the TcpStream and flush the buffer - /// let stream = buffer.into_inner().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - 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_parts().0), - } - } - - /// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but - /// unwritten data. - /// - /// If the underlying writer panicked, it is not known what portion of the data was written. - /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer - /// contents can still be recovered). - /// - /// `into_parts` makes no attempt to flush data and cannot fail. - /// - /// # Examples - /// - /// ``` - /// 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_parts(); - /// assert_eq!(recovered_writer.len(), 0); - /// assert_eq!(&buffered_data.unwrap(), b"ata"); - /// ``` - #[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 }) }; - - // SAFETY: forget(self) prevents double dropping inner - let inner = unsafe { ptr::read(&self.inner) }; - mem::forget(self); - - (inner, buf) - } - // Ensure this function does not get inlined into `write`, so that it // remains inlineable and its common path remains as short as possible. // If this function ends up being called frequently relative to `write`, @@ -511,7 +513,7 @@ impl fmt::Debug for WriterPanicked { } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Write> Write for BufWriter<W> { +impl<W: ?Sized + Write> Write for BufWriter<W> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { // Use < instead of <= to avoid a needless trip through the buffer in some cases. @@ -640,20 +642,20 @@ impl<W: Write> Write for BufWriter<W> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Write> fmt::Debug for BufWriter<W> +impl<W: ?Sized + Write> fmt::Debug for BufWriter<W> where W: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("BufWriter") - .field("writer", &self.inner) + .field("writer", &&self.inner) .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) .finish() } } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Write + Seek> Seek for BufWriter<W> { +impl<W: ?Sized + Write + Seek> Seek for BufWriter<W> { /// Seek to the offset, in bytes, in the underlying writer. /// /// Seeking always writes out the internal buffer before seeking. @@ -664,7 +666,7 @@ impl<W: Write + Seek> Seek for BufWriter<W> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Write> Drop for BufWriter<W> { +impl<W: ?Sized + Write> Drop for BufWriter<W> { fn drop(&mut self) { if !self.panicked { // dtors should not panic, so we ignore a failed flush diff --git a/library/std/src/io/buffered/linewriter.rs b/library/std/src/io/buffered/linewriter.rs index a26a4ab33..3d4ae7041 100644 --- a/library/std/src/io/buffered/linewriter.rs +++ b/library/std/src/io/buffered/linewriter.rs @@ -64,7 +64,7 @@ use crate::io::{self, buffered::LineWriterShim, BufWriter, IntoInnerError, IoSli /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct LineWriter<W: Write> { +pub struct LineWriter<W: ?Sized + Write> { inner: BufWriter<W>, } @@ -109,27 +109,6 @@ impl<W: Write> LineWriter<W> { LineWriter { inner: BufWriter::with_capacity(capacity, inner) } } - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// - /// let reference = file.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.get_ref() - } - /// Gets a mutable reference to the underlying writer. /// /// Caution must be taken when calling methods on the mutable reference @@ -184,8 +163,31 @@ impl<W: Write> LineWriter<W> { } } +impl<W: ?Sized + Write> LineWriter<W> { + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// + /// let reference = file.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &W { + self.inner.get_ref() + } +} + #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Write> Write for LineWriter<W> { +impl<W: ?Sized + Write> Write for LineWriter<W> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { LineWriterShim::new(&mut self.inner).write(buf) } @@ -216,7 +218,7 @@ impl<W: Write> Write for LineWriter<W> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Write> fmt::Debug for LineWriter<W> +impl<W: ?Sized + Write> fmt::Debug for LineWriter<W> where W: fmt::Debug, { diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index 0175d2693..f2a55da05 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -11,11 +11,11 @@ use crate::sys_common::memchr; /// `BufWriters` to be temporarily given line-buffering logic; this is what /// enables Stdout to be alternately in line-buffered or block-buffered mode. #[derive(Debug)] -pub struct LineWriterShim<'a, W: Write> { +pub struct LineWriterShim<'a, W: ?Sized + Write> { buffer: &'a mut BufWriter<W>, } -impl<'a, W: Write> LineWriterShim<'a, W> { +impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { pub fn new(buffer: &'a mut BufWriter<W>) -> Self { Self { buffer } } @@ -49,7 +49,7 @@ impl<'a, W: Write> LineWriterShim<'a, W> { } } -impl<'a, W: Write> Write for LineWriterShim<'a, W> { +impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { /// Write some data into this BufReader with line buffering. This means /// that, if any newlines are present in the data, the data up to the last /// newline is sent directly to the underlying writer, and data after it diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 1d9d93f5b..ef1f4031e 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,6 +1,9 @@ -use super::{BorrowedBuf, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; +use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; use crate::mem::MaybeUninit; +#[cfg(test)] +mod tests; + /// Copies the entire contents of a reader into a writer. /// /// This function will continuously read data from `reader` and then @@ -71,32 +74,113 @@ where R: Read, W: Write, { - BufferedCopySpec::copy_to(reader, writer) + let read_buf = BufferedReaderSpec::buffer_size(reader); + let write_buf = BufferedWriterSpec::buffer_size(writer); + + if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf { + return BufferedReaderSpec::copy_to(reader, writer); + } + + BufferedWriterSpec::copy_from(writer, reader) +} + +/// Specialization of the read-write loop that reuses the internal +/// buffer of a BufReader. If there's no buffer then the writer side +/// should be used intead. +trait BufferedReaderSpec { + fn buffer_size(&self) -> usize; + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64>; +} + +impl<T> BufferedReaderSpec for T +where + Self: Read, + T: ?Sized, +{ + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> { + unimplemented!("only called from specializations"); + } +} + +impl<I> BufferedReaderSpec for BufReader<I> +where + Self: Read, + I: ?Sized, +{ + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> { + let mut len = 0; + + loop { + // Hack: this relies on `impl Read for BufReader` always calling fill_buf + // if the buffer is empty, even for empty slices. + // It can't be called directly here since specialization prevents us + // from adding I: Read + match self.read(&mut []) { + Ok(_) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + let buf = self.buffer(); + if self.buffer().len() == 0 { + return Ok(len); + } + + // In case the writer side is a BufWriter then its write_all + // implements an optimization that passes through large + // buffers to the underlying writer. That code path is #[cold] + // but we're still avoiding redundant memcopies when doing + // a copy between buffered inputs and outputs. + to.write_all(buf)?; + len += buf.len() as u64; + self.discard_buffer(); + } + } } /// Specialization of the read-write loop that either uses a stack buffer /// or reuses the internal buffer of a BufWriter -trait BufferedCopySpec: Write { - fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64>; +trait BufferedWriterSpec: Write { + fn buffer_size(&self) -> usize; + + fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64>; } -impl<W: Write + ?Sized> BufferedCopySpec for W { - default fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> { - stack_buffer_copy(reader, writer) +impl<W: Write + ?Sized> BufferedWriterSpec for W { + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> { + stack_buffer_copy(reader, self) } } -impl<I: Write> BufferedCopySpec for BufWriter<I> { - fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> { - if writer.capacity() < DEFAULT_BUF_SIZE { - return stack_buffer_copy(reader, writer); +impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> { + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> { + if self.capacity() < DEFAULT_BUF_SIZE { + return stack_buffer_copy(reader, self); } let mut len = 0; let mut init = 0; loop { - let buf = writer.buffer_mut(); + let buf = self.buffer_mut(); let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into(); unsafe { @@ -127,7 +211,7 @@ impl<I: Write> BufferedCopySpec for BufWriter<I> { Err(e) => return Err(e), } } else { - writer.flush_buf()?; + self.flush_buf()?; init = 0; } } diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs new file mode 100644 index 000000000..8c816af15 --- /dev/null +++ b/library/std/src/io/copy/tests.rs @@ -0,0 +1,108 @@ +use crate::cmp::{max, min}; +use crate::io::*; + +#[test] +fn copy_copies() { + let mut r = repeat(0).take(4); + let mut w = sink(); + assert_eq!(copy(&mut r, &mut w).unwrap(), 4); + + let mut r = repeat(0).take(1 << 17); + assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); +} + +struct ShortReader { + cap: usize, + read_size: usize, + observed_buffer: usize, +} + +impl Read for ShortReader { + fn read(&mut self, buf: &mut [u8]) -> Result<usize> { + let bytes = min(self.cap, self.read_size); + self.cap -= bytes; + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(bytes) + } +} + +struct WriteObserver { + observed_buffer: usize, +} + +impl Write for WriteObserver { + fn write(&mut self, buf: &[u8]) -> Result<usize> { + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[test] +fn copy_specializes_bufwriter() { + let cap = 117 * 1024; + let buf_sz = 16 * 1024; + let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; + let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); + assert_eq!( + copy(&mut r, &mut w).unwrap(), + cap as u64, + "expected the whole capacity to be copied" + ); + assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); + assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); +} + +#[test] +fn copy_specializes_bufreader() { + let mut source = vec![0; 768 * 1024]; + source[1] = 42; + let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source)); + + let mut sink = Vec::new(); + assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!(source.as_slice(), sink.as_slice()); + + let buf_sz = 71 * 1024; + assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition"); + + let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source)); + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!( + sink.observed_buffer, buf_sz, + "expected a large buffer to be provided to the writer" + ); +} + +#[cfg(unix)] +mod io_benches { + use crate::fs::File; + use crate::fs::OpenOptions; + use crate::io::prelude::*; + use crate::io::BufReader; + + use test::Bencher; + + #[bench] + fn bench_copy_buf_reader(b: &mut Bencher) { + let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); + // use dyn to avoid specializations unrelated to readbuf + let dyn_in = &mut file_in as &mut dyn Read; + let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0)); + let mut writer = + OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed"); + + const BYTES: u64 = 1024 * 1024; + + b.bytes = BYTES; + + b.iter(|| { + reader.get_mut().set_limit(BYTES); + crate::io::copy(&mut reader, &mut writer).unwrap() + }); + } +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 8a007d095..71d91f213 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1416,17 +1416,18 @@ pub trait Write { /// /// This function will attempt to write the entire contents of `buf`, but /// 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 + /// error. Typically, a call to `write` represents one attempt to write to /// any wrapped object. /// /// Calls to `write` are not guaranteed to block waiting for data to be /// written, and a write which would otherwise block can be indicated through /// an [`Err`] variant. /// - /// If the return value is [`Ok(n)`] then it must be guaranteed that - /// `n <= buf.len()`. A return value of `0` typically means that the - /// underlying object is no longer able to accept bytes and will likely not - /// be able to in the future as well, or that the buffer provided is empty. + /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. + /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. /// /// # Errors /// @@ -2754,7 +2755,7 @@ trait SizeHint { } } -impl<T> SizeHint for T { +impl<T: ?Sized> SizeHint for T { #[inline] default fn lower_bound(&self) -> usize { 0 diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index ce5e2c9da..1baa94e64 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,68 +1,9 @@ -use crate::cmp::{max, min}; use crate::io::prelude::*; -use crate::io::{ - copy, empty, repeat, sink, BorrowedBuf, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, - DEFAULT_BUF_SIZE, -}; +use crate::io::{empty, repeat, sink, BorrowedBuf, Empty, Repeat, SeekFrom, Sink}; use crate::mem::MaybeUninit; #[test] -fn copy_copies() { - let mut r = repeat(0).take(4); - let mut w = sink(); - assert_eq!(copy(&mut r, &mut w).unwrap(), 4); - - let mut r = repeat(0).take(1 << 17); - assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); -} - -struct ShortReader { - cap: usize, - read_size: usize, - observed_buffer: usize, -} - -impl Read for ShortReader { - fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - let bytes = min(self.cap, self.read_size); - self.cap -= bytes; - self.observed_buffer = max(self.observed_buffer, buf.len()); - Ok(bytes) - } -} - -struct WriteObserver { - observed_buffer: usize, -} - -impl Write for WriteObserver { - fn write(&mut self, buf: &[u8]) -> Result<usize> { - self.observed_buffer = max(self.observed_buffer, buf.len()); - Ok(buf.len()) - } - - fn flush(&mut self) -> Result<()> { - Ok(()) - } -} - -#[test] -fn copy_specializes_bufwriter() { - let cap = 117 * 1024; - let buf_sz = 16 * 1024; - let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; - let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); - assert_eq!( - copy(&mut r, &mut w).unwrap(), - cap as u64, - "expected the whole capacity to be copied" - ); - assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); - assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); -} - -#[test] fn sink_sinks() { let mut s = sink(); assert_eq!(s.write(&[]).unwrap(), 0); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 318a46d1b..72b9ad348 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -188,6 +188,13 @@ //! [array]: prim@array //! [slice]: prim@slice +// To run std tests without x.py without ending up with two copies of std, Miri needs to be +// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>. +// rustc itself never sets the feature, so this line has no affect there. +#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] +// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. +#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] +// #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] #![doc( @@ -202,12 +209,6 @@ no_global_oom_handling, not(no_global_oom_handling) ))] -// To run std tests without x.py without ending up with two copies of std, Miri needs to be -// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>. -// rustc itself never sets the feature, so this line has no affect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] -// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. -#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index fcc5cfafd..ba1b8cbfa 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -154,7 +154,7 @@ macro_rules! println { /// /// Panics if writing to `io::stderr` fails. /// -/// Writing to non-blocking stdout can cause an error, which will lead +/// Writing to non-blocking stderr can cause an error, which will lead /// this macro to panic. /// /// # Examples @@ -189,7 +189,7 @@ macro_rules! eprint { /// /// Panics if writing to `io::stderr` fails. /// -/// Writing to non-blocking stdout can cause an error, which will lead +/// Writing to non-blocking stderr can cause an error, which will lead /// this macro to panic. /// /// # Examples diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs index dfc6dabbe..6a065cfba 100644 --- a/library/std/src/net/socket_addr/tests.rs +++ b/library/std/src/net/socket_addr/tests.rs @@ -85,7 +85,7 @@ fn ipv6_socket_addr_to_string() { // IPv4-compatible address. assert_eq!( SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(), - "[::192.0.2.128]:8080" + "[::c000:280]:8080" ); // IPv6 address with no zero segments. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 7a3c66e45..db367cfa0 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -47,6 +47,17 @@ fn connect_error() { } #[test] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +fn connect_timeout_error() { + let socket_addr = next_test_ip4(); + let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX); + assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut)); + + let _listener = TcpListener::bind(&socket_addr).unwrap(); + assert!(TcpStream::connect_timeout(&socket_addr, Duration::MAX).is_ok()); +} + +#[test] fn listen_localhost() { let socket_addr = next_test_ip4(); let listener = t!(TcpListener::bind(&socket_addr)); diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs index 6d4d54b7c..b319527a5 100644 --- a/library/std/src/os/ios/fs.rs +++ b/library/std/src/os/ios/fs.rs @@ -6,7 +6,7 @@ use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; #[allow(deprecated)] -use crate::os::ios::raw; +use super::raw; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 5b54cc5f2..634c3cc4a 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -137,6 +137,9 @@ pub mod redox; pub mod solaris; #[cfg(target_os = "solid_asp3")] pub mod solid; +#[cfg(target_os = "tvos")] +#[path = "ios/mod.rs"] +pub(crate) mod tvos; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 6fe111118..401ec1e7a 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -73,6 +73,8 @@ mod platform { pub use crate::os::redox::*; #[cfg(target_os = "solaris")] pub use crate::os::solaris::*; + #[cfg(target_os = "tvos")] + pub use crate::os::tvos::*; #[cfg(target_os = "vita")] pub use crate::os::vita::*; #[cfg(target_os = "vxworks")] @@ -96,6 +98,7 @@ pub mod thread; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "macos", target_os = "netbsd", diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 7565fbc0d..218536689 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -11,12 +11,19 @@ use crate::slice::from_raw_parts; use crate::sys::net::Socket; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? -#[cfg(all(doc, not(target_os = "linux"), not(target_os = "android"), not(target_os = "netbsd")))] +#[cfg(all( + doc, + not(target_os = "linux"), + not(target_os = "android"), + not(target_os = "netbsd"), + not(target_os = "freebsd") +))] #[allow(non_camel_case_types)] mod libc { pub use libc::c_int; pub struct ucred; pub struct cmsghdr; + pub struct sockcred2; pub type pid_t = i32; pub type gid_t = u32; pub type uid_t = u32; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index bf2a51b5e..e20170873 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -11,6 +11,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Owned target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "netbsd", @@ -30,6 +31,7 @@ use crate::time::Duration; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "netbsd", @@ -238,6 +240,7 @@ impl UnixStream { target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "netbsd", diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 729c63d18..2b40b672d 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -15,7 +15,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use cfg_if::cfg_if; cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { type UserId = u16; type GroupId = u16; } else if #[cfg(target_os = "nto")] { diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs index 95967eac2..6a0cc2d2c 100644 --- a/library/std/src/os/unix/ucred.rs +++ b/library/std/src/os/unix/ucred.rs @@ -36,7 +36,7 @@ pub use self::impl_linux::peer_cred; ))] pub use self::impl_bsd::peer_cred; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub use self::impl_mac::peer_cred; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -98,7 +98,7 @@ pub mod impl_bsd { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub mod impl_mac { use super::UCred; use crate::os::unix::io::AsRawFd; diff --git a/library/std/src/os/unix/ucred/tests.rs b/library/std/src/os/unix/ucred/tests.rs index e63a2fc24..dd99ecdd8 100644 --- a/library/std/src/os/unix/ucred/tests.rs +++ b/library/std/src/os/unix/ucred/tests.rs @@ -8,6 +8,7 @@ use libc::{getegid, geteuid, getpid}; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "openbsd" @@ -26,7 +27,13 @@ fn test_socket_pair() { } #[test] -#[cfg(any(target_os = "linux", target_os = "ios", target_os = "macos", target_os = "watchos"))] +#[cfg(any( + target_os = "linux", + target_os = "ios", + target_os = "macos", + target_os = "watchos", + target_os = "tvos", +))] fn test_socket_pair_pids(arg: Type) -> RetType { // Create two connected sockets and get their peer credentials. let (sock_a, sock_b) = UnixStream::pair().unwrap(); diff --git a/library/std/src/path.rs b/library/std/src/path.rs index febdeb514..28cd3c4e4 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -193,7 +193,7 @@ impl<'a> Prefix<'a> { fn len(&self) -> usize { use self::Prefix::*; fn os_str_len(s: &OsStr) -> usize { - s.bytes().len() + s.as_os_str_bytes().len() } match *self { Verbatim(x) => 4 + os_str_len(x), @@ -299,20 +299,6 @@ where } } -unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { - // SAFETY: See note at the top of this module to understand why this and - // `OsStr::bytes` are used: - // - // This casts are safe as OsStr is internally a wrapper around [u8] on all - // platforms. - // - // Note that currently this relies on the special knowledge that std has; - // these types are single-element structs but are not marked - // repr(transparent) or repr(C) which would make these casts not allowable - // outside std. - unsafe { &*(s as *const [u8] as *const OsStr) } -} - // Detect scheme on Redox fn has_redox_scheme(s: &[u8]) -> bool { cfg!(target_os = "redox") && s.contains(&b':') @@ -330,7 +316,7 @@ fn has_physical_root(s: &[u8], prefix: Option<Prefix<'_>>) -> bool { // basic workhorse for splitting stem and extension fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { - if file.bytes() == b".." { + if file.as_os_str_bytes() == b".." { return (Some(file), None); } @@ -338,18 +324,23 @@ fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { // 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 mut iter = file.bytes().rsplitn(2, |b| *b == b'.'); + let mut iter = file.as_os_str_bytes().rsplitn(2, |b| *b == b'.'); let after = iter.next(); let before = iter.next(); if before == Some(b"") { (Some(file), None) } else { - unsafe { (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) } + unsafe { + ( + before.map(|s| OsStr::from_os_str_bytes_unchecked(s)), + after.map(|s| OsStr::from_os_str_bytes_unchecked(s)), + ) + } } } fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { - let slice = file.bytes(); + let slice = file.as_os_str_bytes(); if slice == b".." { return (file, None); } @@ -364,7 +355,12 @@ fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { }; let before = &slice[..i]; let after = &slice[i + 1..]; - unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) } + unsafe { + ( + OsStr::from_os_str_bytes_unchecked(before), + Some(OsStr::from_os_str_bytes_unchecked(after)), + ) + } } //////////////////////////////////////////////////////////////////////////////// @@ -743,7 +739,7 @@ impl<'a> Components<'a> { // separately via `include_cur_dir` b".." => Some(Component::ParentDir), b"" => None, - _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })), + _ => Some(Component::Normal(unsafe { OsStr::from_os_str_bytes_unchecked(comp) })), } } @@ -900,7 +896,7 @@ impl<'a> Iterator for Components<'a> { let raw = &self.path[..self.prefix_len()]; self.path = &self.path[self.prefix_len()..]; return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(raw) }, + raw: unsafe { OsStr::from_os_str_bytes_unchecked(raw) }, parsed: self.prefix.unwrap(), })); } @@ -972,7 +968,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { State::Prefix if self.prefix_len() > 0 => { self.back = State::Done; return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(self.path) }, + raw: unsafe { OsStr::from_os_str_bytes_unchecked(self.path) }, parsed: self.prefix.unwrap(), })); } @@ -1481,17 +1477,17 @@ impl PathBuf { fn _set_extension(&mut self, extension: &OsStr) -> bool { let file_stem = match self.file_stem() { None => return false, - Some(f) => f.bytes(), + Some(f) => f.as_os_str_bytes(), }; // truncate until right after the file stem let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr(); - let start = self.inner.bytes().as_ptr().addr(); + let start = self.inner.as_os_str_bytes().as_ptr().addr(); let v = self.as_mut_vec(); v.truncate(end_file_stem.wrapping_sub(start)); // add the new extension, if any - let new = extension.bytes(); + let new = extension.as_os_str_bytes(); if !new.is_empty() { v.reserve_exact(new.len() + 1); v.push(b'.'); @@ -2011,11 +2007,11 @@ impl Path { // The following (private!) function allows construction of a path from a u8 // slice, which is only safe when it is known to follow the OsStr encoding. unsafe fn from_u8_slice(s: &[u8]) -> &Path { - unsafe { Path::new(u8_slice_as_os_str(s)) } + unsafe { Path::new(OsStr::from_os_str_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. fn as_u8_slice(&self) -> &[u8] { - self.inner.bytes() + self.inner.as_os_str_bytes() } /// Directly wraps a string slice as a `Path` slice. diff --git a/library/std/src/personality.rs b/library/std/src/personality.rs index 63f0ad4f1..386a399f5 100644 --- a/library/std/src/personality.rs +++ b/library/std/src/personality.rs @@ -29,7 +29,7 @@ cfg_if::cfg_if! { all(target_family = "windows", target_env = "gnu"), target_os = "psp", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs index 82edb11cb..6552d96ca 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/personality/gcc.rs @@ -85,7 +85,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c cfg_if::cfg_if! { - if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "watchos"), not(target_os = "netbsd")))] { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { // ARM EHABI personality routine. // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf // diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 8266e8990..80289ca08 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -308,7 +308,7 @@ mod prim_never {} /// /// ```no_run /// // Undefined behaviour -/// unsafe { char::from_u32_unchecked(0x110000) }; +/// let _ = unsafe { char::from_u32_unchecked(0x110000) }; /// ``` /// /// USVs are also the exact set of values that may be encoded in UTF-8. Because diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 9da74a5dd..8f3201b00 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1904,8 +1904,8 @@ impl FromInner<imp::ExitCode> for ExitCode { } impl Child { - /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] - /// error is returned. + /// Forces the child process to exit. If the child has already exited, `Ok(())` + /// is returned. /// /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function. /// @@ -1920,7 +1920,7 @@ impl Child { /// /// let mut command = Command::new("yes"); /// if let Ok(mut child) = command.spawn() { - /// child.kill().expect("command wasn't running"); + /// child.kill().expect("command couldn't be killed"); /// } else { /// println!("yes command didn't start"); /// } diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index d7f4d335d..366b59146 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -582,3 +582,18 @@ fn run_canonical_bat_script() { assert!(output.status.success()); assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!"); } + +#[test] +fn terminate_exited_process() { + let mut cmd = if cfg!(target_os = "android") { + let mut p = shell_cmd(); + p.args(&["-c", "true"]); + p + } else { + known_command() + }; + let mut p = cmd.stdout(Stdio::null()).spawn().unwrap(); + p.wait().unwrap(); + assert!(p.kill().is_ok()); + assert!(p.kill().is_ok()); +} diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 11836b7b6..e39254aa4 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -13,9 +13,10 @@ use crate::sync::{Condvar, Mutex}; /// use std::sync::{Arc, Barrier}; /// use std::thread; /// -/// let mut handles = Vec::with_capacity(10); -/// let barrier = Arc::new(Barrier::new(10)); -/// for _ in 0..10 { +/// let n = 10; +/// let mut handles = Vec::with_capacity(n); +/// let barrier = Arc::new(Barrier::new(n)); +/// for _ in 0..n { /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. @@ -105,9 +106,10 @@ impl Barrier { /// use std::sync::{Arc, Barrier}; /// use std::thread; /// - /// let mut handles = Vec::with_capacity(10); - /// let barrier = Arc::new(Barrier::new(10)); - /// for _ in 0..10 { + /// let n = 10; + /// let mut handles = Vec::with_capacity(n); + /// let barrier = Arc::new(Barrier::new(n)); + /// for _ in 0..n { /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index 4912ca4f8..9aab1b941 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -66,26 +66,32 @@ impl Waker { /// Attempts to find another thread's entry, select the operation, and wake it up. #[inline] pub(crate) fn try_select(&mut self) -> Option<Entry> { - self.selectors - .iter() - .position(|selector| { - // Does the entry belong to a different thread? - selector.cx.thread_id() != current_thread_id() - && selector // Try selecting this operation. - .cx - .try_select(Selected::Operation(selector.oper)) - .is_ok() - && { - // Provide the packet. - selector.cx.store_packet(selector.packet); - // Wake the thread up. - selector.cx.unpark(); - true - } - }) - // Remove the entry from the queue to keep it clean and improve - // performance. - .map(|pos| self.selectors.remove(pos)) + if self.selectors.is_empty() { + None + } else { + let thread_id = current_thread_id(); + + self.selectors + .iter() + .position(|selector| { + // Does the entry belong to a different thread? + selector.cx.thread_id() != thread_id + && selector // Try selecting this operation. + .cx + .try_select(Selected::Operation(selector.oper)) + .is_ok() + && { + // Provide the packet. + selector.cx.store_packet(selector.packet); + // Wake the thread up. + selector.cx.unpark(); + true + } + }) + // Remove the entry from the queue to keep it clean and improve + // performance. + .map(|pos| self.selectors.remove(pos)) + } } /// Notifies all operations waiting to be ready. diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index 0e0c87d1c..c00134c8b 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -347,8 +347,8 @@ pub struct Sender<T> { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: Send> Send for Sender<T> {} -#[stable(feature = "rust1", since = "1.0.0")] -impl<T> !Sync for Sender<T> {} +#[stable(feature = "mpsc_sender_sync", since = "1.72.0")] +unsafe impl<T: Send> Sync for Sender<T> {} /// The sending-half of Rust's synchronous [`sync_channel`] type. /// diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 1b17c3108..8c46080e4 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -91,7 +91,7 @@ impl Once { /// return). /// /// If the given closure recursively invokes `call_once` on the same [`Once`] - /// instance the exact behavior is not specified, allowed outcomes are + /// instance, the exact behavior is not specified: allowed outcomes are /// a panic or a deadlock. /// /// # Examples diff --git a/library/std/src/sys/common/small_c_string.rs b/library/std/src/sys/common/small_c_string.rs index 01acd5191..963d17a47 100644 --- a/library/std/src/sys/common/small_c_string.rs +++ b/library/std/src/sys/common/small_c_string.rs @@ -19,7 +19,7 @@ pub fn run_path_with_cstr<T, F>(path: &Path, f: F) -> io::Result<T> where F: FnOnce(&CStr) -> io::Result<T>, { - run_with_cstr(path.as_os_str().bytes(), f) + run_with_cstr(path.as_os_str().as_os_str_bytes(), f) } #[inline] diff --git a/library/std/src/sys/common/tests.rs b/library/std/src/sys/common/tests.rs index fb6f5d6af..0a1cbcbe8 100644 --- a/library/std/src/sys/common/tests.rs +++ b/library/std/src/sys/common/tests.rs @@ -8,7 +8,7 @@ use core::iter::repeat; fn stack_allocation_works() { let path = Path::new("abc"); let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); + assert_eq!(p, &*CString::new(path.as_os_str().as_os_str_bytes()).unwrap()); Ok(42) }); assert_eq!(result.unwrap(), 42); @@ -25,7 +25,7 @@ fn heap_allocation_works() { let path = repeat("a").take(384).collect::<String>(); let path = Path::new(&path); let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); + assert_eq!(p, &*CString::new(path.as_os_str().as_os_str_bytes()).unwrap()); Ok(42) }); assert_eq!(result.unwrap(), 42); diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index 447044a79..bc5da1a18 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -33,20 +33,21 @@ pub macro thread_local_inner { // 1 == dtor registered, dtor not run // 2 == dtor registered and is running or has run #[thread_local] - static mut STATE: $crate::primitive::u8 = 0; + static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); + // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires + // all that comes with it. unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { - let ptr = ptr as *mut $t; - - unsafe { - $crate::debug_assert_eq!(STATE, 1); - STATE = 2; - $crate::ptr::drop_in_place(ptr); - } + $crate::thread::local_impl::abort_on_dtor_unwind(|| { + let old_state = STATE.replace(2); + $crate::debug_assert_eq!(old_state, 1); + // Safety: safety requirement is passed on to caller. + unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } + }); } unsafe { - match STATE { + match STATE.get() { // 0 == we haven't registered a destructor, so do // so now. 0 => { @@ -54,7 +55,7 @@ pub macro thread_local_inner { $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, destroy, ); - STATE = 1; + STATE.set(1); $crate::option::Option::Some(&VAL) } // 1 == the destructor is registered and the value @@ -148,7 +149,6 @@ impl<T> fmt::Debug for Key<T> { f.debug_struct("Key").finish_non_exhaustive() } } - impl<T> Key<T> { pub const fn new() -> Key<T> { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 77f645883..975509bd4 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -101,3 +101,24 @@ mod lazy { } } } + +/// Run a callback in a scenario which must not unwind (such as a `extern "C" +/// fn` declared in a user crate). If the callback unwinds anyway, then +/// `rtabort` with a message about thread local panicking on drop. +#[inline] +pub fn abort_on_dtor_unwind(f: impl FnOnce()) { + // Using a guard like this is lower cost. + let guard = DtorUnwindGuard; + f(); + core::mem::forget(guard); + + struct DtorUnwindGuard; + impl Drop for DtorUnwindGuard { + #[inline] + fn drop(&mut self) { + // This is not terribly descriptive, but it doesn't need to be as we'll + // already have printed a panic message at this point. + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 9ed4d9c1e..eafd6821f 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -168,7 +168,7 @@ mod imp { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] mod imp { use super::Args; use crate::ffi::CStr; @@ -209,7 +209,7 @@ mod imp { // for i in (0..[args count]) // res.push([args objectAtIndex:i]) // res - #[cfg(any(target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn args() -> Args { use crate::ffi::OsString; use crate::mem; diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 8c3ef88d8..929e9dae7 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -31,6 +31,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "tvos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "tvos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "watchos")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index cb630eede..85e020ae4 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -44,6 +44,7 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "netbsd", target_os = "openbsd", @@ -69,6 +70,7 @@ const fn max_iov() -> usize { target_os = "emscripten", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -181,6 +183,7 @@ impl FileDesc { target_os = "fuchsia", target_os = "illumos", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -222,6 +225,7 @@ impl FileDesc { #[cfg(any( all(target_os = "android", target_pointer_width = "32"), target_os = "ios", + target_os = "tvos", target_os = "macos", ))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { @@ -320,6 +324,7 @@ impl FileDesc { target_os = "fuchsia", target_os = "illumos", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -361,6 +366,7 @@ impl FileDesc { #[cfg(any( all(target_os = "android", target_pointer_width = "32"), target_os = "ios", + target_os = "tvos", target_os = "macos", ))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { @@ -402,7 +408,10 @@ impl FileDesc { } } #[cfg(any( - all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))), + all( + target_env = "newlib", + not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")) + ), target_os = "solaris", target_os = "illumos", target_os = "emscripten", @@ -424,10 +433,10 @@ impl FileDesc { Ok(()) } } - #[cfg(any(target_os = "espidf", target_os = "horizon"))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to, - // because neither supports spawning processes. + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // because none of them supports spawning processes. Ok(()) } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 09e9ae272..fbc7f04ce 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -15,6 +15,7 @@ use crate::mem; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -31,6 +32,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; all(target_os = "linux", target_env = "gnu"), target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", ))] use crate::sys::weak::syscall; @@ -42,6 +44,7 @@ use libc::{c_int, mode_t}; #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "solaris", all(target_os = "linux", target_env = "gnu") @@ -58,6 +61,7 @@ use libc::fstatat64; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use libc::readdir as readdir64; #[cfg(target_os = "linux")] @@ -74,6 +78,7 @@ use libc::readdir64_r; target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -283,6 +288,7 @@ unsafe impl Sync for Dir {} target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita" ))] pub struct DirEntry { dir: Arc<InnerReadDir>, @@ -304,10 +310,16 @@ pub struct DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] struct dirent64_min { d_ino: u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + target_os = "vita" + )))] d_type: u8, } @@ -319,6 +331,7 @@ struct dirent64_min { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] pub struct DirEntry { dir: Arc<InnerReadDir>, @@ -349,7 +362,7 @@ pub struct FilePermissions { pub struct FileTimes { accessed: Option<SystemTime>, modified: Option<SystemTime>, - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] created: Option<SystemTime>, } @@ -508,6 +521,7 @@ impl FileAttr { target_os = "openbsd", target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", ))] pub fn created(&self) -> io::Result<SystemTime> { @@ -519,7 +533,9 @@ impl FileAttr { target_os = "openbsd", target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", + target_os = "vita", )))] pub fn created(&self) -> io::Result<SystemTime> { cfg_has_statx! { @@ -541,6 +557,11 @@ impl FileAttr { currently", )) } + + #[cfg(target_os = "vita")] + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + } } #[cfg(target_os = "nto")] @@ -594,7 +615,7 @@ impl FileTimes { self.modified = Some(t); } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] pub fn set_created(&mut self, t: SystemTime) { self.created = Some(t); } @@ -645,6 +666,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { @@ -725,6 +747,7 @@ impl Iterator for ReadDir { continue; } + #[cfg(not(target_os = "vita"))] let entry = dirent64_min { d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, #[cfg(not(any( @@ -735,6 +758,9 @@ impl Iterator for ReadDir { d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; + #[cfg(target_os = "vita")] + let entry = dirent64_min { d_ino: 0u64 }; + return Some(Ok(DirEntry { entry, name: name.to_owned(), @@ -752,6 +778,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", )))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { @@ -842,6 +869,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", ))] pub fn file_type(&self) -> io::Result<FileType> { self.metadata().map(|m| m.file_type()) @@ -853,6 +881,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", )))] pub fn file_type(&self) -> io::Result<FileType> { match self.entry.d_type { @@ -870,6 +899,7 @@ impl DirEntry { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "linux", target_os = "emscripten", @@ -903,6 +933,7 @@ impl DirEntry { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "netbsd", target_os = "openbsd", @@ -921,6 +952,7 @@ impl DirEntry { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "netbsd", target_os = "openbsd", @@ -939,6 +971,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -951,6 +984,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1080,11 +1114,21 @@ impl File { cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + ))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))] + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + )))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) } @@ -1094,7 +1138,12 @@ impl File { cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } @@ -1113,6 +1162,7 @@ impl File { target_os = "android", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -1222,7 +1272,7 @@ impl File { io::ErrorKind::Unsupported, "setting file times not supported", )) - } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] { + } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] { let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3]; let mut num_times = 0; let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; @@ -1543,7 +1593,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { run_path_with_cstr(original, |original| { run_path_with_cstr(link, |link| { cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { // 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. @@ -1666,6 +1716,8 @@ fn open_to_and_set_permissions( .truncate(true) .open(to)?; let writer_metadata = writer.metadata()?; + // fchmod is broken on vita + #[cfg(not(target_os = "vita"))] if writer_metadata.is_file() { // Set the correct file permissions, in case the file already existed. // Don't set the permissions on already existing non-files like @@ -1680,6 +1732,7 @@ fn open_to_and_set_permissions( target_os = "android", target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", )))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { @@ -1707,7 +1760,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { use crate::sync::atomic::{AtomicBool, Ordering}; @@ -1844,11 +1897,12 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX, ESP-ID, Horizon, and Miri +// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri #[cfg(any( target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri ))] @@ -1861,6 +1915,7 @@ mod remove_dir_impl { target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri )))] diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 16c8e0c0e..7d49bbdcb 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -466,7 +466,7 @@ impl<T: CopyRead> CopyRead for Take<T> { } } -impl<T: CopyRead> CopyRead for BufReader<T> { +impl<T: ?Sized + CopyRead> CopyRead for BufReader<T> { fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> { let buf = self.buffer(); let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))]; @@ -495,7 +495,7 @@ impl<T: CopyRead> CopyRead for BufReader<T> { } } -impl<T: CopyWrite> CopyWrite for BufWriter<T> { +impl<T: ?Sized + CopyWrite> CopyWrite for BufWriter<T> { fn properties(&self) -> CopyParams { self.get_ref().properties() } diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index ee016887e..fe9559f2a 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -10,7 +10,7 @@ macro_rules! unimpl { pub mod net { #![allow(warnings)] use crate::fmt; - use crate::io::{self, IoSlice, IoSliceMut}; + use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; @@ -218,6 +218,10 @@ pub mod net { unimpl!(); } + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { unimpl!(); } diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs index 192fa216d..2dc1b0c60 100644 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -32,6 +32,7 @@ impl LazyInit for AllocatedCondvar { if #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "l4re", target_os = "android", @@ -124,6 +125,7 @@ impl Condvar { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "android", target_os = "espidf", @@ -158,6 +160,7 @@ impl Condvar { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "android", target_os = "espidf", diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index bb9e65e68..326f1481e 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -88,6 +88,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // The poll on Darwin doesn't set POLLNVAL for closed fds. target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "redox", target_os = "l4re", @@ -164,12 +165,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any( - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", - target_os = "vita" - )))] + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break @@ -207,7 +203,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita" )))] static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = crate::sync::atomic::AtomicBool::new(false); @@ -217,7 +212,6 @@ static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita", )))] pub(crate) fn unix_sigpipe_attr_specified() -> bool { UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed) @@ -395,7 +389,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "macos")] { #[link(name = "System")] extern "C" {} - } else if #[cfg(any(target_os = "ios", target_os = "watchos"))] { + } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { #[link(name = "System")] #[link(name = "objc")] #[link(name = "Security", kind = "framework")] @@ -408,6 +402,9 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { #[link(name = "dl")] extern "C" {} + } else if #[cfg(target_os = "vita")] { + #[link(name = "pthread", kind = "static", modifiers = "-bundle")] + extern "C" {} } } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 39edb136c..7258c222a 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -454,12 +454,18 @@ impl Socket { Ok(passcred != 0) } - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) } + #[cfg(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = nonblocking as libc::c_int; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { // FIONBIO is inadequate for sockets on illumos/Solaris, so use the diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 8edfd3313..a68c14758 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -63,7 +63,13 @@ extern "C" { #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] #[cfg_attr( - any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"), + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "freebsd", + target_os = "watchos" + ), link_name = "__error" )] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] @@ -375,7 +381,7 @@ pub fn current_exe() -> io::Result<PathBuf> { Ok(PathBuf::from(OsString::from_vec(e))) } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { let mut sz: u32 = 0; @@ -609,6 +615,7 @@ pub fn home_dir() -> Option<PathBuf> { #[cfg(any( target_os = "android", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "emscripten", target_os = "redox", @@ -623,6 +630,7 @@ pub fn home_dir() -> Option<PathBuf> { #[cfg(not(any( target_os = "android", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "emscripten", target_os = "redox", diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs index 488217f39..f7333fd5a 100644 --- a/library/std/src/sys/unix/os_str.rs +++ b/library/std/src/sys/unix/os_str.rs @@ -193,17 +193,22 @@ impl Buf { impl Slice { #[inline] - fn from_u8_slice(s: &[u8]) -> &Slice { + pub fn as_os_str_bytes(&self) -> &[u8] { + &self.inner + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice { unsafe { mem::transmute(s) } } #[inline] pub fn from_str(s: &str) -> &Slice { - Slice::from_u8_slice(s.as_bytes()) + unsafe { Slice::from_os_str_bytes_unchecked(s.as_bytes()) } } - pub fn to_str(&self) -> Option<&str> { - str::from_utf8(&self.inner).ok() + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { + str::from_utf8(&self.inner) } pub fn to_string_lossy(&self) -> Cow<'_, str> { diff --git a/library/std/src/sys/unix/os_str/tests.rs b/library/std/src/sys/unix/os_str/tests.rs index 22ba0c923..91bc0e61a 100644 --- a/library/std/src/sys/unix/os_str/tests.rs +++ b/library/std/src/sys/unix/os_str/tests.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn slice_debug_output() { - let input = Slice::from_u8_slice(b"\xF0hello,\tworld"); + let input = unsafe { Slice::from_os_str_bytes_unchecked(b"\xF0hello,\tworld") }; let expected = r#""\xF0hello,\tworld""#; let output = format!("{input:?}"); @@ -11,8 +11,7 @@ fn slice_debug_output() { #[test] fn display() { - assert_eq!( - "Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", - Slice::from_u8_slice(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string(), - ); + assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe { + Slice::from_os_str_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string() + },); } diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index a98a69e2d..935245f63 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -30,7 +30,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { // Get the components, skipping the redundant leading "." component if it exists. let mut components = path.strip_prefix(".").unwrap_or(path).components(); - let path_os = path.as_os_str().bytes(); + let path_os = path.as_os_str().as_os_str_bytes(); let mut normalized = if path.is_absolute() { // "If a pathname begins with two successive <slash> characters, the diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index afd03d79c..640648e87 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -164,9 +164,9 @@ pub enum ProgramKind { impl ProgramKind { fn new(program: &OsStr) -> Self { - if program.bytes().starts_with(b"/") { + if program.as_os_str_bytes().starts_with(b"/") { Self::Absolute - } else if program.bytes().contains(&b'/') { + } else if program.as_os_str_bytes().contains(&b'/') { // If the program has more than one component in it, it is a relative path. Self::Relative } else { diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 612d43fe2..0ce93af66 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -15,6 +15,8 @@ use crate::sys::weak::raw_syscall; #[cfg(any( target_os = "macos", + target_os = "watchos", + target_os = "tvos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), @@ -28,15 +30,38 @@ use libc::RTP_ID as pid_t; #[cfg(not(target_os = "vxworks"))] use libc::{c_int, pid_t}; -#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))] +#[cfg(not(any( + target_os = "vxworks", + target_os = "l4re", + target_os = "tvos", + target_os = "watchos", +)))] use libc::{gid_t, uid_t}; cfg_if::cfg_if! { if #[cfg(all(target_os = "nto", target_env = "nto71"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; - // arbitrary number of tries: - const MAX_FORKSPAWN_TRIES: u32 = 4; + use crate::time::Duration; + use crate::sync::LazyLock; + // Get smallest amount of time we can sleep. + // Return a common value if it cannot be determined. + fn get_clock_resolution() -> Duration { + static MIN_DELAY: LazyLock<Duration, fn() -> Duration> = LazyLock::new(|| { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + }); + *MIN_DELAY + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); + // Maximum duration of sleeping before giving up and returning an error + const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); } } @@ -66,7 +91,6 @@ impl Command { if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { return Ok((ret, ours)); } - let (input, output) = sys::pipe::anon_pipe()?; // Whatever happens after the fork is almost for sure going to touch or @@ -148,9 +172,31 @@ impl Command { crate::sys_common::process::wait_with_output(proc, pipes) } + // WatchOS and TVOS headers mark the `fork`/`exec*` functions with + // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the + // `posix_spawn*` functions should be used instead. It isn't entirely clear + // what `PROHIBITED` means here (e.g. if calls to these functions are + // allowed to exist in dead code), but it sounds bad, so we go out of our + // way to avoid that all-together. + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!( + ErrorKind::Unsupported, + "`fork`+`exec`-based process spawning is not supported on this target", + ); + + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))] + #[cfg(not(any( + target_os = "linux", + target_os = "watchos", + target_os = "tvos", + all(target_os = "nto", target_env = "nto71"), + )))] unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { cvt(libc::fork()).map(|res| (res, -1)) } @@ -163,12 +209,25 @@ impl Command { unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { use crate::sys::os::errno; - let mut tries_left = MAX_FORKSPAWN_TRIES; + let mut delay = MIN_FORKSPAWN_SLEEP; + loop { let r = libc::fork(); - if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF { - thread::yield_now(); - tries_left -= 1; + if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "forking returned EBADF too often", + )); + } + delay *= 2; + continue; } else { return cvt(r).map(|res| (res, -1)); } @@ -308,6 +367,7 @@ impl Command { // allocation). Instead we just close it manually. This will never // have the drop glue anyway because this code never returns (the // child will either exec() or invoke libc::exit) + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] unsafe fn do_exec( &mut self, stdio: ChildPipes, @@ -414,8 +474,19 @@ impl Command { Err(io::Error::last_os_error()) } + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_exec( + &mut self, + _stdio: ChildPipes, + _maybe_envp: Option<&CStringArray>, + ) -> Result<!, io::Error> { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + #[cfg(not(any( target_os = "macos", + target_os = "tvos", + target_os = "watchos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), @@ -433,6 +504,9 @@ impl Command { // directly. #[cfg(any( target_os = "macos", + // FIXME: `target_os = "ios"`? + target_os = "tvos", + target_os = "watchos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), @@ -480,17 +554,28 @@ impl Command { attrp: *const posix_spawnattr_t, argv: *const *mut c_char, envp: *const *mut c_char, - ) -> i32 { - let mut tries_left = MAX_FORKSPAWN_TRIES; + ) -> io::Result<i32> { + let mut delay = MIN_FORKSPAWN_SLEEP; loop { match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF if tries_left > 0 => { - thread::yield_now(); - tries_left -= 1; + libc::EBADF => { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "posix_spawnp returned EBADF too often", + )); + } + delay *= 2; continue; } r => { - return r; + return Ok(r); } } } @@ -508,7 +593,7 @@ impl Command { } let addchdir = match self.get_cwd() { Some(cwd) => { - if cfg!(target_os = "macos") { + if cfg!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) { // There is a bug in macOS where a relative executable // path like "../myprogram" will cause `posix_spawn` to // successfully launch the program, but erroneously return @@ -620,14 +705,20 @@ impl Command { let spawn_fn = libc::posix_spawnp; #[cfg(target_os = "nto")] let spawn_fn = retrying_libc_posix_spawnp; - cvt_nz(spawn_fn( + + let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), attrs.0.as_ptr(), self.get_argv().as_ptr() as *const _, envp as *const _, - ))?; + ); + + #[cfg(target_os = "nto")] + let spawn_res = spawn_res?; + + cvt_nz(spawn_res)?; Ok(Some(p)) } } @@ -671,12 +762,9 @@ impl Process { pub fn kill(&mut self) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing - // random processes, so just return an error. + // random processes, so return Ok because the process has exited already. if self.status.is_some() { - Err(io::const_io_error!( - ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process", - )) + Ok(()) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } @@ -788,31 +876,47 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGILL => " (SIGILL)", libc::SIGTRAP => " (SIGTRAP)", libc::SIGABRT => " (SIGABRT)", + #[cfg(not(target_os = "l4re"))] libc::SIGBUS => " (SIGBUS)", libc::SIGFPE => " (SIGFPE)", libc::SIGKILL => " (SIGKILL)", + #[cfg(not(target_os = "l4re"))] libc::SIGUSR1 => " (SIGUSR1)", libc::SIGSEGV => " (SIGSEGV)", + #[cfg(not(target_os = "l4re"))] libc::SIGUSR2 => " (SIGUSR2)", libc::SIGPIPE => " (SIGPIPE)", libc::SIGALRM => " (SIGALRM)", libc::SIGTERM => " (SIGTERM)", + #[cfg(not(target_os = "l4re"))] libc::SIGCHLD => " (SIGCHLD)", + #[cfg(not(target_os = "l4re"))] libc::SIGCONT => " (SIGCONT)", + #[cfg(not(target_os = "l4re"))] libc::SIGSTOP => " (SIGSTOP)", + #[cfg(not(target_os = "l4re"))] libc::SIGTSTP => " (SIGTSTP)", + #[cfg(not(target_os = "l4re"))] libc::SIGTTIN => " (SIGTTIN)", + #[cfg(not(target_os = "l4re"))] libc::SIGTTOU => " (SIGTTOU)", + #[cfg(not(target_os = "l4re"))] libc::SIGURG => " (SIGURG)", + #[cfg(not(target_os = "l4re"))] libc::SIGXCPU => " (SIGXCPU)", + #[cfg(not(target_os = "l4re"))] libc::SIGXFSZ => " (SIGXFSZ)", + #[cfg(not(target_os = "l4re"))] libc::SIGVTALRM => " (SIGVTALRM)", + #[cfg(not(target_os = "l4re"))] libc::SIGPROF => " (SIGPROF)", + #[cfg(not(target_os = "l4re"))] libc::SIGWINCH => " (SIGWINCH)", - #[cfg(not(target_os = "haiku"))] + #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] libc::SIGIO => " (SIGIO)", #[cfg(target_os = "haiku")] libc::SIGPOLL => " (SIGPOLL)", + #[cfg(not(target_os = "l4re"))] libc::SIGSYS => " (SIGSYS)", // For information on Linux signals, run `man 7 signal` #[cfg(all( diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs index c40e7ada0..f70d3cb39 100644 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -144,12 +144,9 @@ impl Process { pub fn kill(&mut self) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing - // random processes, so just return an error. + // random processes, so return Ok because the process has exited already. if self.status.is_some() { - Err(io::const_io_error!( - ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process", - )) + Ok(()) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index d8b63546b..d471be33e 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -14,6 +14,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { unix, not(target_os = "macos"), not(target_os = "ios"), + not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "openbsd"), not(target_os = "freebsd"), @@ -198,7 +199,7 @@ mod imp { // once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is // only used on iOS where direct access to `/dev/urandom` is blocked by the // sandbox. -#[cfg(any(target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] mod imp { use crate::io; use crate::ptr; diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 7307d9b2c..4f2d9cf36 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -150,7 +150,7 @@ impl Thread { } } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn set_name(name: &CStr) { unsafe { let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); @@ -284,7 +284,13 @@ impl Drop for Thread { } } -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", +))] fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { @@ -300,6 +306,7 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { target_os = "emscripten", target_os = "fuchsia", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "solaris", @@ -345,6 +352,29 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { } } + #[cfg(target_os = "netbsd")] + { + unsafe { + let set = libc::_cpuset_create(); + if !set.is_null() { + let mut count: usize = 0; + if libc::pthread_getaffinity_np(libc::pthread_self(), libc::_cpuset_size(set), set) == 0 { + for i in 0..u64::MAX { + match libc::_cpuset_isset(i, set) { + -1 => break, + 0 => continue, + _ => count = count + 1, + } + } + } + libc::_cpuset_destroy(set); + if let Some(count) = NonZeroUsize::new(count) { + return Ok(count); + } + } + } + } + let mut cpus: libc::c_uint = 0; let mut cpus_size = crate::mem::size_of_val(&cpus); diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs index 43046ed07..ae805d843 100644 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/unix/thread_parking/pthread.rs @@ -46,6 +46,7 @@ unsafe fn wait_timeout( #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "espidf", target_os = "horizon", @@ -73,6 +74,7 @@ unsafe fn wait_timeout( #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "espidf", target_os = "horizon", @@ -120,10 +122,12 @@ impl Parker { if #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "l4re", target_os = "android", - target_os = "redox" + target_os = "redox", + target_os = "vita", ))] { addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index a9fbc7ab1..17b4130c2 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -219,7 +219,8 @@ impl From<__timespec64> for Timespec { #[cfg(any( all(target_os = "macos", any(not(target_arch = "aarch64"))), target_os = "ios", - target_os = "watchos" + target_os = "watchos", + target_os = "tvos" ))] mod inner { use crate::sync::atomic::{AtomicU64, Ordering}; @@ -339,7 +340,8 @@ mod inner { #[cfg(not(any( all(target_os = "macos", any(not(target_arch = "aarch64"))), target_os = "ios", - target_os = "watchos" + target_os = "watchos", + target_os = "tvos" )))] mod inner { use crate::fmt; diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs index 62ffee70b..61088ff16 100644 --- a/library/std/src/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -28,7 +28,7 @@ use crate::ptr; use crate::sync::atomic::{self, AtomicPtr, Ordering}; // We can use true weak linkage on ELF targets. -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos")))] pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = { @@ -43,7 +43,7 @@ pub(crate) macro weak { } // On non-ELF targets, use the dlsym approximation of weak linkage. -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] pub(crate) use self::dlsym as weak; pub(crate) struct ExternWeak<F: Copy> { diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index 9a8b2a0be..1b50c2ea6 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -96,7 +96,7 @@ impl WasiFd { 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<()> { + pub(crate) fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { unsafe { wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) } @@ -179,7 +179,7 @@ impl WasiFd { } } - pub fn filestat_get(&self) -> io::Result<wasi::Filestat> { + pub(crate) fn filestat_get(&self) -> io::Result<wasi::Filestat> { unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } @@ -199,7 +199,7 @@ impl WasiFd { unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } } - pub fn path_filestat_get( + pub(crate) fn path_filestat_get( &self, flags: wasi::Lookupflags, path: &str, diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 8d1dbf591..437aae3ae 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -104,7 +104,7 @@ impl FileAttr { Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) } - pub fn as_wasi(&self) -> &wasi::Filestat { + pub(crate) fn as_wasi(&self) -> &wasi::Filestat { &self.meta } } @@ -142,7 +142,7 @@ impl FileType { self.bits == wasi::FILETYPE_SYMBOLIC_LINK } - pub fn bits(&self) -> wasi::Filetype { + pub(crate) fn bits(&self) -> wasi::Filetype { self.bits } } diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 5bfd8b52e..6b597f499 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -226,7 +226,7 @@ pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> i // that it actually gets passed through on the command line or otherwise // it will be dropped entirely when parsed on the other end. ensure_no_nuls(arg)?; - let arg_bytes = arg.bytes(); + let arg_bytes = arg.as_os_str_bytes(); let (quote, escape) = match quote { Quote::Always => (true, true), Quote::Auto => { @@ -297,7 +297,9 @@ pub(crate) fn make_bat_command_line( // * `|<>` pipe/redirect characters. const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; let force_quotes = match arg { - Arg::Regular(arg) if !force_quotes => arg.bytes().iter().any(|c| SPECIAL.contains(c)), + Arg::Regular(arg) if !force_quotes => { + arg.as_os_str_bytes().iter().any(|c| SPECIAL.contains(c)) + } _ => force_quotes, }; append_arg(&mut cmd, arg, force_quotes)?; diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 2bc40c474..d9ccba0e9 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -12,13 +12,13 @@ use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; use crate::ptr; use core::ffi::NonZero_c_ulong; -#[path = "c/windows_sys.rs"] // c.rs is included from two places so we need to specify this mod windows_sys; pub use windows_sys::*; pub type DWORD = c_ulong; pub type NonZeroDWORD = NonZero_c_ulong; pub type LARGE_INTEGER = c_longlong; +#[cfg_attr(target_vendor = "uwp", allow(unused))] pub type LONG = c_long; pub type UINT = c_uint; pub type WCHAR = u16; @@ -267,6 +267,8 @@ pub unsafe fn getaddrinfo( windows_sys::getaddrinfo(node.cast::<u8>(), service.cast::<u8>(), hints, res) } +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { pub unsafe fn NtReadFile( filehandle: BorrowedHandle<'_>, event: HANDLE, @@ -313,6 +315,8 @@ pub unsafe fn NtWriteFile( key.map(|k| k as *const u32).unwrap_or(ptr::null()), ) } +} +} // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. @@ -376,4 +380,98 @@ compat_fn_with_fallback! { ) -> NTSTATUS { panic!("keyed events not available") } + + // These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically. + #[cfg(target_vendor = "uwp")] + pub fn NtCreateFile( + filehandle: *mut HANDLE, + desiredaccess: FILE_ACCESS_RIGHTS, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + allocationsize: *const i64, + fileattributes: FILE_FLAGS_AND_ATTRIBUTES, + shareaccess: FILE_SHARE_MODE, + createdisposition: NTCREATEFILE_CREATE_DISPOSITION, + createoptions: NTCREATEFILE_CREATE_OPTIONS, + eabuffer: *const ::core::ffi::c_void, + ealength: u32 + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn NtReadFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *mut crate::mem::MaybeUninit<u8>, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG> + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn NtWriteFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *const u8, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG> + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 { + Status as u32 + } +} + +// # Arm32 shim +// +// AddVectoredExceptionHandler and WSAStartup use platform-specific types. +// However, Microsoft no longer supports thumbv7a so definitions for those targets +// are not included in the win32 metadata. We work around that by defining them here. +// +// Where possible, these definitions should be kept in sync with https://docs.rs/windows-sys +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { + #[link(name = "kernel32")] + extern "system" { + pub fn AddVectoredExceptionHandler( + first: u32, + handler: PVECTORED_EXCEPTION_HANDLER, + ) -> *mut c_void; + } + pub type PVECTORED_EXCEPTION_HANDLER = Option< + unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, + >; + #[repr(C)] + pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, + } + #[cfg(target_arch = "arm")] + pub enum CONTEXT {} +}} + +#[link(name = "ws2_32")] +extern "system" { + pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; +} +#[cfg(target_arch = "arm")] +#[repr(C)] +pub struct WSADATA { + pub wVersion: u16, + pub wHighVersion: u16, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: PSTR, } diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst index 3e454199f..631aedd26 100644 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ b/library/std/src/sys/windows/c/windows_sys.lst @@ -1930,6 +1930,7 @@ Windows.Win32.Foundation.SetLastError Windows.Win32.Foundation.STATUS_DELETE_PENDING Windows.Win32.Foundation.STATUS_END_OF_FILE Windows.Win32.Foundation.STATUS_INVALID_PARAMETER +Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED Windows.Win32.Foundation.STATUS_PENDING Windows.Win32.Foundation.STATUS_SUCCESS Windows.Win32.Foundation.TRUE @@ -2170,7 +2171,6 @@ Windows.Win32.Networking.WinSock.WSARecv Windows.Win32.Networking.WinSock.WSASend Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND Windows.Win32.Networking.WinSock.WSASocketW -Windows.Win32.Networking.WinSock.WSAStartup Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE Windows.Win32.Networking.WinSock.WSASYSNOTREADY Windows.Win32.Networking.WinSock.WSATRY_AGAIN @@ -2418,12 +2418,10 @@ Windows.Win32.System.Console.STD_HANDLE Windows.Win32.System.Console.STD_INPUT_HANDLE Windows.Win32.System.Console.STD_OUTPUT_HANDLE Windows.Win32.System.Console.WriteConsoleW -Windows.Win32.System.Diagnostics.Debug.AddVectoredExceptionHandler Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 Windows.Win32.System.Diagnostics.Debug.CONTEXT Windows.Win32.System.Diagnostics.Debug.CONTEXT Windows.Win32.System.Diagnostics.Debug.CONTEXT -Windows.Win32.System.Diagnostics.Debug.EXCEPTION_POINTERS Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT @@ -2436,7 +2434,6 @@ Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS Windows.Win32.System.Diagnostics.Debug.FormatMessageW Windows.Win32.System.Diagnostics.Debug.M128A -Windows.Win32.System.Diagnostics.Debug.PVECTORED_EXCEPTION_HANDLER Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT Windows.Win32.System.Environment.FreeEnvironmentStringsW diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs index 36a30f6ba..023770871 100644 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ b/library/std/src/sys/windows/c/windows_sys.rs @@ -40,13 +40,6 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn AddVectoredExceptionHandler( - first: u32, - handler: PVECTORED_EXCEPTION_HANDLER, - ) -> *mut ::core::ffi::c_void; -} -#[link(name = "kernel32")] -extern "system" { pub fn CancelIo(hfile: HANDLE) -> BOOL; } #[link(name = "kernel32")] @@ -712,10 +705,6 @@ extern "system" { } #[link(name = "ws2_32")] extern "system" { - pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut i32) -> SOCKET; } #[link(name = "ws2_32")] @@ -3029,17 +3018,6 @@ pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32; pub type EXCEPTION_DISPOSITION = i32; pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32; #[repr(C)] -pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, -} -impl ::core::marker::Copy for EXCEPTION_POINTERS {} -impl ::core::clone::Clone for EXCEPTION_POINTERS { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] pub struct EXCEPTION_RECORD { pub ExceptionCode: NTSTATUS, pub ExceptionFlags: u32, @@ -3748,9 +3726,6 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; pub const PROGRESS_CONTINUE: u32 = 0u32; pub type PSTR = *mut u8; -pub type PVECTORED_EXCEPTION_HANDLER = ::core::option::Option< - unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, ->; pub type PWSTR = *mut u16; pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; @@ -3888,6 +3863,7 @@ pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = -1073741738i32; pub const STATUS_END_OF_FILE: NTSTATUS = -1073741807i32; pub const STATUS_INVALID_PARAMETER: NTSTATUS = -1073741811i32; +pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = -1073741822i32; pub const STATUS_PENDING: NTSTATUS = 259i32; pub const STATUS_SUCCESS: NTSTATUS = 0i32; pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32; diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 2404bbe2b..1ae42cb7e 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -159,7 +159,7 @@ impl Socket { } let mut timeout = c::timeval { - tv_sec: timeout.as_secs() as c_long, + tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, tv_usec: (timeout.subsec_nanos() / 1000) as c_long, }; diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs index 2f2b0e56e..16c4f55c6 100644 --- a/library/std/src/sys/windows/os_str.rs +++ b/library/std/src/sys/windows/os_str.rs @@ -152,11 +152,21 @@ impl Buf { impl Slice { #[inline] + pub fn as_os_str_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice { + mem::transmute(Wtf8::from_bytes_unchecked(s)) + } + + #[inline] pub fn from_str(s: &str) -> &Slice { unsafe { mem::transmute(Wtf8::from_str(s)) } } - pub fn to_str(&self) -> Option<&str> { + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { self.inner.as_str() } diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index c3573d14c..c9c2d10e6 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -1,7 +1,6 @@ use super::{c, fill_utf16_buf, to_u16s}; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::mem; use crate::path::{Path, PathBuf, Prefix}; use crate::ptr; @@ -11,16 +10,6 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; -/// # Safety -/// -/// `bytes` must be a valid wtf8 encoded slice -#[inline] -unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr { - // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8, - // which is compatible with &[u8]. - mem::transmute(bytes) -} - #[inline] pub fn is_sep_byte(b: u8) -> bool { b == b'/' || b == b'\\' @@ -33,12 +22,12 @@ pub fn is_verbatim_sep(b: u8) -> bool { /// Returns true if `path` looks like a lone filename. pub(crate) fn is_file_name(path: &OsStr) -> bool { - !path.bytes().iter().copied().any(is_sep_byte) + !path.as_os_str_bytes().iter().copied().any(is_sep_byte) } pub(crate) fn has_trailing_slash(path: &OsStr) -> bool { - let is_verbatim = path.bytes().starts_with(br"\\?\"); + let is_verbatim = path.as_os_str_bytes().starts_with(br"\\?\"); let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte }; - if let Some(&c) = path.bytes().last() { is_separator(c) } else { false } + if let Some(&c) = path.as_os_str_bytes().last() { is_separator(c) } else { false } } /// Appends a suffix to a path. @@ -60,7 +49,7 @@ impl<'a, const LEN: usize> PrefixParser<'a, LEN> { fn get_prefix(path: &OsStr) -> [u8; LEN] { let mut prefix = [0; LEN]; // SAFETY: Only ASCII characters are modified. - for (i, &ch) in path.bytes().iter().take(LEN).enumerate() { + for (i, &ch) in path.as_os_str_bytes().iter().take(LEN).enumerate() { prefix[i] = if ch == b'/' { b'\\' } else { ch }; } prefix @@ -93,7 +82,7 @@ impl<'a> PrefixParserSlice<'a, '_> { } fn prefix_bytes(&self) -> &'a [u8] { - &self.path.bytes()[..self.index] + &self.path.as_os_str_bytes()[..self.index] } fn finish(self) -> &'a OsStr { @@ -101,7 +90,7 @@ impl<'a> PrefixParserSlice<'a, '_> { // &[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. - unsafe { bytes_as_os_str(&self.path.bytes()[self.index..]) } + unsafe { OsStr::from_os_str_bytes_unchecked(&self.path.as_os_str_bytes()[self.index..]) } } } @@ -173,7 +162,7 @@ fn parse_drive(path: &OsStr) -> Option<u8> { drive.is_ascii_alphabetic() } - match path.bytes() { + match path.as_os_str_bytes() { [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), _ => None, } @@ -182,7 +171,7 @@ fn parse_drive(path: &OsStr) -> Option<u8> { // Parses a drive prefix exactly, e.g. "C:" fn parse_drive_exact(path: &OsStr) -> Option<u8> { // only parse two bytes: the drive letter and the drive separator - if path.bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { + if path.as_os_str_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { parse_drive(path) } else { None @@ -196,21 +185,26 @@ fn parse_drive_exact(path: &OsStr) -> Option<u8> { fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; - match path.bytes().iter().position(|&x| separator(x)) { + match path.as_os_str_bytes().iter().position(|&x| separator(x)) { Some(separator_start) => { let separator_end = separator_start + 1; - let component = &path.bytes()[..separator_start]; + let component = &path.as_os_str_bytes()[..separator_start]; // Panic safe // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. - let path = &path.bytes()[separator_end..]; + let path = &path.as_os_str_bytes()[separator_end..]; // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') // is encoded in a single byte, therefore `bytes[separator_start]` and // `bytes[separator_end]` must be code point boundaries and thus // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. - unsafe { (bytes_as_os_str(component), bytes_as_os_str(path)) } + unsafe { + ( + OsStr::from_os_str_bytes_unchecked(component), + OsStr::from_os_str_bytes_unchecked(path), + ) + } } None => (path, OsStr::new("")), } @@ -329,7 +323,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { // Verbatim paths should not be modified. if prefix.map(|x| x.is_verbatim()).unwrap_or(false) { // NULs in verbatim paths are rejected for consistency. - if path.bytes().contains(&0) { + if path.as_os_str_bytes().contains(&0) { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index df3667c0f..e3493cbb8 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -395,7 +395,7 @@ fn resolve_exe<'a>( // Test if the file name has the `exe` extension. // This does a case-insensitive `ends_with`. let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() { - exe_path.bytes()[exe_path.len() - EXE_SUFFIX.len()..] + exe_path.as_os_str_bytes()[exe_path.len() - EXE_SUFFIX.len()..] .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes()) } else { false @@ -425,7 +425,7 @@ fn resolve_exe<'a>( // From the `CreateProcessW` docs: // > If the file name does not contain an extension, .exe is appended. // Note that this rule only applies when searching paths. - let has_extension = exe_path.bytes().contains(&b'.'); + let has_extension = exe_path.as_os_str_bytes().contains(&b'.'); // Search the directories given by `search_paths`. let result = search_paths(parent_paths, child_paths, |mut path| { @@ -595,7 +595,16 @@ pub struct Process { impl Process { pub fn kill(&mut self) -> io::Result<()> { - cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?; + let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; + if result == c::FALSE { + let error = unsafe { c::GetLastError() }; + // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been + // terminated (by us, or for any other reason). So check if the process was actually + // terminated, and if so, do not return an error. + if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { + return Err(crate::io::Error::from_raw_os_error(error as i32)); + } + } Ok(()) } diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs index bca4e38d9..5d8fd1378 100644 --- a/library/std/src/sys/windows/rand.rs +++ b/library/std/src/sys/windows/rand.rs @@ -1,5 +1,3 @@ -use crate::ffi::c_void; -use crate::io; use crate::mem; use crate::ptr; use crate::sys::c; @@ -25,6 +23,9 @@ pub fn hashmap_random_keys() -> (u64, u64) { #[cfg(not(target_vendor = "uwp"))] #[inline(never)] fn fallback_rng() -> (u64, u64) { + use crate::ffi::c_void; + use crate::io; + let mut v = (0, 0); let ret = unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut c_void, mem::size_of_val(&v) as c::ULONG) diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 2e3e0859d..3fcaaa508 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -11,6 +11,9 @@ use crate::sys::cvt; use crate::sys::handle::Handle; use core::str::utf8_char_width; +#[cfg(test)] +mod tests; + // 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 { @@ -383,6 +386,10 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> { debug_assert!(utf16.len() <= c::c_int::MAX as usize); debug_assert!(utf8.len() <= c::c_int::MAX as usize); + if utf16.is_empty() { + return Ok(0); + } + let result = unsafe { c::WideCharToMultiByte( c::CP_UTF8, // CodePage diff --git a/library/std/src/sys/windows/stdio/tests.rs b/library/std/src/sys/windows/stdio/tests.rs new file mode 100644 index 000000000..1e53e0bee --- /dev/null +++ b/library/std/src/sys/windows/stdio/tests.rs @@ -0,0 +1,6 @@ +use super::utf16_to_utf8; + +#[test] +fn zero_size_read() { + assert_eq!(utf16_to_utf8(&[], &mut []).unwrap(), 0); +} diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 652c695fc..2976a9f57 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -18,7 +18,7 @@ use crate::ffi::{c_int, c_void}; cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", target_os = "macos", target_os = "watchos", + target_os = "ios", target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index ff96c35fb..c9d3e13cf 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -570,7 +570,7 @@ impl Wtf8 { /// Since the byte slice is not checked for valid WTF-8, this functions is /// marked unsafe. #[inline] - unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { + pub unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { mem::transmute(value) } @@ -614,19 +614,20 @@ impl Wtf8 { Wtf8CodePoints { bytes: self.bytes.iter() } } + /// Access raw bytes of WTF-8 data + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } + /// Tries to convert the string to UTF-8 and return a `&str` slice. /// /// Returns `None` if the string contains surrogates. /// /// This does not copy the data. #[inline] - pub fn as_str(&self) -> Option<&str> { - // Well-formed WTF-8 is also well-formed UTF-8 - // if and only if it contains no surrogate. - match self.next_surrogate(0) { - None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }), - Some(_) => None, - } + pub fn as_str(&self) -> Result<&str, str::Utf8Error> { + str::from_utf8(&self.bytes) } /// Creates an owned `Wtf8Buf` from a borrowed `Wtf8`. diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index 1a302d646..a07bbe6d7 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -521,11 +521,11 @@ fn wtf8_code_points() { #[test] fn wtf8_as_str() { - assert_eq!(Wtf8::from_str("").as_str(), Some("")); - assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩")); + assert_eq!(Wtf8::from_str("").as_str(), Ok("")); + assert_eq!(Wtf8::from_str("aé 💩").as_str(), Ok("aé 💩")); let mut string = Wtf8Buf::new(); string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(string.as_str(), None); + assert!(string.as_str().is_err()); } #[test] diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index f712c8727..e4581c2de 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -195,7 +195,7 @@ mod local; cfg_if::cfg_if! { if #[cfg(test)] { - // Avoid duplicating the global state assoicated with thread-locals between this crate and + // Avoid duplicating the global state associated with thread-locals between this crate and // realstd. Miri relies on this. pub use realstd::thread::{local_impl, AccessError, LocalKey}; } else { @@ -206,7 +206,7 @@ cfg_if::cfg_if! { #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { - pub use crate::sys::common::thread_local::{thread_local_inner, Key}; + pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind}; } } } @@ -889,7 +889,7 @@ impl Drop for PanicGuard { /// it is guaranteed that this function will not panic (it may abort the /// process if the implementation encounters some rare errors). /// -/// # park and unpark +/// # `park` and `unpark` /// /// Every thread is equipped with some basic low-level blocking support, via the /// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] @@ -910,14 +910,6 @@ impl Drop for PanicGuard { /// if it wasn't already. Because the token is initially absent, [`unpark`] /// followed by [`park`] will result in the second call returning immediately. /// -/// In other words, each [`Thread`] acts a bit like a spinlock that can be -/// locked and unlocked using `park` and `unpark`. -/// -/// Notice that being unblocked does not imply any synchronization with someone -/// that unparked this thread, it could also be spurious. -/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and -/// [`unpark`] return immediately without doing anything. -/// /// The API is typically used by acquiring a handle to the current thread, /// placing that handle in a shared data structure so that other threads can /// find it, and then `park`ing in a loop. When some desired condition is met, another @@ -931,6 +923,23 @@ impl Drop for PanicGuard { /// /// * It can be implemented very efficiently on many platforms. /// +/// # Memory Ordering +/// +/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory +/// operations performed before a call to `unpark` are made visible to the thread that +/// consumes the token and returns from `park`. Note that all `park` and `unpark` +/// operations for a given thread form a total order and `park` synchronizes-with +/// _all_ prior `unpark` operations. +/// +/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` +/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same +/// thread form a [release sequence]. +/// +/// Note that being unblocked does not imply a call was made to `unpark`, because +/// wakeups can also be spurious. For example, a valid, but inefficient, +/// implementation could have `park` and `unpark` return immediately without doing anything, +/// making *all* wakeups spurious. +/// /// # Examples /// /// ``` @@ -944,7 +953,7 @@ impl Drop for PanicGuard { /// let parked_thread = thread::spawn(move || { /// // We want to wait until the flag is set. We *could* just spin, but using /// // park/unpark is more efficient. -/// while !flag2.load(Ordering::Acquire) { +/// while !flag2.load(Ordering::Relaxed) { /// println!("Parking thread"); /// thread::park(); /// // We *could* get here spuriously, i.e., way before the 10ms below are over! @@ -961,7 +970,7 @@ impl Drop for PanicGuard { /// // There is no race condition here, if `unpark` /// // happens first, `park` will return immediately. /// // Hence there is no risk of a deadlock. -/// flag.store(true, Ordering::Release); +/// flag.store(true, Ordering::Relaxed); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// @@ -970,6 +979,7 @@ impl Drop for PanicGuard { /// /// [`unpark`]: Thread::unpark /// [`thread::park_timeout`]: park_timeout +/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence #[stable(feature = "rust1", since = "1.0.0")] pub fn park() { let guard = PanicGuard; diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index b65e2572c..5d6b9e94e 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -42,6 +42,7 @@ fn test_named_thread() { all(target_os = "linux", target_env = "gnu"), target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos" ))] #[test] |