esoteric_vm/machine/stack/
mod.rs

1//! A stack.
2//!
3//! Read the documentation for [`Stack`] for more info.
4
5pub mod stackoverflow;
6
7use std::{fmt, ptr};
8
9use stackoverflow::StackOverflow;
10
11use crate::utils::array_debug::DebugArray;
12
13/// A stack.
14///
15/// A stack is a kind of memory that can only be manipulated by
16/// pushing (appending) an element, or by popping (removing) the last element,
17/// like a stack of plates, which makes it a **first in, last out (LOFI) ** data type.
18///
19///
20#[derive(Clone)]
21pub struct Stack {
22    /// The data storage of the stack.
23    pub vec: Vec<u8>,
24}
25
26impl Default for Stack {
27    fn default() -> Self {
28        Self {
29            vec: Vec::with_capacity(4095),
30        }
31    }
32}
33
34impl Stack {
35    /// Returns the capacity of the stack (how big it is) in bytes.
36    #[inline]
37    #[must_use]
38    pub fn total_space(&self) -> usize {
39        self.vec.capacity()
40    }
41    /// Returns how much space of the stack has been used in bytes.
42    #[inline]
43    #[must_use]
44    pub fn used_space(&self) -> usize {
45        self.vec.len()
46    }
47    /// Returns how much space is left of the stack in bytes.
48    #[inline]
49    #[must_use]
50    #[allow(clippy::arithmetic_side_effects)]
51    pub fn space_left(&self) -> usize {
52        self.total_space() - self.used_space()
53    }
54
55    /// Sets how much space is used of the stack.
56    ///
57    /// # Safety
58    ///
59    /// Any value of type [`u8`] can't possibly be invalid.
60    ///
61    /// The caller must guarantee that `new_len` doesn't exceed the capacity of the stack.
62    #[inline]
63    #[warn(warnings)]
64    pub unsafe fn set_used_space(&mut self, new_len: usize) {
65        // SAFETY: doc comment above
66        unsafe {
67            self.vec.set_len(new_len);
68        }
69    }
70
71    /// Pushes a byte onto the [`Stack`].
72    ///
73    /// # Errors
74    ///
75    /// Returns [`StackOverflow`] if the stack has no more space (`capacity == length`).
76    pub fn push_byte(&mut self, byte: u8) -> Result<(), StackOverflow> {
77        if self.space_left() == 0 {
78            return Err(StackOverflow);
79        }
80        self.vec.push(byte);
81        Ok(())
82    }
83    /// Pops a byte from the [`Stack`].
84    ///
85    /// Returns [`None`] if there are no bytes on the [`Stack`].
86    pub fn pop_byte(&mut self) -> Option<u8> {
87        self.vec.pop()
88    }
89
90    /// Copies a slice onto the [`Stack`].
91    ///
92    /// This is done by allocating `bytes` bytes and writing the slice onto the buffer using [`ptr::copy`]
93    ///
94    /// # Errors
95    ///
96    /// Returns [`StackOverflow`] and doesn't allocate if the stack doesn't have enough space left.
97    pub fn push_bytes(&mut self, bytes: &[u8]) -> Result<(), StackOverflow> {
98        let len = self.used_space();
99        let bytes_len = bytes.len();
100
101        self.alloc(bytes_len)?;
102
103        // SAFETY: line above checked that it doesn't overflow
104        let dst = unsafe { self.vec.as_mut_ptr().add(len) };
105
106        // SAFETY: comment above
107        unsafe {
108            ptr::copy(bytes.as_ptr(), dst, bytes_len);
109        }
110
111        Ok(())
112    }
113
114    /// Pushes `bytes` bytes onto the [`Stack`].
115    ///
116    /// # Errors
117    ///
118    /// Returns [`StackOverflow`] and doesn't allocate if the bytes don't fit on the stack (`bytes > capacity - length`).
119    pub fn alloc(&mut self, bytes: usize) -> Result<(), StackOverflow> {
120        if bytes > self.space_left() {
121            return Err(StackOverflow);
122        }
123        for _ in 0..bytes {
124            self.vec.push(0);
125        }
126        Ok(())
127    }
128    /// Pops `bytes` bytes from the [`Stack`].
129    ///
130    /// Pops the amount of bytes specified and returns a slice of them.
131    ///
132    /// # Errors
133    ///
134    /// Returns [`StackOverflow`] if there is not enough space.
135    ///
136    /// # Safety
137    ///
138    /// The caller must guarantee that the slice isn't used when
139    /// the [`Stack`] is pushed onto again to prevent race conditions.
140    pub unsafe fn dealloc(&mut self, bytes: usize) -> Result<(), StackOverflow> {
141        let len = self.used_space();
142        if bytes > len {
143            Err(StackOverflow)
144        } else {
145            #[allow(clippy::arithmetic_side_effects)]
146            let new_len = self.used_space() - bytes;
147
148            // SAFETY: overflows checked above
149            unsafe {
150                self.set_used_space(new_len);
151            }
152
153            Ok(())
154        }
155    }
156    /// Pops a 16-bit big endian unsigned integer from the stack.
157    pub fn pop_u16(&mut self) -> Option<u16> {
158        let mut array = [0, 0];
159
160        array[1] = self.pop_byte()?;
161        array[0] = self.pop_byte()?;
162
163        Some(u16::from_be_bytes(array))
164    }
165    /// Pops a 32-bit big endian unsigned integer from the stack.
166    pub fn pop_u32(&mut self) -> Option<u32> {
167        let mut array = [0, 0, 0, 0];
168
169        array[3] = self.pop_byte()?;
170        array[2] = self.pop_byte()?;
171        array[1] = self.pop_byte()?;
172        array[0] = self.pop_byte()?;
173
174        Some(u32::from_be_bytes(array))
175    }
176    /// Pops a 64-bit big endian unsigned integer from the stack.
177    pub fn pop_u64(&mut self) -> Option<u64> {
178        let mut array = [0, 0, 0, 0, 0, 0, 0, 0];
179
180        array[7] = self.pop_byte()?;
181        array[6] = self.pop_byte()?;
182        array[5] = self.pop_byte()?;
183        array[4] = self.pop_byte()?;
184        array[3] = self.pop_byte()?;
185        array[2] = self.pop_byte()?;
186        array[1] = self.pop_byte()?;
187        array[0] = self.pop_byte()?;
188
189        Some(u64::from_be_bytes(array))
190    }
191}
192
193impl fmt::Debug for Stack {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        #[allow(clippy::indexing_slicing)]
196        let last = self.used_space().checked_sub(16).map(|n| &self.vec[n..]);
197
198        f.write_fmt(format_args!(
199            "{}/{} {:?}",
200            self.used_space(),
201            self.total_space(),
202            &DebugArray::debug(self.vec.get(0..16).unwrap_or(&[]), true, last,)
203        ))
204    }
205}