lighthouse/eth2/utils/ssz/src/decode.rs

176 lines
5.4 KiB
Rust
Raw Normal View History

use super::*;
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<Self, DecodeError>;
}
#[derive(Copy, Clone)]
pub struct Offset {
position: usize,
offset: usize,
}
pub struct SszDecoderBuilder<'a> {
bytes: &'a [u8],
items: Vec<&'a [u8]>,
offsets: Vec<Offset>,
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<T: Decodable>(&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 });
2019-05-05 02:47:57 +00:00
} 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<SszDecoder<'a>, 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<T: Decodable>(&mut self) -> Result<T, DecodeError> {
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<usize, DecodeError> {
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<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)
}
}