55// Your performance intuition is useless. Run perf.
66
77use crate :: error:: Error ;
8+ use crate :: intrinsics:: { unchecked_add, unchecked_mul, unchecked_sub} ;
9+ use crate :: mem:: SizedTypeProperties ;
810use crate :: ptr:: { Alignment , NonNull } ;
9- use crate :: { assert_unsafe_precondition, cmp , fmt, mem} ;
11+ use crate :: { assert_unsafe_precondition, fmt, mem} ;
1012
1113// While this function is used in one place and its implementation
1214// could be inlined, the previous attempts to do so made rustc
@@ -98,7 +100,10 @@ impl Layout {
98100 //
99101 // Above implies that checking for summation overflow is both
100102 // necessary and sufficient.
101- isize:: MAX as usize - ( align. as_usize ( ) - 1 )
103+
104+ // SAFETY: the maximum possible alignment is `isize::MAX + 1`,
105+ // so the subtraction cannot overflow.
106+ unsafe { unchecked_sub ( isize:: MAX as usize + 1 , align. as_usize ( ) ) }
102107 }
103108
104109 /// Internal helper constructor to skip revalidating alignment validity.
@@ -252,9 +257,14 @@ impl Layout {
252257 /// Returns an error if the combination of `self.size()` and the given
253258 /// `align` violates the conditions listed in [`Layout::from_size_align`].
254259 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
260+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
255261 #[ inline]
256- pub fn align_to ( & self , align : usize ) -> Result < Self , LayoutError > {
257- Layout :: from_size_align ( self . size ( ) , cmp:: max ( self . align ( ) , align) )
262+ pub const fn align_to ( & self , align : usize ) -> Result < Self , LayoutError > {
263+ if let Some ( align) = Alignment :: new ( align) {
264+ Layout :: from_size_alignment ( self . size , Alignment :: max ( self . align , align) )
265+ } else {
266+ Err ( LayoutError )
267+ }
258268 }
259269
260270 /// Returns the amount of padding we must insert after `self`
@@ -279,29 +289,45 @@ impl Layout {
279289 without modifying the `Layout`"]
280290 #[ inline]
281291 pub const fn padding_needed_for ( & self , align : usize ) -> usize {
282- let len = self . size ( ) ;
292+ // FIXME: Can we just change the type on this to `Alignment`?
293+ let Some ( align) = Alignment :: new ( align) else { return usize:: MAX } ;
294+ let len_rounded_up = self . size_rounded_up_to_custom_align ( align) ;
295+ // SAFETY: Cannot overflow because the rounded-up value is never less
296+ unsafe { unchecked_sub ( len_rounded_up, self . size ) }
297+ }
283298
299+ /// Returns the smallest multiple of `align` greater than or equal to `self.size()`.
300+ ///
301+ /// This can return at most `Alignment::MAX` (aka `isize::MAX + 1`)
302+ /// because the original size is at most `isize::MAX`.
303+ #[ inline]
304+ const fn size_rounded_up_to_custom_align ( & self , align : Alignment ) -> usize {
305+ let len = self . size ;
306+
307+ // SAFETY:
284308 // Rounded up value is:
285309 // len_rounded_up = (len + align - 1) & !(align - 1);
286310 // and then we return the padding difference: `len_rounded_up - len`.
287311 //
288- // We use modular arithmetic throughout :
312+ // The arithmetic we do here can never overflow :
289313 //
290314 // 1. align is guaranteed to be > 0, so align - 1 is always
291315 // valid.
292316 //
293- // 2. `len + align - 1` can overflow by at most `align - 1`,
294- // so the &-mask with `!(align - 1)` will ensure that in the
295- // case of overflow, `len_rounded_up` will itself be 0.
296- // Thus the returned padding, when added to `len`, yields 0,
297- // which trivially satisfies the alignment `align`.
317+ // 2. len is at most `isize::MAX`, so adding `align - 1` can never
318+ // overflow a `usize`.
298319 //
299- // (Of course, attempts to allocate blocks of memory whose
300- // size and padding overflow in the above manner should cause
301- // the allocator to yield an error anyway.)
302-
303- let len_rounded_up = len. wrapping_add ( align) . wrapping_sub ( 1 ) & !align. wrapping_sub ( 1 ) ;
304- len_rounded_up. wrapping_sub ( len)
320+ // 3. masking by the alignment can remove at most `align - 1`,
321+ // which is what we just added, the value we return is never
322+ // less than the original `len`.
323+ //
324+ // (Size 0 Align MAX is already aligned, so stays the same, but things like
325+ // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.)
326+ unsafe {
327+ let align_m1 = unchecked_sub ( align. as_usize ( ) , 1 ) ;
328+ let len_rounded_up = unchecked_add ( len, align_m1) & !align_m1;
329+ len_rounded_up
330+ }
305331 }
306332
307333 /// Creates a layout by rounding the size of this layout up to a multiple
@@ -315,12 +341,11 @@ impl Layout {
315341 without modifying the original"]
316342 #[ inline]
317343 pub const fn pad_to_align ( & self ) -> Layout {
318- let pad = self . padding_needed_for ( self . align ( ) ) ;
319344 // This cannot overflow. Quoting from the invariant of Layout:
320345 // > `size`, when rounded up to the nearest multiple of `align`,
321346 // > must not overflow isize (i.e., the rounded value must be
322347 // > less than or equal to `isize::MAX`)
323- let new_size = self . size ( ) + pad ;
348+ let new_size = self . size_rounded_up_to_custom_align ( self . align ) ;
324349
325350 // SAFETY: padded size is guaranteed to not exceed `isize::MAX`.
326351 unsafe { Layout :: from_size_align_unchecked ( new_size, self . align ( ) ) }
@@ -333,20 +358,36 @@ impl Layout {
333358 /// layout of the array and `offs` is the distance between the start
334359 /// of each element in the array.
335360 ///
361+ /// (That distance between elements is sometimes known as "stride".)
362+ ///
336363 /// On arithmetic overflow, returns `LayoutError`.
364+ ///
365+ /// # Examples
366+ ///
367+ /// ```
368+ /// #![feature(alloc_layout_extra)]
369+ /// use std::alloc::Layout;
370+ ///
371+ /// // All rust types have a size that's a multiple of their alignment.
372+ /// let normal = Layout::from_size_align(12, 4).unwrap();
373+ /// let repeated = normal.repeat(3).unwrap();
374+ /// assert_eq!(repeated, (Layout::from_size_align(36, 4).unwrap(), 12));
375+ ///
376+ /// // But you can manually make layouts which don't meet that rule.
377+ /// let padding_needed = Layout::from_size_align(6, 4).unwrap();
378+ /// let repeated = padding_needed.repeat(3).unwrap();
379+ /// assert_eq!(repeated, (Layout::from_size_align(24, 4).unwrap(), 8));
380+ /// ```
337381 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
382+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
338383 #[ inline]
339- pub fn repeat ( & self , n : usize ) -> Result < ( Self , usize ) , LayoutError > {
340- // This cannot overflow. Quoting from the invariant of Layout:
341- // > `size`, when rounded up to the nearest multiple of `align`,
342- // > must not overflow isize (i.e., the rounded value must be
343- // > less than or equal to `isize::MAX`)
344- let padded_size = self . size ( ) + self . padding_needed_for ( self . align ( ) ) ;
345- let alloc_size = padded_size. checked_mul ( n) . ok_or ( LayoutError ) ?;
346-
347- // The safe constructor is called here to enforce the isize size limit.
348- let layout = Layout :: from_size_alignment ( alloc_size, self . align ) ?;
349- Ok ( ( layout, padded_size) )
384+ pub const fn repeat ( & self , n : usize ) -> Result < ( Self , usize ) , LayoutError > {
385+ let padded = self . pad_to_align ( ) ;
386+ if let Ok ( repeated) = padded. repeat_packed ( n) {
387+ Ok ( ( repeated, padded. size ( ) ) )
388+ } else {
389+ Err ( LayoutError )
390+ }
350391 }
351392
352393 /// Creates a layout describing the record for `self` followed by
@@ -395,17 +436,23 @@ impl Layout {
395436 /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16])));
396437 /// ```
397438 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
439+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
398440 #[ inline]
399- pub fn extend ( & self , next : Self ) -> Result < ( Self , usize ) , LayoutError > {
400- let new_align = cmp:: max ( self . align , next. align ) ;
401- let pad = self . padding_needed_for ( next. align ( ) ) ;
402-
403- let offset = self . size ( ) . checked_add ( pad) . ok_or ( LayoutError ) ?;
404- let new_size = offset. checked_add ( next. size ( ) ) . ok_or ( LayoutError ) ?;
405-
406- // The safe constructor is called here to enforce the isize size limit.
407- let layout = Layout :: from_size_alignment ( new_size, new_align) ?;
408- Ok ( ( layout, offset) )
441+ pub const fn extend ( & self , next : Self ) -> Result < ( Self , usize ) , LayoutError > {
442+ let new_align = Alignment :: max ( self . align , next. align ) ;
443+ let offset = self . size_rounded_up_to_custom_align ( next. align ) ;
444+
445+ // SAFETY: `offset` is at most `isize::MAX + 1` (such as from aligning
446+ // to `Alignment::MAX`) and `next.size` is at most `isize::MAX` (from the
447+ // `Layout` type invariant). Thus the largest possible `new_size` is
448+ // `isize::MAX + 1 + isize::MAX`, which is `usize::MAX`, and cannot overflow.
449+ let new_size = unsafe { unchecked_add ( offset, next. size ) } ;
450+
451+ if let Ok ( layout) = Layout :: from_size_alignment ( new_size, new_align) {
452+ Ok ( ( layout, offset) )
453+ } else {
454+ Err ( LayoutError )
455+ }
409456 }
410457
411458 /// Creates a layout describing the record for `n` instances of
@@ -421,11 +468,15 @@ impl Layout {
421468 ///
422469 /// On arithmetic overflow, returns `LayoutError`.
423470 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
471+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
424472 #[ inline]
425- pub fn repeat_packed ( & self , n : usize ) -> Result < Self , LayoutError > {
426- let size = self . size ( ) . checked_mul ( n) . ok_or ( LayoutError ) ?;
427- // The safe constructor is called here to enforce the isize size limit.
428- Layout :: from_size_alignment ( size, self . align )
473+ pub const fn repeat_packed ( & self , n : usize ) -> Result < Self , LayoutError > {
474+ if let Some ( size) = self . size . checked_mul ( n) {
475+ // The safe constructor is called here to enforce the isize size limit.
476+ Layout :: from_size_alignment ( size, self . align )
477+ } else {
478+ Err ( LayoutError )
479+ }
429480 }
430481
431482 /// Creates a layout describing the record for `self` followed by
@@ -435,11 +486,15 @@ impl Layout {
435486 ///
436487 /// On arithmetic overflow, returns `LayoutError`.
437488 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
489+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
438490 #[ inline]
439- pub fn extend_packed ( & self , next : Self ) -> Result < Self , LayoutError > {
440- let new_size = self . size ( ) . checked_add ( next. size ( ) ) . ok_or ( LayoutError ) ?;
441- // The safe constructor is called here to enforce the isize size limit.
442- Layout :: from_size_alignment ( new_size, self . align )
491+ pub const fn extend_packed ( & self , next : Self ) -> Result < Self , LayoutError > {
492+ if let Some ( new_size) = self . size . checked_add ( next. size ) {
493+ // The safe constructor is called here to enforce the isize size limit.
494+ Layout :: from_size_alignment ( new_size, self . align )
495+ } else {
496+ Err ( LayoutError )
497+ }
443498 }
444499
445500 /// Creates a layout describing the record for a `[T; n]`.
@@ -451,14 +506,12 @@ impl Layout {
451506 #[ inline]
452507 pub const fn array < T > ( n : usize ) -> Result < Self , LayoutError > {
453508 // Reduce the amount of code we need to monomorphize per `T`.
454- return inner ( mem :: size_of :: < T > ( ) , Alignment :: of :: < T > ( ) , n) ;
509+ return inner ( T :: LAYOUT , n) ;
455510
456511 #[ inline]
457- const fn inner (
458- element_size : usize ,
459- align : Alignment ,
460- n : usize ,
461- ) -> Result < Layout , LayoutError > {
512+ const fn inner ( element_layout : Layout , n : usize ) -> Result < Layout , LayoutError > {
513+ let Layout { size : element_size, align } = element_layout;
514+
462515 // We need to check two things about the size:
463516 // - That the total size won't overflow a `usize`, and
464517 // - That the total size still fits in an `isize`.
@@ -473,7 +526,7 @@ impl Layout {
473526 // This is a useless hint inside this function, but after inlining this helps
474527 // deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
475528 // allocation path) before/after this multiplication.
476- let array_size = unsafe { element_size . unchecked_mul ( n) } ;
529+ let array_size = unsafe { unchecked_mul ( element_size , n) } ;
477530
478531 // SAFETY: We just checked above that the `array_size` will not
479532 // exceed `isize::MAX` even when rounded up to the alignment.
0 commit comments