Skip to content

Commit 4d29038

Browse files
committed
add try_reserve and try_reserve_exact to Vec
1 parent c774c95 commit 4d29038

File tree

7 files changed

+370
-40
lines changed

7 files changed

+370
-40
lines changed

src/liballoc/allocator.rs

+19
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,25 @@ impl fmt::Display for AllocErr {
382382
}
383383
}
384384

385+
/// Augments `AllocErr` with a CapacityOverflow variant.
386+
#[derive(Clone, PartialEq, Eq, Debug)]
387+
#[unstable(feature = "try_reserve", reason = "new API", issue="0000")]
388+
pub enum CollectionAllocErr {
389+
/// Error due to the computed capacity exceeding the collection's maximum
390+
/// (usually `isize::MAX` bytes).
391+
CapacityOverflow,
392+
/// Error due to the allocator (see the `AllocErr` type's docs).
393+
AllocErr(AllocErr),
394+
}
395+
396+
#[unstable(feature = "try_reserve", reason = "new API", issue="0000")]
397+
impl From<AllocErr> for CollectionAllocErr {
398+
fn from(err: AllocErr) -> Self {
399+
CollectionAllocErr::AllocErr(err)
400+
}
401+
}
402+
403+
385404
/// The `CannotReallocInPlace` error is used when `grow_in_place` or
386405
/// `shrink_in_place` were unable to reuse the given memory block for
387406
/// a requested layout.

src/liballoc/raw_vec.rs

+60-39
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use core::ptr::{self, Unique};
1515
use core::slice;
1616
use heap::{Alloc, Layout, Heap};
1717
use super::boxed::Box;
18+
use super::allocator::CollectionAllocErr;
19+
use super::allocator::CollectionAllocErr::*;
1820

