bitcoin_rs_script/
stack.rs1use smallvec::SmallVec;
2use thiserror::Error;
3use tinyvec::ArrayVec;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7pub enum ScriptItem {
8 Num(i64),
10 Bytes(SmallVec<[u8; 32]>),
12}
13
14impl Default for ScriptItem {
15 fn default() -> Self {
16 Self::Bytes(SmallVec::new())
17 }
18}
19
20#[derive(Clone, Debug, Default, PartialEq, Eq)]
22pub struct Stack {
23 items: ArrayVec<[ScriptItem; Self::MAX_DEPTH]>,
24}
25
26impl Stack {
27 pub const MAX_DEPTH: usize = 1000;
29
30 #[must_use]
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn push(&mut self, item: ScriptItem) -> Result<(), StackError> {
38 match self.items.try_push(item) {
39 Some(_) => Err(StackError::Overflow),
40 None => Ok(()),
41 }
42 }
43
44 pub fn pop(&mut self) -> Result<ScriptItem, StackError> {
46 self.items.pop().ok_or(StackError::Underflow)
47 }
48
49 pub fn peek(&self) -> Result<&ScriptItem, StackError> {
51 self.items.last().ok_or(StackError::Underflow)
52 }
53
54 #[must_use]
56 pub fn len(&self) -> usize {
57 self.items.len()
58 }
59
60 #[must_use]
62 pub fn is_empty(&self) -> bool {
63 self.items.is_empty()
64 }
65
66 pub fn clear(&mut self) {
68 self.items.clear();
69 }
70}
71
72#[derive(Copy, Clone, Debug, Error, PartialEq, Eq)]
74pub enum StackError {
75 #[error("script stack overflow")]
77 Overflow,
78 #[error("script stack underflow")]
80 Underflow,
81}
82
83#[cfg(test)]
84mod tests {
85 use super::{ScriptItem, Stack, StackError};
86
87 #[test]
88 fn stack_rejects_overflow_and_reports_underflow() {
89 let mut stack = Stack::new();
90 assert_eq!(stack.pop(), Err(StackError::Underflow));
91 for value in 0..Stack::MAX_DEPTH {
92 let num = i64::try_from(value)
93 .unwrap_or_else(|error| panic!("stack test index should fit in i64: {error}"));
94 assert_eq!(stack.push(ScriptItem::Num(num)), Ok(()));
95 }
96 assert_eq!(stack.len(), Stack::MAX_DEPTH);
97 assert_eq!(stack.push(ScriptItem::Num(1)), Err(StackError::Overflow));
98 }
99}