mod allocation;
use allocation::Allocation;
use std::{
self,
cell::UnsafeCell,
mem::{size_of, MaybeUninit},
slice,
};
thread_local!(
static THREAD_LOCAL: Stack = Stack::new()
);
pub struct Stack(UnsafeCell<Allocation>);
impl Drop for Stack {
fn drop(&mut self) {
let stack = self.0.get_mut();
stack.force_dealloc();
}
}
const fn uninit_array<const N: usize, T>() -> [MaybeUninit<T>; N] {
unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() }
}
impl Stack {
pub fn new() -> Self {
Self(UnsafeCell::new(Allocation::null()))
}
pub fn uninit<T, R, F>(&self, f: F) -> R
where
F: FnOnce(&mut MaybeUninit<T>) -> R,
{
self.uninit_slice(1, |slice| f(&mut slice[0]))
}
pub fn uninit_slice<T, F, R>(&self, len: usize, f: F) -> R
where
F: FnOnce(&mut [MaybeUninit<T>]) -> R,
{
if std::mem::size_of::<T>() == 0 {
let mut tmp = Vec::<MaybeUninit<T>>::with_capacity(len);
let tmp = tmp.as_mut_ptr();
let mut slice = unsafe { slice::from_raw_parts_mut(tmp, len) };
return f(&mut slice);
}
if len == 0 {
return f(&mut []);
}
let (_restore, slice) = unsafe {
let stack = &mut *self.0.get();
stack.get_slice(&self.0, len)
};
f(slice)
}
pub fn buffer<T, F, R, I>(&self, i: I, f: F) -> R
where
I: Iterator<Item = T>,
F: FnOnce(&mut [T]) -> R,
{
if size_of::<T>() == 0 {
let mut v: Vec<_> = i.collect();
return f(&mut v);
}
struct Writer<'a, T> {
restore: Option<DropStack<'a>>,
reserved: &'a mut [MaybeUninit<T>],
written: usize,
}
impl<T> Writer<'_, T> {
fn write(&mut self, item: T) {
self.reserved[self.written] = MaybeUninit::new(item);
self.written += 1;
}
fn inits(&mut self) -> &mut [MaybeUninit<T>] {
&mut self.reserved[..self.written]
}
fn try_reuse(&mut self, stack: &mut Allocation) -> bool {
unsafe {
if let Some(prev) = &self.restore {
if prev.restore.ref_eq(stack) {
let required_bytes = size_of::<T>() * self.reserved.len();
if stack.remaining_bytes() >= required_bytes {
stack.len += required_bytes;
self.reserved = slice::from_raw_parts_mut(
self.reserved.as_mut_ptr() as *mut MaybeUninit<T>,
self.written * 2,
);
return true;
}
}
}
}
false
}
}
impl<T> Drop for Writer<'_, T> {
fn drop(&mut self) {
unsafe {
for init in self.inits() {
init.assume_init_drop();
}
}
}
}
unsafe {
let mut on_stack: [MaybeUninit<T>; 0] = uninit_array();
let mut writer = Writer {
restore: None,
reserved: &mut on_stack,
written: 0,
};
for next in i {
if writer.written == writer.reserved.len() {
let stack = &mut *self.0.get();
if !writer.try_reuse(stack) {
let (restore, slice) =
stack.get_slice(&self.0, (writer.written * 2).max(1));
for i in 0..writer.written {
slice[i].write(writer.reserved[i].assume_init_read());
}
writer.restore = Some(restore);
writer.reserved = slice;
}
}
writer.write(next);
}
let inits = writer.inits();
let buffer = &mut *(inits as *mut [MaybeUninit<T>] as *mut [T]);
f(buffer)
}
}
}
pub fn uninit_slice<T, F, R>(len: usize, f: F) -> R
where
F: FnOnce(&mut [MaybeUninit<T>]) -> R,
{
THREAD_LOCAL.with(|stack| stack.uninit_slice(len, f))
}
pub fn uninit<T, F, R>(f: F) -> R
where
F: FnOnce(&mut MaybeUninit<T>) -> R,
{
THREAD_LOCAL.with(|stack| stack.uninit(f))
}
pub fn buffer<T, F, R, I>(i: I, f: F) -> R
where
I: Iterator<Item = T>,
F: FnOnce(&mut [T]) -> R,
{
THREAD_LOCAL.with(|stack| stack.buffer(i, f))
}
pub(crate) struct DropStack<'a> {
pub restore: Allocation,
pub location: &'a UnsafeCell<Allocation>,
}
impl Drop for DropStack<'_> {
fn drop(&mut self) {
unsafe {
let mut current = &mut *self.location.get();
if current.ref_eq(&self.restore) {
current.len = self.restore.len;
} else {
self.restore.try_dealloc();
}
}
}
}