240 lines
6.8 KiB
Rust

use core::{alloc::Layout, ops::Index, ptr::NonNull};
use crate::{
bucket::Bucket,
sys::{self, PageProvider},
util::{Assert, IsTrue},
};
struct BucketList<P: PageProvider, const N: usize, const M: usize>
where
[u64; M / 64]: Sized,
Assert<{ M % 64 == 0 }>: IsTrue,
{
head: Option<NonNull<Bucket<P, N, M>>>,
}
pub struct BucketAllocator<P: PageProvider> {
// 1024x64 = 16 pages
buckets_1024: BucketList<P, 1024, 64>,
// 512x64 = 8 pages
buckets_512: BucketList<P, 512, 64>,
// 256x128 = 8 pages
buckets_256: BucketList<P, 256, 128>,
// 128x128 = 4 pages
buckets_128: BucketList<P, 128, 128>,
// 64x128 = 2 pages
buckets_64: BucketList<P, 128, 128>,
// 32x256 = 2 pages
buckets_32: BucketList<P, 128, 128>,
}
impl<P: PageProvider, const N: usize, const M: usize> BucketList<P, N, M>
where
[u64; M / 64]: Sized,
Assert<{ M % 64 == 0 }>: IsTrue,
{
const fn new() -> Self {
Self { head: None }
}
fn allocate(&mut self) -> Option<NonNull<u8>> {
let mut node = self.head;
while let Some(mut bucket) = node {
let bucket = unsafe { bucket.as_mut() };
if let Some(ptr) = bucket.allocate() {
return Some(ptr);
}
node = bucket.next;
}
// No usable bucket found
let mut node = Bucket::new()?;
let bucket = unsafe { node.as_mut() };
bucket.next = self.head;
self.head = Some(node);
bucket.allocate()
}
unsafe fn free(&mut self, ptr: NonNull<u8>) {
let mut node = self.head;
while let Some(mut bucket) = node {
let bucket = bucket.as_mut();
if let (true, _last) = bucket.free(ptr) {
// TODO free the node if last?
return;
}
node = bucket.next;
}
panic!("Possible double free detected: pointer {:p} from bucket list {}x{}B, no corresponding bucket found", ptr, N, M);
}
}
impl<P: PageProvider, const N: usize, const M: usize> Index<usize> for BucketList<P, N, M>
where
[u64; M / 64]: Sized,
Assert<{ M % 64 == 0 }>: IsTrue,
{
type Output = Bucket<P, N, M>;
fn index(&self, index: usize) -> &Self::Output {
let mut current = 0;
let mut node = self.head;
while let Some(bucket) = node {
let bucket = unsafe { bucket.as_ref() };
if current == index {
return bucket;
}
current += 1;
node = bucket.next;
}
panic!(
"BucketList index out of range: contains {} buckets, tried to index {}",
current, index
);
}
}
impl<P: PageProvider> BucketAllocator<P> {
pub const fn new() -> Self {
Self {
buckets_1024: BucketList::new(),
buckets_512: BucketList::new(),
buckets_256: BucketList::new(),
buckets_128: BucketList::new(),
buckets_64: BucketList::new(),
buckets_32: BucketList::new(),
}
}
pub fn allocate(&mut self, layout: Layout) -> Option<NonNull<u8>> {
let aligned = layout.pad_to_align();
match aligned.size() {
size if size <= 32 => self.buckets_32.allocate(),
size if size <= 64 => self.buckets_64.allocate(),
size if size <= 128 => self.buckets_128.allocate(),
size if size <= 256 => self.buckets_256.allocate(),
size if size <= 512 => self.buckets_512.allocate(),
size if size <= 1024 => self.buckets_1024.allocate(),
size => P::map_pages(size.div_ceil(sys::PAGE_SIZE)),
}
}
/// # Safety
///
/// Unsafe: may cause double-free problem and panic
pub unsafe fn free(&mut self, ptr: NonNull<u8>, layout: Layout) {
let aligned = layout.pad_to_align();
match aligned.size() {
size if size <= 32 => self.buckets_32.free(ptr),
size if size <= 64 => self.buckets_64.free(ptr),
size if size <= 128 => self.buckets_128.free(ptr),
size if size <= 256 => self.buckets_256.free(ptr),
size if size <= 512 => self.buckets_512.free(ptr),
size if size <= 1024 => self.buckets_1024.free(ptr),
size => {
assert_eq!(usize::from(ptr.addr()) % sys::PAGE_SIZE, 0);
P::unmap_pages(ptr, size.div_ceil(sys::PAGE_SIZE));
}
}
}
}
unsafe impl<P: PageProvider> Sync for BucketAllocator<P> {}
#[cfg(test)]
mod tests {
use core::{alloc::Layout, ptr::NonNull};
use super::{BucketAllocator, BucketList};
use crate::sys::OsPageProvider;
#[test]
fn single_list_allocation() {
let mut list = BucketList::<OsPageProvider, 32, 64>::new();
let mut vec = vec![];
for _ in 0..4 * 64 + 3 {
let ptr = list.allocate().unwrap();
vec.push(ptr);
}
for ptr in vec {
unsafe {
list.free(ptr);
}
}
}
#[test]
fn multi_list_allocation() {
const SIZES: &[usize] = &[1, 3, 7, 15, 16, 24, 33, 65, 126, 255, 500, 1000];
let mut allocator = BucketAllocator::<OsPageProvider>::new();
let mut vec = vec![];
for _ in 0..65 {
for &size in SIZES {
let layout = Layout::from_size_align(size, 16).unwrap();
let ptr = allocator.allocate(layout).unwrap();
assert_eq!(usize::from(ptr.addr()) % 16, 0);
let mut slice = NonNull::slice_from_raw_parts(ptr, size);
unsafe {
slice.as_mut().fill(123);
}
vec.push((ptr, layout));
}
}
for (ptr, layout) in vec {
unsafe {
allocator.free(ptr, layout);
}
}
}
#[test]
#[should_panic]
fn double_free() {
let mut allocator = BucketAllocator::<OsPageProvider>::new();
let layout = Layout::from_size_align(63, 32).unwrap();
let ptr = allocator.allocate(layout).unwrap();
unsafe {
allocator.free(ptr, layout);
allocator.free(ptr, layout);
}
}
#[test]
fn large_alloc() {
const SIZES: &[usize] = &[2000, 2048, 4000, 4096, 8192];
let mut allocator = BucketAllocator::<OsPageProvider>::new();
let mut vec = vec![];
for &size in SIZES {
let layout = Layout::from_size_align(size, 32).unwrap();
let ptr = allocator.allocate(layout).unwrap();
vec.push((ptr, layout));
}
for (ptr, layout) in vec {
assert_eq!(usize::from(ptr.addr()) % 0x1000, 0);
unsafe {
allocator.free(ptr, layout);
}
}
}
}