5
5
// Your performance intuition is useless. Run perf.
6
6
7
7
use crate :: error:: Error ;
8
+ use crate :: intrinsics:: { unchecked_add, unchecked_mul, unchecked_sub} ;
9
+ use crate :: mem:: SizedTypeProperties ;
8
10
use crate :: ptr:: { Alignment , NonNull } ;
9
- use crate :: { assert_unsafe_precondition, cmp , fmt, mem} ;
11
+ use crate :: { assert_unsafe_precondition, fmt, mem} ;
10
12
11
13
// While this function is used in one place and its implementation
12
14
// could be inlined, the previous attempts to do so made rustc
@@ -98,7 +100,10 @@ impl Layout {
98
100
//
99
101
// Above implies that checking for summation overflow is both
100
102
// 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 ( ) ) }
102
107
}
103
108
104
109
/// Internal helper constructor to skip revalidating alignment validity.
@@ -252,9 +257,14 @@ impl Layout {
252
257
/// Returns an error if the combination of `self.size()` and the given
253
258
/// `align` violates the conditions listed in [`Layout::from_size_align`].
254
259
#[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
260
+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
255
261
#[ 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
+ }
258
268
}
259
269
260
270
/// Returns the amount of padding we must insert after `self`
@@ -279,29 +289,42 @@ impl Layout {
279
289
without modifying the `Layout`"]
280
290
#[ inline]
281
291
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
+ }
283
298
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:
284
306
// 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);
287
308
//
288
- // We use modular arithmetic throughout :
309
+ // The arithmetic we do here can never overflow :
289
310
//
290
311
// 1. align is guaranteed to be > 0, so align - 1 is always
291
312
// valid.
292
313
//
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`.
298
316
//
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
+ }
305
328
}
306
329
307
330
/// Creates a layout by rounding the size of this layout up to a multiple
@@ -315,12 +338,11 @@ impl Layout {
315
338
without modifying the original"]
316
339
#[ inline]
317
340
pub const fn pad_to_align ( & self ) -> Layout {
318
- let pad = self . padding_needed_for ( self . align ( ) ) ;
319
341
// This cannot overflow. Quoting from the invariant of Layout:
320
342
// > `size`, when rounded up to the nearest multiple of `align`,
321
343
// > must not overflow isize (i.e., the rounded value must be
322
344
// > 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 ) ;
324
346
325
347
// SAFETY: padded size is guaranteed to not exceed `isize::MAX`.
326
348
unsafe { Layout :: from_size_align_unchecked ( new_size, self . align ( ) ) }
@@ -333,20 +355,36 @@ impl Layout {
333
355
/// layout of the array and `offs` is the distance between the start
334
356
/// of each element in the array.
335
357
///
358
+ /// (That distance between elements is sometimes known as "stride".)
359
+ ///
336
360
/// 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
+ /// ```
337
378
#[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
379
+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
338
380
#[ 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
+ }
350
388
}
351
389
352
390
/// Creates a layout describing the record for `self` followed by
@@ -395,17 +433,23 @@ impl Layout {
395
433
/// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16])));
396
434
/// ```
397
435
#[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
436
+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
398
437
#[ 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
+ }
409
453
}
410
454
411
455
/// Creates a layout describing the record for `n` instances of
@@ -421,11 +465,15 @@ impl Layout {
421
465
///
422
466
/// On arithmetic overflow, returns `LayoutError`.
423
467
#[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
468
+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
424
469
#[ 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
+ }
429
477
}
430
478
431
479
/// Creates a layout describing the record for `self` followed by
@@ -435,10 +483,13 @@ impl Layout {
435
483
///
436
484
/// On arithmetic overflow, returns `LayoutError`.
437
485
#[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
486
+ #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
438
487
#[ 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
442
493
Layout :: from_size_alignment ( new_size, self . align )
443
494
}
444
495
@@ -451,14 +502,12 @@ impl Layout {
451
502
#[ inline]
452
503
pub const fn array < T > ( n : usize ) -> Result < Self , LayoutError > {
453
504
// 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) ;
455
506
456
507
#[ 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
+
462
511
// We need to check two things about the size:
463
512
// - That the total size won't overflow a `usize`, and
464
513
// - That the total size still fits in an `isize`.
@@ -473,7 +522,7 @@ impl Layout {
473
522
// This is a useless hint inside this function, but after inlining this helps
474
523
// deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
475
524
// 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) } ;
477
526
478
527
// SAFETY: We just checked above that the `array_size` will not
479
528
// exceed `isize::MAX` even when rounded up to the alignment.
0 commit comments