use alloc::vec::Vec;
use core::{fmt, mem, ops, slice};
use redox_simple_endian::*;
use crate::BlockPtr;
pub const ALLOC_LIST_ENTRIES: usize = 255;
#[derive(Clone, Default)]
pub struct Allocator {
levels: Vec<Vec<u64>>,
}
impl Allocator {
pub fn levels(&self) -> &Vec<Vec<u64>> {
&self.levels
}
pub fn free(&self) -> u64 {
let mut free = 0;
for level in 0..self.levels.len() {
let level_size = 1 << level;
free += self.levels[level].len() as u64 * level_size;
}
free
}
pub fn allocate(&mut self) -> Option<u64> {
let mut addr_opt = None;
let mut level = 0;
while level < self.levels.len() {
if !self.levels[level].is_empty() {
addr_opt = self.levels[level].pop();
break;
}
level += 1;
}
let addr = addr_opt?;
while level > 0 {
level -= 1;
let level_size = 1 << level;
self.levels[level].push(addr + level_size);
}
Some(addr)
}
pub fn allocate_exact(&mut self, exact_addr: u64) -> Option<u64> {
let mut addr_opt = None;
for level in (0..self.levels.len()).rev() {
let level_size = 1 << level;
if let Some(addr) = addr_opt.take() {
self.levels[level].push(addr);
self.levels[level].push(addr + level_size);
}
for i in 0..self.levels[level].len() {
let start = self.levels[level][i];
if start <= exact_addr {
let end = start + level_size;
if end > exact_addr {
self.levels[level].remove(i);
addr_opt = Some(start);
break;
}
}
}
}
addr_opt
}
pub fn deallocate(&mut self, mut addr: u64) {
let mut level = 0;
loop {
while level >= self.levels.len() {
self.levels.push(Vec::new());
}
let level_size = 1 << level;
let next_size = level_size << 1;
let mut found = false;
let mut i = 0;
while i < self.levels[level].len() {
let level_addr = self.levels[level][i];
if addr % next_size == 0 && addr + level_size == level_addr {
self.levels[level].remove(i);
found = true;
break;
} else if level_addr % next_size == 0 && level_addr + level_size == addr {
self.levels[level].remove(i);
addr = level_addr;
found = true;
break;
}
i += 1;
}
if !found {
self.levels[level].push(addr);
return;
}
level += 1;
}
}
}
#[repr(packed)]
pub struct AllocEntry {
addr: u64le,
count: i64le,
}
impl AllocEntry {
pub fn new(addr: u64, count: i64) -> Self {
Self {
addr: addr.into(),
count: count.into(),
}
}
pub fn addr(&self) -> u64 {
{ self.addr }.to_native()
}
pub fn count(&self) -> i64 {
{ self.count }.to_native()
}
pub fn is_null(&self) -> bool {
self.count() == 0
}
}
impl Clone for AllocEntry {
fn clone(&self) -> Self {
Self {
addr: self.addr,
count: self.count,
}
}
}
impl Copy for AllocEntry {}
impl Default for AllocEntry {
fn default() -> Self {
Self {
addr: 0.into(),
count: 0.into(),
}
}
}
impl fmt::Debug for AllocEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let addr = self.addr();
let count = self.count();
f.debug_struct("AllocEntry")
.field("addr", &addr)
.field("count", &count)
.finish()
}
}
#[repr(packed)]
pub struct AllocList {
pub prev: BlockPtr<AllocList>,
pub entries: [AllocEntry; ALLOC_LIST_ENTRIES],
}
impl Default for AllocList {
fn default() -> Self {
Self {
prev: BlockPtr::default(),
entries: [AllocEntry::default(); ALLOC_LIST_ENTRIES],
}
}
}
impl fmt::Debug for AllocList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prev = self.prev;
let entries: Vec<&AllocEntry> = self
.entries
.iter()
.filter(|entry| entry.count() > 0)
.collect();
f.debug_struct("AllocList")
.field("prev", &prev)
.field("entries", &entries)
.finish()
}
}
impl ops::Deref for AllocList {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const AllocList as *const u8,
mem::size_of::<AllocList>(),
) as &[u8]
}
}
}
impl ops::DerefMut for AllocList {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut AllocList as *mut u8,
mem::size_of::<AllocList>(),
) as &mut [u8]
}
}
}
#[test]
fn alloc_node_size_test() {
assert_eq!(mem::size_of::<AllocList>(), crate::BLOCK_SIZE as usize);
}
#[test]
fn allocator_test() {
let mut alloc = Allocator::default();
assert_eq!(alloc.allocate(), None);
alloc.deallocate(1);
assert_eq!(alloc.allocate(), Some(1));
assert_eq!(alloc.allocate(), None);
for addr in 1023..2048 {
alloc.deallocate(addr);
}
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
if level == 0 {
assert_eq!(alloc.levels[level], [1023]);
} else if level == 10 {
assert_eq!(alloc.levels[level], [1024]);
} else {
assert_eq!(alloc.levels[level], []);
}
}
for addr in 1023..2048 {
assert_eq!(alloc.allocate(), Some(addr));
}
assert_eq!(alloc.allocate(), None);
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
assert_eq!(alloc.levels[level], []);
}
}