1use crate::{opcode, Bytecode, OpCode};
2
3#[derive(Debug, Clone)]
8pub struct BytecodeIterator<'a> {
9 bytes: core::slice::Iter<'a, u8>,
11 start: *const u8,
13}
14
15impl<'a> BytecodeIterator<'a> {
16 #[inline]
18 pub fn new(bytecode: &'a Bytecode) -> Self {
19 let bytes = if bytecode.is_legacy() {
20 bytecode.original_byte_slice()
21 } else {
22 &[]
23 };
24 Self {
25 bytes: bytes.iter(),
26 start: bytes.as_ptr(),
27 }
28 }
29
30 pub fn skip_to_next_opcode(&mut self) {
32 self.next();
33 }
34
35 #[inline]
37 pub fn as_slice(&self) -> &[u8] {
38 self.bytes.as_slice()
39 }
40
41 #[inline]
43 pub fn position(&self) -> usize {
44 unsafe {
46 self.bytes
47 .as_slice()
48 .as_ptr()
49 .offset_from_unsigned(self.start)
50 }
51 }
52
53 #[inline]
54 fn skip_immediate(&mut self, opcode: u8) {
55 let immediate_size = opcode::OPCODE_INFO[opcode as usize]
57 .map(|info| info.immediate_size() as usize)
58 .unwrap_or_default();
59
60 if immediate_size > 0 {
62 let remaining = self.bytes.as_slice();
63 self.bytes = remaining[immediate_size.min(remaining.len())..].iter();
64 }
65 }
66
67 #[inline]
69 pub fn peek(&self) -> Option<u8> {
70 self.bytes.as_slice().first().copied()
71 }
72
73 #[inline]
75 pub fn peek_opcode(&self) -> Option<OpCode> {
76 self.peek().and_then(OpCode::new)
77 }
78}
79
80impl Iterator for BytecodeIterator<'_> {
81 type Item = u8;
82
83 #[inline]
84 fn next(&mut self) -> Option<Self::Item> {
85 self.bytes
86 .next()
87 .copied()
88 .inspect(|¤t| self.skip_immediate(current))
89 }
90
91 #[inline]
92 fn size_hint(&self) -> (usize, Option<usize>) {
93 let byte_len = self.bytes.len();
95 (byte_len.min(1), Some(byte_len))
96 }
97}
98
99impl core::iter::FusedIterator for BytecodeIterator<'_> {}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use primitives::Bytes;
105
106 #[test]
107 fn test_simple_bytecode_iteration() {
108 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[
110 opcode::PUSH1,
111 0x01,
112 opcode::PUSH1,
113 0x02,
114 opcode::ADD,
115 opcode::STOP,
116 ]));
117 let opcodes: Vec<u8> = bytecode.iter_opcodes().collect();
118 assert_eq!(
119 opcodes,
120 vec![opcode::PUSH1, opcode::PUSH1, opcode::ADD, opcode::STOP]
121 );
122 }
123
124 #[test]
125 fn test_bytecode_with_various_push_sizes() {
126 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[
127 opcode::PUSH1,
128 0x01,
129 opcode::PUSH2,
130 0x02,
131 0x03,
132 opcode::PUSH3,
133 0x04,
134 0x05,
135 0x06,
136 opcode::STOP,
137 ]));
138
139 let opcodes: Vec<u8> = bytecode.iter_opcodes().collect();
140
141 assert_eq!(
143 opcodes,
144 vec![opcode::PUSH1, opcode::PUSH2, opcode::PUSH3, opcode::STOP]
145 );
146 }
147
148 #[test]
149 fn test_bytecode_skips_immediates() {
150 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[
151 opcode::PUSH1,
152 0x01,
153 opcode::PUSH2,
154 0x02,
155 0x03,
156 opcode::ADD,
157 opcode::PUSH3,
158 0x04,
159 0x05,
160 0x06,
161 opcode::PUSH32,
162 0x10,
163 0x11,
164 0x12,
165 0x13,
166 0x14,
167 0x15,
168 0x16,
169 0x17,
170 0x18,
171 0x19,
172 0x1a,
173 0x1b,
174 0x1c,
175 0x1d,
176 0x1e,
177 0x1f,
178 0x20,
179 0x21,
180 0x22,
181 0x23,
182 0x24,
183 0x25,
184 0x26,
185 0x27,
186 0x28,
187 0x29,
188 0x2a,
189 0x2b,
190 0x2c,
191 0x2d,
192 0x2e,
193 0x2f,
194 opcode::MUL,
195 opcode::STOP,
196 ]));
197
198 let opcodes: Vec<u8> = bytecode.iter_opcodes().collect();
199 assert_eq!(
200 opcodes,
201 vec![
202 opcode::PUSH1,
203 opcode::PUSH2,
204 opcode::ADD,
205 opcode::PUSH3,
206 opcode::PUSH32,
207 opcode::MUL,
208 opcode::STOP,
209 ]
210 );
211 }
212
213 #[test]
214 fn test_position_tracking() {
215 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[
216 opcode::PUSH1,
217 0x01,
218 opcode::PUSH1,
219 0x02,
220 opcode::ADD,
221 opcode::STOP,
222 ]));
223
224 let mut iter = bytecode.iter_opcodes();
225
226 assert_eq!(iter.position(), 0);
227 assert_eq!(iter.next(), Some(opcode::PUSH1));
228 assert_eq!(iter.position(), 2);
229
230 assert_eq!(iter.next(), Some(opcode::PUSH1));
231 assert_eq!(iter.position(), 4);
232
233 assert_eq!(iter.next(), Some(opcode::ADD));
234 assert_eq!(iter.position(), 5);
235
236 assert_eq!(iter.next(), Some(opcode::STOP));
237 assert_eq!(iter.position(), 6);
238
239 assert_eq!(iter.next(), None);
240 assert_eq!(iter.position(), 6);
241 }
242
243 #[test]
244 fn test_empty_bytecode() {
245 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::STOP]));
246 let opcodes: Vec<u8> = bytecode.iter_opcodes().collect();
247 assert_eq!(opcodes, vec![opcode::STOP]);
248 }
249
250 #[test]
251 fn test_truncated_push_does_not_iterate_padding() {
252 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1]));
253 let opcodes: Vec<u8> = bytecode.iter_opcodes().collect();
254 assert_eq!(opcodes, vec![opcode::PUSH1]);
255 }
256
257 #[test]
258 fn test_position_after_truncated_push() {
259 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH2, 0x01]));
260 let mut iter = bytecode.iter_opcodes();
261
262 assert_eq!(iter.next(), Some(opcode::PUSH2));
263 assert_eq!(iter.position(), 2);
264 assert_eq!(iter.next(), None);
265 }
266}