1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//! A stack.
//!
//! Read the documentation for [`Stack`] for more info.

pub mod stackoverflow;

use std::{fmt, ptr};

use stackoverflow::StackOverflow;

use crate::utils::array_debug::DebugArray;

/// A stack.
///
/// A stack is a kind of memory that can only be manipulated by
/// pushing (appending) an element, or by popping (removing) the last element,
/// like a stack of plates, which makes it a **first in, last out (LOFI) ** data type.
///
///
#[derive(Clone)]
pub struct Stack {
    /// The data storage of the stack.
    pub vec: Vec<u8>,
}

impl Default for Stack {
    fn default() -> Self {
        Self {
            vec: Vec::with_capacity(4095),
        }
    }
}

impl Stack {
    /// Returns the capacity of the stack (how big it is) in bytes.
    #[inline]
    #[must_use]
    pub fn total_space(&self) -> usize {
        self.vec.capacity()
    }
    /// Returns how much space of the stack has been used in bytes.
    #[inline]
    #[must_use]
    pub fn used_space(&self) -> usize {
        self.vec.len()
    }
    /// Returns how much space is left of the stack in bytes.
    #[inline]
    #[must_use]
    #[allow(clippy::arithmetic_side_effects)]
    pub fn space_left(&self) -> usize {
        self.total_space() - self.used_space()
    }

    /// Sets how much space is used of the stack.
    ///
    /// # Safety
    ///
    /// Any value of type [`u8`] can't possibly be invalid.
    ///
    /// The caller must guarantee that `new_len` doesn't exceed the capacity of the stack.
    #[inline]
    #[warn(warnings)]
    pub unsafe fn set_used_space(&mut self, new_len: usize) {
        // SAFETY: doc comment above
        unsafe {
            self.vec.set_len(new_len);
        }
    }

    /// Pushes a byte onto the [`Stack`].
    ///
    /// # Errors
    ///
    /// Returns [`StackOverflow`] if the stack has no more space (`capacity == length`).
    pub fn push_byte(&mut self, byte: u8) -> Result<(), StackOverflow> {
        if self.space_left() == 0 {
            return Err(StackOverflow);
        }
        self.vec.push(byte);
        Ok(())
    }
    /// Pops a byte from the [`Stack`].
    ///
    /// Returns [`None`] if there are no bytes on the [`Stack`].
    pub fn pop_byte(&mut self) -> Option<u8> {
        self.vec.pop()
    }

    /// Copies a slice onto the [`Stack`].
    ///
    /// This is done by allocating `bytes` bytes and writing the slice onto the buffer using [`ptr::copy`]
    ///
    /// # Errors
    ///
    /// Returns [`StackOverflow`] and doesn't allocate if the stack doesn't have enough space left.
    pub fn push_bytes(&mut self, bytes: &[u8]) -> Result<(), StackOverflow> {
        let len = self.used_space();
        let bytes_len = bytes.len();

        self.alloc(bytes_len)?;

        // SAFETY: line above checked that it doesn't overflow
        let dst = unsafe { self.vec.as_mut_ptr().add(len) };

        // SAFETY: comment above
        unsafe {
            ptr::copy(bytes.as_ptr(), dst, bytes_len);
        }

        Ok(())
    }

    /// Pushes `bytes` bytes onto the [`Stack`].
    ///
    /// # Errors
    ///
    /// Returns [`StackOverflow`] and doesn't allocate if the bytes don't fit on the stack (`bytes > capacity - length`).
    pub fn alloc(&mut self, bytes: usize) -> Result<(), StackOverflow> {
        if bytes > self.space_left() {
            return Err(StackOverflow);
        }
        for _ in 0..bytes {
            self.vec.push(0);
        }
        Ok(())
    }
    /// Pops `bytes` bytes from the [`Stack`].
    ///
    /// Pops the amount of bytes specified and returns a slice of them.
    ///
    /// # Errors
    ///
    /// Returns [`StackOverflow`] if there is not enough space.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that the slice isn't used when
    /// the [`Stack`] is pushed onto again to prevent race conditions.
    pub unsafe fn dealloc(&mut self, bytes: usize) -> Result<(), StackOverflow> {
        let len = self.used_space();
        if bytes > len {
            Err(StackOverflow)
        } else {
            #[allow(clippy::arithmetic_side_effects)]
            let new_len = self.used_space() - bytes;

            // SAFETY: overflows checked above
            unsafe {
                self.set_used_space(new_len);
            }

            Ok(())
        }
    }
    /// Pops a 16-bit big endian unsigned integer from the stack.
    pub fn pop_u16(&mut self) -> Option<u16> {
        let mut array = [0, 0];

        array[1] = self.pop_byte()?;
        array[0] = self.pop_byte()?;

        Some(u16::from_be_bytes(array))
    }
    /// Pops a 32-bit big endian unsigned integer from the stack.
    pub fn pop_u32(&mut self) -> Option<u32> {
        let mut array = [0, 0, 0, 0];

        array[3] = self.pop_byte()?;
        array[2] = self.pop_byte()?;
        array[1] = self.pop_byte()?;
        array[0] = self.pop_byte()?;

        Some(u32::from_be_bytes(array))
    }
    /// Pops a 64-bit big endian unsigned integer from the stack.
    pub fn pop_u64(&mut self) -> Option<u64> {
        let mut array = [0, 0, 0, 0, 0, 0, 0, 0];

        array[7] = self.pop_byte()?;
        array[6] = self.pop_byte()?;
        array[5] = self.pop_byte()?;
        array[4] = self.pop_byte()?;
        array[3] = self.pop_byte()?;
        array[2] = self.pop_byte()?;
        array[1] = self.pop_byte()?;
        array[0] = self.pop_byte()?;

        Some(u64::from_be_bytes(array))
    }
}

impl fmt::Debug for Stack {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        #[allow(clippy::indexing_slicing)]
        let last = self.used_space().checked_sub(16).map(|n| &self.vec[n..]);

        f.write_fmt(format_args!(
            "{}/{} {:?}",
            self.used_space(),
            self.total_space(),
            &DebugArray::debug(self.vec.get(0..16).unwrap_or(&[]), true, last,)
        ))
    }
}