diff --git a/eth2/utils/ssz/tests/tests.rs b/eth2/utils/ssz/tests/tests.rs index 3850e871e..876cb4289 100644 --- a/eth2/utils/ssz/tests/tests.rs +++ b/eth2/utils/ssz/tests/tests.rs @@ -1,5 +1,5 @@ use ssz::{Decodable, Encodable}; -use ssz_derive::Encode; +use ssz_derive::{Decode, Encode}; fn round_trip(items: Vec) { for item in items { @@ -40,7 +40,7 @@ fn vec_of_vec_u16_round_trip() { round_trip(items); } -#[derive(Debug, PartialEq, Encode)] +#[derive(Debug, PartialEq, Encode, Decode)] struct FixedLen { a: u16, b: u64, @@ -72,7 +72,18 @@ fn fixed_len_struct_encoding() { } } -#[derive(Debug, PartialEq, Encode)] +#[test] +fn vec_of_fixed_len_struct() { + let items: Vec = vec![ + FixedLen { a: 0, b: 0, c: 0 }, + FixedLen { a: 1, b: 1, c: 1 }, + FixedLen { a: 1, b: 0, c: 1 }, + ]; + + round_trip(items); +} + +#[derive(Debug, PartialEq, Encode, Decode)] struct VariableLen { a: u16, b: Vec, @@ -119,15 +130,30 @@ fn variable_len_struct_encoding() { } } -/* #[test] -fn vec_of_fixed_len_struct() { - let items: Vec = vec![ - FixedLen { a: 0, b: 0, c: 0 }, - FixedLen { a: 1, b: 1, c: 1 }, - FixedLen { a: 1, b: 0, c: 1 }, +fn vec_of_variable_len_struct() { + let items: Vec = vec![ + VariableLen { + a: 0, + b: vec![], + c: 0, + }, + VariableLen { + a: 255, + b: vec![0, 1, 2, 3], + c: 99, + }, + VariableLen { + a: 255, + b: vec![0], + c: 99, + }, + VariableLen { + a: 50, + b: vec![0], + c: 0, + }, ]; round_trip(items); } -*/ diff --git a/eth2/utils/ssz_derive/src/lib.rs b/eth2/utils/ssz_derive/src/lib.rs index fa915ce16..56820256c 100644 --- a/eth2/utils/ssz_derive/src/lib.rs +++ b/eth2/utils/ssz_derive/src/lib.rs @@ -1,26 +1,11 @@ +#![recursion_limit="128"] + extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput}; -/* -/// Returns a Vec of `syn::Ident` for each named field in the struct. -/// -/// # Panics -/// Any unnamed struct field (like in a tuple struct) will raise a panic at compile time. -fn get_named_field_idents<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Ident> { - struct_data - .fields - .iter() - .map(|f| match &f.ident { - Some(ref ident) => ident, - _ => panic!("ssz_derive only supports named struct fields."), - }) - .collect() -} -*/ - /// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields /// that should not be serialized. /// @@ -45,12 +30,9 @@ fn get_serializable_named_field_idents<'a>( .collect() } -/// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields +/// Returns a Vec of `syn::Type` for each named field in the struct, whilst filtering out fields /// that should not be serialized. -/// -/// # Panics -/// Any unnamed struct field (like in a tuple struct) will raise a panic at compile time. -fn get_serializable_named_field_types<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Type> { +fn get_serializable_field_types<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Type> { struct_data .fields .iter() @@ -97,7 +79,7 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream { }; let field_idents = get_serializable_named_field_idents(&struct_data); - let field_types_a = get_serializable_named_field_types(&struct_data); + let field_types_a = get_serializable_field_types(&struct_data); let field_types_b = field_types_a.clone(); let output = quote! { @@ -110,7 +92,7 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream { } fn ssz_fixed_len() -> usize { - if Self::is_ssz_fixed_len() { + if ::is_ssz_fixed_len() { #( <#field_types_b as ssz::Encodable>::ssz_fixed_len() + )* @@ -134,7 +116,6 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream { output.into() } -/* /// Returns true if some field has an attribute declaring it should not be deserialized. /// /// The field attribute is: `#[ssz(skip_deserializing)]` @@ -161,21 +142,38 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream { _ => panic!("ssz_derive only supports structs."), }; - let all_idents = get_named_field_idents(&struct_data); + let mut register_types = vec![]; + let mut decodes = vec![]; + let mut is_fixed_lens = vec![]; + let mut fixed_lens = vec![]; // Build quotes for fields that should be deserialized and those that should be built from // `Default`. - let mut quotes = vec![]; for field in &struct_data.fields { match &field.ident { Some(ref ident) => { if should_skip_deserializing(field) { - quotes.push(quote! { - let #ident = <_>::default(); + // Field should not be deserialized; use a `Default` impl to instantiate. + decodes.push(quote! { + #ident: <_>::default(), }); } else { - quotes.push(quote! { - let (#ident, i) = <_>::ssz_decode(bytes, i)?; + let ty = &field.ty; + + register_types.push(quote! { + builder.register_type::<#ty>()?; + }); + + decodes.push(quote! { + #ident: decoder.decode_next()? + }); + + is_fixed_lens.push(quote! { + <#ty as ssz::Decodable>::is_ssz_fixed_len() + }); + + fixed_lens.push(quote! { + <#ty as ssz::Decodable>::ssz_fixed_len() }); } } @@ -185,22 +183,40 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream { let output = quote! { impl ssz::Decodable for #name { - fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), ssz::DecodeError> { + fn is_ssz_fixed_len() -> bool { #( - #quotes + #is_fixed_lens && + )* + true + } + + fn ssz_fixed_len() -> usize { + if ::is_ssz_fixed_len() { + #( + #fixed_lens + + )* + 0 + } else { + ssz::BYTES_PER_LENGTH_OFFSET + } + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + let mut builder = ssz::SszDecoderBuilder::new(bytes); + + #( + #register_types )* - Ok(( - Self { - #( - #all_idents, - )* - }, - i - )) + let mut decoder = builder.build()?; + + Ok(Self { + #( + #decodes, + )* + }) } } }; output.into() } -*/