diff --git a/androscalpel_serializer/src/core/mod.rs b/androscalpel_serializer/src/core/mod.rs index dd8eb07..dc4c9db 100644 --- a/androscalpel_serializer/src/core/mod.rs +++ b/androscalpel_serializer/src/core/mod.rs @@ -880,4 +880,85 @@ mod test { vec![0x42, 0, 1, 2, 3, 0x66, 0x66] ); } + + #[derive(Serializable, PartialEq, Eq, Debug)] + #[prefix_type(u8)] + enum TestEnumUnnamedDef { + #[prefix(0)] + Zero, + #[prefix(1)] + One(u32), + #[prefix(2)] + Two { a: u16, b: u32 }, + #[default_variant] + Dft(u8, u32), + } + + #[test] + fn size_enum_unnamed_def() { + assert_eq!(TestEnumUnnamedDef::Zero.size(), 1); + assert_eq!(TestEnumUnnamedDef::One(42).size(), 5); + assert_eq!(TestEnumUnnamedDef::Two { a: 1, b: 2 }.size(), 7); + assert_eq!(TestEnumUnnamedDef::Dft(42, 0x01234567).size(), 5); + } + + #[test] + fn serialize_enum_unnamed_def() { + assert_eq!( + TestEnumUnnamedDef::Zero.serialize_to_vec().unwrap(), + vec![0u8] + ); + assert_eq!( + TestEnumUnnamedDef::One(42).serialize_to_vec().unwrap(), + vec![1u8, 0u8, 0u8, 0u8, 42u8] + ); + assert_eq!( + TestEnumUnnamedDef::Two { a: 1, b: 2 } + .serialize_to_vec() + .unwrap(), + vec![2u8, 0u8, 1u8, 0u8, 0u8, 0u8, 2u8] + ); + assert_eq!( + TestEnumUnnamedDef::Dft(42, 0x01234567) + .serialize_to_vec() + .unwrap(), + vec![42u8, 0x01u8, 0x23u8, 0x45, 0x67], + ); + } + + #[test] + fn deserialize_enum_unnamed_def() { + assert_eq!( + TestEnumUnnamedDef::deserialize_from_slice(&[0u8]).unwrap(), + TestEnumUnnamedDef::Zero + ); + assert_eq!( + TestEnumUnnamedDef::deserialize_from_slice(&[1u8, 0u8, 0u8, 0u8, 42u8]).unwrap(), + TestEnumUnnamedDef::One(42) + ); + assert_eq!( + TestEnumUnnamedDef::deserialize_from_slice(&[2u8, 0u8, 1u8, 0u8, 0u8, 0u8, 2u8]) + .unwrap(), + TestEnumUnnamedDef::Two { a: 1, b: 2 } + ); + assert_eq!( + TestEnumUnnamedDef::deserialize_from_slice(&[42u8, 0x01u8, 0x23u8, 0x45, 0x67]) + .unwrap(), + TestEnumUnnamedDef::Dft(42, 0x01234567) + ); + } + + #[derive(Serializable, PartialEq, Eq, Debug)] + #[prefix_type(u8)] + enum TestEnumNamedDef { + #[default_variant] + Dft { prefix: u8 }, + } + // Must panic: TODO: test that, somehow + // #[derive(Serializable, PartialEq, Eq, Debug)] + // #[prefix_type(u8)] + // enum TestInvalidEnumNamedDef { + // #[default_variant] + // Dft { plop: u32, prefix: u8 }, + // } } diff --git a/androscalpel_serializer_derive/src/lib.rs b/androscalpel_serializer_derive/src/lib.rs index 26ea1a4..76be5c0 100644 --- a/androscalpel_serializer_derive/src/lib.rs +++ b/androscalpel_serializer_derive/src/lib.rs @@ -17,7 +17,7 @@ use syn::{ /// the type of the repeated data `D`, the type of the symbole at the end of the list `U`, and /// the specific value of the symbole at the end of the list `end`. /// -/// For example, we can deserialize a Vec until we find 0x0000: +/// For example, we can deserialize a `Vec` until we find `0x0000`: /// /// `[0, 1, 2, 3, 0, 0]` would be serialized as `vec![0, 1, 2, 3]` /// @@ -42,9 +42,57 @@ use syn::{ /// ); /// ``` /// -/// ## Enum +/// ## Enums /// -/// TODO +/// Enums are a litle tricky. +/// To distinguish the enum variant, there binary representation must be prefixed by +/// the serialization of a specific type. This type is the same for the whole enum, +/// and is defined with `prefix_type`, e.g. `#[prefix_type(u8)]`. +/// +/// Each variant (except the default one) must define the prefix they match with `prefix`, +/// e.g. `#[prefix(0x42)]`. +/// +/// An enum can define ONE variant as the default variant. This variant is a catch all, +/// and MUST be have named field or unnamed field (no unit variant!) and: +/// - The first field of named fields variant must be named `prefix` and of the type +/// `prefix_type`. +/// - The first field of unnamed fields variant must be of the type `prefix_type`. +/// +/// The first field of the default variant store the prefix of the variant. +/// +/// ``` +/// pub use androscalpel_serializer::*; +/// +/// #[derive(Serializable, PartialEq, Debug)] +/// #[prefix_type(u8)] +/// enum Example { +/// #[prefix(0)] +/// Zero, +/// #[prefix(1)] +/// One(u32), +/// #[prefix(2)] +/// Two { a: u16, b: u32 }, +/// #[default_variant] +/// Dft(u8, u32) +/// } +/// +/// assert_eq!( +/// Example::deserialize_from_slice(&[0u8]).unwrap(), +/// Example::Zero +/// ); +/// assert_eq!( +/// Example::deserialize_from_slice(&[1u8, 0x01u8, 0x23u8, 0x45, 0x67]).unwrap(), +/// Example::One(0x01234567) +/// ); +/// assert_eq!( +/// Example::deserialize_from_slice(&[2u8, 0xFEu8, 0xDCu8, 0x01u8, 0x23u8, 0x45, 0x67]).unwrap(), +/// Example::Two { a: 0xFEDC, b: 0x01234567 } +/// ); +/// assert_eq!( +/// Example::deserialize_from_slice(&[42u8, 0x01u8, 0x23u8, 0x45, 0x67]).unwrap(), +/// Example::Dft(42, 0x01234567) +/// ); +/// ``` /// /// ## Prefix and suffix /// @@ -68,7 +116,7 @@ use syn::{ /// ``` /// /// -#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix))] +#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix, default_variant))] pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident; @@ -165,6 +213,7 @@ struct ParamsField { #[derive(Default)] struct ParamsVariant { pub prefix: Option, + pub default_variant: Option<()>, } impl ParamsStruct { @@ -224,6 +273,9 @@ impl ParamsVariant { Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => { panic!("Variant cannot take the attribut 'prefix_type'") } + Meta::Path(path) if path.is_ident("default_variant") => { + params.default_variant = Some(()); + } _ => (), } } @@ -327,10 +379,16 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { let recurse = data.variants.iter().map(|v| { let ident = &v.ident; let params = ParamsVariant::parse(&v.attrs); - let prefix = params.prefix.expect( - "Cannot derive Serializable for variant without the #[prefix(val)] attribute", - ); - let PrefixParamsVariant(val) = prefix; + // let prefix = params.prefix.expect( + // "Cannot derive Serializable for variant without the #[prefix(val)] attribute", + // ); + // let PrefixParamsVariant(val) = prefix; + let prefix_quote = match params { + ParamsVariant {prefix: Some(PrefixParamsVariant(val)), default_variant: None } => quote_spanned! { v.span() => <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) }, + ParamsVariant {prefix: None, default_variant: Some(()) } => quote! { 0 }, // The prefix is in the first field + ParamsVariant {prefix: None, default_variant: None } => panic!("Cannot derive Serializable for variant without the #[prefix(val)] or #[default_variant] attribute"), + ParamsVariant {prefix: Some(_), default_variant: Some(()) } => panic! ("A variant cannot be the default one and specify a prefix value"), + }; let match_ = get_enum_match(v); let body = match v.fields { Fields::Named(ref fields) => { @@ -339,7 +397,8 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { get_implem_size_for_field(f, quote! { *#name }) }); quote_spanned! { v.span() => - <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* + //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* + #prefix_quote #(+#recurse )* } } Fields::Unnamed(ref fields) => { @@ -348,10 +407,12 @@ fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream { get_implem_size_for_field(f, quote! { *#ident }) }); quote_spanned! { v.span() => - <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* + //<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )* + #prefix_quote #(+#recurse )* } } - Fields::Unit => quote! { <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) }, + //Fields::Unit => quote! { <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) }, + Fields::Unit => quote! { #prefix_quote }, }; quote_spanned! { v.span() => @@ -447,19 +508,40 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream { let recurse = data.variants.iter().map(|v| { let ident = &v.ident; let params = ParamsVariant::parse(&v.attrs); - let prefix = params.prefix.expect( - "Cannot derive Serializable for variant without the #[prefix(val)] attribute", - ); - let PrefixParamsVariant(val) = prefix; + //let prefix = params.prefix.expect( + // "Cannot derive Serializable for variant without the #[prefix(val)] attribute", + //); + //let PrefixParamsVariant(val) = prefix; + let prefix_quote = match params { + ParamsVariant {prefix: Some(PrefixParamsVariant(val)), default_variant: None } => quote_spanned! { v.span() => <#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; }, + ParamsVariant {prefix: None, default_variant: Some(()) } => quote! {}, + ParamsVariant {prefix: None, default_variant: None } => panic!("Cannot derive Serializable for variant without the #[prefix(val)] or #[default_variant] attribute"), + ParamsVariant {prefix: Some(_), default_variant: Some(()) } => panic! ("A variant cannot be the default one and specify a prefix value"), + }; let match_ = get_enum_match(v); let body = match v.fields { Fields::Named(ref fields) => { + if params.default_variant.is_some() && fields + .named + .first() + .unwrap() + .ident + .as_ref() + .expect( + "Default variant must have a least one field for the prefix" + ) != "prefix" { + panic!( + "The first field of a named field variant must be named `prefix` and of the type of the prefix of the enum" + ); + } + let recurse = fields.named.iter().map(|f| { let name = &f.ident; get_implem_serialize_for_field(f, quote! { *#name }) }); quote_spanned! { v.span() => - <#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; + //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; + #prefix_quote #(#recurse)* } } @@ -469,12 +551,14 @@ fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream { get_implem_serialize_for_field(f, quote! { *#ident }) }); quote_spanned! { v.span() => - <#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; + //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; + #prefix_quote #(#recurse)* } } Fields::Unit => quote! { - <#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; + //<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?; + #prefix_quote }, }; @@ -601,6 +685,10 @@ 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); + // Serialization of default value at the end + if v_params.default_variant.is_some() { + return quote! {}; + } let PrefixParamsVariant(val) = v_params.prefix.expect( "Cannot derive Serializable for variant without the #[prefix(val)] attribute", ); @@ -634,13 +722,63 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream { }, } }); + let mut default_variant = None; + for v in &data.variants { + let v_params = ParamsVariant::parse(&v.attrs); + if v_params.default_variant.is_some() && v_params.prefix.is_some() { + panic!("A variant cannot be both the default variant and check a prefix value"); + } + if v_params.default_variant.is_some() && default_variant.is_some() { + panic!("An enum can only have one variant marked as `default_variant`"); + } + if v_params.default_variant.is_some() { + default_variant = Some(v); + } + } + let default_quote = match default_variant { + None => quote! { + Err(androscalpel_serializer::Error::SerializationError(format!( + "Found prefix {:?} that did not match any variant.", prefix + ))) + }, + Some(v) => { + let v_ident = &v.ident; + match v.fields { + Fields::Named(ref fields) => { + // Skip 1 because the first field is the prefix + // TODO: check first field name is `prefix`? + let recurse = fields.named.iter().skip(1).map(|f| { + let name = &f.ident; + get_implem_deserialize_for_field(f, quote! { #name: }) + }); + quote_spanned! { v.span() => + return Ok(Self::#v_ident {prefix, #(#recurse)*}); + } + } + Fields::Unnamed(ref fields) => { + let recurse = fields + .unnamed + .iter() + .skip(1) + .map(|f| get_implem_deserialize_for_field(f, quote! {})); + quote_spanned! { v.span() => + return Ok(Self::#v_ident (prefix, #(#recurse)*)); + } + } + Fields::Unit => quote_spanned! { v.span() => + return Ok(Self::#v_ident); + }, + } + } + }; quote! { let prefix = <#prefix_ty as androscalpel_serializer::Serializable>::deserialize(input)?; #(#recurse)* - Err(androscalpel_serializer::Error::SerializationError(format!( - "Found prefix {:?} that did not match any variant.", prefix - ))) + //Err(androscalpel_serializer::Error::SerializationError(format!( + // "Found prefix {:?} that did not match any variant.", prefix + //))) + #default_quote } } Data::Union(_) => unimplemented!(),