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,12 @@ 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 {
107+ unchecked_sub ( isize:: MAX as usize + 1 , align. as_usize ( ) )
108+ }
102109 }
103110
104111 /// Internal helper constructor to skip revalidating alignment validity.
@@ -252,9 +259,14 @@ impl Layout {
252259 /// Returns an error if the combination of `self.size()` and the given
253260 /// `align` violates the conditions listed in [`Layout::from_size_align`].
254261 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
262+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
255263 #[ inline]
256- pub fn align_to ( & self , align : usize ) -> Result < Self , LayoutError > {
257- Layout :: from_size_align ( self . size ( ) , cmp:: max ( self . align ( ) , align) )
264+ pub const fn align_to ( & self , align : usize ) -> Result < Self , LayoutError > {
265+ if let Some ( align) = Alignment :: new ( align) {
266+ Layout :: from_size_alignment ( self . size , Alignment :: max ( self . align , align) )
267+ } else {
268+ Err ( LayoutError )
269+ }
258270 }
259271
260272 /// Returns the amount of padding we must insert after `self`
@@ -279,29 +291,47 @@ impl Layout {
279291 without modifying the `Layout`"]
280292 #[ inline]
281293 pub const fn padding_needed_for ( & self , align : usize ) -> usize {
282- let len = self . size ( ) ;
294+ // FIXME: Can we just change the type on this to `Alignment`?
295+ let Some ( align) = Alignment :: new ( align) else { return usize:: MAX } ;
296+ let len_rounded_up = self . size_rounded_up_to_custom_align ( align) ;
297+ // SAFETY: Cannot overflow because the rounded-up value is never less
298+ unsafe {
299+ unchecked_sub ( len_rounded_up, self . size )
300+ }
301+ }
302+
303+ /// Returns the smallest multiple of `align` greater than or equal to `self.size()`.
304+ ///
305+ /// This can return at most `Alignment::MAX` (aka `isize::MAX + 1`)
306+ /// because the original size is at most `isize::MAX`.
307+ #[ inline]
308+ const fn size_rounded_up_to_custom_align ( & self , align : Alignment ) -> usize {
309+ let len = self . size ;
283310
311+ // SAFETY:
284312 // Rounded up value is:
285313 // len_rounded_up = (len + align - 1) & !(align - 1);
286314 // and then we return the padding difference: `len_rounded_up - len`.
287315 //
288- // We use modular arithmetic throughout :
316+ // The arithmetic we do here can never overflow :
289317 //
290318 // 1. align is guaranteed to be > 0, so align - 1 is always
291319 // valid.
292320 //
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`.
321+ // 2. len is at most `isize::MAX`, so adding `align - 1` can never
322+ // overflow a `usize`.
298323 //
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)
324+ // 3. masking by the alignment can remove at most `align - 1`,
325+ // which is what we just added, the value we return is never
326+ // less than the original `len`.
327+ //
328+ // (Size 0 Align MAX is already aligned, so stays the same, but things like
329+ // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.)
330+ unsafe {
331+ let align_m1 = unchecked_sub ( align. as_usize ( ) , 1 ) ;
332+ let len_rounded_up = unchecked_add ( len, align_m1) & !align_m1;
333+ len_rounded_up
334+ }
305335 }
306336
307337 /// Creates a layout by rounding the size of this layout up to a multiple
@@ -315,12 +345,11 @@ impl Layout {
315345 without modifying the original"]
316346 #[ inline]
317347 pub const fn pad_to_align ( & self ) -> Layout {
318- let pad = self . padding_needed_for ( self . align ( ) ) ;
319348 // This cannot overflow. Quoting from the invariant of Layout:
320349 // > `size`, when rounded up to the nearest multiple of `align`,
321350 // > must not overflow isize (i.e., the rounded value must be
322351 // > less than or equal to `isize::MAX`)
323- let new_size = self . size ( ) + pad ;
352+ let new_size = self . size_rounded_up_to_custom_align ( self . align ) ;
324353
325354 // SAFETY: padded size is guaranteed to not exceed `isize::MAX`.
326355 unsafe { Layout :: from_size_align_unchecked ( new_size, self . align ( ) ) }
@@ -333,20 +362,36 @@ impl Layout {
333362 /// layout of the array and `offs` is the distance between the start
334363 /// of each element in the array.
335364 ///
365+ /// (That distance between elements is sometimes known as "stride".)
366+ ///
336367 /// On arithmetic overflow, returns `LayoutError`.
368+ ///
369+ /// # Examples
370+ ///
371+ /// ```
372+ /// #![feature(alloc_layout_extra)]
373+ /// use std::alloc::Layout;
374+ ///
375+ /// // All rust types have a size that's a multiple of their alignment.
376+ /// let normal = Layout::from_size_align(12, 4).unwrap();
377+ /// let repeated = normal.repeat(3).unwrap();
378+ /// assert_eq!(repeated, (Layout::from_size_align(36, 4).unwrap(), 12));
379+ ///
380+ /// // But you can manually make layouts which don't meet that rule.
381+ /// let padding_needed = Layout::from_size_align(6, 4).unwrap();
382+ /// let repeated = padding_needed.repeat(3).unwrap();
383+ /// assert_eq!(repeated, (Layout::from_size_align(24, 4).unwrap(), 8));
384+ /// ```
337385 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
386+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
338387 #[ 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) )
388+ pub const fn repeat ( & self , n : usize ) -> Result < ( Self , usize ) , LayoutError > {
389+ let padded = self . pad_to_align ( ) ;
390+ if let Ok ( repeated) = padded. repeat_packed ( n) {
391+ Ok ( ( repeated, padded. size ( ) ) )
392+ } else {
393+ Err ( LayoutError )
394+ }
350395 }
351396
352397 /// Creates a layout describing the record for `self` followed by
@@ -395,17 +440,24 @@ impl Layout {
395440 /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16])));
396441 /// ```
397442 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
443+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
398444 #[ 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) )
445+ pub const fn extend ( & self , next : Self ) -> Result < ( Self , usize ) , LayoutError > {
446+ let new_align = Alignment :: max ( self . align , next. align ) ;
447+ let offset = self . size_rounded_up_to_custom_align ( next. align ) ;
448+
449+ // SAFETY: `offset` is at most `isize::MAX + 1` (such as from aligning
450+ // to `Alignment::MAX`) and `next.size` is at most `isize::MAX` (from the
451+ // `Layout` type invariant). Thus the largest possible `new_size` is
452+ // `isize::MAX + 1 + isize::MAX`, which is `usize::MAX`, and cannot overflow.
453+ let new_size = unsafe { unchecked_add ( offset, next. size ) } ;
454+
455+ if let Ok ( layout) = Layout :: from_size_alignment ( new_size, new_align)
456+ {
457+ Ok ( ( layout, offset) )
458+ } else {
459+ Err ( LayoutError )
460+ }
409461 }
410462
411463 /// Creates a layout describing the record for `n` instances of
@@ -421,11 +473,15 @@ impl Layout {
421473 ///
422474 /// On arithmetic overflow, returns `LayoutError`.
423475 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
476+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
424477 #[ 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 )
478+ pub const fn repeat_packed ( & self , n : usize ) -> Result < Self , LayoutError > {
479+ if let Some ( size) = self . size . checked_mul ( n) {
480+ // The safe constructor is called here to enforce the isize size limit.
481+ Layout :: from_size_alignment ( size, self . align )
482+ } else {
483+ Err ( LayoutError )
484+ }
429485 }
430486
431487 /// Creates a layout describing the record for `self` followed by
@@ -435,11 +491,15 @@ impl Layout {
435491 ///
436492 /// On arithmetic overflow, returns `LayoutError`.
437493 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
494+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
438495 #[ 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 )
496+ pub const fn extend_packed ( & self , next : Self ) -> Result < Self , LayoutError > {
497+ if let Some ( new_size) = self . size . checked_add ( next. size ) {
498+ // The safe constructor is called here to enforce the isize size limit.
499+ Layout :: from_size_alignment ( new_size, self . align )
500+ } else {
501+ Err ( LayoutError )
502+ }
443503 }
444504
445505 /// Creates a layout describing the record for a `[T; n]`.
@@ -451,29 +511,28 @@ impl Layout {
451511 #[ inline]
452512 pub const fn array < T > ( n : usize ) -> Result < Self , LayoutError > {
453513 // Reduce the amount of code we need to monomorphize per `T`.
454- return inner ( mem :: size_of :: < T > ( ) , Alignment :: of :: < T > ( ) , n) ;
514+ return inner ( T :: LAYOUT , n) ;
455515
456516 #[ inline]
457- const fn inner (
458- element_size : usize ,
459- align : Alignment ,
460- n : usize ,
461- ) -> Result < Layout , LayoutError > {
517+ const fn inner ( element_layout : Layout , n : usize ) -> Result < Layout , LayoutError > {
518+ let Layout { size : element_size, align } = element_layout;
519+
462520 // We need to check two things about the size:
463521 // - That the total size won't overflow a `usize`, and
464522 // - That the total size still fits in an `isize`.
465523 // By using division we can check them both with a single threshold.
466524 // That'd usually be a bad idea, but thankfully here the element size
467525 // and alignment are constants, so the compiler will fold all of it.
468- if element_size != 0 && n > Layout :: max_size_for_align ( align) / element_size {
526+ if element_size != 0 && n > Layout :: max_size_for_align ( align) / element_size
527+ {
469528 return Err ( LayoutError ) ;
470529 }
471530
472531 // SAFETY: We just checked that we won't overflow `usize` when we multiply.
473532 // This is a useless hint inside this function, but after inlining this helps
474533 // deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
475534 // allocation path) before/after this multiplication.
476- let array_size = unsafe { element_size . unchecked_mul ( n) } ;
535+ let array_size = unsafe { unchecked_mul ( element_size , n) } ;
477536
478537 // SAFETY: We just checked above that the `array_size` will not
479538 // exceed `isize::MAX` even when rounded up to the alignment.
0 commit comments