diff options
Diffstat (limited to 'library/std/src')
67 files changed, 1379 insertions, 722 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 12246b5173d..35f17aa781f 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -334,10 +334,11 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// for key in map.keys() { /// println!("{}", key); @@ -356,10 +357,11 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// for val in map.values() { /// println!("{}", val); @@ -378,11 +380,11 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let mut map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// for val in map.values_mut() { /// *val = *val + 10; @@ -405,10 +407,11 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// for (key, val) in map.iter() { /// println!("key: {} val: {}", key, val); @@ -428,10 +431,11 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let mut map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// // Update all values /// for (_, val) in map.iter_mut() { @@ -966,10 +970,11 @@ where /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// let mut vec: Vec<&str> = map.into_keys().collect(); /// // The `IntoKeys` iterator produces keys in arbitrary order, so the @@ -992,10 +997,11 @@ where /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// let mut vec: Vec<i32> = map.into_values().collect(); /// // The `IntoValues` iterator produces values in arbitrary order, so @@ -1186,7 +1192,7 @@ where /// assert_eq!(map1, map2); /// ``` fn from(arr: [(K, V); N]) -> Self { - crate::array::IntoIter::new(arr).collect() + Self::from_iter(arr) } } @@ -1202,8 +1208,9 @@ where /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter = map.iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1239,8 +1246,9 @@ impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let mut map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter = map.iter_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1269,8 +1277,9 @@ impl<'a, K, V> IterMut<'a, K, V> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter = map.into_iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1298,8 +1307,9 @@ impl<K, V> IntoIter<K, V> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter_keys = map.keys(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1335,8 +1345,9 @@ impl<K: Debug, V> fmt::Debug for Keys<'_, K, V> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter_values = map.values(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1372,8 +1383,9 @@ impl<K, V: Debug> fmt::Debug for Values<'_, K, V> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let mut map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter = map.drain(); /// ``` #[stable(feature = "drain", since = "1.6.0")] @@ -1402,8 +1414,9 @@ impl<'a, K, V> Drain<'a, K, V> { /// /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let mut map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter = map.drain_filter(|_k, v| *v % 2 == 0); /// ``` #[unstable(feature = "hash_drain_filter", issue = "59618")] @@ -1426,8 +1439,9 @@ where /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let mut map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter_values = map.values_mut(); /// ``` #[stable(feature = "map_values_mut", since = "1.10.0")] @@ -1447,8 +1461,9 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter_keys = map.into_keys(); /// ``` #[stable(feature = "map_into_keys_values", since = "1.54.0")] @@ -1468,8 +1483,9 @@ pub struct IntoKeys<K, V> { /// ``` /// use std::collections::HashMap; /// -/// let mut map = HashMap::new(); -/// map.insert("a", 1); +/// let map = HashMap::from([ +/// ("a", 1), +/// ]); /// let iter_keys = map.into_values(); /// ``` #[stable(feature = "map_into_keys_values", since = "1.54.0")] @@ -2004,10 +2020,11 @@ impl<K, V, S> IntoIterator for HashMap<K, V, S> { /// ``` /// use std::collections::HashMap; /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); + /// let map = HashMap::from([ + /// ("a", 1), + /// ("b", 2), + /// ("c", 3), + /// ]); /// /// // Not possible with .iter() /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 1fc1d39b181..a1e28c0b0a6 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -233,7 +233,7 @@ impl<T, S> HashSet<T, S> { /// ``` /// use std::collections::HashSet; /// - /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = HashSet::from([1, 2, 3]); /// assert!(!set.is_empty()); /// /// // print 1, 2, 3 in an arbitrary order @@ -489,8 +489,8 @@ where /// /// ``` /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([4, 2, 3, 4]); /// /// // Can be seen as `a - b`. /// for x in a.difference(&b) { @@ -518,8 +518,8 @@ where /// /// ``` /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([4, 2, 3, 4]); /// /// // Print 1, 4 in arbitrary order. /// for x in a.symmetric_difference(&b) { @@ -548,8 +548,8 @@ where /// /// ``` /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([4, 2, 3, 4]); /// /// // Print 2, 3 in arbitrary order. /// for x in a.intersection(&b) { @@ -576,8 +576,8 @@ where /// /// ``` /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([4, 2, 3, 4]); /// /// // Print 1, 2, 3, 4 in arbitrary order. /// for x in a.union(&b) { @@ -608,7 +608,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let set = HashSet::from([1, 2, 3]); /// assert_eq!(set.contains(&1), true); /// assert_eq!(set.contains(&4), false); /// ``` @@ -633,7 +633,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let set = HashSet::from([1, 2, 3]); /// assert_eq!(set.get(&2), Some(&2)); /// assert_eq!(set.get(&4), None); /// ``` @@ -657,7 +657,7 @@ where /// /// use std::collections::HashSet; /// - /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = HashSet::from([1, 2, 3]); /// assert_eq!(set.len(), 3); /// assert_eq!(set.get_or_insert(2), &2); /// assert_eq!(set.get_or_insert(100), &100); @@ -744,7 +744,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let a = HashSet::from([1, 2, 3]); /// let mut b = HashSet::new(); /// /// assert_eq!(a.is_disjoint(&b), true); @@ -770,7 +770,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let sup: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let sup = HashSet::from([1, 2, 3]); /// let mut set = HashSet::new(); /// /// assert_eq!(set.is_subset(&sup), true); @@ -792,7 +792,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let sub: HashSet<_> = [1, 2].iter().cloned().collect(); + /// let sub = HashSet::from([1, 2]); /// let mut set = HashSet::new(); /// /// assert_eq!(set.is_superset(&sub), false); @@ -893,7 +893,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = HashSet::from([1, 2, 3]); /// assert_eq!(set.take(&2), Some(2)); /// assert_eq!(set.take(&2), None); /// ``` @@ -917,8 +917,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let xs = [1, 2, 3, 4, 5, 6]; - /// let mut set: HashSet<i32> = xs.iter().cloned().collect(); + /// let mut set = HashSet::from([1, 2, 3, 4, 5, 6]); /// set.retain(|&k| k % 2 == 0); /// assert_eq!(set.len(), 3); /// ``` @@ -1022,7 +1021,7 @@ where /// assert_eq!(set1, set2); /// ``` fn from(arr: [T; N]) -> Self { - crate::array::IntoIter::new(arr).collect() + Self::from_iter(arr) } } @@ -1097,8 +1096,8 @@ where /// ``` /// use std::collections::HashSet; /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([3, 4, 5]); /// /// let set = &a | &b; /// @@ -1130,8 +1129,8 @@ where /// ``` /// use std::collections::HashSet; /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![2, 3, 4].into_iter().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([2, 3, 4]); /// /// let set = &a & &b; /// @@ -1163,8 +1162,8 @@ where /// ``` /// use std::collections::HashSet; /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([3, 4, 5]); /// /// let set = &a ^ &b; /// @@ -1196,8 +1195,8 @@ where /// ``` /// use std::collections::HashSet; /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); + /// let a = HashSet::from([1, 2, 3]); + /// let b = HashSet::from([3, 4, 5]); /// /// let set = &a - &b; /// @@ -1226,7 +1225,7 @@ where /// ``` /// use std::collections::HashSet; /// -/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); +/// let a = HashSet::from([1, 2, 3]); /// /// let mut iter = a.iter(); /// ``` @@ -1248,7 +1247,7 @@ pub struct Iter<'a, K: 'a> { /// ``` /// use std::collections::HashSet; /// -/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); +/// let a = HashSet::from([1, 2, 3]); /// /// let mut iter = a.into_iter(); /// ``` @@ -1269,7 +1268,7 @@ pub struct IntoIter<K> { /// ``` /// use std::collections::HashSet; /// -/// let mut a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); +/// let mut a = HashSet::from([1, 2, 3]); /// /// let mut drain = a.drain(); /// ``` @@ -1291,7 +1290,7 @@ pub struct Drain<'a, K: 'a> { /// /// use std::collections::HashSet; /// -/// let mut a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); +/// let mut a = HashSet::from([1, 2, 3]); /// /// let mut drain_filtered = a.drain_filter(|v| v % 2 == 0); /// ``` @@ -1315,8 +1314,8 @@ where /// ``` /// use std::collections::HashSet; /// -/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); -/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); +/// let a = HashSet::from([1, 2, 3]); +/// let b = HashSet::from([4, 2, 3, 4]); /// /// let mut intersection = a.intersection(&b); /// ``` @@ -1342,8 +1341,8 @@ pub struct Intersection<'a, T: 'a, S: 'a> { /// ``` /// use std::collections::HashSet; /// -/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); -/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); +/// let a = HashSet::from([1, 2, 3]); +/// let b = HashSet::from([4, 2, 3, 4]); /// /// let mut difference = a.difference(&b); /// ``` @@ -1369,8 +1368,8 @@ pub struct Difference<'a, T: 'a, S: 'a> { /// ``` /// use std::collections::HashSet; /// -/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); -/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); +/// let a = HashSet::from([1, 2, 3]); +/// let b = HashSet::from([4, 2, 3, 4]); /// /// let mut intersection = a.symmetric_difference(&b); /// ``` @@ -1393,8 +1392,8 @@ pub struct SymmetricDifference<'a, T: 'a, S: 'a> { /// ``` /// use std::collections::HashSet; /// -/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect(); -/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); +/// let a = HashSet::from([1, 2, 3]); +/// let b = HashSet::from([4, 2, 3, 4]); /// /// let mut union_iter = a.union(&b); /// ``` diff --git a/library/std/src/env.rs b/library/std/src/env.rs index c6af708f6cd..c06928647d3 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -583,28 +583,25 @@ pub fn home_dir() -> Option<PathBuf> { /// may result in "insecure temporary file" security vulnerabilities. Consider /// using a crate that securely creates temporary files or directories. /// -/// # Unix +/// # Platform-specific behavior /// -/// Returns the value of the `TMPDIR` environment variable if it is +/// On Unix, returns the value of the `TMPDIR` environment variable if it is /// set, otherwise for non-Android it returns `/tmp`. If Android, since there /// is no global temporary folder (it is usually allocated per-app), it returns /// `/data/local/tmp`. +/// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] / +/// [`GetTempPath`][GetTempPath], which this function uses internally. +/// Note that, this [may change in the future][changes]. /// -/// # Windows -/// -/// Returns the value of, in order, the `TMP`, `TEMP`, -/// `USERPROFILE` environment variable if any are set and not the empty -/// string. Otherwise, `temp_dir` returns the path of the Windows directory. -/// This behavior is identical to that of [`GetTempPath`][msdn], which this -/// function uses internally. -/// -/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha +/// [changes]: io#platform-specific-behavior +/// [GetTempPath2]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a +/// [GetTempPath]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha /// /// ```no_run /// use std::env; /// /// fn main() { -/// let mut dir = env::temp_dir(); +/// let dir = env::temp_dir(); /// println!("Temporary directory: {}", dir.display()); /// } /// ``` diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 465bbae8631..9c1b79d6966 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -1259,7 +1259,7 @@ impl CStr { #[inline] #[must_use] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] - #[rustc_const_unstable(feature = "const_cstr_unchecked", issue = "90343")] + #[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")] pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { // SAFETY: Casting to CStr is safe because its internal representation // is a [u8] too (safe only inside std). diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 7f3bb836754..019b64c395e 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -81,9 +81,9 @@ //! [`OsStr`] and Rust strings work similarly to those for [`CString`] //! and [`CStr`]. //! -//! * [`OsString`] represents an owned string in whatever -//! representation the operating system prefers. In the Rust standard -//! library, various APIs that transfer strings to/from the operating +//! * [`OsString`] losslessly represents an owned platform string. However, this +//! representation is not necessarily in a form native to the platform. +//! In the Rust standard library, various APIs that transfer strings to/from the operating //! system use [`OsString`] instead of plain strings. For example, //! [`env::var_os()`] is used to query environment variables; it //! returns an <code>[Option]<[OsString]></code>. If the environment variable @@ -92,9 +92,9 @@ //! your code can detect errors in case the environment variable did //! not in fact contain valid Unicode data. //! -//! * [`OsStr`] represents a borrowed reference to a string in a -//! format that can be passed to the operating system. It can be -//! converted into a UTF-8 Rust string slice in a similar way to +//! * [`OsStr`] losslessly represents a borrowed reference to a platform string. +//! However, this representation is not necessarily in a form native to the platform. +//! It can be converted into a UTF-8 Rust string slice in a similar way to //! [`OsString`]. //! //! # Conversions @@ -113,16 +113,19 @@ //! //! ## On Windows //! +//! An [`OsStr`] can be losslessly converted to a native Windows string. And +//! a native Windows string can be losslessly converted to an [`OsString`]. +//! //! On Windows, [`OsStr`] implements the //! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait, //! which provides an [`encode_wide`] method. This provides an -//! iterator that can be [`collect`]ed into a vector of [`u16`]. +//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul +//! characters is appended, this is the same as a native Windows string. //! //! Additionally, on Windows [`OsString`] implements the //! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code> -//! trait, which provides a [`from_wide`] method. The result of this -//! method is an [`OsString`] which can be round-tripped to a Windows -//! string losslessly. +//! trait, which provides a [`from_wide`] method to convert a native Windows +//! string (without the terminating nul character) to an [`OsString`]. //! //! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value //! [Unicode code point]: https://www.unicode.org/glossary/#code_point diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index a14f1e2ecb2..dae85027b6c 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -13,7 +13,7 @@ mod tests; use crate::ffi::OsString; use crate::fmt; -use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write}; use crate::path::{Path, PathBuf}; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; @@ -623,15 +623,13 @@ impl Read for File { self.inner.read_vectored(bufs) } - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + self.inner.read_buf(buf) } #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() } // Reserves space in the buffer based on the file size when available. @@ -677,6 +675,10 @@ impl Read for &File { self.inner.read(buf) } + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + self.inner.read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.inner.read_vectored(bufs) } @@ -686,12 +688,6 @@ impl Read for &File { self.inner.is_read_vectored() } - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } - // Reserves space in the buffer based on the file size when available. fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { buf.reserve(buffer_capacity_required(self)); @@ -1062,7 +1058,7 @@ impl Metadata { /// } /// ``` #[must_use] - #[stable(feature = "is_symlink", since = "1.57.0")] + #[stable(feature = "is_symlink", since = "1.58.0")] pub fn is_symlink(&self) -> bool { self.file_type().is_symlink() } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 9a8f1e44f1f..749d51d4981 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -38,10 +38,9 @@ macro_rules! error { ($e:expr, $s:expr) => { match $e { Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), - Err(ref err) => assert!( - err.raw_os_error() == Some($s), - format!("`{}` did not have a code of `{}`", err, $s) - ), + Err(ref err) => { + assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s) + } } }; } @@ -58,7 +57,7 @@ macro_rules! error_contains { match $e { Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), Err(ref err) => { - assert!(err.to_string().contains($s), format!("`{}` did not contain `{}`", err, $s)) + assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s) } } }; @@ -1369,7 +1368,7 @@ fn symlink_hard_link() { // "hard_link" should appear as a symlink. assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); - // We sould be able to open "file" via any of the above names. + // We should be able to open "file" via any of the above names. let _ = check!(fs::File::open(tmpdir.join("file"))); assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); let _ = check!(fs::File::open(tmpdir.join("symlink"))); diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 2864e94f60f..b56dc65f0b2 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -1,8 +1,9 @@ use crate::cmp; use crate::fmt; use crate::io::{ - self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, + self, BufRead, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, }; +use crate::mem::MaybeUninit; /// The `BufReader<R>` struct adds buffering to any reader. /// @@ -47,9 +48,10 @@ use crate::io::{ #[stable(feature = "rust1", since = "1.0.0")] pub struct BufReader<R> { inner: R, - buf: Box<[u8]>, + buf: Box<[MaybeUninit<u8>]>, pos: usize, cap: usize, + init: usize, } impl<R: Read> BufReader<R> { @@ -91,11 +93,8 @@ impl<R: Read> BufReader<R> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> { - unsafe { - let mut buf = Box::new_uninit_slice(capacity).assume_init(); - inner.initializer().initialize(&mut buf); - BufReader { inner, buf, pos: 0, cap: 0 } - } + let buf = Box::new_uninit_slice(capacity); + BufReader { inner, buf, pos: 0, cap: 0, init: 0 } } } @@ -171,7 +170,8 @@ impl<R> BufReader<R> { /// ``` #[stable(feature = "bufreader_buffer", since = "1.37.0")] pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.cap] + // SAFETY: self.cap is always <= self.init, so self.buf[self.pos..self.cap] is always init + unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[self.pos..self.cap]) } } /// Returns the number of bytes the internal buffer can hold at once. @@ -271,6 +271,25 @@ impl<R: Read> Read for BufReader<R> { Ok(nread) } + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.pos == self.cap && buf.remaining() >= self.buf.len() { + self.discard_buffer(); + return self.inner.read_buf(buf); + } + + let prev = buf.filled_len(); + + let mut rem = self.fill_buf()?; + rem.read_buf(buf)?; + + self.consume(buf.filled_len() - prev); //slice impl of read_buf known to never unfill buf + + Ok(()) + } + // Small read_exacts from a BufReader are extremely common when used with a deserializer. // The default implementation calls read in a loop, which results in surprisingly poor code // generation for the common path where the buffer has enough bytes to fill the passed-in @@ -303,16 +322,11 @@ impl<R: Read> Read for BufReader<R> { self.inner.is_read_vectored() } - // we can't skip unconditionally because of the large buffer case in read. - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } - // The inner reader might have an optimized `read_to_end`. Drain our buffer and then // delegate to the inner implementation. fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { let nread = self.cap - self.pos; - buf.extend_from_slice(&self.buf[self.pos..self.cap]); + buf.extend_from_slice(&self.buffer()); self.discard_buffer(); Ok(nread + self.inner.read_to_end(buf)?) } @@ -363,10 +377,23 @@ impl<R: Read> BufRead for BufReader<R> { // to tell the compiler that the pos..cap slice is always valid. if self.pos >= self.cap { debug_assert!(self.pos == self.cap); - self.cap = self.inner.read(&mut self.buf)?; + + let mut readbuf = ReadBuf::uninit(&mut self.buf); + + // SAFETY: `self.init` is either 0 or set to `readbuf.initialized_len()` + // from the last time this function was called + unsafe { + readbuf.assume_init(self.init); + } + + self.inner.read_buf(&mut readbuf)?; + + self.cap = readbuf.filled_len(); + self.init = readbuf.initialized_len(); + self.pos = 0; } - Ok(&self.buf[self.pos..self.cap]) + Ok(self.buffer()) } fn consume(&mut self, amt: usize) { diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index feb149c07a5..9d429e7090e 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -1,5 +1,6 @@ use crate::io::prelude::*; -use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom}; +use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, ReadBuf, SeekFrom}; +use crate::mem::MaybeUninit; use crate::panic; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::thread; @@ -56,6 +57,55 @@ fn test_buffered_reader() { } #[test] +fn test_buffered_reader_read_buf() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [MaybeUninit::uninit(); 3]; + let mut buf = ReadBuf::uninit(&mut buf); + + reader.read_buf(&mut buf).unwrap(); + + assert_eq!(buf.filled(), [5, 6, 7]); + assert_eq!(reader.buffer(), []); + + let mut buf = [MaybeUninit::uninit(); 2]; + let mut buf = ReadBuf::uninit(&mut buf); + + reader.read_buf(&mut buf).unwrap(); + + assert_eq!(buf.filled(), [0, 1]); + assert_eq!(reader.buffer(), []); + + let mut buf = [MaybeUninit::uninit(); 1]; + let mut buf = ReadBuf::uninit(&mut buf); + + reader.read_buf(&mut buf).unwrap(); + + assert_eq!(buf.filled(), [2]); + assert_eq!(reader.buffer(), [3]); + + let mut buf = [MaybeUninit::uninit(); 3]; + let mut buf = ReadBuf::uninit(&mut buf); + + reader.read_buf(&mut buf).unwrap(); + + assert_eq!(buf.filled(), [3]); + assert_eq!(reader.buffer(), []); + + reader.read_buf(&mut buf).unwrap(); + + assert_eq!(buf.filled(), [3, 4]); + assert_eq!(reader.buffer(), []); + + buf.clear(); + + reader.read_buf(&mut buf).unwrap(); + + assert_eq!(buf.filled_len(), 0); +} + +#[test] fn test_buffered_reader_seek() { let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index eb60df214c4..6ab96662305 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,4 +1,4 @@ -use super::{BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; +use super::{BufWriter, ErrorKind, Read, ReadBuf, Result, Write, DEFAULT_BUF_SIZE}; use crate::mem::MaybeUninit; /// Copies the entire contents of a reader into a writer. @@ -82,33 +82,30 @@ impl<I: Write> BufferedCopySpec for BufWriter<I> { return stack_buffer_copy(reader, writer); } - // FIXME: #42788 - // - // - This creates a (mut) reference to a slice of - // _uninitialized_ integers, which is **undefined behavior** - // - // - Only the standard library gets to soundly "ignore" this, - // based on its privileged knowledge of unstable rustc - // internals; - unsafe { - let spare_cap = writer.buffer_mut().spare_capacity_mut(); - reader.initializer().initialize(MaybeUninit::slice_assume_init_mut(spare_cap)); - } - let mut len = 0; + let mut init = 0; loop { let buf = writer.buffer_mut(); - let spare_cap = buf.spare_capacity_mut(); - - if spare_cap.len() >= DEFAULT_BUF_SIZE { - match reader.read(unsafe { MaybeUninit::slice_assume_init_mut(spare_cap) }) { - Ok(0) => return Ok(len), // EOF reached - Ok(bytes_read) => { - assert!(bytes_read <= spare_cap.len()); - // SAFETY: The initializer contract guarantees that either it or `read` - // will have initialized these bytes. And we just checked that the number - // of bytes is within the buffer capacity. + let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut()); + + // SAFETY: init is either 0 or the initialized_len of the previous iteration + unsafe { + read_buf.assume_init(init); + } + + if read_buf.capacity() >= DEFAULT_BUF_SIZE { + match reader.read_buf(&mut read_buf) { + Ok(()) => { + let bytes_read = read_buf.filled_len(); + + if bytes_read == 0 { + return Ok(len); + } + + init = read_buf.initialized_len() - bytes_read; + + // SAFETY: ReadBuf guarantees all of its filled bytes are init unsafe { buf.set_len(buf.len() + bytes_read) }; len += bytes_read as u64; // Read again if the buffer still has enough capacity, as BufWriter itself would do @@ -129,28 +126,26 @@ fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( reader: &mut R, writer: &mut W, ) -> Result<u64> { - let mut buf = MaybeUninit::<[u8; DEFAULT_BUF_SIZE]>::uninit(); - // FIXME: #42788 - // - // - This creates a (mut) reference to a slice of - // _uninitialized_ integers, which is **undefined behavior** - // - // - Only the standard library gets to soundly "ignore" this, - // based on its privileged knowledge of unstable rustc - // internals; - unsafe { - reader.initializer().initialize(buf.assume_init_mut()); - } + let mut buf = [MaybeUninit::uninit(); DEFAULT_BUF_SIZE]; + let mut buf = ReadBuf::uninit(&mut buf); + + let mut len = 0; - let mut written = 0; loop { - let len = match reader.read(unsafe { buf.assume_init_mut() }) { - Ok(0) => return Ok(written), - Ok(len) => len, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + match reader.read_buf(&mut buf) { + Ok(()) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e), }; - writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?; - written += len as u64; + + if buf.filled().is_empty() { + break; + } + + len += buf.filled().len() as u64; + writer.write_all(buf.filled())?; + buf.clear(); } + + Ok(len) } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 980b2531192..416cc906e65 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,7 +4,7 @@ mod tests; use crate::io::prelude::*; use crate::cmp; -use crate::io::{self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use core::convert::TryInto; @@ -324,6 +324,16 @@ where Ok(n) } + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + let prev_filled = buf.filled_len(); + + Read::read_buf(&mut self.fill_buf()?, buf)?; + + self.pos += (buf.filled_len() - prev_filled) as u64; + + Ok(()) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let mut nread = 0; for buf in bufs { @@ -346,11 +356,6 @@ where self.pos += n as u64; Ok(()) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index d93c6172cfc..210a9ec7183 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -417,6 +417,33 @@ impl Error { Self::_new(kind, error.into()) } + /// Creates a new I/O error from an arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. It is a shortcut for [`Error::new`] + /// with [`ErrorKind::Other`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_error_other)] + /// + /// use std::io::Error; + /// + /// // errors can be created from strings + /// let custom_error = Error::other("oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::other(custom_error); + /// ``` + #[unstable(feature = "io_error_other", issue = "91946")] + pub fn other<E>(error: E) -> Error + where + E: Into<Box<dyn error::Error + Send + Sync>>, + { + Self::_new(ErrorKind::Other, error.into()) + } + fn _new(kind: ErrorKind, error: Box<dyn error::Error + Send + Sync>) -> Error { Error { repr: Repr::Custom(Box::new(Custom { kind, error })) } } @@ -440,12 +467,18 @@ impl Error { /// `GetLastError` on Windows) and will return a corresponding instance of /// [`Error`] for the error code. /// + /// This should be called immediately after a call to a platform function, + /// otherwise the state of the error value is indeterminate. In particular, + /// other standard library functions may call platform functions that may + /// (or may not) reset the error value even if they succeed. + /// /// # Examples /// /// ``` /// use std::io::Error; /// - /// println!("last OS error: {:?}", Error::last_os_error()); + /// let os_error = Error::last_os_error(); + /// println!("last OS error: {:?}", os_error); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use] diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 7a2a49ba7d7..23201f9fc5c 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -5,7 +5,7 @@ use crate::alloc::Allocator; use crate::cmp; use crate::fmt; use crate::io::{ - self, BufRead, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write, + self, BufRead, Error, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write, }; use crate::mem; @@ -20,6 +20,11 @@ impl<R: Read + ?Sized> Read for &mut R { } #[inline] + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + (**self).read_buf(buf) + } + + #[inline] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { (**self).read_vectored(bufs) } @@ -30,11 +35,6 @@ impl<R: Read + ?Sized> Read for &mut R { } #[inline] - unsafe fn initializer(&self) -> Initializer { - (**self).initializer() - } - - #[inline] fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { (**self).read_to_end(buf) } @@ -124,6 +124,11 @@ impl<R: Read + ?Sized> Read for Box<R> { } #[inline] + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + (**self).read_buf(buf) + } + + #[inline] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { (**self).read_vectored(bufs) } @@ -134,11 +139,6 @@ impl<R: Read + ?Sized> Read for Box<R> { } #[inline] - unsafe fn initializer(&self) -> Initializer { - (**self).initializer() - } - - #[inline] fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { (**self).read_to_end(buf) } @@ -248,6 +248,17 @@ impl Read for &[u8] { } #[inline] + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + let amt = cmp::min(buf.remaining(), self.len()); + let (a, b) = self.split_at(amt); + + buf.append(a); + + *self = b; + Ok(()) + } + + #[inline] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let mut nread = 0; for buf in bufs { @@ -266,11 +277,6 @@ impl Read for &[u8] { } #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } - - #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { return Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer")); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 8cc91566418..ecc9e91b6bd 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -256,7 +256,6 @@ use crate::convert::TryInto; use crate::fmt; use crate::mem::replace; use crate::ops::{Deref, DerefMut}; -use crate::ptr; use crate::slice; use crate::str; use crate::sys; @@ -288,12 +287,16 @@ pub use self::stdio::{_eprint, _print}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink}; +#[unstable(feature = "read_buf", issue = "78485")] +pub use self::readbuf::ReadBuf; + mod buffered; pub(crate) mod copy; mod cursor; mod error; mod impls; pub mod prelude; +mod readbuf; mod stdio; mod util; @@ -355,52 +358,43 @@ where // of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every // time is 4,500 times (!) slower than a default reservation size of 32 if the // reader has a very small amount of data to return. -// -// Because we're extending the buffer with uninitialized data for trusted -// readers, we need to make sure to truncate that if any of this panics. pub(crate) fn default_read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> { let start_len = buf.len(); let start_cap = buf.capacity(); - let mut g = Guard { len: buf.len(), buf }; + + let mut initialized = 0; // Extra initialized bytes from previous loop iteration loop { - // If we've read all the way up to the capacity, reserve more space. - if g.len == g.buf.capacity() { - g.buf.reserve(32); + if buf.len() == buf.capacity() { + buf.reserve(32); // buf is full, need more space } - // Initialize any excess capacity and adjust the length so we can write - // to it. - if g.buf.len() < g.buf.capacity() { - unsafe { - // FIXME(danielhenrymantilla): #42788 - // - // - This creates a (mut) reference to a slice of - // _uninitialized_ integers, which is **undefined behavior** - // - // - Only the standard library gets to soundly "ignore" this, - // based on its privileged knowledge of unstable rustc - // internals; - let capacity = g.buf.capacity(); - g.buf.set_len(capacity); - r.initializer().initialize(&mut g.buf[g.len..]); - } + let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut()); + + // SAFETY: These bytes were initialized but not filled in the previous loop + unsafe { + read_buf.assume_init(initialized); } - let buf = &mut g.buf[g.len..]; - match r.read(buf) { - Ok(0) => return Ok(g.len - start_len), - Ok(n) => { - // We can't allow bogus values from read. If it is too large, the returned vec could have its length - // set past its capacity, or if it overflows the vec could be shortened which could create an invalid - // string if this is called via read_to_string. - assert!(n <= buf.len()); - g.len += n; - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + match r.read_buf(&mut read_buf) { + Ok(()) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e), } - if g.len == g.buf.capacity() && g.buf.capacity() == start_cap { + if read_buf.filled_len() == 0 { + return Ok(buf.len() - start_len); + } + + // store how much was initialized but not filled + initialized = read_buf.initialized_len() - read_buf.filled_len(); + let new_len = read_buf.filled_len() + buf.len(); + + // SAFETY: ReadBuf's invariants mean this much memory is init + unsafe { + buf.set_len(new_len); + } + + if buf.len() == buf.capacity() && buf.capacity() == start_cap { // The buffer might be an exact fit. Let's read into a probe buffer // and see if it returns `Ok(0)`. If so, we've avoided an // unnecessary doubling of the capacity. But if not, append the @@ -409,10 +403,9 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8> loop { match r.read(&mut probe) { - Ok(0) => return Ok(g.len - start_len), + Ok(0) => return Ok(buf.len() - start_len), Ok(n) => { - g.buf.extend_from_slice(&probe[..n]); - g.len += n; + buf.extend_from_slice(&probe[..n]); break; } Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, @@ -474,6 +467,15 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ } } +pub(crate) fn default_read_buf<F>(read: F, buf: &mut ReadBuf<'_>) -> Result<()> +where + F: FnOnce(&mut [u8]) -> Result<usize>, +{ + let n = read(buf.initialize_unfilled())?; + buf.add_filled(n); + Ok(()) +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -656,31 +658,6 @@ pub trait Read { false } - /// Determines if this `Read`er can work with buffers of uninitialized - /// memory. - /// - /// The default implementation returns an initializer which will zero - /// buffers. - /// - /// If a `Read`er guarantees that it can work properly with uninitialized - /// memory, it should call [`Initializer::nop()`]. See the documentation for - /// [`Initializer`] for details. - /// - /// The behavior of this method must be independent of the state of the - /// `Read`er - the method only takes `&self` so that it can be used through - /// trait objects. - /// - /// # Safety - /// - /// This method is unsafe because a `Read`er could otherwise return a - /// non-zeroing `Initializer` from another `Read` type without an `unsafe` - /// block. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::zeroing() - } - /// Read all bytes until EOF in this source, placing them into `buf`. /// /// All bytes read from this source will be appended to the specified buffer @@ -830,7 +807,40 @@ pub trait Read { default_read_exact(self, buf) } - /// Creates a "by reference" adapter for this instance of `Read`. + /// Pull some bytes from this source into the specified buffer. + /// + /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`ReadBuf`] rather than `[u8]` to allow use + /// with uninitialized buffers. The new data will be appended to any existing contents of `buf`. + /// + /// The default implementation delegates to `read`. + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> { + default_read_buf(|b| self.read(b), buf) + } + + /// Read the exact number of bytes required to fill `buf`. + /// + /// This is equivalent to the [`read_exact`](Read::read_exact) method, except that it is passed a [`ReadBuf`] rather than `[u8]` to + /// allow use with uninitialized buffers. + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf_exact(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> { + while buf.remaining() > 0 { + let prev_filled = buf.filled().len(); + match self.read_buf(buf) { + Ok(()) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + + if buf.filled().len() == prev_filled { + return Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill buffer")); + } + } + + Ok(()) + } + + /// Creates a "by reference" adaptor for this instance of `Read`. /// /// The returned adapter also implements `Read` and will simply borrow this /// current reader. @@ -1300,53 +1310,6 @@ impl<'a> Deref for IoSlice<'a> { } } -/// A type used to conditionally initialize buffers passed to `Read` methods. -#[unstable(feature = "read_initializer", issue = "42788")] -#[derive(Debug)] -pub struct Initializer(bool); - -impl Initializer { - /// Returns a new `Initializer` which will zero out buffers. - #[unstable(feature = "read_initializer", issue = "42788")] - #[must_use] - #[inline] - pub fn zeroing() -> Initializer { - Initializer(true) - } - - /// Returns a new `Initializer` which will not zero out buffers. - /// - /// # Safety - /// - /// This may only be called by `Read`ers which guarantee that they will not - /// read from buffers passed to `Read` methods, and that the return value of - /// the method accurately reflects the number of bytes that have been - /// written to the head of the buffer. - #[unstable(feature = "read_initializer", issue = "42788")] - #[must_use] - #[inline] - pub unsafe fn nop() -> Initializer { - Initializer(false) - } - - /// Indicates if a buffer should be initialized. - #[unstable(feature = "read_initializer", issue = "42788")] - #[must_use] - #[inline] - pub fn should_initialize(&self) -> bool { - self.0 - } - - /// Initializes a buffer if necessary. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - pub fn initialize(&self, buf: &mut [u8]) { - if self.should_initialize() { - unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } - } - } -} - /// A trait for objects which are byte-oriented sinks. /// /// Implementors of the `Write` trait are sometimes called 'writers'. @@ -2403,11 +2366,6 @@ impl<T: Read, U: Read> Read for Chain<T, U> { } self.second.read_vectored(bufs) } - - unsafe fn initializer(&self) -> Initializer { - let initializer = self.first.initializer(); - if initializer.should_initialize() { initializer } else { self.second.initializer() } - } } #[stable(feature = "chain_bufread", since = "1.9.0")] @@ -2610,8 +2568,53 @@ impl<T: Read> Read for Take<T> { Ok(n) } - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(()); + } + + let prev_filled = buf.filled_len(); + + if self.limit <= buf.remaining() as u64 { + // if we just use an as cast to convert, limit may wrap around on a 32 bit target + let limit = cmp::min(self.limit, usize::MAX as u64) as usize; + + let extra_init = cmp::min(limit as usize, buf.initialized_len() - buf.filled_len()); + + // SAFETY: no uninit data is written to ibuf + let ibuf = unsafe { &mut buf.unfilled_mut()[..limit] }; + + let mut sliced_buf = ReadBuf::uninit(ibuf); + + // SAFETY: extra_init bytes of ibuf are known to be initialized + unsafe { + sliced_buf.assume_init(extra_init); + } + + self.inner.read_buf(&mut sliced_buf)?; + + let new_init = sliced_buf.initialized_len(); + let filled = sliced_buf.filled_len(); + + // sliced_buf / ibuf must drop here + + // SAFETY: new_init bytes of buf's unfilled buffer have been initialized + unsafe { + buf.assume_init(new_init); + } + + buf.add_filled(filled); + + self.limit -= filled as u64; + } else { + self.inner.read_buf(buf)?; + + //inner may unfill + self.limit -= buf.filled_len().saturating_sub(prev_filled) as u64; + } + + Ok(()) } } diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs new file mode 100644 index 00000000000..d84a500e078 --- /dev/null +++ b/library/std/src/io/readbuf.rs @@ -0,0 +1,245 @@ +#![unstable(feature = "read_buf", issue = "78485")] + +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::fmt::{self, Debug, Formatter}; +use crate::mem::MaybeUninit; + +/// A wrapper around a byte buffer that is incrementally filled and initialized. +/// +/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the +/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet +/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a +/// subset of the initialized region. +/// +/// In summary, the contents of the buffer can be visualized as: +/// ```not_rust +/// [ capacity ] +/// [ filled | unfilled ] +/// [ initialized | uninitialized ] +/// ``` +pub struct ReadBuf<'a> { + buf: &'a mut [MaybeUninit<u8>], + filled: usize, + initialized: usize, +} + +impl Debug for ReadBuf<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadBuf") + .field("init", &self.initialized()) + .field("filled", &self.filled) + .field("capacity", &self.capacity()) + .finish() + } +} + +impl<'a> ReadBuf<'a> { + /// Creates a new `ReadBuf` from a fully initialized buffer. + #[inline] + pub fn new(buf: &'a mut [u8]) -> ReadBuf<'a> { + let len = buf.len(); + + ReadBuf { + //SAFETY: initialized data never becoming uninitialized is an invariant of ReadBuf + buf: unsafe { (buf as *mut [u8]).as_uninit_slice_mut().unwrap() }, + filled: 0, + initialized: len, + } + } + + /// Creates a new `ReadBuf` from a fully uninitialized buffer. + /// + /// Use `assume_init` if part of the buffer is known to be already inintialized. + #[inline] + pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> { + ReadBuf { buf, filled: 0, initialized: 0 } + } + + /// Returns the total capacity of the buffer. + #[inline] + pub fn capacity(&self) -> usize { + self.buf.len() + } + + /// Returns a shared reference to the filled portion of the buffer. + #[inline] + pub fn filled(&self) -> &[u8] { + //SAFETY: We only slice the filled part of the buffer, which is always valid + unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) } + } + + /// Returns a mutable reference to the filled portion of the buffer. + #[inline] + pub fn filled_mut(&mut self) -> &mut [u8] { + //SAFETY: We only slice the filled part of the buffer, which is always valid + unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) } + } + + /// Returns a shared reference to the initialized portion of the buffer. + /// + /// This includes the filled portion. + #[inline] + pub fn initialized(&self) -> &[u8] { + //SAFETY: We only slice the initialized part of the buffer, which is always valid + unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.initialized]) } + } + + /// Returns a mutable reference to the initialized portion of the buffer. + /// + /// This includes the filled portion. + #[inline] + pub fn initialized_mut(&mut self) -> &mut [u8] { + //SAFETY: We only slice the initialized part of the buffer, which is always valid + unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.initialized]) } + } + + /// Returns a mutable reference to the unfilled part of the buffer without ensuring that it has been fully + /// initialized. + /// + /// # Safety + /// + /// The caller must not de-initialize portions of the buffer that have already been initialized. + #[inline] + pub unsafe fn unfilled_mut(&mut self) -> &mut [MaybeUninit<u8>] { + &mut self.buf[self.filled..] + } + + /// Returns a mutable reference to the uninitialized part of the buffer. + /// + /// It is safe to uninitialize any of these bytes. + #[inline] + pub fn uninitialized_mut(&mut self) -> &mut [MaybeUninit<u8>] { + &mut self.buf[self.initialized..] + } + + /// Returns a mutable reference to the unfilled part of the buffer, ensuring it is fully initialized. + /// + /// Since `ReadBuf` tracks the region of the buffer that has been initialized, this is effectively "free" after + /// the first use. + #[inline] + pub fn initialize_unfilled(&mut self) -> &mut [u8] { + // should optimize out the assertion + self.initialize_unfilled_to(self.remaining()) + } + + /// Returns a mutable reference to the first `n` bytes of the unfilled part of the buffer, ensuring it is + /// fully initialized. + /// + /// # Panics + /// + /// Panics if `self.remaining()` is less than `n`. + #[inline] + pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] { + assert!(self.remaining() >= n); + + let extra_init = self.initialized - self.filled; + // If we don't have enough initialized, do zeroing + if n > extra_init { + let uninit = n - extra_init; + let unfilled = &mut self.uninitialized_mut()[0..uninit]; + + for byte in unfilled.iter_mut() { + byte.write(0); + } + + // SAFETY: we just inintialized uninit bytes, and the previous bytes were already init + unsafe { + self.assume_init(n); + } + } + + let filled = self.filled; + + &mut self.initialized_mut()[filled..filled + n] + } + + /// Returns the number of bytes at the end of the slice that have not yet been filled. + #[inline] + pub fn remaining(&self) -> usize { + self.capacity() - self.filled + } + + /// Clears the buffer, resetting the filled region to empty. + /// + /// The number of initialized bytes is not changed, and the contents of the buffer are not modified. + #[inline] + pub fn clear(&mut self) { + self.set_filled(0); // The assertion in `set_filled` is optimized out + } + + /// Increases the size of the filled region of the buffer. + /// + /// The number of initialized bytes is not changed. + /// + /// # Panics + /// + /// Panics if the filled region of the buffer would become larger than the initialized region. + #[inline] + pub fn add_filled(&mut self, n: usize) { + self.set_filled(self.filled + n); + } + + /// Sets the size of the filled region of the buffer. + /// + /// The number of initialized bytes is not changed. + /// + /// Note that this can be used to *shrink* the filled region of the buffer in addition to growing it (for + /// example, by a `Read` implementation that compresses data in-place). + /// + /// # Panics + /// + /// Panics if the filled region of the buffer would become larger than the initialized region. + #[inline] + pub fn set_filled(&mut self, n: usize) { + assert!(n <= self.initialized); + + self.filled = n; + } + + /// Asserts that the first `n` unfilled bytes of the buffer are initialized. + /// + /// `ReadBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer + /// bytes than are already known to be initialized. + /// + /// # Safety + /// + /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized. + #[inline] + pub unsafe fn assume_init(&mut self, n: usize) { + self.initialized = cmp::max(self.initialized, self.filled + n); + } + + /// Appends data to the buffer, advancing the written position and possibly also the initialized position. + /// + /// # Panics + /// + /// Panics if `self.remaining()` is less than `buf.len()`. + #[inline] + pub fn append(&mut self, buf: &[u8]) { + assert!(self.remaining() >= buf.len()); + + // SAFETY: we do not de-initialize any of the elements of the slice + unsafe { + MaybeUninit::write_slice(&mut self.unfilled_mut()[..buf.len()], buf); + } + + // SAFETY: We just added the entire contents of buf to the filled section. + unsafe { self.assume_init(buf.len()) } + self.add_filled(buf.len()); + } + + /// Returns the amount of bytes that have been filled. + #[inline] + pub fn filled_len(&self) -> usize { + self.filled + } + + /// Returns the amount of bytes that have been initialized. + #[inline] + pub fn initialized_len(&self) -> usize { + self.initialized + } +} diff --git a/library/std/src/io/readbuf/tests.rs b/library/std/src/io/readbuf/tests.rs new file mode 100644 index 00000000000..3b7a5a56d22 --- /dev/null +++ b/library/std/src/io/readbuf/tests.rs @@ -0,0 +1,181 @@ +use super::ReadBuf; +use crate::mem::MaybeUninit; + +/// Test that ReadBuf has the correct numbers when created with new +#[test] +fn new() { + let mut buf = [0; 16]; + let rbuf = ReadBuf::new(&mut buf); + + assert_eq!(rbuf.filled_len(), 0); + assert_eq!(rbuf.initialized_len(), 16); + assert_eq!(rbuf.capacity(), 16); + assert_eq!(rbuf.remaining(), 16); +} + +/// Test that ReadBuf has the correct numbers when created with uninit +#[test] +fn uninit() { + let mut buf = [MaybeUninit::uninit(); 16]; + let rbuf = ReadBuf::uninit(&mut buf); + + assert_eq!(rbuf.filled_len(), 0); + assert_eq!(rbuf.initialized_len(), 0); + assert_eq!(rbuf.capacity(), 16); + assert_eq!(rbuf.remaining(), 16); +} + +#[test] +fn initialize_unfilled() { + let mut buf = [MaybeUninit::uninit(); 16]; + let mut rbuf = ReadBuf::uninit(&mut buf); + + rbuf.initialize_unfilled(); + + assert_eq!(rbuf.initialized_len(), 16); +} + +#[test] +fn initialize_unfilled_to() { + let mut buf = [MaybeUninit::uninit(); 16]; + let mut rbuf = ReadBuf::uninit(&mut buf); + + rbuf.initialize_unfilled_to(8); + + assert_eq!(rbuf.initialized_len(), 8); + + rbuf.initialize_unfilled_to(4); + + assert_eq!(rbuf.initialized_len(), 8); + + rbuf.set_filled(8); + + rbuf.initialize_unfilled_to(6); + + assert_eq!(rbuf.initialized_len(), 14); + + rbuf.initialize_unfilled_to(8); + + assert_eq!(rbuf.initialized_len(), 16); +} + +#[test] +fn add_filled() { + let mut buf = [0; 16]; + let mut rbuf = ReadBuf::new(&mut buf); + + rbuf.add_filled(1); + + assert_eq!(rbuf.filled_len(), 1); + assert_eq!(rbuf.remaining(), 15); +} + +#[test] +#[should_panic] +fn add_filled_panic() { + let mut buf = [MaybeUninit::uninit(); 16]; + let mut rbuf = ReadBuf::uninit(&mut buf); + + rbuf.add_filled(1); +} + +#[test] +fn set_filled() { + let mut buf = [0; 16]; + let mut rbuf = ReadBuf::new(&mut buf); + + rbuf.set_filled(16); + + assert_eq!(rbuf.filled_len(), 16); + assert_eq!(rbuf.remaining(), 0); + + rbuf.set_filled(6); + + assert_eq!(rbuf.filled_len(), 6); + assert_eq!(rbuf.remaining(), 10); +} + +#[test] +#[should_panic] +fn set_filled_panic() { + let mut buf = [MaybeUninit::uninit(); 16]; + let mut rbuf = ReadBuf::uninit(&mut buf); + + rbuf.set_filled(16); +} + +#[test] +fn clear() { + let mut buf = [255; 16]; + let mut rbuf = ReadBuf::new(&mut buf); + + rbuf.set_filled(16); + + assert_eq!(rbuf.filled_len(), 16); + assert_eq!(rbuf.remaining(), 0); + + rbuf.clear(); + + assert_eq!(rbuf.filled_len(), 0); + assert_eq!(rbuf.remaining(), 16); + + assert_eq!(rbuf.initialized(), [255; 16]); +} + +#[test] +fn assume_init() { + let mut buf = [MaybeUninit::uninit(); 16]; + let mut rbuf = ReadBuf::uninit(&mut buf); + + unsafe { + rbuf.assume_init(8); + } + + assert_eq!(rbuf.initialized_len(), 8); + + rbuf.add_filled(4); + + unsafe { + rbuf.assume_init(2); + } + + assert_eq!(rbuf.initialized_len(), 8); + + unsafe { + rbuf.assume_init(8); + } + + assert_eq!(rbuf.initialized_len(), 12); +} + +#[test] +fn append() { + let mut buf = [MaybeUninit::new(255); 16]; + let mut rbuf = ReadBuf::uninit(&mut buf); + + rbuf.append(&[0; 8]); + + assert_eq!(rbuf.initialized_len(), 8); + assert_eq!(rbuf.filled_len(), 8); + assert_eq!(rbuf.filled(), [0; 8]); + + rbuf.clear(); + + rbuf.append(&[1; 16]); + + assert_eq!(rbuf.initialized_len(), 16); + assert_eq!(rbuf.filled_len(), 16); + assert_eq!(rbuf.filled(), [1; 16]); +} + +#[test] +fn filled_mut() { + let mut buf = [0; 16]; + let mut rbuf = ReadBuf::new(&mut buf); + + rbuf.add_filled(8); + + let filled = rbuf.filled().to_vec(); + + assert_eq!(&*filled, &*rbuf.filled_mut()); +} diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index f7fc23c1e82..c072f0cafe4 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -7,7 +7,7 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; -use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter, Lines, Split}; +use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, Split}; use crate::lazy::SyncOnceCell; use crate::pin::Pin; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -108,11 +108,6 @@ impl Read for StdinRaw { self.0.is_read_vectored() } - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { handle_ebadf(self.0.read_to_end(buf), 0) } @@ -514,10 +509,6 @@ impl Read for Stdin { fn is_read_vectored(&self) -> bool { self.lock().is_read_vectored() } - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { self.lock().read_to_end(buf) } @@ -552,11 +543,6 @@ impl Read for StdinLock<'_> { self.inner.is_read_vectored() } - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { self.inner.read_to_end(buf) } @@ -1193,7 +1179,7 @@ where }) }) == Ok(Some(())) { - // Succesfully wrote to capture buffer. + // Successfully wrote to capture buffer. return; } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 0321a2b60b1..ea49bfe3421 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,7 +1,8 @@ -use super::{repeat, Cursor, SeekFrom}; +use super::{repeat, Cursor, ReadBuf, SeekFrom}; use crate::cmp::{self, min}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::io::{BufRead, BufReader, Read, Seek, Write}; +use crate::mem::MaybeUninit; use crate::ops::Deref; #[test] @@ -157,6 +158,28 @@ fn read_exact_slice() { } #[test] +fn read_buf_exact() { + let mut buf = [0; 4]; + let mut buf = ReadBuf::new(&mut buf); + + let mut c = Cursor::new(&b""[..]); + assert_eq!(c.read_buf_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + + let mut c = Cursor::new(&b"123456789"[..]); + c.read_buf_exact(&mut buf).unwrap(); + assert_eq!(buf.filled(), b"1234"); + + buf.clear(); + + c.read_buf_exact(&mut buf).unwrap(); + assert_eq!(buf.filled(), b"5678"); + + buf.clear(); + + assert_eq!(c.read_buf_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); +} + +#[test] fn take_eof() { struct R; @@ -559,3 +582,23 @@ fn test_write_all_vectored() { } } } + +#[bench] +fn bench_take_read(b: &mut test::Bencher) { + b.iter(|| { + let mut buf = [0; 64]; + + [255; 128].take(64).read(&mut buf).unwrap(); + }); +} + +#[bench] +fn bench_take_read_buf(b: &mut test::Bencher) { + b.iter(|| { + let mut buf = [MaybeUninit::uninit(); 64]; + + let mut rbuf = ReadBuf::uninit(&mut buf); + + [255; 128].take(64).read_buf(&mut rbuf).unwrap(); + }); +} diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 9cd7c514849..c1300cd67c0 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -5,7 +5,7 @@ mod tests; use crate::fmt; use crate::io::{ - self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, + self, BufRead, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, SizeHint, Write, }; /// A reader which is always at EOF. @@ -47,8 +47,8 @@ impl Read for Empty { } #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() + fn read_buf(&mut self, _buf: &mut ReadBuf<'_>) -> io::Result<()> { + Ok(()) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -130,6 +130,24 @@ impl Read for Repeat { Ok(buf.len()) } + fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + // SAFETY: No uninit bytes are being written + for slot in unsafe { buf.unfilled_mut() } { + slot.write(self.byte); + } + + let remaining = buf.remaining(); + + // SAFETY: the entire unfilled portion of buf has been initialized + unsafe { + buf.assume_init(remaining); + } + + buf.add_filled(remaining); + + Ok(()) + } + #[inline] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let mut nwritten = 0; @@ -143,11 +161,6 @@ impl Read for Repeat { fn is_read_vectored(&self) -> bool { true } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } impl SizeHint for Repeat { diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 7632eaf872a..08972a59a83 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,9 +1,12 @@ use crate::cmp::{max, min}; use crate::io::prelude::*; use crate::io::{ - copy, empty, repeat, sink, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, DEFAULT_BUF_SIZE, + copy, empty, repeat, sink, BufWriter, Empty, ReadBuf, Repeat, Result, SeekFrom, Sink, + DEFAULT_BUF_SIZE, }; +use crate::mem::MaybeUninit; + #[test] fn copy_copies() { let mut r = repeat(0).take(4); @@ -75,6 +78,30 @@ fn empty_reads() { assert_eq!(e.read(&mut [0]).unwrap(), 0); assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); + + let mut buf = []; + let mut buf = ReadBuf::uninit(&mut buf); + e.read_buf(&mut buf).unwrap(); + assert_eq!(buf.filled_len(), 0); + assert_eq!(buf.initialized_len(), 0); + + let mut buf = [MaybeUninit::uninit()]; + let mut buf = ReadBuf::uninit(&mut buf); + e.read_buf(&mut buf).unwrap(); + assert_eq!(buf.filled_len(), 0); + assert_eq!(buf.initialized_len(), 0); + + let mut buf = [MaybeUninit::uninit(); 1024]; + let mut buf = ReadBuf::uninit(&mut buf); + e.read_buf(&mut buf).unwrap(); + assert_eq!(buf.filled_len(), 0); + assert_eq!(buf.initialized_len(), 0); + + let mut buf = [MaybeUninit::uninit(); 1024]; + let mut buf = ReadBuf::uninit(&mut buf); + e.by_ref().read_buf(&mut buf).unwrap(); + assert_eq!(buf.filled_len(), 0); + assert_eq!(buf.initialized_len(), 0); } #[test] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index afd8d8edaa1..22e721d79bf 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -216,10 +216,7 @@ // std may use features in a platform-specific way #![allow(unused_features)] #![feature(rustc_allow_const_fn_unstable)] -#![cfg_attr( - test, - feature(internal_output_capture, print_internals, update_panic_count, thread_local_const_init) -)] +#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) @@ -236,7 +233,6 @@ #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] #![feature(array_error_internals)] -#![feature(asm)] #![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(async_stream)] @@ -253,8 +249,8 @@ #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] #![feature(char_internals)] +#![cfg_attr(not(bootstrap), feature(concat_bytes))] #![feature(concat_idents)] -#![feature(const_cstr_unchecked)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_fn_ptr_basics)] #![feature(const_fn_trait_bound)] @@ -264,8 +260,7 @@ #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(const_option)] -#![cfg_attr(bootstrap, feature(const_raw_ptr_deref))] -#![cfg_attr(not(bootstrap), feature(const_mut_refs))] +#![feature(const_mut_refs)] #![feature(const_socketaddr)] #![feature(const_trait_impl)] #![feature(container_error_extra)] @@ -275,10 +270,9 @@ #![feature(decl_macro)] #![feature(doc_cfg)] #![feature(doc_cfg_hide)] -#![feature(doc_keyword)] +#![feature(rustdoc_internals)] #![feature(doc_masked)] #![feature(doc_notable_trait)] -#![feature(doc_primitive)] #![feature(dropck_eyepatch)] #![feature(duration_checked_float)] #![feature(duration_constants)] @@ -292,14 +286,12 @@ #![feature(gen_future)] #![feature(generator_trait)] #![feature(get_mut_unchecked)] -#![feature(global_asm)] #![feature(hashmap_internals)] #![feature(int_error_internals)] #![feature(integer_atomics)] #![feature(int_log)] #![feature(into_future)] #![feature(intra_doc_pointers)] -#![feature(iter_zip)] #![feature(lang_items)] #![feature(linkage)] #![feature(llvm_asm)] @@ -308,6 +300,7 @@ #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] +#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(mixed_integer_ops)] #![feature(must_not_suspend)] @@ -322,8 +315,9 @@ #![feature(panic_internals)] #![feature(panic_unwind)] #![feature(pin_static_ref)] -#![cfg_attr(not(bootstrap), feature(portable_simd))] +#![feature(portable_simd)] #![feature(prelude_import)] +#![feature(ptr_as_uninit)] #![feature(ptr_internals)] #![feature(rustc_attrs)] #![feature(rustc_private)] @@ -475,7 +469,6 @@ pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[unstable(feature = "portable_simd", issue = "86656")] -#[cfg(not(bootstrap))] pub use core::simd; #[unstable(feature = "async_stream", issue = "79024")] pub use core::stream; @@ -582,6 +575,14 @@ pub use core::{ log_syntax, module_path, option_env, stringify, trace_macros, }; +#[unstable( + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" +)] +#[cfg(not(bootstrap))] +pub use core::concat_bytes; + #[stable(feature = "core_primitive", since = "1.43.0")] pub use core::primitive; diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index 17581f33026..632d4683b41 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -749,7 +749,7 @@ fn ipv4_from_constructors() { } #[test] -fn ipv6_from_contructors() { +fn ipv6_from_constructors() { assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); assert!(Ipv6Addr::LOCALHOST.is_loopback()); assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 5738862fb58..1ba54d892e3 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -6,7 +6,7 @@ mod tests; use crate::io::prelude::*; use crate::fmt; -use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; use crate::sys_common::net as net_imp; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -626,12 +626,6 @@ impl Read for TcpStream { fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } } #[stable(feature = "rust1", since = "1.0.0")] impl Write for TcpStream { @@ -666,12 +660,6 @@ impl Read for &TcpStream { fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &TcpStream { diff --git a/library/std/src/os/fortanix_sgx/arch.rs b/library/std/src/os/fortanix_sgx/arch.rs index 4ce482e23cb..8358cb9e81b 100644 --- a/library/std/src/os/fortanix_sgx/arch.rs +++ b/library/std/src/os/fortanix_sgx/arch.rs @@ -5,6 +5,7 @@ #![unstable(feature = "sgx_platform", issue = "56975")] use crate::mem::MaybeUninit; +use core::arch::asm; /// Wrapper struct to force 16-byte alignment. #[repr(align(16))] diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs index 01392ffab79..f0b38d29845 100644 --- a/library/std/src/os/raw/mod.rs +++ b/library/std/src/os/raw/mod.rs @@ -69,7 +69,8 @@ type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8; target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "riscv64" ) ), all( @@ -112,7 +113,8 @@ type_alias! { "char.md", c_char = i8, NonZero_c_char = NonZeroI8; target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "riscv64" ) ), all( diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 6120d557227..583f861a925 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -11,7 +11,7 @@ use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; use super::{sockaddr_un, SocketAddr}; use crate::fmt; -use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(any( @@ -624,11 +624,6 @@ impl io::Read for UnixStream { fn is_read_vectored(&self) -> bool { io::Read::is_read_vectored(&&*self) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } #[stable(feature = "unix_socket", since = "1.10.0")] @@ -645,11 +640,6 @@ impl<'a> io::Read for &'a UnixStream { fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } #[stable(feature = "unix_socket", since = "1.10.0")] diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index be35ab0ca1e..31d1e3c1e42 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -543,6 +543,16 @@ impl FileTypeExt for fs::FileType { /// Ok(()) /// } /// ``` +/// +/// # Limitations +/// +/// Windows treats symlink creation as a [privileged action][symlink-security], +/// therefore this function is likely to fail unless the user makes changes to +/// their system to permit symlink creation. Users can try enabling Developer +/// Mode, granting the `SeCreateSymbolicLinkPrivilege` privilege, or running +/// the process as an administrator. +/// +/// [symlink-security]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links #[stable(feature = "symlink", since = "1.1.0")] pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> { sys::fs::symlink_inner(original.as_ref(), link.as_ref(), false) @@ -572,6 +582,16 @@ pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io: /// Ok(()) /// } /// ``` +/// +/// # Limitations +/// +/// Windows treats symlink creation as a [privileged action][symlink-security], +/// therefore this function is likely to fail unless the user makes changes to +/// their system to permit symlink creation. Users can try enabling Developer +/// Mode, granting the `SeCreateSymbolicLinkPrivilege` privilege, or running +/// the process as an administrator. +/// +/// [symlink-security]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links #[stable(feature = "symlink", since = "1.1.0")] pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> { sys::fs::symlink_inner(original.as_ref(), link.as_ref(), true) diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 6fc6b8daec0..87854fe4f29 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -365,7 +365,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> // The call to `intrinsics::r#try` is made safe by: // - `do_call`, the first argument, can be called with the initial `data_ptr`. // - `do_catch`, the second argument, can be called with the `data_ptr` as well. - // See their safety preconditions for more informations + // See their safety preconditions for more information unsafe { return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 { Ok(ManuallyDrop::into_inner(data.r)) @@ -398,7 +398,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> // expects normal function pointers. #[inline] fn do_call<F: FnOnce() -> R, R>(data: *mut u8) { - // SAFETY: this is the responsibilty of the caller, see above. + // SAFETY: this is the responsibility of the caller, see above. unsafe { let data = data as *mut Data<F, R>; let data = &mut (*data); @@ -420,7 +420,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> // expects normal function pointers. #[inline] fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) { - // SAFETY: this is the responsibilty of the caller, see above. + // SAFETY: this is the responsibility of the caller, see above. // // When `__rustc_panic_cleaner` is correctly implemented we can rely // on `obj` being the correct thing to pass to `data.p` (after wrapping diff --git a/library/std/src/path.rs b/library/std/src/path.rs index cf2cd5adc48..7d401cff591 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -12,6 +12,13 @@ //! [`PathBuf`]; note that the paths may differ syntactically by the //! normalization described in the documentation for the [`components`] method. //! +//! ## Case sensitivity +//! +//! Unless otherwise indicated path methods that do not access the filesystem, +//! such as [`Path::starts_with`] and [`Path::ends_with`], are case sensitive no +//! matter the platform or filesystem. An exception to this is made for Windows +//! drive letters. +//! //! ## Simple usage //! //! Path manipulation includes both parsing components from slices and building @@ -2810,7 +2817,7 @@ impl Path { /// check errors, call [`fs::symlink_metadata`] and handle its [`Result`]. Then call /// [`fs::Metadata::is_symlink`] if it was [`Ok`]. #[must_use] - #[stable(feature = "is_symlink", since = "1.57.0")] + #[stable(feature = "is_symlink", since = "1.58.0")] pub fn is_symlink(&self) -> bool { fs::symlink_metadata(self).map(|m| m.is_symlink()).unwrap_or(false) } diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 772044f0149..743dd51333d 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -46,20 +46,13 @@ pub use core::prelude::v1::{ }; #[unstable( - feature = "asm", - issue = "72016", - reason = "inline assembly is not stable enough for use and is subject to change" + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" )] +#[cfg(not(bootstrap))] #[doc(no_inline)] -pub use core::prelude::v1::asm; - -#[unstable( - feature = "global_asm", - issue = "35119", - reason = "`global_asm!` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use core::prelude::v1::global_asm; +pub use core::prelude::v1::concat_bytes; // FIXME: Attribute and internal derive macros are not documented because for them rustdoc generates // dead links which fail link checker testing. diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index f47a30c9b5d..8fcd8cdeb10 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -606,8 +606,7 @@ mod prim_pointer {} /// println!("array[{}] = {}", i, x); /// } /// -/// // You can explicitly iterate an array by value using -/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`: +/// // You can explicitly iterate an array by value using `IntoIterator::into_iter` /// for item in IntoIterator::into_iter(array).enumerate() { /// let (i, x): (usize, i32) = item; /// println!("array[{}] = {}", i, x); diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 4e9fd51f282..e012594dd46 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -110,7 +110,7 @@ use crate::convert::Infallible; use crate::ffi::OsStr; use crate::fmt; use crate::fs; -use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::num::NonZeroI32; use crate::path::Path; use crate::str; @@ -362,12 +362,6 @@ impl Read for ChildStdout { fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } } impl AsInner<AnonPipe> for ChildStdout { @@ -429,12 +423,6 @@ impl Read for ChildStderr { fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } } impl AsInner<AnonPipe> for ChildStderr { @@ -1612,7 +1600,6 @@ impl ExitStatusError { /// ``` /// #![feature(exit_status_error)] /// # if cfg!(unix) { - /// use std::convert::TryFrom; /// use std::num::NonZeroI32; /// use std::process::Command; /// diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 094d2efbdd5..67b747e4107 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -4,15 +4,23 @@ use super::{Command, Output, Stdio}; use crate::io::ErrorKind; use crate::str; -// FIXME(#10380) these tests should not all be ignored on android. +#[cfg(target_os = "android")] +fn shell_cmd() -> Command { + Command::new("/system/bin/sh") +} + +#[cfg(not(target_os = "android"))] +fn shell_cmd() -> Command { + Command::new("/bin/sh") +} #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn smoke() { let p = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 0"]).spawn() } else { - Command::new("true").spawn() + shell_cmd().arg("-c").arg("true").spawn() }; assert!(p.is_ok()); let mut p = p.unwrap(); @@ -29,12 +37,12 @@ fn smoke_failure() { } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn exit_reported_right() { let p = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn() } else { - Command::new("false").spawn() + shell_cmd().arg("-c").arg("false").spawn() }; assert!(p.is_ok()); let mut p = p.unwrap(); @@ -44,12 +52,11 @@ fn exit_reported_right() { #[test] #[cfg(unix)] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn signal_reported_right() { use crate::os::unix::process::ExitStatusExt; - let mut p = - Command::new("/bin/sh").arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap(); + let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap(); p.kill().unwrap(); match p.wait().unwrap().signal() { Some(9) => {} @@ -69,31 +76,31 @@ pub fn run_output(mut cmd: Command) -> String { } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn stdout_works() { if cfg!(target_os = "windows") { let mut cmd = Command::new("cmd"); cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped()); assert_eq!(run_output(cmd), "foobar\r\n"); } else { - let mut cmd = Command::new("echo"); - cmd.arg("foobar").stdout(Stdio::piped()); + let mut cmd = shell_cmd(); + cmd.arg("-c").arg("echo foobar").stdout(Stdio::piped()); assert_eq!(run_output(cmd), "foobar\n"); } } #[test] -#[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)] +#[cfg_attr(any(windows, target_os = "vxworks"), ignore)] fn set_current_dir_works() { - let mut cmd = Command::new("/bin/sh"); + let mut cmd = shell_cmd(); cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped()); assert_eq!(run_output(cmd), "/\n"); } #[test] -#[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)] +#[cfg_attr(any(windows, target_os = "vxworks"), ignore)] fn stdin_works() { - let mut p = Command::new("/bin/sh") + let mut p = shell_cmd() .arg("-c") .arg("read line; echo $line") .stdin(Stdio::piped()) @@ -109,19 +116,19 @@ fn stdin_works() { } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn test_process_status() { let mut status = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap() } else { - Command::new("false").status().unwrap() + shell_cmd().arg("-c").arg("false").status().unwrap() }; assert!(status.code() == Some(1)); status = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap() } else { - Command::new("true").status().unwrap() + shell_cmd().arg("-c").arg("true").status().unwrap() }; assert!(status.success()); } @@ -135,12 +142,12 @@ fn test_process_output_fail_to_start() { } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn test_process_output_output() { let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap() } else { - Command::new("echo").arg("hello").output().unwrap() + shell_cmd().arg("-c").arg("echo hello").output().unwrap() }; let output_str = str::from_utf8(&stdout).unwrap(); @@ -150,7 +157,7 @@ fn test_process_output_output() { } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn test_process_output_error() { let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap() @@ -158,41 +165,42 @@ fn test_process_output_error() { Command::new("mkdir").arg("./").output().unwrap() }; - assert!(status.code() == Some(1)); + assert!(status.code().is_some()); + assert!(status.code() != Some(0)); assert_eq!(stdout, Vec::new()); assert!(!stderr.is_empty()); } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn test_finish_once() { let mut prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() } else { - Command::new("false").spawn().unwrap() + shell_cmd().arg("-c").arg("false").spawn().unwrap() }; assert!(prog.wait().unwrap().code() == Some(1)); } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn test_finish_twice() { let mut prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() } else { - Command::new("false").spawn().unwrap() + shell_cmd().arg("-c").arg("false").spawn().unwrap() }; assert!(prog.wait().unwrap().code() == Some(1)); assert!(prog.wait().unwrap().code() == Some(1)); } #[test] -#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +#[cfg_attr(any(target_os = "vxworks"), ignore)] fn test_wait_with_output_once() { let prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap() } else { - Command::new("echo").arg("hello").stdout(Stdio::piped()).spawn().unwrap() + shell_cmd().arg("-c").arg("echo hello").stdout(Stdio::piped()).spawn().unwrap() }; let Output { status, stdout, stderr } = prog.wait_with_output().unwrap(); diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs index c400f5f2c2e..1179a49c22f 100644 --- a/library/std/src/sys/hermit/fd.rs +++ b/library/std/src/sys/hermit/fd.rs @@ -1,6 +1,6 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] -use crate::io::{self, Read}; +use crate::io::{self, Read, ReadBuf}; use crate::mem; use crate::sys::cvt; use crate::sys::hermit::abi; diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index be019d4435d..974c44eb8dd 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -2,7 +2,7 @@ use crate::ffi::{CStr, CString, OsString}; use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; -use crate::io::{IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use crate::os::unix::ffi::OsStrExt; use crate::path::{Path, PathBuf}; use crate::sys::cvt; @@ -312,6 +312,10 @@ impl File { false } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), buf) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 691e7e07902..415cbba101c 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -46,8 +46,17 @@ impl<T> Spinlock<T> { #[inline] fn obtain_lock(&self) { let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; + let mut counter: u16 = 0; while self.dequeue.load(Ordering::SeqCst) != ticket { - hint::spin_loop(); + counter += 1; + if counter < 100 { + hint::spin_loop(); + } else { + counter = 0; + unsafe { + abi::yield_now(); + } + } } } diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs index bb9fa54d02e..ebcc9ab26e0 100644 --- a/library/std/src/sys/itron/thread.rs +++ b/library/std/src/sys/itron/thread.rs @@ -126,7 +126,7 @@ impl Thread { // In this case, `inner`'s ownership has been moved to us, // And we are responsible for dropping it. The acquire // ordering is not necessary because the parent thread made - // no memory acccess needing synchronization since the call + // no memory access needing synchronization since the call // to `acre_tsk`. // Safety: See above. let _ = unsafe { Box::from_raw(inner as *const _ as *mut ThreadInner) }; @@ -264,7 +264,7 @@ impl Drop for Thread { // one will ever join it. // The ownership of `self.inner` is moved to the child thread. // However, the release ordering is not necessary because we - // made no memory acccess needing synchronization since the call + // made no memory access needing synchronization since the call // to `acre_tsk`. } LIFECYCLE_FINISHED => { diff --git a/library/std/src/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs index 52e8bec937c..18e6d5b3fa2 100644 --- a/library/std/src/sys/sgx/abi/mem.rs +++ b/library/std/src/sys/sgx/abi/mem.rs @@ -1,3 +1,5 @@ +use core::arch::asm; + // Do not remove inline: will result in relocation failure #[inline(always)] pub(crate) unsafe fn rel_ptr<T>(offset: u64) -> *const T { diff --git a/library/std/src/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs index 231cc15b849..5df08a4ff59 100644 --- a/library/std/src/sys/sgx/abi/mod.rs +++ b/library/std/src/sys/sgx/abi/mod.rs @@ -1,6 +1,7 @@ #![cfg_attr(test, allow(unused))] // RT initialization logic is not compiled for test use crate::io::Write; +use core::arch::global_asm; use core::sync::atomic::{AtomicUsize, Ordering}; // runtime features diff --git a/library/std/src/sys/solid/abi/mod.rs b/library/std/src/sys/solid/abi/mod.rs index 3205f0db85f..1afc83f766d 100644 --- a/library/std/src/sys/solid/abi/mod.rs +++ b/library/std/src/sys/solid/abi/mod.rs @@ -10,9 +10,9 @@ pub fn breakpoint_program_exited(tid: usize) { match () { // SOLID_BP_PROGRAM_EXITED = 15 #[cfg(target_arch = "arm")] - () => asm!("bkpt #15", in("r0") tid), + () => core::arch::asm!("bkpt #15", in("r0") tid), #[cfg(target_arch = "aarch64")] - () => asm!("hlt #15", in("x0") tid), + () => core::arch::asm!("hlt #15", in("x0") tid), } } } @@ -23,9 +23,9 @@ pub fn breakpoint_abort() { match () { // SOLID_BP_CSABORT = 16 #[cfg(target_arch = "arm")] - () => asm!("bkpt #16"), + () => core::arch::asm!("bkpt #16"), #[cfg(target_arch = "aarch64")] - () => asm!("hlt #16"), + () => core::arch::asm!("hlt #16"), } } } diff --git a/library/std/src/sys/unix/android.rs b/library/std/src/sys/unix/android.rs index 6a46525f682..73ff10ab8a2 100644 --- a/library/std/src/sys/unix/android.rs +++ b/library/std/src/sys/unix/android.rs @@ -18,11 +18,9 @@ #![cfg(target_os = "android")] -use libc::{c_int, c_void, sighandler_t, size_t, ssize_t}; -use libc::{ftruncate, pread, pwrite}; +use libc::{c_int, sighandler_t}; -use super::{cvt, cvt_r, weak::weak}; -use crate::io; +use super::weak::weak; // The `log2` and `log2f` functions apparently appeared in android-18, or at // least you can see they're not present in the android-17 header [1] and they @@ -81,87 +79,3 @@ pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { let f = f.expect("neither `signal` nor `bsd_signal` symbols found"); f(signum, handler) } - -// The `ftruncate64` symbol apparently appeared in android-12, so we do some -// dynamic detection to see if we can figure out whether `ftruncate64` exists. -// -// If it doesn't we just fall back to `ftruncate`, generating an error for -// too-large values. -#[cfg(target_pointer_width = "32")] -pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { - weak!(fn ftruncate64(c_int, i64) -> c_int); - - unsafe { - match ftruncate64.get() { - Some(f) => cvt_r(|| f(fd, size as i64)).map(drop), - None => { - if size > i32::MAX as u64 { - Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot truncate >2GB")) - } else { - cvt_r(|| ftruncate(fd, size as i32)).map(drop) - } - } - } - } -} - -#[cfg(target_pointer_width = "64")] -pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { - unsafe { cvt_r(|| ftruncate(fd, size as i64)).map(drop) } -} - -#[cfg(target_pointer_width = "32")] -pub unsafe fn cvt_pread64( - fd: c_int, - buf: *mut c_void, - count: size_t, - offset: i64, -) -> io::Result<ssize_t> { - use crate::convert::TryInto; - weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t); - pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { - if let Ok(o) = offset.try_into() { - cvt(pread(fd, buf, count, o)) - } else { - Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pread >2GB")) - } - }) -} - -#[cfg(target_pointer_width = "32")] -pub unsafe fn cvt_pwrite64( - fd: c_int, - buf: *const c_void, - count: size_t, - offset: i64, -) -> io::Result<ssize_t> { - use crate::convert::TryInto; - weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t); - pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { - if let Ok(o) = offset.try_into() { - cvt(pwrite(fd, buf, count, o)) - } else { - Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pwrite >2GB")) - } - }) -} - -#[cfg(target_pointer_width = "64")] -pub unsafe fn cvt_pread64( - fd: c_int, - buf: *mut c_void, - count: size_t, - offset: i64, -) -> io::Result<ssize_t> { - cvt(pread(fd, buf, count, offset)) -} - -#[cfg(target_pointer_width = "64")] -pub unsafe fn cvt_pwrite64( - fd: c_int, - buf: *const c_void, - count: size_t, - offset: i64, -) -> io::Result<ssize_t> { - cvt(pwrite(fd, buf, count, offset)) -} diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 0956726084e..2362bff913f 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -4,7 +4,7 @@ mod tests; use crate::cmp; -use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; +use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -99,34 +99,39 @@ impl FileDesc { } pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { - #[cfg(target_os = "android")] - use super::android::cvt_pread64; - - #[cfg(not(target_os = "android"))] - unsafe fn cvt_pread64( - fd: c_int, - buf: *mut c_void, - count: usize, - offset: i64, - ) -> io::Result<isize> { - #[cfg(not(target_os = "linux"))] - use libc::pread as pread64; - #[cfg(target_os = "linux")] - use libc::pread64; - cvt(pread64(fd, buf, count, offset)) - } + #[cfg(not(any(target_os = "linux", target_os = "android")))] + use libc::pread as pread64; + #[cfg(any(target_os = "linux", target_os = "android"))] + use libc::pread64; unsafe { - cvt_pread64( + cvt(pread64( self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT), offset as i64, - ) + )) .map(|n| n as usize) } } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + let ret = cvt(unsafe { + libc::read( + self.as_raw_fd(), + buf.unfilled_mut().as_mut_ptr() as *mut c_void, + cmp::min(buf.remaining(), READ_LIMIT), + ) + })?; + + // Safety: `ret` bytes were written to the initialized portion of the buffer + unsafe { + buf.assume_init(ret as usize); + } + buf.add_filled(ret as usize); + Ok(()) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::write( @@ -161,30 +166,18 @@ impl FileDesc { } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { - #[cfg(target_os = "android")] - use super::android::cvt_pwrite64; - - #[cfg(not(target_os = "android"))] - unsafe fn cvt_pwrite64( - fd: c_int, - buf: *const c_void, - count: usize, - offset: i64, - ) -> io::Result<isize> { - #[cfg(not(target_os = "linux"))] - use libc::pwrite as pwrite64; - #[cfg(target_os = "linux")] - use libc::pwrite64; - cvt(pwrite64(fd, buf, count, offset)) - } + #[cfg(not(any(target_os = "linux", target_os = "android")))] + use libc::pwrite as pwrite64; + #[cfg(any(target_os = "linux", target_os = "android"))] + use libc::pwrite64; unsafe { - cvt_pwrite64( + cvt(pwrite64( self.as_raw_fd(), buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT), offset as i64, - ) + )) .map(|n| n as usize) } } @@ -289,11 +282,6 @@ impl<'a> Read for &'a FileDesc { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } impl AsInner<OwnedFd> for FileDesc { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index a4fff9b2e64..bcf2be0e95f 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -2,7 +2,7 @@ use crate::os::unix::prelude::*; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; -use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::path::{Path, PathBuf}; @@ -46,8 +46,8 @@ use libc::fstatat64; use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] use libc::{ - dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64, - open as open64, stat as stat64, + dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64, + lstat as lstat64, off64_t, open as open64, stat as stat64, }; #[cfg(not(any( target_os = "linux", @@ -835,16 +835,10 @@ impl File { } pub fn truncate(&self, size: u64) -> io::Result<()> { - #[cfg(target_os = "android")] - return crate::sys::android::ftruncate64(self.as_raw_fd(), size); - - #[cfg(not(target_os = "android"))] - { - use crate::convert::TryInto; - let size: off64_t = - size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; - cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop) - } + use crate::convert::TryInto; + let size: off64_t = + size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; + cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { @@ -864,6 +858,10 @@ impl File { self.0.read_at(buf, offset) } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } @@ -1154,7 +1152,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { } else if #[cfg(target_os = "macos")] { // On MacOS, older versions (<=10.9) lack support for linkat while newer // versions have it. We want to use linkat if it is available, so we use weak! - // to check. `linkat` is preferable to `link` ecause it gives us a flag to + // to check. `linkat` is preferable to `link` because it gives us a flag to // specify how symlinks should be handled. We pass 0 as the flags argument, // meaning it shouldn't follow symlinks. weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index a6b43229ba6..e85e4c5d618 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -104,7 +104,7 @@ impl FdMeta { fn potential_sendfile_source(&self) -> bool { match self { - // procfs erronously shows 0 length on non-empty readable files. + // procfs erroneously shows 0 length on non-empty readable files. // and if a file is truly empty then a `read` syscall will determine that and skip the write syscall // thus there would be benefit from attempting sendfile FdMeta::Metadata(meta) @@ -576,7 +576,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> return match err.raw_os_error() { // when file offset + max_length > u64::MAX Some(EOVERFLOW) => CopyResult::Fallback(written), - Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) => { + Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) if written == 0 => { // Try fallback io::copy if either: // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) @@ -584,12 +584,14 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM) // - copy_file_range cannot be used with pipes or device nodes (EINVAL) // - the writer fd was opened with O_APPEND (EBADF²) + // and no bytes were written successfully yet. (All these errnos should + // not be returned if something was already written, but they happen in + // the wild, see #91152.) // // ¹ these cases should be detected by the initial probe but we handle them here // anyway in case syscall interception changes during runtime // ² actually invalid file descriptors would cause this too, but in that case // the fallback code path is expected to encounter the same error again - assert_eq!(written, 0); CopyResult::Fallback(0) } _ => CopyResult::Error(err, written), @@ -612,6 +614,9 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> static HAS_SENDFILE: AtomicBool = AtomicBool::new(true); static HAS_SPLICE: AtomicBool = AtomicBool::new(true); + // Android builds use feature level 14, but the libc wrapper for splice is + // gated on feature level 21+, so we have to invoke the syscall directly. + #[cfg(target_os = "android")] syscall! { fn splice( srcfd: libc::c_int, @@ -623,6 +628,9 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> ) -> libc::ssize_t } + #[cfg(target_os = "linux")] + use libc::splice; + match mode { SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => { return CopyResult::Fallback(0); diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 9ae6d12dcb9..a82a0172126 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -501,7 +501,7 @@ impl FromRawFd for Socket { // res_init unconditionally, we call it only when we detect we're linking // against glibc version < 2.26. (That is, when we both know its needed and // believe it's thread-safe). -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] fn on_resolver_failure() { use crate::sys; @@ -513,5 +513,5 @@ fn on_resolver_failure() { } } -#[cfg(any(not(target_env = "gnu"), target_os = "vxworks"))] +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] fn on_resolver_failure() {} diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 87893d26912..8a028d99306 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -2,7 +2,7 @@ #![allow(unused_imports)] // lots of cfg code here -#[cfg(all(test, target_env = "gnu"))] +#[cfg(test)] mod tests; use crate::os::unix::prelude::*; @@ -97,6 +97,7 @@ pub fn errno() -> i32 { } #[cfg(target_os = "dragonfly")] +#[allow(dead_code)] pub fn set_errno(e: i32) { extern "C" { #[thread_local] @@ -472,10 +473,7 @@ impl Iterator for Env { #[cfg(target_os = "macos")] pub unsafe fn environ() -> *mut *const *const c_char { - extern "C" { - fn _NSGetEnviron() -> *mut *const *const c_char; - } - _NSGetEnviron() + libc::_NSGetEnviron() as *mut *const *const c_char } #[cfg(not(target_os = "macos"))] @@ -636,22 +634,14 @@ pub fn getppid() -> u32 { unsafe { libc::getppid() as u32 } } -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] pub fn glibc_version() -> Option<(usize, usize)> { - if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) { - parse_glibc_version(version_str) - } else { - None - } -} - -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] -fn glibc_version_cstr() -> Option<&'static CStr> { - weak! { - fn gnu_get_libc_version() -> *const libc::c_char + extern "C" { + fn gnu_get_libc_version() -> *const libc::c_char; } - if let Some(f) = gnu_get_libc_version.get() { - unsafe { Some(CStr::from_ptr(f())) } + let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; + if let Ok(version_str) = version_cstr.to_str() { + parse_glibc_version(version_str) } else { None } @@ -659,7 +649,7 @@ fn glibc_version_cstr() -> Option<&'static CStr> { // Returns Some((major, minor)) if the string is a valid "x.y" version, // ignoring any extra dot-separated parts. Otherwise return None. -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse(); match (parsed_ints.next(), parsed_ints.next()) { diff --git a/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs index c445acf2722..efc29955b05 100644 --- a/library/std/src/sys/unix/os/tests.rs +++ b/library/std/src/sys/unix/os/tests.rs @@ -1,14 +1,12 @@ -use super::*; - #[test] -#[cfg(not(target_os = "vxworks"))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] fn test_glibc_version() { // This mostly just tests that the weak linkage doesn't panic wildly... - glibc_version(); + super::glibc_version(); } #[test] -#[cfg(not(target_os = "vxworks"))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] fn test_parse_glibc_version() { let cases = [ ("0.0", Some((0, 0))), @@ -20,6 +18,6 @@ fn test_parse_glibc_version() { ("foo.1", None), ]; for &(version_str, parsed) in cases.iter() { - assert_eq!(parsed, parse_glibc_version(version_str)); + assert_eq!(parsed, super::parse_glibc_version(version_str)); } } diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index 507abb27871..ce77c210a62 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -284,7 +284,7 @@ impl ExitStatus { // // The other view would be to say that the caller on Fuchsia ought to know that `into_raw` // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is - // not possible here becaause we must return a c_int because that's what Unix (including + // not possible here because we must return a c_int because that's what Unix (including // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't // necessarily fit. // diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 3bf1493f3b8..bce35b380e6 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -13,7 +13,7 @@ use crate::sys::process::process_common::*; use crate::os::linux::process::PidFd; #[cfg(target_os = "linux")] -use crate::sys::weak::syscall; +use crate::sys::weak::raw_syscall; #[cfg(any( target_os = "macos", @@ -162,7 +162,7 @@ impl Command { cgroup: u64, } - syscall! { + raw_syscall! { fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long } diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index b99eb2e553f..9e02966b57c 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -7,7 +7,9 @@ use crate::ptr; use crate::sys::{os, stack_overflow}; use crate::time::Duration; -#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use crate::sys::weak::dlsym; +#[cfg(any(target_os = "solaris", target_os = "illumos"))] use crate::sys::weak::weak; #[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -627,10 +629,12 @@ pub mod guard { // We need that information to avoid blowing up when a small stack // is created in an application with big thread-local storage requirements. // See #6233 for rationale and details. -#[cfg(target_os = "linux")] -#[allow(deprecated)] +#[cfg(all(target_os = "linux", target_env = "gnu"))] fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { - weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); + // We use dlsym to avoid an ELF version dependency on GLIBC_PRIVATE. (#23628) + // We shouldn't really be using such an internal symbol, but there's currently + // no other way to account for the TLS size. + dlsym!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); match __pthread_get_minstack.get() { None => libc::PTHREAD_STACK_MIN, @@ -638,9 +642,8 @@ fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { } } -// No point in looking up __pthread_get_minstack() on non-glibc -// platforms. -#[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))] +// No point in looking up __pthread_get_minstack() on non-glibc platforms. +#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))] fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN } diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs index ba432ec5494..55719b87c7e 100644 --- a/library/std/src/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -6,7 +6,7 @@ //! detection. //! //! One option to use here is weak linkage, but that is unfortunately only -//! really workable on Linux. Hence, use dlsym to get the symbol value at +//! really workable with ELF. Otherwise, use dlsym to get the symbol value at //! runtime. This is also done for compatibility with older versions of glibc, //! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that //! we've been dynamically linked to the library the symbol comes from, but that @@ -14,7 +14,8 @@ //! //! A long time ago this used weak linkage for the __pthread_get_minstack //! symbol, but that caused Debian to detect an unnecessarily strict versioned -//! dependency on libc6 (#23628). +//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym` +//! for a runtime lookup of that symbol to avoid the ELF versioned dependency. // There are a variety of `#[cfg]`s controlling which targets are involved in // each instance of `weak!` and `syscall!`. Rather than trying to unify all of @@ -22,31 +23,75 @@ #![allow(dead_code, unused_macros)] use crate::ffi::CStr; -use crate::marker; +use crate::marker::PhantomData; use crate::mem; use crate::sync::atomic::{self, AtomicUsize, Ordering}; +// We can use true weak linkage on ELF targets. +#[cfg(not(any(target_os = "macos", target_os = "ios")))] pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( - #[allow(non_upper_case_globals)] - static $name: crate::sys::weak::Weak<unsafe extern "C" fn($($t),*) -> $ret> = - crate::sys::weak::Weak::new(concat!(stringify!($name), '\0')); + let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = { + extern "C" { + #[linkage = "extern_weak"] + static $name: *const libc::c_void; + } + #[allow(unused_unsafe)] + ExternWeak::new(unsafe { $name }) + }; ) } -pub struct Weak<F> { +// On non-ELF targets, use the dlsym approximation of weak linkage. +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub(crate) use self::dlsym as weak; + +pub(crate) struct ExternWeak<F> { + weak_ptr: *const libc::c_void, + _marker: PhantomData<F>, +} + +impl<F> ExternWeak<F> { + #[inline] + pub(crate) fn new(weak_ptr: *const libc::c_void) -> Self { + ExternWeak { weak_ptr, _marker: PhantomData } + } +} + +impl<F> ExternWeak<F> { + #[inline] + pub(crate) fn get(&self) -> Option<F> { + unsafe { + if self.weak_ptr.is_null() { + None + } else { + Some(mem::transmute_copy::<*const libc::c_void, F>(&self.weak_ptr)) + } + } + } +} + +pub(crate) macro dlsym { + (fn $name:ident($($t:ty),*) -> $ret:ty) => ( + static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> = + DlsymWeak::new(concat!(stringify!($name), '\0')); + let $name = &DLSYM; + ) +} + +pub(crate) struct DlsymWeak<F> { name: &'static str, addr: AtomicUsize, - _marker: marker::PhantomData<F>, + _marker: PhantomData<F>, } -impl<F> Weak<F> { - pub const fn new(name: &'static str) -> Weak<F> { - Weak { name, addr: AtomicUsize::new(1), _marker: marker::PhantomData } +impl<F> DlsymWeak<F> { + pub(crate) const fn new(name: &'static str) -> Self { + DlsymWeak { name, addr: AtomicUsize::new(1), _marker: PhantomData } } - pub fn get(&self) -> Option<F> { - assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>()); + #[inline] + pub(crate) fn get(&self) -> Option<F> { unsafe { // Relaxed is fine here because we fence before reading through the // pointer (see the comment below). @@ -79,9 +124,11 @@ impl<F> Weak<F> { } } - // Cold because it should only happen during first-time initalization. + // Cold because it should only happen during first-time initialization. #[cold] unsafe fn initialize(&self) -> Option<F> { + assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>()); + let val = fetch(self.name); // This synchronizes with the acquire fence in `get`. self.addr.store(val, Ordering::Release); @@ -105,14 +152,12 @@ unsafe fn fetch(name: &str) -> usize { pub(crate) macro syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name: $t),*) -> $ret { - use super::os; - weak! { fn $name($($t),*) -> $ret } if let Some(fun) = $name.get() { fun($($arg_name),*) } else { - os::set_errno(libc::ENOSYS); + super::os::set_errno(libc::ENOSYS); -1 } } @@ -123,11 +168,6 @@ pub(crate) macro syscall { pub(crate) macro syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name:$t),*) -> $ret { - use weak; - // This looks like a hack, but concat_idents only accepts idents - // (not paths). - use libc::*; - weak! { fn $name($($t),*) -> $ret } // Use a weak symbol from libc when possible, allowing `LD_PRELOAD` @@ -135,6 +175,10 @@ pub(crate) macro syscall { if let Some(fun) = $name.get() { fun($($arg_name),*) } else { + // This looks like a hack, but concat_idents only accepts idents + // (not paths). + use libc::*; + syscall( concat_idents!(SYS_, $name), $($arg_name),* @@ -143,3 +187,19 @@ pub(crate) macro syscall { } ) } + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub(crate) macro raw_syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name:$t),*) -> $ret { + // This looks like a hack, but concat_idents only accepts idents + // (not paths). + use libc::*; + + syscall( + concat_idents!(SYS_, $name), + $($arg_name),* + ) as $ret + } + ) +} diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs index 6b45e29c145..d1d2847cd33 100644 --- a/library/std/src/sys/unsupported/fs.rs +++ b/library/std/src/sys/unsupported/fs.rs @@ -1,7 +1,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use crate::path::{Path, PathBuf}; use crate::sys::time::SystemTime; use crate::sys::unsupported; @@ -206,6 +206,10 @@ impl File { self.0 } + pub fn read_buf(&self, _buf: &mut ReadBuf<'_>) -> io::Result<()> { + self.0 + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { self.0 } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 984dda8dc0b..1a3da3746ac 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -3,7 +3,7 @@ use super::fd::WasiFd; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use crate::iter; use crate::mem::{self, ManuallyDrop}; use crate::os::raw::c_int; @@ -411,6 +411,10 @@ impl File { true } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), buf) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.write_vectored(&[IoSlice::new(buf)]) } diff --git a/library/std/src/sys/wasm/alloc.rs b/library/std/src/sys/wasm/alloc.rs index 3223e894102..6dceb1689a8 100644 --- a/library/std/src/sys/wasm/alloc.rs +++ b/library/std/src/sys/wasm/alloc.rs @@ -24,7 +24,7 @@ static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling malloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } @@ -32,7 +32,7 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling calloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } @@ -40,7 +40,7 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling free() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } @@ -48,7 +48,7 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling realloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } diff --git a/library/std/src/sys/wasm/atomics/mutex.rs b/library/std/src/sys/wasm/atomics/mutex.rs index 5ff0ec052b6..3a09f0bf9bb 100644 --- a/library/std/src/sys/wasm/atomics/mutex.rs +++ b/library/std/src/sys/wasm/atomics/mutex.rs @@ -73,7 +73,7 @@ pub struct ReentrantMutex { unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} -// Reentrant mutexes are similarly implemented to mutexs above except that +// Reentrant mutexes are similarly implemented to mutexes above except that // instead of "1" meaning unlocked we use the id of a thread to represent // whether it has locked a mutex. That way we have an atomic counter which // always holds the id of the thread that currently holds the lock (or 0 if the @@ -96,7 +96,7 @@ impl ReentrantMutex { pub unsafe fn lock(&self) { let me = thread::my_id(); while let Err(owner) = self._try_lock(me) { - // SAFETY: the caller must gurantee that `self.ptr()` and `owner` are valid i32. + // SAFETY: the caller must guarantee that `self.ptr()` and `owner` are valid i32. let val = unsafe { wasm32::memory_atomic_wait32(self.ptr(), owner as i32, -1) }; debug_assert!(val == 0 || val == 1); } @@ -136,7 +136,7 @@ impl ReentrantMutex { match *self.recursions.get() { 0 => { self.owner.swap(0, SeqCst); - // SAFETY: the caller must gurantee that `self.ptr()` is valid i32. + // SAFETY: the caller must guarantee that `self.ptr()` is valid i32. unsafe { wasm32::memory_atomic_notify(self.ptr() as *mut i32, 1); } // wake up one waiter, if any diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 50c4547de85..b87b6b5d88e 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -1110,6 +1110,12 @@ compat_fn! { -> () { GetSystemTimeAsFileTime(lpSystemTimeAsFileTime) } + + // >= Win11 / Server 2022 + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a + pub fn GetTempPath2W(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD { + GetTempPathW(nBufferLength, lpBuffer) + } } compat_fn! { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 9859000c8d4..b258fc0478b 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -2,7 +2,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsString; use crate::fmt; -use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use crate::mem; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::path::{Path, PathBuf}; @@ -420,6 +420,10 @@ impl File { self.handle.read_at(buf, offset) } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + self.handle.read_buf(buf) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.handle.write(buf) } diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index 21d86b00226..c3a3482f910 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -1,7 +1,7 @@ #![unstable(issue = "none", feature = "windows_handle")] use crate::cmp; -use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read}; +use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf}; use crate::mem; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, @@ -130,6 +130,39 @@ impl Handle { } } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + let mut read = 0; + let len = cmp::min(buf.remaining(), <c::DWORD>::MAX as usize) as c::DWORD; + let res = cvt(unsafe { + c::ReadFile( + self.as_raw_handle(), + buf.unfilled_mut().as_mut_ptr() as c::LPVOID, + len, + &mut read, + ptr::null_mut(), + ) + }); + + match res { + Ok(_) => { + // Safety: `read` bytes were written to the initialized portion of the buffer + unsafe { + buf.assume_init(read as usize); + } + buf.add_filled(read as usize); + Ok(()) + } + + // The special treatment of BrokenPipe is to deal with Windows + // pipe semantics, which yields this error when *reading* from + // a pipe after the other end has closed; we interpret that as + // EOF on the pipe. + Err(ref e) if e.kind() == ErrorKind::BrokenPipe => Ok(()), + + Err(e) => Err(e), + } + } + pub unsafe fn read_overlapped( &self, buf: &mut [u8], diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index 28fec817f86..084af4325e7 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -288,13 +288,13 @@ pub fn abort_internal() -> ! { unsafe { cfg_if::cfg_if! { if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT); + core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT); crate::intrinsics::unreachable(); } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { - asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT); + core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT); crate::intrinsics::unreachable(); } else if #[cfg(target_arch = "aarch64")] { - asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT); + core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT); crate::intrinsics::unreachable(); } } diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index b5209aa690b..5f8556c3bc3 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -275,7 +275,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { } pub fn temp_dir() -> PathBuf { - super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPathW(sz, buf) }, super::os2path).unwrap() + super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap() } #[cfg(not(target_vendor = "uwp"))] diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index a4fe5f67f69..eb0925b3fda 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -124,7 +124,7 @@ fn write( // // If the data is not valid UTF-8 we write out as many bytes as are valid. // If the first byte is invalid it is either first byte of a multi-byte sequence but the - // provided byte slice is too short or it is the first byte of an invalide multi-byte sequence. + // provided byte slice is too short or it is the first byte of an invalid multi-byte sequence. let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2); let utf8 = match str::from_utf8(&data[..len]) { Ok(s) => s, diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs index 4f59d4dd452..5a8011a9588 100644 --- a/library/std/src/sys/windows/thread_parker.rs +++ b/library/std/src/sys/windows/thread_parker.rs @@ -22,7 +22,7 @@ // // Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a // HANDLE (created with NtCreateKeyedEvent). This means that we can be sure -// a succesfully awoken park() was awoken by unpark() and not a +// a successfully awoken park() was awoken by unpark() and not a // NtReleaseKeyedEvent call from some other code, as these events are not only // matched by the key (address of the parker (state)), but also by this HANDLE. // We lazily allocate this handle the first time it is needed. diff --git a/library/std/src/sys_common/thread_parker/generic.rs b/library/std/src/sys_common/thread_parker/generic.rs index 14cfa958e5e..d99e901bb5f 100644 --- a/library/std/src/sys_common/thread_parker/generic.rs +++ b/library/std/src/sys_common/thread_parker/generic.rs @@ -1,4 +1,4 @@ -//! Parker implementaiton based on a Mutex and Condvar. +//! Parker implementation based on a Mutex and Condvar. use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::SeqCst; @@ -20,7 +20,7 @@ impl Parker { Parker { state: AtomicUsize::new(EMPTY), lock: Mutex::new(()), cvar: Condvar::new() } } - // This implementaiton doesn't require `unsafe`, but other implementations + // This implementation doesn't require `unsafe`, but other implementations // may assume this is only called by the thread that owns the Parker. pub unsafe fn park(&self) { // If we were previously notified then we consume this notification and @@ -55,7 +55,7 @@ impl Parker { } } - // This implementaiton doesn't require `unsafe`, but other implementations + // This implementation doesn't require `unsafe`, but other implementations // may assume this is only called by the thread that owns the Parker. pub unsafe fn park_timeout(&self, dur: Duration) { // Like `park` above we have a fast path for an already-notified thread, and diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 4da59577d78..1d2f6e97680 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -76,7 +76,21 @@ use crate::fmt; /// destroyed, but not all platforms have this guard. Those platforms that do /// not guard typically have a synthetic limit after which point no more /// destructors are run. +/// 3. When the process exits on Windows systems, TLS destructors may only be +/// run on the thread that causes the process to exit. This is because the +/// other threads may be forcibly terminated. /// +/// ## Synchronization in thread-local destructors +/// +/// On Windows, synchronization operations (such as [`JoinHandle::join`]) in +/// thread local destructors are prone to deadlocks and so should be avoided. +/// This is because the [loader lock] is held while a destructor is run. The +/// lock is acquired whenever a thread starts or exits or when a DLL is loaded +/// or unloaded. Therefore these events are blocked for as long as a thread +/// local destructor is running. +/// +/// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices +/// [`JoinHandle::join`]: crate::thread::JoinHandle::join /// [`with`]: LocalKey::with #[stable(feature = "rust1", since = "1.0.0")] pub struct LocalKey<T: 'static> { @@ -164,7 +178,6 @@ macro_rules! __thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[cfg_attr(not(windows), inline)] // see comments below unsafe fn __getit() -> $crate::option::Option<&'static $t> { - const _REQUIRE_UNSTABLE: () = $crate::thread::require_unstable_const_init_thread_local(); const INIT_EXPR: $t = $init; // wasm without atomics maps directly to `static mut`, and dtors @@ -569,7 +582,7 @@ pub mod fast { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } } - // note that this is just a publically-callable function only for the + // note that this is just a publicly-callable function only for the // const-initialized form of thread locals, basically a way to call the // free `register_dtor` function defined elsewhere in libstd. pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { @@ -580,7 +593,7 @@ pub mod fast { pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { // SAFETY: See the definitions of `LazyKeyInner::get` and - // `try_initialize` for more informations. + // `try_initialize` for more information. // // The caller must ensure no mutable references are ever active to // the inner cell or the inner T when this is called. diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 39b53b51bfa..64f6c7fa022 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -204,13 +204,6 @@ pub use self::local::os::Key as __OsLocalKeyInner; #[doc(hidden)] pub use self::local::statik::Key as __StaticLocalKeyInner; -// This is only used to make thread locals with `const { .. }` initialization -// expressions unstable. If and/or when that syntax is stabilized with thread -// locals this will simply be removed. -#[doc(hidden)] -#[unstable(feature = "thread_local_const_init", issue = "84223")] -pub const fn require_unstable_const_init_thread_local() {} - //////////////////////////////////////////////////////////////////////////////// // Builder //////////////////////////////////////////////////////////////////////////////// @@ -1460,9 +1453,12 @@ fn _assert_sync_and_send() { /// The purpose of this API is to provide an easy and portable way to query /// the default amount of parallelism the program should use. Among other things it /// does not expose information on NUMA regions, does not account for -/// differences in (co)processor capabilities, and will not modify the program's -/// global state in order to more accurately query the amount of available -/// parallelism. +/// differences in (co)processor capabilities or current system load, +/// and will not modify the program's global state in order to more accurately +/// query the amount of available parallelism. +/// +/// Where both fixed steady-state and burst limits are available the steady-state +/// capacity will be used to ensure more predictable latencies. /// /// Resource limits can be changed during the runtime of a program, therefore the value is /// not cached and instead recomputed every time this function is called. It should not be diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index ca0d88135a5..4f2c81731a3 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -1,6 +1,7 @@ use super::Builder; use crate::any::Any; use crate::mem; +use crate::panic::panic_any; use crate::result; use crate::sync::{ mpsc::{channel, Sender}, @@ -183,7 +184,7 @@ fn test_simple_newsched_spawn() { } #[test] -fn test_try_panic_message_static_str() { +fn test_try_panic_message_string_literal() { match thread::spawn(move || { panic!("static string"); }) @@ -199,9 +200,9 @@ fn test_try_panic_message_static_str() { } #[test] -fn test_try_panic_message_owned_str() { +fn test_try_panic_any_message_owned_str() { match thread::spawn(move || { - panic!("owned string".to_string()); + panic_any("owned string".to_string()); }) .join() { @@ -215,9 +216,9 @@ fn test_try_panic_message_owned_str() { } #[test] -fn test_try_panic_message_any() { +fn test_try_panic_any_message_any() { match thread::spawn(move || { - panic!(box 413u16 as Box<dyn Any + Send>); + panic_any(box 413u16 as Box<dyn Any + Send>); }) .join() { @@ -233,10 +234,10 @@ fn test_try_panic_message_any() { } #[test] -fn test_try_panic_message_unit_struct() { +fn test_try_panic_any_message_unit_struct() { struct Juju; - match thread::spawn(move || panic!(Juju)).join() { + match thread::spawn(move || panic_any(Juju)).join() { Err(ref e) if e.is::<Juju>() => {} Err(_) | Ok(()) => panic!(), } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index a5e3bd0c290..86cc93c4453 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -273,7 +273,7 @@ impl Instant { // While issues have been seen on arm64 platforms the Arm architecture // requires that the counter monotonically increases and that it must // provide a uniform view of system time (e.g. it must not be possible - // for a core to recieve a message from another core with a time stamp + // for a core to receive a message from another core with a time stamp // and observe time going backwards (ARM DDI 0487G.b D11.1.2). While // there have been a few 64bit SoCs that have bugs which cause time to // not monoticially increase, these have been fixed in the Linux kernel |
