use super::*;
#[test]
fn default_impl() {
let arena = Arena::default();
let ptr = arena.alloc(16);
assert_eq!(ptr.as_ptr() as usize % 8, 0);
unsafe { std::ptr::write_bytes(ptr.as_ptr(), 0xAA, 16) };
let bytes = unsafe { std::slice::from_raw_parts(ptr.as_ptr(), 16) };
assert!(bytes.iter().all(|&b| b == 0xAA));
}
#[test]
fn alloc_basics() {
let arena = Arena::new();
let ptr = arena.alloc(8);
unsafe { ptr.as_ptr().write(0xAB) };
assert_eq!(unsafe { *ptr.as_ptr() }, 0xAB);
for size in [8, 16, 24, 64] {
let p = arena.alloc(size);
assert_eq!(p.as_ptr() as usize % 8, 0, "size={size}");
}
let a = arena.alloc(64);
let b = arena.alloc(64);
let c = arena.alloc(64);
let a_range = a.as_ptr() as usize..a.as_ptr() as usize + 64;
let b_start = b.as_ptr() as usize;
let c_start = c.as_ptr() as usize;
assert!(!a_range.contains(&b_start));
assert!(!a_range.contains(&c_start));
assert_ne!(b_start, c_start);
}
#[test]
fn alloc_growth() {
let arena = Arena::new();
for _ in 0..20 {
let ptr = arena.alloc(128);
unsafe {
std::ptr::write_bytes(ptr.as_ptr(), 0xCC, 128);
}
}
let ptr = arena.alloc(4096);
unsafe {
std::ptr::write_bytes(ptr.as_ptr(), 0xDD, 4096);
}
assert_eq!(ptr.as_ptr() as usize % 8, 0);
}
#[test]
fn realloc_in_place() {
let arena = Arena::new();
let mut ptr = arena.alloc(24);
unsafe { std::ptr::write_bytes(ptr.as_ptr(), 0xAA, 24) };
let original = ptr;
let new_ptr = unsafe { arena.realloc(ptr, 24, 48) };
assert_eq!(new_ptr, original, "expected in-place extension");
let bytes = unsafe { std::slice::from_raw_parts(new_ptr.as_ptr(), 24) };
assert!(bytes.iter().all(|&b| b == 0xAA));
ptr = new_ptr;
for new_size in [96, 192] {
let old_size = new_size / 2;
ptr = unsafe { arena.realloc(ptr, old_size, new_size) };
assert_eq!(
ptr, original,
"size={new_size}: expected in-place extension"
);
}
let bytes = unsafe { std::slice::from_raw_parts(ptr.as_ptr(), 24) };
assert!(bytes.iter().all(|&b| b == 0xAA));
}
#[test]
fn realloc_intervening_alloc_forces_copy() {
let arena = Arena::new();
let a = arena.alloc(24);
unsafe { std::ptr::write_bytes(a.as_ptr(), 0xBB, 24) };
let _b = arena.alloc(8);
let new_ptr = unsafe { arena.realloc(a, 24, 48) };
assert_ne!(new_ptr, a, "expected copy to new location");
let bytes = unsafe { std::slice::from_raw_parts(new_ptr.as_ptr(), 24) };
assert!(bytes.iter().all(|&b| b == 0xBB));
}
#[test]
fn realloc_cross_slab() {
let arena = Arena::new();
let ptr = arena.alloc(INITIAL_SLAB_SIZE - HEADER_SIZE);
unsafe { std::ptr::write_bytes(ptr.as_ptr(), 0xCC, 64) };
let new_ptr = unsafe { arena.realloc(ptr, INITIAL_SLAB_SIZE - HEADER_SIZE, INITIAL_SLAB_SIZE) };
assert_ne!(new_ptr, ptr, "expected new slab allocation");
let bytes = unsafe { std::slice::from_raw_parts(new_ptr.as_ptr(), 64) };
assert!(bytes.iter().all(|&b| b == 0xCC));
}
#[test]
#[allow(clippy::drop_non_drop)]
fn scratch_basics() {
let arena = Arena::new();
arena.alloc(8);
let mut scratch = unsafe { arena.scratch() };
scratch.push(b'h');
scratch.push(b'i');
assert_eq!(scratch.as_bytes(), b"hi");
drop(scratch);
let mut scratch = unsafe { arena.scratch() };
scratch.extend(b"hello ");
scratch.extend(b"world");
assert_eq!(scratch.as_bytes(), b"hello world");
assert_eq!(scratch.as_bytes().len(), 11);
drop(scratch);
let scratch = unsafe { arena.scratch() };
assert_eq!(scratch.as_bytes(), b"");
}
#[test]
fn scratch_commit() {
let arena = Arena::new();
arena.alloc(8);
let mut scratch = unsafe { arena.scratch() };
scratch.extend(b"committed");
let slice = scratch.commit();
assert_eq!(slice, b"committed");
let mut scratch = unsafe { arena.scratch() };
scratch.extend(b"data");
let committed = scratch.commit();
let next = arena.alloc(8);
let committed_range =
committed.as_ptr() as usize..committed.as_ptr() as usize + committed.len();
assert!(!committed_range.contains(&(next.as_ptr() as usize)));
let scratch = unsafe { arena.scratch() };
let slice = scratch.commit();
assert_eq!(slice, b"");
}
#[test]
fn scratch_drop_without_commit() {
let arena = Arena::new();
arena.alloc(8);
let ptr_before = arena.ptr.get();
{
let mut scratch = unsafe { arena.scratch() };
scratch.extend(b"discarded");
}
assert_eq!(arena.ptr.get(), ptr_before);
let mut scratch = unsafe { arena.scratch() };
scratch.extend(b"kept");
let committed = scratch.commit();
assert_eq!(committed, b"kept");
}
#[test]
#[allow(clippy::drop_non_drop)]
fn scratch_growth() {
let arena = Arena::new();
arena.alloc(8);
let mut scratch = unsafe { arena.scratch() };
let pattern: Vec<u8> = (0u8..=255).cycle().take(2048).collect();
scratch.extend(&pattern);
assert_eq!(scratch.as_bytes(), &pattern[..]);
drop(scratch);
let mut scratch = unsafe { arena.scratch() };
for i in 0..2048u16 {
scratch.push((i & 0xFF) as u8);
}
assert_eq!(scratch.as_bytes().len(), 2048);
for (i, &b) in scratch.as_bytes().iter().enumerate() {
assert_eq!(b, (i & 0xFF) as u8, "mismatch at index {i}");
}
drop(scratch);
let mut scratch = unsafe { arena.scratch() };
let mut expected = Vec::new();
for round in 0u8..10 {
let chunk: Vec<u8> = std::iter::repeat_n(round, 512).collect();
scratch.extend(&chunk);
expected.extend(&chunk);
}
assert_eq!(scratch.as_bytes(), &expected[..]);
drop(scratch);
let mut scratch = unsafe { arena.scratch() };
let data: Vec<u8> = (0..2048).map(|i| (i % 251) as u8).collect();
scratch.extend(&data);
let committed = scratch.commit();
assert_eq!(committed, &data[..]);
let next = arena.alloc(64);
let committed_end = committed.as_ptr() as usize + committed.len();
assert!(next.as_ptr() as usize >= committed_end);
}
#[test]
fn alloc_then_scratch_then_alloc() {
let arena = Arena::new();
let a = arena.alloc(32);
unsafe { std::ptr::write_bytes(a.as_ptr(), 0xAA, 32) };
let mut scratch = unsafe { arena.scratch() };
scratch.extend(b"middle");
let mid = scratch.commit();
assert_eq!(mid, b"middle");
let b = arena.alloc(32);
unsafe { std::ptr::write_bytes(b.as_ptr(), 0xBB, 32) };
let first_bytes = unsafe { std::slice::from_raw_parts(a.as_ptr(), 32) };
assert!(first_bytes.iter().all(|&b| b == 0xAA));
}
#[test]
#[should_panic(expected = "layout overflow")]
fn alloc_usize_max_panics() {
let arena = Arena::new();
arena.alloc(8);
arena.alloc(usize::MAX);
}
#[test]
#[should_panic(expected = "layout overflow")]
fn alloc_usize_max_unaligned_panics() {
let arena = Arena::new();
arena.alloc(1);
arena.alloc(usize::MAX);
}
#[test]
#[should_panic(expected = "layout overflow")]
fn alloc_overflow_smallest_wrapping_size() {
let arena = Arena::new();
arena.alloc(1); arena.alloc(usize::MAX - (ALLOC_ALIGN - 2));
}
#[test]
fn alloc_overflow_preserves_arena_state() {
let arena = Arena::new();
arena.alloc(8);
let ptr_before = arena.ptr.get();
let end_before = arena.end.get();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
arena.alloc(usize::MAX);
}));
assert!(result.is_err());
assert_eq!(arena.ptr.get(), ptr_before);
assert_eq!(arena.end.get(), end_before);
let p = arena.alloc(16);
assert_eq!(p.as_ptr() as usize % ALLOC_ALIGN, 0);
}
#[test]
#[should_panic(expected = "layout overflow")]
fn realloc_usize_max_panics() {
let arena = Arena::new();
let ptr = arena.alloc(8);
unsafe { arena.realloc(ptr, 8, usize::MAX) };
}