Skip to content

Commit 62a3622

Browse files
committed
Add try_reserve and friends
1 parent 40fa827 commit 62a3622

File tree

2 files changed

+55
-18
lines changed

2 files changed

+55
-18
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 <simon.sapin@exyr.org>"]
66
license = "MIT/Apache-2.0"

lib.rs

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,16 @@ impl<T: Clone> ExtendFromSlice<T> for Vec<T> {
201201
}
202202
}
203203

204+
/// Error type for APIs with fallible heap allocation
204205
#[derive(Debug)]
205-
enum CollectionAllocErr {
206+
pub enum CollectionAllocErr {
207+
/// Overflow `usize::MAX` or other error during size computation
206208
CapacityOverflow,
209+
/// The allocator return an error
210+
AllocErr {
211+
/// The layout that was passed to the allocator
212+
layout: Layout,
213+
},
207214
}
208215

209216
impl From<LayoutErr> for CollectionAllocErr {
@@ -212,6 +219,14 @@ impl From<LayoutErr> for CollectionAllocErr {
212219
}
213220
}
214221

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+
215230
/// FIXME: use `Layout::array` when we require a Rust version where it’s stable
216231
/// https://github.com/rust-lang/rust/issues/55724
217232
fn layout_array<T>(n: usize) -> Result<Layout, CollectionAllocErr> {
@@ -714,48 +729,61 @@ impl<A: Array> SmallVec<A> {
714729

715730
/// Re-allocate to set the capacity to `max(new_cap, inline_size())`.
716731
///
717-
/// 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`.
718734
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> {
719742
unsafe {
720743
let (ptr, &mut len, cap) = self.triple_mut();
721744
let unspilled = !self.spilled();
722745
assert!(new_cap >= len);
723746
if new_cap <= self.inline_size() {
724747
if unspilled {
725-
return;
748+
return Ok(());
726749
}
727750
self.data = SmallVecData::from_inline(MaybeUninit::uninit());
728751
ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len);
729752
self.capacity = len;
730753
} else if new_cap != cap {
731-
// Panic on overflow
732-
let layout = layout_array::<A::Item>(new_cap).unwrap();
754+
let layout = layout_array::<A::Item>(new_cap)?;
733755
let new_alloc = NonNull::new(alloc::alloc::alloc(layout))
734-
.unwrap_or_else(|| alloc::alloc::handle_alloc_error(layout))
756+
.ok_or(CollectionAllocErr::AllocErr { layout })?
735757
.cast()
736758
.as_ptr();
737759
ptr::copy_nonoverlapping(ptr, new_alloc, len);
738760
self.data = SmallVecData::from_heap(new_alloc, len);
739761
self.capacity = new_cap;
740762
if unspilled {
741-
return;
763+
return Ok(());
742764
}
743765
} else {
744-
return;
766+
return Ok(());
745767
}
746768
deallocate(ptr, cap);
769+
Ok(())
747770
}
748771
}
749772

750773
/// Reserve capacity for `additional` more elements to be inserted.
751774
///
752775
/// May reserve more space to avoid frequent reallocations.
753776
///
754-
/// If the new capacity would overflow `usize` then it will be set to `usize::max_value()`
755-
/// instead. (This means that inserting `additional` new elements is not guaranteed to be
756-
/// possible after calling this function.)
777+
/// Panics if the capacity computation overflows `usize`.
757778
#[inline]
758779
pub fn reserve(&mut self, additional: usize) {
780+
infallible(self.try_reserve(additional))
781+
}
782+
783+
/// Reserve capacity for `additional` more elements to be inserted.
784+
///
785+
/// May reserve more space to avoid frequent reallocations.
786+
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
759787
// prefer triple_mut() even if triple() would work
760788
// so that the optimizer removes duplicated calls to it
761789
// from callers like insert()
@@ -764,21 +792,30 @@ impl<A: Array> SmallVec<A> {
764792
let new_cap = len
765793
.checked_add(additional)
766794
.and_then(usize::checked_next_power_of_two)
767-
.unwrap_or(usize::max_value());
768-
self.grow(new_cap);
795+
.ok_or(CollectionAllocErr::CapacityOverflow)?;
796+
self.try_grow(new_cap)
797+
} else {
798+
Ok(())
769799
}
770800
}
771801

772802
/// Reserve the minimum capacity for `additional` more elements to be inserted.
773803
///
774804
/// Panics if the new capacity overflows `usize`.
775805
pub fn reserve_exact(&mut self, additional: usize) {
806+
infallible(self.try_reserve_exact(additional))
807+
}
808+
809+
/// Reserve the minimum capacity for `additional` more elements to be inserted.
810+
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
776811
let (_, &mut len, cap) = self.triple_mut();
777812
if cap - len < additional {
778-
match len.checked_add(additional) {
779-
Some(cap) => self.grow(cap),
780-
None => panic!("reserve_exact overflow"),
781-
}
813+
let new_cap = len
814+
.checked_add(additional)
815+
.ok_or(CollectionAllocErr::CapacityOverflow)?;
816+
self.try_grow(new_cap)
817+
} else {
818+
Ok(())
782819
}
783820
}
784821

0 commit comments

Comments
 (0)