From 6cb8c5e84f499eadfba08c90cdb839794771e642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Mon, 20 Nov 2023 08:52:32 +0100 Subject: [PATCH] tezos-encoding + derive: keep track of lifetime in NomReader 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. --- CHANGELOG.md | 3 ++- tezos-encoding-derive/src/nom.rs | 14 +++++++++++--- tezos-encoding/src/lib.rs | 2 +- tezos-encoding/src/nom.rs | 10 +++++----- tezos-encoding/src/types.rs | 4 ++-- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed9d6a7057..fba2c4bdd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/tezos-encoding-derive/src/nom.rs b/tezos-encoding-derive/src/nom.rs index b89ddf4e83..1548d1e468 100644 --- a/tezos-encoding-derive/src/nom.rs +++ b/tezos-encoding-derive/src/nom.rs @@ -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; @@ -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 { + 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) } } diff --git a/tezos-encoding/src/lib.rs b/tezos-encoding/src/lib.rs index e432dbe2aa..7132fb84f7 100644 --- a/tezos-encoding/src/lib.rs +++ b/tezos-encoding/src/lib.rs @@ -29,7 +29,7 @@ //! //! #[derive(Debug, PartialEq, HasEncoding, NomReader, BinWriter)] //! struct Outer -//! where T: Debug + PartialEq + HasEncoding + NomReader + BinWriter { +//! where T: Debug + PartialEq + HasEncoding + for<'a> NomReader<'a> + BinWriter { //! #[encoding(dynamic)] //! dynamic_size: Vec //! } diff --git a/tezos-encoding/src/nom.rs b/tezos-encoding/src/nom.rs index 5b43f6dd4d..fe1d260694 100644 --- a/tezos-encoding/src/nom.rs +++ b/tezos-encoding/src/nom.rs @@ -226,13 +226,13 @@ pub type NomError<'a> = error::DecodeError>; pub type NomResult<'a, T> = nom::IResult, T, NomError<'a>>; /// Traits defining message decoding using `nom` primitives. -pub trait NomReader: Sized { - fn nom_read(input: &[u8]) -> NomResult; +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 { map(take(Self::hash_size()), |bytes| { @@ -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 { 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 { map(n_bignum, |big_uint| { BigInt::from_biguint(Sign::Plus, big_uint).into() diff --git a/tezos-encoding/src/types.rs b/tezos-encoding/src/types.rs index bcc2ebff70..b85797d61b 100644 --- a/tezos-encoding/src/types.rs +++ b/tezos-encoding/src/types.rs @@ -292,7 +292,7 @@ impl<'de, const SIZE: usize> Deserialize<'de> for SizedBytes { } } -impl NomReader for SizedBytes { +impl<'a, const SIZE: usize> NomReader<'a> for SizedBytes { fn nom_read(input: &[u8]) -> crate::nom::NomResult { use crate::nom; let (input, slice) = nom::sized(SIZE, nom::bytes)(input)?; @@ -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 { use crate::nom::bytes; let (input, b) = bytes(input)?;