Skip to content

Commit 7b820b0

Browse files
committed
Split finish_grow in two, and move capacity computations.
1 parent bceeb90 commit 7b820b0

File tree

1 file changed

+89
-37
lines changed

1 file changed

+89
-37
lines changed

library/alloc/src/raw_vec.rs

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,6 @@ impl<T> RawVec<T, Global> {
102102
}
103103

104104
impl<T, A: Allocator> RawVec<T, A> {
105-
// Tiny Vecs are dumb. Skip to:
106-
// - 8 if the element size is 1, because any heap allocators is likely
107-
// to round up a request of less than 8 bytes to at least 8 bytes.
108-
// - 4 if elements are moderate-sized (<= 1 KiB).
109-
// - 1 otherwise, to avoid wasting too much space for very short Vecs.
110-
const MIN_NON_ZERO_CAP: usize = if mem::size_of::<T>() == 1 {
111-
8
112-
} else if mem::size_of::<T>() <= 1024 {
113-
4
114-
} else {
115-
1
116-
};
117-
118105
/// Like `new`, but parameterized over the choice of allocator for
119106
/// the returned `RawVec`.
120107
#[rustc_allow_const_fn_unstable(const_fn)]
@@ -381,22 +368,16 @@ impl<T, A: Allocator> RawVec<T, A> {
381368
// of the code that doesn't depend on `T` as possible is in functions that
382369
// are non-generic over `T`.
383370
fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> {
384-
// This is ensured by the calling contexts.
385-
debug_assert!(additional > 0);
386-
387-
// Nothing we can really do about these checks, sadly. (If this method
388-
// is called for a zero-sized `T` after `needs_to_grow()` has
389-
// succeeded, this early return will occur.)
390-
let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
391-
392-
// This guarantees exponential growth. The doubling cannot overflow
393-
// because `cap <= isize::MAX` and the type of `cap` is `usize`.
394-
let cap = cmp::max(self.cap * 2, required_cap);
395-
let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
396-
397-
// `finish_grow` is non-generic over `T`.
371+
// `finish_grow_amortized` is non-generic over `T`.
398372
let elem_layout = Layout::new::<T>();
399-
let ptr = finish_grow(cap, elem_layout, self.current_memory(), &mut self.alloc)?;
373+
let ptr = finish_grow_amortized(
374+
len,
375+
additional,
376+
elem_layout,
377+
self.cap,
378+
self.current_memory(),
379+
&mut self.alloc,
380+
)?;
400381
self.set_ptr(ptr);
401382
Ok(())
402383
}
@@ -409,14 +390,15 @@ impl<T, A: Allocator> RawVec<T, A> {
409390
// `grow_amortized`, but this method is usually instantiated less often so
410391
// it's less critical.
411392
fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> {
412-
// Nothing we can really do about these checks, sadly. (If this method
413-
// is called for a zero-sized `T` after `needs_to_grow()` has
414-
// succeeded, this early return will occur.)
415-
let cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
416-
417-
// `finish_grow` is non-generic over `T`.
393+
// `finish_grow_exact` is non-generic over `T`.
418394
let elem_layout = Layout::new::<T>();
419-
let ptr = finish_grow(cap, elem_layout, self.current_memory(), &mut self.alloc)?;
395+
let ptr = finish_grow_exact(
396+
len,
397+
additional,
398+
elem_layout,
399+
self.current_memory(),
400+
&mut self.alloc,
401+
)?;
420402
self.set_ptr(ptr);
421403
Ok(())
422404
}
@@ -443,15 +425,85 @@ impl<T, A: Allocator> RawVec<T, A> {
443425
// significant, because the number of different `A` types seen in practice is
444426
// much smaller than the number of `T` types.)
445427
#[inline(never)]
446-
fn finish_grow<A>(
447-
cap: usize,
428+
fn finish_grow_amortized<A>(
429+
len: usize,
430+
additional: usize,
431+
elem_layout: Layout,
432+
current_cap: usize,
433+
current_memory: Option<(NonNull<u8>, Layout)>,
434+
alloc: &mut A,
435+
) -> Result<NonNull<[u8]>, TryReserveError>
436+
where
437+
A: Allocator,
438+
{
439+
// This is ensured by the calling contexts.
440+
debug_assert!(additional > 0);
441+
442+
// Tiny Vecs are dumb. Skip to:
443+
// - 8 if the element size is 1, because any heap allocators is likely
444+
// to round up a request of less than 8 bytes to at least 8 bytes.
445+
// - 4 if elements are moderate-sized (<= 1 KiB).
446+
// - 1 otherwise, to avoid wasting too much space for very short Vecs.
447+
let min_non_zero_cap: usize = if elem_layout.size() == 1 {
448+
8
449+
} else if elem_layout.size() <= 1024 {
450+
4
451+
} else {
452+
1
453+
};
454+
455+
// Nothing we can really do about these checks, sadly. (If this method
456+
// is called for a zero-sized `T` after `needs_to_grow()` has
457+
// succeeded, this early return will occur.)
458+
let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
459+
460+
// This guarantees exponential growth. The doubling cannot overflow
461+
// because `cap <= isize::MAX` and the type of `cap` is `usize`.
462+
let cap = cmp::max(current_cap * 2, required_cap);
463+
let cap = cmp::max(min_non_zero_cap, cap);
464+
465+
let array_size = elem_layout.size().checked_mul(cap).ok_or(CapacityOverflow)?;
466+
alloc_guard(array_size)?;
467+
468+
let new_layout = unsafe { Layout::from_size_align_unchecked(array_size, elem_layout.align()) };
469+
470+
let memory = if let Some((ptr, old_layout)) = current_memory {
471+
debug_assert_eq!(old_layout.align(), new_layout.align());
472+
unsafe {
473+
// The allocator checks for alignment equality
474+
intrinsics::assume(old_layout.align() == new_layout.align());
475+
alloc.grow(ptr, old_layout, new_layout)
476+
}
477+
} else {
478+
alloc.allocate(new_layout)
479+
};
480+
481+
memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into())
482+
}
483+
484+
// This function is outside `RawVec` to minimize compile times. See the comment
485+
// above `RawVec::grow_exact` for details. (The `A` parameter isn't
486+
// significant, because the number of different `A` types seen in practice is
487+
// much smaller than the number of `T` types.)
488+
#[inline(never)]
489+
fn finish_grow_exact<A>(
490+
len: usize,
491+
additional: usize,
448492
elem_layout: Layout,
449493
current_memory: Option<(NonNull<u8>, Layout)>,
450494
alloc: &mut A,
451495
) -> Result<NonNull<[u8]>, TryReserveError>
452496
where
453497
A: Allocator,
454498
{
499+
// This is ensured by the calling contexts.
500+
debug_assert!(additional > 0);
501+
502+
// Nothing we can really do about these checks, sadly. (If this method
503+
// is called for a zero-sized `T` after `needs_to_grow()` has
504+
// succeeded, this early return will occur.)
505+
let cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
506+
455507
let array_size = elem_layout.size().checked_mul(cap).ok_or(CapacityOverflow)?;
456508
alloc_guard(array_size)?;
457509

0 commit comments

Comments
 (0)