#![allow(clippy::bool_assert_comparison)]
use crate::allocator::*;
#[test]
fn test_basic_allocation() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr1 = alloc.alloc(layout);
assert!(!ptr1.is_null());
assert_eq!(ptr1.align_offset(8), 0);
let ptr2 = alloc.alloc(layout);
assert!(!ptr2.is_null());
assert!(ptr2 as usize > ptr1 as usize);
}
}
#[test]
fn test_dealloc_last() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr1 = alloc.alloc(layout);
let used1 = alloc.used();
let ptr2 = alloc.alloc(layout);
let used2 = alloc.used();
assert!(used2 > used1);
alloc.dealloc(ptr2, layout);
assert_eq!(alloc.used(), used1);
alloc.alloc(layout);
alloc.dealloc(ptr1, layout);
assert!(alloc.used() > used1);
}
}
#[test]
fn test_realloc_in_place() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr = alloc.alloc(layout);
core::ptr::write_bytes(ptr, 0x42, 32);
let new_ptr = alloc.realloc(ptr, layout, 64);
assert_eq!(new_ptr, ptr);
assert_eq!(*ptr, 0x42);
let shrink_ptr = alloc.realloc(new_ptr, Layout::from_size_align(64, 8).unwrap(), 32);
assert_eq!(shrink_ptr, ptr); }
}
#[test]
fn test_realloc_non_last() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr1 = alloc.alloc(layout);
core::ptr::write_bytes(ptr1, 0x11, 32);
let _ptr2 = alloc.alloc(layout);
let shrink_ptr = alloc.realloc(ptr1, layout, 16);
assert_eq!(shrink_ptr, ptr1);
assert_eq!(*ptr1, 0x11);
let grow_ptr = alloc.realloc(ptr1, layout, 64);
assert_ne!(grow_ptr, ptr1); assert_eq!(*grow_ptr, 0x11); }
}
#[test]
#[ignore] fn test_alignment() {
let alloc = BumpAllocator::<()>::new_test(4096);
unsafe {
for align in [1, 2, 4, 8, 16, 32, 64, 128] {
let layout = Layout::from_size_align(1, align).unwrap();
let ptr = alloc.alloc(layout);
assert!(!ptr.is_null());
assert_eq!(ptr.align_offset(align), 0, "Failed alignment {}", align);
}
}
}
#[test]
fn test_oom() {
let alloc = BumpAllocator::<()>::new_test(128);
let header_size = core::mem::size_of::<Header<()>>();
eprintln!("Header size: {}", header_size);
eprintln!("Total heap: 128");
eprintln!("Available after header: {}", 128 - header_size);
unsafe {
let layout = Layout::from_size_align(100, 8).unwrap();
let ptr1 = alloc.alloc(layout);
assert!(!ptr1.is_null());
let used_after_first = alloc.used();
eprintln!("Used after first alloc: {}", used_after_first);
eprintln!("Remaining: {}", 128 - header_size - used_after_first);
let layout2 = Layout::from_size_align(50, 8).unwrap();
let ptr2 = alloc.alloc(layout2);
eprintln!("Second alloc result: {:?}", ptr2);
assert!(ptr2.is_null(), "Second allocation should fail due to OOM");
}
}
#[test]
fn test_many_small_allocations() {
let alloc = BumpAllocator::<()>::new_test(4096);
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let mut ptrs = Vec::new();
for i in 0..100 {
let ptr = alloc.alloc(layout);
assert!(!ptr.is_null(), "Failed at allocation {}", i);
core::ptr::write(ptr as *mut u64, i);
ptrs.push(ptr);
}
for (i, &ptr) in ptrs.iter().enumerate() {
assert_eq!(*(ptr as *const u64), i as u64);
}
}
}
#[test]
fn test_overflow_detection() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(900, 8).unwrap();
let ptr1 = alloc.alloc(layout);
assert!(!ptr1.is_null());
let huge_layout = Layout::from_size_align(u32::MAX as usize, 8).unwrap();
let ptr2 = alloc.alloc(huge_layout);
assert!(ptr2.is_null());
}
}
#[test]
fn test_old_alloc_entire_heap() {
let heap_size = 128;
let alloc = BumpAllocator::<()>::new_test(heap_size);
unsafe {
let layout = Layout::from_size_align(1, size_of::<u8>()).unwrap();
let mut allocations = 0;
loop {
let ptr = alloc.alloc(layout);
if ptr.is_null() {
break;
}
allocations += 1;
}
let header_size = core::mem::size_of::<Header<()>>();
let expected_min = heap_size - header_size - 8; assert!(
allocations >= expected_min,
"Only allocated {} bytes out of {} available",
allocations,
heap_size - header_size
);
assert_eq!(alloc.alloc(layout), core::ptr::null_mut());
}
}
#[test]
#[ignore] fn test_old_check_alignment() {
let heap_size = 128;
let alloc = BumpAllocator::<()>::new_test(heap_size);
unsafe {
let ptr = alloc.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap());
assert_eq!(0, ptr.align_offset(size_of::<u8>()));
let ptr = alloc.alloc(Layout::from_size_align(1, size_of::<u16>()).unwrap());
assert_eq!(0, ptr.align_offset(size_of::<u16>()));
let ptr = alloc.alloc(Layout::from_size_align(1, size_of::<u32>()).unwrap());
assert_eq!(0, ptr.align_offset(size_of::<u32>()));
let ptr = alloc.alloc(Layout::from_size_align(1, size_of::<u64>()).unwrap());
assert_eq!(0, ptr.align_offset(size_of::<u64>()));
let ptr = alloc.alloc(Layout::from_size_align(1, size_of::<u128>()).unwrap());
assert_eq!(0, ptr.align_offset(size_of::<u128>()));
let ptr = alloc.alloc(Layout::from_size_align(1, 64).unwrap());
assert_eq!(0, ptr.align_offset(64));
}
}
#[test]
fn test_old_alloc_entire_block() {
let heap_size = 128;
let alloc = BumpAllocator::<()>::new_test(heap_size);
unsafe {
let header_size = core::mem::size_of::<Header<()>>();
let alloc_size = heap_size - header_size - 16;
let ptr = alloc.alloc(Layout::from_size_align(alloc_size, 8).unwrap());
assert_ne!(ptr, core::ptr::null_mut());
assert_eq!(0, ptr.align_offset(8));
}
}
#[test]
fn test_old_dealloc_does_nothing() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr1 = alloc.alloc(layout);
let ptr2 = alloc.alloc(layout);
let ptr3 = alloc.alloc(layout);
let used_after_3 = alloc.used();
alloc.dealloc(ptr1, layout);
assert_eq!(alloc.used(), used_after_3);
alloc.dealloc(ptr2, layout);
assert_eq!(alloc.used(), used_after_3);
alloc.dealloc(ptr3, layout);
assert!(alloc.used() < used_after_3);
}
}
#[test]
fn test_pointer_arithmetic_correctness() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr = alloc.alloc(layout);
assert!(!ptr.is_null());
let offset = alloc.to_offset(ptr);
let reconstructed = alloc.from_offset(offset);
assert_eq!(ptr, reconstructed, "Pointer reconstruction failed");
}
}
#[test]
fn test_alignment_overflow() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(900, 8).unwrap();
let ptr1 = alloc.alloc(layout);
assert!(!ptr1.is_null());
let layout2 = Layout::from_size_align(16, 64).unwrap();
let _ptr2 = alloc.alloc(layout2);
}
}
#[test]
fn test_size_overflow_detection() {
let alloc = BumpAllocator::<()>::new_test(256);
unsafe {
let huge_size = (u32::MAX as usize) + 1;
let layout = Layout::from_size_align(huge_size, 8).unwrap();
let ptr = alloc.alloc(layout);
assert!(ptr.is_null(), "Should reject oversized allocation");
}
}
#[test]
fn test_alignment_plus_size_overflow() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout1 = Layout::from_size_align(900, 8).unwrap();
let ptr1 = alloc.alloc(layout1);
assert!(!ptr1.is_null());
let layout2 = Layout::from_size_align(200, 128).unwrap();
let _ptr2 = alloc.alloc(layout2);
}
}
#[test]
fn test_global_state_functionality() {
#[derive(bytemuck::Zeroable)]
struct GlobalData {
counter: Cell<u32>,
flag: Cell<bool>,
}
let alloc = BumpAllocator::<GlobalData>::new_test(1024);
let global = alloc.global();
assert_eq!(global.counter.get(), 0);
assert_eq!(global.flag.get(), false);
global.counter.set(42);
global.flag.set(true);
let global2 = alloc.global();
assert_eq!(global2.counter.get(), 42);
assert_eq!(global2.flag.get(), true);
global.counter.set(100);
assert_eq!(global2.counter.get(), 100);
}
#[test]
fn test_global_state_survives_allocations() {
#[derive(bytemuck::Zeroable)]
struct Counter {
value: Cell<usize>,
}
let alloc = BumpAllocator::<Counter>::new_test(2048);
alloc.global().value.set(1);
unsafe {
for i in 0..10 {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr = alloc.alloc(layout);
assert!(!ptr.is_null(), "Allocation {} failed", i);
let expected = i + 1;
let actual = alloc.global().value.get();
assert_eq!(
actual, expected,
"Global state corrupted at iteration {}",
i
);
alloc.global().value.set(i + 2);
}
assert_eq!(alloc.global().value.get(), 11);
}
}
#[test]
fn test_zero_alignment_rejected() {
let result = Layout::from_size_align(32, 0);
assert!(result.is_err(), "Zero alignment should be rejected");
}
#[test]
fn test_non_power_of_two_alignment_rejected() {
let result = Layout::from_size_align(32, 3);
assert!(
result.is_err(),
"Non-power-of-2 alignment should be rejected"
);
}
#[test]
fn test_large_heap_simulation() {
let alloc = BumpAllocator::<()>::new_test(1024 * 1024);
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let mut last_ptr: *mut u8 = core::ptr::null_mut();
for _ in 0..1000 {
let ptr = alloc.alloc(layout);
assert!(!ptr.is_null());
if !last_ptr.is_null() {
assert!(ptr as usize > last_ptr as usize);
}
last_ptr = ptr;
}
let used = alloc.used();
assert!(used >= 8000); assert!(used < 20000); }
}
#[test]
fn test_realloc_overflow_protection() {
let alloc = BumpAllocator::<()>::new_test(1024);
unsafe {
let layout = Layout::from_size_align(32, 8).unwrap();
let ptr = alloc.alloc(layout);
assert!(!ptr.is_null());
let huge_size = u32::MAX as usize;
let new_ptr = alloc.realloc(ptr, layout, huge_size);
assert!(new_ptr.is_null());
}
}
#[test]
fn test_header_alignment_verification() {
#[derive(bytemuck::Zeroable)]
struct UnalignedGlobal {
_a: u8,
b: u64, }
let alloc = BumpAllocator::<UnalignedGlobal>::new_test(1024);
let global = alloc.global();
let ptr = &global.b as *const _ as usize;
assert_eq!(ptr % 8, 0, "u64 field should be 8-byte aligned");
}
#[test]
#[ignore] fn test_extreme_alignment_requirements() {
let alloc = BumpAllocator::<()>::new_test(8192);
unsafe {
for align in [64, 128, 256, 512] {
let layout = Layout::from_size_align(16, align).unwrap();
let ptr = alloc.alloc(layout);
if !ptr.is_null() {
assert_eq!(
ptr.align_offset(align),
0,
"Failed {}-byte alignment",
align
);
}
}
}
}