feat: allocator: add block allocator

This commit is contained in:
Paul Pan 2024-04-07 00:20:02 +08:00
parent ced2b85b63
commit 0bb447ca28
2 changed files with 154 additions and 0 deletions

View File

@ -0,0 +1,153 @@
use crate::vspace::addr::{AddressOps, PhysAddr};
use core::alloc::Layout;
use core::cmp::min;
#[derive(Copy, Clone, Debug)]
struct Block {
start: PhysAddr,
size: usize,
}
impl Block {
fn start_addr(&self) -> PhysAddr {
self.start
}
fn end_addr(&self) -> PhysAddr {
self.start + self.size
}
fn could_merge(&self, start: PhysAddr, size: usize) -> bool {
self.start_addr() == start + size || self.end_addr() == start
}
fn merge(&mut self, start: PhysAddr, size: usize) -> bool {
if self.could_merge(start, size) {
self.start = min(self.start, start);
self.size += size;
true
} else {
false
}
}
fn could_fit(&self, layout: Layout) -> bool {
self.start_addr().align_up(layout.align()) + layout.size() <= self.end_addr()
}
}
#[derive(Debug)]
pub struct RamBlock<const N: usize> {
blocks: [Option<Block>; N],
}
impl<const N: usize> RamBlock<N> {
pub const fn new() -> Self {
Self { blocks: [None; N] }
}
fn insert(&mut self, start: PhysAddr, size: usize) -> Result<&mut Option<Block>, ()> {
for block in self.blocks.iter_mut() {
if block.is_none() {
*block = Some(Block { start, size });
return Ok(block);
}
}
Err(())
}
pub fn dealloc(&mut self, start: PhysAddr, size: usize) {
/* NOTE: blocks will not be fully merged, for example:
* we have [(0-10), (15-20)], if we dealloc (10, 15),
* we will have [(0-15), (15, 20)] instead of [(0-20)]
*/
// check whether we could combine with the previous block
for block in self.blocks.iter_mut().flatten() {
if block.merge(start, size) {
return;
}
}
// insert into a new slot
self.insert(start, size).expect("No free slot");
}
pub fn alloc(&mut self, layout: Layout) -> Option<PhysAddr> {
let victim = self
.blocks
.iter_mut()
.find(|block| block.is_some_and(|b| b.could_fit(layout)));
victim.as_ref()?;
let victim = victim.unwrap();
if let Some(block) = victim.take() {
let start = block.start_addr().align_up(layout.align());
let end = start + layout.size();
if block.end_addr() > end {
*victim = Some(Block {
start: end,
size: (block.end_addr() - end).as_usize(),
})
}
if block.start_addr() < start {
let _ = self.insert(block.start_addr(), (start - block.start_addr()).as_usize());
}
return Some(start);
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use log::debug;
#[test_case]
fn test_block() {
let mut blk = RamBlock::<4>::new();
blk.dealloc(PhysAddr(0), 100);
let ptr = blk.alloc(Layout::from_size_align(5, 8).unwrap());
assert_eq!(ptr, Some(PhysAddr(0)));
let ptr = blk.alloc(Layout::from_size_align(5, 8).unwrap());
assert_eq!(ptr, Some(PhysAddr(8)));
let ptr = blk.alloc(Layout::from_size_align(5, 1).unwrap());
assert_eq!(ptr, Some(PhysAddr(0xd)));
let ptr = blk.alloc(Layout::from_size_align(5, 0x20).unwrap());
assert_eq!(ptr, Some(PhysAddr(0x20)));
let ptr = blk.alloc(Layout::from_size_align(5, 0x40).unwrap());
assert_eq!(ptr, Some(PhysAddr(0x40)));
let ptr = blk.alloc(Layout::from_size_align(5, 0x80).unwrap());
assert_eq!(ptr, None);
blk.dealloc(PhysAddr(0), 5);
blk.dealloc(PhysAddr(32), 4);
let ptr = blk.alloc(Layout::from_size_align(31, 1).unwrap());
assert_eq!(ptr, Some(PhysAddr(0x45)));
let ptr = blk.alloc(Layout::from_size_align(8, 1).unwrap());
assert_eq!(ptr, Some(PhysAddr(0)));
let ptr = blk.alloc(Layout::from_size_align(18, 1).unwrap());
assert_eq!(ptr, Some(PhysAddr(0x12)));
let ptr = blk.alloc(Layout::from_size_align(27, 1).unwrap());
assert_eq!(ptr, Some(PhysAddr(0x25)));
let ptr = blk.alloc(Layout::from_size_align(1, 1).unwrap());
assert_eq!(ptr, None);
}
}

View File

@ -1,4 +1,5 @@
mod bitmap;
mod block;
mod freelist;
pub use bitmap::*;