diff options
| author | Ben Kimock <kimockb@gmail.com> | 2024-11-15 06:29:30 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-15 06:29:30 +0000 |
| commit | 3e71ed157037d9f77ff5ca73b015fe14e2b48792 (patch) | |
| tree | 3efe043e15a6bd9518d8d833eaac2c8f73649f39 /library/std | |
| parent | 7213a278eb67190fbceac4b4d948906ab49ff635 (diff) | |
| parent | b9116571ff44029f457bf6a60c7866fb28267a83 (diff) | |
Merge pull request #4034 from rust-lang/rustup-2024-11-15
Automatic Rustup
Diffstat (limited to 'library/std')
34 files changed, 1254 insertions, 391 deletions
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index e1e0eb36d23..f86bcdb4796 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -757,6 +757,47 @@ where self.base.get_or_insert_with(value, f) } + /// Gets the given value's corresponding entry in the set for in-place manipulation. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// use std::collections::hash_set::Entry::*; + /// + /// let mut singles = HashSet::new(); + /// let mut dupes = HashSet::new(); + /// + /// for ch in "a short treatise on fungi".chars() { + /// if let Vacant(dupe_entry) = dupes.entry(ch) { + /// // We haven't already seen a duplicate, so + /// // check if we've at least seen it once. + /// match singles.entry(ch) { + /// Vacant(single_entry) => { + /// // We found a new character for the first time. + /// single_entry.insert() + /// } + /// Occupied(single_entry) => { + /// // We've already seen this once, "move" it to dupes. + /// single_entry.remove(); + /// dupe_entry.insert(); + /// } + /// } + /// } + /// } + /// + /// assert!(!singles.contains(&'t') && dupes.contains(&'t')); + /// assert!(singles.contains(&'u') && !dupes.contains(&'u')); + /// assert!(!singles.contains(&'v') && !dupes.contains(&'v')); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn entry(&mut self, value: T) -> Entry<'_, T, S> { + map_entry(self.base.entry(value)) + } + /// Returns `true` if `self` has no elements in common with `other`. /// This is equivalent to checking for an empty intersection. /// @@ -935,6 +976,14 @@ where } } +#[inline] +fn map_entry<'a, K: 'a, V: 'a>(raw: base::Entry<'a, K, V>) -> Entry<'a, K, V> { + match raw { + base::Entry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), + base::Entry::Vacant(base) => Entry::Vacant(VacantEntry { base }), + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Clone for HashSet<T, S> where @@ -1865,6 +1914,406 @@ where } } +/// A view into a single entry in a set, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`HashSet`]. +/// +/// [`HashSet`]: struct.HashSet.html +/// [`entry`]: struct.HashSet.html#method.entry +/// +/// # Examples +/// +/// ``` +/// #![feature(hash_set_entry)] +/// +/// use std::collections::hash_set::HashSet; +/// +/// let mut set = HashSet::new(); +/// set.extend(["a", "b", "c"]); +/// assert_eq!(set.len(), 3); +/// +/// // Existing value (insert) +/// let entry = set.entry("a"); +/// let _raw_o = entry.insert(); +/// assert_eq!(set.len(), 3); +/// // Nonexistent value (insert) +/// set.entry("d").insert(); +/// +/// // Existing value (or_insert) +/// set.entry("b").or_insert(); +/// // Nonexistent value (or_insert) +/// set.entry("e").or_insert(); +/// +/// println!("Our HashSet: {:?}", set); +/// +/// let mut vec: Vec<_> = set.iter().copied().collect(); +/// // The `Iter` iterator produces items in arbitrary order, so the +/// // items must be sorted to test them against a sorted array. +/// vec.sort_unstable(); +/// assert_eq!(vec, ["a", "b", "c", "d", "e"]); +/// ``` +#[unstable(feature = "hash_set_entry", issue = "60896")] +pub enum Entry<'a, T, S> { + /// An occupied entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::hash_set::{Entry, HashSet}; + /// + /// let mut set = HashSet::from(["a", "b"]); + /// + /// match set.entry("a") { + /// Entry::Vacant(_) => unreachable!(), + /// Entry::Occupied(_) => { } + /// } + /// ``` + Occupied(OccupiedEntry<'a, T, S>), + + /// A vacant entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::hash_set::{Entry, HashSet}; + /// + /// let mut set = HashSet::new(); + /// + /// match set.entry("a") { + /// Entry::Occupied(_) => unreachable!(), + /// Entry::Vacant(_) => { } + /// } + /// ``` + Vacant(VacantEntry<'a, T, S>), +} + +#[unstable(feature = "hash_set_entry", issue = "60896")] +impl<T: fmt::Debug, S> fmt::Debug for Entry<'_, T, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Entry::Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), + Entry::Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), + } + } +} + +/// A view into an occupied entry in a `HashSet`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +/// +/// # Examples +/// +/// ``` +/// #![feature(hash_set_entry)] +/// +/// use std::collections::hash_set::{Entry, HashSet}; +/// +/// let mut set = HashSet::new(); +/// set.extend(["a", "b", "c"]); +/// +/// let _entry_o = set.entry("a").insert(); +/// assert_eq!(set.len(), 3); +/// +/// // Existing key +/// match set.entry("a") { +/// Entry::Vacant(_) => unreachable!(), +/// Entry::Occupied(view) => { +/// assert_eq!(view.get(), &"a"); +/// } +/// } +/// +/// assert_eq!(set.len(), 3); +/// +/// // Existing key (take) +/// match set.entry("c") { +/// Entry::Vacant(_) => unreachable!(), +/// Entry::Occupied(view) => { +/// assert_eq!(view.remove(), "c"); +/// } +/// } +/// assert_eq!(set.get(&"c"), None); +/// assert_eq!(set.len(), 2); +/// ``` +#[unstable(feature = "hash_set_entry", issue = "60896")] +pub struct OccupiedEntry<'a, T, S> { + base: base::OccupiedEntry<'a, T, S>, +} + +#[unstable(feature = "hash_set_entry", issue = "60896")] +impl<T: fmt::Debug, S> fmt::Debug for OccupiedEntry<'_, T, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry").field("value", self.get()).finish() + } +} + +/// A view into a vacant entry in a `HashSet`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +/// +/// # Examples +/// +/// ``` +/// #![feature(hash_set_entry)] +/// +/// use std::collections::hash_set::{Entry, HashSet}; +/// +/// let mut set = HashSet::<&str>::new(); +/// +/// let entry_v = match set.entry("a") { +/// Entry::Vacant(view) => view, +/// Entry::Occupied(_) => unreachable!(), +/// }; +/// entry_v.insert(); +/// assert!(set.contains("a") && set.len() == 1); +/// +/// // Nonexistent key (insert) +/// match set.entry("b") { +/// Entry::Vacant(view) => view.insert(), +/// Entry::Occupied(_) => unreachable!(), +/// } +/// assert!(set.contains("b") && set.len() == 2); +/// ``` +#[unstable(feature = "hash_set_entry", issue = "60896")] +pub struct VacantEntry<'a, T, S> { + base: base::VacantEntry<'a, T, S>, +} + +#[unstable(feature = "hash_set_entry", issue = "60896")] +impl<T: fmt::Debug, S> fmt::Debug for VacantEntry<'_, T, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.get()).finish() + } +} + +impl<'a, T, S> Entry<'a, T, S> { + /// Sets the value of the entry, and returns an OccupiedEntry. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// let entry = set.entry("horseyland").insert(); + /// + /// assert_eq!(entry.get(), &"horseyland"); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn insert(self) -> OccupiedEntry<'a, T, S> + where + T: Hash, + S: BuildHasher, + { + match self { + Entry::Occupied(entry) => entry, + Entry::Vacant(entry) => entry.insert_entry(), + } + } + + /// Ensures a value is in the entry by inserting if it was vacant. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// + /// // nonexistent key + /// set.entry("poneyland").or_insert(); + /// assert!(set.contains("poneyland")); + /// + /// // existing key + /// set.entry("poneyland").or_insert(); + /// assert!(set.contains("poneyland")); + /// assert_eq!(set.len(), 1); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn or_insert(self) + where + T: Hash, + S: BuildHasher, + { + if let Entry::Vacant(entry) = self { + entry.insert(); + } + } + + /// Returns a reference to this entry's value. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// set.entry("poneyland").or_insert(); + /// + /// // existing key + /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); + /// // nonexistent key + /// assert_eq!(set.entry("horseland").get(), &"horseland"); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get(&self) -> &T { + match *self { + Entry::Occupied(ref entry) => entry.get(), + Entry::Vacant(ref entry) => entry.get(), + } + } +} + +impl<T, S> OccupiedEntry<'_, T, S> { + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::hash_set::{Entry, HashSet}; + /// + /// let mut set = HashSet::new(); + /// set.entry("poneyland").or_insert(); + /// + /// match set.entry("poneyland") { + /// Entry::Vacant(_) => panic!(), + /// Entry::Occupied(entry) => assert_eq!(entry.get(), &"poneyland"), + /// } + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get(&self) -> &T { + self.base.get() + } + + /// Takes the value out of the entry, and returns it. + /// Keeps the allocated memory for reuse. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// use std::collections::hash_set::Entry; + /// + /// let mut set = HashSet::new(); + /// // The set is empty + /// assert!(set.is_empty() && set.capacity() == 0); + /// + /// set.entry("poneyland").or_insert(); + /// let capacity_before_remove = set.capacity(); + /// + /// if let Entry::Occupied(o) = set.entry("poneyland") { + /// assert_eq!(o.remove(), "poneyland"); + /// } + /// + /// assert_eq!(set.contains("poneyland"), false); + /// // Now set hold none elements but capacity is equal to the old one + /// assert!(set.len() == 0 && set.capacity() == capacity_before_remove); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn remove(self) -> T { + self.base.remove() + } +} + +impl<'a, T, S> VacantEntry<'a, T, S> { + /// Gets a reference to the value that would be used when inserting + /// through the `VacantEntry`. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get(&self) -> &T { + self.base.get() + } + + /// Take ownership of the value. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::hash_set::{Entry, HashSet}; + /// + /// let mut set = HashSet::new(); + /// + /// match set.entry("poneyland") { + /// Entry::Occupied(_) => panic!(), + /// Entry::Vacant(v) => assert_eq!(v.into_value(), "poneyland"), + /// } + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn into_value(self) -> T { + self.base.into_value() + } + + /// Sets the value of the entry with the VacantEntry's value. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// use std::collections::hash_set::Entry; + /// + /// let mut set = HashSet::new(); + /// + /// if let Entry::Vacant(o) = set.entry("poneyland") { + /// o.insert(); + /// } + /// assert!(set.contains("poneyland")); + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn insert(self) + where + T: Hash, + S: BuildHasher, + { + self.base.insert(); + } + + #[inline] + fn insert_entry(self) -> OccupiedEntry<'a, T, S> + where + T: Hash, + S: BuildHasher, + { + OccupiedEntry { base: self.base.insert() } + } +} + #[allow(dead_code)] fn assert_covariance() { fn set<'new>(v: HashSet<&'static str>) -> HashSet<&'new str> { diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 229f979b5b1..e93e915159e 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -188,104 +188,6 @@ impl f128 { self - self.trunc() } - /// Computes the absolute value of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128)] { - /// - /// let x = 3.5_f128; - /// let y = -3.5_f128; - /// - /// assert_eq!(x.abs(), x); - /// assert_eq!(y.abs(), -y); - /// - /// assert!(f128::NAN.abs().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub const fn abs(self) -> Self { - // FIXME(f16_f128): replace with `intrinsics::fabsf128` when available - // We don't do this now because LLVM has lowering bugs for f128 math. - Self::from_bits(self.to_bits() & !(1 << 127)) - } - - /// Returns a number that represents the sign of `self`. - /// - /// - `1.0` if the number is positive, `+0.0` or `INFINITY` - /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` - /// - NaN if the number is NaN - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.5_f128; - /// - /// assert_eq!(f.signum(), 1.0); - /// assert_eq!(f128::NEG_INFINITY.signum(), -1.0); - /// - /// assert!(f128::NAN.signum().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub const fn signum(self) -> f128 { - if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) } - } - - /// Returns a number composed of the magnitude of `self` and the sign of - /// `sign`. - /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. - /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is - /// returned. - /// - /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note - /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust - /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the - /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable - /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more - /// info. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.5_f128; - /// - /// assert_eq!(f.copysign(0.42), 3.5_f128); - /// assert_eq!(f.copysign(-0.42), -3.5_f128); - /// assert_eq!((-f).copysign(0.42), 3.5_f128); - /// assert_eq!((-f).copysign(-0.42), -3.5_f128); - /// - /// assert!(f128::NAN.copysign(1.0).is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub const fn copysign(self, sign: f128) -> f128 { - unsafe { intrinsics::copysignf128(self, sign) } - } - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index bed21cda1cd..5b7fcaa28e0 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -188,103 +188,6 @@ impl f16 { self - self.trunc() } - /// Computes the absolute value of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16)] { - /// - /// let x = 3.5_f16; - /// let y = -3.5_f16; - /// - /// assert_eq!(x.abs(), x); - /// assert_eq!(y.abs(), -y); - /// - /// assert!(f16::NAN.abs().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub const fn abs(self) -> Self { - // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available - Self::from_bits(self.to_bits() & !(1 << 15)) - } - - /// Returns a number that represents the sign of `self`. - /// - /// - `1.0` if the number is positive, `+0.0` or `INFINITY` - /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` - /// - NaN if the number is NaN - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.5_f16; - /// - /// assert_eq!(f.signum(), 1.0); - /// assert_eq!(f16::NEG_INFINITY.signum(), -1.0); - /// - /// assert!(f16::NAN.signum().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub const fn signum(self) -> f16 { - if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) } - } - - /// Returns a number composed of the magnitude of `self` and the sign of - /// `sign`. - /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. - /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is - /// returned. - /// - /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note - /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust - /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the - /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable - /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more - /// info. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.5_f16; - /// - /// assert_eq!(f.copysign(0.42), 3.5_f16); - /// assert_eq!(f.copysign(-0.42), -3.5_f16); - /// assert_eq!((-f).copysign(0.42), 3.5_f16); - /// assert_eq!((-f).copysign(-0.42), -3.5_f16); - /// - /// assert!(f16::NAN.copysign(1.0).is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub const fn copysign(self, sign: f16) -> f16 { - unsafe { intrinsics::copysignf16(self, sign) } - } - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 30cf4e1f756..7cb285bbff5 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -176,90 +176,6 @@ impl f32 { self - self.trunc() } - /// Computes the absolute value of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let x = 3.5_f32; - /// let y = -3.5_f32; - /// - /// assert_eq!(x.abs(), x); - /// assert_eq!(y.abs(), -y); - /// - /// assert!(f32::NAN.abs().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[inline] - pub const fn abs(self) -> f32 { - unsafe { intrinsics::fabsf32(self) } - } - - /// Returns a number that represents the sign of `self`. - /// - /// - `1.0` if the number is positive, `+0.0` or `INFINITY` - /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` - /// - NaN if the number is NaN - /// - /// # Examples - /// - /// ``` - /// let f = 3.5_f32; - /// - /// assert_eq!(f.signum(), 1.0); - /// assert_eq!(f32::NEG_INFINITY.signum(), -1.0); - /// - /// assert!(f32::NAN.signum().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[inline] - pub const fn signum(self) -> f32 { - if self.is_nan() { Self::NAN } else { 1.0_f32.copysign(self) } - } - - /// Returns a number composed of the magnitude of `self` and the sign of - /// `sign`. - /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. - /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is - /// returned. - /// - /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note - /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust - /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the - /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable - /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more - /// info. - /// - /// # Examples - /// - /// ``` - /// let f = 3.5_f32; - /// - /// assert_eq!(f.copysign(0.42), 3.5_f32); - /// assert_eq!(f.copysign(-0.42), -3.5_f32); - /// assert_eq!((-f).copysign(0.42), 3.5_f32); - /// assert_eq!((-f).copysign(-0.42), -3.5_f32); - /// - /// assert!(f32::NAN.copysign(1.0).is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - pub const fn copysign(self, sign: f32) -> f32 { - unsafe { intrinsics::copysignf32(self, sign) } - } - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 51d5476b372..47163c272de 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -176,90 +176,6 @@ impl f64 { self - self.trunc() } - /// Computes the absolute value of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let x = 3.5_f64; - /// let y = -3.5_f64; - /// - /// assert_eq!(x.abs(), x); - /// assert_eq!(y.abs(), -y); - /// - /// assert!(f64::NAN.abs().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[inline] - pub const fn abs(self) -> f64 { - unsafe { intrinsics::fabsf64(self) } - } - - /// Returns a number that represents the sign of `self`. - /// - /// - `1.0` if the number is positive, `+0.0` or `INFINITY` - /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` - /// - NaN if the number is NaN - /// - /// # Examples - /// - /// ``` - /// let f = 3.5_f64; - /// - /// assert_eq!(f.signum(), 1.0); - /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0); - /// - /// assert!(f64::NAN.signum().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[inline] - pub const fn signum(self) -> f64 { - if self.is_nan() { Self::NAN } else { 1.0_f64.copysign(self) } - } - - /// Returns a number composed of the magnitude of `self` and the sign of - /// `sign`. - /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. - /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is - /// returned. - /// - /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note - /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust - /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the - /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable - /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more - /// info. - /// - /// # Examples - /// - /// ``` - /// let f = 3.5_f64; - /// - /// assert_eq!(f.copysign(0.42), 3.5_f64); - /// assert_eq!(f.copysign(-0.42), -3.5_f64); - /// assert_eq!((-f).copysign(0.42), 3.5_f64); - /// assert_eq!((-f).copysign(-0.42), -3.5_f64); - /// - /// assert!(f64::NAN.copysign(1.0).is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_unstable(feature = "const_float_methods", issue = "130843")] - #[inline] - pub const fn copysign(self, sign: f64) -> f64 { - unsafe { intrinsics::copysignf64(self, sign) } - } - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error, yielding a more accurate result than an unfused multiply-add. /// diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index b19d482feaa..79dfb47d0c4 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -112,7 +112,7 @@ impl crate::sealed::Sealed for OsString {} /// [conversions]: super#conversions #[cfg_attr(not(test), rustc_diagnostic_item = "OsStr")] #[stable(feature = "rust1", since = "1.0.0")] -// `OsStr::from_inner` current implementation relies +// `OsStr::from_inner` and `impl CloneToUninit for OsStr` current implementation relies // on `OsStr` being layout-compatible with `Slice`. // However, `OsStr` layout is considered an implementation detail and must not be relied upon. #[repr(transparent)] @@ -1278,9 +1278,9 @@ impl Clone for Box<OsStr> { unsafe impl CloneToUninit for OsStr { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: we're just a wrapper around a platform-specific Slice - unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: we're just a transparent wrapper around a platform-specific Slice + unsafe { self.inner.clone_to_uninit(dst) } } } diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index 67147934b4d..cbec44c8626 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -294,12 +294,12 @@ fn clone_to_uninit() { let a = OsStr::new("hello.txt"); let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)]; - unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut OsStr) }; + unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); let mut b: Box<OsStr> = OsStr::new("world.exe").into(); assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b)); assert_ne!(a, &*b); - unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b)) }; + unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b).cast()) }; assert_eq!(a, &*b); } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 3079c8b1d90..76fdb1a4ffd 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -624,6 +624,223 @@ impl File { self.inner.datasync() } + /// Acquire an exclusive advisory lock on the file. Blocks until the lock can be acquired. + /// + /// This acquires an exclusive advisory lock; no other file handle to this file may acquire + /// another lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then an exclusive lock is held. + /// + /// If the file not open for writing, it is unspecified whether this function returns an error. + /// + /// Note, this is an advisory lock meant to interact with [`lock_shared`], [`try_lock`], + /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, + /// and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` flag. Note that, + /// this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn lock(&self) -> io::Result<()> { + self.inner.lock() + } + + /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired. + /// + /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but + /// none may hold an exclusive lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then a shared lock is held. + /// + /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], + /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, + /// and the `LockFileEx` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock_shared()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn lock_shared(&self) -> io::Result<()> { + self.inner.lock_shared() + } + + /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. + /// + /// This acquires an exclusive advisory lock; no other file handle to this file may acquire + /// another lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then an exclusive lock is held. + /// + /// If the file not open for writing, it is unspecified whether this function returns an error. + /// + /// Note, this is an advisory lock meant to interact with [`lock`], [`lock_shared`], + /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and + /// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` + /// and `LOCKFILE_FAIL_IMMEDIATELY` flags. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.try_lock()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn try_lock(&self) -> io::Result<bool> { + self.inner.try_lock() + } + + /// Acquire a shared advisory lock on the file. + /// Returns `Ok(false)` if the file is exclusively locked. + /// + /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but + /// none may hold an exclusive lock. + /// + /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then a shared lock is held. + /// + /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], + /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`] + /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and + /// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the + /// `LOCKFILE_FAIL_IMMEDIATELY` flag. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.try_lock_shared()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn try_lock_shared(&self) -> io::Result<bool> { + self.inner.try_lock_shared() + } + + /// Release all locks on the file. + /// + /// All remaining locks are released when the file handle, and all clones of it, are dropped. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_UN` flag, + /// and the `UnlockFile` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_lock)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock()?; + /// f.unlock()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_lock", issue = "130994")] + pub fn unlock(&self) -> io::Result<()> { + self.inner.unlock() + } + /// Truncates or extends the underlying file, updating the size of /// this file to become `size`. /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 0672fe6f771..018e1958641 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -204,6 +204,152 @@ fn file_test_io_seek_and_write() { } #[test] +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +fn file_lock_multiple_shared() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_multiple_shared_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that we can acquire concurrent shared locks + check!(f1.lock_shared()); + check!(f2.lock_shared()); + check!(f1.unlock()); + check!(f2.unlock()); + assert!(check!(f1.try_lock_shared())); + assert!(check!(f2.try_lock_shared())); +} + +#[test] +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +fn file_lock_blocking() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_blocking_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that shared locks block exclusive locks + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + check!(f1.unlock()); + + // Check that exclusive locks block shared locks + check!(f1.lock()); + assert!(!check!(f2.try_lock_shared())); +} + +#[test] +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +fn file_lock_drop() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_dup_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that locks are released when the File is dropped + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + drop(f1); + assert!(check!(f2.try_lock())); +} + +#[test] +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +fn file_lock_dup() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_dup_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // Check that locks are not dropped if the File has been cloned + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + let cloned = check!(f1.try_clone()); + drop(f1); + assert!(!check!(f2.try_lock())); + drop(cloned) +} + +#[test] +#[cfg(windows)] +fn file_lock_double_unlock() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_double_unlock_test.txt"); + let f1 = check!(File::create(filename)); + let f2 = check!(OpenOptions::new().write(true).open(filename)); + + // On Windows a file handle may acquire both a shared and exclusive lock. + // Check that both are released by unlock() + check!(f1.lock()); + check!(f1.lock_shared()); + assert!(!check!(f2.try_lock())); + check!(f1.unlock()); + assert!(check!(f2.try_lock())); +} + +#[test] +#[cfg(windows)] +fn file_lock_blocking_async() { + use crate::thread::{sleep, spawn}; + const FILE_FLAG_OVERLAPPED: u32 = 0x40000000; + + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_blocking_async.txt"); + let f1 = check!(File::create(filename)); + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + + check!(f1.lock()); + + // Ensure that lock() is synchronous when the file is opened for asynchronous IO + let t = spawn(move || { + check!(f2.lock()); + }); + sleep(Duration::from_secs(1)); + assert!(!t.is_finished()); + check!(f1.unlock()); + t.join().unwrap(); + + // Ensure that lock_shared() is synchronous when the file is opened for asynchronous IO + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + check!(f1.lock()); + + // Ensure that lock() is synchronous when the file is opened for asynchronous IO + let t = spawn(move || { + check!(f2.lock_shared()); + }); + sleep(Duration::from_secs(1)); + assert!(!t.is_finished()); + check!(f1.unlock()); + t.join().unwrap(); +} + +#[test] fn file_test_io_seek_shakedown() { // 01234567890123 let initial_msg = "qwer-asdf-zxcv"; diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 71dfd0676c9..21e70774954 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1340,6 +1340,25 @@ impl<'a> IoSliceMut<'a> { bufs[0].advance(left); } } + + /// Get the underlying bytes as a mutable slice with the original lifetime. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_as_bytes)] + /// use std::io::IoSliceMut; + /// + /// let mut data = *b"abcdef"; + /// let io_slice = IoSliceMut::new(&mut data); + /// io_slice.into_slice()[0] = b'A'; + /// + /// assert_eq!(&data, b"Abcdef"); + /// ``` + #[unstable(feature = "io_slice_as_bytes", issue = "132818")] + pub const fn into_slice(self) -> &'a mut [u8] { + self.0.into_slice() + } } #[stable(feature = "iovec", since = "1.36.0")] @@ -1482,6 +1501,32 @@ impl<'a> IoSlice<'a> { bufs[0].advance(left); } } + + /// Get the underlying bytes as a slice with the original lifetime. + /// + /// This doesn't borrow from `self`, so is less restrictive than calling + /// `.deref()`, which does. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_as_bytes)] + /// use std::io::IoSlice; + /// + /// let data = b"abcdef"; + /// + /// let mut io_slice = IoSlice::new(data); + /// let tail = &io_slice.as_slice()[3..]; + /// + /// // This works because `tail` doesn't borrow `io_slice` + /// io_slice = IoSlice::new(tail); + /// + /// assert_eq!(io_slice.as_slice(), b"def"); + /// ``` + #[unstable(feature = "io_slice_as_bytes", issue = "132818")] + pub const fn as_slice(self) -> &'a [u8] { + self.0.as_slice() + } } #[stable(feature = "iovec", since = "1.36.0")] diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 56b71c47dc7..89e806c0891 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -531,6 +531,20 @@ fn io_slice_advance_slices_beyond_total_length() { assert!(bufs.is_empty()); } +#[test] +fn io_slice_as_slice() { + let buf = [1; 8]; + let slice = IoSlice::new(&buf).as_slice(); + assert_eq!(slice, buf); +} + +#[test] +fn io_slice_into_slice() { + let mut buf = [1; 8]; + let slice = IoSliceMut::new(&mut buf).into_slice(); + assert_eq!(slice, [1; 8]); +} + /// Creates a new writer that reads from at most `n_bufs` and reads /// `per_call` bytes (in total) per call to write. fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { diff --git a/library/std/src/os/nuttx/raw.rs b/library/std/src/os/nuttx/raw.rs index 113079cf4ab..431e66d69a3 100644 --- a/library/std/src/os/nuttx/raw.rs +++ b/library/std/src/os/nuttx/raw.rs @@ -1,4 +1,4 @@ -//! rtems raw type definitions +//! NuttX raw type definitions #![stable(feature = "raw_ext", since = "1.1.0")] #![deprecated( diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 5662a44d832..b0291e3aa19 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2128,7 +2128,7 @@ impl AsRef<OsStr> for PathBuf { /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Path")] #[stable(feature = "rust1", since = "1.0.0")] -// `Path::new` current implementation relies +// `Path::new` and `impl CloneToUninit for Path` current implementation relies // on `Path` being layout-compatible with `OsStr`. // However, `Path` layout is considered an implementation detail and must not be relied upon. #[repr(transparent)] @@ -3170,9 +3170,9 @@ impl Path { unsafe impl CloneToUninit for Path { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: Path is just a wrapper around OsStr - unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: Path is just a transparent wrapper around OsStr + unsafe { self.inner.clone_to_uninit(dst) } } } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index b75793d2bc9..ff3f7151bb8 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -2068,7 +2068,7 @@ fn clone_to_uninit() { let a = Path::new("hello.txt"); let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<Path>(a)]; - unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut Path) }; + unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); @@ -2076,6 +2076,6 @@ fn clone_to_uninit() { let mut b: Box<Path> = Path::new("world.exe").into(); assert_eq!(size_of_val::<Path>(a), size_of_val::<Path>(&b)); assert_ne!(a, &*b); - unsafe { a.clone_to_uninit(ptr::from_mut::<Path>(&mut b)) }; + unsafe { a.clone_to_uninit(ptr::from_mut::<Path>(&mut b).cast()) }; assert_eq!(a, &*b); } diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 44e146a89ba..0cf4902d6d5 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -153,6 +153,7 @@ use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::time::{Duration, Instant}; /// Creates a new asynchronous channel, returning the sender/receiver halves. +/// /// All data sent on the [`Sender`] will become available on the [`Receiver`] in /// the same order as it was sent, and no [`send`] will block the calling thread /// (this channel has an "infinite buffer", unlike [`sync_channel`], which will @@ -201,6 +202,7 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) { } /// Creates a new synchronous, bounded channel. +/// /// All data sent on the [`Sender`] will become available on the [`Receiver`] /// in the same order as it was sent. Like asynchronous [`channel`]s, the /// [`Receiver`] will block until a message becomes available. `sync_channel` diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index 83a93a06369..c86b546e011 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -483,6 +483,7 @@ pub enum TrySendError<T> { } /// Creates a new asynchronous channel, returning the sender/receiver halves. +/// /// All data sent on the [`Sender`] will become available on the [`Receiver`] in /// the same order as it was sent, and no [`send`] will block the calling thread /// (this channel has an "infinite buffer", unlike [`sync_channel`], which will @@ -527,6 +528,7 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) { } /// Creates a new synchronous, bounded channel. +/// /// All data sent on the [`SyncSender`] will become available on the [`Receiver`] /// in the same order as it was sent. Like asynchronous [`channel`]s, the /// [`Receiver`] will block until a message becomes available. `sync_channel` diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 8e0609fe48c..5b65d862be1 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -352,8 +352,8 @@ impl Slice { unsafe impl CloneToUninit for Slice { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: we're just a wrapper around [u8] - unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: we're just a transparent wrapper around [u8] + unsafe { self.inner.clone_to_uninit(dst) } } } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index b3834388df6..a4ad5966afe 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -275,8 +275,8 @@ impl Slice { unsafe impl CloneToUninit for Slice { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: we're just a wrapper around Wtf8 - unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: we're just a transparent wrapper around Wtf8 + unsafe { self.inner.clone_to_uninit(dst) } } } diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 70f6981f717..17d15ed2e50 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -364,6 +364,26 @@ impl File { self.fsync() } + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result<bool> { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result<bool> { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + pub fn truncate(&self, _size: u64) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/pal/hermit/io.rs index aad1eef71e9..0424a1ac55a 100644 --- a/library/std/src/sys/pal/hermit/io.rs +++ b/library/std/src/sys/pal/hermit/io.rs @@ -33,7 +33,7 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub const fn as_slice(&self) -> &'a [u8] { unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } @@ -71,6 +71,11 @@ impl<'a> IoSliceMut<'a> { } #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index bce9aa6d99c..776a96ff3b7 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -350,6 +350,26 @@ impl File { self.flush() } + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result<bool> { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result<bool> { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + pub fn truncate(&self, _size: u64) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/pal/solid/io.rs b/library/std/src/sys/pal/solid/io.rs index 4b1f788a492..9ef4b7049b6 100644 --- a/library/std/src/sys/pal/solid/io.rs +++ b/library/std/src/sys/pal/solid/io.rs @@ -33,7 +33,7 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub const fn as_slice(&self) -> &'a [u8] { unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } @@ -71,6 +71,11 @@ impl<'a> IoSliceMut<'a> { } #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index f1f843a5f7a..96f99efb21e 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -1254,6 +1254,123 @@ impl File { } } + #[cfg(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn lock(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; + return Ok(()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn lock(&self) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "lock() not supported")) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn lock_shared(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; + return Ok(()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn lock_shared(&self) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn try_lock(&self) -> io::Result<bool> { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); + if let Err(ref err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + return Ok(false); + } + } + result?; + return Ok(true); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn try_lock(&self) -> io::Result<bool> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn try_lock_shared(&self) -> io::Result<bool> { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); + if let Err(ref err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + return Ok(false); + } + } + result?; + return Ok(true); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn try_lock_shared(&self) -> io::Result<bool> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn unlock(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; + return Ok(()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn unlock(&self) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + } + pub fn truncate(&self, size: u64) -> io::Result<()> { let size: off64_t = size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; diff --git a/library/std/src/sys/pal/unix/io.rs b/library/std/src/sys/pal/unix/io.rs index 181c35a971e..0d5a152dc0d 100644 --- a/library/std/src/sys/pal/unix/io.rs +++ b/library/std/src/sys/pal/unix/io.rs @@ -33,7 +33,7 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub const fn as_slice(&self) -> &'a [u8] { unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } @@ -71,6 +71,11 @@ impl<'a> IoSliceMut<'a> { } #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index f0ebc767bad..4a992e32a91 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -23,6 +23,7 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", + target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd", diff --git a/library/std/src/sys/pal/unsupported/fs.rs b/library/std/src/sys/pal/unsupported/fs.rs index 474c9fe97d1..9585ec24f68 100644 --- a/library/std/src/sys/pal/unsupported/fs.rs +++ b/library/std/src/sys/pal/unsupported/fs.rs @@ -198,6 +198,26 @@ impl File { self.0 } + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> io::Result<bool> { + self.0 + } + + pub fn try_lock_shared(&self) -> io::Result<bool> { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + pub fn truncate(&self, _size: u64) -> io::Result<()> { self.0 } diff --git a/library/std/src/sys/pal/unsupported/io.rs b/library/std/src/sys/pal/unsupported/io.rs index 6372fca74e0..604735d32d5 100644 --- a/library/std/src/sys/pal/unsupported/io.rs +++ b/library/std/src/sys/pal/unsupported/io.rs @@ -15,7 +15,7 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub const fn as_slice(&self) -> &'a [u8] { self.0 } } @@ -41,6 +41,11 @@ impl<'a> IoSliceMut<'a> { } #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + self.0 + } + + #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { self.0 } diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 59ecc45b06a..3296c762cca 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -420,6 +420,26 @@ impl File { self.fd.datasync() } + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result<bool> { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result<bool> { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + pub fn truncate(&self, size: u64) -> io::Result<()> { self.fd.filestat_set_size(size) } diff --git a/library/std/src/sys/pal/wasi/io.rs b/library/std/src/sys/pal/wasi/io.rs index b7c2f03daa0..57f81bc6257 100644 --- a/library/std/src/sys/pal/wasi/io.rs +++ b/library/std/src/sys/pal/wasi/io.rs @@ -30,7 +30,7 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub const fn as_slice(&self) -> &'a [u8] { unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } } } @@ -68,6 +68,11 @@ impl<'a> IoSliceMut<'a> { } #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } + } + + #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } } diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 192c95fd203..248ce3c9ff6 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2351,6 +2351,9 @@ Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW Windows.Win32.Storage.FileSystem.GetFullPathNameW Windows.Win32.Storage.FileSystem.GetTempPathW Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES +Windows.Win32.Storage.FileSystem.LOCKFILE_EXCLUSIVE_LOCK +Windows.Win32.Storage.FileSystem.LOCKFILE_FAIL_IMMEDIATELY +Windows.Win32.Storage.FileSystem.LockFileEx Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE @@ -2396,6 +2399,7 @@ Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS Windows.Win32.Storage.FileSystem.SYNCHRONIZE Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING +Windows.Win32.Storage.FileSystem.UnlockFile Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 52444c2c009..19925e59dfe 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -65,6 +65,7 @@ windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinit windows_targets::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL); +windows_targets::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : LOCK_FILE_FLAGS, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32); windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL); @@ -96,6 +97,7 @@ windows_targets::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32) windows_targets::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> BOOLEAN); windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn UnlockFile(hfile : HANDLE, dwfileoffsetlow : u32, dwfileoffsethigh : u32, nnumberofbytestounlocklow : u32, nnumberofbytestounlockhigh : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT); windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); @@ -2730,6 +2732,9 @@ pub struct LINGER { pub l_onoff: u16, pub l_linger: u16, } +pub const LOCKFILE_EXCLUSIVE_LOCK: LOCK_FILE_FLAGS = 2u32; +pub const LOCKFILE_FAIL_IMMEDIATELY: LOCK_FILE_FLAGS = 1u32; +pub type LOCK_FILE_FLAGS = u32; pub type LPOVERLAPPED_COMPLETION_ROUTINE = Option< unsafe extern "system" fn( dwerrorcode: u32, diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 5a9bfccc1fa..07e4f93a379 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -346,6 +346,120 @@ impl File { self.fsync() } + fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> { + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null()); + if event.is_null() { + return Err(io::Error::last_os_error()); + } + overlapped.hEvent = event; + let lock_result = cvt(c::LockFileEx( + self.handle.as_raw_handle(), + flags, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + )); + + let final_result = match lock_result { + Ok(_) => Ok(()), + Err(err) => { + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) { + // Wait for the lock to be acquired, and get the lock operation status. + // This can happen asynchronously, if the file handle was opened for async IO + let mut bytes_transferred = 0; + cvt(c::GetOverlappedResult( + self.handle.as_raw_handle(), + &mut overlapped, + &mut bytes_transferred, + c::TRUE, + )) + .map(|_| ()) + } else { + Err(err) + } + } + }; + c::CloseHandle(overlapped.hEvent); + final_result + } + } + + pub fn lock(&self) -> io::Result<()> { + self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK) + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.acquire_lock(0) + } + + pub fn try_lock(&self) -> io::Result<bool> { + let result = cvt(unsafe { + let mut overlapped = mem::zeroed(); + c::LockFileEx( + self.handle.as_raw_handle(), + c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + ) + }); + + match result { + Ok(_) => Ok(true), + Err(err) + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) + || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => + { + Ok(false) + } + Err(err) => Err(err), + } + } + + pub fn try_lock_shared(&self) -> io::Result<bool> { + let result = cvt(unsafe { + let mut overlapped = mem::zeroed(); + c::LockFileEx( + self.handle.as_raw_handle(), + c::LOCKFILE_FAIL_IMMEDIATELY, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + ) + }); + + match result { + Ok(_) => Ok(true), + Err(err) + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) + || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => + { + Ok(false) + } + Err(err) => Err(err), + } + } + + pub fn unlock(&self) -> io::Result<()> { + // Unlock the handle twice because LockFileEx() allows a file handle to acquire + // both an exclusive and shared lock, in which case the documentation states that: + // "...two unlock operations are necessary to unlock the region; the first unlock operation + // unlocks the exclusive lock, the second unlock operation unlocks the shared lock" + cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?; + let result = + cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) }); + match result { + Ok(_) => Ok(()), + Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()), + Err(err) => Err(err), + } + } + pub fn truncate(&self, size: u64) -> io::Result<()> { let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs index 1e7d02908f6..f2865d2ffc1 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/pal/windows/io.rs @@ -36,7 +36,7 @@ impl<'a> IoSlice<'a> { } #[inline] - pub fn as_slice(&self) -> &[u8] { + pub const fn as_slice(&self) -> &'a [u8] { unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } } @@ -75,6 +75,11 @@ impl<'a> IoSliceMut<'a> { } #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } + } + + #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 19d4c94f450..666942bb8a1 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -1052,8 +1052,8 @@ impl Hash for Wtf8 { unsafe impl CloneToUninit for Wtf8 { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut Self) { - // SAFETY: we're just a wrapper around [u8] - unsafe { self.bytes.clone_to_uninit(&raw mut (*dst).bytes) } + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: we're just a transparent wrapper around [u8] + unsafe { self.bytes.clone_to_uninit(dst) } } } |
