eva_asm/opcode.rs
1//! EVM operation codes and mnemonics.
2
3use derive_more::{Binary, LowerHex, Octal, UpperHex};
4use strum::{Display, EnumCount, EnumIs, FromRepr};
5
6/// EVM operation code.
7#[derive(
8 Clone,
9 Copy,
10 PartialEq,
11 Eq,
12 PartialOrd,
13 Ord,
14 Debug,
15 derive_more::Display,
16 EnumIs,
17 LowerHex,
18 UpperHex,
19 Binary,
20 Octal,
21 Hash,
22)]
23pub enum OpCode {
24 /// A known opcode represented as a mnemonic.
25 Known(Mnemonic),
26 /// An unknown opcode represented as a byte.
27 Unknown(u8),
28}
29
30impl OpCode {
31 /// Convert a byte into an [`OpCode`], returning [`OpCode::Unknown`] if no known mnemonic
32 /// exists.
33 ///
34 /// # Example
35 /// ```
36 /// # use eva_asm::opcode::{OpCode, Mnemonic};
37 /// assert_eq!(OpCode::from_byte(0x5A), OpCode::Known(Mnemonic::GAS));
38 /// assert_eq!(OpCode::from_byte(0xF), OpCode::Unknown(0xF));
39 /// ```
40 #[must_use]
41 #[inline]
42 pub const fn from_byte(byte: u8) -> Self {
43 match Mnemonic::from_repr(byte) {
44 Some(mnemonic) => Self::Known(mnemonic),
45 None => Self::Unknown(byte),
46 }
47 }
48
49 /// Try to convert a byte into a known mnemonic.
50 /// Returns `None` if the opcode is unknown.
51 ///
52 /// # Example
53 /// ```
54 /// # use eva_asm::opcode::{OpCode, Mnemonic};
55 /// assert_eq!(OpCode::try_from_byte(0x5A), Some(OpCode::Known(Mnemonic::GAS)));
56 /// assert_eq!(OpCode::try_from_byte(0xF), None);
57 /// ```
58 #[must_use]
59 #[inline]
60 pub const fn try_from_byte(byte: u8) -> Option<Self> {
61 if let Some(mnemonic) = Mnemonic::from_repr(byte) {
62 Some(Self::Known(mnemonic))
63 } else {
64 None
65 }
66 }
67
68 /// Convert opcode into a byte.
69 ///
70 /// # Example
71 /// ```
72 /// # use eva_asm::opcode::{OpCode, Mnemonic};
73 /// assert_eq!(OpCode::Known(Mnemonic::GAS).into_byte(), 0x5A);
74 /// assert_eq!(OpCode::Unknown(0xF).into_byte(), 0xF);
75 /// ```
76 #[must_use]
77 #[inline]
78 pub const fn into_byte(self) -> u8 {
79 match self {
80 OpCode::Known(mnemonic) => mnemonic as u8,
81 OpCode::Unknown(byte) => byte,
82 }
83 }
84
85 /// Returns a value signifying whether this opcode is of the type `PUSHx`.
86 ///
87 /// # Example
88 /// ```
89 /// # use eva_asm::opcode::{OpCode, Mnemonic};
90 /// assert_eq!(OpCode::Known(Mnemonic::PUSH7).is_push(), true);
91 /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_push(), false);
92 /// ```
93 #[must_use]
94 #[inline]
95 pub const fn is_push(&self) -> bool {
96 match self {
97 OpCode::Known(mnemonic) => mnemonic.is_push(),
98 OpCode::Unknown(_) => false,
99 }
100 }
101
102 /// Returns a value signifying whether this opcode is of the type `DUPx`.
103 ///
104 /// # Example
105 /// ```
106 /// # use eva_asm::opcode::{OpCode, Mnemonic};
107 /// assert_eq!(OpCode::Known(Mnemonic::DUP2).is_dup(), true);
108 /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_dup(), false);
109 /// ```
110 #[must_use]
111 #[inline]
112 pub const fn is_dup(&self) -> bool {
113 match self {
114 OpCode::Known(mnemonic) => mnemonic.is_dup(),
115 OpCode::Unknown(_) => false,
116 }
117 }
118
119 /// Returns a value signifying whether this opcode is of the type `SWAPx`.
120 ///
121 /// # Example
122 /// ```
123 /// # use eva_asm::opcode::{OpCode, Mnemonic};
124 /// assert_eq!(OpCode::Known(Mnemonic::SWAP2).is_swap(), true);
125 /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_swap(), false);
126 /// ```
127 #[must_use]
128 #[inline]
129 pub const fn is_swap(&self) -> bool {
130 match self {
131 OpCode::Known(mnemonic) => mnemonic.is_swap(),
132 OpCode::Unknown(_) => false,
133 }
134 }
135
136 /// Returns a value signifying whether this opcode is of the type `LOGx`.
137 ///
138 /// # Example
139 /// ```
140 /// # use eva_asm::opcode::{OpCode, Mnemonic};
141 /// assert_eq!(OpCode::Known(Mnemonic::LOG2).is_log(), true);
142 /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_log(), false);
143 /// ```
144 #[must_use]
145 #[inline]
146 pub const fn is_log(&self) -> bool {
147 match self {
148 OpCode::Known(mnemonic) => mnemonic.is_log(),
149 OpCode::Unknown(_) => false,
150 }
151 }
152
153 /// Returns [`true`] for opcodes that terminate execution of the smart contract.
154 ///
155 /// # Example
156 /// ```
157 /// # use eva_asm::opcode::{OpCode, Mnemonic};
158 /// assert_eq!(OpCode::Known(Mnemonic::RETURN).is_terminator(), true);
159 /// assert_eq!(OpCode::Unknown(0xF).is_terminator(), true);
160 /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_terminator(), false);
161 /// ```
162 #[must_use]
163 #[inline]
164 pub const fn is_terminator(&self) -> bool {
165 match self {
166 OpCode::Known(mnemonic) => mnemonic.is_terminator(),
167 OpCode::Unknown(_) => true,
168 }
169 }
170}
171
172impl PartialEq<Mnemonic> for OpCode {
173 #[inline]
174 fn eq(&self, other: &Mnemonic) -> bool {
175 u8::from(self) == *other as u8
176 }
177}
178
179impl PartialOrd<Mnemonic> for OpCode {
180 #[inline]
181 fn partial_cmp(&self, other: &Mnemonic) -> Option<std::cmp::Ordering> {
182 u8::from(self).partial_cmp(&(*other as u8))
183 }
184}
185
186impl PartialEq<u8> for OpCode {
187 #[inline]
188 fn eq(&self, other: &u8) -> bool {
189 u8::from(self).eq(other)
190 }
191}
192
193impl PartialOrd<u8> for OpCode {
194 #[inline]
195 fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
196 u8::from(self).partial_cmp(other)
197 }
198}
199
200impl From<OpCode> for u8 {
201 #[inline]
202 fn from(opcode: OpCode) -> Self {
203 opcode.into_byte()
204 }
205}
206
207impl From<&OpCode> for u8 {
208 #[inline]
209 fn from(opcode: &OpCode) -> Self {
210 opcode.into_byte()
211 }
212}
213
214impl From<u8> for OpCode {
215 #[inline]
216 fn from(byte: u8) -> Self {
217 Self::from_byte(byte)
218 }
219}
220
221impl From<Mnemonic> for OpCode {
222 #[inline]
223 fn from(value: Mnemonic) -> Self {
224 Self::Known(value)
225 }
226}
227
228/// EVM operation code mnemonic.
229#[repr(u8)]
230#[non_exhaustive]
231#[derive(
232 Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Display, FromRepr, EnumIs, EnumCount, Hash,
233)]
234pub enum Mnemonic {
235 /// Halts execution.
236 STOP = 0x00,
237 /// Addition operation.
238 ADD = 0x01,
239 /// Multiplication operation.
240 MUL = 0x02,
241 /// Subtraction operation.
242 SUB = 0x03,
243 /// Integer division operation.
244 DIV = 0x04,
245 /// Signed integer division operation (truncated).
246 SDIV = 0x05,
247 /// Modulo remainder operation.
248 MOD = 0x06,
249 /// Signed modulo remainder operation.
250 SMOD = 0x07,
251 /// Modulo addition operation.
252 ADDMOD = 0x08,
253 /// Modulo multiplication operation.
254 MULMOD = 0x09,
255 /// Exponential operation.
256 EXP = 0x0A,
257 /// Extend length of two’s complement signed integer.
258 SIGNEXTEND = 0x0B,
259 /// Less-than comparison.
260 LT = 0x10,
261 /// Greater-than comparison.
262 GT = 0x11,
263 /// Signed less-than comparison.
264 SLT = 0x12,
265 /// Signed greater-than comparison.
266 SGT = 0x13,
267 /// Equality comparison.
268 EQ = 0x14,
269 /// Is-zero comparison.
270 ISZERO = 0x15,
271 /// Bitwise AND operation.
272 AND = 0x16,
273 /// Bitwise OR operation.
274 OR = 0x17,
275 /// Bitwise XOR operation.
276 XOR = 0x18,
277 /// Bitwise NOT operation.
278 NOT = 0x19,
279 /// Retrieve single byte from word.
280 BYTE = 0x1A,
281 /// Left shift operation.
282 SHL = 0x1B,
283 /// Logical right shift operation.
284 SHR = 0x1C,
285 /// Arithmetic (signed) right shift operation.
286 SAR = 0x1D,
287 /// Compute Keccak-256 hash.
288 KECCAK256 = 0x20,
289 /// Get address of currently executing account.
290 ADDRESS = 0x30,
291 /// Get balance of the given account.
292 BALANCE = 0x31,
293 /// Get execution origination address.
294 ORIGIN = 0x32,
295 /// Get caller address.
296 CALLER = 0x33,
297 /// Get deposited value by the instruction/transaction responsible for this execution.
298 CALLVALUE = 0x34,
299 /// Get input data of current environment.
300 CALLDATALOAD = 0x35,
301 /// Get size of input data in current environment.
302 CALLDATASIZE = 0x36,
303 /// Copy input data in current environment to memory.
304 CALLDATACOPY = 0x37,
305 /// Get size of code running in current environment.
306 CODESIZE = 0x38,
307 /// Copy code running in current environment to memory.
308 CODECOPY = 0x39,
309 /// Get price of gas in current environment.
310 GASPRICE = 0x3A,
311 /// Get size of an account’s code.
312 EXTCODESIZE = 0x3B,
313 /// Copy an account’s code to memory.
314 EXTCODECOPY = 0x3C,
315 /// Get size of output data from the previous call from the current environment.
316 RETURNDATASIZE = 0x3D,
317 /// Copy output data from the previous call to memory.
318 RETURNDATACOPY = 0x3E,
319 /// Get hash of an account’s code.
320 EXTCODEHASH = 0x3F,
321 /// Get the hash of one of the 256 most recent complete blocks.
322 BLOCKHASH = 0x40,
323 /// Get the block’s beneficiary address.
324 COINBASE = 0x41,
325 /// Get the block’s timestamp.
326 TIMESTAMP = 0x42,
327 /// Get the block’s number.
328 NUMBER = 0x43,
329 /// Get the block’s difficulty.
330 PREVRANDAO = 0x44,
331 /// Get the block’s gas limit.
332 GASLIMIT = 0x45,
333 /// Get the chain ID.
334 CHAINID = 0x46,
335 /// Get balance of currently executing account.
336 SELFBALANCE = 0x47,
337 /// Get the base fee.
338 BASEFEE = 0x48,
339 /// Get versioned hashes.
340 BLOBHASH = 0x49,
341 /// Returns the value of the blob base-fee of the current block.
342 BLOBBASEFEE = 0x4A,
343 /// Remove item from stack.
344 POP = 0x50,
345 /// Load word from memory.
346 MLOAD = 0x51,
347 /// Save word to memory.
348 MSTORE = 0x52,
349 /// Save byte to memory.
350 MSTORE8 = 0x53,
351 /// Load word from storage.
352 SLOAD = 0x54,
353 /// Save word to storage.
354 SSTORE = 0x55,
355 /// Alter the program counter.
356 JUMP = 0x56,
357 /// Conditionally alter the program counter.
358 JUMPI = 0x57,
359 /// Get the value of the program counter prior to the increment corresponding to this instruction.
360 PC = 0x58,
361 /// Get the size of active memory in bytes.
362 MSIZE = 0x59,
363 /// Get the amount of available gas, including the corresponding reduction for the cost of this instruction.
364 GAS = 0x5A,
365 /// Mark a valid destination for jumps.
366 JUMPDEST = 0x5B,
367 /// Load word from transient storage.
368 TLOAD = 0x5C,
369 /// Save word to transient storage.
370 TSTORE = 0x5D,
371 /// Copy memory areas.
372 MCOPY = 0x5E,
373 /// Place value 0 on stack.
374 PUSH0 = 0x5F,
375 /// Place 1 byte item on stack.
376 PUSH1 = 0x60,
377 /// Place 2 byte item on stack.
378 PUSH2 = 0x61,
379 /// Place 3 byte item on stack.
380 PUSH3 = 0x62,
381 /// Place 4 byte item on stack.
382 PUSH4 = 0x63,
383 /// Place 5 byte item on stack.
384 PUSH5 = 0x64,
385 /// Place 6 byte item on stack.
386 PUSH6 = 0x65,
387 /// Place 7 byte item on stack.
388 PUSH7 = 0x66,
389 /// Place 8 byte item on stack.
390 PUSH8 = 0x67,
391 /// Place 9 byte item on stack.
392 PUSH9 = 0x68,
393 /// Place 10 byte item on stack.
394 PUSH10 = 0x69,
395 /// Place 11 byte item on stack.
396 PUSH11 = 0x6A,
397 /// Place 12 byte item on stack.
398 PUSH12 = 0x6B,
399 /// Place 13 byte item on stack.
400 PUSH13 = 0x6C,
401 /// Place 14 byte item on stack.
402 PUSH14 = 0x6D,
403 /// Place 15 byte item on stack.
404 PUSH15 = 0x6E,
405 /// Place 16 byte item on stack.
406 PUSH16 = 0x6F,
407 /// Place 17 byte item on stack.
408 PUSH17 = 0x70,
409 /// Place 18 byte item on stack.
410 PUSH18 = 0x71,
411 /// Place 19 byte item on stack.
412 PUSH19 = 0x72,
413 /// Place 20 byte item on stack.
414 PUSH20 = 0x73,
415 /// Place 21 byte item on stack.
416 PUSH21 = 0x74,
417 /// Place 22 byte item on stack.
418 PUSH22 = 0x75,
419 /// Place 23 byte item on stack.
420 PUSH23 = 0x76,
421 /// Place 24 byte item on stack.
422 PUSH24 = 0x77,
423 /// Place 25 byte item on stack.
424 PUSH25 = 0x78,
425 /// Place 26 byte item on stack.
426 PUSH26 = 0x79,
427 /// Place 27 byte item on stack.
428 PUSH27 = 0x7A,
429 /// Place 28 byte item on stack.
430 PUSH28 = 0x7B,
431 /// Place 29 byte item on stack.
432 PUSH29 = 0x7C,
433 /// Place 30 byte item on stack.
434 PUSH30 = 0x7D,
435 /// Place 31 byte item on stack.
436 PUSH31 = 0x7E,
437 /// Place 32 byte (full word) item on stack.
438 PUSH32 = 0x7F,
439 /// Duplicate 1st stack item.
440 DUP1 = 0x80,
441 /// Duplicate 2nd stack item.
442 DUP2 = 0x81,
443 /// Duplicate 3rd stack item.
444 DUP3 = 0x82,
445 /// Duplicate 4th stack item.
446 DUP4 = 0x83,
447 /// Duplicate 5th stack item.
448 DUP5 = 0x84,
449 /// Duplicate 6th stack item.
450 DUP6 = 0x85,
451 /// Duplicate 7th stack item.
452 DUP7 = 0x86,
453 /// Duplicate 8th stack item.
454 DUP8 = 0x87,
455 /// Duplicate 9th stack item.
456 DUP9 = 0x88,
457 /// Duplicate 10th stack item.
458 DUP10 = 0x89,
459 /// Duplicate 11th stack item.
460 DUP11 = 0x8A,
461 /// Duplicate 12th stack item.
462 DUP12 = 0x8B,
463 /// Duplicate 13th stack item.
464 DUP13 = 0x8C,
465 /// Duplicate 14th stack item.
466 DUP14 = 0x8D,
467 /// Duplicate 15th stack item.
468 DUP15 = 0x8E,
469 /// Duplicate 16th stack item.
470 DUP16 = 0x8F,
471 /// Exchange 1st and 2nd stack items.
472 SWAP1 = 0x90,
473 /// Exchange 1st and 3rd stack items.
474 SWAP2 = 0x91,
475 /// Exchange 1st and 4th stack items.
476 SWAP3 = 0x92,
477 /// Exchange 1st and 5th stack items.
478 SWAP4 = 0x93,
479 /// Exchange 1st and 6th stack items.
480 SWAP5 = 0x94,
481 /// Exchange 1st and 7th stack items.
482 SWAP6 = 0x95,
483 /// Exchange 1st and 8th stack items.
484 SWAP7 = 0x96,
485 /// Exchange 1st and 9th stack items.
486 SWAP8 = 0x97,
487 /// Exchange 1st and 10th stack items.
488 SWAP9 = 0x98,
489 /// Exchange 1st and 11th stack items.
490 SWAP10 = 0x99,
491 /// Exchange 1st and 12th stack items.
492 SWAP11 = 0x9A,
493 /// Exchange 1st and 13th stack items.
494 SWAP12 = 0x9B,
495 /// Exchange 1st and 14th stack items.
496 SWAP13 = 0x9C,
497 /// Exchange 1st and 15th stack items.
498 SWAP14 = 0x9D,
499 /// Exchange 1st and 16th stack items.
500 SWAP15 = 0x9E,
501 /// Exchange 1st and 17th stack items.
502 SWAP16 = 0x9F,
503 /// Append log record with no topics.
504 LOG0 = 0xA0,
505 /// Append log record with one topic.
506 LOG1 = 0xA1,
507 /// Append log record with two topics.
508 LOG2 = 0xA2,
509 /// Append log record with three topics.
510 LOG3 = 0xA3,
511 /// Append log record with four topics.
512 LOG4 = 0xA4,
513 /// Create a new account with associated code.
514 CREATE = 0xF0,
515 /// Message-call into an account.
516 CALL = 0xF1,
517 /// Message-call into this account with alternative account’s code.
518 CALLCODE = 0xF2,
519 /// Halt execution returning output data.
520 RETURN = 0xF3,
521 /// Message-call into this account with an alternative account’s code, but persisting the current values for sender and value.
522 DELEGATECALL = 0xF4,
523 /// Create a new account with associated code at a predictable address.
524 CREATE2 = 0xF5,
525 /// Static message-call into an account.
526 STATICCALL = 0xFA,
527 /// Halt execution reverting state changes but returning data and remaining gas.
528 REVERT = 0xFD,
529 /// Designated invalid instruction.
530 INVALID = 0xFE,
531 /// Halt execution and register account for later deletion or send all Ether to address (post-Cancun).
532 SELFDESTRUCT = 0xFF,
533}
534
535impl Mnemonic {
536 /// Returns a value signifying whether this mnemonic is of the type `PUSHx`.
537 ///
538 /// # Example
539 /// ```
540 /// # use eva_asm::opcode::Mnemonic;
541 /// assert_eq!(Mnemonic::PUSH7.is_push(), true);
542 /// assert_eq!(Mnemonic::GAS.is_push(), false);
543 /// ```
544 #[must_use]
545 #[inline]
546 pub const fn is_push(&self) -> bool {
547 matches!(
548 self,
549 Self::PUSH0
550 | Self::PUSH1
551 | Self::PUSH2
552 | Self::PUSH3
553 | Self::PUSH4
554 | Self::PUSH5
555 | Self::PUSH6
556 | Self::PUSH7
557 | Self::PUSH8
558 | Self::PUSH9
559 | Self::PUSH10
560 | Self::PUSH11
561 | Self::PUSH12
562 | Self::PUSH13
563 | Self::PUSH14
564 | Self::PUSH15
565 | Self::PUSH16
566 | Self::PUSH17
567 | Self::PUSH18
568 | Self::PUSH19
569 | Self::PUSH20
570 | Self::PUSH21
571 | Self::PUSH22
572 | Self::PUSH23
573 | Self::PUSH24
574 | Self::PUSH25
575 | Self::PUSH26
576 | Self::PUSH27
577 | Self::PUSH28
578 | Self::PUSH29
579 | Self::PUSH30
580 | Self::PUSH31
581 | Self::PUSH32
582 )
583 }
584
585 /// Returns a value signifying whether this mnemonic is of the type `DUPx`.
586 ///
587 /// # Example
588 /// ```
589 /// # use eva_asm::opcode::Mnemonic;
590 /// assert_eq!(Mnemonic::DUP2.is_dup(), true);
591 /// assert_eq!(Mnemonic::GAS.is_dup(), false);
592 /// ```
593 #[must_use]
594 #[inline]
595 pub const fn is_dup(&self) -> bool {
596 matches!(
597 self,
598 Self::DUP1
599 | Self::DUP2
600 | Self::DUP3
601 | Self::DUP4
602 | Self::DUP5
603 | Self::DUP6
604 | Self::DUP7
605 | Self::DUP8
606 | Self::DUP9
607 | Self::DUP10
608 | Self::DUP11
609 | Self::DUP12
610 | Self::DUP13
611 | Self::DUP14
612 | Self::DUP15
613 | Self::DUP16
614 )
615 }
616
617 /// Returns a value signifying whether this mnemonic is of the type `SWAPx`.
618 ///
619 /// # Example
620 /// ```
621 /// # use eva_asm::opcode::Mnemonic;
622 /// assert_eq!(Mnemonic::SWAP2.is_swap(), true);
623 /// assert_eq!(Mnemonic::GAS.is_swap(), false);
624 /// ```
625 #[must_use]
626 #[inline]
627 pub const fn is_swap(&self) -> bool {
628 matches!(
629 self,
630 Self::SWAP1
631 | Self::SWAP2
632 | Self::SWAP3
633 | Self::SWAP4
634 | Self::SWAP5
635 | Self::SWAP6
636 | Self::SWAP7
637 | Self::SWAP8
638 | Self::SWAP9
639 | Self::SWAP10
640 | Self::SWAP11
641 | Self::SWAP12
642 | Self::SWAP13
643 | Self::SWAP14
644 | Self::SWAP15
645 | Self::SWAP16
646 )
647 }
648
649 /// Returns a value signifying whether this mnemonic is of the type `LOGx`.
650 ///
651 /// # Example
652 /// ```
653 /// # use eva_asm::opcode::Mnemonic;
654 /// assert_eq!(Mnemonic::LOG2.is_log(), true);
655 /// assert_eq!(Mnemonic::GAS.is_log(), false);
656 /// ```
657 #[must_use]
658 #[inline]
659 pub const fn is_log(&self) -> bool {
660 matches!(
661 self,
662 Self::LOG0 | Self::LOG1 | Self::LOG2 | Self::LOG3 | Self::LOG4
663 )
664 }
665
666 /// Returns [`true`] for mnemonics that terminate execution of the smart contract.
667 /// # Example
668 /// ```
669 /// # use eva_asm::opcode::Mnemonic;
670 /// assert_eq!(Mnemonic::STOP.is_terminator(), true);
671 /// assert_eq!(Mnemonic::REVERT.is_terminator(), true);
672 /// assert_eq!(Mnemonic::INVALID.is_terminator(), true);
673 /// assert_eq!(Mnemonic::GAS.is_terminator(), false);
674 /// ```
675 #[must_use]
676 #[inline]
677 pub const fn is_terminator(&self) -> bool {
678 matches!(
679 self,
680 Self::STOP | Self::RETURN | Self::REVERT | Self::INVALID | Self::SELFDESTRUCT
681 )
682 }
683}
684
685impl PartialEq<OpCode> for Mnemonic {
686 #[inline]
687 fn eq(&self, other: &OpCode) -> bool {
688 *self as u8 == u8::from(other)
689 }
690}
691
692impl PartialOrd<OpCode> for Mnemonic {
693 #[inline]
694 fn partial_cmp(&self, other: &OpCode) -> Option<std::cmp::Ordering> {
695 (*self as u8).partial_cmp(&u8::from(other))
696 }
697}
698
699impl PartialEq<u8> for Mnemonic {
700 #[inline]
701 fn eq(&self, other: &u8) -> bool {
702 *self as u8 == *other
703 }
704}
705
706impl PartialOrd<u8> for Mnemonic {
707 #[inline]
708 fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
709 (*self as u8).partial_cmp(other)
710 }
711}
712
713/// Implement formatting for mnemonics.
714macro_rules! impl_mnemonic_fmt {
715 ($($fmt: ident),+) => {
716 $(impl std::fmt::$fmt for Mnemonic {
717 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718 std::fmt::$fmt::fmt(&(*self as u8), f)
719 }
720 })+
721 };
722}
723
724impl_mnemonic_fmt!(LowerHex, UpperHex, Binary, Octal);
725
726#[cfg(test)]
727mod tests {
728 use super::*;
729
730 #[test]
731 fn mnemonic_fmt() {
732 let gas = Mnemonic::GAS;
733 assert_eq!(format!("{gas}"), "GAS");
734 assert_eq!(format!("{gas:x}"), "5a");
735 assert_eq!(format!("{gas:X}"), "5A");
736 assert_eq!(format!("{gas:b}"), "1011010");
737 assert_eq!(format!("{gas:o}"), "132");
738 }
739
740 #[test]
741 fn mnemonic_cmp() {
742 let gas = Mnemonic::GAS;
743 let add = Mnemonic::ADD;
744
745 assert_eq!(gas, gas);
746 assert_ne!(gas, add);
747 assert!(gas > add);
748 assert_eq!(gas, OpCode::Known(Mnemonic::GAS));
749 assert_ne!(gas, OpCode::Known(Mnemonic::ADD));
750 assert!(gas > OpCode::Known(Mnemonic::ADD));
751 assert_eq!(gas, 0x5A);
752 assert!(gas > 0x1);
753 }
754
755 #[test]
756 fn opcode_fmt() {
757 let gas = OpCode::Known(Mnemonic::GAS);
758 assert_eq!(format!("{gas}"), "GAS");
759 assert_eq!(format!("{gas:x}"), "5a");
760 assert_eq!(format!("{gas:X}"), "5A");
761 assert_eq!(format!("{gas:b}"), "1011010");
762 assert_eq!(format!("{gas:o}"), "132");
763
764 let unknown = OpCode::from(0xF);
765 assert_eq!(format!("{unknown}"), "15");
766 assert_eq!(format!("{unknown:x}"), "f");
767 assert_eq!(format!("{unknown:X}"), "F");
768 assert_eq!(format!("{unknown:b}"), "1111");
769 assert_eq!(format!("{unknown:o}"), "17");
770 }
771
772 #[test]
773 fn opcode_conversions() {
774 let gas = OpCode::Known(Mnemonic::GAS);
775
776 assert_eq!(u8::from(gas), 0x5A);
777 assert_eq!(u8::from(&gas), 0x5A);
778 assert_eq!(gas.into_byte(), 0x5A);
779 assert_eq!(gas, OpCode::from(Mnemonic::GAS));
780 }
781
782 #[test]
783 fn opcode_cmp() {
784 let gas = OpCode::Known(Mnemonic::GAS);
785 let add = OpCode::Known(Mnemonic::ADD);
786
787 assert_eq!(gas, gas);
788 assert!(gas > add);
789 assert_ne!(gas, add);
790 assert_eq!(gas, Mnemonic::GAS);
791 assert!(gas > Mnemonic::ADD);
792 assert_eq!(gas, 0x5A);
793 assert!(gas > 0x1);
794 }
795}