Skip to content

Commit 78c2528

Browse files
authored
Auto merge of #214 - servo:try_reserve, r=nox
Add `try_reserve` and friends This more cleanly replaces this hack adding fallible allocation from outside the crate: https://github.com/servo/servo/blob/faa9dccfe8/components/fallible/lib.rs#L101-L163
2 parents 6c76c41 + 3ec778f commit 78c2528

File tree

2 files changed

+99
-30
lines changed

2 files changed

+99
-30
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "smallvec"
3-
version = "1.3.0"
3+
version = "1.4.0"
44
edition = "2018"
55
authors = ["Simon Sapin <[email protected]>"]
66
license = "MIT/Apache-2.0"

lib.rs

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
//!
4141
//! When this feature is enabled, `SmallVec` works with any arrays of any size, not just a fixed
4242
//! list of sizes.
43-
//!
43+
//!
4444
//! ### `specialization`
4545
//!
4646
//! **This feature is unstable and requires a nightly build of the Rust toolchain.**
@@ -71,6 +71,7 @@ pub extern crate alloc;
7171
#[cfg(any(test, feature = "write"))]
7272
extern crate std;
7373

74+
use alloc::alloc::{Layout, LayoutErr};
7475
use alloc::boxed::Box;
7576
use alloc::{vec, vec::Vec};
7677
use core::borrow::{Borrow, BorrowMut};
@@ -200,9 +201,46 @@ impl<T: Clone> ExtendFromSlice<T> for Vec<T> {
200201
}
201202
}
202203

204+
/// Error type for APIs with fallible heap allocation
205+
#[derive(Debug)]
206+
pub enum CollectionAllocErr {
207+
/// Overflow `usize::MAX` or other error during size computation
208+
CapacityOverflow,
209+
/// The allocator return an error
210+
AllocErr {
211+
/// The layout that was passed to the allocator
212+
layout: Layout,
213+
},
214+
}
215+
216+
impl From<LayoutErr> for CollectionAllocErr {
217+
fn from(_: LayoutErr) -> Self {
218+
CollectionAllocErr::CapacityOverflow
219+
}
220+
}
221+
222+
fn infallible<T>(result: Result<T, CollectionAllocErr>) -> T {
223+
match result {
224+
Ok(x) => x,
225+
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
226+
Err(CollectionAllocErr::AllocErr { layout }) => alloc::alloc::handle_alloc_error(layout),
227+
}
228+
}
229+
230+
/// FIXME: use `Layout::array` when we require a Rust version where it’s stable
231+
/// https://github.com/rust-lang/rust/issues/55724
232+
fn layout_array<T>(n: usize) -> Result<Layout, CollectionAllocErr> {
233+
let size = mem::size_of::<T>().checked_mul(n)
234+
.ok_or(CollectionAllocErr::CapacityOverflow)?;
235+
let align = mem::align_of::<T>();
236+
Layout::from_size_align(size, align)
237+
.map_err(|_| CollectionAllocErr::CapacityOverflow)
238+
}
239+
203240
unsafe fn deallocate<T>(ptr: *mut T, capacity: usize) {
204-
let _vec: Vec<T> = Vec::from_raw_parts(ptr, 0, capacity);
205-
// Let it drop.
241+
// This unwrap should succeed since the same did when allocating.
242+
let layout = layout_array::<T>(capacity).unwrap();
243+
alloc::alloc::dealloc(ptr as *mut u8, layout)
206244
}
207245

