Bump bumpalo from 3.10.0 to 3.12.0 in /zeroidc (#1857)

* Bump bumpalo from 3.10.0 to 3.12.0 in /zeroidc

Bumps [bumpalo](https://github.com/fitzgen/bumpalo) from 3.10.0 to 3.12.0.
- [Release notes](https://github.com/fitzgen/bumpalo/releases)
- [Changelog](https://github.com/fitzgen/bumpalo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fitzgen/bumpalo/compare/3.10.0...3.12.0)

---
updated-dependencies:
- dependency-name: bumpalo
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* vendor bumpalo update to fix dependabot

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Grant Limberg <grant.limberg@zerotier.com>
This commit is contained in:
dependabot[bot] 2023-01-30 09:03:01 -08:00 committed by GitHub
parent e0a3291235
commit 6aea546d6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 526 additions and 149 deletions

4
zeroidc/Cargo.lock generated
View File

@ -57,9 +57,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.10.0"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "bytes"

View File

@ -1 +1 @@
{"files":{"CHANGELOG.md":"506ba9e82e7d0354739d7ea9a051fd8e77e0a2842b25063d97d72c84f73b5620","Cargo.toml":"ef3049ea38d9acf8d7a1b3a72fb559548232e5e4562fde190e29530d1caff9f2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"65f94e99ddaf4f5d1782a6dae23f35d4293a9a01444a13135a6887017d353cee","README.md":"1b8b798489668a6053520f90534a795ba73e33928022d10d364dfd8f8df7b5a3","src/alloc.rs":"ab0f23fa11c26efdd8f0596ebdf0e3faa75d097881fb59639b0fb23340c106bc","src/boxed.rs":"8a54f74527691012a1416e7e65ae1dc9f9f4711afd252704a7222b04cce60194","src/collections/collect_in.rs":"0588a4ff3967a4323abb4218bbd615af4b123639ab4fae9130c6590c258b3d15","src/collections/mod.rs":"d58dc46eb4f9fcdde574f09bc5b8646f53e42d49c169561d98e0c23e5b36848a","src/collections/raw_vec.rs":"a4eebed2bd81a039e4f120f1e4230585b8f3bbb42c27f79af28d0c05ee7b6866","src/collections/str/lossy.rs":"c5d62b16e01071e2a574ae41ef6693ad12f1e6c786c5d38f7a13ebd6cb23c088","src/collections/str/mod.rs":"d82a8bd417fbf52a589d89a16ea2a0ac4f6ac920c3976ab1f5b6ac0c8493c4f2","src/collections/string.rs":"7719005ca29d6031c7bf06ebcfd94ea27891da1aadd797ca45aadff598d2b7ee","src/collections/vec.rs":"7a757c495e4688a8db8482105206338e86df0ea2774765382091a36602cf3638","src/lib.rs":"72d2f350246d7365b893f40bc106c6bdd6bb2dbb1fc836d6e04f6d76ca2c282f"},"package":"37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"}
{"files":{"CHANGELOG.md":"8b5a7a49c720ba2678c07184f50b3608e2165fbf6704da494fba23c864e691e0","Cargo.toml":"8d5fd21d2b3ed1d7149e864d43f843fd469ccdcd9893ac3c2bef8518294a61dd","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"65f94e99ddaf4f5d1782a6dae23f35d4293a9a01444a13135a6887017d353cee","README.md":"00c9224790248ec71d1505615429699fd685b0290a0c2b6d7c0df0214e7f80eb","src/alloc.rs":"ab0f23fa11c26efdd8f0596ebdf0e3faa75d097881fb59639b0fb23340c106bc","src/boxed.rs":"5fc935f8e1a7bc1b8f6a39b2bcc4355a2be4743f2308fe3ffd557455a3a27cb2","src/collections/collect_in.rs":"0588a4ff3967a4323abb4218bbd615af4b123639ab4fae9130c6590c258b3d15","src/collections/mod.rs":"d58dc46eb4f9fcdde574f09bc5b8646f53e42d49c169561d98e0c23e5b36848a","src/collections/raw_vec.rs":"8829cc9a693fde38aa93e47a7bbbc2dac247620d07f60519f2e6cb44f5494bc5","src/collections/str/lossy.rs":"c5d62b16e01071e2a574ae41ef6693ad12f1e6c786c5d38f7a13ebd6cb23c088","src/collections/str/mod.rs":"d82a8bd417fbf52a589d89a16ea2a0ac4f6ac920c3976ab1f5b6ac0c8493c4f2","src/collections/string.rs":"388d39b999788baf5c14ccc3f5cb57da728060ea3295ddfc28f0f2e1ca5858ec","src/collections/vec.rs":"2eaf52e085e6d04767e97b224e82688dd0debd231c6536d6034f431376aa8bf0","src/lib.rs":"9eb2bdb8359b368a6f3091a66b3a5eb1216672ec1605cb18d5da28292c381cb9"},"package":"0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"}

View File

@ -28,6 +28,55 @@ Released YYYY-MM-DD.
--------------------------------------------------------------------------------
## 3.12.0
Released 2023-01-17.
### Added
* Added the `bumpalo::boxed::Box::bump` and `bumpalo::collections::String::bump`
getters to get the underlying `Bump` that a string or box was allocated into.
### Changed
* Some uses of `Box` that MIRI did not previously consider as UB are now
reported as UB, and `bumpalo`'s internals have been adjusted to avoid the new
UB.
--------------------------------------------------------------------------------
## 3.11.1
Released 2022-10-18.
### Security
* Fixed a bug where when `std::vec::IntoIter` was ported to
`bumpalo::collections::vec::IntoIter`, it didn't get its underlying `Bump`'s
lifetime threaded through. This meant that `rustc` was not checking the
borrows for `bumpalo::collections::IntoIter` and this could result in
use-after-free bugs.
--------------------------------------------------------------------------------
## 3.11.0
Released 2022-08-17.
### Added
* Added support for per-`Bump` allocation limits. These are enforced only in the
slow path when allocating new chunks in the `Bump`, not in the bump allocation
hot path, and therefore impose near zero overhead.
* Added the `bumpalo::boxed::Box::into_inner` method.
### Changed
* Updated to Rust 2021 edition.
* The minimum supported Rust version (MSRV) is now 1.56.0.
--------------------------------------------------------------------------------
## 3.10.0
Released 2022-06-01.

View File

@ -10,9 +10,9 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
edition = "2021"
name = "bumpalo"
version = "3.10.0"
version = "3.12.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
exclude = [
"/.github/*",
@ -51,13 +51,13 @@ harness = false
required-features = ["collections"]
[dev-dependencies.criterion]
version = "0.3.0"
version = "0.3.6"
[dev-dependencies.quickcheck]
version = "0.9.0"
version = "1.0.3"
[dev-dependencies.rand]
version = "0.7"
version = "0.8.5"
[features]
allocator_api = []

View File

@ -147,7 +147,8 @@ in its space itself.
// Drop our `Box<CountDrops>`.
drop(c);
// Its `Drop` implementation was run, and so `NUM_DROPS` has been incremented.
// Its `Drop` implementation was run, and so `NUM_DROPS` has been
// incremented.
assert_eq!(NUM_DROPPED.load(Ordering::SeqCst), 1);
}
```
@ -158,11 +159,11 @@ Bumpalo is a `no_std` crate. It depends only on the `alloc` and `core` crates.
### Thread support
The `Bump` is `!Sync`, which makes it hard to use in certain situations around threads for
example in `rayon`.
The `Bump` is `!Sync`, which makes it hard to use in certain situations around
threads for example in `rayon`.
The [`bumpalo-herd`](https://crates.io/crates/bumpalo-herd) crate provides a pool of `Bump`
allocators for use in such situations.
The [`bumpalo-herd`](https://crates.io/crates/bumpalo-herd) crate provides a
pool of `Bump` allocators for use in such situations.
### Nightly Rust `allocator_api` Support
@ -181,7 +182,8 @@ First, enable the `allocator_api` feature in your `Cargo.toml`:
bumpalo = { version = "3.9", features = ["allocator_api"] }
```
Next, enable the `allocator_api` nightly Rust feature in your `src/lib.rs` or `src/main.rs`:
Next, enable the `allocator_api` nightly Rust feature in your `src/lib.rs` or
`src/main.rs`:
```rust,ignore
#![feature(allocator_api)]
@ -207,8 +209,8 @@ v.push(2);
#### Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust **1.54** and up. It might
This crate is guaranteed to compile on stable Rust **1.56** and up. It might
compile with older versions but that may change in any new patch release.
We reserve the right to increment the MSRV on minor releases, however we will strive
to only do it deliberately and for good reasons.
We reserve the right to increment the MSRV on minor releases, however we will
strive to only do it deliberately and for good reasons.

View File

@ -130,7 +130,7 @@ use {
future::Future,
hash::{Hash, Hasher},
iter::FusedIterator,
mem,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
pin::Pin,
task::{Context, Poll},
@ -171,6 +171,24 @@ impl<'a, T> Box<'a, T> {
pub fn pin_in(x: T, a: &'a Bump) -> Pin<Box<'a, T>> {
Box(a.alloc(x)).into()
}
/// Consumes the `Box`, returning the wrapped value.
///
/// # Examples
///
/// ```
/// use bumpalo::{Bump, boxed::Box};
///
/// let b = Bump::new();
///
/// let hello = Box::new_in("hello".to_owned(), &b);
/// assert_eq!(Box::into_inner(hello), "hello");
/// ```
pub fn into_inner(b: Box<'a, T>) -> T {
// `Box::into_raw` returns a pointer that is properly aligned and non-null.
// The underlying `Bump` only frees the memory, but won't call the destructor.
unsafe { core::ptr::read(Box::into_raw(b)) }
}
}
impl<'a, T: ?Sized> Box<'a, T> {
@ -262,9 +280,8 @@ impl<'a, T: ?Sized> Box<'a, T> {
/// ```
#[inline]
pub fn into_raw(b: Box<'a, T>) -> *mut T {
let ptr = b.0 as *mut T;
mem::forget(b);
ptr
let mut b = ManuallyDrop::new(b);
b.deref_mut().0 as *mut T
}
/// Consumes and leaks the `Box`, returning a mutable reference,
@ -644,9 +661,9 @@ impl<'a, F: ?Sized + Future + Unpin> Future for Box<'a, F> {
/// This impl replaces unsize coercion.
impl<'a, T, const N: usize> From<Box<'a, [T; N]>> for Box<'a, [T]> {
fn from(mut arr: Box<'a, [T; N]>) -> Box<'a, [T]> {
fn from(arr: Box<'a, [T; N]>) -> Box<'a, [T]> {
let mut arr = ManuallyDrop::new(arr);
let ptr = core::ptr::slice_from_raw_parts_mut(arr.as_mut_ptr(), N);
mem::forget(arr);
unsafe { Box::from_raw(ptr) }
}
}
@ -654,10 +671,10 @@ impl<'a, T, const N: usize> From<Box<'a, [T; N]>> for Box<'a, [T]> {
/// This impl replaces unsize coercion.
impl<'a, T, const N: usize> TryFrom<Box<'a, [T]>> for Box<'a, [T; N]> {
type Error = Box<'a, [T]>;
fn try_from(mut slice: Box<'a, [T]>) -> Result<Box<'a, [T; N]>, Box<'a, [T]>> {
fn try_from(slice: Box<'a, [T]>) -> Result<Box<'a, [T; N]>, Box<'a, [T]>> {
if slice.len() == N {
let mut slice = ManuallyDrop::new(slice);
let ptr = slice.as_mut_ptr() as *mut [T; N];
mem::forget(slice);
Ok(unsafe { Box::from_raw(ptr) })
} else {
Err(slice)

View File

@ -60,15 +60,10 @@ impl<'a, T> RawVec<'a, T> {
/// Like `new` but parameterized over the choice of allocator for
/// the returned RawVec.
pub fn new_in(a: &'a Bump) -> Self {
// !0 is usize::MAX. This branch should be stripped at compile time.
// FIXME(mark-i-m): use this line when `if`s are allowed in `const`
//let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 };
// Unique::empty() doubles as "unallocated" and "zero-sized allocation"
// `cap: 0` means "unallocated". zero-sized types are ignored.
RawVec {
ptr: unsafe { NonNull::new_unchecked(mem::align_of::<T>() as *mut T) },
// FIXME(mark-i-m): use `cap` when ifs are allowed in const
cap: [0, !0][(mem::size_of::<T>() == 0) as usize],
ptr: NonNull::dangling(),
cap: 0,
a,
}
}

View File

@ -228,9 +228,9 @@ macro_rules! format {
///
/// let b = Bump::new();
///
/// let story = String::from_str_in("Once upon a time...", &b);
/// let mut story = String::from_str_in("Once upon a time...", &b);
///
/// let ptr = story.as_ptr();
/// let ptr = story.as_mut_ptr();
/// let len = story.len();
/// let capacity = story.capacity();
///
@ -243,7 +243,7 @@ macro_rules! format {
/// // We can re-build a String out of ptr, len, and capacity. This is all
/// // unsafe because we are responsible for making sure the components are
/// // valid:
/// let s = unsafe { String::from_raw_parts_in(ptr as *mut _, len, capacity, &b) } ;
/// let s = unsafe { String::from_raw_parts_in(ptr, len, capacity, &b) } ;
///
/// assert_eq!(String::from_str_in("Once upon a time...", &b), s);
/// ```
@ -737,14 +737,14 @@ impl<'bump> String<'bump> {
/// let b = Bump::new();
///
/// unsafe {
/// let s = String::from_str_in("hello", &b);
/// let ptr = s.as_ptr();
/// let mut s = String::from_str_in("hello", &b);
/// let ptr = s.as_mut_ptr();
/// let len = s.len();
/// let capacity = s.capacity();
///
/// mem::forget(s);
///
/// let s = String::from_raw_parts_in(ptr as *mut _, len, capacity, &b);
/// let s = String::from_raw_parts_in(ptr, len, capacity, &b);
///
/// assert_eq!(s, "hello");
/// }
@ -798,6 +798,24 @@ impl<'bump> String<'bump> {
String { vec: bytes }
}
/// Returns a shared reference to the allocator backing this `String`.
///
/// # Examples
///
/// ```
/// use bumpalo::{Bump, collections::String};
///
/// // uses the same allocator as the provided `String`
/// fn copy_string<'bump>(s: &String<'bump>) -> &'bump str {
/// s.bump().alloc_str(s.as_str())
/// }
/// ```
#[inline]
#[must_use]
pub fn bump(&self) -> &'bump Bump {
self.vec.bump()
}
/// Converts a `String` into a byte vector.
///
/// This consumes the `String`, so we do not need to copy its contents.
@ -1550,7 +1568,7 @@ impl<'bump> String<'bump> {
/// assert_eq!(s, "β is beta");
///
/// // A full range clears the string
/// s.drain(..);
/// drop(s.drain(..));
/// assert_eq!(s, "");
/// ```
pub fn drain<'a, R>(&'a mut self, range: R) -> Drain<'a, 'bump>
@ -2098,6 +2116,8 @@ impl<'a, 'bump> Drop for Drain<'a, 'bump> {
}
}
// TODO: implement `AsRef<str/[u8]>` and `as_str`
impl<'a, 'bump> Iterator for Drain<'a, 'bump> {
type Item = char;

View File

@ -675,6 +675,26 @@ impl<'bump, T: 'bump> Vec<'bump, T> {
}
}
/// Returns a shared reference to the allocator backing this `Vec`.
///
/// # Examples
///
/// ```
/// use bumpalo::{Bump, collections::Vec};
///
/// // uses the same allocator as the provided `Vec`
/// fn add_strings<'bump>(vec: &mut Vec<'bump, &'bump str>) {
/// for string in ["foo", "bar", "baz"] {
/// vec.push(vec.bump().alloc_str(string));
/// }
/// }
/// ```
#[inline]
#[must_use]
pub fn bump(&self) -> &'bump Bump {
self.buf.bump()
}
/// Returns the number of elements the vector can hold without
/// reallocating.
///
@ -977,6 +997,91 @@ impl<'bump, T: 'bump> Vec<'bump, T> {
self
}
/// Returns a raw pointer to the vector's buffer, or a dangling raw pointer
/// valid for zero sized reads if the vector didn't allocate.
///
/// The caller must ensure that the vector outlives the pointer this
/// function returns, or else it will end up pointing to garbage.
/// Modifying the vector may cause its buffer to be reallocated,
/// which would also make any pointers to it invalid.
///
/// The caller must also ensure that the memory the pointer (non-transitively) points to
/// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer
/// derived from it. If you need to mutate the contents of the slice, use [`as_mut_ptr`].
///
/// # Examples
///
/// ```
/// use bumpalo::{Bump, collections::Vec};
///
/// let bump = Bump::new();
///
/// let x = bumpalo::vec![in &bump; 1, 2, 4];
/// let x_ptr = x.as_ptr();
///
/// unsafe {
/// for i in 0..x.len() {
/// assert_eq!(*x_ptr.add(i), 1 << i);
/// }
/// }
/// ```
///
/// [`as_mut_ptr`]: Vec::as_mut_ptr
#[inline]
pub fn as_ptr(&self) -> *const T {
// We shadow the slice method of the same name to avoid going through
// `deref`, which creates an intermediate reference.
let ptr = self.buf.ptr();
unsafe {
if ptr.is_null() {
core::hint::unreachable_unchecked();
}
}
ptr
}
/// Returns an unsafe mutable pointer to the vector's buffer, or a dangling
/// raw pointer valid for zero sized reads if the vector didn't allocate.
///
/// The caller must ensure that the vector outlives the pointer this
/// function returns, or else it will end up pointing to garbage.
/// Modifying the vector may cause its buffer to be reallocated,
/// which would also make any pointers to it invalid.
///
/// # Examples
///
/// ```
/// use bumpalo::{Bump, collections::Vec};
///
/// let bump = Bump::new();
///
/// // Allocate vector big enough for 4 elements.
/// let size = 4;
/// let mut x: Vec<i32> = Vec::with_capacity_in(size, &bump);
/// let x_ptr = x.as_mut_ptr();
///
/// // Initialize elements via raw pointer writes, then set length.
/// unsafe {
/// for i in 0..size {
/// x_ptr.add(i).write(i as i32);
/// }
/// x.set_len(size);
/// }
/// assert_eq!(&*x, &[0, 1, 2, 3]);
/// ```
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
// We shadow the slice method of the same name to avoid going through
// `deref_mut`, which creates an intermediate reference.
let ptr = self.buf.ptr();
unsafe {
if ptr.is_null() {
core::hint::unreachable_unchecked();
}
}
ptr
}
/// Sets the length of a vector.
///
/// This will explicitly set the size of the vector, without actually
@ -1026,19 +1131,27 @@ impl<'bump, T: 'bump> Vec<'bump, T> {
/// ```
///
/// In this example, the vector gets expanded from zero to four items
/// without any memory allocations occurring, resulting in vector
/// values of unallocated memory:
/// but we directly initialize uninitialized memory:
///
// TODO: rely upon `spare_capacity_mut`
/// ```
/// use bumpalo::{Bump, collections::Vec};
///
/// let len = 4;
/// let b = Bump::new();
///
/// let mut vec: Vec<char> = Vec::new_in(&b);
/// let mut vec: Vec<u8> = Vec::with_capacity_in(len, &b);
///
/// for i in 0..len {
/// // SAFETY: we initialize memory via `pointer::write`
/// unsafe { vec.as_mut_ptr().add(i).write(b'a') }
/// }
///
/// unsafe {
/// vec.set_len(4);
/// vec.set_len(len);
/// }
///
/// assert_eq!(b"aaaa", &*vec);
/// ```
#[inline]
pub unsafe fn set_len(&mut self, new_len: usize) {
@ -1343,7 +1456,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> {
} else {
unsafe {
self.len -= 1;
Some(ptr::read(self.get_unchecked(self.len())))
Some(ptr::read(self.as_ptr().add(self.len())))
}
}
}
@ -1381,7 +1494,7 @@ impl<'bump, T: 'bump> Vec<'bump, T> {
let count = (*other).len();
self.reserve(count);
let len = self.len();
ptr::copy_nonoverlapping(other as *const T, self.get_unchecked_mut(len), count);
ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count);
self.len += count;
}
@ -1848,7 +1961,7 @@ impl<'bump, T: 'bump> ops::DerefMut for Vec<'bump, T> {
impl<'bump, T: 'bump> IntoIterator for Vec<'bump, T> {
type Item = T;
type IntoIter = IntoIter<T>;
type IntoIter = IntoIter<'bump, T>;
/// Creates a consuming iterator, that is, one that moves each value out of
/// the vector (from start to end). The vector cannot be used after calling
@ -1868,7 +1981,7 @@ impl<'bump, T: 'bump> IntoIterator for Vec<'bump, T> {
/// }
/// ```
#[inline]
fn into_iter(mut self) -> IntoIter<T> {
fn into_iter(mut self) -> IntoIter<'bump, T> {
unsafe {
let begin = self.as_mut_ptr();
// assume(!begin.is_null());
@ -2129,19 +2242,19 @@ impl<'bump, T> Drop for Vec<'bump, T> {
/// (provided by the [`IntoIterator`] trait).
///
/// [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
pub struct IntoIter<T> {
phantom: PhantomData<T>,
pub struct IntoIter<'bump, T> {
phantom: PhantomData<&'bump [T]>,
ptr: *const T,
end: *const T,
}
impl<T: fmt::Debug> fmt::Debug for IntoIter<T> {
impl<'bump, T: fmt::Debug> fmt::Debug for IntoIter<'bump, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("IntoIter").field(&self.as_slice()).finish()
}
}
impl<'bump, T: 'bump> IntoIter<T> {
impl<'bump, T: 'bump> IntoIter<'bump, T> {
/// Returns the remaining items of this iterator as a slice.
///
/// # Examples
@ -2183,10 +2296,10 @@ impl<'bump, T: 'bump> IntoIter<T> {
}
}
unsafe impl<T: Send> Send for IntoIter<T> {}
unsafe impl<T: Sync> Sync for IntoIter<T> {}
unsafe impl<'bump, T: Send> Send for IntoIter<'bump, T> {}
unsafe impl<'bump, T: Sync> Sync for IntoIter<'bump, T> {}
impl<'bump, T: 'bump> Iterator for IntoIter<T> {
impl<'bump, T: 'bump> Iterator for IntoIter<'bump, T> {
type Item = T;
#[inline]
@ -2227,7 +2340,7 @@ impl<'bump, T: 'bump> Iterator for IntoIter<T> {
}
}
impl<'bump, T: 'bump> DoubleEndedIterator for IntoIter<T> {
impl<'bump, T: 'bump> DoubleEndedIterator for IntoIter<'bump, T> {
#[inline]
fn next_back(&mut self) -> Option<T> {
unsafe {
@ -2248,9 +2361,16 @@ impl<'bump, T: 'bump> DoubleEndedIterator for IntoIter<T> {
}
}
impl<'bump, T: 'bump> ExactSizeIterator for IntoIter<T> {}
impl<'bump, T: 'bump> ExactSizeIterator for IntoIter<'bump, T> {}
impl<'bump, T: 'bump> FusedIterator for IntoIter<T> {}
impl<'bump, T: 'bump> FusedIterator for IntoIter<'bump, T> {}
impl<'bump, T> Drop for IntoIter<'bump, T> {
fn drop(&mut self) {
// drop all remaining elements
self.for_each(drop);
}
}
/// A draining iterator for `Vec<'bump, T>`.
///

View File

@ -252,10 +252,46 @@ impl<E: Display> Display for AllocOrInitError<E> {
/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
/// [`Ok`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok
/// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
///
/// ### `Bump` Allocation Limits
///
/// `bumpalo` supports setting a limit on the maximum bytes of memory that can
/// be allocated for use in a particular `Bump` arena. This limit can be set and removed with
/// [`set_allocation_limit`][Bump::set_allocation_limit].
/// The allocation limit is only enforced when allocating new backing chunks for
/// a `Bump`. Updating the allocation limit will not affect existing allocations
/// or any future allocations within the `Bump`'s current chunk.
///
/// #### Example
///
/// ```
/// let bump = bumpalo::Bump::new();
///
/// assert_eq!(bump.allocation_limit(), None);
/// bump.set_allocation_limit(Some(0));
///
/// assert!(bump.try_alloc(5).is_err());
///
/// bump.set_allocation_limit(Some(6));
///
/// assert_eq!(bump.allocation_limit(), Some(6));
///
/// bump.set_allocation_limit(None);
///
/// assert_eq!(bump.allocation_limit(), None);
/// ```
///
/// #### Warning
///
/// Because of backwards compatibility, allocations that fail
/// due to allocation limits will not present differently than
/// errors due to resource exhaustion.
#[derive(Debug)]
pub struct Bump {
// The current chunk we are bump allocating within.
current_chunk_footer: Cell<NonNull<ChunkFooter>>,
allocation_limit: Cell<Option<usize>>,
}
#[repr(C)]
@ -276,6 +312,12 @@ struct ChunkFooter {
// Bump allocation finger that is always in the range `self.data..=self`.
ptr: Cell<NonNull<u8>>,
// The bytes allocated in all chunks so far, the canonical empty chunk has
// a size of 0 and for all other chunks, `allocated_bytes` will be
// the allocated_bytes of the current chunk plus the allocated bytes
// of the `prev` chunk.
allocated_bytes: usize,
}
/// A wrapper type for the canonical, statically allocated empty chunk.
@ -305,6 +347,9 @@ static EMPTY_CHUNK: EmptyChunkFooter = EmptyChunkFooter(ChunkFooter {
prev: Cell::new(unsafe {
NonNull::new_unchecked(&EMPTY_CHUNK as *const EmptyChunkFooter as *mut ChunkFooter)
}),
// Empty chunks count as 0 allocated bytes in an arena.
allocated_bytes: 0,
});
impl EmptyChunkFooter {
@ -407,6 +452,15 @@ const FIRST_ALLOCATION_GOAL: usize = 1 << 9;
// take the alignment into account.
const DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER: usize = FIRST_ALLOCATION_GOAL - OVERHEAD;
/// The memory size and alignment details for a potential new chunk
/// allocation.
#[derive(Debug, Clone, Copy)]
struct NewChunkMemoryDetails {
new_size_without_footer: usize,
align: usize,
size: usize,
}
/// Wrapper around `Layout::from_size_align` that adds debug assertions.
#[inline]
unsafe fn layout_from_size_align(size: usize, align: usize) -> Layout {
@ -422,6 +476,12 @@ fn allocation_size_overflow<T>() -> T {
panic!("requested allocation size overflowed")
}
// This can be migrated to directly use `usize::abs_diff` when the MSRV
// reaches `1.60`
fn abs_diff(a: usize, b: usize) -> usize {
usize::max(a, b) - usize::min(a, b)
}
impl Bump {
/// Construct a new arena to bump allocate into.
///
@ -471,18 +531,138 @@ impl Bump {
if capacity == 0 {
return Ok(Bump {
current_chunk_footer: Cell::new(EMPTY_CHUNK.get()),
allocation_limit: Cell::new(None),
});
}
let chunk_footer = Self::new_chunk(
None,
unsafe { layout_from_size_align(capacity, 1) },
EMPTY_CHUNK.get(),
)
.ok_or(AllocErr)?;
let layout = unsafe { layout_from_size_align(capacity, 1) };
let chunk_footer = unsafe {
Self::new_chunk(
Bump::new_chunk_memory_details(None, layout).ok_or(AllocErr)?,
layout,
EMPTY_CHUNK.get(),
)
.ok_or(AllocErr)?
};
Ok(Bump {
current_chunk_footer: Cell::new(chunk_footer),
allocation_limit: Cell::new(None),
})
}
/// The allocation limit for this arena in bytes.
///
/// ## Example
///
/// ```
/// let bump = bumpalo::Bump::with_capacity(0);
///
/// assert_eq!(bump.allocation_limit(), None);
///
/// bump.set_allocation_limit(Some(6));
///
/// assert_eq!(bump.allocation_limit(), Some(6));
///
/// bump.set_allocation_limit(None);
///
/// assert_eq!(bump.allocation_limit(), None);
/// ```
pub fn allocation_limit(&self) -> Option<usize> {
self.allocation_limit.get()
}
/// Set the allocation limit in bytes for this arena.
///
/// The allocation limit is only enforced when allocating new backing chunks for
/// a `Bump`. Updating the allocation limit will not affect existing allocations
/// or any future allocations within the `Bump`'s current chunk.
///
/// ## Example
///
/// ```
/// let bump = bumpalo::Bump::with_capacity(0);
///
/// bump.set_allocation_limit(Some(0));
///
/// assert!(bump.try_alloc(5).is_err());
/// ```
pub fn set_allocation_limit(&self, limit: Option<usize>) {
self.allocation_limit.set(limit)
}
/// How much headroom an arena has before it hits its allocation
/// limit.
fn allocation_limit_remaining(&self) -> Option<usize> {
self.allocation_limit.get().and_then(|allocation_limit| {
let allocated_bytes = self.allocated_bytes();
if allocated_bytes > allocation_limit {
None
} else {
Some(abs_diff(allocation_limit, allocated_bytes))
}
})
}
/// Whether a request to allocate a new chunk with a given size for a given
/// requested layout will fit under the allocation limit set on a `Bump`.
fn chunk_fits_under_limit(
allocation_limit_remaining: Option<usize>,
new_chunk_memory_details: NewChunkMemoryDetails,
) -> bool {
allocation_limit_remaining
.map(|allocation_limit_left| {
allocation_limit_left >= new_chunk_memory_details.new_size_without_footer
})
.unwrap_or(true)
}
/// Determine the memory details including final size, alignment and
/// final size without footer for a new chunk that would be allocated
/// to fulfill an allocation request.
fn new_chunk_memory_details(
new_size_without_footer: Option<usize>,
requested_layout: Layout,
) -> Option<NewChunkMemoryDetails> {
let mut new_size_without_footer =
new_size_without_footer.unwrap_or(DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER);
// We want to have CHUNK_ALIGN or better alignment
let mut align = CHUNK_ALIGN;
// If we already know we need to fulfill some request,
// make sure we allocate at least enough to satisfy it
align = align.max(requested_layout.align());
let requested_size =
round_up_to(requested_layout.size(), align).unwrap_or_else(allocation_size_overflow);
new_size_without_footer = new_size_without_footer.max(requested_size);
// We want our allocations to play nice with the memory allocator,
// and waste as little memory as possible.
// For small allocations, this means that the entire allocation
// including the chunk footer and mallocs internal overhead is
// as close to a power of two as we can go without going over.
// For larger allocations, we only need to get close to a page
// boundary without going over.
if new_size_without_footer < PAGE_STRATEGY_CUTOFF {
new_size_without_footer =
(new_size_without_footer + OVERHEAD).next_power_of_two() - OVERHEAD;
} else {
new_size_without_footer =
round_up_to(new_size_without_footer + OVERHEAD, 0x1000)? - OVERHEAD;
}
debug_assert_eq!(align % CHUNK_ALIGN, 0);
debug_assert_eq!(new_size_without_footer % CHUNK_ALIGN, 0);
let size = new_size_without_footer
.checked_add(FOOTER_SIZE)
.unwrap_or_else(allocation_size_overflow);
Some(NewChunkMemoryDetails {
new_size_without_footer,
size,
align,
})
}
@ -491,74 +671,50 @@ impl Bump {
/// If given, `layouts` is a tuple of the current chunk size and the
/// layout of the allocation request that triggered us to fall back to
/// allocating a new chunk of memory.
fn new_chunk(
new_size_without_footer: Option<usize>,
unsafe fn new_chunk(
new_chunk_memory_details: NewChunkMemoryDetails,
requested_layout: Layout,
prev: NonNull<ChunkFooter>,
) -> Option<NonNull<ChunkFooter>> {
unsafe {
let mut new_size_without_footer =
new_size_without_footer.unwrap_or(DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER);
let NewChunkMemoryDetails {
new_size_without_footer,
align,
size,
} = new_chunk_memory_details;
// We want to have CHUNK_ALIGN or better alignment
let mut align = CHUNK_ALIGN;
let layout = layout_from_size_align(size, align);
// If we already know we need to fulfill some request,
// make sure we allocate at least enough to satisfy it
align = align.max(requested_layout.align());
let requested_size = round_up_to(requested_layout.size(), align)
.unwrap_or_else(allocation_size_overflow);
new_size_without_footer = new_size_without_footer.max(requested_size);
debug_assert!(size >= requested_layout.size());
// We want our allocations to play nice with the memory allocator,
// and waste as little memory as possible.
// For small allocations, this means that the entire allocation
// including the chunk footer and mallocs internal overhead is
// as close to a power of two as we can go without going over.
// For larger allocations, we only need to get close to a page
// boundary without going over.
if new_size_without_footer < PAGE_STRATEGY_CUTOFF {
new_size_without_footer =
(new_size_without_footer + OVERHEAD).next_power_of_two() - OVERHEAD;
} else {
new_size_without_footer =
round_up_to(new_size_without_footer + OVERHEAD, 0x1000)? - OVERHEAD;
}
let data = alloc(layout);
let data = NonNull::new(data)?;
debug_assert_eq!(align % CHUNK_ALIGN, 0);
debug_assert_eq!(new_size_without_footer % CHUNK_ALIGN, 0);
let size = new_size_without_footer
.checked_add(FOOTER_SIZE)
.unwrap_or_else(allocation_size_overflow);
let layout = layout_from_size_align(size, align);
// The `ChunkFooter` is at the end of the chunk.
let footer_ptr = data.as_ptr().add(new_size_without_footer);
debug_assert_eq!((data.as_ptr() as usize) % align, 0);
debug_assert_eq!(footer_ptr as usize % CHUNK_ALIGN, 0);
let footer_ptr = footer_ptr as *mut ChunkFooter;
debug_assert!(size >= requested_layout.size());
// The bump pointer is initialized to the end of the range we will
// bump out of.
let ptr = Cell::new(NonNull::new_unchecked(footer_ptr as *mut u8));
let data = alloc(layout);
let data = NonNull::new(data)?;
// The `allocated_bytes` of a new chunk counts the total size
// of the chunks, not how much of the chunks are used.
let allocated_bytes = prev.as_ref().allocated_bytes + new_size_without_footer;
// The `ChunkFooter` is at the end of the chunk.
let footer_ptr = data.as_ptr().add(new_size_without_footer);
debug_assert_eq!((data.as_ptr() as usize) % align, 0);
debug_assert_eq!(footer_ptr as usize % CHUNK_ALIGN, 0);
let footer_ptr = footer_ptr as *mut ChunkFooter;
ptr::write(
footer_ptr,
ChunkFooter {
data,
layout,
prev: Cell::new(prev),
ptr,
allocated_bytes,
},
);
// The bump pointer is initialized to the end of the range we will
// bump out of.
let ptr = Cell::new(NonNull::new_unchecked(footer_ptr as *mut u8));
ptr::write(
footer_ptr,
ChunkFooter {
data,
layout,
prev: Cell::new(prev),
ptr,
},
);
Some(NonNull::new_unchecked(footer_ptr))
}
Some(NonNull::new_unchecked(footer_ptr))
}
/// Reset this bump allocator.
@ -600,7 +756,7 @@ impl Bump {
return;
}
let cur_chunk = self.current_chunk_footer.get();
let mut cur_chunk = self.current_chunk_footer.get();
// Deallocate all chunks except the current one
let prev_chunk = cur_chunk.as_ref().prev.replace(EMPTY_CHUNK.get());
@ -609,6 +765,9 @@ impl Bump {
// Reset the bump finger to the end of the chunk.
cur_chunk.as_ref().ptr.set(cur_chunk.cast());
// Reset the allocated size of the chunk.
cur_chunk.as_mut().allocated_bytes = cur_chunk.as_ref().layout.size();
debug_assert!(
self.current_chunk_footer
.get()
@ -820,7 +979,6 @@ impl Bump {
let rewind_footer = self.current_chunk_footer.get();
let rewind_ptr = unsafe { rewind_footer.as_ref() }.ptr.get();
let mut inner_result_ptr = NonNull::from(self.alloc_with(f));
let inner_result_address = inner_result_ptr.as_ptr() as usize;
match unsafe { inner_result_ptr.as_mut() } {
Ok(t) => Ok(unsafe {
//SAFETY:
@ -842,7 +1000,7 @@ impl Bump {
// reclaim any alignment padding we might have added (which
// `dealloc` cannot do) if we didn't allocate a new chunk for
// this result.
if self.is_last_allocation(NonNull::new_unchecked(inner_result_address as *mut _)) {
if self.is_last_allocation(inner_result_ptr.cast()) {
let current_footer_p = self.current_chunk_footer.get();
let current_ptr = &current_footer_p.as_ref().ptr;
if current_footer_p == rewind_footer {
@ -930,7 +1088,6 @@ impl Bump {
let rewind_footer = self.current_chunk_footer.get();
let rewind_ptr = unsafe { rewind_footer.as_ref() }.ptr.get();
let mut inner_result_ptr = NonNull::from(self.try_alloc_with(f)?);
let inner_result_address = inner_result_ptr.as_ptr() as usize;
match unsafe { inner_result_ptr.as_mut() } {
Ok(t) => Ok(unsafe {
//SAFETY:
@ -952,7 +1109,7 @@ impl Bump {
// reclaim any alignment padding we might have added (which
// `dealloc` cannot do) if we didn't allocate a new chunk for
// this result.
if self.is_last_allocation(NonNull::new_unchecked(inner_result_address as *mut _)) {
if self.is_last_allocation(inner_result_ptr.cast()) {
let current_footer_p = self.current_chunk_footer.get();
let current_ptr = &current_footer_p.as_ref().ptr;
if current_footer_p == rewind_footer {
@ -1316,6 +1473,7 @@ impl Bump {
fn alloc_layout_slow(&self, layout: Layout) -> Option<NonNull<u8>> {
unsafe {
let size = layout.size();
let allocation_limit_remaining = self.allocation_limit_remaining();
// Get a new chunk from the global allocator.
let current_footer = self.current_chunk_footer.get();
@ -1329,18 +1487,39 @@ impl Bump {
let mut base_size = (current_layout.size() - FOOTER_SIZE)
.checked_mul(2)?
.max(min_new_chunk_size);
let sizes = iter::from_fn(|| {
if base_size >= min_new_chunk_size {
let chunk_memory_details = iter::from_fn(|| {
let bypass_min_chunk_size_for_small_limits = match self.allocation_limit() {
Some(limit)
if layout.size() < limit
&& base_size >= layout.size()
&& limit < DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER
&& self.allocated_bytes() == 0 =>
{
true
}
_ => false,
};
if base_size >= min_new_chunk_size || bypass_min_chunk_size_for_small_limits {
let size = base_size;
base_size = base_size / 2;
Some(size)
Bump::new_chunk_memory_details(Some(size), layout)
} else {
None
}
});
let new_footer = sizes
.filter_map(|size| Bump::new_chunk(Some(size), layout, current_footer))
let new_footer = chunk_memory_details
.filter_map(|chunk_memory_details| {
if Bump::chunk_fits_under_limit(
allocation_limit_remaining,
chunk_memory_details,
) {
Bump::new_chunk(chunk_memory_details, layout, current_footer)
} else {
None
}
})
.next()?;
debug_assert_eq!(
@ -1499,6 +1678,10 @@ impl Bump {
/// on it only counting the sum of the sizes of the things
/// you've allocated in the arena.
///
/// The allocated bytes do not include the size of bumpalo's metadata,
/// so the amount of memory requested from the Rust allocator is higher
/// than the returned value.
///
/// ## Example
///
/// ```
@ -1508,24 +1691,9 @@ impl Bump {
/// assert!(bytes >= core::mem::size_of::<u32>() * 5);
/// ```
pub fn allocated_bytes(&self) -> usize {
let mut footer = self.current_chunk_footer.get();
let footer = self.current_chunk_footer.get();
let mut bytes = 0;
unsafe {
while !footer.as_ref().is_empty() {
let foot = footer.as_ref();
let ptr = foot.ptr.get().as_ptr() as usize;
debug_assert!(ptr <= foot as *const _ as usize);
bytes += foot as *const _ as usize - ptr;
footer = foot.prev.get();
}
}
bytes
unsafe { footer.as_ref().allocated_bytes }
}
#[inline]
@ -1770,15 +1938,20 @@ unsafe impl<'a> Allocator for &'a Bump {
}
}
// NB: Only tests which require private types, fields, or methods should be in
// here. Anything that can just be tested via public API surface should be in
// `bumpalo/tests/all/*`.
#[cfg(test)]
mod tests {
use super::*;
// Uses private type `ChunkFooter`.
#[test]
fn chunk_footer_is_five_words() {
assert_eq!(mem::size_of::<ChunkFooter>(), mem::size_of::<usize>() * 5);
assert_eq!(mem::size_of::<ChunkFooter>(), mem::size_of::<usize>() * 6);
}
// Uses private `alloc` module.
#[test]
#[allow(clippy::cognitive_complexity)]
fn test_realloc() {
@ -1828,6 +2001,7 @@ mod tests {
}
}
// Uses our private `alloc` module.
#[test]
fn invalid_read() {
use alloc::Alloc;