From f67f42b5d64e12af80c9b8b2afd607d3441b9d16 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Thu, 7 Nov 2024 16:55:21 +0000 Subject: [PATCH 1/8] HashMap stub --- Cargo.toml | 16 +++++++++++- src/claim.rs | 5 ++-- src/hash/map.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ src/hash/mod.rs | 1 + src/lib.rs | 3 +++ 5 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/hash/map.rs create mode 100644 src/hash/mod.rs diff --git a/Cargo.toml b/Cargo.toml index fc4a67a..0bf3eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,20 @@ rust-version = "nightly" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "nightly"] +[dependencies.hashbrown] +version = "0.15.1" +optional = true +default-features = false +features = [ + "nightly", + "default-hasher", + "inline-more", + "allocator-api2", + "equivalent", + "raw-entry" +] + [features] -default = [] +default = ["hash_collections"] +hash_collections = ["hashbrown"] no_std = [] diff --git a/src/claim.rs b/src/claim.rs index 0703185..c8191c7 100644 --- a/src/claim.rs +++ b/src/claim.rs @@ -1,3 +1,4 @@ +use alloc::alloc::Global; use alloc::rc::Rc; use alloc::sync::Arc; use core::convert::Infallible; @@ -26,7 +27,7 @@ macro_rules! impl_claim_for { impl_claim_for! { (), u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, - f32, f64, bool, char + f32, f64, bool, char, Global } impl Claim for *const T {} @@ -40,4 +41,4 @@ impl Claim for Arc {} impl Claim for Rc {} impl Claim for Infallible {} impl Claim for Option {} -impl Claim for Result {} +impl Claim for Result {} \ No newline at end of file diff --git a/src/hash/map.rs b/src/hash/map.rs new file mode 100644 index 0000000..63514a5 --- /dev/null +++ b/src/hash/map.rs @@ -0,0 +1,67 @@ +use core::alloc::Allocator; +use hashbrown::HashMap as InnerHashMap; +pub use hashbrown::DefaultHashBuilder; +pub use hashbrown::hash_map::{Keys, Values, ValuesMut}; +use crate::claim::Claim; + +pub struct HashMap { + inner: InnerHashMap +} + +impl HashMap { + #[inline] + pub fn new_in(alloc: A) -> Self { + Self::with_hasher_in(DefaultHashBuilder::default(), alloc) + } + + #[inline] + pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + Self::with_capacity_and_hasher_in(capacity, DefaultHashBuilder::default(), alloc) + } +} + +impl HashMap { + #[inline] + pub fn allocator(&self) -> &A { + self.inner.allocator() + } + + #[inline] + pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { + Self { + inner: InnerHashMap::with_hasher_in(hash_builder, alloc) + } + } + + #[inline] + pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { + Self { + inner: InnerHashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) + } + } + + #[inline] + pub fn hasher(&self) -> &S { + &self.inner.hasher() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn keys(&self) -> Keys<'_, K, V> { + self.inner.keys() + } + + #[inline] + pub fn values(&self) -> Values<'_, K, V> { + self.inner.values() + } + + #[inline] + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + self.inner.values_mut() + } +} \ No newline at end of file diff --git a/src/hash/mod.rs b/src/hash/mod.rs new file mode 100644 index 0000000..36c02b6 --- /dev/null +++ b/src/hash/mod.rs @@ -0,0 +1 @@ +pub mod map; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9bb94ee..8c8384e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,3 +8,6 @@ extern crate core; pub mod claim; pub mod try_clone; pub mod vec; + +#[cfg(feature = "hash_collections")] +pub mod hash; From f7448608c214dcc8c1487431ebef4b864c9ec25e Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Thu, 7 Nov 2024 16:55:51 +0000 Subject: [PATCH 2/8] cargo fmt --- src/claim.rs | 2 +- src/hash/map.rs | 28 ++++++++++++++-------------- src/hash/mod.rs | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/claim.rs b/src/claim.rs index c8191c7..410efae 100644 --- a/src/claim.rs +++ b/src/claim.rs @@ -41,4 +41,4 @@ impl Claim for Arc {} impl Claim for Rc {} impl Claim for Infallible {} impl Claim for Option {} -impl Claim for Result {} \ No newline at end of file +impl Claim for Result {} diff --git a/src/hash/map.rs b/src/hash/map.rs index 63514a5..104043a 100644 --- a/src/hash/map.rs +++ b/src/hash/map.rs @@ -1,11 +1,11 @@ +use crate::claim::Claim; use core::alloc::Allocator; -use hashbrown::HashMap as InnerHashMap; -pub use hashbrown::DefaultHashBuilder; pub use hashbrown::hash_map::{Keys, Values, ValuesMut}; -use crate::claim::Claim; +pub use hashbrown::DefaultHashBuilder; +use hashbrown::HashMap as InnerHashMap; pub struct HashMap { - inner: InnerHashMap + inner: InnerHashMap, } impl HashMap { @@ -13,30 +13,30 @@ impl HashMap { pub fn new_in(alloc: A) -> Self { Self::with_hasher_in(DefaultHashBuilder::default(), alloc) } - + #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self::with_capacity_and_hasher_in(capacity, DefaultHashBuilder::default(), alloc) } } -impl HashMap { +impl HashMap { #[inline] pub fn allocator(&self) -> &A { self.inner.allocator() } - + #[inline] pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { Self { - inner: InnerHashMap::with_hasher_in(hash_builder, alloc) + inner: InnerHashMap::with_hasher_in(hash_builder, alloc), } } - + #[inline] pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { Self { - inner: InnerHashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) + inner: InnerHashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc), } } @@ -44,7 +44,7 @@ impl HashMap { pub fn hasher(&self) -> &S { &self.inner.hasher() } - + #[inline] pub fn capacity(&self) -> usize { self.inner.capacity() @@ -54,14 +54,14 @@ impl HashMap { pub fn keys(&self) -> Keys<'_, K, V> { self.inner.keys() } - + #[inline] pub fn values(&self) -> Values<'_, K, V> { self.inner.values() } - + #[inline] pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { self.inner.values_mut() } -} \ No newline at end of file +} diff --git a/src/hash/mod.rs b/src/hash/mod.rs index 36c02b6..1d7f53b 100644 --- a/src/hash/mod.rs +++ b/src/hash/mod.rs @@ -1 +1 @@ -pub mod map; \ No newline at end of file +pub mod map; From ebcef9f20502961f196b83bb11a7fc04b8f6c343 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Fri, 8 Nov 2024 14:35:30 +0000 Subject: [PATCH 3/8] global alloc testing --- Cargo.toml | 4 ++ README.md | 2 + src/global_alloc_test_guard.rs | 79 ++++++++++++++++++++++++++++++++++ src/hash/map.rs | 46 ++++++++++++++++++-- src/lib.rs | 10 ++++- src/vec.rs | 2 + 6 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 src/global_alloc_test_guard.rs diff --git a/Cargo.toml b/Cargo.toml index 0bf3eb5..f9f0fbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,10 @@ features = [ "raw-entry" ] +[dev-dependencies] +heapless = "0.7" + + [features] default = ["hash_collections"] hash_collections = ["hashbrown"] diff --git a/README.md b/README.md index 04ba8b5..6dcf50a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ The wrapper collection types provide two main benefits: * They can't be used incorrectly by virtue of lacking APIs which panic. * Provide additional convenience methods for working with the collection in a checked manner. +*N.B.:* You might find the [`heapless`](https://docs.rs/heapless/latest/heapless/) crate useful too! + ## Restrictions The crate requires a recent build of the Rust "nightly" compiler, as it uses the diff --git a/src/global_alloc_test_guard.rs b/src/global_alloc_test_guard.rs new file mode 100644 index 0000000..2db019a --- /dev/null +++ b/src/global_alloc_test_guard.rs @@ -0,0 +1,79 @@ +use std::alloc::{GlobalAlloc, Layout, System}; +use std::sync::Mutex; +use heapless::Vec; +use std::thread::{self, ThreadId}; + +static NO_ALLOC_THREADS: Mutex> = Mutex::new(Vec::new()); + +pub struct NoGlobalAllocGuard {} + +impl NoGlobalAllocGuard { + pub fn new() -> Self { + let tid = thread::current().id(); + + let mut vec = NO_ALLOC_THREADS.lock() + .expect("NO_ALLOC_THREADS lockable"); + + if vec.contains(&tid) { + panic!("NoGlobalAllocGuard is not re-entrant"); + } + + vec.push(tid).expect("NO_ALLOC_THREADS capacity exceeded"); + + Self {} + } +} + +impl Drop for NoGlobalAllocGuard { + fn drop(&mut self) { + let mut vec = NO_ALLOC_THREADS.lock() + .expect("NO_ALLOC_THREADS lockable"); + + let tid = thread::current().id(); + let idx = vec + .iter() + .position(|recorded_tid| tid == *recorded_tid) + .expect("unmatched thread ID"); + + vec.remove(idx); + } +} + +pub struct GlobalAllocTestGuardAllocator; + +impl GlobalAllocTestGuardAllocator { + fn is_allowed(&self) -> bool { + let tid = thread::current().id(); + let vec = NO_ALLOC_THREADS.lock() + .expect("NO_ALLOC_THREADS lockable"); + !vec.contains(&tid) + } + + fn guard(&self) { + if !self.is_allowed() { + panic!("Global allocation disabled by a NoGlobalAllocGuard."); + } + } +} + +unsafe impl GlobalAlloc for GlobalAllocTestGuardAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.guard(); + System.alloc(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.guard(); + System.dealloc(ptr, layout); + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + self.guard(); + System.alloc_zeroed(layout) + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + self.guard(); + System.realloc(ptr, layout, new_size) + } +} diff --git a/src/hash/map.rs b/src/hash/map.rs index 104043a..d65a6dd 100644 --- a/src/hash/map.rs +++ b/src/hash/map.rs @@ -1,8 +1,9 @@ use crate::claim::Claim; use core::alloc::Allocator; -pub use hashbrown::hash_map::{Keys, Values, ValuesMut}; +use core::hash::{BuildHasher, Hash}; +pub use hashbrown::hash_map::{Keys, Values, ValuesMut, Iter, IterMut}; pub use hashbrown::DefaultHashBuilder; -use hashbrown::HashMap as InnerHashMap; +use hashbrown::{HashMap as InnerHashMap, TryReserveError}; pub struct HashMap { inner: InnerHashMap, @@ -42,7 +43,7 @@ impl HashMap { #[inline] pub fn hasher(&self) -> &S { - &self.inner.hasher() + self.inner.hasher() } #[inline] @@ -50,6 +51,16 @@ impl HashMap { self.inner.capacity() } + #[inline] + pub fn clear(&mut self) { + // TODO(amunra): May this reallocate memory? + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + #[inline] pub fn keys(&self) -> Keys<'_, K, V> { self.inner.keys() @@ -64,4 +75,33 @@ impl HashMap { pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { self.inner.values_mut() } + + #[inline] + pub fn iter(&self) -> Iter<'_, K, V> { + self.inner.iter() + } + + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + self.inner.iter_mut() + } } + +impl HashMap +where + K: Eq + Hash, + A: Allocator + Claim, + S: BuildHasher, +{ + #[inline] + pub fn reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve(additional) + } + + // #[inline] + // pub fn remove(&mut self, k: &Q) -> Option { + // // TODO(amunra): May this reallocate memory? + // self.inner.remove(k) + // } +} + diff --git a/src/lib.rs b/src/lib.rs index 8c8384e..de894b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "no_std", no_std)] +#![cfg_attr(not(test), cfg_attr(feature = "no_std", no_std))] #![feature(allocator_api)] #![cfg_attr(not(doctest), doc = include_str!("../README.md"))] @@ -11,3 +11,11 @@ pub mod vec; #[cfg(feature = "hash_collections")] pub mod hash; + +#[cfg(test)] +pub(crate) mod global_alloc_test_guard; + + +#[cfg(test)] +#[global_allocator] +static GLOBAL: global_alloc_test_guard::GlobalAllocTestGuardAllocator = global_alloc_test_guard::GlobalAllocTestGuardAllocator; \ No newline at end of file diff --git a/src/vec.rs b/src/vec.rs index 7d86c58..96f5fde 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -298,6 +298,7 @@ mod tests { use core::alloc::{AllocError, Layout}; use core::ptr::NonNull; use core::sync::atomic::{AtomicUsize, Ordering}; + use crate::global_alloc_test_guard::NoGlobalAllocGuard; #[derive(Clone)] struct WatermarkAllocator { @@ -351,6 +352,7 @@ mod tests { #[test] fn test_basics() { + let _g = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma.clone()); assert_eq!(vec.len(), 0); From 2ec469e0a69e45e715f8d5bd764c55b9f3161371 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Fri, 8 Nov 2024 15:27:59 +0000 Subject: [PATCH 4/8] initial implementation guarding against unexpected global allocations in tests --- src/global_alloc_test_guard.rs | 81 +++++++++++++++++++++------------- src/hash/map.rs | 3 +- src/lib.rs | 4 +- src/vec.rs | 41 ++++++++++++----- 4 files changed, 84 insertions(+), 45 deletions(-) diff --git a/src/global_alloc_test_guard.rs b/src/global_alloc_test_guard.rs index 2db019a..095662a 100644 --- a/src/global_alloc_test_guard.rs +++ b/src/global_alloc_test_guard.rs @@ -1,52 +1,73 @@ use std::alloc::{GlobalAlloc, Layout, System}; -use std::sync::Mutex; -use heapless::Vec; -use std::thread::{self, ThreadId}; -static NO_ALLOC_THREADS: Mutex> = Mutex::new(Vec::new()); +thread_local! { + static GLOBAL_ALLOC_ALLOWED: std::cell::RefCell = std::cell::RefCell::new(true); +} + +struct NoPubCtor; -pub struct NoGlobalAllocGuard {} +/// A guard that temporarily error if a test performs global allocation in the current thread. +pub struct NoGlobalAllocGuard(NoPubCtor); impl NoGlobalAllocGuard { pub fn new() -> Self { - let tid = thread::current().id(); - - let mut vec = NO_ALLOC_THREADS.lock() - .expect("NO_ALLOC_THREADS lockable"); - - if vec.contains(&tid) { - panic!("NoGlobalAllocGuard is not re-entrant"); - } - - vec.push(tid).expect("NO_ALLOC_THREADS capacity exceeded"); - - Self {} + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + if !*alloc_allowed { + panic!("NoGlobalAllocGuard is not re-entrant."); + } + *alloc_allowed = false; // Disable global allocation + }); + + Self(NoPubCtor) } } impl Drop for NoGlobalAllocGuard { fn drop(&mut self) { - let mut vec = NO_ALLOC_THREADS.lock() - .expect("NO_ALLOC_THREADS lockable"); + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + *alloc_allowed = true; + }); + } +} - let tid = thread::current().id(); - let idx = vec - .iter() - .position(|recorded_tid| tid == *recorded_tid) - .expect("unmatched thread ID"); +pub struct AllowNextGlobalAllocGuard { + was_allowed: bool, +} - vec.remove(idx); +impl AllowNextGlobalAllocGuard { + pub fn new() -> Self { + let was_allowed = GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let was_allowed = *alloc_allowed.borrow(); + if !was_allowed { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + *alloc_allowed = true; + } + was_allowed + }); + + Self { was_allowed } + } +} + +impl Drop for AllowNextGlobalAllocGuard { + fn drop(&mut self) { + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + *alloc_allowed = self.was_allowed; + }); } } +/// Enables the `NoGlobalAllocGuard` by acting as a global allocator. pub struct GlobalAllocTestGuardAllocator; impl GlobalAllocTestGuardAllocator { fn is_allowed(&self) -> bool { - let tid = thread::current().id(); - let vec = NO_ALLOC_THREADS.lock() - .expect("NO_ALLOC_THREADS lockable"); - !vec.contains(&tid) + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + *alloc_allowed.borrow() // Check if allocation is allowed for the current thread + }) } fn guard(&self) { @@ -64,7 +85,7 @@ unsafe impl GlobalAlloc for GlobalAllocTestGuardAllocator { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { self.guard(); - System.dealloc(ptr, layout); + System.dealloc(ptr, layout) } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { diff --git a/src/hash/map.rs b/src/hash/map.rs index d65a6dd..286d212 100644 --- a/src/hash/map.rs +++ b/src/hash/map.rs @@ -1,7 +1,7 @@ use crate::claim::Claim; use core::alloc::Allocator; use core::hash::{BuildHasher, Hash}; -pub use hashbrown::hash_map::{Keys, Values, ValuesMut, Iter, IterMut}; +pub use hashbrown::hash_map::{Iter, IterMut, Keys, Values, ValuesMut}; pub use hashbrown::DefaultHashBuilder; use hashbrown::{HashMap as InnerHashMap, TryReserveError}; @@ -104,4 +104,3 @@ where // self.inner.remove(k) // } } - diff --git a/src/lib.rs b/src/lib.rs index de894b7..72ddad4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ pub mod hash; #[cfg(test)] pub(crate) mod global_alloc_test_guard; - #[cfg(test)] #[global_allocator] -static GLOBAL: global_alloc_test_guard::GlobalAllocTestGuardAllocator = global_alloc_test_guard::GlobalAllocTestGuardAllocator; \ No newline at end of file +static GLOBAL: global_alloc_test_guard::GlobalAllocTestGuardAllocator = + global_alloc_test_guard::GlobalAllocTestGuardAllocator; diff --git a/src/vec.rs b/src/vec.rs index 96f5fde..737f61e 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -290,6 +290,7 @@ impl AsMut<[T]> for Vec { mod tests { use super::*; use crate::claim::Claim; + use crate::global_alloc_test_guard::{AllowNextGlobalAllocGuard, NoGlobalAllocGuard}; use alloc::alloc::Global; use alloc::boxed::Box; use alloc::collections::TryReserveError; @@ -298,45 +299,62 @@ mod tests { use core::alloc::{AllocError, Layout}; use core::ptr::NonNull; use core::sync::atomic::{AtomicUsize, Ordering}; - use crate::global_alloc_test_guard::NoGlobalAllocGuard; #[derive(Clone)] struct WatermarkAllocator { watermark: usize, - in_use: Arc, + in_use: Option>, + } + + impl Drop for WatermarkAllocator { + fn drop(&mut self) { + let in_use = self.in_use.take().unwrap(); + let _g = AllowNextGlobalAllocGuard::new(); + drop(in_use); + } } impl Claim for WatermarkAllocator {} impl WatermarkAllocator { pub(crate) fn in_use(&self) -> usize { - self.in_use.load(Ordering::SeqCst) + self.in_use.as_ref().unwrap().load(Ordering::SeqCst) } } impl WatermarkAllocator { fn new(watermark: usize) -> Self { + let in_use = Some({ + let _g = AllowNextGlobalAllocGuard::new(); + AtomicUsize::new(0).into() + }); Self { watermark, - in_use: AtomicUsize::new(0).into(), + in_use, } } } unsafe impl Allocator for WatermarkAllocator { fn allocate(&self, layout: Layout) -> Result, AllocError> { - let current_in_use = self.in_use.load(Ordering::SeqCst); + let current_in_use = self.in_use.as_ref().unwrap().load(Ordering::SeqCst); let new_in_use = current_in_use + layout.size(); if new_in_use > self.watermark { return Err(AllocError); } - let allocated = Global.allocate(layout)?; - let true_new_in_use = self.in_use.fetch_add(allocated.len(), Ordering::SeqCst); + let allocated = { + let _g = AllowNextGlobalAllocGuard::new(); + Global.allocate(layout)? + }; + let true_new_in_use = self.in_use.as_ref().unwrap().fetch_add(allocated.len(), Ordering::SeqCst); unsafe { if true_new_in_use > self.watermark { let ptr = allocated.as_ptr() as *mut u8; let to_dealloc = NonNull::new_unchecked(ptr); - Global.deallocate(to_dealloc, layout); + { + let _g = AllowNextGlobalAllocGuard::new(); + Global.deallocate(to_dealloc, layout); + } Err(AllocError) } else { Ok(allocated) @@ -345,8 +363,9 @@ mod tests { } unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + let _g = AllowNextGlobalAllocGuard::new(); Global.deallocate(ptr, layout); - self.in_use.fetch_sub(layout.size(), Ordering::SeqCst); + self.in_use.as_ref().unwrap().fetch_sub(layout.size(), Ordering::SeqCst); } } @@ -367,11 +386,11 @@ mod tests { assert_eq!(vec.len(), 4); assert_eq!(vec.capacity(), 4); assert_eq!( - wma.in_use.load(Ordering::SeqCst), + wma.in_use(), vec.capacity() * size_of::() ); assert_eq!( - vec.allocator().in_use.load(Ordering::SeqCst), + vec.allocator().in_use(), vec.capacity() * size_of::() ); let _err: TryReserveError = vec.push(5).unwrap_err(); From 452d1ffa618d17611a8d3dabc1b064dab053760b Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Fri, 8 Nov 2024 15:28:19 +0000 Subject: [PATCH 5/8] cargo fmt --- src/vec.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 737f61e..5a70073 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -328,10 +328,7 @@ mod tests { let _g = AllowNextGlobalAllocGuard::new(); AtomicUsize::new(0).into() }); - Self { - watermark, - in_use, - } + Self { watermark, in_use } } } @@ -346,7 +343,11 @@ mod tests { let _g = AllowNextGlobalAllocGuard::new(); Global.allocate(layout)? }; - let true_new_in_use = self.in_use.as_ref().unwrap().fetch_add(allocated.len(), Ordering::SeqCst); + let true_new_in_use = self + .in_use + .as_ref() + .unwrap() + .fetch_add(allocated.len(), Ordering::SeqCst); unsafe { if true_new_in_use > self.watermark { let ptr = allocated.as_ptr() as *mut u8; @@ -365,7 +366,10 @@ mod tests { unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { let _g = AllowNextGlobalAllocGuard::new(); Global.deallocate(ptr, layout); - self.in_use.as_ref().unwrap().fetch_sub(layout.size(), Ordering::SeqCst); + self.in_use + .as_ref() + .unwrap() + .fetch_sub(layout.size(), Ordering::SeqCst); } } @@ -385,14 +389,8 @@ mod tests { vec.push(4).unwrap(); assert_eq!(vec.len(), 4); assert_eq!(vec.capacity(), 4); - assert_eq!( - wma.in_use(), - vec.capacity() * size_of::() - ); - assert_eq!( - vec.allocator().in_use(), - vec.capacity() * size_of::() - ); + assert_eq!(wma.in_use(), vec.capacity() * size_of::()); + assert_eq!(vec.allocator().in_use(), vec.capacity() * size_of::()); let _err: TryReserveError = vec.push(5).unwrap_err(); assert_eq!(vec.as_slice(), &[1, 2, 3, 4]); assert_eq!(vec.len(), 4); From 773f6e9aaac9d8bc2ebf661ad25a55a400a23040 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Fri, 8 Nov 2024 16:29:18 +0000 Subject: [PATCH 6/8] to every vec test, added a guard that ensures we don't allocate unexpectantly --- Cargo.toml | 4 - src/global_alloc_test_guard.rs | 100 ---------------- src/lib.rs | 5 +- src/testing.rs | 203 +++++++++++++++++++++++++++++++++ src/vec.rs | 160 +++++++++++--------------- 5 files changed, 270 insertions(+), 202 deletions(-) delete mode 100644 src/global_alloc_test_guard.rs create mode 100644 src/testing.rs diff --git a/Cargo.toml b/Cargo.toml index f9f0fbf..0bf3eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,10 +28,6 @@ features = [ "raw-entry" ] -[dev-dependencies] -heapless = "0.7" - - [features] default = ["hash_collections"] hash_collections = ["hashbrown"] diff --git a/src/global_alloc_test_guard.rs b/src/global_alloc_test_guard.rs deleted file mode 100644 index 095662a..0000000 --- a/src/global_alloc_test_guard.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::alloc::{GlobalAlloc, Layout, System}; - -thread_local! { - static GLOBAL_ALLOC_ALLOWED: std::cell::RefCell = std::cell::RefCell::new(true); -} - -struct NoPubCtor; - -/// A guard that temporarily error if a test performs global allocation in the current thread. -pub struct NoGlobalAllocGuard(NoPubCtor); - -impl NoGlobalAllocGuard { - pub fn new() -> Self { - GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { - let mut alloc_allowed = alloc_allowed.borrow_mut(); - if !*alloc_allowed { - panic!("NoGlobalAllocGuard is not re-entrant."); - } - *alloc_allowed = false; // Disable global allocation - }); - - Self(NoPubCtor) - } -} - -impl Drop for NoGlobalAllocGuard { - fn drop(&mut self) { - GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { - let mut alloc_allowed = alloc_allowed.borrow_mut(); - *alloc_allowed = true; - }); - } -} - -pub struct AllowNextGlobalAllocGuard { - was_allowed: bool, -} - -impl AllowNextGlobalAllocGuard { - pub fn new() -> Self { - let was_allowed = GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { - let was_allowed = *alloc_allowed.borrow(); - if !was_allowed { - let mut alloc_allowed = alloc_allowed.borrow_mut(); - *alloc_allowed = true; - } - was_allowed - }); - - Self { was_allowed } - } -} - -impl Drop for AllowNextGlobalAllocGuard { - fn drop(&mut self) { - GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { - let mut alloc_allowed = alloc_allowed.borrow_mut(); - *alloc_allowed = self.was_allowed; - }); - } -} - -/// Enables the `NoGlobalAllocGuard` by acting as a global allocator. -pub struct GlobalAllocTestGuardAllocator; - -impl GlobalAllocTestGuardAllocator { - fn is_allowed(&self) -> bool { - GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { - *alloc_allowed.borrow() // Check if allocation is allowed for the current thread - }) - } - - fn guard(&self) { - if !self.is_allowed() { - panic!("Global allocation disabled by a NoGlobalAllocGuard."); - } - } -} - -unsafe impl GlobalAlloc for GlobalAllocTestGuardAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - self.guard(); - System.alloc(layout) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - self.guard(); - System.dealloc(ptr, layout) - } - - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - self.guard(); - System.alloc_zeroed(layout) - } - - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - self.guard(); - System.realloc(ptr, layout, new_size) - } -} diff --git a/src/lib.rs b/src/lib.rs index 72ddad4..8aced48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,9 +13,8 @@ pub mod vec; pub mod hash; #[cfg(test)] -pub(crate) mod global_alloc_test_guard; +pub(crate) mod testing; #[cfg(test)] #[global_allocator] -static GLOBAL: global_alloc_test_guard::GlobalAllocTestGuardAllocator = - global_alloc_test_guard::GlobalAllocTestGuardAllocator; +static GLOBAL: testing::GlobalAllocTestGuardAllocator = testing::GlobalAllocTestGuardAllocator; diff --git a/src/testing.rs b/src/testing.rs new file mode 100644 index 0000000..7650da6 --- /dev/null +++ b/src/testing.rs @@ -0,0 +1,203 @@ +use crate::claim::Claim; +use alloc::sync::Arc; +use core::ptr::NonNull; +use core::sync::atomic::{AtomicUsize, Ordering}; +use std::alloc::{AllocError, Allocator, Global, GlobalAlloc, Layout, System}; + +thread_local! { + static GLOBAL_ALLOC_ALLOWED: std::cell::RefCell = std::cell::RefCell::new(true); +} + +struct NoPubCtor; + +/// A guard that temporarily error if a test performs global allocation in the current thread. +pub struct NoGlobalAllocGuard(NoPubCtor); + +impl NoGlobalAllocGuard { + pub fn new() -> Self { + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + if !*alloc_allowed { + panic!("NoGlobalAllocGuard is not re-entrant."); + } + *alloc_allowed = false; // Disable global allocation + }); + + Self(NoPubCtor) + } +} + +impl Drop for NoGlobalAllocGuard { + fn drop(&mut self) { + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + *alloc_allowed = true; + }); + } +} + +pub struct AllowGlobalAllocGuard { + was_allowed: bool, +} + +impl AllowGlobalAllocGuard { + pub fn new() -> Self { + let was_allowed = GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let was_allowed = *alloc_allowed.borrow(); + if !was_allowed { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + *alloc_allowed = true; + } + was_allowed + }); + + Self { was_allowed } + } +} + +impl Drop for AllowGlobalAllocGuard { + fn drop(&mut self) { + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + let mut alloc_allowed = alloc_allowed.borrow_mut(); + *alloc_allowed = self.was_allowed; + }); + } +} + +/// Enables the `NoGlobalAllocGuard` by acting as a global allocator. +pub struct GlobalAllocTestGuardAllocator; + +impl GlobalAllocTestGuardAllocator { + fn is_allowed(&self) -> bool { + GLOBAL_ALLOC_ALLOWED.with(|alloc_allowed| { + *alloc_allowed.borrow() // Check if allocation is allowed for the current thread + }) + } + + fn guard(&self) { + if !self.is_allowed() { + panic!("Caught unexpected global allocation with the NoGlobalAllocGuard. Run tests under debugger."); + } + } +} + +unsafe impl GlobalAlloc for GlobalAllocTestGuardAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.guard(); + System.alloc(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.guard(); + System.dealloc(ptr, layout) + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + self.guard(); + System.alloc_zeroed(layout) + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + self.guard(); + System.realloc(ptr, layout, new_size) + } +} + +#[derive(Clone)] +pub struct WatermarkAllocator { + watermark: usize, + in_use: Option>, +} + +impl Drop for WatermarkAllocator { + fn drop(&mut self) { + let in_use = self.in_use.take().unwrap(); + let _g = AllowGlobalAllocGuard::new(); + drop(in_use); + } +} + +impl WatermarkAllocator { + pub fn new(watermark: usize) -> Self { + let in_use = Some({ + let _g = AllowGlobalAllocGuard::new(); + AtomicUsize::new(0).into() + }); + Self { watermark, in_use } + } + + pub fn in_use(&self) -> usize { + self.in_use.as_ref().unwrap().load(Ordering::SeqCst) + } +} + +impl Claim for WatermarkAllocator {} + +unsafe impl Allocator for WatermarkAllocator { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + let current_in_use = self.in_use.as_ref().unwrap().load(Ordering::SeqCst); + let new_in_use = current_in_use + layout.size(); + if new_in_use > self.watermark { + return Err(AllocError); + } + let allocated = { + let _g = AllowGlobalAllocGuard::new(); + Global.allocate(layout)? + }; + let true_new_in_use = self + .in_use + .as_ref() + .unwrap() + .fetch_add(allocated.len(), Ordering::SeqCst); + unsafe { + if true_new_in_use > self.watermark { + let ptr = allocated.as_ptr() as *mut u8; + let to_dealloc = NonNull::new_unchecked(ptr); + { + let _g = AllowGlobalAllocGuard::new(); + Global.deallocate(to_dealloc, layout); + } + Err(AllocError) + } else { + Ok(allocated) + } + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + let _g = AllowGlobalAllocGuard::new(); + Global.deallocate(ptr, layout); + self.in_use + .as_ref() + .unwrap() + .fetch_sub(layout.size(), Ordering::SeqCst); + } +} + +/// A second watermark allocator. This is just to test cases where we need generic types +/// to interoperate, even when their allocator differs. E.g. `lhs: Vec == rhs: Vec`. +#[derive(Clone)] +pub struct WatermarkAllocator2(WatermarkAllocator); + +impl WatermarkAllocator2 { + pub fn new(watermark: usize) -> Self { + let inner = WatermarkAllocator::new(watermark); + Self(inner) + } + + pub fn in_use(&self) -> usize { + self.0.in_use() + } +} + +impl Claim for WatermarkAllocator2 {} + +unsafe impl Allocator for WatermarkAllocator2 { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.0.allocate(layout) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.deallocate(ptr, layout) + } +} diff --git a/src/vec.rs b/src/vec.rs index 5a70073..2780403 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -290,92 +290,15 @@ impl AsMut<[T]> for Vec { mod tests { use super::*; use crate::claim::Claim; - use crate::global_alloc_test_guard::{AllowNextGlobalAllocGuard, NoGlobalAllocGuard}; - use alloc::alloc::Global; + use crate::testing::{WatermarkAllocator, WatermarkAllocator2}; + use crate::testing::{AllowGlobalAllocGuard, NoGlobalAllocGuard}; use alloc::boxed::Box; use alloc::collections::TryReserveError; - use alloc::sync::Arc; use alloc::{format, vec}; - use core::alloc::{AllocError, Layout}; - use core::ptr::NonNull; - use core::sync::atomic::{AtomicUsize, Ordering}; - - #[derive(Clone)] - struct WatermarkAllocator { - watermark: usize, - in_use: Option>, - } - - impl Drop for WatermarkAllocator { - fn drop(&mut self) { - let in_use = self.in_use.take().unwrap(); - let _g = AllowNextGlobalAllocGuard::new(); - drop(in_use); - } - } - - impl Claim for WatermarkAllocator {} - - impl WatermarkAllocator { - pub(crate) fn in_use(&self) -> usize { - self.in_use.as_ref().unwrap().load(Ordering::SeqCst) - } - } - - impl WatermarkAllocator { - fn new(watermark: usize) -> Self { - let in_use = Some({ - let _g = AllowNextGlobalAllocGuard::new(); - AtomicUsize::new(0).into() - }); - Self { watermark, in_use } - } - } - - unsafe impl Allocator for WatermarkAllocator { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - let current_in_use = self.in_use.as_ref().unwrap().load(Ordering::SeqCst); - let new_in_use = current_in_use + layout.size(); - if new_in_use > self.watermark { - return Err(AllocError); - } - let allocated = { - let _g = AllowNextGlobalAllocGuard::new(); - Global.allocate(layout)? - }; - let true_new_in_use = self - .in_use - .as_ref() - .unwrap() - .fetch_add(allocated.len(), Ordering::SeqCst); - unsafe { - if true_new_in_use > self.watermark { - let ptr = allocated.as_ptr() as *mut u8; - let to_dealloc = NonNull::new_unchecked(ptr); - { - let _g = AllowNextGlobalAllocGuard::new(); - Global.deallocate(to_dealloc, layout); - } - Err(AllocError) - } else { - Ok(allocated) - } - } - } - - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - let _g = AllowNextGlobalAllocGuard::new(); - Global.deallocate(ptr, layout); - self.in_use - .as_ref() - .unwrap() - .fetch_sub(layout.size(), Ordering::SeqCst); - } - } #[test] fn test_basics() { - let _g = NoGlobalAllocGuard::new(); + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma.clone()); assert_eq!(vec.len(), 0); @@ -402,6 +325,7 @@ mod tests { #[test] fn test_with_capacity_in() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let vec: Vec = Vec::with_capacity_in(4, wma.clone()).unwrap(); assert_eq!(vec.len(), 0); @@ -414,6 +338,7 @@ mod tests { #[test] fn test_reserve() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec: Vec = Vec::new_in(wma); vec.reserve(32).unwrap(); @@ -424,17 +349,22 @@ mod tests { #[test] fn test_fmt_debug() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); vec.push(2).unwrap(); vec.push(3).unwrap(); vec.push(4).unwrap(); - assert_eq!(format!("{:?}", vec), "[1, 2, 3, 4]"); + { + let _allow_global_alloc = AllowGlobalAllocGuard::new(); + assert_eq!(format!("{:?}", vec), "[1, 2, 3, 4]"); + } } #[test] fn test_iter() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); @@ -451,6 +381,7 @@ mod tests { #[test] fn test_iter_mut() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); @@ -467,6 +398,7 @@ mod tests { #[test] fn test_as_ptr() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma.clone()); assert_eq!(wma.in_use(), 0); @@ -485,6 +417,7 @@ mod tests { #[test] fn test_as_mut_ptr() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(64); let mut vec = Vec::new_in(wma.clone()); assert_eq!(wma.in_use(), 0); @@ -507,6 +440,7 @@ mod tests { #[test] fn test_index() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); @@ -527,6 +461,7 @@ mod tests { #[test] fn test_extend_from_slice_clone() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.extend_from_slice(&[Claimable(1), Claimable(2), Claimable(3), Claimable(4)]) @@ -535,6 +470,7 @@ mod tests { #[test] fn test_extend_from_slice_copy() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.extend_from_slice(&[1, 2, 3, 4]).unwrap(); @@ -547,6 +483,7 @@ mod tests { #[test] fn test_deref() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); @@ -558,6 +495,7 @@ mod tests { #[test] fn test_deref_mut() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); @@ -609,6 +547,8 @@ mod tests { #[test] fn test_extend() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); + // Test the optimised with mixed pre-reserved and dynamic allocation extend paths. let wma = WatermarkAllocator::new(32 * size_of::()); { @@ -646,6 +586,7 @@ mod tests { #[test] fn test_truncate() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.push(1).unwrap(); @@ -661,6 +602,7 @@ mod tests { #[test] fn test_extend_with() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(32); let mut vec = Vec::new_in(wma); vec.extend_with(3, 1).unwrap(); @@ -669,6 +611,7 @@ mod tests { #[test] fn test_resize() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(64); let mut vec = Vec::new_in(wma); vec.resize(3, 1).unwrap(); @@ -681,6 +624,7 @@ mod tests { #[test] fn test_resize_with() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(64); let mut vec = Vec::new_in(wma); vec.resize_with(3, || 1).unwrap(); @@ -712,15 +656,17 @@ mod tests { #[test] fn test_eq() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(64); + let wma2 = WatermarkAllocator2::new(1024); // __impl_slice_eq1! { [A1: Allocator, A2: Allocator] Vec, Vec } { let mut lhs = Vec::new_in(wma.clone()); - let mut rhs = Vec::new_in(Global); + let mut rhs = Vec::new_in(wma2); - lhs.extend(vec![1, 2, 3]).unwrap(); - rhs.extend(vec![w(1), w(2), w(3)]).unwrap(); + lhs.extend([1, 2, 3]).unwrap(); + rhs.extend([w(1), w(2), w(3)]).unwrap(); assert_eq!(lhs, rhs); assert_eq!(rhs, lhs); @@ -733,7 +679,7 @@ mod tests { // __impl_slice_eq1! { [A: Allocator] &[T], Vec } { let mut lhs = Vec::new_in(wma.clone()); - lhs.extend(vec![1, 2, 3]).unwrap(); + lhs.extend([1, 2, 3]).unwrap(); let rhs: &[IntWrapper] = &[w(1), w(2), w(3)]; assert_eq!(lhs, rhs); assert_eq!(rhs, lhs); @@ -747,9 +693,14 @@ mod tests { // __impl_slice_eq1! { [A: Allocator] &mut [T], Vec } { let mut lhs = Vec::new_in(wma.clone()); - lhs.extend(vec![1, 2, 3]).unwrap(); + lhs.extend([1, 2, 3]).unwrap(); - let mut rhs_vec = vec![w(1), w(2), w(3)]; + let mut rhs_vec = { + let _allow_global_alloc = AllowGlobalAllocGuard::new(); + let mut v = vec![w(1), w(2), w(3)]; + v.reserve(1); + v + }; let rhs: &mut [IntWrapper] = &mut rhs_vec; assert_eq!(lhs, rhs); @@ -759,28 +710,44 @@ mod tests { let rhs2: &mut [IntWrapper] = &mut rhs_vec; assert_ne!(lhs, rhs2); assert_ne!(rhs2, lhs); + + { + let _allow_global_alloc = AllowGlobalAllocGuard::new(); + drop(rhs_vec) + } } // __impl_slice_eq1! { [A: Allocator] Vec, [U] } // __impl_slice_eq1! { [A: Allocator] [T], Vec } { let mut lhs = Vec::new_in(wma.clone()); - lhs.extend(vec![1, 2, 3]).unwrap(); - - let rhs: Box<[IntWrapper]> = Box::new([w(1), w(2), w(3)]); + lhs.extend([1, 2, 3]).unwrap(); + + // We explicitly elide the `len` part here by using a box. + let (rhs, rhs2) = { + let _allow_global_alloc = AllowGlobalAllocGuard::new(); + let rhs: Box<[IntWrapper]> = Box::new([w(1), w(2), w(3)]); + let rhs2: Box<[IntWrapper]> = Box::new([w(1), w(2), w(3), w(4)]); + (rhs, rhs2) + }; assert_eq!(lhs, *rhs); assert_eq!(*rhs, lhs); - let rhs2: Box<[IntWrapper]> = Box::new([w(1), w(2), w(3), w(4)]); assert_ne!(lhs, *rhs2); assert_ne!(*rhs2, lhs); + + { + let _allow_global_alloc = AllowGlobalAllocGuard::new(); + drop(rhs); + drop(rhs2); + } } // __impl_slice_eq1! { [A: Allocator, const N: usize] Vec, [U; N] } // __impl_slice_eq1! { [A: Allocator, const N: usize] [T; N], Vec } { let mut lhs = Vec::new_in(wma.clone()); - lhs.extend(vec![1, 2, 3]).unwrap(); + lhs.extend([1, 2, 3]).unwrap(); let rhs: [IntWrapper; 3] = [w(1), w(2), w(3)]; assert_eq!(lhs, rhs); // Compare Vec with fixed-size array @@ -795,7 +762,7 @@ mod tests { // __impl_slice_eq1! { [A: Allocator, const N: usize] &[T; N], Vec } { let mut lhs = Vec::new_in(wma.clone()); - lhs.extend(vec![1, 2, 3]).unwrap(); + lhs.extend([1, 2, 3]).unwrap(); let rhs_arr: [IntWrapper; 3] = [w(1), w(2), w(3)]; let rhs: &[IntWrapper; 3] = &rhs_arr; @@ -820,9 +787,10 @@ mod tests { #[test] fn test_as_ref() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(128); let mut vec1 = Vec::new_in(wma); - vec1.extend(vec![1, 2, 3]).unwrap(); + vec1.extend([1, 2, 3]).unwrap(); let vec2 = vec1.try_clone().unwrap(); assert_eq!(vec1, vec2); @@ -846,9 +814,10 @@ mod tests { #[test] fn test_as_mut() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(128); let mut vec1 = Vec::new_in(wma); - vec1.extend(vec![1, 2, 3]).unwrap(); + vec1.extend([1, 2, 3]).unwrap(); let vec2 = vec1.try_clone().unwrap(); assert_eq!(vec1, vec2); @@ -861,6 +830,7 @@ mod tests { #[test] fn test_try_clone() { + let _no_global_alloc_guard = NoGlobalAllocGuard::new(); let wma = WatermarkAllocator::new(64); let mut vec1 = Vec::new_in(wma.clone()); vec1.extend([1usize, 2, 3, 4, 5, 6, 7, 8]).unwrap(); From 0d445e60de7a3154d01b1b8589c7c0fc5203df8e Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Fri, 8 Nov 2024 16:29:34 +0000 Subject: [PATCH 7/8] cargo fmt --- src/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vec.rs b/src/vec.rs index 2780403..03b1732 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -290,8 +290,8 @@ impl AsMut<[T]> for Vec { mod tests { use super::*; use crate::claim::Claim; - use crate::testing::{WatermarkAllocator, WatermarkAllocator2}; use crate::testing::{AllowGlobalAllocGuard, NoGlobalAllocGuard}; + use crate::testing::{WatermarkAllocator, WatermarkAllocator2}; use alloc::boxed::Box; use alloc::collections::TryReserveError; use alloc::{format, vec}; From 185c944a17f40c7f970a01b1a2de63da8e51aee1 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Fri, 8 Nov 2024 16:36:02 +0000 Subject: [PATCH 8/8] clippy --- src/testing.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/testing.rs b/src/testing.rs index 7650da6..7998b27 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use std::alloc::{AllocError, Allocator, Global, GlobalAlloc, Layout, System}; thread_local! { - static GLOBAL_ALLOC_ALLOWED: std::cell::RefCell = std::cell::RefCell::new(true); + static GLOBAL_ALLOC_ALLOWED: std::cell::RefCell = const { std::cell::RefCell::new(true) }; } struct NoPubCtor; @@ -184,10 +184,6 @@ impl WatermarkAllocator2 { let inner = WatermarkAllocator::new(watermark); Self(inner) } - - pub fn in_use(&self) -> usize { - self.0.in_use() - } } impl Claim for WatermarkAllocator2 {}