use super::*; pub mod impls; #[derive(Debug, PartialEq)] pub enum DecodeError { /// The bytes supplied were too short to be decoded into the specified type. InvalidByteLength { len: usize, expected: usize }, /// The given bytes were too short to be read as a length prefix. InvalidLengthPrefix { len: usize, expected: usize }, /// A length offset pointed to a byte that was out-of-bounds (OOB). /// /// A bytes may be OOB for the following reasons: /// /// - It is `>= bytes.len()`. /// - When decoding variable length items, the 1st offset points "backwards" into the fixed /// length items (i.e., `length[0] < BYTES_PER_LENGTH_OFFSET`). /// - When decoding variable-length items, the `n`'th offset was less than the `n-1`'th offset. OutOfBoundsByte { i: usize }, /// The given bytes were invalid for some application-level reason. BytesInvalid(String), } pub trait Decodable: Sized { fn is_ssz_fixed_len() -> bool; /// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes. /// /// By default, this is set to `BYTES_PER_LENGTH_OFFSET` which is suitable for variable length /// objects, but not fixed-length objects. Fixed-length objects _must_ return a value which /// represents their length. fn ssz_fixed_len() -> usize { BYTES_PER_LENGTH_OFFSET } fn from_ssz_bytes(bytes: &[u8]) -> Result; } #[derive(Copy, Clone)] pub struct Offset { position: usize, offset: usize, } pub struct SszDecoderBuilder<'a> { bytes: &'a [u8], items: Vec<&'a [u8]>, offsets: Vec, items_index: usize, } impl<'a> SszDecoderBuilder<'a> { pub fn new(bytes: &'a [u8]) -> Self { Self { bytes, items: vec![], offsets: vec![], items_index: 0, } } pub fn register_type(&mut self) -> Result<(), DecodeError> { if T::is_ssz_fixed_len() { let start = self.items_index; self.items_index += T::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 { let offset = read_offset(&self.bytes[self.items_index..])?; let previous_offset = self .offsets .last() .and_then(|o| Some(o.offset)) .unwrap_or_else(|| BYTES_PER_LENGTH_OFFSET); if previous_offset > offset { return Err(DecodeError::OutOfBoundsByte { i: offset }); } else if offset > self.bytes.len() { return Err(DecodeError::OutOfBoundsByte { i: offset }); } self.offsets.push(Offset { position: self.items.len(), offset, }); self.items_index += BYTES_PER_LENGTH_OFFSET; } Ok(()) } fn apply_offsets(&mut self) -> Result<(), DecodeError> { if !self.offsets.is_empty() { let mut insertions = 0; let mut running_offset = self.offsets[0].offset; for i in 1..=self.offsets.len() { let (slice_option, position) = if i == self.offsets.len() { (self.bytes.get(running_offset..), self.offsets.len()) } else { let offset = self.offsets[i]; let start = running_offset; running_offset = offset.offset; (self.bytes.get(start..running_offset), offset.position) }; let slice = slice_option .ok_or_else(|| DecodeError::OutOfBoundsByte { i: running_offset })?; self.items.insert(position + insertions, slice); insertions += 1; } } Ok(()) } pub fn build(mut self) -> Result, DecodeError> { self.apply_offsets()?; Ok(SszDecoder { items: self.items }) } } pub struct SszDecoder<'a> { items: Vec<&'a [u8]>, } impl<'a> SszDecoder<'a> { /// Decodes the next item. /// /// # Panics /// /// Panics when attempting to decode more items than actually exist. pub fn decode_next(&mut self) -> Result { T::from_ssz_bytes(self.items.remove(0)) } } /// Reads a `BYTES_PER_LENGTH_OFFSET`-byte length from `bytes`, where `bytes.len() >= /// BYTES_PER_LENGTH_OFFSET`. fn read_offset(bytes: &[u8]) -> Result { decode_offset(bytes.get(0..BYTES_PER_LENGTH_OFFSET).ok_or_else(|| { DecodeError::InvalidLengthPrefix { len: bytes.len(), expected: BYTES_PER_LENGTH_OFFSET, } })?) } /// Decode bytes as a little-endian usize, returning an `Err` if `bytes.len() != /// BYTES_PER_LENGTH_OFFSET`. fn decode_offset(bytes: &[u8]) -> Result { 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) } }