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 ;
@@ -211,82 +211,6 @@ impl<T, A: AllocRef> RawVec<T, A> {
211211 }
212212 }
213213
214- /// Doubles the size of the type's backing allocation. This is common enough
215- /// to want to do that it's easiest to just have a dedicated method. Slightly
216- /// more efficient logic can be provided for this than the general case.
217- ///
218- /// This function is ideal for when pushing elements one-at-a-time because
219- /// you don't need to incur the costs of the more general computations
220- /// reserve needs to do to guard against overflow. You do however need to
221- /// manually check if your `len == capacity`.
222- ///
223- /// # Panics
224- ///
225- /// * Panics if `T` is zero-sized on the assumption that you managed to exhaust
226- /// all `usize::MAX` slots in your imaginary buffer.
227- /// * Panics on 32-bit platforms if the requested capacity exceeds
228- /// `isize::MAX` bytes.
229- ///
230- /// # Aborts
231- ///
232- /// Aborts on OOM
233- ///
234- /// # Examples
235- ///
236- /// ```
237- /// # #![feature(raw_vec_internals)]
238- /// # extern crate alloc;
239- /// # use std::ptr;
240- /// # use alloc::raw_vec::RawVec;
241- /// struct MyVec<T> {
242- /// buf: RawVec<T>,
243- /// len: usize,
244- /// }
245- ///
246- /// impl<T> MyVec<T> {
247- /// pub fn push(&mut self, elem: T) {
248- /// if self.len == self.buf.capacity() { self.buf.double(); }
249- /// // double would have aborted or panicked if the len exceeded
250- /// // `isize::MAX` so this is safe to do unchecked now.
251- /// unsafe {
252- /// ptr::write(self.buf.ptr().add(self.len), elem);
253- /// }
254- /// self.len += 1;
255- /// }
256- /// }
257- /// # fn main() {
258- /// # let mut vec = MyVec { buf: RawVec::new(), len: 0 };
259- /// # vec.push(1);
260- /// # }
261- /// ```
262- #[ inline( never) ]
263- #[ cold]
264- pub fn double ( & mut self ) {
265- match self . grow ( Double , MayMove , Uninitialized ) {
266- Err ( CapacityOverflow ) => capacity_overflow ( ) ,
267- Err ( AllocError { layout, .. } ) => handle_alloc_error ( layout) ,
268- Ok ( ( ) ) => { /* yay */ }
269- }
270- }
271-
272- /// Attempts to double the size of the type's backing allocation in place. This is common
273- /// enough to want to do that it's easiest to just have a dedicated method. Slightly
274- /// more efficient logic can be provided for this than the general case.
275- ///
276- /// Returns `true` if the reallocation attempt has succeeded.
277- ///
278- /// # Panics
279- ///
280- /// * Panics if `T` is zero-sized on the assumption that you managed to exhaust
281- /// all `usize::MAX` slots in your imaginary buffer.
282- /// * Panics on 32-bit platforms if the requested capacity exceeds
283- /// `isize::MAX` bytes.
284- #[ inline( never) ]
285- #[ cold]
286- pub fn double_in_place ( & mut self ) -> bool {
287- self . grow ( Double , InPlace , Uninitialized ) . is_ok ( )
288- }
289-
290214 /// Ensures that the buffer contains at least enough space to hold
291215 /// `used_capacity + needed_extra_capacity` elements. If it doesn't already have
292216 /// enough capacity, will reallocate enough space plus comfortable slack
@@ -354,7 +278,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
354278 needed_extra_capacity : usize ,
355279 ) -> Result < ( ) , TryReserveError > {
356280 if self . needs_to_grow ( used_capacity, needed_extra_capacity) {
357- self . grow ( Amortized { used_capacity, needed_extra_capacity } , MayMove , Uninitialized )
281+ self . grow_amortized ( used_capacity, needed_extra_capacity, MayMove )
358282 } else {
359283 Ok ( ( ) )
360284 }
@@ -381,8 +305,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
381305 // This is more readable than putting this in one line:
382306 // `!self.needs_to_grow(...) || self.grow(...).is_ok()`
383307 if self . needs_to_grow ( used_capacity, needed_extra_capacity) {
384- self . grow ( Amortized { used_capacity, needed_extra_capacity } , InPlace , Uninitialized )
385- . is_ok ( )
308+ self . grow_amortized ( used_capacity, needed_extra_capacity, InPlace ) . is_ok ( )
386309 } else {
387310 true
388311 }
@@ -423,7 +346,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
423346 needed_extra_capacity : usize ,
424347 ) -> Result < ( ) , TryReserveError > {
425348 if self . needs_to_grow ( used_capacity, needed_extra_capacity) {
426- self . grow ( Exact { used_capacity, needed_extra_capacity } , MayMove , Uninitialized )
349+ self . grow_exact ( used_capacity, needed_extra_capacity)
427350 } else {
428351 Ok ( ( ) )
429352 }
@@ -448,14 +371,6 @@ impl<T, A: AllocRef> RawVec<T, A> {
448371 }
449372}
450373
451- #[ derive( Copy , Clone ) ]
452- enum Strategy {
453- Double ,
454- Amortized { used_capacity : usize , needed_extra_capacity : usize } ,
455- Exact { used_capacity : usize , needed_extra_capacity : usize } ,
456- }
457- use Strategy :: * ;
458-
459374impl < T , A : AllocRef > RawVec < T , A > {
460375 /// Returns if the buffer needs to grow to fulfill the needed extra capacity.
461376 /// Mainly used to make inlining reserve-calls possible without inlining `grow`.
@@ -473,68 +388,59 @@ impl<T, A: AllocRef> RawVec<T, A> {
473388 self . cap = Self :: capacity_from_bytes ( memory. size ) ;
474389 }
475390
476- /// Single method to handle all possibilities of growing the buffer.
477- 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 (
478399 & mut self ,
479- strategy : Strategy ,
400+ used_capacity : usize ,
401+ needed_extra_capacity : usize ,
480402 placement : ReallocPlacement ,
481- init : AllocInit ,
482403 ) -> Result < ( ) , TryReserveError > {
483- let elem_size = mem:: size_of :: < T > ( ) ;
484- if elem_size == 0 {
404+ if mem:: size_of :: < T > ( ) == 0 {
485405 // Since we return a capacity of `usize::MAX` when `elem_size` is
486406 // 0, getting to here necessarily means the `RawVec` is overfull.
487407 return Err ( CapacityOverflow ) ;
488408 }
489- let new_layout = match strategy {
490- Double => unsafe {
491- // Since we guarantee that we never allocate more than `isize::MAX` bytes,
492- // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow.
493- // Additionally the alignment will never be too large as to "not be satisfiable",
494- // so `Layout::from_size_align` will always return `Some`.
495- //
496- // TL;DR, we bypass runtime checks due to dynamic assertions in this module,
497- // allowing us to use `from_size_align_unchecked`.
498- let cap = if self . cap == 0 {
499- // Skip to 4 because tiny `Vec`'s are dumb; but not if that would cause overflow.
500- if elem_size > usize:: MAX / 8 { 1 } else { 4 }
501- } else {
502- self . cap * 2
503- } ;
504- Layout :: from_size_align_unchecked ( cap * elem_size, mem:: align_of :: < T > ( ) )
505- } ,
506- Amortized { used_capacity, needed_extra_capacity } => {
507- // Nothing we can really do about these checks, sadly.
508- let required_cap =
509- used_capacity. checked_add ( needed_extra_capacity) . ok_or ( CapacityOverflow ) ?;
510- // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
511- let double_cap = self . cap * 2 ;
512- // `double_cap` guarantees exponential growth.
513- let cap = cmp:: max ( double_cap, required_cap) ;
514- Layout :: array :: < T > ( cap) . map_err ( |_| CapacityOverflow ) ?
515- }
516- Exact { used_capacity, needed_extra_capacity } => {
517- let cap =
518- used_capacity. checked_add ( needed_extra_capacity) . ok_or ( CapacityOverflow ) ?;
519- Layout :: array :: < T > ( cap) . map_err ( |_| CapacityOverflow ) ?
520- }
521- } ;
522- alloc_guard ( new_layout. size ( ) ) ?;
523409
524- let memory = if let Some ( ( ptr, old_layout) ) = self . current_memory ( ) {
525- debug_assert_eq ! ( old_layout. align( ) , new_layout. align( ) ) ;
526- unsafe {
527- self . alloc
528- . grow ( ptr, old_layout, new_layout. size ( ) , placement, init)
529- . map_err ( |_| AllocError { layout : new_layout, non_exhaustive : ( ) } ) ?
530- }
531- } else {
532- match placement {
533- MayMove => self . alloc . alloc ( new_layout, init) ,
534- InPlace => Err ( AllocErr ) ,
535- }
536- . map_err ( |_| AllocError { layout : new_layout, non_exhaustive : ( ) } ) ?
537- } ;
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 ) ?;
538444 self . set_memory ( memory) ;
539445 Ok ( ( ) )
540446 }
@@ -562,6 +468,38 @@ impl<T, A: AllocRef> RawVec<T, A> {
562468 }
563469}
564470
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+
565503impl < T > RawVec < T , Global > {
566504 /// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
567505 ///
0 commit comments