use std::{marker::PhantomData, ptr::NonNull};
use crate::layouts::Backend;
#[repr(C)]
pub struct ScratchOwned<B: Backend> {
pub data: B::OwnedBuf,
pub _phantom: PhantomData<B>,
}
pub struct ScratchArena<'a, B: Backend> {
data: NonNull<B::OwnedBuf>,
start: usize,
end: usize,
_phantom: PhantomData<&'a mut B::OwnedBuf>,
}
impl<B: Backend> ScratchOwned<B> {
pub fn arena(&mut self) -> ScratchArena<'_, B> {
ScratchArena {
data: NonNull::from(&mut self.data),
start: 0,
end: B::len_bytes(&self.data),
_phantom: PhantomData,
}
}
}
impl<'a, B: Backend> ScratchArena<'a, B> {
pub fn borrow<'b>(&'b mut self) -> ScratchArena<'b, B> {
ScratchArena {
data: self.data,
start: self.start,
end: self.end,
_phantom: PhantomData,
}
}
pub fn scope<R>(&mut self, f: impl for<'b> FnOnce(ScratchArena<'b, B>) -> R) -> R {
f(self.borrow())
}
pub fn apply_mut(mut self, f: impl FnOnce(&mut ScratchArena<'a, B>)) -> Self {
f(&mut self);
self
}
pub fn consume<R>(&mut self, f: impl for<'b> FnOnce(ScratchArena<'b, B>) -> (R, ScratchArena<'b, B>)) -> R {
let arena = ScratchArena {
data: self.data,
start: self.start,
end: self.end,
_phantom: PhantomData,
};
let (res, rem) = f(arena);
self.start = rem.start;
self.end = rem.end;
res
}
pub fn available(&self) -> usize {
self.end.saturating_sub(align_up::<B>(self.start))
}
pub fn split_at(self, len: usize) -> (Self, Self) {
let start: usize = align_up::<B>(self.start);
let mid: usize = start.checked_add(len).expect("scratch arena split overflow");
assert!(
mid <= self.end,
"Attempted to take {len} from scratch arena with {} aligned bytes left",
self.available()
);
(
Self {
data: self.data,
start,
end: mid,
_phantom: PhantomData,
},
Self {
data: self.data,
start: mid,
end: self.end,
_phantom: PhantomData,
},
)
}
pub fn split(self, n: usize, len: usize) -> (Vec<Self>, Self) {
assert!(self.available() >= n * len);
let mut arenas: Vec<Self> = Vec::with_capacity(n);
let mut arena: Self = self;
for _ in 0..n {
let (taken, rem) = arena.split_at(len);
arena = rem;
arenas.push(taken);
}
(arenas, arena)
}
pub fn take_region(self, len: usize) -> (B::BufMut<'a>, Self) {
let start: usize = align_up::<B>(self.start);
let end: usize = start.checked_add(len).expect("scratch arena take overflow");
assert!(
end <= self.end,
"Attempted to take {len} from scratch arena with {} aligned bytes left",
self.available()
);
let data: &mut B::OwnedBuf = unsafe {
self.data.as_ptr().as_mut().expect("scratch arena owner pointer is null")
};
let region: B::BufMut<'a> = B::region_mut(data, start, len);
(
region,
Self {
data: self.data,
start: end,
end: self.end,
_phantom: PhantomData,
},
)
}
}
#[inline]
fn align_up<B: Backend>(offset: usize) -> usize {
offset.next_multiple_of(B::SCRATCH_ALIGN)
}