#![cfg(feature = "dst")]
mod common;
mod dst {
#![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::multiple_unsafe_ops_per_block, reason = "tests group related unsafe ops")]
#![allow(clippy::cast_possible_truncation, reason = "test data is small")]
#![allow(clippy::assertions_on_result_states, reason = "tests prefer assert!")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "test code")]
use core::sync::atomic::{AtomicUsize, Ordering};
use multitude::Arena;
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
#[test]
fn alloc_dst_rc_byte_slice() {
let arena = Arena::new();
let len = 5_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((i + 100) as u8);
}
})
};
assert_eq!(r.len(), 5);
for (i, byte) in r.iter().enumerate() {
assert_eq!(*byte, (i + 100) as u8);
}
}
#[test]
fn try_alloc_dst_rc_succeeds() {
let arena = Arena::new();
let layout = core::alloc::Layout::array::<u32>(2).unwrap();
let r = unsafe {
arena
.try_alloc_dst_rc::<[u32]>(layout, 2_usize, |fat: *mut [u32]| {
let p = fat.cast::<u32>();
p.add(0).write(7);
p.add(1).write(8);
})
.unwrap()
};
assert_eq!(&*r, &[7, 8]);
}
#[test]
fn alloc_dst_arc_byte_slice() {
let arena = Arena::new();
let len = 5_usize;
let layout = core::alloc::Layout::array::<u8>(len).unwrap();
let r = unsafe {
arena.alloc_dst_arc::<[u8]>(layout, len, |fat: *mut [u8]| {
let p = fat.cast::<u8>();
for i in 0..len {
p.add(i).write((i + 200) as u8);
}
})
};
assert_eq!(r.len(), 5);
for (i, byte) in r.iter().enumerate() {
assert_eq!(*byte, (i + 200) as u8);
}
}
#[test]
fn try_alloc_dst_arc_succeeds() {
let arena = Arena::new();
let layout = core::alloc::Layout::array::<u8>(3).unwrap();
let r = unsafe {
arena
.try_alloc_dst_arc::<[u8]>(layout, 3_usize, |fat: *mut [u8]| {
let p = fat.cast::<u8>();
p.add(0).write(1);
p.add(1).write(2);
p.add(2).write(3);
})
.unwrap()
};
assert_eq!(&*r, &[1, 2, 3]);
}
#[test]
fn alloc_dst_arc_outlives_arena() {
let r = {
let arena = Arena::new();
let layout = core::alloc::Layout::array::<u32>(4).unwrap();
unsafe {
arena.alloc_dst_arc::<[u32]>(layout, 4_usize, |fat: *mut [u32]| {
let p = fat.cast::<u32>();
for i in 0..4 {
p.add(i).write(11 * (i as u32 + 1));
}
})
}
};
assert_eq!(&*r, &[11, 22, 33, 44]);
}
#[test]
fn alloc_dst_arc_send_across_threads() {
let arena = Arena::new();
let layout = core::alloc::Layout::array::<u32>(3).unwrap();
let r = unsafe {
arena.alloc_dst_arc::<[u32]>(layout, 3_usize, |fat: *mut [u32]| {
let p = fat.cast::<u32>();
p.add(0).write(7);
p.add(1).write(8);
p.add(2).write(9);
})
};
let r2 = r.clone();
let h = std::thread::spawn(move || r2.iter().sum::<u32>());
assert_eq!(h.join().unwrap(), 24);
assert_eq!(&*r, &[7, 8, 9]);
}
#[test]
fn alloc_dst_rc_runs_drop_at_chunk_teardown() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct Tracked(#[expect(dead_code, reason = "field exists only for size")] u32);
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
{
let arena = Arena::new();
let layout = core::alloc::Layout::array::<Tracked>(3).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[Tracked]>(layout, 3_usize, |fat: *mut [Tracked]| {
let p = fat.cast::<Tracked>();
p.add(0).write(Tracked(1));
p.add(1).write(Tracked(2));
p.add(2).write(Tracked(3));
})
};
assert_eq!(r.len(), 3);
assert_eq!(COUNT.load(Ordering::SeqCst), 0);
drop(r);
}
assert_eq!(COUNT.load(Ordering::SeqCst), 3);
}
#[test]
fn try_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 try_alloc_dst_rc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let half_chunk = 32 * 1024_usize;
let layout = core::alloc::Layout::from_size_align(half_chunk, half_chunk).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 try_alloc_dst_arc_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let half_chunk = 32 * 1024_usize;
let layout = core::alloc::Layout::from_size_align(half_chunk, half_chunk).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]
fn try_alloc_dst_box_rejects_half_chunk_alignment() {
let arena: Arena = Arena::new();
let half_chunk = 32 * 1024_usize;
let layout = core::alloc::Layout::from_size_align(half_chunk, half_chunk).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());
}
}
mod dst_box {
#![allow(clippy::std_instead_of_core, reason = "tests use std")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "tests group related unsafe ops")]
#![allow(clippy::missing_panics_doc, reason = "test code")]
#![allow(clippy::cast_possible_truncation, reason = "test indices are small and bounded")]
use core::sync::atomic::{AtomicUsize, Ordering};
use multitude::Arena;
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
#[test]
fn alloc_dst_box_byte_slice() {
let arena = Arena::new();
let len = 10_usize;
let layout = core::alloc::Layout::array::<u8>(len).unwrap();
let b = unsafe {
arena.alloc_dst_box::<[u8]>(layout, len, |fat: *mut [u8]| {
let p = fat.cast::<u8>();
for i in 0..len {
p.add(i).write(i as u8);
}
})
};
assert_eq!(b.len(), 10);
for (i, byte) in b.iter().enumerate() {
assert_eq!(*byte, i as u8);
}
}
#[test]
fn try_alloc_dst_box_succeeds() {
let arena = Arena::new();
let layout = core::alloc::Layout::array::<u32>(2).unwrap();
let b = unsafe {
arena
.try_alloc_dst_box::<[u32]>(layout, 2_usize, |fat: *mut [u32]| {
let p = fat.cast::<u32>();
p.add(0).write(11);
p.add(1).write(22);
})
.unwrap()
};
assert_eq!(&*b, &[11, 22]);
}
#[test]
fn alloc_dst_box_runs_drop_immediately() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct Tracked(String);
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let layout = core::alloc::Layout::array::<Tracked>(3).unwrap();
let b = unsafe {
arena.alloc_dst_box::<[Tracked]>(layout, 3_usize, |fat: *mut [Tracked]| {
let p = fat.cast::<Tracked>();
p.add(0).write(Tracked("a".to_string()));
p.add(1).write(Tracked("b".to_string()));
p.add(2).write(Tracked("c".to_string()));
})
};
assert_eq!(b.len(), 3);
assert_eq!(b[0].0, "a");
assert_eq!(b[2].0, "c");
let before = COUNT.load(Ordering::SeqCst);
drop(b);
assert_eq!(
COUNT.load(Ordering::SeqCst),
before + 3,
"drop_in_place([T;3]) must drop each element"
);
drop(arena);
assert_eq!(COUNT.load(Ordering::SeqCst), before + 3, "no extra drops at arena teardown");
}
#[test]
fn alloc_slice_copy_box_basic() {
let arena = Arena::new();
let b = arena.alloc_slice_copy_box([1_u32, 2, 3]);
assert_eq!(&*b, &[1, 2, 3]);
let b: multitude::Box<[u8]> = arena.alloc_slice_copy_box([10_u8, 20, 30]);
assert_eq!(&*b, &[10, 20, 30]);
}
#[test]
fn alloc_slice_copy_box_mutable() {
let arena = Arena::new();
let mut b = arena.alloc_slice_copy_box([10_u32, 20, 30]);
b[1] = 200;
assert_eq!(&*b, &[10, 200, 30]);
}
#[test]
fn try_alloc_slice_copy_box_works() {
let arena = Arena::new();
let b = arena.try_alloc_slice_copy_box([1_u8, 2, 3]).unwrap();
assert_eq!(&*b, &[1, 2, 3]);
}
#[test]
fn alloc_slice_clone_box_basic() {
let arena = Arena::new();
let originals = [
std::string::String::from("a"),
std::string::String::from("b"),
std::string::String::from("c"),
];
let b = arena.alloc_slice_clone_box(&originals);
assert_eq!(b.len(), 3);
assert_eq!(b[0], "a");
assert_eq!(b[2], "c");
let src = [
std::string::String::from("x"),
std::string::String::from("y"),
std::string::String::from("z"),
];
let b: multitude::Box<[String]> = arena.alloc_slice_clone_box(src);
assert_eq!(b.len(), 3);
assert_eq!(b[2], "z");
}
#[test]
fn try_alloc_slice_clone_box_works() {
let arena = Arena::new();
let b = arena.try_alloc_slice_clone_box([100_u32, 200]).unwrap();
assert_eq!(&*b, &[100, 200]);
}
#[test]
fn alloc_slice_fill_with_box_basic() {
let arena = Arena::new();
let b: multitude::Box<[u64]> = arena.alloc_slice_fill_with_box(5, |i| (i as u64) * 10);
assert_eq!(&*b, &[0, 10, 20, 30, 40]);
let b: multitude::Box<[u32]> = arena.alloc_slice_fill_with_box(4, |i| (i + 1) as u32);
assert_eq!(&*b, &[1, 2, 3, 4]);
}
#[test]
fn try_alloc_slice_fill_with_box_works() {
let arena = Arena::new();
let b: multitude::Box<[u32]> = arena.try_alloc_slice_fill_with_box(3, |i| u32::try_from(i + 100).unwrap()).unwrap();
assert_eq!(&*b, &[100, 101, 102]);
}
#[test]
fn alloc_slice_fill_iter_box_basic() {
let arena = Arena::new();
let b: multitude::Box<[i32]> = arena.alloc_slice_fill_iter_box([7_i32, 8, 9]);
assert_eq!(&*b, &[7, 8, 9]);
let b: multitude::Box<[u8]> = arena.alloc_slice_fill_iter_box(0_u8..5);
assert_eq!(&*b, &[0, 1, 2, 3, 4]);
}
#[test]
fn try_alloc_slice_fill_iter_box_works() {
let arena = Arena::new();
let b: multitude::Box<[u32]> = arena.try_alloc_slice_fill_iter_box([42_u32, 43, 44]).unwrap();
assert_eq!(&*b, &[42, 43, 44]);
}
#[test]
fn alloc_slice_fill_iter_box_empty() {
let arena = Arena::new();
let b: multitude::Box<[u32]> = arena.alloc_slice_fill_iter_box(core::iter::empty::<u32>());
assert!(b.is_empty());
}
#[test]
fn alloc_slice_clone_box_drops_elements_immediately() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone)]
struct Tracked;
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let originals = [Tracked, Tracked, Tracked];
let b = arena.alloc_slice_clone_box(&originals);
assert_eq!(b.len(), 3);
let count_before = COUNT.load(Ordering::SeqCst);
drop(b);
let count_after = COUNT.load(Ordering::SeqCst);
assert_eq!(count_after - count_before, 3, "drop_in_place([T;3]) must drop each element");
drop(originals);
drop(arena);
assert_eq!(COUNT.load(Ordering::SeqCst), count_after + 3);
}
#[test]
fn alloc_slice_fill_with_box_drops_elements_immediately() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct Tracked;
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let b: multitude::Box<[Tracked]> = arena.alloc_slice_fill_with_box(5, |_| Tracked);
assert_eq!(b.len(), 5);
let before = COUNT.load(Ordering::SeqCst);
drop(b);
assert_eq!(COUNT.load(Ordering::SeqCst), before + 5);
drop(arena); assert_eq!(COUNT.load(Ordering::SeqCst), before + 5);
}
#[test]
fn alloc_slice_copy_box_no_drop_for_copy_types() {
let arena = Arena::new();
let b = arena.alloc_slice_copy_box([1_u8, 2, 3, 4, 5]);
assert_eq!(b.len(), 5);
drop(b);
let b2 = arena.alloc_slice_copy_box([9_u8, 8, 7]);
assert_eq!(&*b2, &[9, 8, 7]);
}
#[test]
fn alloc_slice_fill_with_box_zero_len_works() {
let arena = Arena::new();
let b: multitude::Box<[u32]> = arena.alloc_slice_fill_with_box(0, |_| panic!("never called"));
assert!(b.is_empty());
}
#[test]
fn alloc_slice_fill_with_box_zst_with_drop() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct ZstDrop;
impl Drop for ZstDrop {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let b: multitude::Box<[ZstDrop]> = arena.alloc_slice_fill_with_box(7, |_| ZstDrop);
drop(b);
assert_eq!(COUNT.load(Ordering::SeqCst), 7);
}
#[test]
fn alloc_slice_fill_with_box_panic_drops_initialized_prefix() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
let _ = DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _b: multitude::Box<[DropCounter]> = arena.alloc_slice_fill_with_box(10, |i| {
assert!(i != 3, "intentional");
DropCounter
});
}));
assert!(result.is_err());
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
let b: multitude::Box<[u32]> = arena.alloc_slice_fill_with_box(2, |i| u32::try_from(i).unwrap());
assert_eq!(&*b, &[0, 1]);
}
#[test]
#[should_panic(expected = "caller violated ExactSizeIterator contract")]
fn alloc_slice_fill_iter_box_panics_on_short_iter() {
struct Liar(usize);
impl Iterator for Liar {
type Item = u32;
fn next(&mut self) -> Option<u32> {
None
}
}
impl ExactSizeIterator for Liar {
fn len(&self) -> usize {
self.0
}
}
let arena = Arena::new();
let _b: multitude::Box<[u32]> = arena.alloc_slice_fill_iter_box(Liar(2));
}
#[test]
fn alloc_slice_box_high_alignment_drop_locates_entry_correctly() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
#[repr(align(32))]
struct A32(#[expect(dead_code, reason = "field present only to give the type a non-zero size")] u8);
impl Drop for A32 {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let _decoy: &mut u8 = arena.alloc(0_u8);
let b: multitude::Box<[A32]> = arena.alloc_slice_fill_with_box(4, |_| A32(0));
assert_eq!(b.len(), 4);
let before = COUNT.load(Ordering::SeqCst);
drop(b);
assert_eq!(COUNT.load(Ordering::SeqCst), before + 4);
}
#[test]
fn arena_box_slice_into_rc_basic() {
let arena = Arena::new();
let b = arena.alloc_slice_copy_box([1_u32, 2, 3]);
let r = b.into_rc();
assert_eq!(&*r, &[1, 2, 3]);
}
#[test]
fn arena_box_slice_into_rc_after_mutation() {
let arena = Arena::new();
let mut b = arena.alloc_slice_copy_box([10_u32, 20, 30]);
b[1] = 99;
let r = b.into_rc();
assert_eq!(&*r, &[10, 99, 30]);
}
#[test]
fn alloc_dst_box_drop_type_into_rc_runs_drop_exactly_once() {
struct DropCounter(std::sync::Arc<AtomicUsize>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
let counter = std::sync::Arc::new(AtomicUsize::new(0));
{
let arena = Arena::new();
let len = 3_usize;
let layout = core::alloc::Layout::array::<DropCounter>(len).unwrap();
let b = unsafe {
arena.alloc_dst_box::<[DropCounter]>(layout, len, |fat: *mut [DropCounter]| {
let p = fat.cast::<DropCounter>();
for i in 0..len {
p.add(i).write(DropCounter(std::sync::Arc::clone(&counter)));
}
})
};
let r = b.into_rc();
assert_eq!(r.len(), 3);
assert_eq!(counter.load(Ordering::Relaxed), 0, "no drops while Rc is live");
drop(r);
drop(arena);
}
assert_eq!(counter.load(Ordering::Relaxed), 3, "each element dropped exactly once");
}
#[test]
fn alloc_dst_box_sized_drop_type_into_rc_runs_drop_exactly_once() {
struct DropCounter(std::sync::Arc<AtomicUsize>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
let counter = std::sync::Arc::new(AtomicUsize::new(0));
{
let arena = Arena::new();
let layout = core::alloc::Layout::new::<DropCounter>();
let b = unsafe {
arena.alloc_dst_box::<DropCounter>(layout, (), |p: *mut DropCounter| {
p.write(DropCounter(std::sync::Arc::clone(&counter)));
})
};
let r = b.into_rc();
assert_eq!(counter.load(Ordering::Relaxed), 0);
drop(r);
drop(arena);
}
assert_eq!(counter.load(Ordering::Relaxed), 1);
}
#[test]
fn arena_box_slice_into_rc_outlives_arena() {
let r = {
let arena = Arena::new();
let b = arena.alloc_slice_copy_box([7_u8, 8, 9]);
b.into_rc()
};
assert_eq!(&*r, &[7, 8, 9]);
}
#[test]
fn arena_box_slice_into_rc_preserves_drop_semantics() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct Tracked;
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, Ordering::SeqCst);
}
}
COUNT.store(0, Ordering::SeqCst);
let arena = Arena::new();
let b: multitude::Box<[Tracked]> = arena.alloc_slice_fill_with_box(3, |_| Tracked);
let r = b.into_rc();
assert_eq!(COUNT.load(Ordering::SeqCst), 0);
assert_eq!(r.len(), 3);
drop(r);
drop(arena);
assert_eq!(COUNT.load(Ordering::SeqCst), 3);
}
#[test]
fn arena_box_slice_into_rc_clones_share_chunk() {
let arena = Arena::new();
let b = arena.alloc_slice_copy_box([42_u32; 5]);
let r = b.into_rc();
let r2 = r.clone();
drop(r);
assert_eq!(&*r2, &[42; 5]);
}
#[test]
fn try_alloc_dst_box_rejects_drop_slice_with_overflowing_len() {
struct DropCounter(std::sync::Arc<AtomicUsize>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
let arena = Arena::new();
let n: usize = (u16::MAX as usize) + 1;
let Ok(layout) = core::alloc::Layout::array::<DropCounter>(n) else {
return;
};
let result = unsafe {
arena.try_alloc_dst_box::<[DropCounter]>(layout, n, |_fat: *mut [DropCounter]| {
unreachable!("alloc must be rejected before init runs");
})
};
assert!(result.is_err(), "DST slice with len > u16::MAX and T: Drop must be rejected");
}
}
mod dst_panic_safety {
#![allow(clippy::std_instead_of_core, reason = "tests use std")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "tests group related unsafe ops")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "test code")]
use std::panic::{AssertUnwindSafe, catch_unwind};
use multitude::Arena;
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
#[test]
fn dst_rc_init_panic_then_evict_is_sound() {
let arena = Arena::new();
let _r1 = arena.alloc_rc(String::from("a"));
let _r2 = arena.alloc_rc(String::from("b"));
let layout = core::alloc::Layout::array::<u32>(4).unwrap();
let result = catch_unwind(AssertUnwindSafe(|| {
unsafe {
arena.alloc_dst_rc::<[u32]>(layout, 4_usize, |_fat: *mut [u32]| {
panic!("planned panic in DST init");
});
}
}));
assert!(result.is_err());
for _ in 0..256 {
let _ = arena.alloc_rc(String::from("xxxxxxxxxx"));
}
drop(arena);
}
#[test]
fn dst_arc_init_panic_then_evict_is_sound() {
let arena = Arena::new();
let _a1 = arena.alloc_arc(String::from("a"));
let _a2 = arena.alloc_arc(String::from("b"));
let layout = core::alloc::Layout::array::<u32>(4).unwrap();
let result = catch_unwind(AssertUnwindSafe(|| {
unsafe {
arena.alloc_dst_arc::<[u32]>(layout, 4_usize, |_fat: *mut [u32]| {
panic!("planned panic in DST init");
});
}
}));
assert!(result.is_err());
for _ in 0..256 {
let _ = arena.alloc_arc(String::from("xxxxxxxxxx"));
}
drop(arena);
}
}
mod from_coverage_extras_dst {
#![allow(clippy::items_after_statements, reason = "relocated tests put inner types near use")]
#![allow(clippy::clone_on_ref_ptr, reason = "relocated tests use .clone() on Arc/Rc")]
#![allow(dead_code, reason = "relocated helpers retain fields for layout")]
#![allow(
unfulfilled_lint_expectations,
reason = "relocated #[expect] may be fulfilled at file or feature level"
)]
#![allow(
clippy::undocumented_unsafe_blocks,
reason = "relocated test bodies preserve original safety reasoning"
)]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "relocated tests group related unsafe ops")]
#![allow(clippy::cast_possible_truncation, reason = "relocated tests use bounded values")]
#![allow(clippy::cast_sign_loss, reason = "relocated tests use non-negative values")]
#![allow(clippy::empty_drop, reason = "relocated tests use empty Drop impls to mark dropability")]
#![allow(clippy::assertions_on_result_states, reason = "relocated tests deliberately assert error returns")]
#![allow(clippy::empty_line_after_doc_comments, reason = "relocated test doc-comments")]
use core::sync::atomic::{AtomicUsize, Ordering};
use std::panic::catch_unwind;
use crate::common::FailingAllocator as _FA;
fn fail_arena() -> Arena<_FA> {
Arena::new_in(_FA::new(0))
}
fn expect_panic<F: FnOnce() + std::panic::UnwindSafe>(f: F) {
let r = catch_unwind(f);
assert!(r.is_err(), "expected panic");
}
use std::panic::AssertUnwindSafe;
use multitude::{Arena, ArenaBuilder, Box};
#[expect(dead_code, reason = "helper used by some relocated tests")]
struct OneByteDrop(u8);
impl Drop for OneByteDrop {
fn drop(&mut self) {}
}
#[expect(unused_imports, reason = "relocated tests may reference common helpers")]
use crate::common::{self, FailingAllocator, SendFailingAllocator};
#[test]
fn alloc_box_oversized_drop_type_uses_has_drop_layout() {
static DROPPED: AtomicUsize = AtomicUsize::new(0);
struct BigDrop {
_bytes: [u8; 4096],
}
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().max_normal_alloc(4 * 1024).build();
{
let _b: Box<BigDrop> = arena.alloc_box(BigDrop { _bytes: [0; 4096] });
}
assert_eq!(DROPPED.load(Ordering::SeqCst), 1);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_copy_box_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_copy_box([0_u8; 4]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_clone_box_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_clone_box([1_u32, 2]);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_fill_with_box_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_fill_with_box::<u32, _>(4, |i| i as u32);
}
#[test]
#[should_panic(expected = "multitude: allocator returned AllocError")]
fn alloc_slice_fill_iter_box_panics_on_failing_allocator() {
let arena: Arena<FailingAllocator> = Arena::new_in(FailingAllocator::new(0));
let _ = arena.alloc_slice_fill_iter_box([1_u32, 2, 3]);
}
#[test]
fn arena_box_slice_from_into_arena_rc_slice() {
let arena: Arena = Arena::new();
let b: Box<[u32]> = arena.alloc_slice_fill_with_box(3, |i| i as u32 + 10);
let r: multitude::Rc<[u32]> = b.into();
assert_eq!(&*r, &[10, 11, 12][..]);
}
#[test]
fn panic_alloc_uninit_slice_box() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_uninit_slice_box::<u32>(4);
});
}
#[test]
fn panic_alloc_zeroed_slice_box() {
expect_panic(|| {
let a = fail_arena();
let _ = a.alloc_zeroed_slice_box::<u32>(4);
});
}
#[test]
fn try_alloc_uninit_slice_box_err() {
let a = fail_arena();
a.try_alloc_uninit_slice_box::<u32>(4).unwrap_err();
}
#[test]
fn try_alloc_zeroed_slice_box_err() {
let a = fail_arena();
a.try_alloc_zeroed_slice_box::<u32>(4).unwrap_err();
}
#[test]
fn dst_reserve_rejects_overaligned() {
let arena = Arena::new();
let layout = core::alloc::Layout::from_size_align(8, 131_072).unwrap();
let result = unsafe {
arena.try_alloc_dst_rc::<[u8]>(layout, 8_usize, |fat: *mut [u8]| {
let p = fat.cast::<u8>();
for i in 0..8 {
p.add(i).write(i as u8);
}
})
};
result.unwrap_err();
}
#[test]
fn dst_init_panic_guard_releases_refcount() {
let arena = Arena::new();
let layout = core::alloc::Layout::array::<u8>(4).unwrap();
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
unsafe {
arena.alloc_dst_rc::<[u8]>(layout, 4_usize, |_fat: *mut [u8]| {
panic!("deliberate panic in DST init");
})
};
}));
assert!(result.is_err());
let _v = arena.alloc_rc(42_u32);
}
#[test]
#[expect(clippy::too_many_lines, reason = "exhaustive alignment coverage requires many similar blocks")]
#[expect(clippy::empty_drop, reason = "Drop impls needed to make needs_drop::<T>() true")]
fn dst_drop_shim_dispatch_various_alignments() {
let arena = Arena::new();
{
let layout = core::alloc::Layout::from_size_align(4, 2).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[u16]>(layout, 2_usize, |fat: *mut [u16]| {
let p = fat.cast::<u16>();
p.add(0).write(10);
p.add(1).write(20);
})
};
assert_eq!(&*r, &[10_u16, 20]);
}
{
let layout = core::alloc::Layout::from_size_align(8, 4).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[u32]>(layout, 2_usize, |fat: *mut [u32]| {
let p = fat.cast::<u32>();
p.add(0).write(100);
p.add(1).write(200);
})
};
assert_eq!(&*r, &[100_u32, 200]);
}
{
let layout = core::alloc::Layout::from_size_align(16, 8).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[u64]>(layout, 2_usize, |fat: *mut [u64]| {
let p = fat.cast::<u64>();
p.add(0).write(1000);
p.add(1).write(2000);
})
};
assert_eq!(&*r, &[1000_u64, 2000]);
}
{
#[repr(align(16))]
#[derive(Debug, PartialEq)]
struct A16(u64, u64);
impl Drop for A16 {
fn drop(&mut self) {}
}
let layout = core::alloc::Layout::from_size_align(32, 16).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[A16]>(layout, 2_usize, |fat: *mut [A16]| {
let p = fat.cast::<A16>();
p.add(0).write(A16(1, 2));
p.add(1).write(A16(3, 4));
})
};
assert_eq!(r[0], A16(1, 2));
}
{
#[repr(align(32))]
#[derive(Debug, PartialEq)]
struct A32(u64);
impl Drop for A32 {
fn drop(&mut self) {}
}
let layout = core::alloc::Layout::from_size_align(64, 32).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[A32]>(layout, 2_usize, |fat: *mut [A32]| {
let p = fat.cast::<A32>();
p.add(0).write(A32(11));
p.add(1).write(A32(22));
})
};
assert_eq!(r[0], A32(11));
}
{
#[repr(align(64))]
#[derive(Debug, PartialEq)]
struct A64(u64);
impl Drop for A64 {
fn drop(&mut self) {}
}
let layout = core::alloc::Layout::from_size_align(128, 64).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[A64]>(layout, 2_usize, |fat: *mut [A64]| {
let p = fat.cast::<A64>();
p.add(0).write(A64(111));
p.add(1).write(A64(222));
})
};
assert_eq!(r[0], A64(111));
}
{
#[repr(align(128))]
#[derive(Debug, PartialEq)]
struct A128(u64);
impl Drop for A128 {
fn drop(&mut self) {}
}
let layout = core::alloc::Layout::from_size_align(256, 128).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[A128]>(layout, 2_usize, |fat: *mut [A128]| {
let p = fat.cast::<A128>();
p.add(0).write(A128(7));
p.add(1).write(A128(8));
})
};
assert_eq!(r[0], A128(7));
}
macro_rules! test_align {
($name:ident, $align:literal) => {{
#[repr(align($align))]
#[derive(Debug, PartialEq)]
struct Aligned(u64);
impl Drop for Aligned {
fn drop(&mut self) {}
}
let layout = core::alloc::Layout::from_size_align($align * 2, $align).unwrap();
let r = unsafe {
arena.alloc_dst_rc::<[Aligned]>(layout, 2_usize, |fat: *mut [Aligned]| {
let p = fat.cast::<Aligned>();
p.add(0).write(Aligned(1));
p.add(1).write(Aligned(2));
})
};
assert_eq!(r[0], Aligned(1));
}};
}
test_align!(a256, 256);
test_align!(a512, 512);
test_align!(a1024, 1024);
test_align!(a2048, 2048);
test_align!(a4096, 4096);
test_align!(a8192, 8192);
test_align!(a16384, 16384);
}
#[test]
fn try_alloc_dst_arc_accepts_sized_metadata() {
let arena = Arena::new();
let layout = core::alloc::Layout::new::<u32>();
let result = unsafe { arena.try_alloc_dst_arc::<u32>(layout, (), |p| p.write(42_u32)) };
let arc = result.expect("sized DST allocation through try_alloc_dst_arc should succeed");
assert_eq!(*arc, 42);
}
#[test]
fn try_alloc_dst_rc_accepts_sized_metadata() {
let arena = Arena::new();
let layout = core::alloc::Layout::new::<u32>();
let result = unsafe { arena.try_alloc_dst_rc::<u32>(layout, (), |p| p.write(7_u32)) };
let rc = result.expect("sized DST allocation through try_alloc_dst_rc should succeed");
assert_eq!(*rc, 7);
}
#[test]
fn try_alloc_dst_arc_refill_failure_propagates() {
let alloc = common::SendFailingAllocator::new(1);
let arena = ArenaBuilder::new_in(alloc).max_normal_alloc(4096).try_build().unwrap();
let layout = core::alloc::Layout::array::<u8>(2048).unwrap();
let mut errs = 0;
for _ in 0..16 {
let r = unsafe { arena.try_alloc_dst_arc::<[u8]>(layout, 2048, |_| {}) };
if r.is_err() {
errs += 1;
}
}
assert!(errs >= 1, "expected at least one refill failure");
}
#[test]
fn try_alloc_dst_rc_refill_failure_propagates() {
let alloc = common::FailingAllocator::new(1);
let arena = ArenaBuilder::new_in(alloc).max_normal_alloc(4096).try_build().unwrap();
let layout = core::alloc::Layout::array::<u8>(2048).unwrap();
let mut errs = 0;
for _ in 0..16 {
let r = unsafe { arena.try_alloc_dst_rc::<[u8]>(layout, 2048, |_| {}) };
if r.is_err() {
errs += 1;
}
}
assert!(errs >= 1, "expected at least one refill failure");
}
#[test]
fn try_alloc_dst_rc_drop_refill_failure_propagates() {
let alloc = common::FailingAllocator::new(1);
let arena = ArenaBuilder::new_in(alloc).max_normal_alloc(4096).try_build().unwrap();
let layout = core::alloc::Layout::array::<String>(64).unwrap();
let mut errs = 0;
for _ in 0..16 {
let r = unsafe {
arena.try_alloc_dst_rc::<[String]>(layout, 64, |p| {
for i in 0..64 {
core::ptr::write(p.cast::<String>().add(i), String::new());
}
})
};
if r.is_err() {
errs += 1;
}
}
assert!(errs >= 1, "expected at least one refill failure");
}
#[test]
fn try_alloc_dst_arc_drop_refill_failure_propagates() {
let alloc = common::SendFailingAllocator::new(1);
let arena = ArenaBuilder::new_in(alloc).max_normal_alloc(4096).try_build().unwrap();
let layout = core::alloc::Layout::array::<Vec<u8>>(64).unwrap();
let mut errs = 0;
for _ in 0..16 {
let r = unsafe {
arena.try_alloc_dst_arc::<[Vec<u8>]>(layout, 64, |p| {
for i in 0..64 {
core::ptr::write(p.cast::<Vec<u8>>().add(i), Vec::new());
}
})
};
if r.is_err() {
errs += 1;
}
}
assert!(errs >= 1, "expected at least one refill failure");
}
}
mod from_mutants_extras_dst {
#![allow(clippy::items_after_statements, reason = "relocated tests put inner types near use")]
#![allow(clippy::clone_on_ref_ptr, reason = "relocated tests use .clone() on Arc/Rc")]
#![allow(dead_code, reason = "relocated helpers retain fields for layout")]
#![allow(
unfulfilled_lint_expectations,
reason = "relocated #[expect] may be fulfilled at file or feature level"
)]
#![allow(
clippy::undocumented_unsafe_blocks,
reason = "relocated test bodies preserve original safety reasoning"
)]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "relocated tests group related unsafe ops")]
#![allow(clippy::cast_possible_truncation, reason = "relocated tests use bounded values")]
#![allow(clippy::cast_sign_loss, reason = "relocated tests use non-negative values")]
#![allow(clippy::empty_drop, reason = "relocated tests use empty Drop impls to mark dropability")]
#![allow(clippy::assertions_on_result_states, reason = "relocated tests deliberately assert error returns")]
#![allow(clippy::empty_line_after_doc_comments, reason = "relocated test doc-comments")]
use multitude::vec::Vec as ArenaVec;
use multitude::{Arena, Box as ArenaBox, Rc};
#[expect(dead_code, reason = "helper used by some relocated tests")]
struct OneByteDrop(u8);
impl Drop for OneByteDrop {
fn drop(&mut self) {}
}
#[expect(unused_imports, reason = "relocated tests may reference common helpers")]
use crate::common::{self, FailingAllocator, SendFailingAllocator};
#[test]
fn allocate_shared_layout_high_align_offset_zero_preserved() {
use core::alloc::Layout;
let arena = multitude::Arena::new();
for _ in 0..64 {
let layout = Layout::array::<u128>(32).unwrap();
let metadata: usize = 32;
#[expect(
clippy::multiple_unsafe_ops_per_block,
reason = "single logical unsafe operation: the alloc_dst_arc DST initialization"
)]
let arc: multitude::Arc<[u128]> = unsafe {
arena.alloc_dst_arc::<[u128]>(layout, metadata, |p: *mut [u128]| {
let base = p.cast::<u128>();
for i in 0_u128..32 {
core::ptr::write(base.add(i as usize), i);
}
})
};
let raw_addr = arc.as_ptr().cast::<u128>() as usize;
assert_eq!(raw_addr % 16, 0, "payload must be aligned to u128");
assert_eq!(arc.len(), 32);
for i in 0_u128..32 {
assert_eq!(arc[i as usize], i);
}
}
}
#[test]
fn vec_into_arena_box_into_rc_drops_correctly() {
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
let counter = StdArc::new(AtomicUsize::new(0));
struct DT(StdArc<AtomicUsize>);
impl Drop for DT {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
{
let arena = multitude::Arena::new();
let mut v: multitude::vec::Vec<DT, _> = multitude::vec::Vec::new_in(&arena);
v.push(DT(counter.clone()));
v.push(DT(counter.clone()));
v.push(DT(counter.clone()));
let b: multitude::Box<[DT]> = v.into_arena_box();
let rc = multitude::Box::<[DT], _>::into_rc(b);
assert_eq!(rc.len(), 3);
}
assert_eq!(counter.load(Ordering::Relaxed), 3);
}
#[test]
fn empty_uninit_slice_box_into_rc_does_not_abort() {
let arena = multitude::Arena::new();
let b = arena.alloc_uninit_slice_box::<String>(0);
let init: multitude::Box<[String]> = unsafe { b.assume_init() };
assert_eq!(init.len(), 0);
let rc = multitude::Box::<[String], _>::into_rc(init);
assert_eq!(rc.len(), 0);
}
#[test]
fn into_arena_rc_empty_drop_type_takes_copy_path() {
struct D;
impl Drop for D {
fn drop(&mut self) {}
}
let arena = Arena::new();
let v: ArenaVec<'_, D> = arena.alloc_vec_with_capacity(4);
assert_eq!(v.len(), 0);
let rc: Rc<[D], _> = v.into_arena_rc();
assert_eq!(rc.len(), 0);
}
#[test]
fn into_arena_box_empty_drop_type_takes_copy_path() {
struct D;
impl Drop for D {
fn drop(&mut self) {}
}
let arena = Arena::new();
let v: ArenaVec<'_, D> = arena.alloc_vec_with_capacity(4);
assert_eq!(v.len(), 0);
let b: ArenaBox<[D], _> = v.into_arena_box();
assert_eq!(b.len(), 0);
}
#[cfg(all(feature = "dst", feature = "stats"))]
#[test]
fn into_arena_rc_with_full_capacity_does_not_attempt_reclaim() {
let arena = Arena::new();
let mut v: ArenaVec<'_, u32> = arena.alloc_vec_with_capacity(4);
for i in 0..4_u32 {
v.push(i);
}
assert_eq!(v.len(), v.capacity());
let rc: Rc<[u32], _> = v.into_arena_rc();
assert_eq!(rc.len(), 4);
drop(rc);
}
#[test]
fn into_arena_box_with_full_capacity_for_drop_type_no_reclaim() {
let arena = Arena::new();
let mut v: ArenaVec<'_, OneByteDrop> = arena.alloc_vec_with_capacity(4);
for i in 0..4 {
v.push(OneByteDrop(i));
}
assert_eq!(v.len(), v.capacity());
let b: ArenaBox<[OneByteDrop], _> = v.into_arena_box();
assert_eq!(b.len(), 4);
}
#[test]
fn into_arena_box_with_full_capacity_for_non_drop_type_no_reclaim() {
let arena = Arena::new();
let mut v: ArenaVec<'_, u32> = arena.alloc_vec_with_capacity(4);
for i in 0..4 {
v.push(i);
}
assert_eq!(v.len(), v.capacity());
let b: ArenaBox<[u32], _> = v.into_arena_box();
assert_eq!(b.len(), 4);
}
#[test]
fn into_arena_box_with_unused_tail_reclaims() {
let arena = Arena::new();
let mut v: ArenaVec<'_, u32> = arena.alloc_vec_with_capacity(8);
v.push(1);
v.push(2);
let b: ArenaBox<[u32], _> = v.into_arena_box();
assert_eq!(b.len(), 2);
let _r: Rc<u32> = arena.alloc_rc(99);
}
#[test]
fn into_arena_box_copy_walks_all_elements() {
use core::cell::Cell;
struct D<'a> {
seen: &'a Cell<u32>,
idx: u32,
}
impl Drop for D<'_> {
fn drop(&mut self) {
self.seen.set(self.seen.get() | (1_u32 << self.idx));
}
}
let mask = Cell::new(0_u32);
let arena = Arena::new();
{
let mut v: ArenaVec<'_, D<'_>> = arena.alloc_vec_with_capacity(2);
v.push(D { seen: &mask, idx: 0 });
v.push(D { seen: &mask, idx: 1 });
v.push(D { seen: &mask, idx: 2 });
let _other: Rc<u64> = arena.alloc_rc(0);
let _b: ArenaBox<[D<'_>], _> = v.into_arena_box();
}
drop(arena);
assert_eq!(mask.get(), 0b111, "all elements should drop exactly once");
}
#[test]
fn into_arena_box_copy_advances_consumed_index() {
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicU32, Ordering};
struct D {
idx: u32,
seen: StdArc<AtomicU32>,
}
impl Drop for D {
fn drop(&mut self) {
self.seen.fetch_or(1_u32 << (self.idx % 32), Ordering::Relaxed);
}
}
let seen = StdArc::new(AtomicU32::new(0));
{
let arena = Arena::new();
let mut v: ArenaVec<'_, D> = arena.alloc_vec_with_capacity(1100);
for i in 0..1100_u32 {
v.push(D {
idx: i,
seen: seen.clone(),
});
}
let b: ArenaBox<[D], _> = v.into_arena_box();
for (i, d) in b.iter().enumerate() {
assert_eq!(d.idx, i as u32, "element {i} should have idx {i}");
}
drop(b);
}
assert_eq!(seen.load(Ordering::Relaxed), u32::MAX);
}
#[test]
fn box_slice_into_rc_with_drop_type_runs_drop_once() {
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
let counter = StdArc::new(AtomicUsize::new(0));
struct D(StdArc<AtomicUsize>);
impl Drop for D {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
let arena = Arena::new();
{
let mut v: ArenaVec<'_, D> = arena.alloc_vec_with_capacity(3);
for _ in 0..3 {
v.push(D(counter.clone()));
}
let b: ArenaBox<[D]> = v.into_arena_box();
let _rc = multitude::Box::<[D], _>::into_rc(b);
}
drop(arena);
assert_eq!(counter.load(Ordering::Relaxed), 3);
}
#[test]
fn dst_arc_rejects_excessive_alignment_via_layout() {
let arena = Arena::new();
let layout = core::alloc::Layout::from_size_align(64, 32 * 1024).unwrap();
let result = unsafe { arena.try_alloc_dst_arc::<[u8]>(layout, 64, |_| {}) };
result.unwrap_err();
}
}