extern crate alloc;
extern crate cbitmap;
use cbitmap::bitmap::*;
type Cacheline = [u8; 64];
type Page = [u8; 4096];
#[repr(C)]
struct CachelineManager<'a> {
_bitmap: &'a Bitmap<8>,
page: &'a mut Page,
}
#[derive(Debug)]
enum ManagerError {
Oor(i128),
Oom,
Realloc,
Unallocated,
Unknown,
}
use ManagerError::*;
impl<'a> CachelineManager<'a> {
const LINECNT: usize = 63;
unsafe fn get_map_ptr(&self) -> core::ptr::NonNull<Bitmap<8>> {
let raw = self.page.as_ptr().cast::<Bitmap<8>>();
raw.as_ref().unwrap().into()
}
unsafe fn get_lines_start_ptr(&self) -> core::ptr::NonNull<Cacheline> {
let raw = self.page.as_ptr().cast::<Cacheline>().add(1);
raw.as_ref().unwrap().into()
}
fn new(page: &'a mut Page) -> Self {
let bitmap = unsafe {
let mapptr = page.as_mut_ptr().cast::<Bitmap<8>>();
*mapptr = newmap!(0b1; 64);
mapptr.as_ref().unwrap()
};
Self {
_bitmap: bitmap,
page,
}
}
fn allocate(&self) -> Result<&mut Cacheline, ManagerError> {
unsafe {
let map = self.get_map_ptr().as_mut();
let start = self.get_lines_start_ptr().as_ptr();
let idx = map.find_first_zero().ok_or(Oom)?;
match map.test(idx) {
true => Err(Realloc),
false => {
map.set(idx);
start.add(idx - 1).as_mut().ok_or(Unknown)
}
}
}
}
fn deallocate(&self, line: &mut Cacheline) -> Result<(), ManagerError> {
let idx = self.get_idx(line)?;
unsafe {
let map = self.get_map_ptr().as_mut();
if !map.test(idx) {
Err(Unallocated)
} else {
map.reset(idx);
Ok(())
}
}
}
fn get_idx(&self, line: &Cacheline) -> Result<usize, ManagerError> {
let page = self.page.as_ptr() as usize;
let ptr = line.as_ptr() as usize;
if ptr < page {
Err(Oor(
((ptr as i128) - (page as i128)) / (core::mem::size_of::<Cacheline>() as i128)
))
} else {
let len = ptr - page;
let idx = len / core::mem::size_of::<Cacheline>();
match idx > Self::LINECNT {
true => Err(Oor(idx as i128)),
false => Ok(idx),
}
}
}
}
fn main() {
let mut page = Box::new([0u8; 4096]);
let manager = CachelineManager::new(page.as_mut());
let mut lines = vec![];
println!("Allocating 8 cachelines, their indexes:");
for _ in 0..8 {
lines.push(manager.allocate().unwrap());
}
for line in &mut lines {
let idx = manager.get_idx(line).unwrap() as u8;
println!("idx = {idx}");
line[0] = idx;
}
println!("Deallocating cacheline 7.");
let line7 = lines.remove(7 - 1);
manager.deallocate(line7).unwrap();
println!("Reallocating a cacheline, its idx should be 7.");
let newline = manager.allocate().unwrap();
assert_eq!(manager.get_idx(newline).unwrap(), 7);
lines.push(newline);
for _ in 8..63 {
lines.push(manager.allocate().unwrap());
}
println!("Using up the page: #lines = {}.", lines.len());
let err = manager.allocate();
assert!(err.is_err());
println!("Allocate another cacheline but failed: {err:?}");
let (err, ptr) = unsafe {
let line = lines.pop().unwrap();
let rawptr = line.as_mut_ptr().cast::<Cacheline>();
manager.deallocate(line).unwrap();
(manager.deallocate(rawptr.as_mut().unwrap()), rawptr)
};
assert!(err.is_err());
println!("Deallocate a cacheline twice but failed: {err:?}");
assert_eq!(lines.len(), 62);
let (err, ptr) = unsafe {
(manager.deallocate(ptr.add(1).as_mut().unwrap()), ptr)
};
assert!(err.is_err());
println!("Deallocate a cacheline at index 64 but failed: {err:?}");
let (err, _ptr) = unsafe {
(manager.deallocate(ptr.sub(64).as_mut().unwrap()), ptr)
};
assert!(err.is_err());
println!("Deallocate a cacheline at index -1 but failed: {err:?}");
}