1
1
#![ unstable( feature = "raw_vec_internals" , reason = "implementation detail" , issue = "none" ) ]
2
2
#![ doc( hidden) ]
3
3
4
- use core:: alloc:: MemoryBlock ;
4
+ use core:: alloc:: { LayoutErr , MemoryBlock } ;
5
5
use core:: cmp;
6
6
use core:: mem:: { self , ManuallyDrop , MaybeUninit } ;
7
7
use core:: ops:: Drop ;
@@ -278,7 +278,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
278
278
needed_extra_capacity : usize ,
279
279
) -> Result < ( ) , TryReserveError > {
280
280
if self . needs_to_grow ( used_capacity, needed_extra_capacity) {
281
- self . grow ( Amortized , used_capacity, needed_extra_capacity, MayMove , Uninitialized )
281
+ self . grow_amortized ( used_capacity, needed_extra_capacity, MayMove )
282
282
} else {
283
283
Ok ( ( ) )
284
284
}
@@ -305,8 +305,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
305
305
// This is more readable than putting this in one line:
306
306
// `!self.needs_to_grow(...) || self.grow(...).is_ok()`
307
307
if self . needs_to_grow ( used_capacity, needed_extra_capacity) {
308
- self . grow ( Amortized , used_capacity, needed_extra_capacity, InPlace , Uninitialized )
309
- . is_ok ( )
308
+ self . grow_amortized ( used_capacity, needed_extra_capacity, InPlace ) . is_ok ( )
310
309
} else {
311
310
true
312
311
}
@@ -347,7 +346,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
347
346
needed_extra_capacity : usize ,
348
347
) -> Result < ( ) , TryReserveError > {
349
348
if self . needs_to_grow ( used_capacity, needed_extra_capacity) {
350
- self . grow ( Exact , used_capacity, needed_extra_capacity, MayMove , Uninitialized )
349
+ self . grow_exact ( used_capacity, needed_extra_capacity)
351
350
} else {
352
351
Ok ( ( ) )
353
352
}
@@ -372,13 +371,6 @@ impl<T, A: AllocRef> RawVec<T, A> {
372
371
}
373
372
}
374
373
375
- #[ derive( Copy , Clone ) ]
376
- enum Strategy {
377
- Amortized ,
378
- Exact ,
379
- }
380
- use Strategy :: * ;
381
-
382
374
impl < T , A : AllocRef > RawVec < T , A > {
383
375
/// Returns if the buffer needs to grow to fulfill the needed extra capacity.
384
376
/// Mainly used to make inlining reserve-calls possible without inlining `grow`.
@@ -396,54 +388,59 @@ impl<T, A: AllocRef> RawVec<T, A> {
396
388
self . cap = Self :: capacity_from_bytes ( memory. size ) ;
397
389
}
398
390
399
- /// Single method to handle all possibilities of growing the buffer.
400
- fn grow (
391
+ // This method is usually instantiated many times. So we want it to be as
392
+ // small as possible, to improve compile times. But we also want as much of
393
+ // its contents to be statically computable as possible, to make the
394
+ // generated code run faster. Therefore, this method is carefully written
395
+ // so that all of the code that depends on `T` is within it, while as much
396
+ // of the code that doesn't depend on `T` as possible is in functions that
397
+ // are non-generic over `T`.
398
+ fn grow_amortized (
401
399
& mut self ,
402
- strategy : Strategy ,
403
400
used_capacity : usize ,
404
401
needed_extra_capacity : usize ,
405
402
placement : ReallocPlacement ,
406
- init : AllocInit ,
407
403
) -> Result < ( ) , TryReserveError > {
408
- let elem_size = mem:: size_of :: < T > ( ) ;
409
- if elem_size == 0 {
404
+ if mem:: size_of :: < T > ( ) == 0 {
410
405
// Since we return a capacity of `usize::MAX` when `elem_size` is
411
406
// 0, getting to here necessarily means the `RawVec` is overfull.
412
407
return Err ( CapacityOverflow ) ;
413
408
}
414
- let new_layout = match strategy {
415
- Amortized => {
416
- // Nothing we can really do about these checks, sadly.
417
- let required_cap =
418
- used_capacity. checked_add ( needed_extra_capacity) . ok_or ( CapacityOverflow ) ?;
419
- // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
420
- let double_cap = self . cap * 2 ;
421
- // `double_cap` guarantees exponential growth.
422
- let cap = cmp:: max ( double_cap, required_cap) ;
423
- Layout :: array :: < T > ( cap) . map_err ( |_| CapacityOverflow ) ?
424
- }
425
- Exact => {
426
- let cap =
427
- used_capacity. checked_add ( needed_extra_capacity) . ok_or ( CapacityOverflow ) ?;
428
- Layout :: array :: < T > ( cap) . map_err ( |_| CapacityOverflow ) ?
429
- }
430
- } ;
431
- alloc_guard ( new_layout. size ( ) ) ?;
432
409
433
- let memory = if let Some ( ( ptr, old_layout) ) = self . current_memory ( ) {
434
- debug_assert_eq ! ( old_layout. align( ) , new_layout. align( ) ) ;
435
- unsafe {
436
- self . alloc
437
- . grow ( ptr, old_layout, new_layout. size ( ) , placement, init)
438
- . map_err ( |_| AllocError { layout : new_layout, non_exhaustive : ( ) } ) ?
439
- }
440
- } else {
441
- match placement {
442
- MayMove => self . alloc . alloc ( new_layout, init) ,
443
- InPlace => Err ( AllocErr ) ,
444
- }
445
- . map_err ( |_| AllocError { layout : new_layout, non_exhaustive : ( ) } ) ?
446
- } ;
410
+ // Nothing we can really do about these checks, sadly.
411
+ let required_cap =
412
+ used_capacity. checked_add ( needed_extra_capacity) . ok_or ( CapacityOverflow ) ?;
413
+ // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
414
+ let double_cap = self . cap * 2 ;
415
+ // `double_cap` guarantees exponential growth.
416
+ let cap = cmp:: max ( double_cap, required_cap) ;
417
+ let new_layout = Layout :: array :: < T > ( cap) ;
418
+
419
+ // `finish_grow` is non-generic over `T`.
420
+ let memory = finish_grow ( new_layout, placement, self . current_memory ( ) , & mut self . alloc ) ?;
421
+ self . set_memory ( memory) ;
422
+ Ok ( ( ) )
423
+ }
424
+
425
+ // The constraints on this method are much the same as those on
426
+ // `grow_amortized`, but this method is usually instantiated less often so
427
+ // it's less critical.
428
+ fn grow_exact (
429
+ & mut self ,
430
+ used_capacity : usize ,
431
+ needed_extra_capacity : usize ,
432
+ ) -> Result < ( ) , TryReserveError > {
433
+ if mem:: size_of :: < T > ( ) == 0 {
434
+ // Since we return a capacity of `usize::MAX` when the type size is
435
+ // 0, getting to here necessarily means the `RawVec` is overfull.
436
+ return Err ( CapacityOverflow ) ;
437
+ }
438
+
439
+ let cap = used_capacity. checked_add ( needed_extra_capacity) . ok_or ( CapacityOverflow ) ?;
440
+ let new_layout = Layout :: array :: < T > ( cap) ;
441
+
442
+ // `finish_grow` is non-generic over `T`.
443
+ let memory = finish_grow ( new_layout, MayMove , self . current_memory ( ) , & mut self . alloc ) ?;
447
444
self . set_memory ( memory) ;
448
445
Ok ( ( ) )
449
446
}
@@ -471,6 +468,38 @@ impl<T, A: AllocRef> RawVec<T, A> {
471
468
}
472
469
}
473
470
471
+ // This function is outside `RawVec` to minimize compile times. See the comment
472
+ // above `RawVec::grow_amortized` for details. (The `A` parameter isn't
473
+ // significant, because the number of different `A` types seen in practice is
474
+ // much smaller than the number of `T` types.)
475
+ fn finish_grow < A > (
476
+ new_layout : Result < Layout , LayoutErr > ,
477
+ placement : ReallocPlacement ,
478
+ current_memory : Option < ( NonNull < u8 > , Layout ) > ,
479
+ alloc : & mut A ,
480
+ ) -> Result < MemoryBlock , TryReserveError >
481
+ where
482
+ A : AllocRef ,
483
+ {
484
+ // Check for the error here to minimize the size of `RawVec::grow_*`.
485
+ let new_layout = new_layout. map_err ( |_| CapacityOverflow ) ?;
486
+
487
+ alloc_guard ( new_layout. size ( ) ) ?;
488
+
489
+ let memory = if let Some ( ( ptr, old_layout) ) = current_memory {
490
+ debug_assert_eq ! ( old_layout. align( ) , new_layout. align( ) ) ;
491
+ unsafe { alloc. grow ( ptr, old_layout, new_layout. size ( ) , placement, Uninitialized ) }
492
+ } else {
493
+ match placement {
494
+ MayMove => alloc. alloc ( new_layout, Uninitialized ) ,
495
+ InPlace => Err ( AllocErr ) ,
496
+ }
497
+ }
498
+ . map_err ( |_| AllocError { layout : new_layout, non_exhaustive : ( ) } ) ?;
499
+
500
+ Ok ( memory)
501
+ }
502
+
474
503
impl < T > RawVec < T , Global > {
475
504
/// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
476
505
///
0 commit comments