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}