Skip to content

Commit 1b3be3e

Browse files
committed
Add a Layout::array_unchecked for RawVec to use
Instead of it writing the code for it itself.
1 parent 6292b2a commit 1b3be3e

File tree

2 files changed

+54
-27
lines changed

2 files changed

+54
-27
lines changed

library/alloc/src/raw_vec.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,9 @@ impl<T, A: Allocator> RawVec<T, A> {
300300
if T::IS_ZST || self.cap.0 == 0 {
301301
None
302302
} else {
303-
// We could use Layout::array here which ensures the absence of isize and usize overflows
304-
// and could hypothetically handle differences between stride and size, but this memory
305-
// has already been allocated so we know it can't overflow and currently Rust does not
306-
// support such types. So we can do better by skipping some checks and avoid an unwrap.
307-
const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
308-
unsafe {
309-
let align = mem::align_of::<T>();
310-
let size = mem::size_of::<T>().unchecked_mul(self.cap.0);
311-
let layout = Layout::from_size_align_unchecked(size, align);
312-
Some((self.ptr.cast().into(), layout))
313-
}
303+
// SAFETY: This memory has already been allocated so we know it can't overflow
304+
let layout = unsafe { Layout::array_unchecked::<T>(self.cap.0) };
305+
Some((self.ptr.cast().into(), layout))
314306
}
315307
}
316308

library/core/src/alloc/layout.rs

+51-16
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
// collections, resulting in having to optimize down excess IR multiple times.
55
// Your performance intuition is useless. Run perf.
66

7+
use crate::assert_unsafe_precondition;
78
use crate::cmp;
89
use crate::error::Error;
910
use crate::fmt;
11+
use crate::intrinsics;
1012
use crate::mem;
1113
use crate::ptr::{Alignment, NonNull};
1214

@@ -425,43 +427,76 @@ impl Layout {
425427
Layout::from_size_alignment(new_size, self.align)
426428
}
427429

428-
/// Creates a layout describing the record for a `[T; n]`.
430+
/// Creates a layout describing the record for a `[T; len]`.
429431
///
430432
/// On arithmetic overflow or when the total size would exceed
431433
/// `isize::MAX`, returns `LayoutError`.
432434
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
433435
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
434436
#[inline]
435-
pub const fn array<T>(n: usize) -> Result<Self, LayoutError> {
437+
pub const fn array<T>(len: usize) -> Result<Self, LayoutError> {
436438
// Reduce the amount of code we need to monomorphize per `T`.
437-
return inner(mem::size_of::<T>(), Alignment::of::<T>(), n);
439+
return inner(Layout::new::<T>(), len);
438440

439441
#[inline]
440-
const fn inner(
441-
element_size: usize,
442-
align: Alignment,
443-
n: usize,
444-
) -> Result<Layout, LayoutError> {
442+
const fn inner(element: Layout, len: usize) -> Result<Layout, LayoutError> {
445443
// We need to check two things about the size:
446444
// - That the total size won't overflow a `usize`, and
447445
// - That the total size still fits in an `isize`.
448446
// By using division we can check them both with a single threshold.
449447
// That'd usually be a bad idea, but thankfully here the element size
450448
// and alignment are constants, so the compiler will fold all of it.
451-
if element_size != 0 && n > Layout::max_size_for_align(align) / element_size {
449+
if element.size != 0 && len > Layout::max_size_for_align(element.align) / element.size {
452450
return Err(LayoutError);
453451
}
454452

455-
// SAFETY: We just checked that we won't overflow `usize` when we multiply.
453+
// SAFETY: We just checked above that the `array_size` will not
454+
// exceed `isize::MAX` even when rounded up to the alignment.
455+
unsafe { Ok(Layout::array_of_unchecked(element, len)) }
456+
}
457+
}
458+
459+
/// Creates a layout describing the record for a `[T; n]`, without checking
460+
/// whether the `len` is an allowed length.
461+
///
462+
/// # Safety
463+
///
464+
/// `len` must be small enough that the total size *in bytes* of the
465+
/// resulting layout is `isize::MAX` or less.
466+
///
467+
/// Equivalently, either `T` is a ZST or `len <= isize::MAX / size_of::<T>()`.
468+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
469+
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
470+
#[inline]
471+
pub const unsafe fn array_unchecked<T>(len: usize) -> Self {
472+
// SAFETY: `T` is a Rust type, so its size is a multiple of align,
473+
// and our safety precondition ensures that len is small enough.
474+
unsafe { Self::array_of_unchecked(Layout::new::<T>(), len) }
475+
}
476+
477+
/// # Safety
478+
/// - The element layout must be that of a rust type, with size a multiple of align
479+
/// - The total size of the resulting array must be at most `isize::MAX`
480+
#[inline]
481+
const unsafe fn array_of_unchecked(element: Layout, len: usize) -> Layout {
482+
assert_unsafe_precondition!(
483+
check_language_ub,
484+
"array_of_unchecked element must have size an integer multiple of \
485+
alignment and the total length must be be isize::MAX or less",
486+
(
487+
size: usize = element.size,
488+
align: usize = element.align.as_usize(),
489+
len: usize = len,
490+
) => size % align == 0 && (size as u128) * (len as u128) <= (isize::MAX as u128),
491+
);
492+
493+
// SAFETY: Out preconditions are exactly what's needed here
494+
unsafe {
456495
// This is a useless hint inside this function, but after inlining this helps
457496
// deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
458497
// allocation path) before/after this multiplication.
459-
let array_size = unsafe { element_size.unchecked_mul(n) };
460-
461-
// SAFETY: We just checked above that the `array_size` will not
462-
// exceed `isize::MAX` even when rounded up to the alignment.
463-
// And `Alignment` guarantees it's a power of two.
464-
unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) }
498+
let bytes = intrinsics::unchecked_mul(element.size, len);
499+
Self { size: bytes, align: element.align }
465500
}
466501
}
467502
}

0 commit comments

Comments
 (0)