use alloc::vec::Vec;
use crate::error::{ExitError, ExitException};
use crate::uint::U256;
#[derive(Clone, Debug)]
pub struct Stack {
data: Vec<U256>,
limit: usize,
}
macro_rules! impl_perform_popn_pushn {
(
$name:ident,
$pop_len:expr,
$push_len:expr,
($($peek_pop:expr),*),
($($peek_push:expr),*),
$pop_pushn_f:ident
) => {
#[allow(unused_parens)]
pub fn $name<R, F>(&mut self, f: F) -> Result<R, ExitError> where
F: FnOnce(
$(impl_perform_popn_pushn!(INTERNAL_TYPE_RU256, $peek_pop)),*
) -> Result<(($(impl_perform_popn_pushn!(INTERNAL_TYPE_U256, $peek_push)),*), R), ExitError>
{
match self.check_pop_push($pop_len, $push_len) {
Ok(()) => (),
Err(e) => return Err(e.into()),
}
let (p, ret) = match f($(self.unchecked_peek($peek_pop)),*) {
Ok(p1) => p1,
Err(e) => return Err(e.into()),
};
self.$pop_pushn_f($pop_len, p);
Ok(ret)
}
};
(INTERNAL_TYPE_RU256, $e:expr) => { &U256 };
(INTERNAL_TYPE_U256, $e:expr) => { U256 };
}
impl Stack {
#[must_use]
pub const fn new(limit: usize) -> Self {
Self {
data: Vec::new(),
limit,
}
}
#[inline]
#[must_use]
pub const fn limit(&self) -> usize {
self.limit
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline]
#[must_use]
pub const fn data(&self) -> &Vec<U256> {
&self.data
}
pub fn clear(&mut self) {
self.data.clear();
}
#[inline]
pub fn pop(&mut self) -> Result<U256, ExitException> {
self.data.pop().ok_or(ExitException::StackUnderflow)
}
#[inline]
pub fn push(&mut self, value: U256) -> Result<(), ExitException> {
if self.data.len() + 1 > self.limit {
return Err(ExitException::StackOverflow);
}
self.data.push(value);
Ok(())
}
pub fn check_pop_push(&self, pop: usize, push: usize) -> Result<(), ExitException> {
if self.data.len() < pop {
return Err(ExitException::StackUnderflow);
}
if self.data.len() - pop + push > self.limit {
return Err(ExitException::StackOverflow);
}
Ok(())
}
fn unchecked_peek(&self, no_from_top: usize) -> &U256 {
&self.data[self.data.len() - no_from_top - 1]
}
fn unchecked_pop_push1(&mut self, pop: usize, p1: U256) {
for _ in 0..pop {
self.data.pop();
}
self.data.push(p1);
}
fn unchecked_pop_push0(&mut self, pop: usize, _p1: ()) {
for _ in 0..pop {
self.data.pop();
}
}
#[inline]
pub fn peek(&self, no_from_top: usize) -> Result<U256, ExitException> {
if self.data.len() > no_from_top {
Ok(self.data[self.data.len() - no_from_top - 1])
} else {
Err(ExitException::StackUnderflow)
}
}
#[inline]
pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), ExitException> {
if self.data.len() > no_from_top {
let len = self.data.len();
self.data[len - no_from_top - 1] = val;
Ok(())
} else {
Err(ExitException::StackUnderflow)
}
}
impl_perform_popn_pushn!(perform_pop0_push1, 0, 1, (), (0), unchecked_pop_push1);
impl_perform_popn_pushn!(perform_pop1_push0, 1, 0, (0), (), unchecked_pop_push0);
impl_perform_popn_pushn!(perform_pop1_push1, 1, 1, (0), (0), unchecked_pop_push1);
impl_perform_popn_pushn!(perform_pop2_push1, 2, 1, (0, 1), (0), unchecked_pop_push1);
impl_perform_popn_pushn!(perform_pop3_push0, 3, 0, (0, 1, 2), (), unchecked_pop_push0);
impl_perform_popn_pushn!(
perform_pop4_push0,
4,
0,
(0, 1, 2, 3),
(),
unchecked_pop_push0
);
impl_perform_popn_pushn!(
perform_pop6_push0,
6,
0,
(0, 1, 2, 3, 4, 5),
(),
unchecked_pop_push0
);
impl_perform_popn_pushn!(
perform_pop7_push0,
7,
0,
(0, 1, 2, 3, 4, 5, 6),
(),
unchecked_pop_push0
);
}