11#![ unstable( feature = "raw_vec_internals" , reason = "implementation detail" , issue = "none" ) ]
22#![ doc( hidden) ]
33
4- use core:: alloc:: MemoryBlock ;
4+ use core:: alloc:: { LayoutErr , MemoryBlock } ;
55use core:: cmp;
66use core:: mem:: { self , ManuallyDrop , MaybeUninit } ;
77use core:: ops:: Drop ;
@@ -278,7 +278,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
278278 needed_extra_capacity : usize ,
279279 ) -> Result < ( ) , TryReserveError > {
280280 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 )
282282 } else {
283283 Ok ( ( ) )
284284 }
@@ -305,8 +305,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
305305 // This is more readable than putting this in one line:
306306 // `!self.needs_to_grow(...) || self.grow(...).is_ok()`
307307 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 ( )
310309 } else {
311310 true
312311 }
@@ -347,7 +346,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
347346 needed_extra_capacity : usize ,
348347 ) -> Result < ( ) , TryReserveError > {
349348 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)
351350 } else {
352351 Ok ( ( ) )
353352 }
@@ -372,13 +371,6 @@ impl<T, A: AllocRef> RawVec<T, A> {
372371 }
373372}
374373
375- #[ derive( Copy , Clone ) ]
376- enum Strategy {
377- Amortized ,
378- Exact ,
379- }
380- use Strategy :: * ;
381-
382374impl < T , A : AllocRef > RawVec < T , A > {
383375 /// Returns if the buffer needs to grow to fulfill the needed extra capacity.
384376 /// Mainly used to make inlining reserve-calls possible without inlining `grow`.
@@ -396,54 +388,59 @@ impl<T, A: AllocRef> RawVec<T, A> {
396388 self . cap = Self :: capacity_from_bytes ( memory. size ) ;
397389 }
398390
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 (
401399 & mut self ,
402- strategy : Strategy ,
403400 used_capacity : usize ,
404401 needed_extra_capacity : usize ,
405402 placement : ReallocPlacement ,
406- init : AllocInit ,
407403 ) -> Result < ( ) , TryReserveError > {
408- let elem_size = mem:: size_of :: < T > ( ) ;
409- if elem_size == 0 {
404+ if mem:: size_of :: < T > ( ) == 0 {
410405 // Since we return a capacity of `usize::MAX` when `elem_size` is
411406 // 0, getting to here necessarily means the `RawVec` is overfull.
412407 return Err ( CapacityOverflow ) ;
413408 }
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 ( ) ) ?;
432409
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 ) ?;
447444 self . set_memory ( memory) ;
448445 Ok ( ( ) )
449446 }
@@ -471,6 +468,38 @@ impl<T, A: AllocRef> RawVec<T, A> {
471468 }
472469}
473470
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+
474503impl < T > RawVec < T , Global > {
475504 /// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
476505 ///
0 commit comments