use core::fmt;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "rlp", derive(alloy_rlp::RlpEncodableWrapper, alloy_rlp::RlpDecodableWrapper))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[repr(transparent)]
pub struct BlockAccessIndex(pub u64);
impl BlockAccessIndex {
pub const PRE_EXECUTION: Self = Self(0);
#[inline]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[inline]
pub const fn get(self) -> u64 {
self.0
}
#[inline]
pub const fn increment(&mut self) {
self.0 += 1;
}
#[inline]
pub const fn phase(self, tx_len: usize) -> Option<BlockAccessPhase> {
let tx_len_u64 = tx_len as u64;
if self.0 == 0 {
Some(BlockAccessPhase::PreExecution)
} else if self.0 <= tx_len_u64 {
Some(BlockAccessPhase::Transaction((self.0 - 1) as usize))
} else if self.0 == tx_len_u64 + 1 {
Some(BlockAccessPhase::PostExecution)
} else {
None
}
}
}
impl fmt::Display for BlockAccessIndex {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl fmt::LowerHex for BlockAccessIndex {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for BlockAccessIndex {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
alloy_primitives::U64::from(self.0).serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for BlockAccessIndex {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
alloy_primitives::U64::deserialize(deserializer).map(|value| Self(value.to()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BlockAccessPhase {
PreExecution,
Transaction(usize),
PostExecution,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pre_execution_is_index_zero() {
assert_eq!(BlockAccessIndex::new(0).phase(0), Some(BlockAccessPhase::PreExecution));
assert_eq!(BlockAccessIndex::new(0).phase(5), Some(BlockAccessPhase::PreExecution));
assert_eq!(BlockAccessIndex::PRE_EXECUTION.phase(5), Some(BlockAccessPhase::PreExecution));
}
#[test]
fn transaction_indices_are_one_based() {
assert_eq!(BlockAccessIndex::new(1).phase(3), Some(BlockAccessPhase::Transaction(0)));
assert_eq!(BlockAccessIndex::new(2).phase(3), Some(BlockAccessPhase::Transaction(1)));
assert_eq!(BlockAccessIndex::new(3).phase(3), Some(BlockAccessPhase::Transaction(2)));
}
#[test]
fn post_execution_is_tx_len_plus_one() {
assert_eq!(BlockAccessIndex::new(4).phase(3), Some(BlockAccessPhase::PostExecution));
assert_eq!(BlockAccessIndex::new(1).phase(0), Some(BlockAccessPhase::PostExecution));
}
#[test]
fn out_of_range_returns_none() {
assert_eq!(BlockAccessIndex::new(5).phase(3), None);
assert_eq!(BlockAccessIndex::new(u64::MAX).phase(3), None);
}
#[test]
fn empty_block_has_only_pre_and_post() {
assert_eq!(BlockAccessIndex::new(0).phase(0), Some(BlockAccessPhase::PreExecution));
assert_eq!(BlockAccessIndex::new(1).phase(0), Some(BlockAccessPhase::PostExecution));
assert_eq!(BlockAccessIndex::new(2).phase(0), None);
}
#[test]
fn increment_bumps_by_one() {
let mut idx = BlockAccessIndex::new(3);
idx.increment();
assert_eq!(idx, BlockAccessIndex::new(4));
}
#[test]
fn new_and_get_roundtrip() {
let idx = BlockAccessIndex::new(42);
assert_eq!(idx.get(), 42);
}
#[test]
fn display_matches_inner() {
extern crate alloc;
assert_eq!(alloc::format!("{}", BlockAccessIndex::new(7)), "7");
assert_eq!(alloc::format!("{:x}", BlockAccessIndex::new(255)), "ff");
}
#[cfg(feature = "serde")]
#[test]
fn serde_hex_quantity_roundtrip() {
let idx = BlockAccessIndex::new(26);
let json = serde_json::to_string(&idx).unwrap();
assert_eq!(json, "\"0x1a\"");
let back: BlockAccessIndex = serde_json::from_str(&json).unwrap();
assert_eq!(back, idx);
}
#[cfg(feature = "rlp")]
#[test]
fn rlp_matches_raw_u64() {
use alloy_rlp::Decodable;
let idx = BlockAccessIndex::new(300);
let encoded = alloy_rlp::encode(idx);
assert_eq!(encoded, alloy_rlp::encode(300u64));
let decoded = BlockAccessIndex::decode(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, idx);
}
}