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 ;
@@ -211,82 +211,6 @@ impl<T, A: AllocRef> RawVec<T, A> {
211
211
}
212
212
}
213
213
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
-
290
214
/// Ensures that the buffer contains at least enough space to hold
291
215
/// `used_capacity + needed_extra_capacity` elements. If it doesn't already have
292
216
/// enough capacity, will reallocate enough space plus comfortable slack
@@ -354,7 +278,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
354
278
needed_extra_capacity : usize ,
355
279
) -> Result < ( ) , TryReserveError > {
356
280
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 )
358
282
} else {
359
283
Ok ( ( ) )
360
284
}
@@ -381,8 +305,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
381
305
// This is more readable than putting this in one line:
382
306
// `!self.needs_to_grow(...) || self.grow(...).is_ok()`
383
307
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 ( )
386
309
} else {
387
310
true
388
311
}
@@ -423,7 +346,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
423
346
needed_extra_capacity : usize ,
424
347
) -> Result < ( ) , TryReserveError > {
425
348
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)
427
350
} else {
428
351
Ok ( ( ) )
429
352
}
@@ -448,14 +371,6 @@ impl<T, A: AllocRef> RawVec<T, A> {
448
371
}
449
372
}
450
373
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
-
459
374
impl < T , A : AllocRef > RawVec < T , A > {
460
375
/// Returns if the buffer needs to grow to fulfill the needed extra capacity.
461
376
/// Mainly used to make inlining reserve-calls possible without inlining `grow`.
@@ -473,68 +388,59 @@ impl<T, A: AllocRef> RawVec<T, A> {
473
388
self . cap = Self :: capacity_from_bytes ( memory. size ) ;
474
389
}
475
390
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 (
478
399
& mut self ,
479
- strategy : Strategy ,
400
+ used_capacity : usize ,
401
+ needed_extra_capacity : usize ,
480
402
placement : ReallocPlacement ,
481
- init : AllocInit ,
482
403
) -> Result < ( ) , TryReserveError > {
483
- let elem_size = mem:: size_of :: < T > ( ) ;
484
- if elem_size == 0 {
404
+ if mem:: size_of :: < T > ( ) == 0 {
485
405
// Since we return a capacity of `usize::MAX` when `elem_size` is
486
406
// 0, getting to here necessarily means the `RawVec` is overfull.
487
407
return Err ( CapacityOverflow ) ;
488
408
}
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 ( ) ) ?;
523
409
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 ) ?;
538
444
self . set_memory ( memory) ;
539
445
Ok ( ( ) )
540
446
}
@@ -562,6 +468,38 @@ impl<T, A: AllocRef> RawVec<T, A> {
562
468
}
563
469
}
564
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
+
565
503
impl < T > RawVec < T , Global > {
566
504
/// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
567
505
///
0 commit comments