Skip to content

Commit

Permalink
tezos-encoding + derive: keep track of lifetime in NomReader
Browse files Browse the repository at this point in the history
The `NomReader` trait is implicitly parameterized by the lifetime of
the input byte slice. This commit makes the quantification on this
explicit. This is needed to implement `NomReader` for structures which
keep a reference to some slice of the input, for example to implement
lazy deserialization.
  • Loading branch information
Raphaël Cauderlier committed Dec 8, 2023
1 parent 6675c1c commit 6cb8c5e
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 12 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Changed

- Nothing.
- `tezos_data_encoding`: The `NomReader` trait is now explicitly
parameterized by the lifetime of the input byte slice.

### Deprecated

Expand Down
14 changes: 11 additions & 3 deletions tezos-encoding-derive/src/nom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use once_cell::sync::Lazy as SyncLazy;
use crate::encoding::*;
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::parse_quote;
use syn::spanned::Spanned;

const NOM_TUPLE_MAX: usize = 26;
Expand All @@ -18,14 +19,21 @@ pub fn generate_nom_read_for_data(
) -> TokenStream {
let name = data.name;
let nom_read = generate_nom_read(&data.encoding);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// We want to derive NomReader<'a> for a fresh 'a. To do this we
// use a mix of the solutions proposed in
// https://github.com/dtolnay/syn/issues/90
let a: syn::GenericParam = parse_quote!('_a);
let mut extended_generics = generics.clone();
extended_generics.params.push(a.clone());
let (impl_generics, _, _) = extended_generics.split_for_impl();
let (_, ty_generics, where_clause) = generics.split_for_impl();
quote_spanned! {
data.name.span()=>
#[allow(unused_parens)]
#[allow(clippy::unnecessary_cast)]
#[allow(clippy::redundant_closure_call)]
impl #impl_generics tezos_data_encoding::nom::NomReader for #name #ty_generics #where_clause {
fn nom_read(bytes: &[u8]) -> tezos_data_encoding::nom::NomResult<Self> {
impl #impl_generics tezos_data_encoding::nom::NomReader<#a> for #name #ty_generics #where_clause {
fn nom_read(bytes: &#a [u8]) -> tezos_data_encoding::nom::NomResult<#a, Self> {
#nom_read(bytes)
}
}
Expand Down
2 changes: 1 addition & 1 deletion tezos-encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
//!
//! #[derive(Debug, PartialEq, HasEncoding, NomReader, BinWriter)]
//! struct Outer<T>
//! where T: Debug + PartialEq + HasEncoding + NomReader + BinWriter {
//! where T: Debug + PartialEq + HasEncoding + for<'a> NomReader<'a> + BinWriter {
//! #[encoding(dynamic)]
//! dynamic_size: Vec<T>
//! }
Expand Down
10 changes: 5 additions & 5 deletions tezos-encoding/src/nom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,13 @@ pub type NomError<'a> = error::DecodeError<NomInput<'a>>;
pub type NomResult<'a, T> = nom::IResult<NomInput<'a>, T, NomError<'a>>;

/// Traits defining message decoding using `nom` primitives.
pub trait NomReader: Sized {
fn nom_read(input: &[u8]) -> NomResult<Self>;
pub trait NomReader<'a>: Sized {
fn nom_read(input: &'a [u8]) -> NomResult<'a, Self>;
}

macro_rules! hash_nom_reader {
($hash_name:ident) => {
impl NomReader for crypto::hash::$hash_name {
impl<'a> NomReader<'a> for crypto::hash::$hash_name {
#[inline(always)]
fn nom_read(input: &[u8]) -> NomResult<Self> {
map(take(Self::hash_size()), |bytes| {
Expand Down Expand Up @@ -270,13 +270,13 @@ hash_nom_reader!(BlsSignature);
hash_nom_reader!(NonceHash);
hash_nom_reader!(SmartRollupHash);

impl NomReader for Zarith {
impl<'a> NomReader<'a> for Zarith {
fn nom_read(bytes: &[u8]) -> NomResult<Self> {
map(z_bignum, |big_int| big_int.into())(bytes)
}
}

impl NomReader for Mutez {
impl<'a> NomReader<'a> for Mutez {
fn nom_read(bytes: &[u8]) -> NomResult<Self> {
map(n_bignum, |big_uint| {
BigInt::from_biguint(Sign::Plus, big_uint).into()
Expand Down
4 changes: 2 additions & 2 deletions tezos-encoding/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ impl<'de, const SIZE: usize> Deserialize<'de> for SizedBytes<SIZE> {
}
}

impl<const SIZE: usize> NomReader for SizedBytes<SIZE> {
impl<'a, const SIZE: usize> NomReader<'a> for SizedBytes<SIZE> {
fn nom_read(input: &[u8]) -> crate::nom::NomResult<Self> {
use crate::nom;
let (input, slice) = nom::sized(SIZE, nom::bytes)(input)?;
Expand Down Expand Up @@ -387,7 +387,7 @@ impl HasEncoding for Bytes {
}
}

impl NomReader for Bytes {
impl<'a> NomReader<'a> for Bytes {
fn nom_read(input: &[u8]) -> crate::nom::NomResult<Self> {
use crate::nom::bytes;
let (input, b) = bytes(input)?;
Expand Down

0 comments on commit 6cb8c5e

Please sign in to comment.