Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/bytecode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ serde = { workspace = true, features = ["derive", "rc"], optional = true }
paste = { workspace = true, optional = true }
phf = { workspace = true, features = ["macros"], optional = true }

[dev-dependencies]
serde_json = { workspace = true }

[features]
default = ["std", "parse"]
std = [
"serde?/std",
"serde_json/std",
"primitives/std",
"bitvec/std",
"phf?/std",
Expand Down
130 changes: 89 additions & 41 deletions crates/bytecode/src/legacy/jump_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ use core::{
cmp::Ordering,
hash::{Hash, Hasher},
};
use primitives::{hex, OnceLock};
use primitives::{hex, Bytes, OnceLock};
use std::{fmt::Debug, sync::Arc};

/// A table of valid `jump` destinations.
///
/// It is immutable, cheap to clone and memory efficient, with one bit per byte in the bytecode.
#[derive(Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct JumpTable {
/// Pointer into `table` to avoid `Arc` overhead on lookup.
/// Cached pointer to table data to avoid Arc overhead on lookup
#[cfg_attr(feature = "serde", serde(skip))]
table_ptr: *const u8,
/// Number of bits in the table.
len: usize,
/// Actual bit vec
table: Arc<BitVec<u8>>,
table: Arc<Bytes>,
}

// SAFETY: BitVec data is immutable through Arc, pointer won't be invalidated
Expand Down Expand Up @@ -47,31 +49,10 @@ impl Ord for JumpTable {
}
}

#[cfg(feature = "serde")]
impl serde::Serialize for JumpTable {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.table.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for JumpTable {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let bitvec = BitVec::deserialize(deserializer)?;
Ok(Self::new(bitvec))
}
}

impl Debug for JumpTable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("JumpTable")
.field("map", &hex::encode(self.table.as_raw_slice()))
.field("map", &hex::encode(self.table.as_ref()))
.finish()
}
}
Expand All @@ -84,24 +65,38 @@ impl Default for JumpTable {
}
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for JumpTable {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct JumpTableSerde {
len: usize,
table: Arc<Bytes>,
}

let data = JumpTableSerde::deserialize(deserializer)?;
Ok(Self::from_bytes_arc(data.table, data.len))
}
}

impl JumpTable {
/// Create new JumpTable directly from an existing BitVec.
///
/// Uses [`Self::from_bytes`] internally.
#[inline]
pub fn new(jumps: BitVec<u8>) -> Self {
let table = Arc::new(jumps);
let table_ptr = table.as_raw_slice().as_ptr();
let len = table.len();

Self {
table,
table_ptr,
len,
}
let bit_len = jumps.len();
let bytes = jumps.into_vec().into();
Self::from_bytes(bytes, bit_len)
}

/// Gets the raw bytes of the jump map.
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.table.as_raw_slice()
&self.table
}

/// Gets the length of the jump map.
Expand All @@ -120,21 +115,48 @@ impl JumpTable {
///
/// Bit length represents number of used bits inside slice.
///
/// Uses [`Self::from_bytes`] internally.
///
/// # Panics
///
/// Panics if number of bits in slice is less than bit_len.
#[inline]
pub fn from_slice(slice: &[u8], bit_len: usize) -> Self {
Self::from_bytes(Bytes::from(slice.to_vec()), bit_len)
}

/// Create new JumpTable directly from an existing Bytes.
///
/// Bit length represents number of used bits inside slice.
///
/// Panics if bytes length is less than bit_len * 8.
#[inline]
pub fn from_bytes(bytes: Bytes, bit_len: usize) -> Self {
Self::from_bytes_arc(Arc::new(bytes), bit_len)
}

/// Create new JumpTable directly from an existing Bytes.
///
/// Bit length represents number of used bits inside slice.
///
/// Panics if bytes length is less than bit_len * 8.
#[inline]
pub fn from_bytes_arc(table: Arc<Bytes>, bit_len: usize) -> Self {
const BYTE_LEN: usize = 8;
assert!(
slice.len() * BYTE_LEN >= bit_len,
table.len() * BYTE_LEN >= bit_len,
"slice bit length {} is less than bit_len {}",
slice.len() * BYTE_LEN,
table.len() * BYTE_LEN,
bit_len
);
let mut bitvec = BitVec::from_slice(slice);
unsafe { bitvec.set_len(bit_len) };
Self::new(bitvec)

let table_ptr = table.as_ptr();

Self {
table_ptr,
table,
len: bit_len,
}
}

/// Checks if `pc` is a valid jump destination.
Expand Down Expand Up @@ -183,12 +205,38 @@ mod tests {
assert!(!jump_table.is_valid(11));
assert!(!jump_table.is_valid(12));
}

#[test]
#[cfg(feature = "serde")]
fn test_serde_roundtrip() {
let original = JumpTable::from_slice(&[0x0D, 0x06], 13);

// Serialize to JSON
let serialized = serde_json::to_string(&original).expect("Failed to serialize");

// Deserialize from JSON
let deserialized: JumpTable =
serde_json::from_str(&serialized).expect("Failed to deserialize");

// Check that the deserialized table matches the original
assert_eq!(original.len, deserialized.len);
assert_eq!(original.table, deserialized.table);

// Verify functionality is preserved
for i in 0..13 {
assert_eq!(
original.is_valid(i),
deserialized.is_valid(i),
"Mismatch at index {i}"
);
}
}
}

#[cfg(test)]
mod bench_is_valid {
use super::*;
use std::time::Instant;
use std::{sync::Arc, time::Instant};

const ITERATIONS: usize = 1_000_000;
const TEST_SIZE: usize = 10_000;
Expand Down
9 changes: 2 additions & 7 deletions crates/ee-tests/tests/op_revm_testdata/test_deposit_tx.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand Down
20 changes: 4 additions & 16 deletions crates/ee-tests/tests/op_revm_testdata/test_halted_deposit_tx.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand All @@ -38,15 +33,8 @@
"LegacyAnalyzed": {
"bytecode": "0x5000",
"jump_table": {
"bits": 1,
"data": [
0
],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 1,
"table": "0x00"
},
"original_len": 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand All @@ -40,13 +35,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand All @@ -65,13 +55,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand All @@ -90,13 +75,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand All @@ -115,13 +95,8 @@
"LegacyAnalyzed": {
"bytecode": "0x00",
"jump_table": {
"bits": 0,
"data": [],
"head": {
"index": 0,
"width": 8
},
"order": "bitvec::order::Lsb0"
"len": 0,
"table": "0x"
},
"original_len": 0
}
Expand Down
Loading
Loading