diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index 1482b92f0..75881fd99 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -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" diff --git a/zeroidc/vendor/bumpalo/.cargo-checksum.json b/zeroidc/vendor/bumpalo/.cargo-checksum.json index 8c1b8d125..d747ed0ac 100644 --- a/zeroidc/vendor/bumpalo/.cargo-checksum.json +++ b/zeroidc/vendor/bumpalo/.cargo-checksum.json @@ -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"} \ No newline at end of file +{"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"} \ No newline at end of file diff --git a/zeroidc/vendor/bumpalo/CHANGELOG.md b/zeroidc/vendor/bumpalo/CHANGELOG.md index 51fe0530f..afc142eb9 100644 --- a/zeroidc/vendor/bumpalo/CHANGELOG.md +++ b/zeroidc/vendor/bumpalo/CHANGELOG.md @@ -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. diff --git a/zeroidc/vendor/bumpalo/Cargo.toml b/zeroidc/vendor/bumpalo/Cargo.toml index 50cbc5041..02ec679c2 100644 --- a/zeroidc/vendor/bumpalo/Cargo.toml +++ b/zeroidc/vendor/bumpalo/Cargo.toml @@ -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 "] 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 = [] diff --git a/zeroidc/vendor/bumpalo/README.md b/zeroidc/vendor/bumpalo/README.md index 9a69bdbe3..3d73e2967 100644 --- a/zeroidc/vendor/bumpalo/README.md +++ b/zeroidc/vendor/bumpalo/README.md @@ -147,7 +147,8 @@ in its space itself. // Drop our `Box`. 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. diff --git a/zeroidc/vendor/bumpalo/src/boxed.rs b/zeroidc/vendor/bumpalo/src/boxed.rs index 63e693e56..af0737cfb 100644 --- a/zeroidc/vendor/bumpalo/src/boxed.rs +++ b/zeroidc/vendor/bumpalo/src/boxed.rs @@ -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.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> 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> for Box<'a, [T]> { /// This impl replaces unsize coercion. impl<'a, T, const N: usize> TryFrom> for Box<'a, [T; N]> { type Error = Box<'a, [T]>; - fn try_from(mut slice: Box<'a, [T]>) -> Result, Box<'a, [T]>> { + fn try_from(slice: Box<'a, [T]>) -> Result, 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) diff --git a/zeroidc/vendor/bumpalo/src/collections/raw_vec.rs b/zeroidc/vendor/bumpalo/src/collections/raw_vec.rs index 46208674d..ac3bd0758 100644 --- a/zeroidc/vendor/bumpalo/src/collections/raw_vec.rs +++ b/zeroidc/vendor/bumpalo/src/collections/raw_vec.rs @@ -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::() == 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::() as *mut T) }, - // FIXME(mark-i-m): use `cap` when ifs are allowed in const - cap: [0, !0][(mem::size_of::() == 0) as usize], + ptr: NonNull::dangling(), + cap: 0, a, } } diff --git a/zeroidc/vendor/bumpalo/src/collections/string.rs b/zeroidc/vendor/bumpalo/src/collections/string.rs index 2bc99fd4c..ffd1db92d 100644 --- a/zeroidc/vendor/bumpalo/src/collections/string.rs +++ b/zeroidc/vendor/bumpalo/src/collections/string.rs @@ -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` and `as_str` + impl<'a, 'bump> Iterator for Drain<'a, 'bump> { type Item = char; diff --git a/zeroidc/vendor/bumpalo/src/collections/vec.rs b/zeroidc/vendor/bumpalo/src/collections/vec.rs index a025c9fde..312aa055b 100644 --- a/zeroidc/vendor/bumpalo/src/collections/vec.rs +++ b/zeroidc/vendor/bumpalo/src/collections/vec.rs @@ -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 ≎ 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 = 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 = Vec::new_in(&b); + /// let mut vec: Vec = 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; + 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 { + 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 { - phantom: PhantomData, +pub struct IntoIter<'bump, T> { + phantom: PhantomData<&'bump [T]>, ptr: *const T, end: *const T, } -impl fmt::Debug for IntoIter { +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 { +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 { } } -unsafe impl Send for IntoIter {} -unsafe impl Sync for IntoIter {} +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 { +impl<'bump, T: 'bump> Iterator for IntoIter<'bump, T> { type Item = T; #[inline] @@ -2227,7 +2340,7 @@ impl<'bump, T: 'bump> Iterator for IntoIter { } } -impl<'bump, T: 'bump> DoubleEndedIterator for IntoIter { +impl<'bump, T: 'bump> DoubleEndedIterator for IntoIter<'bump, T> { #[inline] fn next_back(&mut self) -> Option { unsafe { @@ -2248,9 +2361,16 @@ impl<'bump, T: 'bump> DoubleEndedIterator for IntoIter { } } -impl<'bump, T: 'bump> ExactSizeIterator for IntoIter {} +impl<'bump, T: 'bump> ExactSizeIterator for IntoIter<'bump, T> {} -impl<'bump, T: 'bump> FusedIterator for IntoIter {} +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>`. /// diff --git a/zeroidc/vendor/bumpalo/src/lib.rs b/zeroidc/vendor/bumpalo/src/lib.rs index 2feb8b834..74dfcd436 100644 --- a/zeroidc/vendor/bumpalo/src/lib.rs +++ b/zeroidc/vendor/bumpalo/src/lib.rs @@ -252,10 +252,46 @@ impl Display for AllocOrInitError { /// [`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>, + allocation_limit: Cell>, } #[repr(C)] @@ -276,6 +312,12 @@ struct ChunkFooter { // Bump allocation finger that is always in the range `self.data..=self`. ptr: Cell>, + + // 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 { 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 { + 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) { + self.allocation_limit.set(limit) + } + + /// How much headroom an arena has before it hits its allocation + /// limit. + fn allocation_limit_remaining(&self) -> Option { + 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, + 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, + requested_layout: Layout, + ) -> Option { + 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, + unsafe fn new_chunk( + new_chunk_memory_details: NewChunkMemoryDetails, requested_layout: Layout, prev: NonNull, ) -> Option> { - 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 = ¤t_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 = ¤t_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> { 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::() * 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::(), mem::size_of::() * 5); + assert_eq!(mem::size_of::(), mem::size_of::() * 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;