diff options
Diffstat (limited to 'library/core/src/slice')
-rw-r--r-- | library/core/src/slice/index.rs | 21 | ||||
-rw-r--r-- | library/core/src/slice/iter.rs | 32 | ||||
-rw-r--r-- | library/core/src/slice/mod.rs | 173 |
3 files changed, 197 insertions, 29 deletions
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 6d2f7330d..c295a0e06 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -31,9 +31,8 @@ where } } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cold] #[track_caller] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { @@ -48,19 +47,20 @@ const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { } // FIXME const-hack +#[inline] #[track_caller] fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range start index {index} out of range for slice of length {len}"); } +#[inline] #[track_caller] const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! { panic!("slice start index is out of range for slice"); } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cold] #[track_caller] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { @@ -71,19 +71,20 @@ const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { } // FIXME const-hack +#[inline] #[track_caller] fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range end index {index} out of range for slice of length {len}"); } +#[inline] #[track_caller] const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! { panic!("slice end index is out of range for slice"); } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cold] #[track_caller] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] const fn slice_index_order_fail(index: usize, end: usize) -> ! { @@ -92,27 +93,27 @@ const fn slice_index_order_fail(index: usize, end: usize) -> ! { } // FIXME const-hack +#[inline] #[track_caller] fn slice_index_order_fail_rt(index: usize, end: usize) -> ! { panic!("slice index starts at {index} but ends at {end}"); } +#[inline] #[track_caller] const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! { panic!("slice index start is larger than end"); } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cold] #[track_caller] const fn slice_start_index_overflow_fail() -> ! { panic!("attempted to index slice from after maximum usize"); } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cold] #[track_caller] const fn slice_end_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 8a8962828..062289767 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1834,6 +1834,20 @@ impl<'a, T> ChunksExact<'a, T> { /// Returns the remainder of the original slice that is not going to be /// returned by the iterator. The returned slice has at most `chunk_size-1` /// elements. + /// + /// # Example + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.chunks_exact(2); + /// assert_eq!(iter.remainder(), &['m'][..]); + /// assert_eq!(iter.next(), Some(&['l', 'o'][..])); + /// assert_eq!(iter.remainder(), &['m'][..]); + /// assert_eq!(iter.next(), Some(&['r', 'e'][..])); + /// assert_eq!(iter.remainder(), &['m'][..]); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.remainder(), &['m'][..]); + /// ``` #[must_use] #[stable(feature = "chunks_exact", since = "1.31.0")] pub fn remainder(&self) -> &'a [T] { @@ -2869,7 +2883,7 @@ unsafe impl<T> Sync for RChunksMut<'_, T> where T: Sync {} /// ``` /// /// [`rchunks_exact`]: slice::rchunks_exact -/// [`remainder`]: ChunksExact::remainder +/// [`remainder`]: RChunksExact::remainder /// [slices]: slice #[derive(Debug)] #[stable(feature = "rchunks", since = "1.31.0")] @@ -2892,6 +2906,20 @@ impl<'a, T> RChunksExact<'a, T> { /// Returns the remainder of the original slice that is not going to be /// returned by the iterator. The returned slice has at most `chunk_size-1` /// elements. + /// + /// # Example + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.rchunks_exact(2); + /// assert_eq!(iter.remainder(), &['l'][..]); + /// assert_eq!(iter.next(), Some(&['e', 'm'][..])); + /// assert_eq!(iter.remainder(), &['l'][..]); + /// assert_eq!(iter.next(), Some(&['o', 'r'][..])); + /// assert_eq!(iter.remainder(), &['l'][..]); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.remainder(), &['l'][..]); + /// ``` #[must_use] #[stable(feature = "rchunks", since = "1.31.0")] pub fn remainder(&self) -> &'a [T] { @@ -3031,7 +3059,7 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> { /// ``` /// /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut -/// [`into_remainder`]: ChunksExactMut::into_remainder +/// [`into_remainder`]: RChunksExactMut::into_remainder /// [slices]: slice #[derive(Debug)] #[stable(feature = "rchunks", since = "1.31.0")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 4f1bb1734..d9281a925 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -7,6 +7,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Greater, Less}; +use crate::fmt; use crate::intrinsics::{assert_unsafe_precondition, exact_div}; use crate::marker::Copy; use crate::mem::{self, SizedTypeProperties}; @@ -464,7 +465,7 @@ impl<T> [T] { /// [`as_mut_ptr`]: slice::as_mut_ptr #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] - #[inline] + #[inline(always)] #[must_use] pub const fn as_ptr(&self) -> *const T { self as *const [T] as *const T @@ -494,7 +495,7 @@ impl<T> [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[rustc_allow_const_fn_unstable(const_mut_refs)] - #[inline] + #[inline(always)] #[must_use] pub const fn as_mut_ptr(&mut self) -> *mut T { self as *mut [T] as *mut T @@ -3467,10 +3468,11 @@ impl<T> [T] { /// maintained. /// /// This method splits the slice into three distinct slices: prefix, correctly aligned middle - /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest - /// length possible for a given type and input slice, but only your algorithm's performance - /// should depend on that, not its correctness. It is permissible for all of the input data to - /// be returned as the prefix or suffix slice. + /// slice of a new type, and the suffix slice. How exactly the slice is split up is not + /// specified; the middle part may be smaller than necessary. However, if this fails to return a + /// maximal middle part, that is because code is running in a context where performance does not + /// matter, such as a sanitizer attempting to find alignment bugs. Regular code running + /// in a default (debug or release) execution *will* return a maximal middle part. /// /// This method has no purpose when either input element `T` or output element `U` are /// zero-sized and will return the original slice without splitting anything. @@ -3524,14 +3526,15 @@ impl<T> [T] { } } - /// Transmute the slice to a slice of another type, ensuring alignment of the types is - /// maintained. + /// Transmute the mutable slice to a mutable slice of another type, ensuring alignment of the + /// types is maintained. /// /// This method splits the slice into three distinct slices: prefix, correctly aligned middle - /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest - /// length possible for a given type and input slice, but only your algorithm's performance - /// should depend on that, not its correctness. It is permissible for all of the input data to - /// be returned as the prefix or suffix slice. + /// slice of a new type, and the suffix slice. How exactly the slice is split up is not + /// specified; the middle part may be smaller than necessary. However, if this fails to return a + /// maximal middle part, that is because code is running in a context where performance does not + /// matter, such as a sanitizer attempting to find alignment bugs. Regular code running + /// in a default (debug or release) execution *will* return a maximal middle part. /// /// This method has no purpose when either input element `T` or output element `U` are /// zero-sized and will return the original slice without splitting anything. @@ -3667,7 +3670,8 @@ impl<T> [T] { unsafe { self.align_to() } } - /// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix. + /// Split a mutable slice into a mutable prefix, a middle of aligned SIMD types, + /// and a mutable suffix. /// /// This is a safe wrapper around [`slice::align_to_mut`], so has the same weak /// postconditions as that method. You're only assured that @@ -3751,9 +3755,9 @@ impl<T> [T] { /// [`is_sorted`]: slice::is_sorted #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] #[must_use] - pub fn is_sorted_by<F>(&self, mut compare: F) -> bool + pub fn is_sorted_by<'a, F>(&'a self, mut compare: F) -> bool where - F: FnMut(&T, &T) -> Option<Ordering>, + F: FnMut(&'a T, &'a T) -> Option<Ordering>, { self.iter().is_sorted_by(|a, b| compare(*a, *b)) } @@ -3777,9 +3781,9 @@ impl<T> [T] { #[inline] #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] #[must_use] - pub fn is_sorted_by_key<F, K>(&self, f: F) -> bool + pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool where - F: FnMut(&T) -> K, + F: FnMut(&'a T) -> K, K: PartialOrd, { self.iter().is_sorted_by_key(f) @@ -4081,6 +4085,88 @@ impl<T> [T] { *self = rem; Some(last) } + + /// Returns mutable references to many indices at once, without doing any checks. + /// + /// For a safe alternative see [`get_many_mut`]. + /// + /// # Safety + /// + /// Calling this method with overlapping or out-of-bounds indices is *[undefined behavior]* + /// even if the resulting references are not used. + /// + /// # Examples + /// + /// ``` + /// #![feature(get_many_mut)] + /// + /// let x = &mut [1, 2, 4]; + /// + /// unsafe { + /// let [a, b] = x.get_many_unchecked_mut([0, 2]); + /// *a *= 10; + /// *b *= 100; + /// } + /// assert_eq!(x, &[10, 2, 400]); + /// ``` + /// + /// [`get_many_mut`]: slice::get_many_mut + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + #[unstable(feature = "get_many_mut", issue = "104642")] + #[inline] + pub unsafe fn get_many_unchecked_mut<const N: usize>( + &mut self, + indices: [usize; N], + ) -> [&mut T; N] { + // NB: This implementation is written as it is because any variation of + // `indices.map(|i| self.get_unchecked_mut(i))` would make miri unhappy, + // or generate worse code otherwise. This is also why we need to go + // through a raw pointer here. + let slice: *mut [T] = self; + let mut arr: mem::MaybeUninit<[&mut T; N]> = mem::MaybeUninit::uninit(); + let arr_ptr = arr.as_mut_ptr(); + + // SAFETY: We expect `indices` to contain disjunct values that are + // in bounds of `self`. + unsafe { + for i in 0..N { + let idx = *indices.get_unchecked(i); + *(*arr_ptr).get_unchecked_mut(i) = &mut *slice.get_unchecked_mut(idx); + } + arr.assume_init() + } + } + + /// Returns mutable references to many indices at once. + /// + /// Returns an error if any index is out-of-bounds, or if the same index was + /// passed more than once. + /// + /// # Examples + /// + /// ``` + /// #![feature(get_many_mut)] + /// + /// let v = &mut [1, 2, 3]; + /// if let Ok([a, b]) = v.get_many_mut([0, 2]) { + /// *a = 413; + /// *b = 612; + /// } + /// assert_eq!(v, &[413, 2, 612]); + /// ``` + #[unstable(feature = "get_many_mut", issue = "104642")] + #[inline] + pub fn get_many_mut<const N: usize>( + &mut self, + indices: [usize; N], + ) -> Result<[&mut T; N], GetManyMutError<N>> { + if !get_many_check_valid(&indices, self.len()) { + return Err(GetManyMutError { _private: () }); + } + // SAFETY: The `get_many_check_valid()` call checked that all indices + // are disjunct and in bounds. + unsafe { Ok(self.get_many_unchecked_mut(indices)) } + } } impl<T, const N: usize> [[T; N]] { @@ -4303,3 +4389,56 @@ impl<T, const N: usize> SlicePattern for [T; N] { self } } + +/// This checks every index against each other, and against `len`. +/// +/// This will do `binomial(N + 1, 2) = N * (N + 1) / 2 = 0, 1, 3, 6, 10, ..` +/// comparison operations. +fn get_many_check_valid<const N: usize>(indices: &[usize; N], len: usize) -> bool { + // NB: The optimzer should inline the loops into a sequence + // of instructions without additional branching. + let mut valid = true; + for (i, &idx) in indices.iter().enumerate() { + valid &= idx < len; + for &idx2 in &indices[..i] { + valid &= idx != idx2; + } + } + valid +} + +/// The error type returned by [`get_many_mut<N>`][`slice::get_many_mut`]. +/// +/// It indicates one of two possible errors: +/// - An index is out-of-bounds. +/// - The same index appeared multiple times in the array. +/// +/// # Examples +/// +/// ``` +/// #![feature(get_many_mut)] +/// +/// let v = &mut [1, 2, 3]; +/// assert!(v.get_many_mut([0, 999]).is_err()); +/// assert!(v.get_many_mut([1, 1]).is_err()); +/// ``` +#[unstable(feature = "get_many_mut", issue = "104642")] +// NB: The N here is there to be forward-compatible with adding more details +// to the error type at a later point +pub struct GetManyMutError<const N: usize> { + _private: (), +} + +#[unstable(feature = "get_many_mut", issue = "104642")] +impl<const N: usize> fmt::Debug for GetManyMutError<N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GetManyMutError").finish_non_exhaustive() + } +} + +#[unstable(feature = "get_many_mut", issue = "104642")] +impl<const N: usize> fmt::Display for GetManyMutError<N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt("an index is out of bounds or appeared multiple times in the array", f) + } +} |