use ps_alloc::{
alloc, free, realloc, AllocationError, DeallocationError, ReallocationError, HEADER_SIZE,
};
#[test]
fn alloc_free_roundtrip() {
let ptr = alloc(100).unwrap();
unsafe {
for i in 0..100 {
ptr.add(i).write(i as u8);
}
for i in 0..100 {
assert_eq!(ptr.add(i).read(), i as u8);
}
free(ptr).unwrap();
}
}
#[test]
fn alloc_zero_size() {
let ptr = alloc(0).unwrap();
assert!(!ptr.is_null());
assert_eq!(ptr as usize % 16, 0);
unsafe { free(ptr).unwrap() };
}
#[test]
fn alloc_alignment() {
for size in [1, 2, 3, 8, 15, 16, 17, 31, 32, 33, 64, 100, 1000] {
let ptr = alloc(size).unwrap();
assert_eq!(ptr as usize % 16, 0, "size {size}");
unsafe { free(ptr).unwrap() };
}
}
#[test]
fn alloc_overflow() {
assert_eq!(alloc(usize::MAX), Err(AllocationError::ArithmeticError));
}
#[test]
fn alloc_layout_error() {
let result = alloc(isize::MAX as usize);
assert!(
matches!(result, Err(AllocationError::LayoutError(_))),
"expected a layout error: {result:?}"
);
}
#[test]
fn free_null() {
let result = unsafe { free(std::ptr::null_mut()) };
assert_eq!(result, Err(DeallocationError::NullPtr));
}
#[test]
fn free_misaligned() {
let ptr = alloc(8).unwrap();
let result = unsafe { free(ptr.add(1)) };
assert_eq!(result, Err(DeallocationError::ImproperAlignment));
unsafe { free(ptr).unwrap() };
}
#[test]
#[cfg_attr(miri, ignore)]
fn double_free_detected() {
let ptr = alloc(8).unwrap();
unsafe {
free(ptr).unwrap();
let result = free(ptr);
assert!(
matches!(
result,
Err(DeallocationError::DoubleFree | DeallocationError::CorruptedMarker)
),
"double free was not detected: {result:?}"
);
}
}
#[test]
fn corrupted_marker_detected() {
let ptr = alloc(8).unwrap();
unsafe {
let header_ptr = ptr.sub(HEADER_SIZE);
let mut saved = [0u8; HEADER_SIZE];
std::ptr::copy_nonoverlapping(header_ptr, saved.as_mut_ptr(), HEADER_SIZE);
header_ptr.write_bytes(0, HEADER_SIZE);
assert_eq!(free(ptr), Err(DeallocationError::CorruptedMarker));
std::ptr::copy_nonoverlapping(saved.as_ptr(), header_ptr, HEADER_SIZE);
free(ptr).unwrap();
}
}
#[test]
fn realloc_grow_preserves_data() {
let ptr = alloc(16).unwrap();
unsafe {
for i in 0..16 {
ptr.add(i).write(i as u8);
}
let new_ptr = realloc(ptr, 64).unwrap();
for i in 0..16 {
assert_eq!(new_ptr.add(i).read(), i as u8);
}
free(new_ptr).unwrap();
}
}
#[test]
fn realloc_shrink_preserves_data() {
let ptr = alloc(64).unwrap();
unsafe {
for i in 0..64 {
ptr.add(i).write(i as u8);
}
let new_ptr = realloc(ptr, 8).unwrap();
for i in 0..8 {
assert_eq!(new_ptr.add(i).read(), i as u8);
}
free(new_ptr).unwrap();
}
}
#[test]
fn realloc_null_is_alloc() {
let ptr = unsafe { realloc(std::ptr::null_mut(), 32) }.unwrap();
assert!(!ptr.is_null());
unsafe { free(ptr).unwrap() };
}
#[test]
fn realloc_null_zero_is_alloc_zero() {
let ptr = unsafe { realloc(std::ptr::null_mut(), 0) }.unwrap();
assert!(!ptr.is_null());
unsafe { free(ptr).unwrap() };
}
#[test]
fn realloc_zero_is_free() {
let ptr = alloc(32).unwrap();
let result = unsafe { realloc(ptr, 0) }.unwrap();
assert!(result.is_null());
}
#[test]
fn realloc_same_rounded_size_is_noop() {
let ptr = alloc(10).unwrap();
let new_ptr = unsafe { realloc(ptr, 16) }.unwrap();
assert_eq!(new_ptr, ptr);
unsafe { free(new_ptr).unwrap() };
}
#[test]
fn realloc_misaligned() {
let ptr = alloc(8).unwrap();
let result = unsafe { realloc(ptr.add(1), 8) };
assert_eq!(result, Err(ReallocationError::ImproperAlignment));
unsafe { free(ptr).unwrap() };
}
#[test]
fn realloc_corrupted_marker_detected() {
let ptr = alloc(8).unwrap();
unsafe {
let header_ptr = ptr.sub(HEADER_SIZE);
let mut saved = [0u8; HEADER_SIZE];
std::ptr::copy_nonoverlapping(header_ptr, saved.as_mut_ptr(), HEADER_SIZE);
header_ptr.write_bytes(0, HEADER_SIZE);
assert_eq!(realloc(ptr, 64), Err(ReallocationError::MarkerCorrupted));
std::ptr::copy_nonoverlapping(saved.as_ptr(), header_ptr, HEADER_SIZE);
free(ptr).unwrap();
}
}
#[test]
fn realloc_overflow_leaves_pointer_valid() {
let ptr = alloc(8).unwrap();
unsafe {
for i in 0..8 {
ptr.add(i).write(i as u8);
}
let result = realloc(ptr, usize::MAX);
assert_eq!(
result,
Err(ReallocationError::AllocationError(
AllocationError::ArithmeticError
))
);
for i in 0..8 {
assert_eq!(ptr.add(i).read(), i as u8);
}
free(ptr).unwrap();
}
}
#[test]
fn realloc_chain() {
unsafe {
let ptr = alloc(8).unwrap();
let ptr = realloc(ptr, 100).unwrap();
let ptr = realloc(ptr, 3000).unwrap();
let ptr = realloc(ptr, 5).unwrap();
free(ptr).unwrap();
}
}
#[test]
fn errors_are_send_sync() {
fn assert_send_sync<T: Send + Sync + Unpin>() {}
assert_send_sync::<AllocationError>();
assert_send_sync::<DeallocationError>();
assert_send_sync::<ReallocationError>();
}