#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::error::DecodeError;
use crate::BYTES_PER_LENGTH_OFFSET;
pub trait SszDecode: Sized {
fn is_fixed_size() -> bool;
fn fixed_size() -> usize;
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError>;
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
let item_size = Self::fixed_size();
bytes
.chunks_exact(item_size)
.map(Self::from_ssz_bytes)
.collect()
}
}
impl SszDecode for bool {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
1
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
if bytes.len() != 1 {
return Err(DecodeError::InvalidFixedLength {
expected: 1,
got: bytes.len(),
});
}
match bytes[0] {
0 => Ok(false),
1 => Ok(true),
b => Err(DecodeError::InvalidBooleanByte(b)),
}
}
}
fn decode_uint<const N: usize, T>(
bytes: &[u8],
from_le_bytes: impl FnOnce([u8; N]) -> T,
) -> Result<T, DecodeError> {
if bytes.len() != N {
return Err(DecodeError::InvalidFixedLength {
expected: N,
got: bytes.len(),
});
}
let mut arr = [0u8; N];
arr.copy_from_slice(bytes);
Ok(from_le_bytes(arr))
}
#[cfg(feature = "alloc")]
fn decode_fixed_vec_le<T>(bytes: &[u8], item_size: usize) -> Result<Vec<T>, DecodeError> {
#[cfg(target_endian = "little")]
{
if bytes.len() % item_size != 0 {
return Err(DecodeError::InvalidByteLength {
expected: item_size,
got: bytes.len(),
});
}
let count = bytes.len() / item_size;
let mut result = Vec::<T>::with_capacity(count);
unsafe {
core::ptr::copy_nonoverlapping(
bytes.as_ptr(),
result.as_mut_ptr() as *mut u8,
bytes.len(),
);
result.set_len(count);
}
Ok(result)
}
#[cfg(not(target_endian = "little"))]
{
let _ = bytes;
let _ = item_size;
unreachable!()
}
}
impl SszDecode for u8 {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
1
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_uint::<1, Self>(bytes, Self::from_le_bytes)
}
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
#[cfg(target_endian = "little")]
{
decode_fixed_vec_le(bytes, 1)
}
#[cfg(not(target_endian = "little"))]
{
bytes.chunks_exact(1).map(Self::from_ssz_bytes).collect()
}
}
}
impl SszDecode for u16 {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
2
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_uint::<2, Self>(bytes, Self::from_le_bytes)
}
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
#[cfg(target_endian = "little")]
{
decode_fixed_vec_le(bytes, 2)
}
#[cfg(not(target_endian = "little"))]
{
bytes.chunks_exact(2).map(Self::from_ssz_bytes).collect()
}
}
}
impl SszDecode for u32 {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
4
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_uint::<4, Self>(bytes, Self::from_le_bytes)
}
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
#[cfg(target_endian = "little")]
{
decode_fixed_vec_le(bytes, 4)
}
#[cfg(not(target_endian = "little"))]
{
bytes.chunks_exact(4).map(Self::from_ssz_bytes).collect()
}
}
}
impl SszDecode for u64 {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
8
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_uint::<8, Self>(bytes, Self::from_le_bytes)
}
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
#[cfg(target_endian = "little")]
{
decode_fixed_vec_le(bytes, 8)
}
#[cfg(not(target_endian = "little"))]
{
bytes.chunks_exact(8).map(Self::from_ssz_bytes).collect()
}
}
}
impl SszDecode for u128 {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
16
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_uint::<16, Self>(bytes, Self::from_le_bytes)
}
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
#[cfg(target_endian = "little")]
{
decode_fixed_vec_le(bytes, 16)
}
#[cfg(not(target_endian = "little"))]
{
bytes.chunks_exact(16).map(Self::from_ssz_bytes).collect()
}
}
}
fn decode_byte_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N], DecodeError> {
if bytes.len() != N {
return Err(DecodeError::InvalidFixedLength {
expected: N,
got: bytes.len(),
});
}
Ok(bytes
.try_into()
.expect("bug: length already validated above"))
}
#[cfg(feature = "alloc")]
fn decode_byte_array_vec<const N: usize>(bytes: &[u8]) -> Result<Vec<[u8; N]>, DecodeError> {
if !bytes.len().is_multiple_of(N) {
return Err(DecodeError::InvalidByteLength {
expected: N,
got: bytes.len(),
});
}
let count = bytes.len() / N;
let mut result = Vec::<[u8; N]>::with_capacity(count);
unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), result.as_mut_ptr() as *mut u8, bytes.len());
result.set_len(count);
}
Ok(result)
}
impl<const N: usize> SszDecode for [u8; N] {
#[inline(always)]
fn is_fixed_size() -> bool {
true
}
#[inline(always)]
fn fixed_size() -> usize {
N
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_byte_array::<N>(bytes)
}
#[cfg(feature = "alloc")]
fn ssz_decode_fixed_vec(bytes: &[u8]) -> Result<Vec<Self>, DecodeError> {
decode_byte_array_vec::<N>(bytes)
}
}
impl<T: SszDecode> SszDecode for Vec<T> {
#[inline(always)]
fn is_fixed_size() -> bool {
false
}
#[inline(always)]
fn fixed_size() -> usize {
0
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
if bytes.is_empty() {
return Ok(Vec::new());
}
if T::is_fixed_size() {
let item_size = T::fixed_size();
if !bytes.len().is_multiple_of(item_size) {
return Err(DecodeError::InvalidByteLength {
expected: item_size,
got: bytes.len(),
});
}
T::ssz_decode_fixed_vec(bytes)
} else {
decode_variable_length_items(bytes)
}
}
}
fn decode_variable_length_items<T: SszDecode>(bytes: &[u8]) -> Result<Vec<T>, DecodeError> {
if bytes.len() < BYTES_PER_LENGTH_OFFSET {
return Err(DecodeError::InvalidByteLength {
expected: BYTES_PER_LENGTH_OFFSET,
got: bytes.len(),
});
}
let first_offset = read_offset(bytes, 0)?;
if first_offset % BYTES_PER_LENGTH_OFFSET != 0 {
return Err(DecodeError::InvalidFirstOffset {
expected: 0, got: first_offset,
});
}
let num_items = first_offset / BYTES_PER_LENGTH_OFFSET;
if num_items == 0 {
return Err(DecodeError::InvalidFirstOffset {
expected: BYTES_PER_LENGTH_OFFSET,
got: 0,
});
}
let mut offsets = Vec::with_capacity(num_items + 1);
for i in 0..num_items {
let offset = read_offset(bytes, i * BYTES_PER_LENGTH_OFFSET)?;
if offsets.last().is_some_and(|&last| offset < last) {
return Err(DecodeError::OffsetsAreNotMonotonicallyIncreasing);
}
if offset > bytes.len() {
return Err(DecodeError::OffsetOutOfBounds {
offset,
length: bytes.len(),
});
}
offsets.push(offset);
}
offsets.push(bytes.len());
offsets
.windows(2)
.map(|pair| T::from_ssz_bytes(&bytes[pair[0]..pair[1]]))
.collect()
}
fn read_offset(bytes: &[u8], pos: usize) -> Result<usize, DecodeError> {
if pos + BYTES_PER_LENGTH_OFFSET > bytes.len() {
return Err(DecodeError::OffsetOutOfBounds {
offset: pos,
length: bytes.len(),
});
}
let mut buf = [0u8; 4];
buf.copy_from_slice(&bytes[pos..pos + 4]);
Ok(u32::from_le_bytes(buf) as usize)
}
pub struct ContainerDecoder<'a> {
bytes: &'a [u8],
fixed_part_len: usize,
cursor: usize,
offsets: Vec<usize>,
variable_index: usize,
}
impl<'a> ContainerDecoder<'a> {
pub fn new(bytes: &'a [u8], fixed_part_len: usize) -> Result<Self, DecodeError> {
if bytes.len() < fixed_part_len {
return Err(DecodeError::InvalidByteLength {
expected: fixed_part_len,
got: bytes.len(),
});
}
Ok(Self {
bytes,
fixed_part_len,
cursor: 0,
offsets: Vec::new(),
variable_index: 0,
})
}
pub fn decode_fixed<T: SszDecode>(&mut self) -> Result<T, DecodeError> {
let size = T::fixed_size();
let end = self.cursor + size;
if end > self.bytes.len() {
return Err(DecodeError::InvalidByteLength {
expected: end,
got: self.bytes.len(),
});
}
let result = T::from_ssz_bytes(&self.bytes[self.cursor..end])?;
self.cursor = end;
Ok(result)
}
pub fn read_variable_offset(&mut self) -> Result<(), DecodeError> {
let offset = read_offset(self.bytes, self.cursor)?;
if self.offsets.is_empty() {
if offset != self.fixed_part_len {
return Err(DecodeError::InvalidFirstOffset {
expected: self.fixed_part_len,
got: offset,
});
}
} else if offset
< *self
.offsets
.last()
.expect("bug: offsets verified non-empty")
{
return Err(DecodeError::OffsetsAreNotMonotonicallyIncreasing);
}
if offset > self.bytes.len() {
return Err(DecodeError::OffsetOutOfBounds {
offset,
length: self.bytes.len(),
});
}
self.offsets.push(offset);
self.cursor += BYTES_PER_LENGTH_OFFSET;
Ok(())
}
pub fn decode_variable<T: SszDecode>(&mut self) -> Result<T, DecodeError> {
let idx = self.variable_index;
if idx >= self.offsets.len() {
return Err(DecodeError::InvalidByteLength {
expected: idx + 1,
got: self.offsets.len(),
});
}
let start = self.offsets[idx];
let end = if idx + 1 < self.offsets.len() {
self.offsets[idx + 1]
} else {
self.bytes.len()
};
self.variable_index += 1;
T::from_ssz_bytes(&self.bytes[start..end])
}
pub fn finish_fixed(self) -> Result<(), DecodeError> {
if self.cursor != self.bytes.len() {
return Err(DecodeError::AdditionalBytes {
expected: self.cursor,
got: self.bytes.len(),
});
}
Ok(())
}
}