rtvm_interpreter/interpreter/
shared_memory.rs1use rtvm_primitives::{B256, U256};
2
3use core::{
4 cmp::min,
5 fmt,
6 ops::{BitAnd, Not, Range},
7};
8use std::vec::Vec;
9
10#[derive(Clone, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct SharedMemory {
17 buffer: Vec<u8>,
19 checkpoints: Vec<usize>,
22 last_checkpoint: usize,
24 #[cfg(feature = "memory_limit")]
26 memory_limit: u64,
27}
28
29pub const EMPTY_SHARED_MEMORY: SharedMemory = SharedMemory {
33 buffer: Vec::new(),
34 checkpoints: Vec::new(),
35 last_checkpoint: 0,
36 #[cfg(feature = "memory_limit")]
37 memory_limit: u64::MAX,
38};
39
40impl fmt::Debug for SharedMemory {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.debug_struct("SharedMemory")
43 .field("current_len", &self.len())
44 .field(
45 "context_memory",
46 &crate::primitives::hex::encode(self.context_memory()),
47 )
48 .finish_non_exhaustive()
49 }
50}
51
52impl Default for SharedMemory {
53 #[inline]
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl SharedMemory {
60 #[inline]
64 pub fn new() -> Self {
65 Self::with_capacity(4 * 1024) }
67
68 #[inline]
70 pub fn with_capacity(capacity: usize) -> Self {
71 Self {
72 buffer: Vec::with_capacity(capacity),
73 checkpoints: Vec::with_capacity(32),
74 last_checkpoint: 0,
75 #[cfg(feature = "memory_limit")]
76 memory_limit: u64::MAX,
77 }
78 }
79
80 #[cfg(feature = "memory_limit")]
85 #[inline]
86 pub fn new_with_memory_limit(memory_limit: u64) -> Self {
87 Self {
88 memory_limit,
89 ..Self::new()
90 }
91 }
92
93 #[cfg(feature = "memory_limit")]
96 #[inline]
97 pub fn limit_reached(&self, new_size: usize) -> bool {
98 (self.last_checkpoint + new_size) as u64 > self.memory_limit
99 }
100
101 #[inline]
103 pub fn new_context(&mut self) {
104 let new_checkpoint = self.buffer.len();
105 self.checkpoints.push(new_checkpoint);
106 self.last_checkpoint = new_checkpoint;
107 }
108
109 #[inline]
111 pub fn free_context(&mut self) {
112 if let Some(old_checkpoint) = self.checkpoints.pop() {
113 self.last_checkpoint = self.checkpoints.last().cloned().unwrap_or_default();
114 unsafe { self.buffer.set_len(old_checkpoint) };
116 }
117 }
118
119 #[inline]
121 pub fn len(&self) -> usize {
122 self.buffer.len() - self.last_checkpoint
123 }
124
125 #[inline]
127 pub fn is_empty(&self) -> bool {
128 self.len() == 0
129 }
130
131 #[inline]
133 pub fn resize(&mut self, new_size: usize) {
134 self.buffer.resize(self.last_checkpoint + new_size, 0);
135 }
136
137 #[inline]
143 #[cfg_attr(debug_assertions, track_caller)]
144 pub fn slice(&self, offset: usize, size: usize) -> &[u8] {
145 self.slice_range(offset..offset + size)
146 }
147
148 #[inline]
149 #[cfg_attr(debug_assertions, track_caller)]
150 pub fn slice_range(&self, range: Range<usize>) -> &[u8] {
151 let last_checkpoint = self.last_checkpoint;
152
153 self.buffer
154 .get(last_checkpoint + range.start..last_checkpoint + range.end)
155 .unwrap_or_else(|| {
156 debug_unreachable!(
157 "slice OOB: {}..{}; len: {}",
158 range.start,
159 range.end,
160 self.len()
161 )
162 })
163 }
164
165 #[inline]
171 #[cfg_attr(debug_assertions, track_caller)]
172 pub fn slice_mut(&mut self, offset: usize, size: usize) -> &mut [u8] {
173 let len = self.len();
174 let end = offset + size;
175 let last_checkpoint = self.last_checkpoint;
176
177 self.buffer
178 .get_mut(last_checkpoint + offset..last_checkpoint + offset + size)
179 .unwrap_or_else(|| debug_unreachable!("slice OOB: {offset}..{end}; len: {}", len))
180 }
181
182 #[inline]
188 pub fn get_byte(&self, offset: usize) -> u8 {
189 self.slice(offset, 1)[0]
190 }
191
192 #[inline]
198 pub fn get_word(&self, offset: usize) -> B256 {
199 self.slice(offset, 32).try_into().unwrap()
200 }
201
202 #[inline]
208 pub fn get_u256(&self, offset: usize) -> U256 {
209 self.get_word(offset).into()
210 }
211
212 #[inline]
218 #[cfg_attr(debug_assertions, track_caller)]
219 pub fn set_byte(&mut self, offset: usize, byte: u8) {
220 self.set(offset, &[byte]);
221 }
222
223 #[inline]
229 #[cfg_attr(debug_assertions, track_caller)]
230 pub fn set_word(&mut self, offset: usize, value: &B256) {
231 self.set(offset, &value[..]);
232 }
233
234 #[inline]
240 #[cfg_attr(debug_assertions, track_caller)]
241 pub fn set_u256(&mut self, offset: usize, value: U256) {
242 self.set(offset, &value.to_be_bytes::<32>());
243 }
244
245 #[inline]
251 #[cfg_attr(debug_assertions, track_caller)]
252 pub fn set(&mut self, offset: usize, value: &[u8]) {
253 if !value.is_empty() {
254 self.slice_mut(offset, value.len()).copy_from_slice(value);
255 }
256 }
257
258 #[inline]
265 #[cfg_attr(debug_assertions, track_caller)]
266 pub fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) {
267 if data_offset >= data.len() {
268 self.slice_mut(memory_offset, len).fill(0);
270 return;
271 }
272 let data_end = min(data_offset + len, data.len());
273 let data_len = data_end - data_offset;
274 debug_assert!(data_offset < data.len() && data_end <= data.len());
275 let data = unsafe { data.get_unchecked(data_offset..data_end) };
276 self.slice_mut(memory_offset, data_len)
277 .copy_from_slice(data);
278
279 self.slice_mut(memory_offset + data_len, len - data_len)
282 .fill(0);
283 }
284
285 #[inline]
291 #[cfg_attr(debug_assertions, track_caller)]
292 pub fn copy(&mut self, dst: usize, src: usize, len: usize) {
293 self.context_memory_mut().copy_within(src..src + len, dst);
294 }
295
296 #[inline]
298 pub fn context_memory(&self) -> &[u8] {
299 unsafe {
301 self.buffer
302 .get_unchecked(self.last_checkpoint..self.buffer.len())
303 }
304 }
305
306 #[inline]
308 pub fn context_memory_mut(&mut self) -> &mut [u8] {
309 let buf_len = self.buffer.len();
310 unsafe { self.buffer.get_unchecked_mut(self.last_checkpoint..buf_len) }
312 }
313}
314
315#[inline]
318pub fn next_multiple_of_32(x: usize) -> usize {
319 let r = x.bitand(31).not().wrapping_add(1).bitand(31);
320 x.saturating_add(r)
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_next_multiple_of_32() {
329 for i in 0..32 {
331 let x = i * 32;
332 assert_eq!(x, next_multiple_of_32(x));
333 }
334
335 for x in 0..1024 {
337 if x % 32 == 0 {
338 continue;
339 }
340 let next_multiple = x + 32 - (x % 32);
341 assert_eq!(next_multiple, next_multiple_of_32(x));
342 }
343
344 assert_eq!(usize::MAX, next_multiple_of_32(usize::MAX));
346 }
347
348 #[test]
349 fn new_free_context() {
350 let mut shared_memory = SharedMemory::new();
351 shared_memory.new_context();
352
353 assert_eq!(shared_memory.buffer.len(), 0);
354 assert_eq!(shared_memory.checkpoints.len(), 1);
355 assert_eq!(shared_memory.last_checkpoint, 0);
356
357 unsafe { shared_memory.buffer.set_len(32) };
358 assert_eq!(shared_memory.len(), 32);
359 shared_memory.new_context();
360
361 assert_eq!(shared_memory.buffer.len(), 32);
362 assert_eq!(shared_memory.checkpoints.len(), 2);
363 assert_eq!(shared_memory.last_checkpoint, 32);
364 assert_eq!(shared_memory.len(), 0);
365
366 unsafe { shared_memory.buffer.set_len(96) };
367 assert_eq!(shared_memory.len(), 64);
368 shared_memory.new_context();
369
370 assert_eq!(shared_memory.buffer.len(), 96);
371 assert_eq!(shared_memory.checkpoints.len(), 3);
372 assert_eq!(shared_memory.last_checkpoint, 96);
373 assert_eq!(shared_memory.len(), 0);
374
375 shared_memory.free_context();
377 assert_eq!(shared_memory.buffer.len(), 96);
378 assert_eq!(shared_memory.checkpoints.len(), 2);
379 assert_eq!(shared_memory.last_checkpoint, 32);
380 assert_eq!(shared_memory.len(), 64);
381
382 shared_memory.free_context();
383 assert_eq!(shared_memory.buffer.len(), 32);
384 assert_eq!(shared_memory.checkpoints.len(), 1);
385 assert_eq!(shared_memory.last_checkpoint, 0);
386 assert_eq!(shared_memory.len(), 32);
387
388 shared_memory.free_context();
389 assert_eq!(shared_memory.buffer.len(), 0);
390 assert_eq!(shared_memory.checkpoints.len(), 0);
391 assert_eq!(shared_memory.last_checkpoint, 0);
392 assert_eq!(shared_memory.len(), 0);
393 }
394
395 #[test]
396 fn resize() {
397 let mut shared_memory = SharedMemory::new();
398 shared_memory.new_context();
399
400 shared_memory.resize(32);
401 assert_eq!(shared_memory.buffer.len(), 32);
402 assert_eq!(shared_memory.len(), 32);
403 assert_eq!(shared_memory.buffer.get(0..32), Some(&[0_u8; 32] as &[u8]));
404
405 shared_memory.new_context();
406 shared_memory.resize(96);
407 assert_eq!(shared_memory.buffer.len(), 128);
408 assert_eq!(shared_memory.len(), 96);
409 assert_eq!(
410 shared_memory.buffer.get(32..128),
411 Some(&[0_u8; 96] as &[u8])
412 );
413
414 shared_memory.free_context();
415 shared_memory.resize(64);
416 assert_eq!(shared_memory.buffer.len(), 64);
417 assert_eq!(shared_memory.len(), 64);
418 assert_eq!(shared_memory.buffer.get(0..64), Some(&[0_u8; 64] as &[u8]));
419 }
420}