85 lines
2.4 KiB
Rust
85 lines
2.4 KiB
Rust
use std::mem::{MaybeUninit, replace, transmute};
|
|
use std::ops::Deref;
|
|
|
|
use crate::error::*;
|
|
|
|
macro_rules! into {
|
|
($e:expr) => {
|
|
// SAFETY: This macro is used to transmute `MaybeUninit<Value<'vm>>` to `Value<'vm>`
|
|
// or `&MaybeUninit<Value<'vm>>` to `&Value<'vm>`.
|
|
// This is safe because the `Stack` ensures that only initialized values are accessed
|
|
// within the `0..top` range.
|
|
unsafe { transmute($e) }
|
|
};
|
|
}
|
|
|
|
pub struct Stack<T, const CAP: usize> {
|
|
items: Box<[MaybeUninit<T>; CAP]>,
|
|
top: usize,
|
|
}
|
|
|
|
|
|
impl<T, const CAP: usize> Default for Stack<T, CAP> {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl<T, const CAP: usize> Stack<T, CAP> {
|
|
pub fn new() -> Self {
|
|
Stack {
|
|
items: unsafe {
|
|
std::mem::transmute::<Box<MaybeUninit<[T; CAP]>>, Box<[MaybeUninit<T>; CAP]>>(
|
|
Box::new_uninit(),
|
|
)
|
|
},
|
|
top: 0,
|
|
}
|
|
}
|
|
|
|
pub fn push(&mut self, item: T) -> Result<()> {
|
|
self.items
|
|
.get_mut(self.top)
|
|
.map_or_else(|| Err(Error::EvalError("stack overflow".to_string())), Ok)?
|
|
.write(item);
|
|
self.top += 1;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn pop(&mut self) -> T {
|
|
let item = self.items.get_mut(self.top - 1).unwrap();
|
|
self.top -= 1;
|
|
|
|
// SAFETY: `item` at `self.top` was previously written and is initialized.
|
|
// We replace it with `MaybeUninit::uninit()` and then `assume_init`
|
|
// on the original value, which is safe as it was initialized.
|
|
unsafe { replace(item, MaybeUninit::uninit()).assume_init() }
|
|
}
|
|
|
|
pub fn tos(&self) -> &T {
|
|
into!(&self.items[self.top - 1])
|
|
}
|
|
|
|
pub fn tos_mut(&mut self) -> &mut T {
|
|
into!(&mut self.items[self.top - 1])
|
|
}
|
|
}
|
|
|
|
impl<T, const CAP: usize> Deref for Stack<T, CAP> {
|
|
type Target = [T];
|
|
fn deref(&self) -> &Self::Target {
|
|
into!(&self.items[0..self.top])
|
|
}
|
|
}
|
|
|
|
impl<T, const CAP: usize> Drop for Stack<T, CAP> {
|
|
fn drop(&mut self) {
|
|
self.items.as_mut_slice()[0..self.top]
|
|
.iter_mut()
|
|
// SAFETY: Items in the range `0..self.top` are guaranteed to be initialized.
|
|
// `assume_init_drop` is called to correctly drop these initialized `Value`s.
|
|
.map(|item| unsafe { item.assume_init_drop() })
|
|
.for_each(drop)
|
|
}
|
|
}
|