Add docs to SSZ
This commit is contained in:
parent
16a8cdb714
commit
94ab2479fe
@ -2,6 +2,7 @@ use super::*;
|
||||
|
||||
pub mod impls;
|
||||
|
||||
/// Returned when SSZ decoding fails.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DecodeError {
|
||||
/// The bytes supplied were too short to be decoded into the specified type.
|
||||
@ -21,7 +22,14 @@ pub enum DecodeError {
|
||||
BytesInvalid(String),
|
||||
}
|
||||
|
||||
/// Provides SSZ decoding (de-serialization) via the `from_ssz_bytes(&bytes)` method.
|
||||
///
|
||||
/// See `examples/` for manual implementations or the crate root for implementations using
|
||||
/// `#[derive(Decode)]`.
|
||||
pub trait Decodable: Sized {
|
||||
/// Returns `true` if this object has a fixed-length.
|
||||
///
|
||||
/// I.e., there are no variable length items in this object or any of it's contained objects.
|
||||
fn is_ssz_fixed_len() -> bool;
|
||||
|
||||
/// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
|
||||
@ -33,6 +41,10 @@ pub trait Decodable: Sized {
|
||||
BYTES_PER_LENGTH_OFFSET
|
||||
}
|
||||
|
||||
/// Attempts to decode `Self` from `bytes`, returning a `DecodeError` on failure.
|
||||
///
|
||||
/// The supplied bytes must be the exact length required to decode `Self`, excess bytes will
|
||||
/// result in an error.
|
||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError>;
|
||||
}
|
||||
|
||||
@ -42,6 +54,12 @@ pub struct Offset {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
/// Builds an `SszDecoder`.
|
||||
///
|
||||
/// The purpose of this struct is to split some SSZ bytes into individual slices. The builder is
|
||||
/// then converted into a `SszDecoder` which decodes those values into object instances.
|
||||
///
|
||||
/// See [`SszDecoder`](struct.SszDecoder.html) for usage examples.
|
||||
pub struct SszDecoderBuilder<'a> {
|
||||
bytes: &'a [u8],
|
||||
items: Vec<&'a [u8]>,
|
||||
@ -50,6 +68,8 @@ pub struct SszDecoderBuilder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> SszDecoderBuilder<'a> {
|
||||
/// Instantiate a new builder that should build a `SszDecoder` over the given `bytes` which
|
||||
/// are assumed to be the SSZ encoding of some object.
|
||||
pub fn new(bytes: &'a [u8]) -> Self {
|
||||
Self {
|
||||
bytes,
|
||||
@ -59,6 +79,7 @@ impl<'a> SszDecoderBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares that some type `T` is the next item in `bytes`.
|
||||
pub fn register_type<T: Decodable>(&mut self) -> Result<(), DecodeError> {
|
||||
if T::is_ssz_fixed_len() {
|
||||
let start = self.items_index;
|
||||
@ -137,6 +158,7 @@ impl<'a> SszDecoderBuilder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Finalizes the builder, returning a `SszDecoder` that may be used to instantiate objects.
|
||||
pub fn build(mut self) -> Result<SszDecoder<'a>, DecodeError> {
|
||||
self.finalize()?;
|
||||
|
||||
@ -144,6 +166,45 @@ impl<'a> SszDecoderBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes some slices of SSZ into object instances. Should be instantiated using
|
||||
/// [`SszDecoderBuilder`](struct.SszDecoderBuilder.html).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use ssz_derive::{Encode, Decode};
|
||||
/// use ssz::{Decodable, Encodable, SszDecoder, SszDecoderBuilder};
|
||||
///
|
||||
/// #[derive(PartialEq, Debug, Encode, Decode)]
|
||||
/// struct Foo {
|
||||
/// a: u64,
|
||||
/// b: Vec<u16>,
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let foo = Foo {
|
||||
/// a: 42,
|
||||
/// b: vec![1, 3, 3, 7]
|
||||
/// };
|
||||
///
|
||||
/// let bytes = foo.as_ssz_bytes();
|
||||
///
|
||||
/// let mut builder = SszDecoderBuilder::new(&bytes);
|
||||
///
|
||||
/// builder.register_type::<u64>().unwrap();
|
||||
/// builder.register_type::<Vec<u16>>().unwrap();
|
||||
///
|
||||
/// let mut decoder = builder.build().unwrap();
|
||||
///
|
||||
/// let decoded_foo = Foo {
|
||||
/// a: decoder.decode_next().unwrap(),
|
||||
/// b: decoder.decode_next().unwrap(),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(foo, decoded_foo);
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub struct SszDecoder<'a> {
|
||||
items: Vec<&'a [u8]>,
|
||||
}
|
||||
|
@ -2,9 +2,20 @@ use super::*;
|
||||
|
||||
mod impls;
|
||||
|
||||
/// Provides SSZ encoding (serialization) via the `as_ssz_bytes(&self)` method.
|
||||
///
|
||||
/// See `examples/` for manual implementations or the crate root for implementations using
|
||||
/// `#[derive(Encode)]`.
|
||||
pub trait Encodable {
|
||||
/// Returns `true` if this object has a fixed-length.
|
||||
///
|
||||
/// I.e., there are no variable length items in this object or any of it's contained objects.
|
||||
fn is_ssz_fixed_len() -> bool;
|
||||
|
||||
/// Append the encoding `self` to `buf`.
|
||||
///
|
||||
/// Note, variable length objects need only to append their "variable length" portion, they do
|
||||
/// not need to provide their offset.
|
||||
fn ssz_append(&self, buf: &mut Vec<u8>);
|
||||
|
||||
/// The number of bytes this object occupies in the fixed-length portion of the SSZ bytes.
|
||||
@ -16,6 +27,9 @@ pub trait Encodable {
|
||||
BYTES_PER_LENGTH_OFFSET
|
||||
}
|
||||
|
||||
/// Returns the full-form encoding of this object.
|
||||
///
|
||||
/// The default implementation of this method should suffice for most cases.
|
||||
fn as_ssz_bytes(&self) -> Vec<u8> {
|
||||
let mut buf = vec![];
|
||||
|
||||
@ -29,6 +43,41 @@ pub trait Encodable {
|
||||
///
|
||||
/// **You must call `finalize(..)` after the final `append(..)` call** to ensure the bytes are
|
||||
/// written to `buf`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// Use `SszEncoder` to produce identical output to `foo.as_ssz_bytes()`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use ssz_derive::{Encode, Decode};
|
||||
/// use ssz::{Decodable, Encodable, SszEncoder};
|
||||
///
|
||||
/// #[derive(PartialEq, Debug, Encode, Decode)]
|
||||
/// struct Foo {
|
||||
/// a: u64,
|
||||
/// b: Vec<u16>,
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let foo = Foo {
|
||||
/// a: 42,
|
||||
/// b: vec![1, 3, 3, 7]
|
||||
/// };
|
||||
///
|
||||
/// let mut buf: Vec<u8> = vec![];
|
||||
/// let offset = <u64 as Encodable>::ssz_fixed_len() + <Vec<u16> as Encodable>::ssz_fixed_len();
|
||||
///
|
||||
/// let mut encoder = SszEncoder::container(&mut buf, offset);
|
||||
///
|
||||
/// encoder.append(&foo.a);
|
||||
/// encoder.append(&foo.b);
|
||||
///
|
||||
/// encoder.finalize();
|
||||
///
|
||||
/// assert_eq!(foo.as_ssz_bytes(), buf);
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub struct SszEncoder<'a> {
|
||||
offset: usize,
|
||||
buf: &'a mut Vec<u8>,
|
||||
@ -36,10 +85,14 @@ pub struct SszEncoder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> SszEncoder<'a> {
|
||||
/// Instantiate a new encoder for encoding a SSZ list.
|
||||
///
|
||||
/// Identical to `Self::container`.
|
||||
pub fn list(buf: &'a mut Vec<u8>, num_fixed_bytes: usize) -> Self {
|
||||
Self::container(buf, num_fixed_bytes)
|
||||
}
|
||||
|
||||
/// Instantiate a new encoder for encoding a SSZ container.
|
||||
pub fn container(buf: &'a mut Vec<u8>, num_fixed_bytes: usize) -> Self {
|
||||
buf.reserve(num_fixed_bytes);
|
||||
|
||||
@ -50,6 +103,7 @@ impl<'a> SszEncoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Append some `item` to the SSZ bytes.
|
||||
pub fn append<T: Encodable>(&mut self, item: &T) {
|
||||
if T::is_ssz_fixed_len() {
|
||||
item.ssz_append(&mut self.buf);
|
||||
@ -61,6 +115,10 @@ impl<'a> SszEncoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the variable bytes to `self.bytes`.
|
||||
///
|
||||
/// This method must be called after the final `append(..)` call when serializing
|
||||
/// variable-length items.
|
||||
pub fn finalize(&mut self) -> &mut Vec<u8> {
|
||||
self.buf.append(&mut self.variable_bytes);
|
||||
|
||||
|
@ -1,13 +1,52 @@
|
||||
//! Provides encoding (serialization) and decoding (deserialization) in the SimpleSerialize (SSZ)
|
||||
//! format designed for use in Ethereum 2.0.
|
||||
//!
|
||||
//! Conforms to
|
||||
//! [v0.6.1](https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/simple-serialize.md) of the
|
||||
//! Ethereum 2.0 specification.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use ssz_derive::{Encode, Decode};
|
||||
//! use ssz::{Decodable, Encodable};
|
||||
//!
|
||||
//! #[derive(PartialEq, Debug, Encode, Decode)]
|
||||
//! struct Foo {
|
||||
//! a: u64,
|
||||
//! b: Vec<u16>,
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let foo = Foo {
|
||||
//! a: 42,
|
||||
//! b: vec![1, 3, 3, 7]
|
||||
//! };
|
||||
//!
|
||||
//! let ssz_bytes: Vec<u8> = foo.as_ssz_bytes();
|
||||
//!
|
||||
//! let decoded_foo = Foo::from_ssz_bytes(&ssz_bytes).unwrap();
|
||||
//!
|
||||
//! assert_eq!(foo, decoded_foo);
|
||||
//! }
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! See `examples/` for manual implementations of the `Encodable` and `Decodable` traits.
|
||||
|
||||
mod decode;
|
||||
mod encode;
|
||||
mod macros;
|
||||
|
||||
pub use decode::{
|
||||
impls::decode_list_of_variable_length_items, Decodable, DecodeError, SszDecoderBuilder,
|
||||
impls::decode_list_of_variable_length_items, Decodable, DecodeError, SszDecoder,
|
||||
SszDecoderBuilder,
|
||||
};
|
||||
pub use encode::{Encodable, SszEncoder};
|
||||
|
||||
/// The number of bytes used to represent an offset.
|
||||
pub const BYTES_PER_LENGTH_OFFSET: usize = 4;
|
||||
/// The maximum value that can be represented using `BYTES_PER_LENGTH_OFFSET`.
|
||||
pub const MAX_LENGTH_VALUE: usize = (1 << (BYTES_PER_LENGTH_OFFSET * 8)) - 1;
|
||||
|
||||
/// Convenience function to SSZ encode an object supporting ssz::Encode.
|
||||
|
@ -1,3 +1,8 @@
|
||||
/// Implements `Encodable` for `$impl_type` using an implementation of `From<$impl_type> for
|
||||
/// $from_type`.
|
||||
///
|
||||
/// In effect, this allows for easy implementation of `Encodable` for some type that implements a
|
||||
/// `From` conversion into another type that already has `Encodable` implemented.
|
||||
#[macro_export]
|
||||
macro_rules! impl_encode_via_from {
|
||||
($impl_type: ty, $from_type: ty) => {
|
||||
@ -19,6 +24,11 @@ macro_rules! impl_encode_via_from {
|
||||
};
|
||||
}
|
||||
|
||||
/// Implements `Decodable` for `$impl_type` using an implementation of `From<$impl_type> for
|
||||
/// $from_type`.
|
||||
///
|
||||
/// In effect, this allows for easy implementation of `Decodable` for some type that implements a
|
||||
/// `From` conversion into another type that already has `Decodable` implemented.
|
||||
#[macro_export]
|
||||
macro_rules! impl_decode_via_from {
|
||||
($impl_type: ty, $from_type: tt) => {
|
||||
|
Loading…
Reference in New Issue
Block a user