use std::{cell::UnsafeCell, mem::replace};
use crate::runtime::Cell;
const LIMIT: usize = 10_000;
const CAPACITY: usize = 1_000;
thread_local! {
static STACK: UnsafeCell<Stack> = const { UnsafeCell::new(Stack::new()) };
}
pub(crate) type StackDepth = usize;
pub(super) struct Stack {
cells: Vec<Cell>,
max_depth: usize,
min_capacity: usize,
max_capacity: usize,
}
impl Stack {
#[inline(always)]
const fn new() -> Self {
Self {
cells: Vec::new(),
max_depth: LIMIT,
min_capacity: CAPACITY / 2,
max_capacity: CAPACITY,
}
}
#[inline(always)]
pub(super) fn enter_frame(frame: StackDepth, arity: StackDepth) -> Option<StackDepth> {
STACK.with(|stack| {
let stack = unsafe { &mut *stack.get() };
let diff = frame.checked_sub(arity).unwrap_or_default();
let depth = stack.cells.len();
if depth + diff > stack.max_depth {
return None;
}
stack.cells.reserve(diff);
Some(depth.checked_sub(arity).unwrap_or_default())
})
}
#[inline(always)]
pub(super) fn leave_frame(begin: StackDepth) {
STACK.with(|stack| {
let stack = unsafe { &mut *stack.get() };
stack.cells.truncate(begin);
if stack.cells.capacity() > stack.max_capacity {
stack.cells.shrink_to(stack.min_capacity);
}
})
}
#[inline(always)]
pub(super) fn push_nil() {
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
stack.cells.push(Cell::nil());
});
}
#[inline(always)]
pub(super) fn push(cell: Cell) {
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
stack.cells.push(cell);
});
}
#[inline(always)]
pub(super) fn pop_1(frame_begin: StackDepth) -> Cell {
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
if stack.cells.len() <= frame_begin {
return Cell::nil();
}
stack.cells.pop().unwrap_or(Cell::nil())
})
}
#[inline(always)]
pub(super) fn pop_2(frame_begin: StackDepth) -> (Cell, Cell) {
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
let cell_2 = match stack.cells.len() <= frame_begin {
true => Cell::nil(),
false => stack.cells.pop().unwrap_or(Cell::nil()),
};
let cell_1 = match stack.cells.len() <= frame_begin {
true => Cell::nil(),
false => stack.cells.pop().unwrap_or(Cell::nil()),
};
(cell_1, cell_2)
})
}
#[inline(always)]
pub(super) fn pop_many(frame_begin: StackDepth, length: StackDepth) -> Vec<Cell> {
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
let mut at = stack.cells.len().checked_sub(length).unwrap_or_default();
if at < frame_begin {
at = frame_begin;
}
if at > stack.cells.len() {
return Vec::new();
}
stack.cells.split_off(at)
})
}
#[inline(always)]
pub(super) fn peek_1(frame_begin: StackDepth) -> Cell {
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
if stack.cells.len() <= frame_begin {
return Cell::nil();
}
let Some(last) = stack.cells.last() else {
return Cell::nil();
};
last.clone()
})
}
#[inline(always)]
pub(super) fn lift(frame_begin: StackDepth, mut depth: StackDepth) {
depth += frame_begin;
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
let cell = match stack.cells.get_mut(depth) {
Some(cell) => replace(cell, Cell::nil()),
None => Cell::nil(),
};
stack.cells.push(cell);
})
}
#[inline(always)]
pub(super) fn swap(frame_begin: StackDepth, mut depth: StackDepth) {
depth += frame_begin;
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
if depth >= stack.cells.len() {
return;
}
let last = stack.cells.len() - 1;
stack.cells.swap(depth, last);
})
}
#[inline(always)]
pub(super) fn dup(frame_begin: StackDepth, mut depth: StackDepth) {
depth += frame_begin;
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
let cell = match stack.cells.get(depth) {
Some(cell) => cell.clone(),
None => Cell::nil(),
};
stack.cells.push(cell);
})
}
#[inline(always)]
pub(super) fn shrink(frame_begin: StackDepth, mut depth: StackDepth) {
depth += frame_begin;
STACK.with(move |stack| {
let stack = unsafe { &mut *stack.get() };
stack.cells.truncate(depth)
})
}
}