|
4 | 4 | // collections, resulting in having to optimize down excess IR multiple times.
|
5 | 5 | // Your performance intuition is useless. Run perf.
|
6 | 6 |
|
| 7 | +use crate::assert_unsafe_precondition; |
7 | 8 | use crate::cmp;
|
8 | 9 | use crate::error::Error;
|
9 | 10 | use crate::fmt;
|
| 11 | +use crate::intrinsics; |
10 | 12 | use crate::mem;
|
11 | 13 | use crate::ptr::{Alignment, NonNull};
|
12 | 14 |
|
@@ -425,43 +427,76 @@ impl Layout {
|
425 | 427 | Layout::from_size_alignment(new_size, self.align)
|
426 | 428 | }
|
427 | 429 |
|
428 |
| - /// Creates a layout describing the record for a `[T; n]`. |
| 430 | + /// Creates a layout describing the record for a `[T; len]`. |
429 | 431 | ///
|
430 | 432 | /// On arithmetic overflow or when the total size would exceed
|
431 | 433 | /// `isize::MAX`, returns `LayoutError`.
|
432 | 434 | #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
|
433 | 435 | #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
|
434 | 436 | #[inline]
|
435 |
| - pub const fn array<T>(n: usize) -> Result<Self, LayoutError> { |
| 437 | + pub const fn array<T>(len: usize) -> Result<Self, LayoutError> { |
436 | 438 | // 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); |
438 | 440 |
|
439 | 441 | #[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> { |
445 | 443 | // We need to check two things about the size:
|
446 | 444 | // - That the total size won't overflow a `usize`, and
|
447 | 445 | // - That the total size still fits in an `isize`.
|
448 | 446 | // By using division we can check them both with a single threshold.
|
449 | 447 | // That'd usually be a bad idea, but thankfully here the element size
|
450 | 448 | // 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 { |
452 | 450 | return Err(LayoutError);
|
453 | 451 | }
|
454 | 452 |
|
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 { |
456 | 495 | // This is a useless hint inside this function, but after inlining this helps
|
457 | 496 | // deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
|
458 | 497 | // 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 } |
465 | 500 | }
|
466 | 501 | }
|
467 | 502 | }
|
|
0 commit comments