use std::{alloc::Layout, cell::Cell, cmp, mem::ManuallyDrop, ptr::NonNull};
use super::{Arena, CHUNK_ALIGN, ChunkFooter};
const TOP: usize = (isize::MAX as usize) + 1;
struct TestArena<const MIN_ALIGN: usize = 1> {
inner: ManuallyDrop<Arena<MIN_ALIGN>>,
}
impl<const MIN_ALIGN: usize> TestArena<MIN_ALIGN> {
fn new(start: usize, cursor: usize) -> Self {
assert!(
start.is_multiple_of(CHUNK_ALIGN),
"start {start:#x} must be a multiple of CHUNK_ALIGN ({CHUNK_ALIGN})"
);
assert!(cursor >= start, "cursor {cursor:#x} must be >= start {start:#x}");
let capacity = cursor - start;
let chunk_align = cmp::max(MIN_ALIGN, CHUNK_ALIGN);
assert!(
Layout::from_size_align(capacity, chunk_align).is_ok(),
"capacity {capacity} (cursor {cursor:#x} - start {start:#x}) with align {chunk_align} must be a valid `Layout`",
);
let cursor_ptr = NonNull::new(cursor as *mut u8).unwrap();
let start_ptr = NonNull::new(start as *mut u8).unwrap();
let arena = Arena {
cursor_ptr: Cell::new(cursor_ptr),
current_chunk_footer_ptr: Cell::new(Some(cursor_ptr.cast::<ChunkFooter>())),
start_ptr: Cell::new(start_ptr),
can_grow: false,
#[cfg(all(feature = "track_allocations", not(feature = "disable_track_allocations")))]
stats: crate::tracking::AllocationStats::default(),
};
Self { inner: ManuallyDrop::new(arena) }
}
fn try_alloc_layout_fast(&self, layout: Layout) -> Option<NonNull<u8>> {
self.inner.try_alloc_layout_fast(layout)
}
}
#[test]
fn bottom_half_enough_room() {
let arena = TestArena::<1>::new(0x1000, 0x2000);
let layout = Layout::from_size_align(32, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x2000 - 32);
}
#[test]
fn bottom_half_exact_fit() {
let arena = TestArena::<1>::new(0x1000, 0x1020); let layout = Layout::from_size_align(32, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1000);
}
#[test]
fn bottom_half_not_enough_room() {
let arena = TestArena::<1>::new(0x1000, 0x1010); let layout = Layout::from_size_align(32, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn bottom_half_zero_capacity() {
let arena = TestArena::<1>::new(0x1000, 0x1000);
let layout = Layout::from_size_align(1, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn top_half_enough_room() {
let start = TOP + 0x1000;
let cursor = start + 0x1000;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(32, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 32);
}
#[test]
fn top_half_exact_fit() {
let start = TOP + 0x1000;
let cursor = start + 32;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(32, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), start);
}
#[test]
fn top_half_not_enough_room() {
let start = TOP + 0x1000;
let cursor = start + 16;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(32, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn top_half_zero_capacity() {
let start = TOP + 0x1000;
let arena = TestArena::<1>::new(start, start);
let layout = Layout::from_size_align(1, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn wrapping_sub_wraps_bottom_half() {
let arena = TestArena::<1>::new(0x100, 0x200);
let layout = Layout::from_size_align(0x1000, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn wrapping_sub_wraps_across_midpoint() {
let start = TOP;
let cursor = TOP + 0x10;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(0x20, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn zst_zero_capacity() {
let arena = TestArena::<1>::new(0x1000, 0x1000);
let layout = Layout::from_size_align(0, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1000);
}
#[test]
fn zst_top_half() {
let addr = TOP + 0x1000;
let arena = TestArena::<1>::new(addr, addr);
let layout = Layout::from_size_align(0, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), addr);
}
#[test]
fn greater_enough_room() {
let arena = TestArena::<1>::new(0x1000, 0x1037);
let layout = Layout::from_size_align(16, 8).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1030 - 16);
assert!(result.unwrap().as_ptr().addr().is_multiple_of(8));
}
#[test]
fn greater_not_enough_room() {
let arena = TestArena::<1>::new(0x1000, 0x1017);
let layout = Layout::from_size_align(32, 8).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn greater_rounds_below_start() {
let arena = TestArena::<1>::new(0x1010, 0x1013);
let layout = Layout::from_size_align(4, 16).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn greater_top_half() {
let start = TOP + 0x1000;
let cursor = start + 0x100;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(32, 8).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 32);
assert!(result.unwrap().as_ptr().addr().is_multiple_of(8));
}
#[test]
fn greater_top_half_rounds_below_start() {
let start = TOP + 0x10;
let cursor = TOP + 0x13;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(4, 16).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn max_size_equal_path() {
let layout = Layout::from_size_align(isize::MAX as usize, 1).unwrap();
let arena = TestArena::<1>::new(0x1000, 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
let arena = TestArena::<1>::new(TOP + 0x1000, TOP + 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn max_size_less_path_rounds_above_isize_max() {
let layout = Layout::from_size_align(isize::MAX as usize, 1).unwrap();
let arena = TestArena::<8>::new(0x1000, 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
let arena = TestArena::<8>::new(TOP + 0x1000, TOP + 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn max_size_less_path_min_align_16() {
let layout = Layout::from_size_align(isize::MAX as usize, 1).unwrap();
let arena = TestArena::<16>::new(0x1000, 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
let arena = TestArena::<16>::new(TOP + 0x1000, TOP + 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn max_size_greater_path() {
let layout = Layout::from_size_align((isize::MAX as usize) - 7, 8).unwrap();
let arena = TestArena::<1>::new(0x1000, 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
let arena = TestArena::<1>::new(TOP + 0x1000, TOP + 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn near_max_size_less_path_already_aligned() {
let layout = Layout::from_size_align((isize::MAX as usize) - 7, 1).unwrap();
let arena = TestArena::<8>::new(0x1000, 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn boundary_greater_path_max_layout_with_max_padding() {
let layout = Layout::from_size_align((isize::MAX as usize) - 255, 256).unwrap();
let arena = TestArena::<1>::new(0xF0, 0xF0);
assert!(arena.try_alloc_layout_fast(layout).is_none());
let arena = TestArena::<1>::new(TOP + 0xF0, TOP + 0xF0);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn boundary_total_subtraction_equals_isize_max_plus_one() {
let layout = Layout::from_size_align((isize::MAX as usize) - 14, 1).unwrap();
let arena = TestArena::<16>::new(0x1000, 0x1000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
let arena = TestArena::<16>::new(TOP + 0x1000, TOP + 0x1000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn near_max_size_less_path_rounds_to_near_max() {
let layout = Layout::from_size_align((isize::MAX as usize) - 8, 1).unwrap();
let arena = TestArena::<8>::new(0x1000, 0x2000);
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn consecutive_allocations() {
let arena = TestArena::<1>::new(0x1000, 0x1100); let layout = Layout::from_size_align(32, 1).unwrap();
let p1 = arena.try_alloc_layout_fast(layout).unwrap();
assert_eq!(p1.as_ptr().addr(), 0x1100 - 32);
let p2 = arena.try_alloc_layout_fast(layout).unwrap();
assert_eq!(p2.as_ptr().addr(), 0x1100 - 64);
let p3 = arena.try_alloc_layout_fast(layout).unwrap();
assert_eq!(p3.as_ptr().addr(), 0x1100 - 96);
}
#[test]
fn consecutive_allocations_top_half() {
let start = TOP + 0x1000;
let cursor = start + 0x100;
let arena = TestArena::<8>::new(start, cursor);
let layout = Layout::from_size_align(32, 8).unwrap();
let p1 = arena.try_alloc_layout_fast(layout).unwrap();
assert_eq!(p1.as_ptr().addr(), cursor - 32);
let p2 = arena.try_alloc_layout_fast(layout).unwrap();
assert_eq!(p2.as_ptr().addr(), cursor - 64);
}
#[test]
fn fill_then_fail() {
let arena = TestArena::<1>::new(0x1000, 0x1040); let layout = Layout::from_size_align(16, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_some()); assert!(arena.try_alloc_layout_fast(layout).is_some()); assert!(arena.try_alloc_layout_fast(layout).is_some()); assert!(arena.try_alloc_layout_fast(layout).is_some()); assert!(arena.try_alloc_layout_fast(layout).is_none()); }
#[test]
fn off_by_one_too_small() {
let arena = TestArena::<1>::new(0x1000, 0x101F); let layout = Layout::from_size_align(32, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn off_by_one_exact() {
let arena = TestArena::<1>::new(0x1000, 0x1020); let layout = Layout::from_size_align(32, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_some());
}
#[test]
fn off_by_one_just_enough() {
let arena = TestArena::<1>::new(0x1000, 0x1021); let layout = Layout::from_size_align(32, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_some());
}
#[test]
fn off_by_one_top_half() {
let start = TOP + 0x1000;
let arena_small = TestArena::<1>::new(start, start + 31);
let arena_exact = TestArena::<1>::new(start, start + 32);
let layout = Layout::from_size_align(32, 1).unwrap();
assert!(arena_small.try_alloc_layout_fast(layout).is_none());
assert!(arena_exact.try_alloc_layout_fast(layout).is_some());
}
#[test]
fn chunk_spans_midpoint() {
let start = TOP - 0x100;
let cursor = TOP + 0x100;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(16, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 16);
}
#[test]
fn chunk_spans_midpoint_not_enough_room() {
let start = TOP - 0x10;
let cursor = TOP + 0x10; let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(64, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn near_usize_max() {
let cursor = usize::MAX - (CHUNK_ALIGN - 1); let start = cursor - 0x100;
let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(16, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 16);
}
#[test]
fn near_usize_max_not_enough_room() {
let cursor = usize::MAX - (CHUNK_ALIGN - 1);
let start = cursor - 0x20; let arena = TestArena::<1>::new(start, cursor);
let layout = Layout::from_size_align(64, 1).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn size_one_bottom_half() {
let arena = TestArena::<1>::new(0x1000, 0x1001);
let layout = Layout::from_size_align(1, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1000);
}
#[test]
fn size_one_top_half() {
let start = TOP + 0x1000;
let arena = TestArena::<1>::new(start, start + 1);
let layout = Layout::from_size_align(1, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), start);
}
#[test]
fn zst_greater_path() {
let arena = TestArena::<1>::new(0x1000, 0x1037);
let layout = Layout::from_size_align(0, 8).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1030);
}
#[test]
fn zst_greater_path_exact_start() {
let arena = TestArena::<1>::new(0x1000, 0x1003);
let layout = Layout::from_size_align(0, 16).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1000);
}
#[test]
fn greater_cursor_already_aligned() {
let arena = TestArena::<1>::new(0x1000, 0x1040);
let layout = Layout::from_size_align(16, 8).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1040 - 16);
}
#[test]
fn arena16_equal_path() {
let arena = TestArena::<16>::new(0x1000, 0x1100);
let layout = Layout::from_size_align(48, 16).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1100 - 48);
assert!(result.unwrap().as_ptr().addr().is_multiple_of(16));
}
#[test]
fn arena16_less_path() {
let arena = TestArena::<16>::new(0x1000, 0x1100);
let layout = Layout::from_size_align(5, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1100 - 16);
}
#[test]
fn arena16_top_half() {
let start = TOP + 0x1000;
let cursor = start + 0x100;
let arena = TestArena::<16>::new(start, cursor);
let layout = Layout::from_size_align(32, 16).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 32);
assert!(result.unwrap().as_ptr().addr().is_multiple_of(16));
}
#[test]
fn lowest_valid_start() {
let cursor = CHUNK_ALIGN + 0x100;
let arena = TestArena::<1>::new(CHUNK_ALIGN, cursor);
let layout = Layout::from_size_align(32, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 32);
}
#[test]
fn lowest_valid_start_not_enough_room() {
let arena = TestArena::<1>::new(CHUNK_ALIGN, CHUNK_ALIGN + 16);
let layout = Layout::from_size_align(32, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_none());
}
#[test]
fn less_rounds_size_up() {
let arena = TestArena::<8>::new(0x1000, 0x1100);
let layout = Layout::from_size_align(3, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), 0x1100 - 8);
}
#[test]
fn less_top_half() {
let start = TOP + 0x1000;
let cursor = start + 0x100;
let arena = TestArena::<8>::new(start, cursor);
let layout = Layout::from_size_align(3, 1).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 8);
}
#[test]
fn equal_top_half() {
let start = TOP + 0x1000;
let cursor = start + 0x100;
let arena = TestArena::<8>::new(start, cursor);
let layout = Layout::from_size_align(32, 8).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 32);
}
#[test]
fn equal_not_enough_room_top_half() {
let start = TOP + 0x1000;
let cursor = start + 16; let arena = TestArena::<8>::new(start, cursor);
let layout = Layout::from_size_align(32, 8).unwrap();
assert!(arena.try_alloc_layout_fast(layout).is_none());
}
#[test]
fn over_aligned() {
let start = 0x1000;
let cursor = start + 0x1008;
let arena = TestArena::<8>::new(start, cursor);
let layout = Layout::from_size_align(8, 16).unwrap();
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 8);
assert!(result.unwrap().as_ptr().addr().is_multiple_of(16));
let result = arena.try_alloc_layout_fast(layout);
assert!(result.is_some());
assert_eq!(result.unwrap().as_ptr().addr(), cursor - 24);
assert!(result.unwrap().as_ptr().addr().is_multiple_of(16));
}