1#![doc = essential_asm_gen::gen_ops_docs_table!()]
5#![cfg_attr(not(feature = "std"), no_std)]
6#![forbid(unsafe_code)]
7#![warn(missing_docs)]
8
9use core::fmt;
10#[doc(inline)]
11pub use essential_types::Word;
12#[doc(inline)]
13pub use op::{Op, *};
14#[doc(inline)]
15pub use opcode::{InvalidOpcodeError, NotEnoughBytesError, Op as Opcode};
16
17pub mod effects;
19
20mod op {
22 pub trait ToBytes {
24 type Bytes: IntoIterator<Item = u8>;
26 fn to_bytes(&self) -> Self::Bytes;
28 }
29
30 pub trait ToOpcode {
32 type Opcode;
34 fn to_opcode(&self) -> Self::Opcode;
36 }
37
38 pub trait TryFromBytes: Sized {
40 type Error: core::fmt::Debug + core::fmt::Display;
42 fn try_from_bytes(
46 bytes: &mut impl Iterator<Item = u8>,
47 ) -> Option<Result<Self, Self::Error>>;
48 }
49
50 essential_asm_gen::gen_all_op_decls!();
51 essential_asm_gen::gen_all_op_impls!();
52
53 pub mod bytes_iter {
55 essential_asm_gen::gen_all_op_bytes_iter!();
56 }
57
58 pub mod short {
60 use super::{Op, *};
61 essential_asm_gen::gen_all_op_consts!();
62 }
63}
64
65pub mod opcode {
67 use core::fmt;
68
69 pub trait ParseOp {
71 type Op;
73 type Error: core::fmt::Debug + core::fmt::Display;
75 fn parse_op(&self, bytes: &mut impl Iterator<Item = u8>) -> Result<Self::Op, Self::Error>;
82 }
83
84 #[derive(Debug)]
86 pub struct InvalidOpcodeError(pub u8);
87
88 #[derive(Debug)]
92 pub struct NotEnoughBytesError;
93
94 impl fmt::Display for InvalidOpcodeError {
95 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96 write!(f, "Invalid Opcode 0x{:02X}", self.0)
97 }
98 }
99
100 impl fmt::Display for NotEnoughBytesError {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 write!(f, "Provided iterator did not yield enough bytes")
103 }
104 }
105
106 #[cfg(feature = "std")]
107 impl std::error::Error for InvalidOpcodeError {}
108
109 #[cfg(feature = "std")]
110 impl std::error::Error for NotEnoughBytesError {}
111
112 essential_asm_gen::gen_all_opcode_decls!();
113 essential_asm_gen::gen_all_opcode_impls!();
114}
115
116#[derive(Debug)]
118pub enum FromBytesError {
119 InvalidOpcode(InvalidOpcodeError),
121 NotEnoughBytes(NotEnoughBytesError),
123}
124
125impl fmt::Display for FromBytesError {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 f.write_str("failed to parse ops from bytes: ")?;
128 match self {
129 Self::InvalidOpcode(err) => err.fmt(f),
130 Self::NotEnoughBytes(err) => err.fmt(f),
131 }
132 }
133}
134
135#[cfg(feature = "std")]
136impl std::error::Error for FromBytesError {}
137
138impl From<InvalidOpcodeError> for FromBytesError {
139 fn from(err: InvalidOpcodeError) -> Self {
140 Self::InvalidOpcode(err)
141 }
142}
143
144impl From<NotEnoughBytesError> for FromBytesError {
145 fn from(err: NotEnoughBytesError) -> Self {
146 Self::NotEnoughBytes(err)
147 }
148}
149
150pub fn from_bytes(
156 bytes: impl IntoIterator<Item = u8>,
157) -> impl Iterator<Item = Result<Op, FromBytesError>> {
158 let mut iter = bytes.into_iter();
159 core::iter::from_fn(move || Op::try_from_bytes(&mut iter))
160}
161
162pub fn to_bytes(ops: impl IntoIterator<Item = Op>) -> impl Iterator<Item = u8> {
165 ops.into_iter().flat_map(|op| op.to_bytes())
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn opcode_roundtrip_u8() {
174 for byte in 0..=u8::MAX {
175 if let Ok(opcode) = Opcode::try_from(byte) {
176 println!("{byte:02X}: {opcode:?}");
177 assert_eq!(u8::from(opcode), byte);
178 }
179 }
180 }
181
182 fn roundtrip(ops: Vec<Op>) {
183 assert!(!ops.is_empty());
184 let bytes: Vec<_> = to_bytes(ops.iter().cloned()).collect();
185 assert_eq!(
186 ops,
187 from_bytes(bytes).collect::<Result<Vec<_>, _>>().unwrap()
188 );
189 }
190
191 #[test]
192 fn roundtrip_args_start() {
193 let ops: Vec<Op> = vec![
194 Stack::Push(0x1234567812345678).into(),
195 Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
196 Memory::Load.into(),
197 Memory::Store.into(),
198 ];
199 roundtrip(ops);
200 }
201
202 #[test]
203 fn roundtrip_args_end() {
204 let ops: Vec<Op> = vec![
205 Stack::Swap.into(),
206 Stack::Dup.into(),
207 Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
208 ];
209 roundtrip(ops);
210 }
211
212 #[test]
213 fn roundtrip_args_interspersed() {
214 let ops: Vec<Op> = vec![
215 Stack::Push(0x1234567812345678).into(),
216 Stack::Swap.into(),
217 Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
218 Stack::Dup.into(),
219 Stack::Push(0x1234567812345678).into(),
220 ];
221 roundtrip(ops);
222 }
223
224 #[test]
225 fn roundtrip_no_args() {
226 let ops: Vec<Op> = vec![
227 Memory::Store.into(),
228 Access::ThisAddress.into(),
229 Memory::Load.into(),
230 Access::ThisContractAddress.into(),
231 Access::PredicateDataLen.into(),
232 ];
233 roundtrip(ops);
234 }
235
236 fn expect_invalid_opcode(opcode_byte: u8) {
237 let bytes = vec![opcode_byte];
238 let err = from_bytes(bytes)
239 .collect::<Result<Vec<_>, _>>()
240 .unwrap_err();
241 match err {
242 FromBytesError::InvalidOpcode(InvalidOpcodeError(byte)) => {
243 assert_eq!(byte, opcode_byte)
244 }
245 _ => panic!("unexpected error variant"),
246 }
247 }
248
249 #[test]
250 fn invalid_opcode_0x00() {
251 let opcode_byte = 0x00;
252 expect_invalid_opcode(opcode_byte);
253 }
254
255 #[test]
256 fn invalid_opcode_0xff() {
257 let opcode_byte = 0xFF;
258 expect_invalid_opcode(opcode_byte);
259 }
260
261 #[test]
262 fn not_enough_bytes() {
263 let opcode_byte = opcode::Stack::Push as u8;
264 let bytes = vec![opcode_byte];
265 let err = from_bytes(bytes)
266 .collect::<Result<Vec<_>, _>>()
267 .unwrap_err();
268 match err {
269 FromBytesError::NotEnoughBytes(_) => (),
270 _ => panic!("unexpected error variant"),
271 }
272 }
273}