use super::*;
use smallvec::{smallvec, SmallVec};
use std::cmp::Ordering;
type SmallVec8<T> = SmallVec<[T; 8]>;
pub mod impls;
#[derive(Debug, PartialEq, Clone)]
pub enum DecodeError {
InvalidByteLength { len: usize, expected: usize },
InvalidLengthPrefix { len: usize, expected: usize },
OutOfBoundsByte { i: usize },
OffsetIntoFixedPortion(usize),
OffsetSkipsVariableBytes(usize),
OffsetsAreDecreasing(usize),
OffsetOutOfBounds(usize),
InvalidListFixedBytesLen(usize),
ZeroLengthItem,
BytesInvalid(String),
UnionSelectorInvalid(u8),
}
pub fn sanitize_offset(
offset: usize,
previous_offset: Option<usize>,
num_bytes: usize,
num_fixed_bytes: Option<usize>,
) -> Result<usize, DecodeError> {
if num_fixed_bytes.map_or(false, |fixed_bytes| offset < fixed_bytes) {
Err(DecodeError::OffsetIntoFixedPortion(offset))
} else if previous_offset.is_none()
&& num_fixed_bytes.map_or(false, |fixed_bytes| offset != fixed_bytes)
{
Err(DecodeError::OffsetSkipsVariableBytes(offset))
} else if offset > num_bytes {
Err(DecodeError::OffsetOutOfBounds(offset))
} else if previous_offset.map_or(false, |prev| prev > offset) {
Err(DecodeError::OffsetsAreDecreasing(offset))
} else {
Ok(offset)
}
}
pub trait Decode: Sized {
fn is_ssz_fixed_len() -> bool;
fn ssz_fixed_len() -> usize {
BYTES_PER_LENGTH_OFFSET
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError>;
}
#[derive(Copy, Clone, Debug)]
pub struct Offset {
position: usize,
offset: usize,
}
pub struct SszDecoderBuilder<'a> {
bytes: &'a [u8],
items: SmallVec8<&'a [u8]>,
offsets: SmallVec8<Offset>,
items_index: usize,
}
impl<'a> SszDecoderBuilder<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self {
bytes,
items: smallvec![],
offsets: smallvec![],
items_index: 0,
}
}
pub fn register_anonymous_variable_length_item(&mut self) -> Result<(), DecodeError> {
struct Anonymous;
impl Decode for Anonymous {
fn is_ssz_fixed_len() -> bool {
false
}
fn from_ssz_bytes(_bytes: &[u8]) -> Result<Self, DecodeError> {
unreachable!("Anonymous should never be decoded")
}
}
self.register_type::<Anonymous>()
}
pub fn register_type<T: Decode>(&mut self) -> Result<(), DecodeError> {
self.register_type_parameterized(T::is_ssz_fixed_len(), T::ssz_fixed_len())
}
pub fn register_type_parameterized(
&mut self,
is_ssz_fixed_len: bool,
ssz_fixed_len: usize,
) -> Result<(), DecodeError> {
if is_ssz_fixed_len {
let start = self.items_index;
self.items_index += ssz_fixed_len;
let slice = self.bytes.get(start..self.items_index).ok_or_else(|| {
DecodeError::InvalidByteLength {
len: self.bytes.len(),
expected: self.items_index,
}
})?;
self.items.push(slice);
} else {
self.offsets.push(Offset {
position: self.items.len(),
offset: sanitize_offset(
read_offset(&self.bytes[self.items_index..])?,
self.offsets.last().map(|o| o.offset),
self.bytes.len(),
None,
)?,
});
self.items.push(&[]);
self.items_index += BYTES_PER_LENGTH_OFFSET;
}
Ok(())
}
fn finalize(&mut self) -> Result<(), DecodeError> {
if let Some(first_offset) = self.offsets.first().map(|o| o.offset) {
match first_offset.cmp(&self.items_index) {
Ordering::Less => return Err(DecodeError::OffsetIntoFixedPortion(first_offset)),
Ordering::Greater => {
return Err(DecodeError::OffsetSkipsVariableBytes(first_offset))
}
Ordering::Equal => (),
}
for pair in self.offsets.windows(2) {
let a = pair[0];
let b = pair[1];
self.items[a.position] = &self.bytes[a.offset..b.offset];
}
if let Some(last) = self.offsets.last() {
self.items[last.position] = &self.bytes[last.offset..]
}
} else {
if self.items_index != self.bytes.len() {
return Err(DecodeError::InvalidByteLength {
len: self.bytes.len(),
expected: self.items_index,
});
}
}
Ok(())
}
pub fn build(mut self) -> Result<SszDecoder<'a>, DecodeError> {
self.finalize()?;
Ok(SszDecoder { items: self.items })
}
}
pub struct SszDecoder<'a> {
items: SmallVec8<&'a [u8]>,
}
impl<'a> SszDecoder<'a> {
pub fn decode_next<T: Decode>(&mut self) -> Result<T, DecodeError> {
self.decode_next_with(|slice| T::from_ssz_bytes(slice))
}
pub fn decode_next_with<T, F>(&mut self, f: F) -> Result<T, DecodeError>
where
F: FnOnce(&'a [u8]) -> Result<T, DecodeError>,
{
f(self.items.remove(0))
}
}
pub fn split_union_bytes(bytes: &[u8]) -> Result<(UnionSelector, &[u8]), DecodeError> {
let selector = bytes
.first()
.copied()
.ok_or(DecodeError::OutOfBoundsByte { i: 0 })
.and_then(UnionSelector::new)?;
let body = bytes
.get(1..)
.ok_or(DecodeError::OutOfBoundsByte { i: 1 })?;
Ok((selector, body))
}
pub fn read_offset(bytes: &[u8]) -> Result<usize, DecodeError> {
decode_offset(bytes.get(0..BYTES_PER_LENGTH_OFFSET).ok_or_else(|| {
DecodeError::InvalidLengthPrefix {
len: bytes.len(),
expected: BYTES_PER_LENGTH_OFFSET,
}
})?)
}
fn decode_offset(bytes: &[u8]) -> Result<usize, DecodeError> {
let len = bytes.len();
let expected = BYTES_PER_LENGTH_OFFSET;
if len != expected {
Err(DecodeError::InvalidLengthPrefix { len, expected })
} else {
let mut array: [u8; BYTES_PER_LENGTH_OFFSET] = std::default::Default::default();
array.clone_from_slice(bytes);
Ok(u32::from_le_bytes(array) as usize)
}
}