From 5dd96fb173f5b2b5e31ebbd3a714175767daebe1 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Tue, 22 Aug 2023 17:23:36 +0200 Subject: [PATCH] add prefix / suffix to field --- androscalpel_serializer/src/constant.rs | 3 + androscalpel_serializer/src/core/mod.rs | 27 +++- androscalpel_serializer/src/lib.rs | 2 + androscalpel_serializer_derive/src/lib.rs | 166 +++++++++++++++++++--- 4 files changed, 176 insertions(+), 22 deletions(-) create mode 100644 androscalpel_serializer/src/constant.rs diff --git a/androscalpel_serializer/src/constant.rs b/androscalpel_serializer/src/constant.rs new file mode 100644 index 0000000..23f4b3e --- /dev/null +++ b/androscalpel_serializer/src/constant.rs @@ -0,0 +1,3 @@ +//! Constants definition + +pub use androscalpel_serializer_derive::*; diff --git a/androscalpel_serializer/src/core/mod.rs b/androscalpel_serializer/src/core/mod.rs index 7db9892..89e9b3c 100644 --- a/androscalpel_serializer/src/core/mod.rs +++ b/androscalpel_serializer/src/core/mod.rs @@ -5,7 +5,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom, Write}; pub use androscalpel_serializer_derive::*; -mod leb; +pub mod leb; pub use leb::*; #[derive(Debug, PartialEq, Eq)] @@ -849,4 +849,29 @@ mod test { ); assert!(TestEnum::deserialize_from_slice(&[255u8]).is_err()); } + + #[derive(Serializable, PartialEq, Debug)] + struct TestPrefixSuffix { + #[prefix([0x42])] + #[suffix([0x66, 0x66])] + a: u32, + } + + #[test] + fn deserialize_prefix_suffix() { + assert_eq!( + TestPrefixSuffix::deserialize_from_slice(&[0x42, 0, 1, 2, 3, 0x66, 0x66]).unwrap(), + TestPrefixSuffix { a: 0x00010203 } + ); + } + + #[test] + fn serialize_prefix_suffix() { + assert_eq!( + TestPrefixSuffix { a: 0x00010203 } + .serialize_to_vec() + .unwrap(), + vec![0x42, 0, 1, 2, 3, 0x66, 0x66] + ); + } } diff --git a/androscalpel_serializer/src/lib.rs b/androscalpel_serializer/src/lib.rs index ea2646e..5ea5c63 100644 --- a/androscalpel_serializer/src/lib.rs +++ b/androscalpel_serializer/src/lib.rs @@ -1,3 +1,5 @@ +pub mod constant; pub mod core; pub use crate::core::*; +pub use constant::*; diff --git a/androscalpel_serializer_derive/src/lib.rs b/androscalpel_serializer_derive/src/lib.rs index b2ef2da..e41a2cb 100644 --- a/androscalpel_serializer_derive/src/lib.rs +++ b/androscalpel_serializer_derive/src/lib.rs @@ -42,7 +42,29 @@ use syn::{ /// ); /// ``` /// -#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type))] +/// ## Prefix and suffix +/// +/// To define a constant prefix before a serialised field or suffixe after, use `#[prefix()]` +/// and `#[suffix()]`. The prefix/suffix must be `u8`s `Iterator`. +/// +/// ``` +/// pub use androscalpel_serializer::*; +/// +/// #[derive(Serializable, PartialEq, Debug)] +/// struct Example { +/// #[prefix([0x42])] +/// #[suffix([0x66, 0x66])] +/// a: u32, +/// } +/// +/// assert_eq!( +/// Example::deserialize_from_slice(&[0x42, 0, 1, 2, 3, 0x66, 0x66]).unwrap(), +/// Example { a: 0x00010203 } +/// ); +/// ``` +/// +/// +#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix))] pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident; @@ -83,9 +105,9 @@ impl Parse for UntilParams { } } -/// Parsed Parameters for the `#[prefix(val)]` attribute. -struct PrefixParams(TokenStream); -impl Parse for PrefixParams { +/// Parsed Parameters for the `#[prefix(val)]` attribute of an enum variant. +struct PrefixParamsVariant(TokenStream); +impl Parse for PrefixParamsVariant { fn parse(input: ParseStream) -> syn::Result { let value = input.parse()?; Ok(Self(value)) @@ -107,16 +129,36 @@ struct ParamsStruct { pub prefix_type: Option, } +/// Parsed Parameters for the `#[prefix(val)]` attribute of an struct field. +struct PrefixParamsField(TokenStream); +impl Parse for PrefixParamsField { + fn parse(input: ParseStream) -> syn::Result { + let value = input.parse()?; + Ok(Self(value)) + } +} + +/// Parsed Parameters for the `#[suffix(val)]` attribute of an struct field. +struct SuffixParamsField(TokenStream); +impl Parse for SuffixParamsField { + fn parse(input: ParseStream) -> syn::Result { + let value = input.parse()?; + Ok(Self(value)) + } +} + /// All the attributes parameters for a field #[derive(Default)] struct ParamsField { pub until: Option, + pub prefix: Option, + pub suffix: Option, } /// All the attributes parameters for a variant #[derive(Default)] struct ParamsVariant { - pub prefix: Option, + pub prefix: Option, } impl ParamsStruct { @@ -147,8 +189,11 @@ impl ParamsField { Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => { params.until = Some(syn::parse2(tokens.clone()).unwrap()) } - Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => { - panic!("Fields cannot take the attribut 'prefix'") + Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => { + params.prefix = Some(syn::parse2(tokens.clone()).unwrap()) + } + Meta::List(MetaList { path, tokens, .. }) if path.is_ident("suffix") => { + params.suffix = Some(syn::parse2(tokens.clone()).unwrap()) } Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => { panic!("Fields cannot take the attribut 'prefix_type'") @@ -211,22 +256,38 @@ fn get_enum_match(variant: &Variant) -> TokenStream { /// for a specific field `f` accessible using `field_ref`. fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { let params = ParamsField::parse(&f.attrs); - match (&f.ty, params) { + let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix { + quote_spanned! { f.span() => + #stream.iter().collect::>().len() + } + } else { + quote_spanned! { f.span() => 0 } + }; + let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix { + quote_spanned! { f.span() => + #stream.iter().collect::>().len() + } + } else { + quote_spanned! { f.span() => 0 } + }; + let main_stream = match (&f.ty, params) { ( _, ParamsField { until: Some(UntilParams(d, u, until)), + .. }, ) => quote_spanned! { f.span() => androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until) }, - (Type::Array(_), ParamsField { until: None }) => quote_spanned! { f.span() => + (Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() => #field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::() }, - (_, ParamsField { until: None }) => quote_spanned! { f.span() => + (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() => androscalpel_serializer::Serializable::size(&#field_ref) }, - } + }; + quote_spanned! { f.span() => #prefix_stream + #main_stream + #suffix_stream } } /// Return the implementation of the [`androscalpel_serializer::Serializable::size`]. @@ -263,7 +324,7 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { let prefix = params.prefix.expect( "Cannot derive Serializable for variant without the #[prefix(val)] attribute", ); - let PrefixParams(val) = prefix; + let PrefixParamsVariant(val) = prefix; let match_ = get_enum_match(v); let body = match v.fields { Fields::Named(ref fields) => { @@ -304,23 +365,47 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { let params = ParamsField::parse(&f.attrs); // TODO: Improve error handling - match (&f.ty, params) { + let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix { + quote_spanned! { f.span() => + for byte in #stream { + ::serialize(&byte, output)?; + } + } + } else { + quote_spanned! { f.span() => } + }; + let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix { + quote_spanned! { f.span() => + for byte in #stream { + ::serialize(&byte, output)?; + } + } + } else { + quote_spanned! { f.span() => } + }; + let main_stream = match (&f.ty, params) { ( _, ParamsField { until: Some(UntilParams(d, u, until)), + .. }, ) => quote_spanned! { f.span() => androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?; }, - (Type::Array(_), ParamsField { until: None }) => quote_spanned! { f.span() => + (Type::Array(_), ParamsField { until: None, .. }) => quote_spanned! { f.span() => for x in #field_ref { androscalpel_serializer::Serializable::serialize(&x, output)?; } }, - (_, ParamsField { until: None }) => quote_spanned! { f.span() => + (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() => androscalpel_serializer::Serializable::serialize(&#field_ref, output)?; }, + }; + quote_spanned! { f.span() => + #prefix_stream + #main_stream + #suffix_stream } } /// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`]. @@ -359,7 +444,7 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream { let prefix = params.prefix.expect( "Cannot derive Serializable for variant without the #[prefix(val)] attribute", ); - let PrefixParams(val) = prefix; + let PrefixParamsVariant(val) = prefix; let match_ = get_enum_match(v); let body = match v.fields { Fields::Named(ref fields) => { @@ -408,30 +493,69 @@ fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenS let params = ParamsField::parse(&f.attrs); let ty = &f.ty; // TODO: Improve error handling + let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix { + quote_spanned! { f.span() => + for byte in #stream { + if ::deserialize(input)? != byte { + return Err(androscalpel_serializer::Error::DeserializationError( + "Prefix do not match #stream".into() + )); + } + } + } + } else { + quote_spanned! { f.span() => } + }; + let suffix_stream = if let Some(SuffixParamsField(ref stream)) = params.suffix { + quote_spanned! { f.span() => + for byte in #stream { + if ::deserialize(input)? != byte { + return Err(androscalpel_serializer::Error::DeserializationError( + "Suffix do not match #stream".into() + )); + } + } + } + } else { + quote_spanned! { f.span() => } + }; match (ty, params) { ( _, ParamsField { until: Some(UntilParams(d, u, until)), + .. }, ) => quote_spanned! { f.span() => - #field_ref <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?, + #field_ref { + #prefix_stream + let val = <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?; + #suffix_stream + val + }, }, - (Type::Array(arr), ParamsField { until: None }) => { + (Type::Array(arr), ParamsField { until: None, .. }) => { let len = &arr.len; let arr_ty = &arr.elem; quote_spanned! { f.span() => #field_ref { + #prefix_stream let mut vec_ = vec![]; for _ in 0..(#len) { vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?); } + #suffix_stream vec_.try_into().unwrap() }, } } - (_, ParamsField { until: None }) => quote_spanned! { f.span() => - #field_ref <#ty as androscalpel_serializer::Serializable>::deserialize(input)?, + (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() => + #field_ref { + #prefix_stream + let val = <#ty as androscalpel_serializer::Serializable>::deserialize(input)?; + #suffix_stream + val + }, }, } } @@ -471,7 +595,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream { let recurse = data.variants.iter().map(|v| { let v_ident = &v.ident; let v_params = ParamsVariant::parse(&v.attrs); - let PrefixParams(val) = v_params.prefix.expect( + let PrefixParamsVariant(val) = v_params.prefix.expect( "Cannot derive Serializable for variant without the #[prefix(val)] attribute", ); match v.fields {