Skip to content

Commit 61a4ea6

Browse files
committed
perf(eof): avoid some allocations
1 parent 8700c8a commit 61a4ea6

File tree

3 files changed

+55
-54
lines changed

3 files changed

+55
-54
lines changed

crates/interpreter/src/interpreter/analysis.rs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
use revm_primitives::{eof::EofDecodeError, HashSet};
2-
31
use crate::{
42
instructions::utility::{read_i16, read_u16},
53
opcode,
64
primitives::{
75
bitvec::prelude::{bitvec, BitVec, Lsb0},
8-
eof::TypesSection,
6+
eof::{EofDecodeError, TypesSection},
97
legacy::JumpTable,
10-
Bytecode, Bytes, Eof, LegacyAnalyzedBytecode,
8+
Bytecode, Bytes, Eof, HashSet, LegacyAnalyzedBytecode,
119
},
1210
OPCODE_INFO_JUMPTABLE, STACK_LIMIT,
1311
};
14-
use std::{sync::Arc, vec, vec::Vec};
12+
use core::convert::identity;
13+
use std::{borrow::Cow, sync::Arc, vec, vec::Vec};
1514

1615
const EOF_NON_RETURNING_FUNCTION: u8 = 0x80;
1716

@@ -66,33 +65,36 @@ fn analyze(code: &[u8]) -> JumpTable {
6665
JumpTable(Arc::new(jumps))
6766
}
6867

