add prefix / suffix to field

This commit is contained in:
Jean-Marie Mineau 2023-08-22 17:23:36 +02:00
parent 33e770e04a
commit 5dd96fb173
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
4 changed files with 176 additions and 22 deletions

View file

@ -0,0 +1,3 @@
//! Constants definition
pub use androscalpel_serializer_derive::*;

View file

@ -5,7 +5,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom, Write};
pub use androscalpel_serializer_derive::*; pub use androscalpel_serializer_derive::*;
mod leb; pub mod leb;
pub use leb::*; pub use leb::*;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -849,4 +849,29 @@ mod test {
); );
assert!(TestEnum::deserialize_from_slice(&[255u8]).is_err()); 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]
);
}
} }

View file

@ -1,3 +1,5 @@
pub mod constant;
pub mod core; pub mod core;
pub use crate::core::*; pub use crate::core::*;
pub use constant::*;

View file

@ -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(<prefix>)]`
/// and `#[suffix(<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 { pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let name = input.ident; let name = input.ident;
@ -83,9 +105,9 @@ impl Parse for UntilParams {
} }
} }
/// Parsed Parameters for the `#[prefix(val)]` attribute. /// Parsed Parameters for the `#[prefix(val)]` attribute of an enum variant.
struct PrefixParams(TokenStream); struct PrefixParamsVariant(TokenStream);
impl Parse for PrefixParams { impl Parse for PrefixParamsVariant {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
let value = input.parse()?; let value = input.parse()?;
Ok(Self(value)) Ok(Self(value))
@ -107,16 +129,36 @@ struct ParamsStruct {
pub prefix_type: Option<PrefixTypeParams>, pub prefix_type: Option<PrefixTypeParams>,
} }
/// Parsed Parameters for the `#[prefix(val)]` attribute of an struct field.
struct PrefixParamsField(TokenStream);
impl Parse for PrefixParamsField {
fn parse(input: ParseStream) -> syn::Result<Self> {
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<Self> {
let value = input.parse()?;
Ok(Self(value))
}
}
/// All the attributes parameters for a field /// All the attributes parameters for a field
#[derive(Default)] #[derive(Default)]
struct ParamsField { struct ParamsField {
pub until: Option<UntilParams>, pub until: Option<UntilParams>,
pub prefix: Option<PrefixParamsField>,
pub suffix: Option<SuffixParamsField>,
} }
/// All the attributes parameters for a variant /// All the attributes parameters for a variant
#[derive(Default)] #[derive(Default)]
struct ParamsVariant { struct ParamsVariant {
pub prefix: Option<PrefixParams>, pub prefix: Option<PrefixParamsVariant>,
} }
impl ParamsStruct { impl ParamsStruct {
@ -147,8 +189,11 @@ impl ParamsField {
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => { Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => {
params.until = Some(syn::parse2(tokens.clone()).unwrap()) params.until = Some(syn::parse2(tokens.clone()).unwrap())
} }
Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => { Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => {
panic!("Fields cannot take the attribut '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") => { Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
panic!("Fields cannot take the attribut '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`. /// for a specific field `f` accessible using `field_ref`.
fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream { fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
let params = ParamsField::parse(&f.attrs); 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::<Vec<_>>().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::<Vec<_>>().len()
}
} else {
quote_spanned! { f.span() => 0 }
};
let main_stream = match (&f.ty, params) {
( (
_, _,
ParamsField { ParamsField {
until: Some(UntilParams(d, u, until)), until: Some(UntilParams(d, u, until)),
..
}, },
) => quote_spanned! { f.span() => ) => quote_spanned! { f.span() =>
androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until) 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::<usize>() #field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::<usize>()
}, },
(_, ParamsField { until: None }) => quote_spanned! { f.span() => (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
androscalpel_serializer::Serializable::size(&#field_ref) 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`]. /// 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( let prefix = params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute", "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 match_ = get_enum_match(v);
let body = match v.fields { let body = match v.fields {
Fields::Named(ref 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 { fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
let params = ParamsField::parse(&f.attrs); let params = ParamsField::parse(&f.attrs);
// TODO: Improve error handling // 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 {
<u8 as androscalpel_serializer::Serializable>::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 {
<u8 as androscalpel_serializer::Serializable>::serialize(&byte, output)?;
}
}
} else {
quote_spanned! { f.span() => }
};
let main_stream = match (&f.ty, params) {
( (
_, _,
ParamsField { ParamsField {
until: Some(UntilParams(d, u, until)), until: Some(UntilParams(d, u, until)),
..
}, },
) => quote_spanned! { f.span() => ) => quote_spanned! { f.span() =>
androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?; 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 { for x in #field_ref {
androscalpel_serializer::Serializable::serialize(&x, output)?; 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)?; 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`]. /// 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( let prefix = params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute", "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 match_ = get_enum_match(v);
let body = match v.fields { let body = match v.fields {
Fields::Named(ref 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 params = ParamsField::parse(&f.attrs);
let ty = &f.ty; let ty = &f.ty;
// TODO: Improve error handling // TODO: Improve error handling
let prefix_stream = if let Some(PrefixParamsField(ref stream)) = params.prefix {
quote_spanned! { f.span() =>
for byte in #stream {
if <u8 as androscalpel_serializer::Serializable>::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 <u8 as androscalpel_serializer::Serializable>::deserialize(input)? != byte {
return Err(androscalpel_serializer::Error::DeserializationError(
"Suffix do not match #stream".into()
));
}
}
}
} else {
quote_spanned! { f.span() => }
};
match (ty, params) { match (ty, params) {
( (
_, _,
ParamsField { ParamsField {
until: Some(UntilParams(d, u, until)), until: Some(UntilParams(d, u, until)),
..
}, },
) => quote_spanned! { f.span() => ) => 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 len = &arr.len;
let arr_ty = &arr.elem; let arr_ty = &arr.elem;
quote_spanned! { f.span() => quote_spanned! { f.span() =>
#field_ref { #field_ref {
#prefix_stream
let mut vec_ = vec![]; let mut vec_ = vec![];
for _ in 0..(#len) { for _ in 0..(#len) {
vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?); vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?);
} }
#suffix_stream
vec_.try_into().unwrap() vec_.try_into().unwrap()
}, },
} }
} }
(_, ParamsField { until: None }) => quote_spanned! { f.span() => (_, ParamsField { until: None, .. }) => quote_spanned! { f.span() =>
#field_ref <#ty as androscalpel_serializer::Serializable>::deserialize(input)?, #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 recurse = data.variants.iter().map(|v| {
let v_ident = &v.ident; let v_ident = &v.ident;
let v_params = ParamsVariant::parse(&v.attrs); 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", "Cannot derive Serializable for variant without the #[prefix(val)] attribute",
); );
match v.fields { match v.fields {