Skip to main content

stack_assembly/
memory.rs

1use std::fmt;
2
3use crate::{Effect, Value};
4
5/// # A linear memory, freely addressable per word
6///
7/// The memory can be accessed from a script through the `read` and `write`
8/// operators.
9///
10/// Aside from this, the stack is an important communication channel between
11/// script and host. Please refer to [`Eval`]'s [`memory`] field for more
12/// information on that.
13///
14/// ## Constructing a `Memory` instance
15///
16/// By default, `Memory` has a size of 1024 words and is initially empty. This
17/// is controlled by its [`Default` implementation].
18///
19/// If you want to override this size, you can do so by overwriting the
20/// [`values`] field with a [`Vec`] of the desired length.
21///
22/// [`Eval`]: crate::Eval
23/// [`memory`]: struct.Eval.html#structfield.memory
24/// [`Default` implementation]: #impl-Default-for-Memory
25/// [`values`]: #structfield.values
26pub struct Memory {
27    /// # The values in the memory
28    pub values: Vec<Value>,
29}
30
31impl Memory {
32    /// # Read the value at the provided address
33    pub fn read(&self, address: u32) -> Result<Value, InvalidAddress> {
34        let Ok(address): Result<usize, _> = address.try_into() else {
35            // It is not possible to have memories larger than what can be
36            // addressed by `usize`. So by definition, any address that's too
37            // large to convert to `usize`, can not be valid.
38            return Err(InvalidAddress);
39        };
40
41        let Some(value) = self.values.get(address).copied() else {
42            return Err(InvalidAddress);
43        };
44
45        Ok(value)
46    }
47
48    /// # Write a value to an address
49    pub fn write(
50        &mut self,
51        address: u32,
52        value: Value,
53    ) -> Result<(), InvalidAddress> {
54        let Ok(address): Result<usize, _> = address.try_into() else {
55            // It is not possible to have memories larger than what can be
56            // addressed by `usize`. So by definition, any address that's too
57            // large to convert to `usize`, can not be valid.
58            return Err(InvalidAddress);
59        };
60
61        if address >= self.values.len() {
62            return Err(InvalidAddress);
63        }
64
65        self.values[address] = value;
66
67        Ok(())
68    }
69
70    /// # Access the memory as a slice of `i32` values
71    pub fn to_i32_slice(&self) -> &[i32] {
72        bytemuck::cast_slice(&self.values)
73    }
74
75    /// # Access the memory as a slice of `u32` values
76    pub fn to_u32_slice(&self) -> &[u32] {
77        bytemuck::cast_slice(&self.values)
78    }
79}
80
81impl Default for Memory {
82    fn default() -> Self {
83        Self {
84            values: vec![Value::from(0); 1024],
85        }
86    }
87}
88
89impl fmt::Debug for Memory {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        // This is not perfect, but it's way more compact than the derived
92        // implementation.
93
94        let mut values = self.values.iter().peekable();
95
96        write!(f, "[")?;
97
98        while let Some(value) = values.next() {
99            write!(f, "{value:?}")?;
100
101            if values.peek().is_some() {
102                write!(f, ", ")?;
103            }
104        }
105
106        write!(f, "]")?;
107
108        Ok(())
109    }
110}
111
112#[derive(Debug)]
113pub struct InvalidAddress;
114
115impl From<InvalidAddress> for Effect {
116    fn from(InvalidAddress: InvalidAddress) -> Self {
117        Effect::InvalidAddress
118    }
119}