mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-17 01:13:11 +00:00
implementing fallible allocation API (try_reserve) for Vec, String and HashMap
This commit is contained in:
parent
fab632f975
commit
92bfcd2b19
@ -373,6 +373,24 @@ impl fmt::Display for CannotReallocInPlace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Augments `AllocErr` with a CapacityOverflow variant.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub enum CollectionAllocErr {
|
||||||
|
/// Error due to the computed capacity exceeding the collection's maximum
|
||||||
|
/// (usually `isize::MAX` bytes).
|
||||||
|
CapacityOverflow,
|
||||||
|
/// Error due to the allocator (see the `AllocErr` type's docs).
|
||||||
|
AllocErr(AllocErr),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
impl From<AllocErr> for CollectionAllocErr {
|
||||||
|
fn from(err: AllocErr) -> Self {
|
||||||
|
CollectionAllocErr::AllocErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An implementation of `Alloc` can allocate, reallocate, and
|
/// An implementation of `Alloc` can allocate, reallocate, and
|
||||||
/// deallocate arbitrary blocks of data described via `Layout`.
|
/// deallocate arbitrary blocks of data described via `Layout`.
|
||||||
///
|
///
|
||||||
|
@ -117,6 +117,7 @@
|
|||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
#![feature(str_internals)]
|
#![feature(str_internals)]
|
||||||
#![feature(trusted_len)]
|
#![feature(trusted_len)]
|
||||||
|
#![feature(try_reserve)]
|
||||||
#![feature(unboxed_closures)]
|
#![feature(unboxed_closures)]
|
||||||
#![feature(unicode)]
|
#![feature(unicode)]
|
||||||
#![feature(unsize)]
|
#![feature(unsize)]
|
||||||
|
@ -15,6 +15,8 @@ use core::ptr::{self, Unique};
|
|||||||
use core::slice;
|
use core::slice;
|
||||||
use heap::{Alloc, Layout, Heap};
|
use heap::{Alloc, Layout, Heap};
|
||||||
use super::boxed::Box;
|
use super::boxed::Box;
|
||||||
|
use super::allocator::CollectionAllocErr;
|
||||||
|
use super::allocator::CollectionAllocErr::*;
|
||||||
|
|
||||||
/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
|
/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
|
||||||
/// a buffer of memory on the heap without having to worry about all the corner cases
|
/// 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> {
|
|||||||
let elem_size = mem::size_of::<T>();
|
let elem_size = mem::size_of::<T>();
|
||||||
|
|
||||||
let alloc_size = cap.checked_mul(elem_size).expect("capacity overflow");
|
let alloc_size = cap.checked_mul(elem_size).expect("capacity overflow");
|
||||||
alloc_guard(alloc_size);
|
alloc_guard(alloc_size).expect("capacity overflow");
|
||||||
|
|
||||||
// handles ZSTs and `cap = 0` alike
|
// handles ZSTs and `cap = 0` alike
|
||||||
let ptr = if alloc_size == 0 {
|
let ptr = if alloc_size == 0 {
|
||||||
@ -308,7 +310,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
let new_cap = 2 * self.cap;
|
let new_cap = 2 * self.cap;
|
||||||
let new_size = new_cap * elem_size;
|
let new_size = new_cap * elem_size;
|
||||||
let new_layout = Layout::from_size_align_unchecked(new_size, cur.align());
|
let new_layout = Layout::from_size_align_unchecked(new_size, cur.align());
|
||||||
alloc_guard(new_size);
|
alloc_guard(new_size).expect("capacity overflow");
|
||||||
let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8,
|
let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8,
|
||||||
cur,
|
cur,
|
||||||
new_layout);
|
new_layout);
|
||||||
@ -367,7 +369,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
// overflow and the alignment is sufficiently small.
|
// overflow and the alignment is sufficiently small.
|
||||||
let new_cap = 2 * self.cap;
|
let new_cap = 2 * self.cap;
|
||||||
let new_size = new_cap * elem_size;
|
let new_size = new_cap * elem_size;
|
||||||
alloc_guard(new_size);
|
alloc_guard(new_size).expect("capacity overflow");
|
||||||
let ptr = self.ptr() as *mut _;
|
let ptr = self.ptr() as *mut _;
|
||||||
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||||
match self.a.grow_in_place(ptr, old_layout, new_layout) {
|
match self.a.grow_in_place(ptr, old_layout, new_layout) {
|
||||||
@ -403,7 +405,9 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
/// # Aborts
|
/// # Aborts
|
||||||
///
|
///
|
||||||
/// Aborts on OOM
|
/// Aborts on OOM
|
||||||
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize)
|
||||||
|
-> Result<(), CollectionAllocErr> {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// NOTE: we don't early branch on ZSTs here because we want this
|
// NOTE: we don't early branch on ZSTs here because we want this
|
||||||
// to actually catch "asking for more than usize::MAX" in that case.
|
// to actually catch "asking for more than usize::MAX" in that case.
|
||||||
@ -413,16 +417,15 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
// Don't actually need any more capacity.
|
// Don't actually need any more capacity.
|
||||||
// Wrapping in case they gave a bad `used_cap`.
|
// Wrapping in case they gave a bad `used_cap`.
|
||||||
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
|
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing we can really do about these checks :(
|
// Nothing we can really do about these checks :(
|
||||||
let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow");
|
let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
|
||||||
let new_layout = match Layout::array::<T>(new_cap) {
|
let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
|
||||||
Some(layout) => layout,
|
|
||||||
None => panic!("capacity overflow"),
|
alloc_guard(new_layout.size())?;
|
||||||
};
|
|
||||||
alloc_guard(new_layout.size());
|
|
||||||
let res = match self.current_layout() {
|
let res = match self.current_layout() {
|
||||||
Some(layout) => {
|
Some(layout) => {
|
||||||
let old_ptr = self.ptr.as_ptr() as *mut u8;
|
let old_ptr = self.ptr.as_ptr() as *mut u8;
|
||||||
@ -430,26 +433,34 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
}
|
}
|
||||||
None => self.a.alloc(new_layout),
|
None => self.a.alloc(new_layout),
|
||||||
};
|
};
|
||||||
let uniq = match res {
|
|
||||||
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
|
self.ptr = Unique::new_unchecked(res? as *mut T);
|
||||||
Err(e) => self.a.oom(e),
|
|
||||||
};
|
|
||||||
self.ptr = uniq;
|
|
||||||
self.cap = new_cap;
|
self.cap = new_cap;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
||||||
|
match self.try_reserve_exact(used_cap, needed_extra_cap) {
|
||||||
|
Err(CapacityOverflow) => panic!("capacity overflow"),
|
||||||
|
Err(AllocErr(e)) => self.a.oom(e),
|
||||||
|
Ok(()) => { /* yay */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculates the buffer's new size given that it'll hold `used_cap +
|
/// Calculates the buffer's new size given that it'll hold `used_cap +
|
||||||
/// needed_extra_cap` elements. This logic is used in amortized reserve methods.
|
/// needed_extra_cap` elements. This logic is used in amortized reserve methods.
|
||||||
/// Returns `(new_capacity, new_alloc_size)`.
|
/// Returns `(new_capacity, new_alloc_size)`.
|
||||||
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> usize {
|
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize)
|
||||||
|
-> Result<usize, CollectionAllocErr> {
|
||||||
|
|
||||||
// Nothing we can really do about these checks :(
|
// Nothing we can really do about these checks :(
|
||||||
let required_cap = used_cap.checked_add(needed_extra_cap)
|
let required_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
|
||||||
.expect("capacity overflow");
|
|
||||||
// Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
|
// Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
|
||||||
let double_cap = self.cap * 2;
|
let double_cap = self.cap * 2;
|
||||||
// `double_cap` guarantees exponential growth.
|
// `double_cap` guarantees exponential growth.
|
||||||
cmp::max(double_cap, required_cap)
|
Ok(cmp::max(double_cap, required_cap))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that the buffer contains at least enough space to hold
|
/// Ensures that the buffer contains at least enough space to hold
|
||||||
@ -504,8 +515,9 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
/// # vector.push_all(&[1, 3, 5, 7, 9]);
|
/// # vector.push_all(&[1, 3, 5, 7, 9]);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize)
|
||||||
unsafe {
|
-> Result<(), CollectionAllocErr> {
|
||||||
|
unsafe {
|
||||||
// NOTE: we don't early branch on ZSTs here because we want this
|
// NOTE: we don't early branch on ZSTs here because we want this
|
||||||
// to actually catch "asking for more than usize::MAX" in that case.
|
// to actually catch "asking for more than usize::MAX" in that case.
|
||||||
// If we make it past the first branch then we are guaranteed to
|
// If we make it past the first branch then we are guaranteed to
|
||||||
@ -514,17 +526,15 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
// Don't actually need any more capacity.
|
// Don't actually need any more capacity.
|
||||||
// Wrapping in case they give a bad `used_cap`
|
// Wrapping in case they give a bad `used_cap`
|
||||||
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
|
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap);
|
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?;
|
||||||
|
let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
|
||||||
|
|
||||||
|
// FIXME: may crash and burn on over-reserve
|
||||||
|
alloc_guard(new_layout.size())?;
|
||||||
|
|
||||||
let new_layout = match Layout::array::<T>(new_cap) {
|
|
||||||
Some(layout) => layout,
|
|
||||||
None => panic!("capacity overflow"),
|
|
||||||
};
|
|
||||||
// FIXME: may crash and burn on over-reserve
|
|
||||||
alloc_guard(new_layout.size());
|
|
||||||
let res = match self.current_layout() {
|
let res = match self.current_layout() {
|
||||||
Some(layout) => {
|
Some(layout) => {
|
||||||
let old_ptr = self.ptr.as_ptr() as *mut u8;
|
let old_ptr = self.ptr.as_ptr() as *mut u8;
|
||||||
@ -532,15 +542,22 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
}
|
}
|
||||||
None => self.a.alloc(new_layout),
|
None => self.a.alloc(new_layout),
|
||||||
};
|
};
|
||||||
let uniq = match res {
|
|
||||||
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
|
self.ptr = Unique::new_unchecked(res? as *mut T);
|
||||||
Err(e) => self.a.oom(e),
|
|
||||||
};
|
|
||||||
self.ptr = uniq;
|
|
||||||
self.cap = new_cap;
|
self.cap = new_cap;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as try_reserve, but errors are lowered to a call to oom().
|
||||||
|
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
||||||
|
match self.try_reserve(used_cap, needed_extra_cap) {
|
||||||
|
Err(CapacityOverflow) => panic!("capacity overflow"),
|
||||||
|
Err(AllocErr(e)) => self.a.oom(e),
|
||||||
|
Ok(()) => { /* yay */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Attempts to ensure that the buffer contains at least enough space to hold
|
/// Attempts to ensure that the buffer contains at least enough space to hold
|
||||||
/// `used_cap + needed_extra_cap` elements. If it doesn't already have
|
/// `used_cap + needed_extra_cap` elements. If it doesn't already have
|
||||||
/// enough capacity, will reallocate in place enough space plus comfortable slack
|
/// enough capacity, will reallocate in place enough space plus comfortable slack
|
||||||
@ -576,7 +593,8 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap);
|
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)
|
||||||
|
.expect("capacity overflow");
|
||||||
|
|
||||||
// Here, `cap < used_cap + needed_extra_cap <= new_cap`
|
// Here, `cap < used_cap + needed_extra_cap <= new_cap`
|
||||||
// (regardless of whether `self.cap - used_cap` wrapped).
|
// (regardless of whether `self.cap - used_cap` wrapped).
|
||||||
@ -585,7 +603,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||||||
let ptr = self.ptr() as *mut _;
|
let ptr = self.ptr() as *mut _;
|
||||||
let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
|
let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
|
||||||
// FIXME: may crash and burn on over-reserve
|
// FIXME: may crash and burn on over-reserve
|
||||||
alloc_guard(new_layout.size());
|
alloc_guard(new_layout.size()).expect("capacity overflow");
|
||||||
match self.a.grow_in_place(ptr, old_layout, new_layout) {
|
match self.a.grow_in_place(ptr, old_layout, new_layout) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.cap = new_cap;
|
self.cap = new_cap;
|
||||||
@ -709,14 +727,14 @@ unsafe impl<#[may_dangle] T, A: Alloc> Drop for RawVec<T, A> {
|
|||||||
// all 4GB in user-space. e.g. PAE or x32
|
// all 4GB in user-space. e.g. PAE or x32
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc_guard(alloc_size: usize) {
|
fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> {
|
||||||
if mem::size_of::<usize>() < 8 {
|
if mem::size_of::<usize>() < 8 && alloc_size > ::core::isize::MAX as usize {
|
||||||
assert!(alloc_size <= ::core::isize::MAX as usize,
|
Err(CapacityOverflow)
|
||||||
"capacity overflow");
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -71,6 +71,7 @@ use Bound::{Excluded, Included, Unbounded};
|
|||||||
use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars};
|
use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars};
|
||||||
use vec::Vec;
|
use vec::Vec;
|
||||||
use boxed::Box;
|
use boxed::Box;
|
||||||
|
use super::allocator::CollectionAllocErr;
|
||||||
|
|
||||||
/// A UTF-8 encoded, growable string.
|
/// A UTF-8 encoded, growable string.
|
||||||
///
|
///
|
||||||
@ -920,6 +921,79 @@ impl String {
|
|||||||
self.vec.reserve_exact(additional)
|
self.vec.reserve_exact(additional)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to reserve capacity for at least `additional` more elements to be inserted
|
||||||
|
/// in the given `String`. The collection may reserve more space to avoid
|
||||||
|
/// frequent reallocations. After calling `reserve`, capacity will be
|
||||||
|
/// greater than or equal to `self.len() + additional`. Does nothing if
|
||||||
|
/// capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::CollectionAllocErr;
|
||||||
|
///
|
||||||
|
/// fn process_data(data: &str) -> Result<String, CollectionAllocErr> {
|
||||||
|
/// let mut output = String::new();
|
||||||
|
///
|
||||||
|
/// // Pre-reserve the memory, exiting if we can't
|
||||||
|
/// output.try_reserve(data.len())?;
|
||||||
|
///
|
||||||
|
/// // Now we know this can't OOM in the middle of our complex work
|
||||||
|
/// output.push_str(data);
|
||||||
|
///
|
||||||
|
/// Ok(output)
|
||||||
|
/// }
|
||||||
|
/// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
|
self.vec.try_reserve(additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to reserves the minimum capacity for exactly `additional` more elements to
|
||||||
|
/// be inserted in the given `String`. After calling `reserve_exact`,
|
||||||
|
/// capacity will be greater than or equal to `self.len() + additional`.
|
||||||
|
/// Does nothing if the capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// Note that the allocator may give the collection more space than it
|
||||||
|
/// requests. Therefore capacity can not be relied upon to be precisely
|
||||||
|
/// minimal. Prefer `reserve` if future insertions are expected.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::CollectionAllocErr;
|
||||||
|
///
|
||||||
|
/// fn process_data(data: &str) -> Result<String, CollectionAllocErr> {
|
||||||
|
/// let mut output = String::new();
|
||||||
|
///
|
||||||
|
/// // Pre-reserve the memory, exiting if we can't
|
||||||
|
/// output.try_reserve(data.len())?;
|
||||||
|
///
|
||||||
|
/// // Now we know this can't OOM in the middle of our complex work
|
||||||
|
/// output.push_str(data);
|
||||||
|
///
|
||||||
|
/// Ok(output)
|
||||||
|
/// }
|
||||||
|
/// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
|
self.vec.try_reserve_exact(additional)
|
||||||
|
}
|
||||||
|
|
||||||
/// Shrinks the capacity of this `String` to match its length.
|
/// Shrinks the capacity of this `String` to match its length.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#![feature(splice)]
|
#![feature(splice)]
|
||||||
#![feature(str_escape)]
|
#![feature(str_escape)]
|
||||||
#![feature(string_retain)]
|
#![feature(string_retain)]
|
||||||
|
#![feature(try_reserve)]
|
||||||
#![feature(unboxed_closures)]
|
#![feature(unboxed_closures)]
|
||||||
#![feature(unicode)]
|
#![feature(unicode)]
|
||||||
#![feature(exact_chunks)]
|
#![feature(exact_chunks)]
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::CollectionAllocErr::*;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::{usize, isize};
|
||||||
|
|
||||||
pub trait IntoCow<'a, B: ?Sized> where B: ToOwned {
|
pub trait IntoCow<'a, B: ?Sized> where B: ToOwned {
|
||||||
fn into_cow(self) -> Cow<'a, B>;
|
fn into_cow(self) -> Cow<'a, B>;
|
||||||
@ -504,3 +507,163 @@ fn test_into_boxed_str() {
|
|||||||
let ys = xs.into_boxed_str();
|
let ys = xs.into_boxed_str();
|
||||||
assert_eq!(&*ys, "hello my name is bob");
|
assert_eq!(&*ys, "hello my name is bob");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reserve_exact() {
|
||||||
|
// This is all the same as test_reserve
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert_eq!(s.capacity(), 0);
|
||||||
|
|
||||||
|
s.reserve_exact(2);
|
||||||
|
assert!(s.capacity() >= 2);
|
||||||
|
|
||||||
|
for _i in 0..16 {
|
||||||
|
s.push('0');
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(s.capacity() >= 16);
|
||||||
|
s.reserve_exact(16);
|
||||||
|
assert!(s.capacity() >= 32);
|
||||||
|
|
||||||
|
s.push('0');
|
||||||
|
|
||||||
|
s.reserve_exact(16);
|
||||||
|
assert!(s.capacity() >= 33)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve() {
|
||||||
|
|
||||||
|
// These are the interesting cases:
|
||||||
|
// * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM)
|
||||||
|
// * > isize::MAX should always fail
|
||||||
|
// * On 16/32-bit should CapacityOverflow
|
||||||
|
// * On 64-bit should OOM
|
||||||
|
// * overflow may trigger when adding `len` to `cap` (in number of elements)
|
||||||
|
// * overflow may trigger when multiplying `new_cap` by size_of::<T> (to get bytes)
|
||||||
|
|
||||||
|
const MAX_CAP: usize = isize::MAX as usize;
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
||||||
|
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
||||||
|
// Any platform that succeeds for these requests is technically broken with
|
||||||
|
// ptr::offset because LLVM is the worst.
|
||||||
|
let guards_against_isize = size_of::<usize>() < 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Note: basic stuff is checked by test_reserve
|
||||||
|
let mut empty_string: String = String::new();
|
||||||
|
|
||||||
|
// Check isize::MAX doesn't count as an overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
// Play it again, frank! (just to be sure)
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if guards_against_isize {
|
||||||
|
// Check isize::MAX + 1 does count as overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!") }
|
||||||
|
|
||||||
|
// Check usize::MAX does count as overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
} else {
|
||||||
|
// Check isize::MAX + 1 is an OOM
|
||||||
|
if let Err(AllocErr(_)) = empty_string.try_reserve(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
|
||||||
|
// Check usize::MAX is an OOM
|
||||||
|
if let Err(AllocErr(_)) = empty_string.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Same basic idea, but with non-zero len
|
||||||
|
let mut ten_bytes: String = String::from("0123456789");
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
// Should always overflow in the add-to-len
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve_exact() {
|
||||||
|
|
||||||
|
// This is exactly the same as test_try_reserve with the method changed.
|
||||||
|
// See that test for comments.
|
||||||
|
|
||||||
|
const MAX_CAP: usize = isize::MAX as usize;
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
let guards_against_isize = size_of::<usize>() < 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut empty_string: String = String::new();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!") }
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = empty_string.try_reserve_exact(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
|
||||||
|
if let Err(AllocErr(_)) = empty_string.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ten_bytes: String = String::from("0123456789");
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -10,8 +10,9 @@
|
|||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::panic;
|
use std::{usize, isize, panic};
|
||||||
use std::vec::{Drain, IntoIter};
|
use std::vec::{Drain, IntoIter};
|
||||||
|
use std::collections::CollectionAllocErr::*;
|
||||||
|
|
||||||
struct DropCounter<'a> {
|
struct DropCounter<'a> {
|
||||||
count: &'a mut u32,
|
count: &'a mut u32,
|
||||||
@ -965,3 +966,209 @@ fn drain_filter_complex() {
|
|||||||
assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
|
assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reserve_exact() {
|
||||||
|
// This is all the same as test_reserve
|
||||||
|
|
||||||
|
let mut v = Vec::new();
|
||||||
|
assert_eq!(v.capacity(), 0);
|
||||||
|
|
||||||
|
v.reserve_exact(2);
|
||||||
|
assert!(v.capacity() >= 2);
|
||||||
|
|
||||||
|
for i in 0..16 {
|
||||||
|
v.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(v.capacity() >= 16);
|
||||||
|
v.reserve_exact(16);
|
||||||
|
assert!(v.capacity() >= 32);
|
||||||
|
|
||||||
|
v.push(16);
|
||||||
|
|
||||||
|
v.reserve_exact(16);
|
||||||
|
assert!(v.capacity() >= 33)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve() {
|
||||||
|
|
||||||
|
// These are the interesting cases:
|
||||||
|
// * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM)
|
||||||
|
// * > isize::MAX should always fail
|
||||||
|
// * On 16/32-bit should CapacityOverflow
|
||||||
|
// * On 64-bit should OOM
|
||||||
|
// * overflow may trigger when adding `len` to `cap` (in number of elements)
|
||||||
|
// * overflow may trigger when multiplying `new_cap` by size_of::<T> (to get bytes)
|
||||||
|
|
||||||
|
const MAX_CAP: usize = isize::MAX as usize;
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
||||||
|
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
||||||
|
// Any platform that succeeds for these requests is technically broken with
|
||||||
|
// ptr::offset because LLVM is the worst.
|
||||||
|
let guards_against_isize = size_of::<usize>() < 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Note: basic stuff is checked by test_reserve
|
||||||
|
let mut empty_bytes: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
// Check isize::MAX doesn't count as an overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
// Play it again, frank! (just to be sure)
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if guards_against_isize {
|
||||||
|
// Check isize::MAX + 1 does count as overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!") }
|
||||||
|
|
||||||
|
// Check usize::MAX does count as overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
} else {
|
||||||
|
// Check isize::MAX + 1 is an OOM
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
|
||||||
|
// Check usize::MAX is an OOM
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Same basic idea, but with non-zero len
|
||||||
|
let mut ten_bytes: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
// Should always overflow in the add-to-len
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Same basic idea, but with interesting type size
|
||||||
|
let mut ten_u32s: Vec<u32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_u32s.try_reserve(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
// Should fail in the mul-by-size
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) {
|
||||||
|
} else {
|
||||||
|
panic!("usize::MAX should trigger an overflow!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve_exact() {
|
||||||
|
|
||||||
|
// This is exactly the same as test_try_reserve with the method changed.
|
||||||
|
// See that test for comments.
|
||||||
|
|
||||||
|
const MAX_CAP: usize = isize::MAX as usize;
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
let guards_against_isize = size_of::<usize>() < 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut empty_bytes: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!") }
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ten_bytes: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ten_u32s: Vec<u32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::collections::vec_deque::{Drain};
|
use std::collections::vec_deque::{Drain};
|
||||||
|
use std::collections::CollectionAllocErr::*;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::{usize, isize};
|
||||||
|
|
||||||
use self::Taggy::*;
|
use self::Taggy::*;
|
||||||
use self::Taggypar::*;
|
use self::Taggypar::*;
|
||||||
@ -1022,3 +1025,208 @@ fn test_placement_in() {
|
|||||||
}
|
}
|
||||||
assert_eq!(buf, [5,4,3,1,2,6]);
|
assert_eq!(buf, [5,4,3,1,2,6]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reserve_exact_2() {
|
||||||
|
// This is all the same as test_reserve
|
||||||
|
|
||||||
|
let mut v = VecDeque::new();
|
||||||
|
|
||||||
|
v.reserve_exact(2);
|
||||||
|
assert!(v.capacity() >= 2);
|
||||||
|
|
||||||
|
for i in 0..16 {
|
||||||
|
v.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(v.capacity() >= 16);
|
||||||
|
v.reserve_exact(16);
|
||||||
|
assert!(v.capacity() >= 32);
|
||||||
|
|
||||||
|
v.push_back(16);
|
||||||
|
|
||||||
|
v.reserve_exact(16);
|
||||||
|
assert!(v.capacity() >= 48)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve() {
|
||||||
|
|
||||||
|
// These are the interesting cases:
|
||||||
|
// * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM)
|
||||||
|
// * > isize::MAX should always fail
|
||||||
|
// * On 16/32-bit should CapacityOverflow
|
||||||
|
// * On 64-bit should OOM
|
||||||
|
// * overflow may trigger when adding `len` to `cap` (in number of elements)
|
||||||
|
// * overflow may trigger when multiplying `new_cap` by size_of::<T> (to get bytes)
|
||||||
|
|
||||||
|
const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1;
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
||||||
|
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
||||||
|
// Any platform that succeeds for these requests is technically broken with
|
||||||
|
// ptr::offset because LLVM is the worst.
|
||||||
|
let guards_against_isize = size_of::<usize>() < 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Note: basic stuff is checked by test_reserve
|
||||||
|
let mut empty_bytes: VecDeque<u8> = VecDeque::new();
|
||||||
|
|
||||||
|
// Check isize::MAX doesn't count as an overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
// Play it again, frank! (just to be sure)
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if guards_against_isize {
|
||||||
|
// Check isize::MAX + 1 does count as overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!") }
|
||||||
|
|
||||||
|
// Check usize::MAX does count as overflow
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
} else {
|
||||||
|
// Check isize::MAX is an OOM
|
||||||
|
// VecDeque starts with capacity 7, always adds 1 to the capacity
|
||||||
|
// and also rounds the number to next power of 2 so this is the
|
||||||
|
// furthest we can go without triggering CapacityOverflow
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_CAP) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Same basic idea, but with non-zero len
|
||||||
|
let mut ten_bytes: VecDeque<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
// Should always overflow in the add-to-len
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Same basic idea, but with interesting type size
|
||||||
|
let mut ten_u32s: VecDeque<u32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_u32s.try_reserve(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
// Should fail in the mul-by-size
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) {
|
||||||
|
} else {
|
||||||
|
panic!("usize::MAX should trigger an overflow!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve_exact() {
|
||||||
|
|
||||||
|
// This is exactly the same as test_try_reserve with the method changed.
|
||||||
|
// See that test for comments.
|
||||||
|
|
||||||
|
const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1;
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
let guards_against_isize = size_of::<usize>() < 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut empty_bytes: VecDeque<u8> = VecDeque::new();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!") }
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
} else {
|
||||||
|
// Check isize::MAX is an OOM
|
||||||
|
// VecDeque starts with capacity 7, always adds 1 to the capacity
|
||||||
|
// and also rounds the number to next power of 2 so this is the
|
||||||
|
// furthest we can go without triggering CapacityOverflow
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_CAP) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ten_bytes: VecDeque<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ten_u32s: VecDeque<u32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) {
|
||||||
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||||
|
}
|
||||||
|
if guards_against_isize {
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an overflow!"); }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!") }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -86,6 +86,7 @@ use borrow::Cow;
|
|||||||
use boxed::Box;
|
use boxed::Box;
|
||||||
use raw_vec::RawVec;
|
use raw_vec::RawVec;
|
||||||
use super::range::RangeArgument;
|
use super::range::RangeArgument;
|
||||||
|
use super::allocator::CollectionAllocErr;
|
||||||
use Bound::{Excluded, Included, Unbounded};
|
use Bound::{Excluded, Included, Unbounded};
|
||||||
|
|
||||||
/// A contiguous growable array type, written `Vec<T>` but pronounced 'vector'.
|
/// A contiguous growable array type, written `Vec<T>` but pronounced 'vector'.
|
||||||
@ -489,6 +490,83 @@ impl<T> Vec<T> {
|
|||||||
self.buf.reserve_exact(self.len, additional);
|
self.buf.reserve_exact(self.len, additional);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to reserve capacity for at least `additional` more elements to be inserted
|
||||||
|
/// in the given `Vec<T>`. The collection may reserve more space to avoid
|
||||||
|
/// frequent reallocations. After calling `reserve`, capacity will be
|
||||||
|
/// greater than or equal to `self.len() + additional`. Does nothing if
|
||||||
|
/// capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::CollectionAllocErr;
|
||||||
|
///
|
||||||
|
/// fn process_data(data: &[u32]) -> Result<Vec<u32>, CollectionAllocErr> {
|
||||||
|
/// let mut output = Vec::new();
|
||||||
|
///
|
||||||
|
/// // Pre-reserve the memory, exiting if we can't
|
||||||
|
/// output.try_reserve(data.len())?;
|
||||||
|
///
|
||||||
|
/// // Now we know this can't OOM in the middle of our complex work
|
||||||
|
/// output.extend(data.iter().map(|&val| {
|
||||||
|
/// val * 2 + 5 // very complicated
|
||||||
|
/// }));
|
||||||
|
///
|
||||||
|
/// Ok(output)
|
||||||
|
/// }
|
||||||
|
/// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
|
self.buf.try_reserve(self.len, additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to reserves the minimum capacity for exactly `additional` more elements to
|
||||||
|
/// be inserted in the given `Vec<T>`. After calling `reserve_exact`,
|
||||||
|
/// capacity will be greater than or equal to `self.len() + additional`.
|
||||||
|
/// Does nothing if the capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// Note that the allocator may give the collection more space than it
|
||||||
|
/// requests. Therefore capacity can not be relied upon to be precisely
|
||||||
|
/// minimal. Prefer `reserve` if future insertions are expected.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::CollectionAllocErr;
|
||||||
|
///
|
||||||
|
/// fn process_data(data: &[u32]) -> Result<Vec<u32>, CollectionAllocErr> {
|
||||||
|
/// let mut output = Vec::new();
|
||||||
|
///
|
||||||
|
/// // Pre-reserve the memory, exiting if we can't
|
||||||
|
/// output.try_reserve(data.len())?;
|
||||||
|
///
|
||||||
|
/// // Now we know this can't OOM in the middle of our complex work
|
||||||
|
/// output.extend(data.iter().map(|&val| {
|
||||||
|
/// val * 2 + 5 // very complicated
|
||||||
|
/// }));
|
||||||
|
///
|
||||||
|
/// Ok(output)
|
||||||
|
/// }
|
||||||
|
/// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
|
self.buf.try_reserve_exact(self.len, additional)
|
||||||
|
}
|
||||||
|
|
||||||
/// Shrinks the capacity of the vector as much as possible.
|
/// Shrinks the capacity of the vector as much as possible.
|
||||||
///
|
///
|
||||||
/// It will drop down as close as possible to the length but the allocator
|
/// It will drop down as close as possible to the length but the allocator
|
||||||
|
@ -31,6 +31,7 @@ use core::cmp;
|
|||||||
|
|
||||||
use raw_vec::RawVec;
|
use raw_vec::RawVec;
|
||||||
|
|
||||||
|
use super::allocator::CollectionAllocErr;
|
||||||
use super::range::RangeArgument;
|
use super::range::RangeArgument;
|
||||||
use Bound::{Excluded, Included, Unbounded};
|
use Bound::{Excluded, Included, Unbounded};
|
||||||
use super::vec::Vec;
|
use super::vec::Vec;
|
||||||
@ -566,6 +567,97 @@ impl<T> VecDeque<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to reserves the minimum capacity for exactly `additional` more elements to
|
||||||
|
/// be inserted in the given `VecDeque<T>`. After calling `reserve_exact`,
|
||||||
|
/// capacity will be greater than or equal to `self.len() + additional`.
|
||||||
|
/// Does nothing if the capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// Note that the allocator may give the collection more space than it
|
||||||
|
/// requests. Therefore capacity can not be relied upon to be precisely
|
||||||
|
/// minimal. Prefer `reserve` if future insertions are expected.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::CollectionAllocErr;
|
||||||
|
/// use std::collections::VecDeque;
|
||||||
|
///
|
||||||
|
/// fn process_data(data: &[u32]) -> Result<VecDeque<u32>, CollectionAllocErr> {
|
||||||
|
/// let mut output = VecDeque::new();
|
||||||
|
///
|
||||||
|
/// // Pre-reserve the memory, exiting if we can't
|
||||||
|
/// output.try_reserve_exact(data.len())?;
|
||||||
|
///
|
||||||
|
/// // Now we know this can't OOM in the middle of our complex work
|
||||||
|
/// output.extend(data.iter().map(|&val| {
|
||||||
|
/// val * 2 + 5 // very complicated
|
||||||
|
/// }));
|
||||||
|
///
|
||||||
|
/// Ok(output)
|
||||||
|
/// }
|
||||||
|
/// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
|
self.try_reserve(additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to reserve capacity for at least `additional` more elements to be inserted
|
||||||
|
/// in the given `VecDeque<T>`. The collection may reserve more space to avoid
|
||||||
|
/// frequent reallocations. After calling `reserve`, capacity will be
|
||||||
|
/// greater than or equal to `self.len() + additional`. Does nothing if
|
||||||
|
/// capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::CollectionAllocErr;
|
||||||
|
/// use std::collections::VecDeque;
|
||||||
|
///
|
||||||
|
/// fn process_data(data: &[u32]) -> Result<VecDeque<u32>, CollectionAllocErr> {
|
||||||
|
/// let mut output = VecDeque::new();
|
||||||
|
///
|
||||||
|
/// // Pre-reserve the memory, exiting if we can't
|
||||||
|
/// output.try_reserve(data.len())?;
|
||||||
|
///
|
||||||
|
/// // Now we know this can't OOM in the middle of our complex work
|
||||||
|
/// output.extend(data.iter().map(|&val| {
|
||||||
|
/// val * 2 + 5 // very complicated
|
||||||
|
/// }));
|
||||||
|
///
|
||||||
|
/// Ok(output)
|
||||||
|
/// }
|
||||||
|
/// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
|
let old_cap = self.cap();
|
||||||
|
let used_cap = self.len() + 1;
|
||||||
|
let new_cap = used_cap.checked_add(additional)
|
||||||
|
.and_then(|needed_cap| needed_cap.checked_next_power_of_two())
|
||||||
|
.ok_or(CollectionAllocErr::CapacityOverflow)?;
|
||||||
|
|
||||||
|
if new_cap > old_cap {
|
||||||
|
self.buf.try_reserve_exact(used_cap, new_cap - used_cap)?;
|
||||||
|
unsafe {
|
||||||
|
self.handle_cap_increase(old_cap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Shrinks the capacity of the `VecDeque` as much as possible.
|
/// Shrinks the capacity of the `VecDeque` as much as possible.
|
||||||
///
|
///
|
||||||
/// It will drop down as close as possible to the length but the allocator may still inform the
|
/// It will drop down as close as possible to the length but the allocator may still inform the
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
use self::Entry::*;
|
use self::Entry::*;
|
||||||
use self::VacantEntryState::*;
|
use self::VacantEntryState::*;
|
||||||
|
|
||||||
|
use alloc::heap::{Heap, Alloc};
|
||||||
|
use alloc::allocator::CollectionAllocErr;
|
||||||
use cell::Cell;
|
use cell::Cell;
|
||||||
use borrow::Borrow;
|
use borrow::Borrow;
|
||||||
use cmp::max;
|
use cmp::max;
|
||||||
@ -42,21 +44,28 @@ impl DefaultResizePolicy {
|
|||||||
/// provide that capacity, accounting for maximum loading. The raw capacity
|
/// provide that capacity, accounting for maximum loading. The raw capacity
|
||||||
/// is always zero or a power of two.
|
/// is always zero or a power of two.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn raw_capacity(&self, len: usize) -> usize {
|
fn try_raw_capacity(&self, len: usize) -> Result<usize, CollectionAllocErr> {
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
0
|
Ok(0)
|
||||||
} else {
|
} else {
|
||||||
// 1. Account for loading: `raw_capacity >= len * 1.1`.
|
// 1. Account for loading: `raw_capacity >= len * 1.1`.
|
||||||
// 2. Ensure it is a power of two.
|
// 2. Ensure it is a power of two.
|
||||||
// 3. Ensure it is at least the minimum size.
|
// 3. Ensure it is at least the minimum size.
|
||||||
let mut raw_cap = len * 11 / 10;
|
let mut raw_cap = len.checked_mul(11)
|
||||||
assert!(raw_cap >= len, "raw_cap overflow");
|
.map(|l| l / 10)
|
||||||
raw_cap = raw_cap.checked_next_power_of_two().expect("raw_capacity overflow");
|
.and_then(|l| l.checked_next_power_of_two())
|
||||||
|
.ok_or(CollectionAllocErr::CapacityOverflow)?;
|
||||||
|
|
||||||
raw_cap = max(MIN_NONZERO_RAW_CAPACITY, raw_cap);
|
raw_cap = max(MIN_NONZERO_RAW_CAPACITY, raw_cap);
|
||||||
raw_cap
|
Ok(raw_cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn raw_capacity(&self, len: usize) -> usize {
|
||||||
|
self.try_raw_capacity(len).expect("raw_capacity overflow")
|
||||||
|
}
|
||||||
|
|
||||||
/// The capacity of the given raw capacity.
|
/// The capacity of the given raw capacity.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn capacity(&self, raw_cap: usize) -> usize {
|
fn capacity(&self, raw_cap: usize) -> usize {
|
||||||
@ -775,17 +784,45 @@ impl<K, V, S> HashMap<K, V, S>
|
|||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn reserve(&mut self, additional: usize) {
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
|
match self.try_reserve(additional) {
|
||||||
|
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
|
||||||
|
Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e),
|
||||||
|
Ok(()) => { /* yay */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to reserve capacity for at least `additional` more elements to be inserted
|
||||||
|
/// in the given `HashMap<K,V>`. The collection may reserve more space to avoid
|
||||||
|
/// frequent reallocations.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(try_reserve)]
|
||||||
|
/// use std::collections::HashMap;
|
||||||
|
/// let mut map: HashMap<&str, isize> = HashMap::new();
|
||||||
|
/// map.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?");
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||||
let remaining = self.capacity() - self.len(); // this can't overflow
|
let remaining = self.capacity() - self.len(); // this can't overflow
|
||||||
if remaining < additional {
|
if remaining < additional {
|
||||||
let min_cap = self.len().checked_add(additional).expect("reserve overflow");
|
let min_cap = self.len().checked_add(additional)
|
||||||
let raw_cap = self.resize_policy.raw_capacity(min_cap);
|
.ok_or(CollectionAllocErr::CapacityOverflow)?;
|
||||||
self.resize(raw_cap);
|
let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?;
|
||||||
|
self.try_resize(raw_cap)?;
|
||||||
} else if self.table.tag() && remaining <= self.len() {
|
} else if self.table.tag() && remaining <= self.len() {
|
||||||
// Probe sequence is too long and table is half full,
|
// Probe sequence is too long and table is half full,
|
||||||
// resize early to reduce probing length.
|
// resize early to reduce probing length.
|
||||||
let new_capacity = self.table.capacity() * 2;
|
let new_capacity = self.table.capacity() * 2;
|
||||||
self.resize(new_capacity);
|
self.try_resize(new_capacity)?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resizes the internal vectors to a new capacity. It's your
|
/// Resizes the internal vectors to a new capacity. It's your
|
||||||
@ -795,15 +832,15 @@ impl<K, V, S> HashMap<K, V, S>
|
|||||||
/// 2) Ensure `new_raw_cap` is a power of two or zero.
|
/// 2) Ensure `new_raw_cap` is a power of two or zero.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[cold]
|
#[cold]
|
||||||
fn resize(&mut self, new_raw_cap: usize) {
|
fn try_resize(&mut self, new_raw_cap: usize) -> Result<(), CollectionAllocErr> {
|
||||||
assert!(self.table.size() <= new_raw_cap);
|
assert!(self.table.size() <= new_raw_cap);
|
||||||
assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0);
|
assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0);
|
||||||
|
|
||||||
let mut old_table = replace(&mut self.table, RawTable::new(new_raw_cap));
|
let mut old_table = replace(&mut self.table, RawTable::try_new(new_raw_cap)?);
|
||||||
let old_size = old_table.size();
|
let old_size = old_table.size();
|
||||||
|
|
||||||
if old_table.size() == 0 {
|
if old_table.size() == 0 {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bucket = Bucket::head_bucket(&mut old_table);
|
let mut bucket = Bucket::head_bucket(&mut old_table);
|
||||||
@ -838,6 +875,7 @@ impl<K, V, S> HashMap<K, V, S>
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(self.table.size(), old_size);
|
assert_eq!(self.table.size(), old_size);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shrinks the capacity of the map as much as possible. It will drop
|
/// Shrinks the capacity of the map as much as possible. It will drop
|
||||||
@ -2717,6 +2755,9 @@ mod test_map {
|
|||||||
use cell::RefCell;
|
use cell::RefCell;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use panic;
|
use panic;
|
||||||
|
use realstd::collections::CollectionAllocErr::*;
|
||||||
|
use realstd::mem::size_of;
|
||||||
|
use realstd::usize;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zero_capacities() {
|
fn test_zero_capacities() {
|
||||||
@ -3651,4 +3692,33 @@ mod test_map {
|
|||||||
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { hm.entry(0) <- makepanic(); }));
|
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { hm.entry(0) <- makepanic(); }));
|
||||||
assert_eq!(hm.len(), 0);
|
assert_eq!(hm.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_reserve() {
|
||||||
|
|
||||||
|
let mut empty_bytes: HashMap<u8,u8> = HashMap::new();
|
||||||
|
|
||||||
|
const MAX_USIZE: usize = usize::MAX;
|
||||||
|
|
||||||
|
// HashMap and RawTables use complicated size calculations
|
||||||
|
// hashes_size is sizeof(HashUint) * capacity;
|
||||||
|
// pairs_size is sizeof((K. V)) * capacity;
|
||||||
|
// alignment_hashes_size is 8
|
||||||
|
// alignment_pairs size is 4
|
||||||
|
let size_of_multiplier = (size_of::<usize>() + size_of::<(u8, u8)>()).next_power_of_two();
|
||||||
|
// The following formula is used to calculate the new capacity
|
||||||
|
let max_no_ovf = ((MAX_USIZE / 11) * 10) / size_of_multiplier - 1;
|
||||||
|
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
|
||||||
|
} else { panic!("usize::MAX should trigger an overflow!"); }
|
||||||
|
|
||||||
|
if size_of::<usize>() < 8 {
|
||||||
|
if let Err(CapacityOverflow) = empty_bytes.try_reserve(max_no_ovf) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger a CapacityOverflow!") }
|
||||||
|
} else {
|
||||||
|
if let Err(AllocErr(_)) = empty_bytes.try_reserve(max_no_ovf) {
|
||||||
|
} else { panic!("isize::MAX + 1 should trigger an OOM!") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use mem::{align_of, size_of, needs_drop};
|
|||||||
use mem;
|
use mem;
|
||||||
use ops::{Deref, DerefMut};
|
use ops::{Deref, DerefMut};
|
||||||
use ptr::{self, Unique, NonNull};
|
use ptr::{self, Unique, NonNull};
|
||||||
|
use alloc::allocator::CollectionAllocErr;
|
||||||
|
|
||||||
use self::BucketState::*;
|
use self::BucketState::*;
|
||||||
|
|
||||||
@ -741,14 +742,15 @@ fn test_offset_calculation() {
|
|||||||
impl<K, V> RawTable<K, V> {
|
impl<K, V> RawTable<K, V> {
|
||||||
/// Does not initialize the buckets. The caller should ensure they,
|
/// Does not initialize the buckets. The caller should ensure they,
|
||||||
/// at the very least, set every hash to EMPTY_BUCKET.
|
/// at the very least, set every hash to EMPTY_BUCKET.
|
||||||
unsafe fn new_uninitialized(capacity: usize) -> RawTable<K, V> {
|
/// Returns an error if it cannot allocate or capacity overflows.
|
||||||
|
unsafe fn try_new_uninitialized(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
|
||||||
if capacity == 0 {
|
if capacity == 0 {
|
||||||
return RawTable {
|
return Ok(RawTable {
|
||||||
size: 0,
|
size: 0,
|
||||||
capacity_mask: capacity.wrapping_sub(1),
|
capacity_mask: capacity.wrapping_sub(1),
|
||||||
hashes: TaggedHashUintPtr::new(EMPTY as *mut HashUint),
|
hashes: TaggedHashUintPtr::new(EMPTY as *mut HashUint),
|
||||||
marker: marker::PhantomData,
|
marker: marker::PhantomData,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need for `checked_mul` before a more restrictive check performed
|
// No need for `checked_mul` before a more restrictive check performed
|
||||||
@ -768,25 +770,38 @@ impl<K, V> RawTable<K, V> {
|
|||||||
align_of::<HashUint>(),
|
align_of::<HashUint>(),
|
||||||
pairs_size,
|
pairs_size,
|
||||||
align_of::<(K, V)>());
|
align_of::<(K, V)>());
|
||||||
assert!(!oflo, "capacity overflow");
|
if oflo {
|
||||||
|
return Err(CollectionAllocErr::CapacityOverflow);
|
||||||
|
}
|
||||||
|
|
||||||
// One check for overflow that covers calculation and rounding of size.
|
// One check for overflow that covers calculation and rounding of size.
|
||||||
let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>()).unwrap();
|
let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>())
|
||||||
assert!(size >=
|
.ok_or(CollectionAllocErr::CapacityOverflow)?;
|
||||||
capacity.checked_mul(size_of_bucket)
|
let capacity_mul_size_of_bucket = capacity.checked_mul(size_of_bucket);
|
||||||
.expect("capacity overflow"),
|
if capacity_mul_size_of_bucket.is_none() || size < capacity_mul_size_of_bucket.unwrap() {
|
||||||
"capacity overflow");
|
return Err(CollectionAllocErr::CapacityOverflow);
|
||||||
|
}
|
||||||
|
|
||||||
let buffer = Heap.alloc(Layout::from_size_align(size, alignment).unwrap())
|
let buffer = Heap.alloc(Layout::from_size_align(size, alignment)
|
||||||
.unwrap_or_else(|e| Heap.oom(e));
|
.ok_or(CollectionAllocErr::CapacityOverflow)?)?;
|
||||||
|
|
||||||
let hashes = buffer as *mut HashUint;
|
let hashes = buffer as *mut HashUint;
|
||||||
|
|
||||||
RawTable {
|
Ok(RawTable {
|
||||||
capacity_mask: capacity.wrapping_sub(1),
|
capacity_mask: capacity.wrapping_sub(1),
|
||||||
size: 0,
|
size: 0,
|
||||||
hashes: TaggedHashUintPtr::new(hashes),
|
hashes: TaggedHashUintPtr::new(hashes),
|
||||||
marker: marker::PhantomData,
|
marker: marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does not initialize the buckets. The caller should ensure they,
|
||||||
|
/// at the very least, set every hash to EMPTY_BUCKET.
|
||||||
|
unsafe fn new_uninitialized(capacity: usize) -> RawTable<K, V> {
|
||||||
|
match Self::try_new_uninitialized(capacity) {
|
||||||
|
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
|
||||||
|
Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e),
|
||||||
|
Ok(table) => { table }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,13 +824,23 @@ impl<K, V> RawTable<K, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to create a new raw table from a given capacity. If it cannot allocate,
|
||||||
|
/// it returns with AllocErr.
|
||||||
|
pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
|
||||||
|
unsafe {
|
||||||
|
let ret = RawTable::try_new_uninitialized(capacity)?;
|
||||||
|
ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new raw table from a given capacity. All buckets are
|
/// Creates a new raw table from a given capacity. All buckets are
|
||||||
/// initially empty.
|
/// initially empty.
|
||||||
pub fn new(capacity: usize) -> RawTable<K, V> {
|
pub fn new(capacity: usize) -> RawTable<K, V> {
|
||||||
unsafe {
|
match Self::try_new(capacity) {
|
||||||
let ret = RawTable::new_uninitialized(capacity);
|
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
|
||||||
ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
|
Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e),
|
||||||
ret
|
Ok(table) => { table }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,6 +438,9 @@ pub use self::hash_set::HashSet;
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub use alloc::range;
|
pub use alloc::range;
|
||||||
|
|
||||||
|
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||||
|
pub use alloc::allocator::CollectionAllocErr;
|
||||||
|
|
||||||
mod hash;
|
mod hash;
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
@ -314,6 +314,7 @@
|
|||||||
#![feature(thread_local)]
|
#![feature(thread_local)]
|
||||||
#![feature(toowned_clone_into)]
|
#![feature(toowned_clone_into)]
|
||||||
#![feature(try_from)]
|
#![feature(try_from)]
|
||||||
|
#![feature(try_reserve)]
|
||||||
#![feature(unboxed_closures)]
|
#![feature(unboxed_closures)]
|
||||||
#![feature(unicode)]
|
#![feature(unicode)]
|
||||||
#![feature(untagged_unions)]
|
#![feature(untagged_unions)]
|
||||||
|
14
src/test/ui/feature-gate-try_reserve.rs
Normal file
14
src/test/ui/feature-gate-try_reserve.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v = Vec::new();
|
||||||
|
v.try_reserve(10); //~ ERROR: use of unstable library feature 'try_reserve'
|
||||||
|
}
|
11
src/test/ui/feature-gate-try_reserve.stderr
Normal file
11
src/test/ui/feature-gate-try_reserve.stderr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
error[E0658]: use of unstable library feature 'try_reserve': new API (see issue #48043)
|
||||||
|
--> $DIR/feature-gate-try_reserve.rs:13:7
|
||||||
|
|
|
||||||
|
LL | v.try_reserve(10); //~ ERROR: use of unstable library feature 'try_reserve'
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: add #![feature(try_reserve)] to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
If you want more information on this error, try using "rustc --explain E0658"
|
Loading…
Reference in New Issue
Block a user