revm_context_interface/
local.rs

1//! Local context trait [`LocalContextTr`] and related types.
2use core::{
3    cell::{Ref, RefCell},
4    ops::Range,
5};
6use std::{rc::Rc, vec::Vec};
7
8/// Non-empty, item-pooling Vec.
9#[derive(Debug, Clone)]
10pub struct FrameStack<T> {
11    stack: Vec<T>,
12    index: Option<usize>,
13}
14
15impl<T> Default for FrameStack<T> {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl<T> FrameStack<T> {
22    /// Creates a new, empty stack. It must be initialized with init before use.
23    pub fn new() -> Self {
24        Self {
25            stack: Vec::with_capacity(4),
26            index: None,
27        }
28    }
29
30    /// Initializes the stack with a single item.
31    #[inline]
32    pub fn start_init(&mut self) -> OutFrame<'_, T> {
33        self.index = None;
34        if self.stack.is_empty() {
35            self.stack.reserve(1);
36        }
37        self.out_frame_at(0)
38    }
39
40    /// Finishes initialization.
41    ///
42    /// # Safety
43    ///
44    /// This method is unsafe because it assumes that the `token` is initialized from this FrameStack object.
45    #[inline]
46    pub unsafe fn end_init(&mut self, token: FrameToken) {
47        token.assert();
48        if self.stack.is_empty() {
49            unsafe { self.stack.set_len(1) };
50        }
51        self.index = Some(0);
52    }
53
54    /// Returns the current index of the stack.
55    #[inline]
56    pub fn index(&self) -> Option<usize> {
57        self.index
58    }
59
60    /// Increments the index.
61    ///
62    /// # Safety
63    ///
64    /// This method is unsafe because it assumes that the `token` is obtained from `get_next` and
65    /// that `end_init` is called to initialize the FrameStack.
66    #[inline]
67    pub unsafe fn push(&mut self, token: FrameToken) {
68        token.assert();
69        let index = self.index.as_mut().unwrap();
70        if *index + 1 == self.stack.len() {
71            unsafe { self.stack.set_len(self.stack.len() + 1) };
72            self.stack.reserve(1);
73        }
74        *index += 1;
75    }
76
77    /// Clears the stack by setting the index to 0.
78    /// It does not destroy the stack.
79    #[inline]
80    pub fn clear(&mut self) {
81        self.index = None;
82    }
83
84    /// Decrements the index.
85    #[inline]
86    pub fn pop(&mut self) {
87        self.index = self.index.unwrap_or(0).checked_sub(1);
88    }
89
90    /// Returns the current item.
91    #[inline]
92    pub fn get(&mut self) -> &mut T {
93        debug_assert!(self.stack.capacity() > self.index.unwrap() + 1);
94        unsafe { &mut *self.stack.as_mut_ptr().add(self.index.unwrap()) }
95    }
96
97    /// Get next uninitialized item.
98    #[inline]
99    pub fn get_next(&mut self) -> OutFrame<'_, T> {
100        self.out_frame_at(self.index.unwrap() + 1)
101    }
102
103    fn out_frame_at(&mut self, idx: usize) -> OutFrame<'_, T> {
104        unsafe {
105            OutFrame::new_maybe_uninit(self.stack.as_mut_ptr().add(idx), idx < self.stack.len())
106        }
107    }
108}
109
110/// A potentially initialized frame. Used when initializing a new frame in the main loop.
111#[allow(missing_debug_implementations)]
112pub struct OutFrame<'a, T> {
113    ptr: *mut T,
114    init: bool,
115    lt: core::marker::PhantomData<&'a mut T>,
116}
117
118impl<'a, T> OutFrame<'a, T> {
119    /// Creates a new initialized `OutFrame` from a mutable reference to a type `T`.
120    pub fn new_init(slot: &'a mut T) -> Self {
121        unsafe { Self::new_maybe_uninit(slot, true) }
122    }
123
124    /// Creates a new uninitialized `OutFrame` from a mutable reference to a `MaybeUninit<T>`.
125    pub fn new_uninit(slot: &'a mut core::mem::MaybeUninit<T>) -> Self {
126        unsafe { Self::new_maybe_uninit(slot.as_mut_ptr(), false) }
127    }
128
129    /// Creates a new `OutFrame` from a raw pointer to a type `T`.
130    ///
131    /// # Safety
132    ///
133    /// This method is unsafe because it assumes that the pointer is valid and points to a location
134    /// where a type `T` can be stored. It also assumes that the `init` flag correctly reflects whether
135    /// the type `T` has been initialized or not.
136    pub unsafe fn new_maybe_uninit(ptr: *mut T, init: bool) -> Self {
137        Self {
138            ptr,
139            init,
140            lt: Default::default(),
141        }
142    }
143
144    /// Returns a mutable reference to the type `T`, initializing it if it hasn't been initialized yet.
145    pub fn get(&mut self, f: impl FnOnce() -> T) -> &mut T {
146        if !self.init {
147            self.do_init(f);
148        }
149        unsafe { &mut *self.ptr }
150    }
151
152    #[inline(never)]
153    #[cold]
154    fn do_init(&mut self, f: impl FnOnce() -> T) {
155        unsafe {
156            self.init = true;
157            self.ptr.write(f());
158        }
159    }
160
161    /// Returns a mutable reference to the type `T`, without checking if it has been initialized.
162    ///
163    /// # Safety
164    ///
165    /// This method is unsafe because it assumes that the `OutFrame` has been initialized before use.
166    pub unsafe fn get_unchecked(&mut self) -> &mut T {
167        debug_assert!(self.init, "OutFrame must be initialized before use");
168        unsafe { &mut *self.ptr }
169    }
170
171    /// Consumes the `OutFrame`, returning a `FrameToken` that indicates the frame has been initialized.
172    pub fn consume(self) -> FrameToken {
173        FrameToken(self.init)
174    }
175}
176
177/// Used to guarantee that a frame is initialized before use.
178#[allow(missing_debug_implementations)]
179pub struct FrameToken(bool);
180
181impl FrameToken {
182    /// Asserts that the frame token is initialized.
183    #[cfg_attr(debug_assertions, track_caller)]
184    pub fn assert(self) {
185        assert!(self.0, "FrameToken must be initialized before use");
186    }
187}
188
189/// Local context used for caching initcode from Initcode transactions.
190pub trait LocalContextTr {
191    /// Interpreter shared memory buffer. A reused memory buffer for calls.
192    fn shared_memory_buffer(&self) -> &Rc<RefCell<Vec<u8>>>;
193
194    /// Slice of the shared memory buffer returns None if range is not valid or buffer can't be borrowed.
195    fn shared_memory_buffer_slice(&self, range: Range<usize>) -> Option<Ref<'_, [u8]>> {
196        let buffer = self.shared_memory_buffer();
197        buffer.borrow().get(range.clone())?;
198        Some(Ref::map(buffer.borrow(), |b| {
199            b.get(range).unwrap_or_default()
200        }))
201    }
202
203    /// Clear the local context.
204    fn clear(&mut self);
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn frame_stack() {
213        let mut stack = FrameStack::new();
214        let mut frame = stack.start_init();
215        frame.get(|| 1);
216        let token = frame.consume();
217        unsafe { stack.end_init(token) };
218
219        assert_eq!(stack.index(), Some(0));
220        assert_eq!(stack.stack.len(), 1);
221
222        let a = stack.get();
223        assert_eq!(a, &mut 1);
224        let mut b = stack.get_next();
225        assert!(!b.init);
226        assert_eq!(b.get(|| 2), &mut 2);
227        let token = b.consume(); // TODO: remove
228        unsafe { stack.push(token) };
229
230        assert_eq!(stack.index(), Some(1));
231        assert_eq!(stack.stack.len(), 2);
232        let a = stack.get();
233        assert_eq!(a, &mut 2);
234        let b = stack.get_next();
235        assert!(!b.init);
236
237        stack.pop();
238
239        assert_eq!(stack.index(), Some(0));
240        assert_eq!(stack.stack.len(), 2);
241        let a = stack.get();
242        assert_eq!(a, &mut 1);
243        let mut b = stack.get_next();
244        assert!(b.init);
245        assert_eq!(unsafe { b.get_unchecked() }, &mut 2);
246    }
247}