ethrex_levm/opcode_handlers/
exchange.rs1use crate::{
7 constants::STACK_LIMIT,
8 errors::{ExceptionalHalt, OpcodeResult, VMError},
9 gas_cost,
10 opcode_handlers::OpcodeHandler,
11 vm::VM,
12};
13use std::mem;
14
15pub struct OpSwapHandler<const N: usize>;
17impl<const N: usize> OpcodeHandler for OpSwapHandler<N> {
18 #[inline(always)]
19 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
20 vm.current_call_frame
21 .increase_consumed_gas(gas_cost::SWAPN)?;
22
23 vm.current_call_frame.stack.swap::<N>()?;
24
25 Ok(OpcodeResult::Continue)
26 }
27}
28
29pub struct OpSwapNHandler;
31impl OpcodeHandler for OpSwapNHandler {
32 #[inline(always)]
33 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
34 vm.current_call_frame
35 .increase_consumed_gas(gas_cost::SWAPN)?;
36
37 let relative_offset = vm
38 .current_call_frame
39 .bytecode
40 .dispatch_buf()
41 .get(vm.current_call_frame.pc)
42 .copied()
43 .unwrap_or_default();
44
45 if (0x5B..0x80).contains(&relative_offset) {
50 return Err(ExceptionalHalt::InvalidOpcode.into());
51 }
52 let relative_offset = relative_offset.wrapping_add(145);
53
54 let absolute_offset = vm
58 .current_call_frame
59 .stack
60 .offset
61 .checked_add(usize::from(relative_offset))
62 .ok_or(ExceptionalHalt::StackUnderflow)?;
63
64 if absolute_offset >= STACK_LIMIT {
66 return Err(ExceptionalHalt::StackUnderflow.into());
67 }
68
69 let top_offset = vm.current_call_frame.stack.offset;
70
71 #[expect(unsafe_code, reason = "bound already checked")]
72 unsafe {
73 let [x, y] = vm
74 .current_call_frame
75 .stack
76 .values
77 .get_disjoint_unchecked_mut([top_offset, absolute_offset]);
78 mem::swap(x, y);
79 }
80
81 vm.current_call_frame.pc = vm.current_call_frame.pc.wrapping_add(1);
82 Ok(OpcodeResult::Continue)
83 }
84}
85
86pub struct OpExchangeHandler;
88impl OpcodeHandler for OpExchangeHandler {
89 #[inline(always)]
90 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
91 vm.current_call_frame
92 .increase_consumed_gas(gas_cost::EXCHANGE)?;
93
94 let relative_offset = vm
95 .current_call_frame
96 .bytecode
97 .dispatch_buf()
98 .get(vm.current_call_frame.pc)
99 .copied()
100 .unwrap_or_default();
101
102 if (0x52..0x80).contains(&relative_offset) {
111 return Err(ExceptionalHalt::InvalidOpcode.into());
112 }
113
114 let relative_offset = {
115 let byte = relative_offset ^ 0x8F;
116
117 let q = byte >> 4;
118 let r = byte & 0x0F;
119
120 #[expect(
121 clippy::arithmetic_side_effects,
122 reason = "ranges are limited, cannot overflow or underflow"
123 )]
124 if q < r {
125 (q + 1, r + 1)
126 } else {
127 (r + 1, 29 - q)
128 }
129 };
130
131 let absolute_offset = {
133 let stack_offset = vm.current_call_frame.stack.offset;
134
135 let q = stack_offset
136 .checked_add(usize::from(relative_offset.0))
137 .ok_or(ExceptionalHalt::StackUnderflow)?;
138 let r = stack_offset
139 .checked_add(usize::from(relative_offset.1))
140 .ok_or(ExceptionalHalt::StackUnderflow)?;
141
142 if q >= STACK_LIMIT || r >= STACK_LIMIT {
144 return Err(ExceptionalHalt::StackUnderflow.into());
145 }
146
147 (q, r)
148 };
149
150 #[expect(unsafe_code, reason = "bound already checked")]
151 unsafe {
152 let [x, y] = vm
153 .current_call_frame
154 .stack
155 .values
156 .get_disjoint_unchecked_mut([absolute_offset.0, absolute_offset.1]);
157 mem::swap(x, y);
158 }
159
160 vm.current_call_frame.pc = vm.current_call_frame.pc.wrapping_add(1);
161 Ok(OpcodeResult::Continue)
162 }
163}