use super::{BStackAllocator, BStackBulkAllocator, BStackSlice};
use crate::BStack;
use std::{fmt, io};
pub struct LinearBStackAllocator {
stack: BStack,
}
impl LinearBStackAllocator {
pub fn new(stack: BStack) -> Self {
Self { stack }
}
}
impl fmt::Debug for LinearBStackAllocator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LinearBStackAllocator")
.finish_non_exhaustive()
}
}
impl From<BStack> for LinearBStackAllocator {
fn from(stack: BStack) -> Self {
Self::new(stack)
}
}
impl From<LinearBStackAllocator> for BStack {
fn from(alloc: LinearBStackAllocator) -> Self {
alloc.into_stack()
}
}
impl BStackAllocator for LinearBStackAllocator {
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>> {
let offset = self.stack.extend(len)?;
Ok(unsafe { BStackSlice::from_raw_parts(self, offset, len) })
}
fn realloc<'a>(
&'a self,
slice: BStackSlice<'a, Self>,
new_len: u64,
) -> io::Result<BStackSlice<'a, Self>> {
let current_tail = self.stack.len()?;
if slice.end() != current_tail {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"LinearBStackAllocator::realloc: non-tail slice cannot be resized in place",
));
}
match new_len.cmp(&slice.len()) {
std::cmp::Ordering::Equal => Ok(slice),
std::cmp::Ordering::Greater => {
self.stack.extend(new_len - slice.len())?;
Ok(unsafe { BStackSlice::from_raw_parts(self, slice.start(), new_len) })
}
std::cmp::Ordering::Less => {
self.stack.discard(slice.len() - new_len)?;
Ok(unsafe { BStackSlice::from_raw_parts(self, slice.start(), new_len) })
}
}
}
fn dealloc(&self, slice: BStackSlice<'_, Self>) -> io::Result<()> {
let current_tail = self.stack.len()?;
if slice.end() == current_tail {
self.stack.discard(slice.len())?;
}
Ok(())
}
}
impl BStackBulkAllocator for LinearBStackAllocator {
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 total = lengths
.iter()
.try_fold(0u64, |acc, &len| acc.checked_add(len))
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"alloc_bulk: total length overflows u64",
)
})?;
let base = self.stack.extend(total)?;
let mut result = Vec::with_capacity(lengths.len());
let mut offset = base;
for &len in lengths {
result.push(unsafe { BStackSlice::from_raw_parts(self, offset, len) });
offset += len;
}
Ok(result)
}
fn dealloc_bulk<'a>(
&'a self,
slices: impl AsRef<[Self::Allocated<'a>]>,
) -> Result<(), Self::Error> {
let slices = slices.as_ref();
if slices.is_empty() {
return Ok(());
}
let current_tail = self.stack.len()?;
let mut sorted: Vec<BStackSlice<'_, Self>> = slices.to_vec();
sorted.sort_by_key(|s| std::cmp::Reverse(s.end()));
let mut discard_start = current_tail;
for slice in &sorted {
if slice.end() == discard_start {
discard_start = slice.start();
}
}
let to_discard = current_tail - discard_start;
if to_discard > 0 {
self.stack.discard(to_discard)?;
}
Ok(())
}
}