mod common;
mod coverage {
#![allow(clippy::clone_on_ref_ptr, reason = "tests prefer concise method-call form")]
#![allow(clippy::std_instead_of_core, reason = "tests use std")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::large_stack_arrays, reason = "test allocations are intentional")]
#![allow(clippy::collection_is_never_read, reason = "tests retain smart pointers to keep chunks alive")]
#![allow(unused_results, reason = "test code")]
#![allow(clippy::used_underscore_binding, reason = "intentional drop-after binding")]
#![allow(clippy::cast_possible_truncation, reason = "test data is small")]
#![allow(clippy::explicit_into_iter_loop, reason = "test clarity")]
#![allow(clippy::assertions_on_result_states, reason = "tests deliberately assert error returns")]
#![allow(clippy::items_after_statements, reason = "test-local statics next to their use")]
#![allow(
clippy::cast_ptr_alignment,
reason = "test writes a u32 to a u8-typed reservation we created with u32 layout"
)]
#![allow(clippy::undocumented_unsafe_blocks, reason = "test code")]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "tests group related unsafe ops")]
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "dst")]
use multitude::Arc;
use multitude::strings::RcStr;
use multitude::vec::{CollectIn, Vec};
use multitude::{Arena, ArenaBuilder};
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
use crate::common::{FailingAllocator, SendFailingAllocator};
#[test]
fn allocator_shrink_at_cursor_lowers_bump() {
let arena: Arena = Arena::new();
let mut v: allocator_api2::vec::Vec<u32, &Arena> = allocator_api2::vec::Vec::with_capacity_in(1024, &arena);
for i in 0..10_u32 {
v.push(i);
}
v.shrink_to_fit();
assert_eq!(v.len(), 10);
let _other = arena.alloc_rc(0_u64);
assert_eq!(v.len(), 10);
}
#[test]
fn allocator_shrink_not_at_cursor_no_op() {
let arena: Arena = Arena::new();
let mut v: allocator_api2::vec::Vec<u32, &Arena> = allocator_api2::vec::Vec::with_capacity_in(1024, &arena);
for i in 0..10_u32 {
v.push(i);
}
let _decoy = arena.alloc_rc(0_u64); v.shrink_to_fit();
assert_eq!(v.len(), 10);
}
#[test]
fn allocator_deallocate_triggers_teardown_when_last_ref() {
let arena: Arena = Arena::builder().build();
{
let mut v: allocator_api2::vec::Vec<u8, &Arena> = allocator_api2::vec::Vec::new_in(&arena);
for _ in 0..16_000_u32 {
v.push(0);
}
drop(v);
}
}
#[test]
fn builder_allocator_in_chains_allocator() {
let alloc = FailingAllocator::new(usize::MAX);
let arena = Arena::builder().max_normal_alloc(4 * 1024).allocator_in(alloc).try_build().unwrap();
let v = arena.alloc_rc(123_u32);
assert_eq!(*v, 123);
}
#[test]
fn builder_debug_format() {
let s = format!("{:?}", Arena::builder());
assert!(s.contains("ArenaBuilder"));
assert!(s.contains("max_normal_alloc"));
}
#[test]
fn builder_preallocate_alloc_failed() {
let alloc = FailingAllocator::new(0);
let result = Arena::builder().with_capacity_local(512).allocator_in(alloc).try_build();
assert!(result.is_err());
}
#[test]
fn byte_budget_exhaustion_returns_alloc_error() {
let arena: Arena = Arena::builder().byte_budget(4 * 1024).build();
let mut handles = std::vec::Vec::new();
let mut hit_err = false;
for _ in 0..if cfg!(miri) { 100_u32 } else { 1000_u32 } {
if let Ok(h) = arena.try_alloc_rc([0_u8; 256]) {
handles.push(h);
} else {
hit_err = true;
break;
}
}
assert!(hit_err, "byte_budget did not stop allocations");
assert!(arena.try_alloc_rc(0_u32).is_err());
}
#[test]
fn arena_box_drop_unlinks_middle_of_drop_list() {
let arena = Arena::new();
let mut b1 = arena.alloc_box(std::string::String::from("first"));
let mut b2 = arena.alloc_box(std::string::String::from("middle"));
let mut b3 = arena.alloc_box(std::string::String::from("last"));
b1.push('!');
b2.push('!');
b3.push('!');
drop(b2); assert_eq!(*b1, "first!");
assert_eq!(*b3, "last!");
}
#[test]
fn cached_local_chunk_revived_as_shared() {
let arena: Arena = Arena::builder().with_capacity_local(1024).build();
let shared = arena.alloc_arc(99_u64);
assert_eq!(*shared, 99);
let join = std::thread::spawn(move || *shared);
assert_eq!(99, join.join().unwrap());
}
#[test]
fn arena_drop_tears_down_unreferenced_current_chunk() {
let arena: Arena = Arena::builder().build();
let _v = arena.alloc_rc(0_u32); drop(_v); drop(arena); }
#[test]
fn try_get_chunk_rotation_tears_down_unreferenced_chunk() {
let arena: Arena = Arena::builder().build();
static DROPS: AtomicUsize = AtomicUsize::new(0);
struct Counted(#[expect(dead_code, reason = "field present to give the type a non-zero size")] u32);
impl Drop for Counted {
fn drop(&mut self) {
let _ = DROPS.fetch_add(1, Ordering::SeqCst);
}
}
DROPS.store(0, Ordering::SeqCst);
for i in 0..200_u32 {
let h = arena.alloc_rc(Counted(i));
drop(h);
}
let drops_during_rotation = DROPS.load(Ordering::SeqCst);
assert!(drops_during_rotation > 0, "rotation-time teardown should have run destructors");
assert!(drops_during_rotation < 200, "current chunk's destructors run only at arena drop");
drop(arena);
assert_eq!(DROPS.load(Ordering::SeqCst), 200);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_box_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_box(0_u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_with_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_rc_with(|| 0_u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_copy_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_copy_rc([0_u8; 4]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_clone_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_clone_rc(&[std::string::String::from("x")]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_fill_with_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_fill_with_rc::<u32, _>(4, |i| i as u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_fill_iter_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_fill_iter_rc([1u32, 2, 3]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_box_with_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_box_with(|| 0_u32);
}
#[test]
#[cfg(feature = "dst")]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_dst_rc_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let layout = core::alloc::Layout::array::<u8>(1).unwrap();
let _ = unsafe {
arena.alloc_dst_rc::<[u8]>(layout, 1_usize, |_| {
unreachable!("init must not be called when allocation fails");
})
};
}
#[test]
#[cfg(feature = "dst")]
fn alloc_dst_rc_rejects_excessive_alignment() {
let arena: Arena = Arena::new();
let huge_align = 128 * 1024_usize;
let layout = core::alloc::Layout::from_size_align(huge_align, huge_align).unwrap();
let r = unsafe {
arena.try_alloc_dst_rc::<[u8]>(layout, 0_usize, |_| {
unreachable!("init must not be called when allocation fails");
})
};
assert!(r.is_err());
}
#[test]
fn alloc_slice_rejects_overflow() {
let arena: Arena = Arena::new();
let huge_len = usize::MAX / 2;
assert!(arena.try_alloc_slice_fill_with_rc::<u32, _>(huge_len, |i| i as u32).is_err());
}
#[test]
fn alloc_slice_rejects_isize_max() {
let arena: Arena = Arena::new();
let too_big = isize::MAX as usize;
assert!(arena.try_alloc_slice_fill_with_rc::<u8, _>(too_big, |i| i as u8).is_err());
}
#[test]
#[cfg(feature = "dst")]
fn alloc_dst_arc_rejects_excessive_alignment() {
let arena: Arena = Arena::new();
let huge_align = 128 * 1024_usize;
let layout = core::alloc::Layout::from_size_align(huge_align, huge_align).unwrap();
let r = unsafe {
arena.try_alloc_dst_arc::<[u8]>(layout, 0_usize, |_| {
unreachable!("init must not be called when allocation fails");
})
};
assert!(r.is_err());
}
#[test]
#[cfg(feature = "dst")]
fn alloc_dst_box_rejects_excessive_alignment() {
let arena: Arena = Arena::new();
let huge_align = 128 * 1024_usize;
let layout = core::alloc::Layout::from_size_align(huge_align, huge_align).unwrap();
let r = unsafe {
arena.try_alloc_dst_box::<[u8]>(layout, 0_usize, |_| {
unreachable!("init must not be called when allocation fails");
})
};
assert!(r.is_err());
}
#[repr(align(131072))]
struct HugeAlign(#[expect(dead_code, reason = "field present to give the type a non-zero size")] u8);
#[test]
fn try_alloc_with_rejects_excessive_alignment() {
let arena: Arena = Arena::new();
let result: Result<&mut HugeAlign, _> = arena.try_alloc_with(|| HugeAlign(0));
assert!(result.is_err());
}
#[test]
fn try_alloc_rc_with_rejects_excessive_alignment() {
let arena: Arena = Arena::new();
let result: Result<multitude::Rc<HugeAlign>, _> = arena.try_alloc_rc_with(|| HugeAlign(0));
assert!(result.is_err());
}
#[test]
fn try_alloc_string_with_capacity_huge_returns_err() {
let arena: Arena = Arena::new();
let too_big = usize::MAX;
assert!(arena.try_alloc_string_with_capacity(too_big).is_err());
}
#[test]
fn try_alloc_string_with_capacity_isize_max_returns_err() {
let arena: Arena = Arena::new();
let cap = (isize::MAX as usize) - 4; assert!(arena.try_alloc_string_with_capacity(cap).is_err());
}
#[test]
fn try_alloc_slice_fill_with_rc_isize_max_returns_err() {
let arena: Arena = Arena::new();
let len = (isize::MAX as usize / 8) + 1;
assert!(arena.try_alloc_slice_fill_with_rc::<u64, _>(len, |i| i as u64).is_err());
}
#[test]
fn try_alloc_slice_fill_with_rc_in_small_chunk_register_drop_oversized() {
let _arena: Arena = Arena::builder().build();
}
#[test]
fn alloc_rc_oversized_drop_type_uses_has_drop_layout() {
static DROPPED: AtomicUsize = AtomicUsize::new(0);
struct BigDrop {
_bytes: [u8; 128 * 1024],
}
impl Drop for BigDrop {
fn drop(&mut self) {
let _ = DROPPED.fetch_add(1, Ordering::SeqCst);
}
}
DROPPED.store(0, Ordering::SeqCst);
let arena: Arena = Arena::builder().build();
{
let h = arena.alloc_rc_with::<_, _>(|| BigDrop { _bytes: [0; 128 * 1024] });
assert_eq!(h._bytes[0], 0);
}
assert_eq!(DROPPED.load(Ordering::SeqCst), 1);
}
#[test]
#[cfg(feature = "dst")]
fn alloc_dst_rc_oversized_layout_succeeds() {
let arena: Arena = Arena::builder().build();
let len = 8 * 1024_usize;
let layout = core::alloc::Layout::array::<u8>(len).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[u8]>(layout, len, |fat: *mut [u8]| {
let p = fat.cast::<u8>();
for i in 0..len {
p.add(i).write(0);
}
})
};
assert_eq!(r.len(), len);
}
#[test]
fn arena_string_grow_through_chunk_rotation() {
let arena: Arena = Arena::builder().build();
let mut s = arena.alloc_string();
let chunk = "x".repeat(64);
for _ in 0..200 {
s.push_str(&chunk);
}
assert_eq!(s.len(), 200 * 64);
}
#[test]
fn arena_vec_deref_mut_modifies_in_place() {
let arena = Arena::new();
let mut v: Vec<u32, _> = arena.alloc_vec();
v.push(1);
v.push(2);
v.push(3);
let slice: &mut [u32] = &mut v;
slice[0] = 99;
assert_eq!(v.as_slice(), &[99, 2, 3]);
}
#[test]
fn collect_in_empty_iterator_uses_new_in() {
let arena = Arena::new();
let v: Vec<u32, _> = (0..10_u32).filter(|_| false).collect_in(&arena);
assert!(v.is_empty());
}
#[test]
#[cfg(feature = "dst")]
fn alloc_dst_arc_runs_drop_on_chunk_teardown() {
use core::sync::atomic::{AtomicUsize, Ordering as Ord};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
DROP_COUNT.store(0, Ord::SeqCst);
struct Tracked(#[expect(dead_code, reason = "field exists only for size")] u32);
impl Drop for Tracked {
fn drop(&mut self) {
let _ = DROP_COUNT.fetch_add(1, Ord::SeqCst);
}
}
let arena: Arena = Arena::new();
{
let layout = core::alloc::Layout::array::<Tracked>(1).unwrap();
let arc: Arc<[Tracked]> = unsafe {
arena.alloc_dst_arc::<[Tracked]>(layout, 1_usize, |fat: *mut [Tracked]| {
fat.cast::<Tracked>().write(Tracked(0xCAFE_F00D));
})
};
assert_eq!(arc.len(), 1);
let h = std::thread::spawn(move || arc.len());
let val = h.join().unwrap();
assert_eq!(val, 1);
}
drop(arena);
assert_eq!(DROP_COUNT.load(Ord::SeqCst), 1, "drop must run exactly once");
}
#[test]
fn arena_string_drop_runs_teardown_when_last_ref() {
let arena: Arena = Arena::builder().build();
let mut s = arena.alloc_string_with_capacity(2048); s.push_str("hello");
let _filler = arena.alloc_slice_copy_rc([0_u8; 1500]);
let _other = arena.alloc_rc(0_u64);
drop(s); }
#[test]
fn arena_rc_str_drop_runs_teardown_when_last_ref() {
let s: RcStr = {
let arena = Arena::new();
arena.alloc_str_rc("outlives the arena")
};
assert_eq!(&*s, "outlives the arena");
drop(s); }
#[test]
fn try_alloc_returns_err_on_failing_allocator() {
let alloc = FailingAllocator::new(0);
let arena: Arena<FailingAllocator> = Arena::new_in(alloc);
assert!(arena.try_alloc_rc(0_u32).is_err());
assert!(arena.try_alloc_box(0_u32).is_err());
assert!(arena.try_alloc_slice_copy_rc::<u8>(&[1, 2, 3]).is_err());
assert!(arena.try_alloc_slice_clone_rc::<u32>(&[1, 2, 3]).is_err());
assert!(arena.try_alloc_slice_fill_with_rc::<u32, _>(3, |i| i as u32).is_err());
assert!(arena.try_alloc_slice_fill_iter_rc([1u32, 2, 3]).is_err());
assert!(arena.try_alloc_rc_with(|| 0_u32).is_err());
assert!(arena.try_alloc_box_with(|| 0_u32).is_err());
#[cfg(feature = "dst")]
{
let layout = core::alloc::Layout::array::<u8>(1).unwrap();
let r = unsafe {
arena.try_alloc_dst_rc::<[u8]>(layout, 1_usize, |_| {
unreachable!("init must not be called when allocation fails");
})
};
assert!(r.is_err());
}
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_panics_on_failing_allocator() {
let alloc = FailingAllocator::new(0);
let arena: Arena<FailingAllocator> = Arena::new_in(alloc);
let _ = arena.alloc_rc(0_u32);
}
#[test]
fn builder_type_is_constructible() {
let _: ArenaBuilder = Arena::builder();
}
#[test]
fn arena_try_alloc_str_arc_succeeds() {
use multitude::strings::ArcStr;
let arena: Arena = Arena::new();
let s: ArcStr = arena.try_alloc_str_arc("hello arc").unwrap();
assert_eq!(s.as_str(), "hello arc");
}
#[test]
fn arena_try_alloc_str_rc_succeeds() {
let arena: Arena = Arena::new();
let s: RcStr = arena.try_alloc_str_rc("hello rc").unwrap();
assert_eq!(s.as_str(), "hello rc");
}
#[test]
fn arena_try_alloc_str_box_succeeds() {
use multitude::strings::BoxStr;
let arena: Arena = Arena::new();
let s: BoxStr = arena.try_alloc_str_box("hello box").unwrap();
assert_eq!(s.as_str(), "hello box");
}
#[test]
fn arena_box_str_as_mut_via_trait() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_str_box("abc");
let m: &mut str = AsMut::<str>::as_mut(&mut s);
unsafe { m.as_bytes_mut()[0] = b'A' };
assert_eq!(s.as_str(), "Abc");
}
#[test]
fn alloc_string_with_capacity_allocates_buffer() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string_with_capacity(64);
assert!(s.capacity() >= 64);
s.push_str("hello world");
assert_eq!(s.as_str(), "hello world");
}
#[test]
fn arena_string_into_arena_str_reclaims_slack_at_cursor() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string_with_capacity(128);
s.push_str("short");
let rc = s.into_arena_str();
assert_eq!(rc.as_str(), "short");
let _follow_on = arena.alloc_str("follow");
}
#[test]
fn try_alloc_vec_with_capacity_succeeds() {
let arena: Arena = Arena::new();
let mut v = arena.try_alloc_vec_with_capacity::<u32>(16).unwrap();
assert!(v.capacity() >= 16);
v.push(1);
v.push(2);
assert_eq!(&*v, &[1, 2]);
}
#[test]
fn arena_vec_empty_into_rc_returns_empty_slice() {
let arena: Arena = Arena::new();
let v: Vec<u32> = arena.alloc_vec();
let h = v.into_arena_rc();
assert!(h.is_empty());
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_arc_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_arc(0_u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_arc_with_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_arc_with(|| 0_u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_copy_arc_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_slice_copy_arc([0_u8; 4]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_clone_arc_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_slice_clone_arc([1_u32, 2]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_fill_with_arc_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_slice_fill_with_arc::<u32, _>(4, |i| i as u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_fill_iter_arc_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_slice_fill_iter_arc([1_u32, 2, 3]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_str_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_str("hi");
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_str_rc_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_str_rc("hi");
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_str_arc_panics_on_failing_allocator() {
let arena: Arena<SendFailingAllocator> = Arena::new_in(SendFailingAllocator::new(0));
let _ = arena.alloc_str_arc("hi");
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_str_box_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_str_box("hi");
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_string_with_capacity_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_string_with_capacity(64);
}
#[test]
#[should_panic(expected = "multitude::ArenaBuilder::build")]
fn build_panics_on_failing_allocator() {
let _: Arena<FailingAllocator> = Arena::builder()
.allocator_in(FailingAllocator::new(0))
.with_capacity_local(512)
.build();
}
#[test]
#[should_panic(expected = "multitude::ArenaBuilder::build")]
fn build_panics_on_send_failing_allocator() {
let _: Arena<SendFailingAllocator> = Arena::builder()
.allocator_in(SendFailingAllocator::new(0))
.with_capacity_local(512)
.build();
}
#[repr(align(131072))]
struct HugeAlignBox(#[expect(dead_code, reason = "field gives the type a non-zero size")] u8);
#[test]
fn try_alloc_uninit_box_rejects_excessive_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_box::<HugeAlignBox>();
assert!(r.is_err());
}
#[test]
fn arena_string_replace_range_excluded_start() {
use core::ops::Bound;
let arena: Arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("hello");
s.replace_range((Bound::Excluded(0_usize), Bound::Excluded(3_usize)), "X");
assert_eq!(&*s, "hXlo");
}
#[test]
fn arena_string_replace_range_grow_path() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("ab");
s.replace_range(0..1, "lots of replacement text");
assert_eq!(&*s, "lots of replacement textb");
}
#[test]
fn arena_string_replace_range_added_gt_removed_no_grow() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string_with_capacity(64);
s.push_str("abc");
s.replace_range(0..1, "XY"); assert_eq!(&*s, "XYbc");
}
#[test]
fn arena_string_try_reserve_additional_overflow_returns_err() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("a");
let r = s.try_reserve(usize::MAX);
assert!(r.is_err());
}
#[test]
fn arena_string_try_reserve_within_existing_capacity_is_noop() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string_with_capacity(64);
s.push_str("hi");
s.try_reserve(8).unwrap();
assert!(s.capacity() >= 64);
let mut exact = arena.alloc_string_with_capacity(8);
exact.push_str("abc");
exact.try_reserve(5).unwrap();
assert_eq!(exact.capacity(), 8);
}
#[test]
fn arena_string_try_reserve_grow_path_succeeds() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("seed");
let prior = s.capacity();
s.try_reserve(prior * 4).unwrap();
assert!(s.capacity() >= prior * 4 + s.len());
}
#[test]
fn arena_string_try_reserve_grow_path_overflow_returns_err() {
let arena: Arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("seed"); let additional = (isize::MAX as usize) - 4;
let r = s.try_reserve(additional);
assert!(r.is_err());
}
use std::panic::AssertUnwindSafe;
fn expect_panic<F: FnOnce()>(f: F) {
let r = std::panic::catch_unwind(AssertUnwindSafe(f));
assert!(r.is_err(), "expected panic but call returned");
}
fn fail_arena() -> Arena<FailingAllocator> {
Arena::new_in(FailingAllocator::new(0))
}
fn send_fail_arena() -> Arena<SendFailingAllocator> {
Arena::new_in(SendFailingAllocator::new(0))
}
#[test]
fn panic_alloc_with() {
expect_panic(|| {
let a = fail_arena();
let _: &mut u64 = a.alloc_with(|| 42);
});
}
#[test]
fn panic_alloc_str() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_str("hi");
});
}
#[test]
fn panic_alloc_slice_fill_with_rc() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_slice_fill_with_rc::<u32, _>(4, |i| i as u32);
});
}
#[test]
fn panic_alloc_slice_fill_iter() {
expect_panic(|| {
let a = fail_arena();
let _: &mut [u32] = a.alloc_slice_fill_iter([1_u32, 2, 3]);
});
}
#[test]
fn panic_alloc_uninit_box() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_uninit_box::<u32>();
});
}
#[test]
fn panic_alloc_zeroed_box() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_zeroed_box::<u32>();
});
}
#[test]
fn panic_alloc_uninit_rc() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_uninit_rc::<u32>();
});
}
#[test]
fn panic_alloc_zeroed_rc() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_zeroed_rc::<u32>();
});
}
#[test]
fn panic_alloc_uninit_arc() {
expect_panic(|| {
let a = send_fail_arena();
let _ = a.alloc_uninit_arc::<u32>();
});
}
#[test]
fn panic_alloc_zeroed_arc() {
expect_panic(|| {
let a = send_fail_arena();
let _ = a.alloc_zeroed_arc::<u32>();
});
}
#[test]
fn panic_alloc_uninit_slice_rc() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_uninit_slice_rc::<u32>(4);
});
}
#[test]
fn panic_alloc_zeroed_slice_rc() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_zeroed_slice_rc::<u32>(4);
});
}
#[test]
fn panic_alloc_uninit_slice_arc() {
expect_panic(|| {
let a = send_fail_arena();
let _ = a.alloc_uninit_slice_arc::<u32>(4);
});
}
#[test]
fn panic_alloc_zeroed_slice_arc() {
expect_panic(|| {
let a = send_fail_arena();
let _ = a.alloc_zeroed_slice_arc::<u32>(4);
});
}
#[test]
fn try_alloc_str_err() {
let a = fail_arena();
assert!(a.try_alloc_str("hi").is_err());
}
#[test]
fn try_alloc_uninit_box_err() {
let a = fail_arena();
assert!(a.try_alloc_uninit_box::<u32>().is_err());
}
#[test]
fn try_alloc_zeroed_box_err() {
let a = fail_arena();
assert!(a.try_alloc_zeroed_box::<u32>().is_err());
}
#[test]
fn try_alloc_uninit_rc_err() {
let a = fail_arena();
assert!(a.try_alloc_uninit_rc::<u32>().is_err());
}
#[test]
fn try_alloc_zeroed_rc_err() {
let a = fail_arena();
assert!(a.try_alloc_zeroed_rc::<u32>().is_err());
}
#[test]
fn try_alloc_uninit_arc_err() {
let a = send_fail_arena();
assert!(a.try_alloc_uninit_arc::<u32>().is_err());
}
#[test]
fn try_alloc_zeroed_arc_err() {
let a = send_fail_arena();
assert!(a.try_alloc_zeroed_arc::<u32>().is_err());
}
#[test]
fn try_alloc_uninit_slice_rc_err() {
let a = fail_arena();
assert!(a.try_alloc_uninit_slice_rc::<u32>(4).is_err());
}
#[test]
fn try_alloc_zeroed_slice_rc_err() {
let a = fail_arena();
assert!(a.try_alloc_zeroed_slice_rc::<u32>(4).is_err());
}
#[test]
fn try_alloc_uninit_slice_arc_err() {
let a = send_fail_arena();
assert!(a.try_alloc_uninit_slice_arc::<u32>(4).is_err());
}
#[test]
fn try_alloc_zeroed_slice_arc_err() {
let a = send_fail_arena();
assert!(a.try_alloc_zeroed_slice_arc::<u32>(4).is_err());
}
#[test]
fn try_alloc_uninit_slice_rc_drop_type_err() {
let a = fail_arena();
assert!(a.try_alloc_uninit_slice_rc::<String>(2).is_err());
}
#[test]
fn try_alloc_slice_fill_with_rc_drop_type_err() {
let a = fail_arena();
assert!(a.try_alloc_slice_fill_with_rc::<String, _>(2, |i| format!("{i}")).is_err());
}
#[test]
fn arena_string_try_push_str_initial_alloc_err() {
let a = fail_arena();
let mut s = multitude::strings::String::new_in(&a);
assert!(s.try_push_str("hello").is_err());
}
#[test]
fn arena_string_try_grow_to_at_least_grow_path_err() {
let a = Arena::builder().allocator_in(FailingAllocator::new(1)).build();
let mut s = multitude::strings::String::try_with_capacity_in(4, &a).unwrap();
s.try_push_str("abcd").unwrap();
assert!(s.try_reserve(64 * 1024).is_err());
}
#[test]
fn panic_arena_string_grow_to_at_least() {
expect_panic(|| {
let a = Arena::builder().allocator_in(FailingAllocator::new(1)).build();
let mut s = multitude::strings::String::try_with_capacity_in(4, &a).unwrap();
s.try_push_str("abcd").unwrap();
s.push_str("x".repeat(64 * 1024));
});
}
#[test]
fn grow_for_string_old_chunk_torn_down() {
let a = Arena::builder().build();
let mut s = a.alloc_string();
s.push_str("x".repeat(64));
s.push_str("y".repeat(8 * 1024));
drop(s);
}
#[test]
fn oversized_no_drop_branch() {
let a = Arena::builder().max_normal_alloc(4 * 1024).build();
let _s = a.alloc_slice_copy(&[0_u8; 1500][..]);
}
#[test]
fn oversized_with_drop_branch() {
let a = Arena::builder().max_normal_alloc(4 * 1024).build();
let _s = a.alloc_slice_fill_with_rc::<String, _>(64, |i| format!("{i}"));
}
#[test]
fn panic_alloc_slice_fill_with() {
expect_panic(|| {
let a = fail_arena();
let _: &mut [u32] = a.alloc_slice_fill_with(4, |i| i as u32);
});
}
#[test]
fn arena_vec_into_arena_rc_empty_drop_type() {
let arena: Arena = Arena::new();
let v: Vec<String> = Vec::new_in(&arena);
let r: multitude::Rc<[String]> = v.into_arena_rc();
assert!(r.is_empty());
}
#[test]
fn vec_try_reserve_no_growth_needed() {
let arena = Arena::new();
let mut v: Vec<u32> = Vec::new_in(&arena);
v.push(1);
v.push(2);
assert!(v.try_reserve(1).is_ok());
assert_eq!(v.len(), 2);
}
#[test]
fn vec_try_reserve_exact_realloc_and_overflow() {
let arena = Arena::new();
let mut v: Vec<u32> = Vec::new_in(&arena);
v.push(1);
assert!(v.try_reserve_exact(100).is_ok());
assert!(v.capacity() >= 101);
assert!(v.try_reserve_exact(1).is_ok());
let err = v.try_reserve_exact(usize::MAX);
assert!(err.is_err());
}
#[test]
fn vec_resize_with_shrink() {
let arena = Arena::new();
let mut v: Vec<u32> = Vec::new_in(&arena);
for i in 0..10 {
v.push(i);
}
v.resize_with(3, || unreachable!());
assert_eq!(v.len(), 3);
assert_eq!(&*v, &[0, 1, 2]);
}
#[test]
fn vec_drain_with_exclusive_start_and_inclusive_end() {
use core::ops::Bound;
let arena = Arena::new();
let mut v: Vec<u32> = Vec::new_in(&arena);
for i in 0..10 {
v.push(i);
}
let drained: std::vec::Vec<_> = v.drain((Bound::Excluded(0), Bound::Included(3))).collect();
assert_eq!(drained, vec![1, 2, 3]);
assert_eq!(v.len(), 7);
let arena2 = Arena::new();
let mut v2: Vec<u32> = Vec::new_in(&arena2);
for i in 0..5 {
v2.push(i);
}
let drained2: std::vec::Vec<_> = v2.drain(..).collect();
assert_eq!(drained2, vec![0, 1, 2, 3, 4]);
assert_eq!(v2.len(), 0);
}
#[test]
fn vec_zst_operations() {
let arena = Arena::new();
let mut v: Vec<()> = Vec::new_in(&arena);
for _ in 0..100 {
v.push(());
}
assert_eq!(v.len(), 100);
v.shrink_to_fit();
assert_eq!(v.len(), 100);
}
#[test]
fn vec_drain_debug_and_next_back() {
let arena = Arena::new();
let mut v: Vec<u32> = Vec::new_in(&arena);
for i in 0..5 {
v.push(i);
}
let mut drain = v.drain(1..4);
let s = std::format!("{drain:?}");
assert!(s.contains("Drain"), "Debug output: {s}");
assert!(s.contains("remaining"), "Debug output: {s}");
assert_eq!(drain.next_back(), Some(3));
assert_eq!(drain.next_back(), Some(2));
assert_eq!(drain.next(), Some(1));
assert_eq!(drain.next_back(), None);
}
#[test]
fn vec_insert_triggers_growth() {
let arena = Arena::new();
let mut v: Vec<u32> = Vec::new_in(&arena);
for i in 0..4 {
v.push(i);
}
assert_eq!(v.capacity(), 4);
v.insert(2, 99);
assert_eq!(v[2], 99);
assert!(v.capacity() > 4);
}
#[test]
fn vec_push_panics_on_alloc_failure() {
expect_panic(|| {
let arena = Arena::new_in(FailingAllocator::new(1)); let mut v: Vec<u64, _> = Vec::new_in(&arena);
for _ in 0..if cfg!(miri) { 100 } else { 10_000 } {
v.push(0);
}
});
}
#[test]
fn vec_reserve_panics_on_alloc_failure() {
expect_panic(|| {
let arena = Arena::new_in(FailingAllocator::new(0));
let mut v: Vec<u64, _> = Vec::new_in(&arena);
v.reserve(1);
});
}
#[test]
fn vec_reserve_exact_panics_on_alloc_failure() {
expect_panic(|| {
let arena = Arena::new_in(FailingAllocator::new(0));
let mut v: Vec<u64, _> = Vec::new_in(&arena);
v.reserve_exact(1);
});
}
#[test]
fn shared_bump_fast_path_bail_on_oversize() {
let arena = Arena::builder().max_normal_alloc(4096).build();
let arc = arena.alloc_arc([0_u64; 1024]); assert_eq!(arc[0], 0);
}
#[test]
fn shared_bump_fit_in_current_chunk() {
let arena = Arena::new();
let _a1 = arena.alloc_arc(1_u32);
let _a2 = arena.alloc_arc(2_u32);
}
#[test]
fn shared_oversized_inc_ref_on_non_normal_chunk() {
let arena = Arena::builder().max_normal_alloc(4096).build();
let data = [42_u8; 8192]; let arc_slice = arena.alloc_slice_copy_arc(&data[..]);
assert_eq!(arc_slice.len(), 8192);
assert_eq!(arc_slice[0], 42);
}
#[test]
fn shared_eviction_of_pinned_chunk() {
let arena = Arena::builder().build();
let mut s = arena.alloc_string();
let n = if cfg!(miri) { 500 } else { 10_000 };
for _ in 0..n {
s.push('A'); }
assert!(s.len() >= n);
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_copy_rejects_overaligned() {
#[repr(align(131072))]
#[derive(Clone, Copy)]
#[expect(dead_code, reason = "field needed for alignment/size but not read")]
struct HugeAlign(u8);
let arena = Arena::new();
let data = [HugeAlign(0)];
let result = arena.try_alloc_slice_copy(&data[..]);
assert!(result.is_err());
}
#[test]
fn try_alloc_slice_copy_rejects_overflow() {
let arena = Arena::new();
let result = arena.try_alloc_slice_fill_with::<u64, _>(usize::MAX / 4, |_| 0);
assert!(result.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_fill_with_rejects_overaligned() {
#[repr(align(131072))]
struct HugeAlignDrop(#[expect(dead_code, reason = "field needed for alignment/size but not read")] u8);
#[expect(clippy::empty_drop, reason = "Drop impl makes needs_drop::<T>() true for test")]
impl Drop for HugeAlignDrop {
fn drop(&mut self) {}
}
let arena = Arena::new();
let result = arena.try_alloc_slice_fill_with::<HugeAlignDrop, _>(1, |_| HugeAlignDrop(0));
assert!(result.is_err());
}
#[test]
fn try_alloc_slice_fill_with_no_drop_fast_path() {
let arena = Arena::new();
let result = arena.try_alloc_slice_fill_with::<u32, _>(10, |i| i as u32);
assert!(result.is_ok());
let slice = result.unwrap();
assert_eq!(slice.len(), 10);
assert_eq!(slice[5], 5);
}
#[test]
fn try_alloc_slice_fill_with_overflow() {
let arena = Arena::new();
let result = arena.try_alloc_slice_fill_with::<u64, _>(usize::MAX / 4, |_| 0);
assert!(result.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_copy_inner_rejects_overaligned() {
#[repr(align(131072))]
#[derive(Clone, Copy)]
#[expect(dead_code, reason = "field needed for alignment/size but not read")]
struct HugeAlign(u8);
let arena = Arena::new();
let data = [HugeAlign(0)];
let result = arena.try_alloc_slice_copy_arc(&data[..]);
assert!(result.is_err());
let result2 = arena.try_alloc_slice_copy_rc(&data[..]);
assert!(result2.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn acquire_slice_slot_rejects_overaligned() {
#[repr(align(131072))]
#[derive(Clone)]
struct HugeAlign(#[expect(dead_code, reason = "field needed for alignment/size but not read")] u8);
let arena = Arena::new();
let result = arena.try_alloc_slice_fill_with_rc::<HugeAlign, _>(1, |_| HugeAlign(0));
assert!(result.is_err());
}
#[test]
fn reserve_slice_overflow() {
let arena = Arena::new();
let len = (isize::MAX as usize) / 8 + 1; let result = arena.try_alloc_slice_fill_with_rc::<u64, _>(len, |_| 0);
assert!(result.is_err());
}
#[test]
fn alloc_slice_fill_with_overflow() {
let arena = Arena::new();
let len = (isize::MAX as usize) / 8 + 1;
let result = arena.try_alloc_slice_fill_with::<u64, _>(len, |_| 0);
assert!(result.is_err());
}
#[test]
fn alloc_slice_fill_with_non_drop_fast_path() {
let arena = Arena::new();
let _ = arena.alloc_slice_fill_with::<u32, _>(4, |i| i as u32);
let slice = arena.alloc_slice_fill_with::<u32, _>(4, |i| (i + 10) as u32);
assert_eq!(slice, &[10, 11, 12, 13]);
}
#[test]
fn reserve_slice_oversized_inc_ref() {
let arena = Arena::builder().max_normal_alloc(4096).build();
let mut v: Vec<std::string::String, _> = Vec::new_in(&arena);
for i in 0..200 {
v.push(format!("item_{i}"));
}
let rc = v.into_arena_rc();
assert_eq!(rc.len(), 200);
assert_eq!(&*rc[0], "item_0");
}
#[test]
fn try_reserve_uninit_fast_path_with_drop_type() {
let arena = Arena::new();
let _first = arena.try_alloc_uninit_rc::<std::string::String>().unwrap();
let _second = arena.try_alloc_uninit_rc::<std::string::String>().unwrap();
}
#[test]
fn slice_init_guard_drops_prefix_on_panic() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone)]
#[expect(dead_code, reason = "field needed for alignment/size but not read")]
struct Tracked(u32);
impl Drop for Tracked {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::Relaxed);
}
}
DROP_COUNT.store(0, Ordering::Relaxed);
let arena = Arena::new();
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
let _ = arena.alloc_slice_fill_with::<Tracked, _>(5, |i| {
assert!(i != 3, "deliberate panic at index 3");
Tracked(i as u32)
});
}));
assert!(result.is_err());
assert!(DROP_COUNT.load(Ordering::Relaxed) >= 3);
}
#[test]
fn alloc_slice_copy_fast_path_bump() {
let arena = Arena::new();
let _x: &mut u8 = arena.alloc(42_u8);
let s = arena.alloc_slice_copy([1_u8, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(s, &[1, 2, 3, 4, 5, 6, 7, 8]);
}
#[cfg(not(target_os = "windows"))]
#[repr(align(32768))]
#[derive(Clone, Copy)]
struct HalfChunkAlignNoDrop(#[expect(dead_code, reason = "field gives the type a non-zero size")] u8);
#[repr(align(32768))]
struct HalfChunkAlignDrop(#[expect(dead_code, reason = "field gives the type a non-zero size")] u8);
#[expect(clippy::empty_drop, reason = "Drop impl makes needs_drop::<T>() true for the test")]
impl Drop for HalfChunkAlignDrop {
fn drop(&mut self) {}
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_rc_with_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r: Result<multitude::Rc<HalfChunkAlignDrop>, _> = arena.try_alloc_rc_with(|| HalfChunkAlignDrop(0));
assert!(r.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_arc_with_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r: Result<multitude::Arc<HalfChunkAlignDrop>, _> = arena.try_alloc_arc_with(|| HalfChunkAlignDrop(0));
assert!(r.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_box_with_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r: Result<multitude::Box<HalfChunkAlignDrop>, _> = arena.try_alloc_box_with(|| HalfChunkAlignDrop(0));
assert!(r.is_err());
}
#[test]
fn try_alloc_uninit_box_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_box::<HalfChunkAlignDrop>();
assert!(r.is_err());
}
#[test]
fn try_alloc_uninit_rc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_rc::<HalfChunkAlignDrop>();
assert!(r.is_err());
}
#[test]
fn try_alloc_uninit_arc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_arc::<HalfChunkAlignDrop>();
assert!(r.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_fill_with_rc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_slice_fill_with_rc::<HalfChunkAlignDrop, _>(1, |_| HalfChunkAlignDrop(0));
assert!(r.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_fill_with_arc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_slice_fill_with_arc::<HalfChunkAlignDrop, _>(1, |_| HalfChunkAlignDrop(0));
assert!(r.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_fill_with_box_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_slice_fill_with_box::<HalfChunkAlignDrop, _>(1, |_| HalfChunkAlignDrop(0));
assert!(r.is_err());
}
#[test]
fn try_alloc_uninit_slice_rc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_slice_rc::<HalfChunkAlignDrop>(1);
assert!(r.is_err());
}
#[test]
fn try_alloc_uninit_slice_arc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_slice_arc::<HalfChunkAlignDrop>(1);
assert!(r.is_err());
}
#[test]
fn try_alloc_uninit_slice_box_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let r = arena.try_alloc_uninit_slice_box::<HalfChunkAlignDrop>(1);
assert!(r.is_err());
}
#[test]
#[cfg(not(target_os = "windows"))]
fn try_alloc_slice_copy_arc_allows_half_chunk_align_for_copy_t() {
let arena: Arena = Arena::new();
let data = [HalfChunkAlignNoDrop(0), HalfChunkAlignNoDrop(1)];
let r = arena.try_alloc_slice_copy_arc(&data[..]);
assert!(r.is_ok());
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
#[cfg(not(target_os = "windows"))]
fn vec_into_arena_rc_panics_on_half_chunk_align_drop() {
let arena: Arena = Arena::new();
let mut v: multitude::vec::Vec<HalfChunkAlignDrop> = arena.alloc_vec_with_capacity(1);
v.push(HalfChunkAlignDrop(0));
let _rc = v.into_arena_rc();
}
#[test]
fn alloc_rc_with_closure_panic_releases_refcount() {
use std::panic::AssertUnwindSafe;
let arena: Arena = Arena::new();
let _stable = arena.alloc_rc(0_u32);
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
let _: multitude::Rc<u64> = arena.alloc_rc_with(|| panic!("deliberate panic in alloc_rc_with"));
}));
assert!(result.is_err());
let after = arena.alloc_rc(99_u32);
assert_eq!(*after, 99);
}
#[test]
fn alloc_arc_with_closure_panic_releases_refcount() {
use std::panic::AssertUnwindSafe;
let arena: Arena = Arena::new();
let _stable = arena.alloc_arc(0_u32);
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
let _: multitude::Arc<u64> = arena.alloc_arc_with(|| panic!("deliberate panic in alloc_arc_with"));
}));
assert!(result.is_err());
let after = arena.alloc_arc(99_u32);
assert_eq!(*after, 99);
}
#[test]
fn alloc_box_with_closure_panic_releases_refcount() {
use std::panic::AssertUnwindSafe;
let arena: Arena = Arena::new();
let _stable = arena.alloc_box(0_u32);
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
let _: multitude::Box<u64> = arena.alloc_box_with(|| panic!("deliberate panic in alloc_box_with"));
}));
assert!(result.is_err());
let after = arena.alloc_box(99_u32);
assert_eq!(*after, 99);
}
#[test]
fn alloc_rc_with_panicking_closure_does_not_run_drop() {
use std::panic::AssertUnwindSafe;
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
DROP_COUNT.store(0, Ordering::Relaxed);
struct Tracked;
impl Drop for Tracked {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::Relaxed);
}
}
let arena: Arena = Arena::new();
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
let _: multitude::Rc<Tracked> = arena.alloc_rc_with(|| panic!("panic before producing the value"));
}));
assert!(result.is_err());
drop(arena);
assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 0);
}
#[test]
fn drain_cache_runs_on_size_class_promotion() {
let arena: Arena = Arena::builder().max_normal_alloc(8 * 1024).build();
{
let _r: multitude::Rc<u8> = arena.alloc_rc(0_u8);
let _a: multitude::Arc<u8> = arena.alloc_arc(0_u8);
}
let _big: multitude::Rc<[u8; 7 * 1024]> = arena.alloc_rc([0_u8; 7 * 1024]);
}
#[test]
fn byte_budget_exact_fit_succeeds() {
let arena: Arena = Arena::builder().byte_budget(1024).build();
let r: Result<multitude::Rc<u8>, _> = arena.try_alloc_rc(0_u8);
assert!(r.is_ok());
}
#[test]
fn byte_budget_strict_excess_fails_at_second_chunk() {
let arena: Arena = Arena::builder().byte_budget(1024).build();
let mut held = std::vec::Vec::new();
let mut hit_err = false;
for _ in 0..2000_u32 {
if let Ok(h) = arena.try_alloc_rc(0_u8) {
held.push(h);
} else {
hit_err = true;
break;
}
}
assert!(hit_err);
}
#[test]
fn drain_cache_pops_wrong_class_chunks_after_promotion() {
let arena: Arena = Arena::builder().max_normal_alloc(8 * 1024).build();
let big_arc: multitude::Arc<[u8; 900]> = arena.alloc_arc([0_u8; 900]);
drop(big_arc);
let _small_arc: multitude::Arc<[u8; 200]> = arena.alloc_arc([0_u8; 200]);
{
let _r: multitude::Rc<u8> = arena.alloc_rc(0_u8);
}
let _big: multitude::Rc<[u8; 7 * 1024]> = arena.alloc_rc([0_u8; 7 * 1024]);
}
#[test]
fn vec_resize_clones_exactly_extra_minus_one() {
use core::sync::atomic::{AtomicUsize, Ordering};
static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
CLONE_COUNT.store(0, Ordering::Relaxed);
#[derive(Default)]
struct CloneCounter;
impl Clone for CloneCounter {
fn clone(&self) -> Self {
CLONE_COUNT.fetch_add(1, Ordering::Relaxed);
Self
}
}
let arena: Arena = Arena::new();
let mut v: multitude::vec::Vec<CloneCounter> = arena.alloc_vec();
v.push(CloneCounter);
v.push(CloneCounter);
assert_eq!(CLONE_COUNT.load(Ordering::Relaxed), 0);
v.resize(5, CloneCounter);
assert_eq!(v.len(), 5);
assert_eq!(CLONE_COUNT.load(Ordering::Relaxed), 2);
}
}
mod coverage_more {
#![allow(clippy::clone_on_ref_ptr, reason = "tests prefer concise method-call form")]
#![allow(clippy::std_instead_of_core, reason = "tests use std")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::large_stack_arrays, reason = "test allocations are intentional")]
#![allow(clippy::collection_is_never_read, reason = "tests retain smart pointers to keep chunks alive")]
#![allow(unused_results, reason = "test code")]
#![allow(clippy::used_underscore_binding, reason = "intentional drop-after binding")]
#![allow(clippy::cast_possible_truncation, reason = "test data is small")]
#![allow(clippy::explicit_into_iter_loop, reason = "test clarity")]
#![allow(clippy::assertions_on_result_states, reason = "tests deliberately assert error returns")]
#![allow(clippy::items_after_statements, reason = "test-local statics next to their use")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "test code")]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "tests group related unsafe ops")]
use core::alloc::Layout;
use std::panic::{self, AssertUnwindSafe};
use std::sync::atomic::{AtomicUsize, Ordering};
use allocator_api2::alloc::Allocator;
use multitude::strings::String as ArenaString;
use multitude::vec::Vec as ArenaVec;
use multitude::{Arc, Arena, ArenaBuilder, Rc};
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
use crate::common::{FailingAllocator, SendFailingAllocator};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Droppy(&'static str);
impl Drop for Droppy {
fn drop(&mut self) {
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
#[derive(Clone)]
struct DropZst;
impl Drop for DropZst {
fn drop(&mut self) {
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
#[repr(align(32768))]
struct TooAligned;
#[test]
fn arc_from_arena_vec_uses_into_arena_arc() {
let arena = Arena::new();
let mut v: ArenaVec<'_, i32> = arena.alloc_vec();
v.push(1);
v.push(2);
let a: Arc<[i32]> = v.into();
assert_eq!(&*a, &[1, 2]);
}
#[test]
fn rc_and_arc_slice_assume_init_with_drop_types_retarget_drop_entries() {
let arena = Arena::new();
let rc_uninit = arena.alloc_uninit_slice_rc::<Droppy>(2);
unsafe {
let base = Rc::as_ptr(&rc_uninit).cast::<core::mem::MaybeUninit<Droppy>>().cast_mut();
(*base.add(0)).write(Droppy("rc-a"));
(*base.add(1)).write(Droppy("rc-b"));
}
let rc = unsafe { rc_uninit.assume_init() };
assert_eq!(rc[0].0, "rc-a");
let arc_uninit = arena.alloc_uninit_slice_arc::<Droppy>(2);
unsafe {
let base = Arc::as_ptr(&arc_uninit).cast::<core::mem::MaybeUninit<Droppy>>().cast_mut();
(*base.add(0)).write(Droppy("arc-a"));
(*base.add(1)).write(Droppy("arc-b"));
}
let arc = unsafe { arc_uninit.assume_init() };
assert_eq!(arc[1].0, "arc-b");
}
#[test]
fn rc_and_arc_single_assume_init_with_drop_types_retarget_drop_entries() {
let arena = Arena::new();
let rc_uninit = arena.alloc_uninit_rc::<Droppy>();
unsafe {
Rc::as_ptr(&rc_uninit)
.cast_mut()
.write(core::mem::MaybeUninit::new(Droppy("rc-one")));
}
let rc = unsafe { rc_uninit.assume_init() };
assert_eq!(rc.0, "rc-one");
let arc_uninit = arena.alloc_uninit_arc::<Droppy>();
unsafe {
Arc::as_ptr(&arc_uninit)
.cast_mut()
.write(core::mem::MaybeUninit::new(Droppy("arc-one")));
}
let arc = unsafe { arc_uninit.assume_init() };
assert_eq!(arc.0, "arc-one");
}
#[test]
fn builder_preallocate_shared_releases_budget_on_allocator_error() {
assert!(
ArenaBuilder::new_in(SendFailingAllocator::new(0))
.with_capacity_shared(512)
.try_build()
.is_err()
);
}
#[test]
fn oversized_shared_alloc_error_releases_budget() {
let arena = ArenaBuilder::new_in(SendFailingAllocator::new(0)).max_normal_alloc(4096).build();
let src = std::vec![7_u8; 5000];
assert!(arena.try_alloc_slice_copy_arc(src).is_err());
}
#[test]
fn shared_cache_discards_too_small_chunk_before_large_request() {
let arena = ArenaBuilder::new().with_capacity_shared(512).build();
let big = std::vec![3_u8; 4096];
let a = arena.alloc_slice_copy_arc(&big);
assert_eq!(a.len(), big.len());
}
#[test]
fn preallocate_local_updates_high_water_on_larger_class() {
let arena = ArenaBuilder::new().with_capacity_local(1024).build();
let value = arena.alloc(42_u32);
assert_eq!(*value, 42);
}
#[test]
fn local_ref_dropped_after_arena_uses_destroy_fallback() {
let rc = {
let arena = Arena::new();
arena.alloc_rc(Droppy("late"))
};
assert_eq!(rc.0, "late");
drop(rc);
}
#[test]
fn string_retain_panic_restores_guard_len() {
let arena = Arena::new();
let mut s = ArenaString::from_str_in("abcd", &arena);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
s.retain(|ch| {
assert_ne!(ch, 'd', "retain must stop at the panic");
assert!(ch != 'c', "predicate panic");
ch != 'b'
});
}));
assert!(result.is_err());
assert_eq!(s.as_str(), "a");
}
#[test]
fn vec_retain_panic_preserves_kept_prefix() {
use std::cell::Cell;
let arena = Arena::new();
let mut v: multitude::vec::Vec<'_, i32> = arena.alloc_vec();
v.extend([1_i32, 2, 3, 4, 5]);
let seen = Cell::new(0_i32);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
v.retain(|x| {
seen.set(seen.get() + 1);
assert!(*x != 3, "predicate panic at element 3");
*x % 2 == 1
});
}));
assert!(result.is_err());
assert!(
!v.is_empty(),
"kept prefix [1, ...] must survive the panic; std::Vec::retain has the same contract"
);
assert_eq!(v[0], 1, "element 1 must be retained");
}
#[test]
fn vec_dedup_panic_preserves_kept_prefix() {
let arena = Arena::new();
let mut v: multitude::vec::Vec<'_, i32> = arena.alloc_vec();
v.extend([1_i32, 1, 2, 2, 3, 3]);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
v.dedup_by(|a, _b| {
assert!(*a != 3, "dedup panic");
false
});
}));
assert!(result.is_err());
assert!(!v.is_empty(), "Vec must not be fully wiped on dedup-predicate panic");
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn string_push_panics_on_allocator_error() {
let arena = ArenaBuilder::new_in(FailingAllocator::new(0)).build();
let mut s = arena.alloc_string();
s.push('x');
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn string_reserve_panics_on_allocator_error() {
let arena = ArenaBuilder::new_in(FailingAllocator::new(0)).build();
let mut s = arena.alloc_string();
s.reserve(128);
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn string_replace_range_panics_from_grow_to_at_least() {
let arena = ArenaBuilder::new_in(FailingAllocator::new(1)).build();
let mut s = ArenaString::from_str_in("a", &arena);
let replacement = "x".repeat(70_000);
s.replace_range(0..1, &replacement);
}
#[test]
fn string_reserve_zero_on_nonempty_string_is_noop() {
let arena = Arena::new();
let mut s = ArenaString::from_str_in("already allocated", &arena);
let cap = s.capacity();
s.reserve(0);
assert_eq!(s.capacity(), cap);
assert_eq!(s.as_str(), "already allocated");
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn vec_with_capacity_panics_on_allocator_error() {
let arena = ArenaBuilder::new_in(FailingAllocator::new(0)).build();
let _v: ArenaVec<'_, u8, _> = ArenaVec::with_capacity_in(8, &arena);
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn vec_into_arena_arc_panics_on_shared_allocator_error() {
let arena = ArenaBuilder::new_in(SendFailingAllocator::new(1)).build();
let mut v: ArenaVec<'_, u8, _> = ArenaVec::with_capacity_in(4, &arena);
v.extend([1, 2, 3, 4]);
let _arc = v.into_arena_arc();
}
#[test]
fn vec_into_arena_box_copy_handles_zst_fallback() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<()>();
for _ in 0..16 {
v.push(());
}
let b = v.into_arena_box();
assert_eq!(b.len(), 16);
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn vec_into_arena_box_copy_panics_on_zst_drop_alloc_error() {
let arena = ArenaBuilder::new_in(FailingAllocator::new(0)).build();
let mut v = arena.alloc_vec::<DropZst>();
v.extend([DropZst, DropZst, DropZst]);
let _ = v.into_arena_box();
}
#[test]
fn vec_into_arena_box_falls_back_when_drop_entry_install_misses() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<Droppy>();
v.extend([Droppy("a"), Droppy("b")]);
let _decoy = arena.alloc_slice_fill_with(70_000, |i| i as u8);
let b = v.into_arena_box();
assert_eq!(b.len(), 2);
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn vec_into_arena_box_panics_when_drop_slice_is_too_long_for_entry() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<Droppy>();
v.extend((0..=u16::MAX).map(|_| Droppy("many")));
let _ = v.into_arena_box();
}
#[test]
fn vec_resize_moves_final_clone_source_into_last_slot() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<std::string::String>();
v.resize(3, "x".to_owned());
assert_eq!(&*v, &["x", "x", "x"]);
}
#[test]
fn vec_realloc_edge_cases_are_observable_through_public_api() {
let arena = Arena::new();
let mut v = ArenaVec::with_capacity_in(8, &arena);
v.extend([1_u32, 2, 3, 4]);
v.reserve_exact(0);
assert!(v.capacity() >= 8);
v.reserve(32);
assert_eq!(&*v, &[1, 2, 3, 4]);
v.clear();
v.shrink_to_fit();
assert_eq!(v.capacity(), 0);
}
#[test]
fn vec_shrink_to_fit_oversized_chunk_is_a_noop() {
let arena = ArenaBuilder::new_in(FailingAllocator::new(1)).max_normal_alloc(4096).build();
let mut v = ArenaVec::with_capacity_in(70_000, &arena);
let cap_before = v.capacity();
v.extend([1_u32, 2, 3, 4]);
v.shrink_to_fit();
assert_eq!(v.capacity(), cap_before);
assert_eq!(v.len(), 4);
}
#[test]
#[should_panic(expected = "allocator returned AllocError")]
fn vec_into_arena_rc_panics_when_drop_slice_is_too_long_for_entry() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<Droppy>();
v.extend((0..=u16::MAX).map(|_| Droppy("many")));
let _ = v.into_arena_rc();
}
#[test]
fn arena_allocator_grow_falls_back_when_in_place_growth_is_ineligible() {
let arena = Arena::new();
let alloc = &arena;
let old = Layout::from_size_align(8, 8).unwrap();
let ptr = alloc.allocate(old).unwrap().cast::<u8>();
let different_align = Layout::from_size_align(16, 16).unwrap();
let grown = unsafe { Allocator::grow(&alloc, ptr, old, different_align) }.unwrap();
unsafe { Allocator::deallocate(&alloc, grown.cast(), different_align) };
let old = Layout::from_size_align(16, 8).unwrap();
let ptr = alloc.allocate(old).unwrap().cast::<u8>();
let smaller = Layout::from_size_align(8, 8).unwrap();
let shrunk = unsafe { Allocator::grow(&alloc, ptr, old, smaller) }.unwrap();
unsafe { Allocator::deallocate(&alloc, shrunk.cast(), smaller) };
}
#[test]
fn arena_slice_fill_iter_drop_paths_for_ref_rc_arc_and_box() {
let arena = Arena::new();
let r = arena.alloc_slice_fill_iter([Droppy("a"), Droppy("b")]);
assert_eq!(r[1].0, "b");
let rc = arena.alloc_slice_fill_iter_rc([Droppy("c"), Droppy("d")]);
assert_eq!(rc[0].0, "c");
let arc = arena.alloc_slice_fill_iter_arc([Droppy("e"), Droppy("f")]);
assert_eq!(arc[1].0, "f");
let bx = arena.alloc_slice_fill_iter_box([Droppy("g"), Droppy("h")]);
assert_eq!(bx[0].0, "g");
}
#[test]
fn arena_slice_clone_no_drop_branch() {
let arena = Arena::new();
let values = [10_u32, 20, 30];
let cloned = arena.alloc_slice_clone(values);
assert_eq!(cloned, &mut [10, 20, 30]);
}
#[test]
fn arena_rejects_overaligned_smart_pointer_allocations() {
let arena = Arena::new();
assert!(arena.try_alloc_uninit_rc::<TooAligned>().is_err());
assert!(arena.try_alloc_uninit_arc::<TooAligned>().is_err());
assert!(arena.try_alloc_uninit_slice_arc::<TooAligned>(1).is_err());
#[cfg(not(windows))]
{
assert!(arena.try_alloc_rc(TooAligned).is_err());
assert!(arena.try_alloc_arc(TooAligned).is_err());
assert!(arena.try_alloc_box(TooAligned).is_err());
}
}
#[test]
fn arena_rejects_too_many_drop_entries_for_smart_slices() {
let arena = Arena::new();
let too_many = u16::MAX as usize + 1;
assert!(arena.try_alloc_slice_fill_with_rc(too_many, |_| Droppy("rc")).is_err());
assert!(arena.try_alloc_slice_fill_with_arc(too_many, |_| Droppy("arc")).is_err());
}
#[test]
fn arena_box_value_larger_than_normal_chunk_uses_slow_path() {
let arena = Arena::new();
let boxed = arena.try_alloc_box([7_u8; 70_000]).unwrap();
assert_eq!(boxed[0], 7);
assert_eq!(boxed[69_999], 7);
let rc = arena.try_alloc_rc([3_u8; 70_000]).unwrap();
assert_eq!(rc[0], 3);
assert_eq!(rc[69_999], 3);
}
#[test]
fn shared_refill_preserves_reentrant_drop_allocation() {
static REENTERED: AtomicUsize = AtomicUsize::new(0);
REENTERED.store(0, Ordering::SeqCst);
struct ReentrantDrop {
arena: *const Arena,
}
unsafe impl Send for ReentrantDrop {}
unsafe impl Sync for ReentrantDrop {}
impl Drop for ReentrantDrop {
fn drop(&mut self) {
let arena = unsafe { &*self.arena };
let value = arena.alloc_arc(0xCAFE_u64);
assert_eq!(*value, 0xCAFE);
REENTERED.fetch_add(1, Ordering::SeqCst);
}
}
let arena = Arena::new();
let arena_ptr: *const Arena = &raw const arena;
let reentrant = arena.alloc_arc(ReentrantDrop { arena: arena_ptr });
drop(reentrant);
for i in 0_u8..32 {
let filler = arena.alloc_arc([i; 4096]);
drop(filler);
}
let outer = arena.alloc_arc([0x55_u8; 4096]);
assert_eq!(outer[0], 0x55);
assert_eq!(REENTERED.load(Ordering::SeqCst), 1);
}
}
mod coverage_complete {
#![allow(clippy::clone_on_ref_ptr, reason = "tests prefer concise method-call form")]
#![allow(clippy::std_instead_of_core, reason = "tests use std")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::large_stack_arrays, reason = "test allocations are intentional")]
#![allow(clippy::collection_is_never_read, reason = "tests retain smart pointers to keep chunks alive")]
#![allow(unused_results, reason = "test code")]
#![allow(clippy::used_underscore_binding, reason = "intentional drop-after binding")]
#![allow(clippy::cast_possible_truncation, reason = "test data is small")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "test code")]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "tests group related unsafe ops")]
#![allow(clippy::items_after_statements, reason = "test-local statics next to their use")]
#![allow(clippy::assertions_on_result_states, reason = "tests deliberately assert Err returns")]
#![allow(clippy::ptr_as_ptr, reason = "test code uses `as` casts for raw pointers")]
use multitude::vec::Vec as ArenaVec;
use multitude::{Arc, Arena, Rc};
use crate::common;
#[derive(Clone)]
struct Droppy(&'static str);
impl Drop for Droppy {
fn drop(&mut self) {
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
#[test]
fn rc_single_assume_init_loop_traverses_past_first_drop_entry() {
let arena = Arena::new();
let rc_uninit = arena.alloc_uninit_rc::<Droppy>();
let _decoy: Rc<Droppy> = arena.alloc_rc(Droppy("decoy"));
unsafe {
Rc::as_ptr(&rc_uninit)
.cast_mut()
.write(core::mem::MaybeUninit::new(Droppy("target")));
}
let rc = unsafe { rc_uninit.assume_init() };
assert_eq!(rc.0, "target");
}
#[test]
fn rc_slice_assume_init_loop_traverses_past_first_drop_entry() {
let arena = Arena::new();
let rc_uninit = arena.alloc_uninit_slice_rc::<Droppy>(2);
let _decoy: Rc<Droppy> = arena.alloc_rc(Droppy("decoy"));
unsafe {
let base = Rc::as_ptr(&rc_uninit).cast::<core::mem::MaybeUninit<Droppy>>().cast_mut();
(*base.add(0)).write(Droppy("a"));
(*base.add(1)).write(Droppy("b"));
}
let rc = unsafe { rc_uninit.assume_init() };
assert_eq!(rc[0].0, "a");
}
#[test]
fn arc_single_assume_init_loop_traverses_past_first_drop_entry() {
let arena = Arena::new();
let arc_uninit = arena.alloc_uninit_arc::<Droppy>();
let _decoy: Arc<Droppy> = arena.alloc_arc(Droppy("decoy"));
unsafe {
Arc::as_ptr(&arc_uninit)
.cast_mut()
.write(core::mem::MaybeUninit::new(Droppy("target")));
}
let arc = unsafe { arc_uninit.assume_init() };
assert_eq!(arc.0, "target");
}
#[test]
fn arc_slice_assume_init_loop_traverses_past_first_drop_entry() {
let arena = Arena::new();
let arc_uninit = arena.alloc_uninit_slice_arc::<Droppy>(2);
let _decoy: Arc<Droppy> = arena.alloc_arc(Droppy("decoy"));
unsafe {
let base = Arc::as_ptr(&arc_uninit).cast::<core::mem::MaybeUninit<Droppy>>().cast_mut();
(*base.add(0)).write(Droppy("a"));
(*base.add(1)).write(Droppy("b"));
}
let arc = unsafe { arc_uninit.assume_init() };
assert_eq!(arc[1].0, "b");
}
#[test]
fn vec_swap_remove_last_index_skips_copy() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.extend([1_u32, 2, 3]);
let last = v.swap_remove(2);
assert_eq!(last, 3);
assert_eq!(v.as_slice(), &[1, 2]);
}
#[test]
fn vec_into_iter_partial_drop_compacts_tail() {
use core::sync::atomic::{AtomicUsize, Ordering};
static DROPPED: AtomicUsize = AtomicUsize::new(0);
struct Tracked(#[expect(dead_code, reason = "field only exists to make Tracked non-ZST")] u32);
impl Drop for Tracked {
fn drop(&mut self) {
DROPPED.fetch_add(1, Ordering::Relaxed);
}
}
DROPPED.store(0, Ordering::Relaxed);
let arena = Arena::new();
let mut v: ArenaVec<'_, Tracked> = arena.alloc_vec_with_capacity(4);
for i in 0..4_u32 {
v.push(Tracked(i));
}
let mut it = v.into_iter();
let _a = it.next().unwrap();
let _b = it.next().unwrap();
drop(_a);
drop(_b);
assert_eq!(DROPPED.load(Ordering::Relaxed), 2);
drop(it);
assert_eq!(DROPPED.load(Ordering::Relaxed), 4);
}
#[repr(align(32768))]
#[derive(Clone, Copy)]
struct OverAligned32K;
unsafe impl Send for OverAligned32K {}
unsafe impl Sync for OverAligned32K {}
#[test]
fn try_alloc_slice_fill_with_arc_rejects_over_aligned() {
let arena = Arena::new();
let result = arena.try_alloc_slice_fill_with_arc::<OverAligned32K, _>(2, |_| OverAligned32K);
assert!(result.is_err());
}
#[test]
fn shared_cache_push_pop_contention_drives_cas_retries() {
use std::sync::Barrier;
use std::thread;
use multitude::ArenaBuilder;
let arena: Arena = ArenaBuilder::new().max_normal_alloc(4096).byte_budget(128 * 1024 * 1024).build();
let nthreads = 8;
let per_thread = 4096;
let mut sets: Vec<Vec<multitude::Arc<u64>>> = (0..nthreads).map(|_| Vec::with_capacity(per_thread)).collect();
for set in &mut sets {
for _ in 0..per_thread {
set.push(arena.alloc_arc(42));
}
}
let barrier = std::sync::Arc::new(Barrier::new(nthreads));
let mut handles = Vec::new();
for set in sets {
let b = barrier.clone();
handles.push(thread::spawn(move || {
b.wait();
for a in set {
drop(a);
}
}));
}
for h in handles {
let _ = h.join();
}
}
#[test]
fn vec_shrink_to_fit_is_a_noop_when_not_at_cursor() {
let alloc = common::FailingAllocator::new(1);
let arena = Arena::new_in(alloc);
let mut v = arena.alloc_vec::<u8>();
v.reserve(100);
let cap_before = v.capacity();
let _filler: &mut [u8] = arena.alloc_slice_fill_with::<u8, _>(400, |_| 0);
unsafe { v.set_len(50) };
v.shrink_to_fit();
assert_eq!(v.capacity(), cap_before);
assert_eq!(v.len(), 50);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn vec_into_arena_rc_copy_panics_on_allocator_error() {
let alloc = common::FailingAllocator::new(0);
let arena = Arena::new_in(alloc);
let v = arena.alloc_vec::<u8>();
let _rc: Rc<[u8], _> = v.into_arena_rc();
}
}
mod coverage_llvmcov {
#![allow(clippy::std_instead_of_core, reason = "test code uses std")]
#![allow(clippy::missing_panics_doc, reason = "test code")]
#![allow(clippy::unwrap_used, reason = "test code")]
use multitude::{Arc, Arena, Rc};
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
#[test]
fn arc_into_pin_via_from_impl() {
let arena = Arena::new();
let arc: Arc<u32> = arena.alloc_arc(42_u32);
let pinned: core::pin::Pin<Arc<u32>> = arc.into();
assert_eq!(*pinned, 42);
}
#[test]
fn rc_into_pin_via_from_impl() {
let arena = Arena::new();
let rc: Rc<u32> = arena.alloc_rc(42_u32);
let pinned: core::pin::Pin<Rc<u32>> = rc.into();
assert_eq!(*pinned, 42);
}
#[test]
fn box_into_pin_via_from_impl() {
let arena = Arena::new();
let b: multitude::Box<u32> = arena.alloc_box(42_u32);
let pinned: core::pin::Pin<multitude::Box<u32>> = b.into();
assert_eq!(*pinned, 42);
}
#[test]
fn string_insert_str_at_end_of_string() {
let arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("hi");
s.insert_str(s.len(), "!");
assert_eq!(s.as_str(), "hi!");
}
#[test]
fn string_replace_range_empty_at_end() {
let arena = Arena::new();
let mut s = arena.alloc_string();
s.push_str("abc");
let n = s.len();
s.replace_range(n..n, "xyz");
assert_eq!(s.as_str(), "abcxyz");
}
#[test]
fn vec_resize_with_clone_panic_drops_partial() {
use std::cell::Cell;
use std::panic::AssertUnwindSafe;
struct Tracker<'a> {
clones_made: &'a Cell<usize>,
clones_dropped: &'a Cell<usize>,
panic_after: usize,
}
impl Clone for Tracker<'_> {
fn clone(&self) -> Self {
let n = self.clones_made.get() + 1;
self.clones_made.set(n);
assert!(n != self.panic_after, "clone #{n} panics by design");
Tracker {
clones_made: self.clones_made,
clones_dropped: self.clones_dropped,
panic_after: self.panic_after,
}
}
}
impl Drop for Tracker<'_> {
fn drop(&mut self) {
self.clones_dropped.set(self.clones_dropped.get() + 1);
}
}
let clones_made = Cell::new(0);
let clones_dropped = Cell::new(0);
let arena = Arena::new();
{
let mut v: multitude::vec::Vec<'_, Tracker<'_>> = arena.alloc_vec_with_capacity(8);
v.push(Tracker {
clones_made: &clones_made,
clones_dropped: &clones_dropped,
panic_after: 3,
});
let seed = Tracker {
clones_made: &clones_made,
clones_dropped: &clones_dropped,
panic_after: 3,
};
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
v.resize(6, seed);
}));
assert!(result.is_err(), "panicking clone in resize must propagate");
}
drop(arena);
assert!(
clones_dropped.get() >= 2,
"Guard must drop the 2 successful clones rolled back by the resize panic; got {}",
clones_dropped.get()
);
}
}