revmc_builtins/
utils.rs

1use crate::gas;
2use revm_interpreter::{as_usize_saturated, Gas, InstructionResult, SharedMemory};
3use revmc_context::{EvmContext, EvmWord};
4
5/// Splits the stack pointer into `N` elements by casting it to an array.
6///
7/// NOTE: this returns the arguments in **reverse order**. Use [`read_words!`] to get them in order.
8///
9/// The returned lifetime is valid for the entire duration of the builtin.
10///
11/// # Safety
12///
13/// Caller must ensure that `N` matches the number of elements popped in JIT code.
14#[inline(always)]
15pub(crate) unsafe fn read_words_rev<'a, const N: usize>(sp: *mut EvmWord) -> &'a mut [EvmWord; N] {
16    &mut *sp.cast::<[EvmWord; N]>()
17}
18
19#[inline]
20pub(crate) fn ensure_memory(
21    ecx: &mut EvmContext<'_>,
22    offset: usize,
23    len: usize,
24) -> InstructionResult {
25    ensure_memory_inner(ecx.memory, ecx.gas, offset, len)
26}
27
28#[inline]
29pub(crate) fn ensure_memory_inner(
30    memory: &mut SharedMemory,
31    gas: &mut Gas,
32    offset: usize,
33    len: usize,
34) -> InstructionResult {
35    let new_size = offset.saturating_add(len);
36    if new_size > memory.len() {
37        return resize_memory_inner(memory, gas, new_size);
38    }
39    InstructionResult::Stop
40}
41
42#[inline]
43pub(crate) fn resize_memory(ecx: &mut EvmContext<'_>, new_size: usize) -> InstructionResult {
44    resize_memory_inner(ecx.memory, ecx.gas, new_size)
45}
46
47fn resize_memory_inner(
48    memory: &mut SharedMemory,
49    gas: &mut Gas,
50    new_size: usize,
51) -> InstructionResult {
52    // Calculate words needed (memory is always word-aligned)
53    let new_num_words = revm_interpreter::interpreter::num_words(new_size);
54    let current_words = gas.memory().words_num;
55
56    if new_num_words > current_words {
57        // Calculate gas cost for memory expansion
58        // memory_gas(num_words, linear_cost, quadratic_cost)
59        // MEMORY = 3 (linear cost per word), MEMORY_QUAD_COEFFICIENT = 512 (1/512 for quadratic)
60        let new_cost = crate::gas::memory_gas(new_num_words, 3, 512);
61        let old_cost = crate::gas::memory_gas(current_words, 3, 512);
62        let cost = new_cost.saturating_sub(old_cost);
63
64        if !gas.record_cost(cost) {
65            return InstructionResult::MemoryOOG;
66        }
67
68        // Update memory words tracking
69        gas.memory_mut().words_num = new_num_words;
70
71        // Resize the actual memory (must be word-aligned, as per EVM spec)
72        memory.resize(new_num_words * 32);
73    }
74    InstructionResult::Stop
75}
76
77pub(crate) unsafe fn copy_operation(
78    ecx: &mut EvmContext<'_>,
79    rev![memory_offset, data_offset, len]: &mut [EvmWord; 3],
80    data: &[u8],
81) -> InstructionResult {
82    let len = try_into_usize!(len);
83    if len != 0 {
84        gas_opt!(ecx, gas::dyn_verylowcopy_cost(len as u64));
85        let memory_offset = try_into_usize!(memory_offset);
86        ensure_memory!(ecx, memory_offset, len);
87        let data_offset = data_offset.to_u256();
88        let data_offset = as_usize_saturated!(data_offset);
89        ecx.memory.set_data(memory_offset, data_offset, len, data);
90    }
91    InstructionResult::Stop
92}