use std::{num::NonZero, ptr};
use crate::runtime::{heap, raw::Memory};
pub(super) struct Segment<'a> {
pub(super) arena: &'a heap::raw::Arena,
pub(super) index: heap::raw::Index,
pub(super) order: heap::raw::Level,
}
pub(super) struct Vacant<'a> {
source: &'a mut heap::raw::State,
block: Block<'a>,
}
pub(super) struct Occupied<'a> {
pub(super) source: &'a mut heap::raw::State,
pub(super) block: Block<'a>,
}
pub(super) struct Block<'a> {
pub(super) arena: &'a heap::raw::Arena,
pub(super) index: heap::raw::Index,
pub(super) order: heap::raw::Order,
}
impl<'a> Segment<'a> {
pub(super) fn reserve(
mut self,
source: &'a mut heap::raw::State,
order: heap::raw::Order,
) -> Vacant<'a> {
for position in (order.index()..self.order.index()).rev() {
let level = heap::raw::Level::mask(position as u8);
self.order = level;
let width = 1usize.wrapping_shl(level.index() as u32);
let index = unsafe { NonZero::new_unchecked(self.index.0.get() + width) };
source.link(Self {
arena: self.arena,
index: heap::raw::Index(index),
order: self.order,
});
}
Vacant {
source,
block: Block {
arena: self.arena,
index: self.index,
order,
},
}
}
}
impl Vacant<'_> {
pub(super) fn publish(self) -> *mut u8 {
let pointer = self.block.write();
self.source.references += 1;
pointer
}
fn replace(self, old: Block<'_>) -> *mut u8 {
let pointer = self.block.write();
unsafe {
ptr::copy_nonoverlapping(old.pointer(), pointer, heap::raw::payload_bytes(old.order))
};
self.source.recycle(old.into_segment());
pointer
}
}
impl Occupied<'_> {
pub(super) fn release(self) -> Option<Memory> {
let memory = self.source.release();
if memory.is_none() {
self.source.recycle(self.block.into_segment());
}
memory
}
pub(super) fn grow(mut self, order: heap::raw::Order) -> *mut u8 {
if let Some(pointer) = self.grow_adjacent(order) {
return pointer;
}
let Self { source, block } = self;
source.reserve(block.arena, order, |next| next.replace(block))
}
fn grow_adjacent(&mut self, order: heap::raw::Order) -> Option<*mut u8> {
let start = self.block.index.get();
let width = 1usize << order.index();
let base = start & !(width - 1);
let arena = self.block.arena;
if width > arena.units() - base {
return None;
}
let previous = self.block.order;
let buddies = (previous.index()..order.index()).map(|position| {
let width = 1usize << position;
let buddy = (start ^ width) & !(width - 1);
let index = heap::raw::Index(unsafe { NonZero::new_unchecked(buddy + 1) });
(index, heap::raw::Level::mask(position as u8))
});
for (index, level) in buddies.clone() {
heap::raw::State::free(arena, index, level)?;
}
for (index, level) in buddies {
let free = unsafe { heap::raw::Free::new(arena, index, level) };
self.source.unlink(free);
}
self.block.order = order;
if base == start {
return Some(self.block.write_order());
}
let original = self.block.pointer();
self.block.index = heap::raw::Index(unsafe { NonZero::new_unchecked(base + 1) });
let pointer = self.block.write();
unsafe { ptr::copy_nonoverlapping(original, pointer, heap::raw::payload_bytes(previous)) };
Some(pointer)
}
}
impl<'a> Block<'a> {
fn pointer(&self) -> *mut u8 {
unsafe { self.arena.block(self.index).add(heap::raw::Header::BYTES) }
}
fn write(&self) -> *mut u8 {
let header = self.arena.block(self.index).cast::<heap::raw::Header>();
unsafe {
(&raw mut (*header).owner).write(Some(heap::raw::Owner::new(self.arena)));
};
self.write_order()
}
fn write_order(&self) -> *mut u8 {
let header = self.arena.block(self.index).cast::<heap::raw::Header>();
unsafe { (&raw mut (*header).metadata.order).write(self.order) };
self.pointer()
}
fn into_segment(self) -> Segment<'a> {
Segment {
arena: self.arena,
index: self.index,
order: self.order.widen(),
}
}
}
pub(super) struct Partition<'a> {
arena: &'a heap::raw::Arena,
offset: usize,
left: usize,
}
impl<'a> Partition<'a> {
pub(super) fn new(arena: &'a heap::raw::Arena) -> Self {
Self {
arena,
offset: 0,
left: arena.units(),
}
}
}
impl<'a> Iterator for Partition<'a> {
type Item = Segment<'a>;
fn next(&mut self) -> Option<Self::Item> {
let left = NonZero::new(self.left)?;
let order = heap::raw::Level::mask(left.ilog2() as u8);
let index = heap::raw::Index(unsafe { NonZero::new_unchecked(self.offset + 1) });
let width = 1usize.wrapping_shl(order.index() as u32);
self.offset += width;
self.left -= width;
Some(Segment {
arena: self.arena,
index,
order,
})
}
}