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,42 @@ 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+ // SAFETY:
284306 // Rounded up value is:
285- // len_rounded_up = (len + align - 1) & !(align - 1);
286- // and then we return the padding difference: `len_rounded_up - len`.
307+ // size_rounded_up = (size + align - 1) & !(align - 1);
287308 //
288- // We use modular arithmetic throughout :
309+ // The arithmetic we do here can never overflow :
289310 //
290311 // 1. align is guaranteed to be > 0, so align - 1 is always
291312 // valid.
292313 //
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`.
314+ // 2. size is at most `isize::MAX`, so adding `align - 1` (which is at
315+ // most `isize::MAX`) can never overflow a `usize`.
298316 //
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)
317+ // 3. masking by the alignment can remove at most `align - 1`,
318+ // which is what we just added, thus the value we return is never
319+ // less than the original `size`.
320+ //
321+ // (Size 0 Align MAX is already aligned, so stays the same, but things like
322+ // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.)
323+ unsafe {
324+ let align_m1 = unchecked_sub ( align. as_usize ( ) , 1 ) ;
325+ let size_rounded_up = unchecked_add ( self . size , align_m1) & !align_m1;
326+ size_rounded_up
327+ }
305328 }
306329
307330 /// Creates a layout by rounding the size of this layout up to a multiple
@@ -315,12 +338,11 @@ impl Layout {
315338 without modifying the original"]
316339 #[ inline]
317340 pub const fn pad_to_align ( & self ) -> Layout {
318- let pad = self . padding_needed_for ( self . align ( ) ) ;
319341 // This cannot overflow. Quoting from the invariant of Layout:
320342 // > `size`, when rounded up to the nearest multiple of `align`,
321343 // > must not overflow isize (i.e., the rounded value must be
322344 // > less than or equal to `isize::MAX`)
323- let new_size = self . size ( ) + pad ;
345+ let new_size = self . size_rounded_up_to_custom_align ( self . align ) ;
324346
325347 // SAFETY: padded size is guaranteed to not exceed `isize::MAX`.
326348 unsafe { Layout :: from_size_align_unchecked ( new_size, self . align ( ) ) }
@@ -333,20 +355,36 @@ impl Layout {
333355 /// layout of the array and `offs` is the distance between the start
334356 /// of each element in the array.
335357 ///
358+ /// (That distance between elements is sometimes known as "stride".)
359+ ///
336360 /// On arithmetic overflow, returns `LayoutError`.
361+ ///
362+ /// # Examples
363+ ///
364+ /// ```
365+ /// #![feature(alloc_layout_extra)]
366+ /// use std::alloc::Layout;
367+ ///
368+ /// // All rust types have a size that's a multiple of their alignment.
369+ /// let normal = Layout::from_size_align(12, 4).unwrap();
370+ /// let repeated = normal.repeat(3).unwrap();
371+ /// assert_eq!(repeated, (Layout::from_size_align(36, 4).unwrap(), 12));
372+ ///
373+ /// // But you can manually make layouts which don't meet that rule.
374+ /// let padding_needed = Layout::from_size_align(6, 4).unwrap();
375+ /// let repeated = padding_needed.repeat(3).unwrap();
376+ /// assert_eq!(repeated, (Layout::from_size_align(24, 4).unwrap(), 8));
377+ /// ```
337378 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
379+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
338380 #[ 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) )
381+ pub const fn repeat ( & self , n : usize ) -> Result < ( Self , usize ) , LayoutError > {
382+ let padded = self . pad_to_align ( ) ;
383+ if let Ok ( repeated) = padded. repeat_packed ( n) {
384+ Ok ( ( repeated, padded. size ( ) ) )
385+ } else {
386+ Err ( LayoutError )
387+ }
350388 }
351389
352390 /// Creates a layout describing the record for `self` followed by
@@ -395,17 +433,23 @@ impl Layout {
395433 /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16])));
396434 /// ```
397435 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
436+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
398437 #[ 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) )
438+ pub const fn extend ( & self , next : Self ) -> Result < ( Self , usize ) , LayoutError > {
439+ let new_align = Alignment :: max ( self . align , next. align ) ;
440+ let offset = self . size_rounded_up_to_custom_align ( next. align ) ;
441+
442+ // SAFETY: `offset` is at most `isize::MAX + 1` (such as from aligning
443+ // to `Alignment::MAX`) and `next.size` is at most `isize::MAX` (from the
444+ // `Layout` type invariant). Thus the largest possible `new_size` is
445+ // `isize::MAX + 1 + isize::MAX`, which is `usize::MAX`, and cannot overflow.
446+ let new_size = unsafe { unchecked_add ( offset, next. size ) } ;
447+
448+ if let Ok ( layout) = Layout :: from_size_alignment ( new_size, new_align) {
449+ Ok ( ( layout, offset) )
450+ } else {
451+ Err ( LayoutError )
452+ }
409453 }
410454
411455 /// Creates a layout describing the record for `n` instances of
@@ -421,11 +465,15 @@ impl Layout {
421465 ///
422466 /// On arithmetic overflow, returns `LayoutError`.
423467 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
468+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
424469 #[ 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 )
470+ pub const fn repeat_packed ( & self , n : usize ) -> Result < Self , LayoutError > {
471+ if let Some ( size) = self . size . checked_mul ( n) {
472+ // The safe constructor is called here to enforce the isize size limit.
473+ Layout :: from_size_alignment ( size, self . align )
474+ } else {
475+ Err ( LayoutError )
476+ }
429477 }
430478
431479 /// Creates a layout describing the record for `self` followed by
@@ -435,10 +483,13 @@ impl Layout {
435483 ///
436484 /// On arithmetic overflow, returns `LayoutError`.
437485 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
486+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
438487 #[ 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.
488+ pub const fn extend_packed ( & self , next : Self ) -> Result < Self , LayoutError > {
489+ // SAFETY: each `size` is at most `isize::MAX == usize::MAX/2`, so the
490+ // sum is at most `usize::MAX/2*2 == usize::MAX - 1`, and cannot overflow.
491+ let new_size = unsafe { unchecked_add ( self . size , next. size ) } ;
492+ // The safe constructor enforces that the new size isn't too big for the alignment
442493 Layout :: from_size_alignment ( new_size, self . align )
443494 }
444495
@@ -451,14 +502,12 @@ impl Layout {
451502 #[ inline]
452503 pub const fn array < T > ( n : usize ) -> Result < Self , LayoutError > {
453504 // Reduce the amount of code we need to monomorphize per `T`.
454- return inner ( mem :: size_of :: < T > ( ) , Alignment :: of :: < T > ( ) , n) ;
505+ return inner ( T :: LAYOUT , n) ;
455506
456507 #[ inline]
457- const fn inner (
458- element_size : usize ,
459- align : Alignment ,
460- n : usize ,
461- ) -> Result < Layout , LayoutError > {
508+ const fn inner ( element_layout : Layout , n : usize ) -> Result < Layout , LayoutError > {
509+ let Layout { size : element_size, align } = element_layout;
510+
462511 // We need to check two things about the size:
463512 // - That the total size won't overflow a `usize`, and
464513 // - That the total size still fits in an `isize`.
@@ -473,7 +522,7 @@ impl Layout {
473522 // This is a useless hint inside this function, but after inlining this helps
474523 // deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
475524 // allocation path) before/after this multiplication.
476- let array_size = unsafe { element_size . unchecked_mul ( n) } ;
525+ let array_size = unsafe { unchecked_mul ( element_size , n) } ;
477526
478527 // SAFETY: We just checked above that the `array_size` will not
479528 // exceed `isize::MAX` even when rounded up to the alignment.
0 commit comments