1921
/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
2022
/// a buffer of memory on the heap without having to worry about all the corner cases
@@ -84,7 +86,7 @@ impl<T, A: Alloc> RawVec<T, A> {
8486
let elem_size = mem::size_of::<T>();
8587

8688
let alloc_size = cap.checked_mul(elem_size).expect("capacity overflow");
87-
alloc_guard(alloc_size);
89+
alloc_guard(alloc_size).expect("capacity overflow");
8890

8991
// handles ZSTs and `cap = 0` alike
9092
let ptr = if alloc_size == 0 {
@@ -308,7 +310,7 @@ impl<T, A: Alloc> RawVec<T, A> {
308310
let new_cap = 2 * self.cap;
309311
let new_size = new_cap * elem_size;
310312
let new_layout = Layout::from_size_align_unchecked(new_size, cur.align());
311-
alloc_guard(new_size);
313+
alloc_guard(new_size).expect("capacity overflow");
312314
let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8,
313315
cur,
314316
new_layout);
@@ -367,7 +369,7 @@ impl<T, A: Alloc> RawVec<T, A> {
367369
// overflow and the alignment is sufficiently small.
368370
let new_cap = 2 * self.cap;
369371
let new_size = new_cap * elem_size;
370-
alloc_guard(new_size);
372+
alloc_guard(new_size).expect("capacity overflow");
371373
let ptr = self.ptr() as *mut _;
372374
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
373375
match self.a.grow_in_place(ptr, old_layout, new_layout) {
@@ -403,7 +405,9 @@ impl<T, A: Alloc> RawVec<T, A> {
403405
/// # Aborts
404406
///
405407
/// Aborts on OOM
406-
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
408+
pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize)
409+
-> Result<(), CollectionAllocErr> {
410+
407411
unsafe {
408412
// NOTE: we don't early branch on ZSTs here because we want this
409413
// to actually catch "asking for more than usize::MAX" in that case.
@@ -413,43 +417,51 @@ impl<T, A: Alloc> RawVec<T, A> {
413417
// Don't actually need any more capacity.
414418
// Wrapping in case they gave a bad `used_cap`.
415419
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
416-
return;
420+
return Ok(());
417421
}
418422

419423
// Nothing we can really do about these checks :(
420-
let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow");
421-
let new_layout = match Layout::array::<T>(new_cap) {
422-
Some(layout) => layout,
423-
None => panic!("capacity overflow"),
424-
};
425-
alloc_guard(new_layout.size());
424+
let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
425+
let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
426+
427+
alloc_guard(new_layout.size())?;
428+
426429
let res = match self.current_layout() {
427430
Some(layout) => {
428431
let old_ptr = self.ptr.as_ptr() as *mut u8;
429432
self.a.realloc(old_ptr, layout, new_layout)
430433
}
431434
None => self.a.alloc(new_layout),
432435
};
433-
let uniq = match res {
434-
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
435-
Err(e) => self.a.oom(e),
436-
};
437-
self.ptr = uniq;
436+
437+
self.ptr = Unique::new_unchecked(res? as *mut T);
438438
self.cap = new_cap;
439+
440+
Ok(())
441+
}
442+
}
443+
444+
/// The same as try_reserve_exact, but errors are lowered to a call to oom().
445+
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
446+
match self.try_reserve_exact(used_cap, needed_extra_cap) {
447+
Err(CapacityOverflow) => panic!("capacity overflow"),
448+
Err(AllocErr(e)) => self.a.oom(e),
449+
Ok(()) => { /* yay */ }
439450
}
440451
}
441452

442453
/// Calculates the buffer's new size given that it'll hold `used_cap +
443454
/// needed_extra_cap` elements. This logic is used in amortized reserve methods.
444455
/// Returns `(new_capacity, new_alloc_size)`.
445-
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> usize {
456+
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize)
457+
-> Result<usize, CollectionAllocErr> {
458+
446459
// Nothing we can really do about these checks :(
447-
let required_cap = used_cap.checked_add(needed_extra_cap)
448-
.expect("capacity overflow");
460+
let required_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
449461
// Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
450462
let double_cap = self.cap * 2;
451463
// `double_cap` guarantees exponential growth.
452-
cmp::max(double_cap, required_cap)
464+
Ok(cmp::max(double_cap, required_cap))
453465
}
454466

455467
/// Ensures that the buffer contains at least enough space to hold
@@ -504,7 +516,8 @@ impl<T, A: Alloc> RawVec<T, A> {
504516
/// # vector.push_all(&[1, 3, 5, 7, 9]);
505517
/// # }
506518
/// ```
507-
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
519+
pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize)
520+
-> Result<(), CollectionAllocErr> {
508521
unsafe {
509522
// NOTE: we don't early branch on ZSTs here because we want this
510523
// to actually catch "asking for more than usize::MAX" in that case.
@@ -514,30 +527,36 @@ impl<T, A: Alloc> RawVec<T, A> {
514527
// Don't actually need any more capacity.
515528
// Wrapping in case they give a bad `used_cap`
516529
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
517-
return;
530+
return Ok(());
518531
}
519532

520-
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap);
533+
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?;
534+
let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
521535

522-
let new_layout = match Layout::array::<T>(new_cap) {
523-
Some(layout) => layout,
524-
None => panic!("capacity overflow"),
525-
};
526536
// FIXME: may crash and burn on over-reserve
527-
alloc_guard(new_layout.size());
537+
alloc_guard(new_layout.size())?;
538+
528539
let res = match self.current_layout() {
529540
Some(layout) => {
530541
let old_ptr = self.ptr.as_ptr() as *mut u8;
531542
self.a.realloc(old_ptr, layout, new_layout)
532543
}
533544
None => self.a.alloc(new_layout),
534545
};
535-
let uniq = match res {
536-
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
537-
Err(e) => self.a.oom(e),
538-
};
539-
self.ptr = uniq;
546+
547+
self.ptr = Unique::new_unchecked(res? as *mut T);
540548
self.cap = new_cap;
549+
550+
Ok(())
551+
}
552+
}
553+
554+
/// The same as try_reserve, but errors are lowered to a call to oom().
555+
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
556+
match self.try_reserve(used_cap, needed_extra_cap) {
557+
Err(CapacityOverflow) => panic!("capacity overflow"),
558+
Err(AllocErr(e)) => self.a.oom(e),
559+
Ok(()) => { /* yay */ }
541560
}
542561
}
543562

@@ -576,7 +595,8 @@ impl<T, A: Alloc> RawVec<T, A> {
576595
return false;
577596
}
578597

579-
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap);
598+
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)
599+
.expect("capacity overflow");
580600

581601
// Here, `cap < used_cap + needed_extra_cap <= new_cap`
582602
// (regardless of whether `self.cap - used_cap` wrapped).
@@ -585,7 +605,7 @@ impl<T, A: Alloc> RawVec<T, A> {
585605
let ptr = self.ptr() as *mut _;
586606
let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
587607
// FIXME: may crash and burn on over-reserve
588-
alloc_guard(new_layout.size());
608+
alloc_guard(new_layout.size()).expect("capacity overflow");
589609
match self.a.grow_in_place(ptr, old_layout, new_layout) {
590610
Ok(_) => {
591611
self.cap = new_cap;
@@ -709,10 +729,11 @@ unsafe impl<#[may_dangle] T, A: Alloc> Drop for RawVec<T, A> {
709729
// all 4GB in user-space. e.g. PAE or x32
710730

711731
#[inline]
712-
fn alloc_guard(alloc_size: usize) {
713-
if mem::size_of::<usize>() < 8 {
714-
assert!(alloc_size <= ::core::isize::MAX as usize,
715-
"capacity overflow");
732+
fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> {
733+
if mem::size_of::<usize>() < 8 && alloc_size <= ::core::isize::MAX as usize {
734+
Err(CapacityOverflow)
735+
} else {
736+
Ok(())
716737
}
717738
}
718739

src/liballoc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#![feature(splice)]
2727
#![feature(str_escape)]
2828
#![feature(test)]
29+
#![feature(try_reserve)]
2930
#![feature(unboxed_closures)]
3031
#![feature(unicode)]
3132

0 commit comments

Comments
 (0)