Add ssz examples, decoder struct
This commit is contained in:
parent
7865f0de89
commit
0c0edb0653
58
eth2/utils/ssz2/examples/struct_definition.rs
Normal file
58
eth2/utils/ssz2/examples/struct_definition.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use ssz2::{Decodable, DecodeError, Encodable, SszDecoderBuilder, SszStream};
|
||||||
|
|
||||||
|
pub struct Foo {
|
||||||
|
a: u16,
|
||||||
|
b: Vec<u8>,
|
||||||
|
c: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for Foo {
|
||||||
|
fn is_ssz_fixed_len() -> bool {
|
||||||
|
<u16 as Encodable>::is_ssz_fixed_len() && <Vec<u16> as Encodable>::is_ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut stream = SszStream::new();
|
||||||
|
|
||||||
|
stream.append(&self.a);
|
||||||
|
stream.append(&self.b);
|
||||||
|
stream.append(&self.c);
|
||||||
|
|
||||||
|
stream.drain()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for Foo {
|
||||||
|
fn is_ssz_fixed_len() -> bool {
|
||||||
|
<u16 as Decodable>::is_ssz_fixed_len() && <Vec<u16> as Decodable>::is_ssz_fixed_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||||
|
let mut builder = SszDecoderBuilder::new(bytes);
|
||||||
|
|
||||||
|
builder.register_type::<u16>()?;
|
||||||
|
builder.register_type::<Vec<u8>>()?;
|
||||||
|
builder.register_type::<u16>()?;
|
||||||
|
|
||||||
|
let mut decoder = builder.build()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
a: decoder.decode_next()?,
|
||||||
|
b: decoder.decode_next()?,
|
||||||
|
c: decoder.decode_next()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo {
|
||||||
|
a: 42,
|
||||||
|
b: vec![0, 1, 2, 3],
|
||||||
|
c: 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
foo.as_ssz_bytes(),
|
||||||
|
vec![42, 0, 8, 0, 0, 0, 11, 0, 0, 1, 2, 3]
|
||||||
|
);
|
||||||
|
}
|
@ -36,6 +36,144 @@ pub trait Decodable: Sized {
|
|||||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError>;
|
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 });
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
/// Decode the given bytes for the given type
|
/// Decode the given bytes for the given type
|
||||||
|
@ -28,6 +28,7 @@ macro_rules! impl_decodable_for_uint {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_decodable_for_uint!(u8, 8);
|
||||||
impl_decodable_for_uint!(u16, 16);
|
impl_decodable_for_uint!(u16, 16);
|
||||||
impl_decodable_for_uint!(u32, 32);
|
impl_decodable_for_uint!(u32, 32);
|
||||||
impl_decodable_for_uint!(u64, 64);
|
impl_decodable_for_uint!(u64, 64);
|
||||||
@ -94,33 +95,6 @@ impl<T: Decodable> Decodable for Vec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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`.
|
|
||||||
pub 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
use super::decode::decode_ssz_list;
|
use super::decode::decode_ssz_list;
|
||||||
use super::ethereum_types::{Address, H256};
|
use super::ethereum_types::{Address, H256};
|
||||||
|
@ -17,19 +17,26 @@ pub trait Encodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct VariableLengths {
|
||||||
|
pub fixed_bytes_position: usize,
|
||||||
|
pub variable_bytes_length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// Provides a buffer for appending SSZ values.
|
/// Provides a buffer for appending SSZ values.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SszStream {
|
pub struct SszStream {
|
||||||
fixed: Vec<u8>,
|
fixed_bytes: Vec<u8>,
|
||||||
variable: Vec<u8>,
|
variable_bytes: Vec<u8>,
|
||||||
|
variable_lengths: Vec<VariableLengths>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SszStream {
|
impl SszStream {
|
||||||
/// Create a new, empty stream for writing SSZ values.
|
/// Create a new, empty stream for writing SSZ values.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
SszStream {
|
SszStream {
|
||||||
fixed: vec![],
|
fixed_bytes: vec![],
|
||||||
variable: vec![],
|
variable_bytes: vec![],
|
||||||
|
variable_lengths: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,18 +45,42 @@ impl SszStream {
|
|||||||
let mut bytes = item.as_ssz_bytes();
|
let mut bytes = item.as_ssz_bytes();
|
||||||
|
|
||||||
if T::is_ssz_fixed_len() {
|
if T::is_ssz_fixed_len() {
|
||||||
self.fixed.append(&mut bytes);
|
self.fixed_bytes.append(&mut bytes);
|
||||||
} else {
|
} else {
|
||||||
self.fixed.append(&mut encode_length(bytes.len()));
|
self.variable_lengths.push(VariableLengths {
|
||||||
self.variable.append(&mut bytes);
|
fixed_bytes_position: self.fixed_bytes.len(),
|
||||||
|
variable_bytes_length: bytes.len(),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.fixed_bytes
|
||||||
|
.append(&mut vec![0; BYTES_PER_LENGTH_OFFSET]);
|
||||||
|
self.variable_bytes.append(&mut bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the offsets (if any) in the fixed-length bytes to correctly point to the values in
|
||||||
|
/// the variable length part.
|
||||||
|
pub fn apply_offsets(&mut self) {
|
||||||
|
let mut running_offset = self.fixed_bytes.len();
|
||||||
|
|
||||||
|
for v in &self.variable_lengths {
|
||||||
|
let offset = running_offset;
|
||||||
|
running_offset += v.variable_bytes_length;
|
||||||
|
|
||||||
|
self.fixed_bytes.splice(
|
||||||
|
v.fixed_bytes_position..v.fixed_bytes_position + BYTES_PER_LENGTH_OFFSET,
|
||||||
|
encode_length(offset),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append the variable-length bytes to the fixed-length bytes and return the result.
|
/// Append the variable-length bytes to the fixed-length bytes and return the result.
|
||||||
pub fn drain(mut self) -> Vec<u8> {
|
pub fn drain(mut self) -> Vec<u8> {
|
||||||
self.fixed.append(&mut self.variable);
|
self.apply_offsets();
|
||||||
|
|
||||||
self.fixed
|
self.fixed_bytes.append(&mut self.variable_bytes);
|
||||||
|
|
||||||
|
self.fixed_bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ pub mod decode;
|
|||||||
mod decode;
|
mod decode;
|
||||||
mod encode;
|
mod encode;
|
||||||
|
|
||||||
pub use decode::{Decodable, DecodeError};
|
pub use decode::{Decodable, DecodeError, SszDecoderBuilder};
|
||||||
pub use encode::{Encodable, SszStream};
|
pub use encode::{Encodable, SszStream};
|
||||||
|
|
||||||
pub const BYTES_PER_LENGTH_OFFSET: usize = 4;
|
pub const BYTES_PER_LENGTH_OFFSET: usize = 4;
|
||||||
|
Loading…
Reference in New Issue
Block a user