use crate::*;
macro_rules! new_zone {
() => {
ZoneAllocator {
small_slabs: [
SCAllocator::new(1 << 3), SCAllocator::new(1 << 4), SCAllocator::new(1 << 5), SCAllocator::new(1 << 6), SCAllocator::new(1 << 7), SCAllocator::new(1 << 8), ],
big_slabs: [
SCAllocator::new(1 << 9), SCAllocator::new(1 << 10), SCAllocator::new(1 << 11), SCAllocator::new(1 << 12), SCAllocator::new(1 << 13), SCAllocator::new(1 << 14), SCAllocator::new(1 << 15), SCAllocator::new(1 << 16), SCAllocator::new(1 << 17), ],
}
};
}
pub struct ZoneAllocator<'a> {
small_slabs: [SCAllocator<'a, ObjectPage<'a>>; ZoneAllocator::MAX_BASE_SIZE_CLASSES],
big_slabs: [SCAllocator<'a, LargeObjectPage<'a>>; ZoneAllocator::MAX_LARGE_SIZE_CLASSES],
}
impl<'a> Default for ZoneAllocator<'a> {
fn default() -> ZoneAllocator<'a> {
new_zone!()
}
}
enum Slab {
Base(usize),
Large(usize),
Unsupported,
}
impl<'a> ZoneAllocator<'a> {
pub const MAX_ALLOC_SIZE: usize = 1 << 17;
pub const MAX_BASE_ALLOC_SIZE: usize = 256;
const MAX_BASE_SIZE_CLASSES: usize = 6;
const MAX_LARGE_SIZE_CLASSES: usize = 9;
#[cfg(feature = "unstable")]
pub const fn new() -> ZoneAllocator<'a> {
new_zone!()
}
#[cfg(not(feature = "unstable"))]
pub fn new() -> ZoneAllocator<'a> {
new_zone!()
}
pub fn get_max_size(current_size: usize) -> Option<usize> {
match current_size {
0..=8 => Some(8),
9..=16 => Some(16),
17..=32 => Some(32),
33..=64 => Some(64),
65..=128 => Some(128),
129..=256 => Some(256),
257..=512 => Some(512),
513..=1024 => Some(1024),
1025..=2048 => Some(2048),
2049..=4096 => Some(4096),
4097..=8192 => Some(8192),
8193..=16384 => Some(16384),
16385..=32767 => Some(32767),
32768..=65536 => Some(65536),
65537..=131_072 => Some(131_072),
_ => None,
}
}
fn get_slab(requested_size: usize) -> Slab {
match requested_size {
0..=8 => Slab::Base(0),
9..=16 => Slab::Base(1),
17..=32 => Slab::Base(2),
33..=64 => Slab::Base(3),
65..=128 => Slab::Base(4),
129..=256 => Slab::Base(5),
257..=512 => Slab::Large(0),
513..=1024 => Slab::Large(1),
1025..=2048 => Slab::Large(2),
2049..=4096 => Slab::Large(3),
4097..=8192 => Slab::Large(4),
8193..=16384 => Slab::Large(5),
16385..=32767 => Slab::Large(6),
32768..=65536 => Slab::Large(7),
65537..=131_072 => Slab::Large(8),
_ => Slab::Unsupported,
}
}
pub fn try_reclaim_base_pages<F>(&mut self, mut to_reclaim: usize, mut dealloc: F)
where
F: Fn(*mut ObjectPage),
{
for i in 0..ZoneAllocator::MAX_BASE_SIZE_CLASSES {
let slab = &mut self.small_slabs[i];
let just_reclaimed = slab.try_reclaim_pages(to_reclaim, &mut dealloc);
to_reclaim = to_reclaim.saturating_sub(just_reclaimed);
if to_reclaim == 0 {
break;
}
}
}
pub fn try_reclaim_large_pages<F>(&mut self, mut to_reclaim: usize, mut dealloc: F)
where
F: FnMut(*mut LargeObjectPage),
{
for i in 0..ZoneAllocator::MAX_LARGE_SIZE_CLASSES {
let slab = &mut self.big_slabs[i];
let just_reclaimed = slab.try_reclaim_pages(to_reclaim, &mut dealloc);
to_reclaim = to_reclaim.saturating_sub(just_reclaimed);
if to_reclaim == 0 {
break;
}
}
}
}
unsafe impl<'a> crate::Allocator<'a> for ZoneAllocator<'a> {
fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(idx) => self.small_slabs[idx].allocate(layout),
Slab::Large(idx) => self.big_slabs[idx].allocate(layout),
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(idx) => self.small_slabs[idx].deallocate(ptr, layout),
Slab::Large(idx) => self.big_slabs[idx].deallocate(ptr, layout),
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
unsafe fn refill(
&mut self,
layout: Layout,
new_page: &'a mut ObjectPage<'a>,
) -> Result<(), AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(idx) => {
self.small_slabs[idx].refill(new_page);
Ok(())
}
Slab::Large(_idx) => Err(AllocationError::InvalidLayout),
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
unsafe fn refill_large(
&mut self,
layout: Layout,
new_page: &'a mut LargeObjectPage<'a>,
) -> Result<(), AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(_idx) => Err(AllocationError::InvalidLayout),
Slab::Large(idx) => {
self.big_slabs[idx].refill(new_page);
Ok(())
}
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
}