From efe7144773168738276cbdacbcacd2fdebcb366e Mon Sep 17 00:00:00 2001 From: Hadrien G Date: Sat, 12 Oct 2019 17:42:42 +0200 Subject: [PATCH 1/3] Adapt to upcoming changes in Abomonation --- Cargo.toml | 2 +- src/lib.rs | 55 +++++++++++++++++++++++++++++++-------------------- tests/test.rs | 21 ++++++++++++++++++++ 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 785aeb6..bd07caf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,4 @@ quote = "1.0" synstructure = "0.12" [dev-dependencies] -abomonation = "0.7" +abomonation = { git = "https://github.com/HadrienG2/abomonation.git", branch = "support-more-types" } diff --git a/src/lib.rs b/src/lib.rs index 53aea19..7bb7b87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,38 +14,51 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea }); let entomb = s.each(|bi| quote! { - ::abomonation::Abomonation::entomb(#bi, _write)?; + ::abomonation::Entomb::entomb(#bi, _write)?; }); let extent = s.each(|bi| quote! { - sum += ::abomonation::Abomonation::extent(#bi); + sum += ::abomonation::Entomb::extent(#bi); }); s.bind_with(|_| synstructure::BindStyle::RefMut); let exhume = s.each(|bi| quote! { - let temp = bytes; - bytes = ::abomonation::Abomonation::exhume(#bi, temp)?; + bytes = ::abomonation::Exhume::exhume(From::from(#bi), bytes)?; }); - s.bound_impl(quote!(abomonation::Abomonation), quote! { - #[inline] unsafe fn entomb(&self, _write: &mut W) -> ::std::io::Result<()> { - match *self { #entomb } - Ok(()) - } - #[allow(unused_mut)] - #[inline] fn extent(&self) -> usize { - let mut sum = 0; - match *self { #extent } - sum + s.gen_impl(quote! { + extern crate abomonation; + extern crate std; + + gen unsafe impl abomonation::Entomb for @Self { + unsafe fn entomb(&self, _write: &mut W) -> ::std::io::Result<()> { + match *self { #entomb } + Ok(()) + } + + #[allow(unused_mut)] + fn extent(&self) -> usize { + let mut sum = 0; + match *self { #extent } + sum + } } - #[allow(unused_mut)] - #[inline] unsafe fn exhume<'a,'b>( - &'a mut self, - mut bytes: &'b mut [u8] - ) -> Option<&'b mut [u8]> { - match *self { #exhume } - Some(bytes) + + gen unsafe impl<'de> abomonation::Exhume<'de> for @Self + where Self: 'de, + { + #[allow(unused_mut)] + unsafe fn exhume( + self_: std::ptr::NonNull, + mut bytes: &'de mut [u8] + ) -> Option<&'de mut [u8]> { + // FIXME: This (briefly) constructs an &mut _ to invalid data + // (via "ref mut"), which is UB. The proposed &raw mut + // operator would allow avoiding this. + match *self_.as_ptr() { #exhume } + Some(bytes) + } } }) } diff --git a/tests/test.rs b/tests/test.rs index 7cc4a54..d6a47fd 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -202,4 +202,25 @@ mod tests { assert!(rest.len() == 0); } } + + #[derive(Eq, PartialEq, Abomonation)] + pub struct StructWithRef<'a> { + a: &'a str, + b: bool, + } + + #[test] + fn test_struct_with_ref() { + let record = StructWithRef { a: &"test", b: true }; + + let mut bytes = Vec::new(); + unsafe { encode(&record, &mut bytes).unwrap(); } + + assert_eq!(bytes.len(), measure(&record)); + + if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { + assert!(result == &record); + assert!(rest.len() == 0); + } + } } From 3e0c125b12341d388b4a12e13099a7c2f3dfec98 Mon Sep 17 00:00:00 2001 From: Hadrien G Date: Sat, 12 Oct 2019 18:19:24 +0200 Subject: [PATCH 2/3] Simplify extent() using fold --- src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7bb7b87..8aa6053 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,9 +17,10 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea ::abomonation::Entomb::entomb(#bi, _write)?; }); - let extent = s.each(|bi| quote! { - sum += ::abomonation::Entomb::extent(#bi); - }); + let extent = s.fold( + quote!(0), + |acc, bi| quote!(#acc + ::abomonation::Entomb::extent(#bi)) + ); s.bind_with(|_| synstructure::BindStyle::RefMut); @@ -37,11 +38,8 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea Ok(()) } - #[allow(unused_mut)] fn extent(&self) -> usize { - let mut sum = 0; match *self { #extent } - sum } } From feec442d091fa57986847d8ede6c70306747493d Mon Sep 17 00:00:00 2001 From: Hadrien G Date: Tue, 12 Nov 2019 15:40:53 +0100 Subject: [PATCH 3/3] Add memory alignment support --- Cargo.toml | 2 +- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++------------ tests/test.rs | 11 ++++++++++- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd07caf..dcbf517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,4 @@ quote = "1.0" synstructure = "0.12" [dev-dependencies] -abomonation = { git = "https://github.com/HadrienG2/abomonation.git", branch = "support-more-types" } +abomonation = { git = "https://github.com/HadrienG2/abomonation.git", branch = "alignment" } diff --git a/src/lib.rs b/src/lib.rs index 8aa6053..074c49a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ #![recursion_limit="128"] -use synstructure::decl_derive; use quote::quote; +use std::collections::HashSet; +use synstructure::decl_derive; decl_derive!([Abomonation, attributes(unsafe_abomonate_ignore)] => derive_abomonation); @@ -17,15 +18,40 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea ::abomonation::Entomb::entomb(#bi, _write)?; }); - let extent = s.fold( - quote!(0), - |acc, bi| quote!(#acc + ::abomonation::Entomb::extent(#bi)) - ); + // T::alignment() is the max of mem::align_of and the U::alignment()s of + // every U type used as a struct member or inside of an enum variant (which + // includes the alignment of recursively abomonated data) + // + // Unfortunately, we cannot use synstructure's nice `fold()` convenience + // here because it's based on generating match arms for a `self` value, + // whereas here we're trying to implement an inherent type method without + // having such a `self` handy. + // + // We can, however, use Structure::variants() and VariantInfo::bindings() + // to enumerate all the types which _would appear_ in such match arms' + // inner bindings. + // + let mut alignment = vec![ + quote!(let mut align = ::std::mem::align_of::();) + ]; + let mut probed_types = HashSet::new(); + for variant_info in s.variants() { + for binding_info in variant_info.bindings() { + let binding_type = &binding_info.ast().ty; + // Do not query a type's alignment() multiple times + if probed_types.insert(binding_type) { + alignment.push( + quote!(align = align.max(<#binding_type as ::abomonation::Entomb>::alignment());) + ); + } + } + } + alignment.push(quote!(align)); s.bind_with(|_| synstructure::BindStyle::RefMut); let exhume = s.each(|bi| quote! { - bytes = ::abomonation::Exhume::exhume(From::from(#bi), bytes)?; + ::abomonation::Exhume::exhume(From::from(#bi), reader)?; }); s.gen_impl(quote! { @@ -33,13 +59,16 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea extern crate std; gen unsafe impl abomonation::Entomb for @Self { - unsafe fn entomb(&self, _write: &mut W) -> ::std::io::Result<()> { + unsafe fn entomb( + &self, + _write: &mut ::abomonation::align::AlignedWriter + ) -> ::std::io::Result<()> { match *self { #entomb } Ok(()) } - fn extent(&self) -> usize { - match *self { #extent } + fn alignment() -> usize { + #(#alignment)* } } @@ -49,13 +78,13 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea #[allow(unused_mut)] unsafe fn exhume( self_: std::ptr::NonNull, - mut bytes: &'de mut [u8] - ) -> Option<&'de mut [u8]> { + reader: &mut ::abomonation::align::AlignedReader<'de> + ) -> Option<&'de mut Self> { // FIXME: This (briefly) constructs an &mut _ to invalid data // (via "ref mut"), which is UB. The proposed &raw mut // operator would allow avoiding this. match *self_.as_ptr() { #exhume } - Some(bytes) + Some(&mut *self_.as_ptr()) } } }) diff --git a/tests/test.rs b/tests/test.rs index d6a47fd..1368a2c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -6,6 +6,7 @@ extern crate abomonation_derive; #[cfg(test)] mod tests { use abomonation::*; + use abomonation::align::AlignedBytes; #[derive(Eq, PartialEq, Abomonation)] pub struct Struct { @@ -26,6 +27,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -47,6 +49,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -68,6 +71,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -89,6 +93,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::>>::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::>>(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -115,6 +120,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -126,7 +132,7 @@ mod tests { pub enum DataEnum { A(String, u64, Vec), B, - C(String, String, String) + C(String, String, u16) } #[test] @@ -141,6 +147,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -197,6 +204,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); // decode from binary data + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0); @@ -218,6 +226,7 @@ mod tests { assert_eq!(bytes.len(), measure(&record)); + let mut bytes = AlignedBytes::::new(&mut bytes); if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { assert!(result == &record); assert!(rest.len() == 0);