use core::ptr::Unique;
use core::mem::{self, size_of};
use super::align_up;
pub struct HoleList {
first: Hole, }
impl HoleList {
pub const fn empty() -> HoleList {
HoleList {
first: Hole {
size: 0,
next: None,
},
}
}
pub unsafe fn new(hole_addr: usize, hole_size: usize) -> HoleList {
assert!(size_of::<Hole>() == Self::min_size());
let ptr = hole_addr as *mut Hole;
mem::forget(mem::replace(&mut *ptr,
Hole {
size: hole_size,
next: None,
}));
HoleList {
first: Hole {
size: 0,
next: Some(Unique::new(ptr)),
},
}
}
pub fn allocate_first_fit(&mut self, size: usize, align: usize) -> Option<*mut u8> {
assert!(size >= Self::min_size());
allocate_first_fit(&mut self.first, size, align).map(|allocation| {
if let Some(padding) = allocation.front_padding {
deallocate(&mut self.first, padding.addr, padding.size);
}
if let Some(padding) = allocation.back_padding {
deallocate(&mut self.first, padding.addr, padding.size);
}
allocation.info.addr as *mut u8
})
}
pub unsafe fn deallocate(&mut self, ptr: *mut u8, size: usize) {
deallocate(&mut self.first, ptr as usize, size)
}
pub fn min_size() -> usize {
size_of::<usize>() * 2
}
#[cfg(test)]
pub fn first_hole(&self) -> Option<(usize, usize)> {
self.first.next.as_ref().map(|hole| (**hole as usize, unsafe { hole.get().size }))
}
}
#[cfg(not(test))]
struct Hole {
size: usize,
next: Option<Unique<Hole>>,
}
#[cfg(test)]
pub struct Hole {
pub size: usize,
pub next: Option<Unique<Hole>>,
}
impl Hole {
fn info(&self) -> HoleInfo {
HoleInfo {
addr: self as *const _ as usize,
size: self.size,
}
}
fn next_unwrap(&mut self) -> &mut Hole {
unsafe { self.next.as_mut().unwrap().get_mut() }
}
}
#[derive(Debug, Clone, Copy)]
struct HoleInfo {
addr: usize,
size: usize,
}
struct Allocation {
info: HoleInfo,
front_padding: Option<HoleInfo>,
back_padding: Option<HoleInfo>,
}
fn split_hole(hole: HoleInfo, required_size: usize, required_align: usize) -> Option<Allocation> {
let aligned_hole = {
let aligned_hole_addr = align_up(hole.addr, required_align);
if aligned_hole_addr + required_size > hole.addr + hole.size {
return None;
}
HoleInfo {
addr: aligned_hole_addr,
size: hole.size - (aligned_hole_addr - hole.addr),
}
};
let front_padding = if aligned_hole.addr == hole.addr {
None
} else if aligned_hole.addr < hole.addr + HoleList::min_size() {
return None;
} else {
Some(HoleInfo {
addr: hole.addr,
size: aligned_hole.addr - hole.addr,
})
};
let back_padding = if aligned_hole.size == required_size {
None
} else if aligned_hole.size - required_size < HoleList::min_size() {
return None;
} else {
Some(HoleInfo {
addr: aligned_hole.addr + required_size,
size: aligned_hole.size - required_size,
})
};
Some(Allocation {
info: HoleInfo {
addr: aligned_hole.addr,
size: required_size,
},
front_padding: front_padding,
back_padding: back_padding,
})
}
fn allocate_first_fit(mut previous: &mut Hole, size: usize, align: usize) -> Option<Allocation> {
loop {
let allocation: Option<Allocation> = previous.next.as_mut().and_then(|current| {
split_hole(unsafe { current.get() }.info(), size, align)
});
match allocation {
Some(allocation) => {
previous.next = previous.next_unwrap().next.take();
return Some(allocation);
}
None if previous.next.is_some() => {
previous = move_helper(previous).next_unwrap();
}
None => {
return None;
}
}
}
}
fn deallocate(mut hole: &mut Hole, addr: usize, mut size: usize) {
loop {
assert!(size >= HoleList::min_size());
let hole_addr = if hole.size == 0 {
0
} else {
hole as *mut _ as usize
};
assert!(hole_addr + hole.size <= addr);
let next_hole_info = hole.next.as_ref().map(|next| unsafe { next.get().info() });
match next_hole_info {
Some(next) if hole_addr + hole.size == addr && addr + size == next.addr => {
hole.size += size + next.size; hole.next = hole.next_unwrap().next.take(); }
Some(_) if hole_addr + hole.size == addr => {
hole.size += size; }
Some(next) if addr + size == next.addr => {
hole.next = hole.next_unwrap().next.take(); size += next.size; continue;
}
Some(next) if next.addr <= addr => {
hole = move_helper(hole).next_unwrap(); continue;
}
_ => {
let new_hole = Hole {
size: size,
next: hole.next.take(), };
let ptr = addr as *mut Hole;
mem::forget(mem::replace(unsafe { &mut *ptr }, new_hole));
hole.next = Some(unsafe { Unique::new(ptr) });
}
}
break;
}
}
fn move_helper<T>(x: T) -> T {
x
}