Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/refactor(rlp): improve implementations #182

Merged
merged 3 commits into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions crates/primitives/src/bits/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ macro_rules! wrap_fixed_bytes {

$crate::impl_fixed_bytes_traits!($name, $n);
$crate::impl_getrandom!($name);
$crate::impl_rlp!($name);
$crate::impl_rlp!($name, $n);
$crate::impl_serde!($name);
$crate::impl_arbitrary!($name, $n);

Expand Down Expand Up @@ -377,10 +377,10 @@ macro_rules! impl_getrandom {
#[macro_export]
#[cfg(feature = "rlp")]
macro_rules! impl_rlp {
($t:ty) => {
($t:ty, $n:literal) => {
impl $crate::private::alloy_rlp::Decodable for $t {
#[inline]
fn decode(buf: &mut &[u8]) -> Result<Self, $crate::private::alloy_rlp::DecodeError> {
fn decode(buf: &mut &[u8]) -> $crate::private::alloy_rlp::Result<Self> {
$crate::private::alloy_rlp::Decodable::decode(buf).map(Self)
}
}
Expand All @@ -396,14 +396,18 @@ macro_rules! impl_rlp {
$crate::private::alloy_rlp::Encodable::encode(&self.0, out)
}
}

$crate::private::alloy_rlp::impl_max_encoded_len!($t, {
$n + $crate::private::alloy_rlp::length_of_length($n)
});
};
}

#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "rlp"))]
macro_rules! impl_rlp {
($t:ty) => {};
($t:ty, $n:literal) => {};
}

#[doc(hidden)]
Expand Down
10 changes: 7 additions & 3 deletions crates/primitives/src/bits/rlp.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use super::FixedBytes;
use alloy_rlp::{impl_max_encoded_len, length_of_length, Decodable, Encodable};
use alloy_rlp::{length_of_length, Decodable, Encodable, MaxEncodedLen, MaxEncodedLenAssoc};

impl<const N: usize> Decodable for FixedBytes<N> {
#[inline]
fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::DecodeError> {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Decodable::decode(buf).map(Self)
}
}
Expand All @@ -24,8 +24,12 @@ impl<const N: usize> Encodable for FixedBytes<N> {
// https://github.com/rust-lang/rust/issues/76560
macro_rules! fixed_bytes_max_encoded_len {
($($sz:literal),+) => {$(
impl_max_encoded_len!(FixedBytes<$sz>, $sz + length_of_length($sz));
unsafe impl MaxEncodedLen<{ $sz + length_of_length($sz) }> for FixedBytes<$sz> {}
)+};
}

fixed_bytes_max_encoded_len!(0, 1, 2, 4, 8, 16, 20, 32, 64, 128, 256, 512, 1024);

unsafe impl<const N: usize> MaxEncodedLenAssoc for FixedBytes<N> {
const LEN: usize = N + length_of_length(N);
}
7 changes: 5 additions & 2 deletions crates/primitives/src/bytes/rlp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ use super::Bytes;
use alloy_rlp::{Decodable, Encodable};

impl Encodable for Bytes {
#[inline]
fn length(&self) -> usize {
self.0.length()
}

#[inline]
fn encode(&self, out: &mut dyn bytes::BufMut) {
self.0.encode(out)
}
}

impl Decodable for Bytes {
fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::DecodeError> {
Ok(Self(bytes::Bytes::decode(buf)?))
#[inline]
fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
bytes::Bytes::decode(buf).map(Self)
}
}
120 changes: 58 additions & 62 deletions crates/rlp-derive/src/de.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::utils::{attributes_include, field_ident, is_optional, parse_struct, EMPTY_STRING_CODE};
use crate::utils::{
attributes_include, field_ident, is_optional, make_generics, parse_struct, EMPTY_STRING_CODE,
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Error, Result};
Expand All @@ -11,92 +13,86 @@ pub(crate) fn impl_decodable(ast: &syn::DeriveInput) -> Result<TokenStream> {
let supports_trailing_opt = attributes_include(&ast.attrs, "trailing");

let mut encountered_opt_item = false;
let mut stmts = Vec::with_capacity(body.fields.len());
let mut decode_stmts = Vec::with_capacity(body.fields.len());
for (i, field) in fields {
let is_opt = is_optional(field);
if is_opt {
if !supports_trailing_opt {
return Err(Error::new_spanned(field, "Optional fields are disabled. Add `#[rlp(trailing)]` attribute to the struct in order to enable"));
let msg = "optional fields are disabled.\nAdd the `#[rlp(trailing)]` attribute to the struct in order to enable optional fields";
return Err(Error::new_spanned(field, msg))
}
encountered_opt_item = true;
} else if encountered_opt_item && !attributes_include(&field.attrs, "default") {
return Err(Error::new_spanned(
field,
"All subsequent fields must be either optional or default.",
))
let msg =
"all the fields after the first optional field must be either optional or default";
return Err(Error::new_spanned(field, msg))
}

stmts.push(decodable_field(i, field, is_opt));
decode_stmts.push(decodable_field(i, field, is_opt));
}

let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let impl_block = quote! {
impl #impl_generics alloy_rlp::Decodable for #name #ty_generics #where_clause {
fn decode(mut buf: &mut &[u8]) -> Result<Self, alloy_rlp::DecodeError> {
let b = &mut &**buf;
let rlp_head = alloy_rlp::Header::decode(b)?;

if !rlp_head.list {
return Err(alloy_rlp::DecodeError::UnexpectedString);
}
if alloy_rlp::Buf::remaining(b) < rlp_head.payload_length {
return Err(alloy_rlp::DecodeError::InputTooShort);
}

let started_len = b.len();
let this = Self {
#(#stmts)*
};

let consumed = started_len - b.len();
if consumed != rlp_head.payload_length {
return Err(alloy_rlp::DecodeError::ListLengthMismatch {
expected: rlp_head.payload_length,
got: consumed,
});
}

*buf = *b;

Ok(this)
}
}
};
let generics = make_generics(&ast.generics, quote!(alloy_rlp::Decodable));
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

Ok(quote! {
const _: () = {
extern crate alloy_rlp;
#impl_block

impl #impl_generics alloy_rlp::Decodable for #name #ty_generics #where_clause {
#[inline]
fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
let alloy_rlp::Header { list, payload_length } = alloy_rlp::Header::decode(b)?;
if !list {
return Err(alloy_rlp::Error::UnexpectedString);
}

let started_len = b.len();
if started_len < payload_length {
return Err(alloy_rlp::DecodeError::InputTooShort);
}

let this = Self {
#(#decode_stmts)*
};

let consumed = started_len - b.len();
if consumed != payload_length {
return Err(alloy_rlp::Error::ListLengthMismatch {
expected: payload_length,
got: consumed,
});
}

Ok(this)
}
}
};
})
}

pub(crate) fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> Result<TokenStream> {
let body = parse_struct(ast, "RlpEncodableWrapper")?;

assert_eq!(
body.fields.iter().count(),
1,
"#[derive(RlpEncodableWrapper)] is only defined for structs with one field."
);
if body.fields.iter().count() != 1 {
let msg = "`RlpEncodableWrapper` is only defined for structs with one field.";
return Err(Error::new(ast.ident.span(), msg))
}

let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let impl_block = quote! {
impl #impl_generics alloy_rlp::Decodable for #name #ty_generics #where_clause {
fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::DecodeError> {
Ok(Self(alloy_rlp::Decodable::decode(buf)?))
}
}
};
let generics = make_generics(&ast.generics, quote!(alloy_rlp::Decodable));
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

Ok(quote! {
const _: () = {
extern crate alloy_rlp;
#impl_block

impl #impl_generics alloy_rlp::Decodable for #name #ty_generics #where_clause {
#[inline]
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
alloy_rlp::private::Result::map(alloy_rlp::Decodable::decode(buf), Self)
}
}
};
})
}
Expand All @@ -105,12 +101,12 @@ fn decodable_field(index: usize, field: &syn::Field, is_opt: bool) -> TokenStrea
let ident = field_ident(index, field);

if attributes_include(&field.attrs, "default") {
quote! { #ident: Default::default(), }
quote! { #ident: alloy_rlp::private::Default::default(), }
} else if is_opt {
quote! {
#ident: if started_len - b.len() < rlp_head.payload_length {
if b.first().map(|b| *b == #EMPTY_STRING_CODE).unwrap_or_default() {
bytes::Buf::advance(b, 1);
#ident: if started_len - b.len() < payload_length {
if alloy_rlp::private::Option::map_or(b.first(), false, |b| *b == #EMPTY_STRING_CODE) {
alloy_rlp::Buf::advance(b, 1);
None
} else {
Some(alloy_rlp::Decodable::decode(b)?)
Expand Down
Loading