use super::{BStackAllocator, BStackBulkAllocator, BStackSlice};
use crate::BStack;
use std::io;
const ALGT_MAGIC: [u8; 8] = *b"ALGT\x00\x01\x00\x00";
const ALGT_MAGIC_PREFIX: [u8; 6] = *b"ALGT\x00\x01";
const MAGIC_OFFSET: u64 = 32;
const ROOT_OFFSET: u64 = 40;
const ARENA_START: u64 = 48;
const MIN_ALLOC: u64 = 32;
const NULL_PTR: u64 = 0;
const NODE_SIZE_OFF: u64 = 0;
const NODE_BF_OFF: u64 = 8; const NODE_HEIGHT_OFF: u64 = 9; const NODE_LEFT_OFF: u64 = 16;
const NODE_RIGHT_OFF: u64 = 24;
macro_rules! read_buf {
($buf:expr, $off:expr => $ty:ty) => {{
let start = $off as usize;
let end = start + std::mem::size_of::<$ty>();
$buf[start..end].try_into().unwrap()
}};
}
macro_rules! write_buf {
($val:expr => $buf:expr, $off:expr) => {{
let bytes = $val.to_le_bytes();
let start = $off as usize;
let end = start + bytes.len();
$buf[start..end].copy_from_slice(&bytes);
}};
}
macro_rules! read_buf_le {
($buf:expr, $off:expr => $ty:ty) => {
<$ty>::from_le_bytes(read_buf!($buf, $off => $ty))
};
}
pub struct GhostTreeBstackAllocator {
stack: BStack,
}
impl GhostTreeBstackAllocator {
pub fn new(stack: BStack) -> io::Result<Self> {
let size = stack.len()?;
if size == 0 {
stack.extend(ARENA_START)?;
stack.set(MAGIC_OFFSET, ALGT_MAGIC)?;
return Ok(Self { stack });
}
if size < ARENA_START {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"GhostTreeBstackAllocator: payload is {size} B, \
too small for the {ARENA_START}-byte header"
),
));
}
let mut magic_buf = [0u8; 6];
stack.get_into(MAGIC_OFFSET, &mut magic_buf)?;
if magic_buf != ALGT_MAGIC_PREFIX {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"GhostTreeBstackAllocator: magic number mismatch",
));
}
let arena_used = size - ARENA_START;
let remainder = arena_used % 32;
if remainder != 0 {
stack.extend(32 - remainder)?;
}
let this = Self { stack };
this.coalesce_and_rebalance()?;
Ok(this)
}
}
impl GhostTreeBstackAllocator {
#[inline]
fn read_root(&self) -> io::Result<u64> {
let buf = &mut [0u8; 8];
self.stack.get_into(ROOT_OFFSET, buf)?;
Ok(read_buf_le!(buf, 0 => u64))
}
#[inline]
fn write_root(&self, ptr: u64) -> io::Result<()> {
let mut buf = [0u8; 8];
write_buf!(ptr => buf, 0);
self.stack.set(ROOT_OFFSET, buf)?;
Ok(())
}
fn read_node(&self, ptr: u64) -> io::Result<(u64, i8, u8, u64, u64)> {
let buf = &mut [0u8; 32];
self.stack.get_into(ptr, buf)?;
let size = read_buf_le!(buf, NODE_SIZE_OFF => u64);
let bf = read_buf_le!(buf, NODE_BF_OFF => i8);
let height = read_buf_le!(buf, NODE_HEIGHT_OFF => u8);
let left = read_buf_le!(buf, NODE_LEFT_OFF => u64);
let right = read_buf_le!(buf, NODE_RIGHT_OFF => u64);
Ok((size, bf, height, left, right))
}
fn write_node(
&self,
ptr: u64,
size: u64,
bf: i8,
height: u8,
left: u64,
right: u64,
) -> io::Result<()> {
let mut buf = [0u8; 32];
write_buf!(size => buf, NODE_SIZE_OFF);
write_buf!(bf => buf, NODE_BF_OFF);
write_buf!(height => buf, NODE_HEIGHT_OFF);
write_buf!(left => buf, NODE_LEFT_OFF);
write_buf!(right => buf, NODE_RIGHT_OFF);
self.stack.set(ptr, buf)?;
Ok(())
}
#[inline]
fn align_up_ptr(ptr: u64) -> u64 {
((ptr + 15) & !31) + 16
}
#[inline]
fn align_up_len(len: u64) -> u64 {
((len + 31) & !31).max(MIN_ALLOC)
}
#[inline]
fn avl_height(&self, ptr: u64) -> io::Result<u8> {
if ptr == NULL_PTR {
return Ok(0);
}
let (_, _, height, _, _) = self.read_node(ptr)?;
Ok(height)
}
#[inline]
fn avl_write_and_update(&self, ptr: u64, size: u64, left: u64, right: u64) -> io::Result<i8> {
let lh = self.avl_height(left)? as i16;
let rh = self.avl_height(right)? as i16;
let bf = (rh - lh) as i8;
let height = (1 + lh.max(rh)) as u8;
self.write_node(ptr, size, bf, height, left, right)?;
Ok(bf)
}
#[inline]
fn avl_update_bf(&self, node: u64) -> io::Result<i8> {
let (size, _, _, left, right) = self.read_node(node)?;
self.avl_write_and_update(node, size, left, right)
}
fn avl_rotate_right(&self, node: u64) -> io::Result<u64> {
let (node_sz, _, _, pivot, node_r) = self.read_node(node)?;
let (pivot_sz, _, _, pivot_l, pivot_r) = self.read_node(pivot)?;
self.avl_write_and_update(node, node_sz, pivot_r, node_r)?;
self.avl_write_and_update(pivot, pivot_sz, pivot_l, node)?;
Ok(pivot)
}
fn avl_rotate_left(&self, node: u64) -> io::Result<u64> {
let (node_sz, _, _, node_l, pivot) = self.read_node(node)?;
let (pivot_sz, _, _, pivot_l, pivot_r) = self.read_node(pivot)?;
self.avl_write_and_update(node, node_sz, node_l, pivot_l)?;
self.avl_write_and_update(pivot, pivot_sz, node, pivot_r)?;
Ok(pivot)
}
fn avl_rebalance(&self, node: u64) -> io::Result<u64> {
let bf = self.avl_update_bf(node)?;
if bf < -1 {
let (_, _, _, left, _) = self.read_node(node)?;
let (_, left_bf, _, _, _) = self.read_node(left)?;
if left_bf > 0 {
let new_left = self.avl_rotate_left(left)?;
let (node_sz, _, _, _, node_r) = self.read_node(node)?;
self.avl_write_and_update(node, node_sz, new_left, node_r)?;
}
self.avl_rotate_right(node)
} else if bf > 1 {
let (_, _, _, _, right) = self.read_node(node)?;
let (_, right_bf, _, _, _) = self.read_node(right)?;
if right_bf < 0 {
let new_right = self.avl_rotate_right(right)?;
let (node_sz, _, _, node_l, _) = self.read_node(node)?;
self.avl_write_and_update(node, node_sz, node_l, new_right)?;
}
self.avl_rotate_left(node)
} else {
Ok(node)
}
}
fn avl_insert_rec(&self, root: u64, ptr: u64, size: u64) -> io::Result<u64> {
if root == NULL_PTR {
self.write_node(ptr, size, 0, 1, NULL_PTR, NULL_PTR)?;
return Ok(ptr);
}
let (root_sz, _, _, left, right) = self.read_node(root)?;
if (size, ptr) < (root_sz, root) {
let new_left = self.avl_insert_rec(left, ptr, size)?;
self.avl_write_and_update(root, root_sz, new_left, right)?;
} else {
let new_right = self.avl_insert_rec(right, ptr, size)?;
self.avl_write_and_update(root, root_sz, left, new_right)?;
}
self.avl_rebalance(root)
}
fn avl_insert(&self, ptr: u64, size: u64) -> io::Result<()> {
let root = self.read_root()?;
let new_root = self.avl_insert_rec(root, ptr, size)?;
self.write_root(new_root)
}
fn avl_min(&self, subtree: u64) -> io::Result<(u64, u64)> {
let (size, _, _, left, _) = self.read_node(subtree)?;
if left == NULL_PTR {
Ok((subtree, size))
} else {
self.avl_min(left)
}
}
fn avl_remove_rec(&self, root: u64, ptr: u64, size: u64) -> io::Result<u64> {
if root == NULL_PTR {
return Ok(NULL_PTR);
}
let (root_sz, _, _, left, right) = self.read_node(root)?;
if (size, ptr) < (root_sz, root) {
let new_left = self.avl_remove_rec(left, ptr, size)?;
self.avl_write_and_update(root, root_sz, new_left, right)?;
return self.avl_rebalance(root);
}
if (size, ptr) > (root_sz, root) {
let new_right = self.avl_remove_rec(right, ptr, size)?;
self.avl_write_and_update(root, root_sz, left, new_right)?;
return self.avl_rebalance(root);
}
if left == NULL_PTR {
return Ok(right);
}
if right == NULL_PTR {
return Ok(left);
}
let (succ, succ_sz) = self.avl_min(right)?;
let new_right = self.avl_remove_rec(right, succ, succ_sz)?;
self.avl_write_and_update(succ, succ_sz, left, new_right)?;
self.avl_rebalance(succ)
}
fn avl_find_best_fit_and_remove_rec(
&self,
root: u64,
min_size: u64,
) -> io::Result<(u64, Option<(u64, u64)>)> {
if root == NULL_PTR {
return Ok((NULL_PTR, None));
}
let (root_sz, _, _, left, right) = self.read_node(root)?;
if root_sz >= min_size {
let (new_left, found) = self.avl_find_best_fit_and_remove_rec(left, min_size)?;
if let Some(candidate) = found {
self.avl_write_and_update(root, root_sz, new_left, right)?;
let new_root = self.avl_rebalance(root)?;
return Ok((new_root, Some(candidate)));
}
let new_root = if new_left == NULL_PTR {
right
} else if right == NULL_PTR {
new_left
} else {
let (succ, succ_sz) = self.avl_min(right)?;
let new_right = self.avl_remove_rec(right, succ, succ_sz)?;
self.avl_write_and_update(succ, succ_sz, new_left, new_right)?;
self.avl_rebalance(succ)?
};
Ok((new_root, Some((root, root_sz))))
} else {
let (new_right, found) = self.avl_find_best_fit_and_remove_rec(right, min_size)?;
if found.is_some() {
self.avl_write_and_update(root, root_sz, left, new_right)?;
let new_root = self.avl_rebalance(root)?;
Ok((new_root, found))
} else {
Ok((root, None))
}
}
}
fn avl_find_best_fit_and_remove(&self, min_size: u64) -> io::Result<Option<(u64, u64)>> {
let root = self.read_root()?;
let (new_root, found) = self.avl_find_best_fit_and_remove_rec(root, min_size)?;
self.write_root(new_root)?;
Ok(found)
}
fn avl_walk_inorder(
&self,
root: u64,
f: &mut dyn FnMut(u64, u64) -> io::Result<()>,
) -> io::Result<()> {
if root == NULL_PTR {
return Ok(());
}
let (size, _, _, left, right) = self.read_node(root)?;
self.avl_walk_inorder(left, f)?;
f(root, size)?;
self.avl_walk_inorder(right, f)
}
fn coalesce_and_rebalance(&self) -> io::Result<()> {
let root = self.read_root()?;
let mut blocks: Vec<(u64, u64)> = Vec::new(); self.avl_walk_inorder(root, &mut |ptr, size| {
blocks.push((ptr, size));
Ok(())
})?;
if blocks.is_empty() {
return Ok(());
}
blocks.sort_by_key(|&(ptr, _)| ptr);
let mut coalesced: Vec<(u64, u64)> = Vec::new();
let mut seams: Vec<u64> = Vec::new();
for (ptr, size) in blocks {
if let Some(last) = coalesced.last_mut()
&& last.0 + last.1 == ptr
{
seams.push(ptr);
last.1 += size;
continue;
}
coalesced.push((ptr, size));
}
for seam in seams {
self.stack.zero(seam, MIN_ALLOC)?;
}
coalesced.sort_by_key(|&(ptr, size)| (size, ptr));
fn build(this: &GhostTreeBstackAllocator, blocks: &[(u64, u64)]) -> io::Result<u64> {
if blocks.is_empty() {
return Ok(NULL_PTR);
}
let mid = blocks.len() / 2;
let (ptr, size) = blocks[mid];
let left = build(this, &blocks[..mid])?;
let right = build(this, &blocks[mid + 1..])?;
this.avl_write_and_update(ptr, size, left, right)?;
Ok(ptr)
}
let new_root = build(self, &coalesced)?;
self.write_root(new_root)
}
}
impl BStackAllocator for GhostTreeBstackAllocator {
type Error = io::Error;
type Allocated<'a> = BStackSlice<'a, Self>;
fn stack(&self) -> &BStack {
&self.stack
}
fn into_stack(self) -> BStack {
self.stack
}
fn alloc(&self, len: u64) -> io::Result<BStackSlice<'_, Self>> {
if len == 0 {
return Ok(unsafe { BStackSlice::from_raw_parts(self, 0, 0) });
}
let aligned = Self::align_up_len(len);
if let Some((ptr, block_size)) = self.avl_find_best_fit_and_remove(aligned)? {
let remainder = block_size - aligned;
if remainder >= MIN_ALLOC {
self.avl_insert(ptr, remainder)?;
Ok(unsafe { BStackSlice::from_raw_parts(self, ptr + remainder, len) })
} else {
self.stack.zero(ptr, MIN_ALLOC)?;
Ok(unsafe { BStackSlice::from_raw_parts(self, ptr, len) })
}
} else {
let start = self.stack.extend(aligned)?;
Ok(unsafe { BStackSlice::from_raw_parts(self, start, len) })
}
}
fn realloc<'a>(
&'a self,
slice: BStackSlice<'a, Self>,
new_len: u64,
) -> io::Result<BStackSlice<'a, Self>> {
if slice.is_empty() {
return self.alloc(new_len);
}
if slice.start() < ARENA_START || slice.start() != Self::align_up_ptr(slice.start()) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"realloc: slice origin is not a valid allocator address",
));
}
if new_len == 0 {
self.dealloc(slice)?;
return Ok(unsafe { BStackSlice::from_raw_parts(self, 0, 0) });
}
let old_len = slice.len();
let aligned_old = Self::align_up_len(old_len);
let aligned_new = Self::align_up_len(new_len);
if aligned_new == aligned_old {
if new_len < old_len {
let tail_ptr = slice.start() + new_len;
let tail_len = old_len - new_len;
self.stack.zero(tail_ptr, tail_len)?;
}
return Ok(unsafe { BStackSlice::from_raw_parts(self, slice.start(), new_len) });
}
let is_tail = slice.start() + aligned_old == self.stack.len()?;
if aligned_new < aligned_old {
let freed_tail = aligned_old - aligned_new;
let tail_ptr = slice.start() + aligned_new;
if is_tail {
if new_len < aligned_new {
self.stack
.zero(slice.start() + new_len, aligned_new - new_len)?;
}
self.stack.discard(freed_tail)?;
} else {
self.stack
.zero(slice.start() + new_len, aligned_old - new_len)?;
self.avl_insert(tail_ptr, freed_tail)?;
}
return Ok(unsafe { BStackSlice::from_raw_parts(self, slice.start(), new_len) });
}
if is_tail {
self.stack.extend(aligned_new - aligned_old)?;
return Ok(unsafe { BStackSlice::from_raw_parts(self, slice.start(), new_len) });
}
let new_slice = self.alloc(new_len)?;
let data = self.stack.get(slice.start(), slice.start() + old_len)?;
self.stack.set(new_slice.start(), &data)?;
self.dealloc(slice)?;
Ok(new_slice)
}
fn dealloc(&self, slice: BStackSlice<'_, Self>) -> io::Result<()> {
if slice.is_empty() {
return Ok(());
}
if slice.start() < ARENA_START || slice.start() != Self::align_up_ptr(slice.start()) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"dealloc: slice origin is not a valid allocator address",
));
}
let ptr = slice.start();
let true_len = Self::align_up_len(slice.len());
if ptr + true_len == self.stack.len()? {
return self.stack.discard(true_len);
}
self.stack.zero(ptr, true_len)?;
self.avl_insert(ptr, true_len)
}
}
impl BStackBulkAllocator for GhostTreeBstackAllocator {
fn alloc_bulk(
&self,
lengths: impl AsRef<[u64]>,
) -> Result<Vec<Self::Allocated<'_>>, Self::Error> {
let lengths = lengths.as_ref();
if lengths.is_empty() {
return Ok(Vec::new());
}
let aligned: Vec<u64> = lengths
.iter()
.map(|&l| if l == 0 { 0 } else { Self::align_up_len(l) })
.collect();
let total = aligned
.iter()
.copied()
.try_fold(0u64, |acc, a| acc.checked_add(a))
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"alloc_bulk: total size overflows u64",
)
})?;
if total == 0 {
return Ok(lengths
.iter()
.map(|_| unsafe { BStackSlice::from_raw_parts(self, 0, 0) })
.collect());
}
let block_ptr = if let Some((ptr, _)) = self.avl_find_best_fit_and_remove(total)? {
self.stack.zero(ptr, MIN_ALLOC)?;
ptr
} else {
self.stack.extend(total)?
};
let mut result = Vec::with_capacity(lengths.len());
let mut offset = 0u64;
for (&len, &al) in lengths.iter().zip(aligned.iter()) {
if len == 0 {
result.push(unsafe { BStackSlice::from_raw_parts(self, 0, 0) });
} else {
result.push(unsafe { BStackSlice::from_raw_parts(self, block_ptr + offset, len) });
offset += al;
}
}
Ok(result)
}
fn dealloc_bulk<'a>(
&'a self,
slices: impl AsRef<[Self::Allocated<'a>]>,
) -> Result<(), Self::Error> {
let slices = slices.as_ref();
let mut entries: Vec<(u64, u64)> = Vec::new();
for s in slices {
if s.is_empty() {
continue;
}
if s.start() < ARENA_START || s.start() != Self::align_up_ptr(s.start()) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"dealloc_bulk: invalid slice origin",
));
}
entries.push((s.start(), Self::align_up_len(s.len())));
}
if entries.is_empty() {
return Ok(());
}
entries.sort_by_key(|&(ptr, _)| ptr);
let mut merged: Vec<(u64, u64)> = Vec::new();
for (ptr, size) in entries {
if let Some(last) = merged.last_mut()
&& last.0 + last.1 == ptr
{
last.1 += size;
} else {
merged.push((ptr, size));
}
}
for (ptr, size) in merged {
if ptr + size == self.stack.len()? {
self.stack.discard(size)?;
} else {
self.stack.zero(ptr, size)?;
self.avl_insert(ptr, size)?;
}
}
Ok(())
}
}