Skip to content

Commit 2933ad9

Browse files
committed
Add stronger alternatives to align_to
1 parent af669c2 commit 2933ad9

File tree

1 file changed

+202
-0
lines changed

1 file changed

+202
-0
lines changed

library/core/src/slice/mod.rs

+202
Original file line numberDiff line numberDiff line change
@@ -3485,6 +3485,10 @@ impl<T> [T] {
34853485
/// matter, such as a sanitizer attempting to find alignment bugs. Regular code running
34863486
/// in a default (debug or release) execution *will* return a maximal middle part.
34873487
///
3488+
/// If this behavior is not what you desire, as you don't want fallback paths for the bytes
3489+
/// outside the aligned part, consider using [aligned_subslice] or [transmute_elements] instead,
3490+
/// as these have stronger guarantees.
3491+
///
34883492
/// This method has no purpose when either input element `T` or output element `U` are
34893493
/// zero-sized and will return the original slice without splitting anything.
34903494
///
@@ -3547,6 +3551,10 @@ impl<T> [T] {
35473551
/// matter, such as a sanitizer attempting to find alignment bugs. Regular code running
35483552
/// in a default (debug or release) execution *will* return a maximal middle part.
35493553
///
3554+
/// If this behavior is not what you desire, as you don't want fallback paths for the bytes
3555+
/// outside the aligned part, consider using [aligned_subslice_mut] or [transmute_elements_mut]
3556+
/// instead, as these have stronger guarantees.
3557+
///
35503558
/// This method has no purpose when either input element `T` or output element `U` are
35513559
/// zero-sized and will return the original slice without splitting anything.
35523560
///
@@ -3607,6 +3615,200 @@ impl<T> [T] {
36073615
}
36083616
}
36093617

3618+
/// Get a subslice where the first element is aligned to a given alignment and the size
3619+
/// of the slice is a multiple of the alignment.
3620+
///
3621+
/// # Panics
3622+
///
3623+
/// This method requires the alignment to be a multiple (larger than 1) of the alignment of
3624+
/// the slice element's alignment.
3625+
///
3626+
/// # Examples
3627+
///
3628+
/// Basic usage:
3629+
///
3630+
/// ```
3631+
/// #![feature(slice_align_to_ish)]
3632+
/// let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
3633+
/// let ints = bytes.aligned_subslice(std::mem::align_of::<u32>());
3634+
/// assert_eq!(ints.len(), 1);
3635+
/// ```
3636+
#[must_use]
3637+
#[unstable(feature = "slice_align_to_ish", issue = "none")]
3638+
#[inline]
3639+
pub fn aligned_subslice(&self, align: usize) -> &[T] {
3640+
let size = crate::mem::size_of::<T>();
3641+
assert!(
3642+
size < align,
3643+
"aligned_subslice does nothing for alignments below or at the element type's alignment"
3644+
);
3645+
assert!(
3646+
align % size == 0,
3647+
"aligned_subslice only works for alignments that are multiples of the element's size"
3648+
);
3649+
let offset = self.as_ptr().addr() % align;
3650+
// SAFETY: See the `align_to_mut` method for the detailed safety comment.
3651+
let end_offset = unsafe { self.as_ptr().offset(self.len() as isize) }.addr() % align;
3652+
let end = self.len() - (align / size - end_offset);
3653+
&self[offset..end]
3654+
}
3655+
3656+
/// Get a subslice where the first element is aligned to a given alignment and the size
3657+
/// of the slice is a multiple of the alignment.
3658+
///
3659+
/// # Panics
3660+
///
3661+
/// This method requires the alignment to be a multiple (larger than 1) of the alignment of
3662+
/// the slice element's alignment.
3663+
///
3664+
/// # Examples
3665+
///
3666+
/// Basic usage:
3667+
///
3668+
/// ```
3669+
/// #![feature(slice_align_to_ish)]
3670+
/// let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
3671+
/// let ints = bytes.aligned_subslice_mut(std::mem::align_of::<u32>());
3672+
/// assert_eq!(ints.len(), 1);
3673+
/// ```
3674+
#[must_use]
3675+
#[unstable(feature = "slice_align_to_ish", issue = "none")]
3676+
#[inline]
3677+
pub fn aligned_subslice_mut(&mut self, align: usize) -> &mut [T] {
3678+
let size = crate::mem::size_of::<T>();
3679+
assert!(
3680+
size < align,
3681+
"aligned_subslice does nothing for alignments below or at the element type's alignment"
3682+
);
3683+
assert!(
3684+
align % size == 0,
3685+
"aligned_subslice only works for alignments that are multiples of the element's size"
3686+
);
3687+
let offset = self.as_ptr().addr() % align;
3688+
// SAFETY: See the `align_to_mut` method for the detailed safety comment.
3689+
let end_offset = unsafe { self.as_ptr().offset(self.len() as isize) }.addr() % align;
3690+
let end = self.len() - (align / size - end_offset);
3691+
&mut self[offset..end]
3692+
}
3693+
3694+
/// Transmute the slice elements to another type.
3695+
///
3696+
/// If the target element type is smaller than the source element type, the
3697+
/// returned slice will have multiple elements per element of the original slice.
3698+
///
3699+
/// Cannot be used to go to an element type with higher alignment requirements.
3700+
/// Use `aligned_subslice` for that instead.
3701+
///
3702+
/// # Panics
3703+
///
3704+
/// The element sizes and the slice length must be such that all elements of the source
3705+
/// slice fit exactly into a slice of the destination element type. Resize your input slice
3706+
/// before invoking `transmute_elements` to uphold this checked requirement.
3707+
///
3708+
/// # Safety
3709+
///
3710+
/// This method is essentially a `transmute` between different elements, and even from
3711+
/// multiple elements into a single one or vice versa, so all the usual caveats
3712+
/// pertaining to `transmute::<T, U>` also apply here.
3713+
///
3714+
/// # Examples
3715+
///
3716+
/// Basic usage:
3717+
///
3718+
/// ```
3719+
/// unsafe {
3720+
/// let ints: [u32; 2] = [1, 2];
3721+
/// let smaller_ints = ints.transmute_elements::<u16>();
3722+
/// assert_eq!(smaller_ints.len(), 4);
3723+
/// }
3724+
/// ```
3725+
#[must_use]
3726+
#[unstable(feature = "slice_align_to_ish", issue = "none")]
3727+
#[track_caller]
3728+
pub const unsafe fn transmute_elements<U>(&self) -> &[U] {
3729+
const {
3730+
let align_u = crate::mem::align_of::<U>();
3731+
let align_t = crate::mem::align_of::<T>();
3732+
assert!(align_u <= align_t, "use `aligned_subslice` instead");
3733+
};
3734+
let size_u = crate::mem::size_of::<U>();
3735+
let size_t = crate::mem::size_of::<T>();
3736+
if size_u > size_t {
3737+
assert!(
3738+
self.len() * size_u % size_t == 0,
3739+
"input slice does not fit exactly into a slice of the output element type"
3740+
);
3741+
} else {
3742+
assert!(
3743+
self.len() * size_t % size_u == 0,
3744+
"input slice does not fit exactly into a slice of the output element type"
3745+
);
3746+
}
3747+
// SAFETY: The size of the slice is such that with the new element size, all new
3748+
// elements are still within the bounds of the original slice. The change in element
3749+
// type is something the caller needs to make sure is sound.
3750+
unsafe { from_raw_parts(self.as_ptr() as *const _, self.len() * size_t / size_u) }
3751+
}
3752+
3753+
/// Transmute the slice elements to another type.
3754+
///
3755+
/// If the target element type is smaller than the source element type, the
3756+
/// returned slice will have multiple elements per element of the original slice.
3757+
///
3758+
/// Cannot be used to go to an element type with higher alignment requirements.
3759+
/// Use `aligned_subslice_mut` for that instead.
3760+
///
3761+
/// # Panics
3762+
///
3763+
/// The element sizes and the slice length must be such that all elements of the source
3764+
/// slice fit exactly into a slice of the destination element type. Resize your input slice
3765+
/// before invoking `transmute_elements_mut` to uphold this checked requirement.
3766+
///
3767+
/// # Safety
3768+
///
3769+
/// This method is essentially a `transmute` between different elements, and even from
3770+
/// multiple elements into a single one or vice versa, so all the usual caveats
3771+
/// pertaining to `transmute::<T, U>` also apply here.
3772+
///
3773+
/// # Examples
3774+
///
3775+
/// Basic usage:
3776+
///
3777+
/// ```
3778+
/// unsafe {
3779+
/// let mut ints: [u32; 2] = [1, 2];
3780+
/// let smaller_ints = ints.transmute_elements_mut::<u16>();
3781+
/// assert_eq!(smaller_ints.len(), 4);
3782+
/// }
3783+
/// ```
3784+
#[must_use]
3785+
#[unstable(feature = "slice_align_to_ish", issue = "none")]
3786+
#[track_caller]
3787+
pub const unsafe fn transmute_elements_mut<U>(&mut self) -> &mut [U] {
3788+
const {
3789+
let align_u = crate::mem::align_of::<U>();
3790+
let align_t = crate::mem::align_of::<T>();
3791+
assert!(align_u <= align_t, "use `aligned_subslice_mut` instead");
3792+
};
3793+
let size_u = crate::mem::size_of::<U>();
3794+
let size_t = crate::mem::size_of::<T>();
3795+
if size_u > size_t {
3796+
assert!(
3797+
self.len() * size_u % size_t == 0,
3798+
"input slice does not fit exactly into a slice of the output element type"
3799+
);
3800+
} else {
3801+
assert!(
3802+
self.len() * size_t % size_u == 0,
3803+
"input slice does not fit exactly into a slice of the output element type"
3804+
);
3805+
}
3806+
// SAFETY: The size of the slice is such that with the new element size, all new
3807+
// elements are still within the bounds of the original slice. The change in element
3808+
// type is something the caller needs to make sure is sound.
3809+
unsafe { from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len() * size_t / size_u) }
3810+
}
3811+
36103812
/// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix.
36113813
///
36123814
/// This is a safe wrapper around [`slice::align_to`], so has the same weak

0 commit comments

Comments
 (0)