use lite_alloc::reset_heap;
use lite_alloc::single_threaded::SegregatedBumpAllocator;
use std::alloc::{GlobalAlloc, Layout};
use std::sync::{Mutex, MutexGuard};
static TEST_MUTEX: Mutex<()> = Mutex::new(());
struct SafeAllocator {
inner: SegregatedBumpAllocator,
_guard: MutexGuard<'static, ()>,
}
impl SafeAllocator {
fn new() -> Self {
let guard = TEST_MUTEX.lock().unwrap_or_else(|p| p.into_inner());
unsafe {
SegregatedBumpAllocator::reset();
reset_heap();
Self {
inner: SegregatedBumpAllocator::new(),
_guard: guard,
}
}
}
fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { self.inner.alloc(layout) }
}
fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { self.inner.dealloc(ptr, layout) }
}
#[cfg(feature = "realloc")]
fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
unsafe { self.inner.realloc(ptr, layout, new_size) }
}
}
impl Drop for SafeAllocator {
fn drop(&mut self) {
unsafe {
SegregatedBumpAllocator::reset();
reset_heap();
}
}
}
#[test]
fn test_small_bins() {
let allocator = SafeAllocator::new();
let layout16 = Layout::from_size_align(10, 1).unwrap();
let ptr1 = allocator.alloc(layout16);
assert!(!ptr1.is_null());
let layout32 = Layout::from_size_align(20, 1).unwrap();
let ptr2 = allocator.alloc(layout32);
assert!(!ptr2.is_null());
allocator.dealloc(ptr1, layout16);
let ptr3 = allocator.alloc(layout16);
assert_eq!(ptr1, ptr3);
assert_ne!(ptr3, ptr2);
}
#[test]
fn test_large_bypass() {
let allocator = SafeAllocator::new();
let layout_large = Layout::from_size_align(200, 16).unwrap();
let ptr1 = allocator.alloc(layout_large);
allocator.dealloc(ptr1, layout_large);
let ptr2 = allocator.alloc(layout_large);
assert_ne!(ptr1, ptr2);
}
#[test]
fn test_mixed_bins() {
let allocator = SafeAllocator::new();
let l16 = Layout::from_size_align(16, 16).unwrap();
let l32 = Layout::from_size_align(32, 16).unwrap();
let l64 = Layout::from_size_align(64, 16).unwrap();
let l128 = Layout::from_size_align(128, 16).unwrap();
let p1 = allocator.alloc(l16);
let p2 = allocator.alloc(l32);
let p3 = allocator.alloc(l64);
let p4 = allocator.alloc(l128);
let buckets = [p1, p2, p3, p4];
for i in 0..4 {
for j in i + 1..4 {
assert_ne!(buckets[i], buckets[j]);
}
}
allocator.dealloc(p2, l32);
allocator.dealloc(p4, l128);
let p2_new = allocator.alloc(l32);
assert_eq!(p2, p2_new);
let p4_new = allocator.alloc(l128);
assert_eq!(p4, p4_new);
}
#[test]
fn test_high_alignment_bypass() {
let allocator = SafeAllocator::new();
let layout = Layout::from_size_align(16, 128).unwrap();
let ptr = allocator.alloc(layout);
assert!(!ptr.is_null());
assert_eq!(ptr as usize % 128, 0);
allocator.dealloc(ptr, layout);
}
#[cfg(feature = "realloc")]
#[test]
fn test_realloc_bin_growth() {
let allocator = SafeAllocator::new();
let l16 = Layout::from_size_align(16, 16).unwrap();
let ptr = allocator.alloc(l16);
unsafe { ptr.write(0x11) };
let _obstacle = allocator.alloc(l16);
let ptr_new = allocator.realloc(ptr, l16, 32);
assert_ne!(ptr, ptr_new); unsafe { assert_eq!(*ptr_new, 0x11) };
}