208246
/// An iterator that removes the items from a `SmallVec` and yields them by value.
@@ -691,69 +729,100 @@ impl<A: Array> SmallVec<A> {
691729

692730
/// Re-allocate to set the capacity to `max(new_cap, inline_size())`.
693731
///
694-
/// Panics if `new_cap` is less than the vector's length.
732+
/// Panics if `new_cap` is less than the vector's length
733+
/// or if the capacity computation overflows `usize`.
695734
pub fn grow(&mut self, new_cap: usize) {
735+
infallible(self.try_grow(new_cap))
736+
}
737+
738+
/// Re-allocate to set the capacity to `max(new_cap, inline_size())`.
739+
///
740+
/// Panics if `new_cap` is less than the vector's length
741+
pub fn try_grow(&mut self, new_cap: usize) -> Result<(), CollectionAllocErr> {
696742
unsafe {
697743
let (ptr, &mut len, cap) = self.triple_mut();
698744
let unspilled = !self.spilled();
699745
assert!(new_cap >= len);
700746
if new_cap <= self.inline_size() {
701747
if unspilled {
702-
return;
748+
return Ok(());
703749
}
704750
self.data = SmallVecData::from_inline(MaybeUninit::uninit());
705751
ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len);
706752
self.capacity = len;
753+
deallocate(ptr, cap);
707754
} else if new_cap != cap {
708-
let mut vec = Vec::with_capacity(new_cap);
709-
let new_alloc = vec.as_mut_ptr();
710-
mem::forget(vec);
711-
ptr::copy_nonoverlapping(ptr, new_alloc, len);
712-
self.data = SmallVecData::from_heap(new_alloc, len);
713-
self.capacity = new_cap;
755+
let layout = layout_array::<A::Item>(new_cap)?;
756+
let new_alloc;
714757
if unspilled {
715-
return;
758+
new_alloc = NonNull::new(alloc::alloc::alloc(layout))
759+
.ok_or(CollectionAllocErr::AllocErr { layout })?
760+
.cast()
761+
.as_ptr();
762+
ptr::copy_nonoverlapping(ptr, new_alloc, len);
763+
} else {
764+
// This should never fail since the same succeeded
765+
// when previously allocating `ptr`.
766+
let old_layout = layout_array::<A::Item>(cap)?;
767+
768+
let new_ptr = alloc::alloc::realloc(ptr as *mut u8, old_layout, layout.size());
769+
new_alloc = NonNull::new(new_ptr)
770+
.ok_or(CollectionAllocErr::AllocErr { layout })?
771+
.cast()
772+
.as_ptr();
716773
}
717-
} else {
718-
return;
774+
self.data = SmallVecData::from_heap(new_alloc, len);
775+
self.capacity = new_cap;
719776
}
720-
deallocate(ptr, cap);
777+
Ok(())
721778
}
722779
}
723780

724781
/// Reserve capacity for `additional` more elements to be inserted.
725782
///
726783
/// May reserve more space to avoid frequent reallocations.
727784
///
728-
/// If the new capacity would overflow `usize` then it will be set to `usize::max_value()`
729-
/// instead. (This means that inserting `additional` new elements is not guaranteed to be
730-
/// possible after calling this function.)
785+
/// Panics if the capacity computation overflows `usize`.
731786
#[inline]
732787
pub fn reserve(&mut self, additional: usize) {
788+
infallible(self.try_reserve(additional))
789+
}
790+
791+
/// Reserve capacity for `additional` more elements to be inserted.
792+
///
793+
/// May reserve more space to avoid frequent reallocations.
794+
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
733795
// prefer triple_mut() even if triple() would work
734796
// so that the optimizer removes duplicated calls to it
735797
// from callers like insert()
736798
let (_, &mut len, cap) = self.triple_mut();
737-
if cap - len < additional {
738-
let new_cap = len
739-
.checked_add(additional)
740-
.and_then(usize::checked_next_power_of_two)
741-
.unwrap_or(usize::max_value());
742-
self.grow(new_cap);
799+
if cap - len >= additional {
800+
return Ok(());
743801
}
802+
let new_cap = len
803+
.checked_add(additional)
804+
.and_then(usize::checked_next_power_of_two)
805+
.ok_or(CollectionAllocErr::CapacityOverflow)?;
806+
self.try_grow(new_cap)
744807
}
745808

746809
/// Reserve the minimum capacity for `additional` more elements to be inserted.
747810
///
748811
/// Panics if the new capacity overflows `usize`.
749812
pub fn reserve_exact(&mut self, additional: usize) {
813+
infallible(self.try_reserve_exact(additional))
814+
}
815+
816+
/// Reserve the minimum capacity for `additional` more elements to be inserted.
817+
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
750818
let (_, &mut len, cap) = self.triple_mut();
751-
if cap - len < additional {
752-
match len.checked_add(additional) {
753-
Some(cap) => self.grow(cap),
754-
None => panic!("reserve_exact overflow"),
755-
}
819+
if cap - len >= additional {
820+
return Ok(());
756821
}
822+
let new_cap = len
823+
.checked_add(additional)
824+
.ok_or(CollectionAllocErr::CapacityOverflow)?;
825+
self.try_grow(new_cap)
757826
}
758827

759828
/// Shrink the capacity of the vector as much as possible.

0 commit comments

Comments
 (0)