use super::prev_power_of_two;
use alloc::collections::BTreeSet;
use core::alloc::Layout;
use core::cmp::{max, min};
use core::ops::Range;
#[cfg(feature = "use_spin")]
use core::ops::Deref;
#[cfg(feature = "use_spin")]
use spin::Mutex;
pub struct FrameAllocator<const ORDER: usize = 33> {
free_list: [BTreeSet<usize>; ORDER],
allocated: usize,
total: usize,
}
impl<const ORDER: usize> FrameAllocator<ORDER> {
pub const fn new() -> Self {
Self {
free_list: [const { BTreeSet::new() }; ORDER],
allocated: 0,
total: 0,
}
}
pub fn add_frame(&mut self, start: usize, end: usize) {
assert!(start <= end);
let mut total = 0;
let mut current_start = start;
let max_block_size = 1 << (ORDER - 1);
while current_start < end {
let lowbit = if current_start > 0 {
current_start & (!current_start + 1)
} else {
max_block_size
};
let size = min(
min(lowbit, prev_power_of_two(end - current_start)),
max_block_size,
);
total += size;
self.free_list[size.trailing_zeros() as usize].insert(current_start);
current_start += size;
}
self.total += total;
}
pub fn insert(&mut self, range: Range<usize>) {
self.add_frame(range.start, range.end);
}
pub fn alloc(&mut self, count: usize) -> Option<usize> {
let size = count.next_power_of_two();
self.alloc_power_of_two(size)
}
pub fn alloc_aligned(&mut self, layout: Layout) -> Option<usize> {
let size = max(layout.size().next_power_of_two(), layout.align());
self.alloc_power_of_two(size)
}
pub fn alloc_at(&mut self, start: usize, count: usize) -> Option<usize> {
let size = count.next_power_of_two();
self.alloc_at_power_of_two(start, size)
}
fn alloc_power_of_two(&mut self, size: usize) -> Option<usize> {
let class = size.trailing_zeros() as usize;
for i in class..self.free_list.len() {
if !self.free_list[i].is_empty() {
for j in (class + 1..i + 1).rev() {
if let Some(block_ref) = self.free_list[j].iter().next() {
let block = *block_ref;
self.free_list[j - 1].insert(block + (1 << (j - 1)));
self.free_list[j - 1].insert(block);
self.free_list[j].remove(&block);
} else {
return None;
}
}
let result = self.free_list[class].iter().next();
if let Some(result_ref) = result {
let result = *result_ref;
self.free_list[class].remove(&result);
self.allocated += size;
return Some(result);
} else {
return None;
}
}
}
None
}
fn alloc_at_power_of_two(&mut self, start: usize, size: usize) -> Option<usize> {
let class = size.trailing_zeros() as usize;
if start & (size - 1) != 0 {
return None;
}
for i in class..self.free_list.len() {
let block_start = start & !((1 << i) - 1);
if self.free_list[i].remove(&block_start) {
let mut current_start = block_start;
for j in (class..i).rev() {
let mid = current_start + (1 << j);
if start >= mid {
self.free_list[j].insert(current_start);
current_start = mid;
} else {
self.free_list[j].insert(mid);
}
}
self.allocated += size;
return Some(start);
}
}
None
}
pub fn dealloc(&mut self, start_frame: usize, count: usize) {
let size = count.next_power_of_two();
self.dealloc_power_of_two(start_frame, size)
}
pub fn dealloc_aligned(&mut self, start_frame: usize, layout: Layout) {
let size = max(layout.size().next_power_of_two(), layout.align());
self.dealloc_power_of_two(start_frame, size)
}
fn dealloc_power_of_two(&mut self, start_frame: usize, size: usize) {
let class = size.trailing_zeros() as usize;
let mut current_ptr = start_frame;
let mut current_class = class;
while current_class < self.free_list.len() - 1 {
let buddy = current_ptr ^ (1 << current_class);
if self.free_list[current_class].remove(&buddy) {
current_ptr = min(current_ptr, buddy);
current_class += 1;
} else {
break;
}
}
self.free_list[current_class].insert(current_ptr);
self.allocated -= size;
}
}
impl<const ORDER: usize> Default for FrameAllocator<ORDER> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "use_spin")]
#[derive(Default)]
pub struct LockedFrameAllocator<const ORDER: usize = 33>(Mutex<FrameAllocator<ORDER>>);
#[cfg(feature = "use_spin")]
impl<const ORDER: usize> LockedFrameAllocator<ORDER> {
pub const fn new() -> Self {
Self(Mutex::new(FrameAllocator::new()))
}
}
#[cfg(feature = "use_spin")]
impl<const ORDER: usize> Deref for LockedFrameAllocator<ORDER> {
type Target = Mutex<FrameAllocator<ORDER>>;
fn deref(&self) -> &Mutex<FrameAllocator<ORDER>> {
&self.0
}
}