69-
pub fn validate_raw_eof(bytecode: Bytes) -> Result<Eof, EofError> {
70-
let eof = Eof::decode(bytecode)?;
68+
/// Decodes `raw` into an [`Eof`] container and validates it.
69+
pub fn validate_raw_eof(raw: Bytes) -> Result<Eof, EofError> {
70+
let eof = Eof::decode(raw)?;
7171
validate_eof(&eof)?;
7272
Ok(eof)
7373
}
7474

75-
/// Validate Eof structures.
75+
/// Fully validates an [`Eof`] container.
7676
pub fn validate_eof(eof: &Eof) -> Result<(), EofError> {
77-
// clone is cheap as it is Bytes and a header.
78-
let mut queue = vec![eof.clone()];
77+
if eof.body.container_section.is_empty() {
78+
validate_eof_codes(eof)?;
79+
return Ok(());
80+
}
7981

80-
while let Some(eof) = queue.pop() {
81-
// iterate over types
82+
let mut stack = Vec::with_capacity(4);
83+
stack.push(Cow::Borrowed(eof));
84+
while let Some(eof) = stack.pop() {
85+
// Validate the current container.
8286
validate_eof_codes(&eof)?;
83-
// iterate over containers, convert them to Eof and add to analyze_eof
84-
for container in eof.body.container_section {
85-
queue.push(Eof::decode(container)?);
87+
// Decode subcontainers and push them to the stack.
88+
for container in &eof.body.container_section {
89+
stack.push(Cow::Owned(Eof::decode(container.clone())?));
8690
}
8791
}
8892

89-
// Eof is valid
9093
Ok(())
9194
}
9295

93-
/// Validate EOF
96+
/// Validates an [`Eof`] structure, without recursing into containers.
9497
pub fn validate_eof_codes(eof: &Eof) -> Result<(), EofValidationError> {
95-
let mut queued_codes = vec![false; eof.body.code_section.len()];
9698
if eof.body.code_section.len() != eof.body.types_section.len() {
9799
return Err(EofValidationError::InvalidTypesSection);
98100
}
@@ -101,8 +103,6 @@ pub fn validate_eof_codes(eof: &Eof) -> Result<(), EofValidationError> {
101103
// no code sections. This should be already checked in decode.
102104
return Err(EofValidationError::NoCodeSections);
103105
}
104-
// first section is default one.
105-
queued_codes[0] = true;
106106

107107
// the first code section must have a type signature
108108
// (0, 0x80, max_stack_height) (0 inputs non-returning function)
@@ -111,11 +111,16 @@ pub fn validate_eof_codes(eof: &Eof) -> Result<(), EofValidationError> {
111111
return Err(EofValidationError::InvalidTypesSection);
112112
}
113113

114+
// first section is default one.
115+
let mut code_sections_accessed = vec![false; eof.body.code_section.len()];
116+
code_sections_accessed[0] = true;
117+
114118
// start validation from code section 0.
115-
let mut queue = vec![0];
116-
while let Some(index) = queue.pop() {
119+
let mut stack = Vec::with_capacity(16);
120+
stack.push(0);
121+
while let Some(index) = stack.pop() {
117122
let code = &eof.body.code_section[index];
118-
let accessed_codes = validate_eof_code(
123+
let accessed = validate_eof_code(
119124
code,
120125
eof.header.data_size as usize,
121126
index,
@@ -124,15 +129,15 @@ pub fn validate_eof_codes(eof: &Eof) -> Result<(), EofValidationError> {
124129
)?;
125130

126131
// queue accessed codes.
127-
accessed_codes.into_iter().for_each(|i| {
128-
if !queued_codes[i] {
129-
queued_codes[i] = true;
130-
queue.push(i);
132+
accessed.into_iter().for_each(|i| {
133+
if !code_sections_accessed[i] {
134+
code_sections_accessed[i] = true;
135+
stack.push(i);
131136
}
132137
});
133138
}
134139
// iterate over accessed codes and check if all are accessed.
135-
if queued_codes.into_iter().any(|x| !x) {
140+
if !code_sections_accessed.into_iter().all(identity) {
136141
return Err(EofValidationError::CodeSectionNotAccessed);
137142
}
138143

crates/primitives/src/bytecode/eof.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,9 @@ pub const EOF_MAGIC: u16 = 0xEF00;
2121
/// EOF magic number in array form.
2222
pub static EOF_MAGIC_BYTES: Bytes = bytes!("ef00");
2323

24-
/// EOF - Ethereum Object Format.
24+
/// Ethereum Object Format (EOF) container.
2525
///
26-
/// It consist of a header, body and raw original bytes Specified in EIP.
27-
/// Most of body contain Bytes so it references to the raw bytes.
28-
///
29-
/// If there is a need to create new EOF from scratch, it is recommended to use `EofBody` and
30-
/// use `encode` function to create full [`Eof`] object.
26+
/// It consists of a header, body and the raw original bytes.
3127
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3228
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3329
pub struct Eof {
@@ -42,7 +38,7 @@ impl Default for Eof {
4238
// types section with zero inputs, zero outputs and zero max stack size.
4339
types_section: vec![TypesSection::default()],
4440
// One code section with a STOP byte.
45-
code_section: vec![[0x00].into()],
41+
code_section: vec![Bytes::from_static(&[0x00])],
4642
container_section: vec![],
4743
data_section: Bytes::new(),
4844
is_data_filled: true,
@@ -52,6 +48,11 @@ impl Default for Eof {
5248
}
5349

5450
impl Eof {
51+
/// Creates a new EOF container from the given body.
52+
pub fn new(body: EofBody) -> Self {
53+
body.into_eof()
54+
}
55+
5556
/// Returns len of the header and body in bytes.
5657
pub fn size(&self) -> usize {
5758
self.header.size() + self.header.body_size()
@@ -88,22 +89,15 @@ impl Eof {
8889

8990
/// Decode EOF that have additional dangling bytes.
9091
/// Assume that data section is fully filled.
91-
pub fn decode_dangling(mut eof: Bytes) -> Result<(Self, Bytes), EofDecodeError> {
92-
let (header, _) = EofHeader::decode(&eof)?;
92+
pub fn decode_dangling(mut raw: Bytes) -> Result<(Self, Bytes), EofDecodeError> {
93+
let (header, _) = EofHeader::decode(&raw)?;
9394
let eof_size = header.body_size() + header.size();
94-
if eof_size > eof.len() {
95+
if eof_size > raw.len() {
9596
return Err(EofDecodeError::MissingInput);
9697
}
97-
let dangling_data = eof.split_off(eof_size);
98-
let body = EofBody::decode(&eof, &header)?;
99-
Ok((
100-
Self {
101-
header,
102-
body,
103-
raw: eof,
104-
},
105-
dangling_data,
106-
))
98+
let dangling_data = raw.split_off(eof_size);
99+
let body = EofBody::decode(&raw, &header)?;
100+
Ok((Self { header, body, raw }, dangling_data))
107101
}
108102

109103
/// Decode EOF from raw bytes.

crates/primitives/src/bytecode/eof/body.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use super::{Eof, EofDecodeError, EofHeader, TypesSection};
22
use crate::Bytes;
33
use std::vec::Vec;
44

5-
/// EOF Body, contains types, code, container and data sections.
5+
/// EOF container body.
66
///
7-
/// Can be used to create new EOF object.
7+
/// Contains types, code, container and data sections.
8+
///
9+
/// Can be used to create a new EOF container using the [`into_eof`](EofBody::into_eof) method.
810
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
911
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1012
pub struct EofBody {
@@ -16,12 +18,12 @@ pub struct EofBody {
1618
}
1719

1820
impl EofBody {
19-
// Get code section
21+
/// Returns the code section at the given index.
2022
pub fn code(&self, index: usize) -> Option<&Bytes> {
2123
self.code_section.get(index)
2224
}
2325

24-
/// Create EOF from body.
26+
/// Creates an EOF container from this body.
2527
pub fn into_eof(self) -> Eof {
2628
// TODO add bounds checks.
2729
let header = EofHeader {
@@ -46,7 +48,7 @@ impl EofBody {
4648
}
4749
}
4850

49-
/// Encode Body into buffer.
51+
/// Encodes this body into the given buffer.
5052
pub fn encode(&self, buffer: &mut Vec<u8>) {
5153
for types_section in &self.types_section {
5254
types_section.encode(buffer);
@@ -63,7 +65,7 @@ impl EofBody {
6365
buffer.extend_from_slice(&self.data_section);
6466
}
6567

66-
/// Decode EOF body from buffer and Header.
68+
/// Decodes an EOF container body from the given buffer and header.
6769
pub fn decode(input: &Bytes, header: &EofHeader) -> Result<Self, EofDecodeError> {
6870
let header_len = header.size();
6971
let partial_body_len =

0 commit comments

Comments
 (0)