essential_state_read_vm/
state_memory.rs1use core::ops::Range;
4
5use essential_constraint_vm::{error::StackError, Stack};
6
7use crate::{asm::Word, OpSyncResult, StateMemoryError, StateMemoryResult};
8
9#[cfg(test)]
10mod tests;
11
12#[derive(Clone, Debug, Default, PartialEq)]
17pub struct StateMemory(Vec<Vec<Word>>);
18
19impl StateMemory {
20 pub const SLOT_LIMIT: usize = 4096;
22
23 pub const VALUE_LIMIT: usize = 4096;
25
26 pub fn alloc_slots(&mut self, size: usize) -> StateMemoryResult<()> {
28 if self.len() + size > Self::SLOT_LIMIT {
29 return Err(StateMemoryError::Overflow);
30 }
31 self.0.resize_with(self.len() + size, Default::default);
32 Ok(())
33 }
34
35 pub fn load(&self, slot_ix: usize, range: Range<usize>) -> StateMemoryResult<&[Word]> {
37 let slot = self
38 .get(slot_ix)
39 .ok_or(StateMemoryError::IndexOutOfBounds)?
40 .get(range)
41 .ok_or(StateMemoryError::IndexOutOfBounds)?;
42 Ok(slot)
43 }
44
45 pub fn store(
47 &mut self,
48 slot_ix: usize,
49 value_ix: usize,
50 data: Vec<Word>,
51 ) -> StateMemoryResult<()> {
52 let slot = self
53 .0
54 .get_mut(slot_ix)
55 .ok_or(StateMemoryError::IndexOutOfBounds)?;
56
57 if value_ix.saturating_add(data.len()) > Self::VALUE_LIMIT {
58 return Err(StateMemoryError::Overflow);
59 }
60
61 let (_, rem) = slot
62 .split_at_mut_checked(value_ix)
63 .ok_or(StateMemoryError::IndexOutOfBounds)?;
64 let len = rem.len().min(data.len());
65 rem[..len].copy_from_slice(&data[..len]);
66 if len < data.len() {
67 slot.extend_from_slice(&data[len..]);
68 }
69 Ok(())
70 }
71
72 pub fn truncate(&mut self, slot_ix: usize, len: usize) -> StateMemoryResult<()> {
74 self.0
75 .get_mut(slot_ix)
76 .ok_or(StateMemoryError::IndexOutOfBounds)?
77 .truncate(len);
78 Ok(())
79 }
80
81 pub fn value_len(&self, slot_ix: usize) -> StateMemoryResult<usize> {
83 let slot = self
84 .0
85 .get(slot_ix)
86 .ok_or(StateMemoryError::IndexOutOfBounds)?;
87 Ok(slot.len())
88 }
89
90 pub fn store_slots_range(
92 &mut self,
93 index: usize,
94 values: Vec<Vec<Word>>,
95 ) -> StateMemoryResult<()> {
96 if values.iter().any(|val| val.len() > Self::VALUE_LIMIT) {
97 return Err(StateMemoryError::Overflow);
98 }
99
100 let slots = self
101 .0
102 .get_mut(index..(index + values.len()))
103 .ok_or(StateMemoryError::IndexOutOfBounds)?;
104
105 for (slot, value) in slots.iter_mut().zip(values) {
106 *slot = value;
107 }
108 Ok(())
109 }
110}
111
112impl core::ops::Deref for StateMemory {
113 type Target = Vec<Vec<Word>>;
114 fn deref(&self) -> &Self::Target {
115 &self.0
116 }
117}
118
119impl From<StateMemory> for Vec<Vec<Word>> {
120 fn from(state_slots: StateMemory) -> Self {
121 state_slots.0
122 }
123}
124
125pub fn alloc_slots(stack: &mut Stack, slots: &mut StateMemory) -> OpSyncResult<()> {
127 let size = stack.pop()?;
128 let size = usize::try_from(size).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
129 slots.alloc_slots(size)?;
130 Ok(())
131}
132
133pub fn length(stack: &mut Stack, slots: &StateMemory) -> OpSyncResult<()> {
135 let len = Word::try_from(slots.len()).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
136 stack.push(len)?;
137 Ok(())
138}
139
140pub fn value_len(stack: &mut Stack, slots: &StateMemory) -> OpSyncResult<()> {
142 let slot = stack.pop()?;
143 let slot = usize::try_from(slot).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
144 let len =
145 Word::try_from(slots.value_len(slot)?).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
146 stack.push(len)?;
147 Ok(())
148}
149
150pub fn truncate(stack: &mut Stack, slots: &mut StateMemory) -> OpSyncResult<()> {
152 let len = stack.pop()?;
153 let index = stack.pop()?;
154 let len = usize::try_from(len).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
155 let index = usize::try_from(index).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
156 slots.truncate(index, len)?;
157 Ok(())
158}
159
160pub fn load(stack: &mut Stack, slots: &StateMemory) -> OpSyncResult<()> {
162 let len = stack.pop()?;
163 let value_ix = stack.pop()?;
164 let slot_ix = stack.pop()?;
165 let slot_ix = usize::try_from(slot_ix).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
166 let range = range_from_start_len(value_ix, len).ok_or(StateMemoryError::IndexOutOfBounds)?;
167 let value = slots.load(slot_ix, range)?;
168 stack.extend(value.iter().copied())?;
169 Ok(())
170}
171
172pub fn store(stack: &mut Stack, slots: &mut StateMemory) -> OpSyncResult<()> {
174 let data = stack.pop_len_words::<_, _, StackError>(|value| Ok(value.to_vec()))?;
175 let value_ix = stack.pop()?;
176 let value_ix = usize::try_from(value_ix).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
177 let slot_ix = stack.pop()?;
178 let slot_ix = usize::try_from(slot_ix).map_err(|_| StateMemoryError::IndexOutOfBounds)?;
179 slots.store(slot_ix, value_ix, data)?;
180 Ok(())
181}
182
183fn range_from_start_len(start: Word, len: Word) -> Option<std::ops::Range<usize>> {
184 let start = usize::try_from(start).ok()?;
185 let len = usize::try_from(len).ok()?;
186 let end = start.checked_add(len)?;
187 Some(start..end)
188}