From 984b0b05cf1e508659e3fc7e4f88fc58124da985 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Wed, 18 Sep 2019 12:17:37 +0200 Subject: [PATCH 01/18] Move to a NonNull-based exhume() interface --- src/lib.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e617dc..f857aab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ use std::io::Write; // for bytes.write_all; push_all is unstable and extend is s use std::io::Result as IOResult; use std::marker::PhantomData; use std::num::*; +use std::ptr::NonNull; pub mod abomonated; @@ -124,9 +125,9 @@ pub unsafe fn decode(bytes: &mut [u8]) -> Option<(&T, &mut [u8]) if bytes.len() < mem::size_of::() { None } else { let (split1, split2) = bytes.split_at_mut(mem::size_of::()); - let result: &mut T = mem::transmute(split1.get_unchecked_mut(0)); - if let Some(remaining) = result.exhume(split2) { - Some((result, remaining)) + let result: NonNull = mem::transmute(split1.get_unchecked_mut(0)); + if let Some(remaining) = T::exhume(result, split2) { + Some((&*result.as_ptr(), remaining)) } else { None @@ -165,10 +166,17 @@ pub trait Abomonation { /// reports any failures in writing to `write`. #[inline(always)] unsafe fn entomb(&self, _write: &mut W) -> IOResult<()> { Ok(()) } - /// Recover any information for `&mut self` not evident from its binary representation. + /// Recover any information for `self_` not evident from its binary representation. /// /// Most commonly this populates pointers with valid references into `bytes`. - #[inline(always)] unsafe fn exhume<'a,'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { Some(bytes) } + /// + /// Implementors should take note that `self_` is initially in an invalid state, as its inner + /// pointers may be dangling. As Rust references come with a data validity invariant, building + /// references to invalid state is undefined behavior, so one should strive to implement + /// `exhume` using raw pointer operations as much as feasible. + // + // FIXME: Replace self_ with self once Rust has arbitrary self types + #[inline(always)] unsafe fn exhume<'a>(_self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { Some(bytes) } /// Reports the number of further bytes required to entomb `self`. #[inline(always)] fn extent(&self) -> usize { 0 } @@ -227,7 +235,7 @@ macro_rules! unsafe_abomonate { $( self.$field.entomb(write)?; )* Ok(()) } - #[inline] unsafe fn exhume<'a,'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + #[inline] unsafe fn exhume<'a>(self_: ::std::ptr::NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { $( let temp = bytes; bytes = self.$field.exhume(temp)?; )* Some(bytes) } @@ -251,7 +259,7 @@ macro_rules! tuple_abomonate { Ok(()) } #[allow(non_snake_case)] - #[inline(always)] unsafe fn exhume<'a,'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { let ($(ref mut $name,)*) = *self; $( let temp = bytes; bytes = $name.exhume(temp)?; )* Some(bytes) @@ -314,7 +322,7 @@ impl Abomonation for Option { } Ok(()) } - #[inline(always)] unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut[u8]) -> Option<&'b mut [u8]> { + #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { if let &mut Some(ref mut inner) = self { let tmp = bytes; bytes = inner.exhume(tmp)?; } @@ -333,7 +341,7 @@ impl Abomonation for Result { }; Ok(()) } - #[inline(always)] unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut[u8]) -> Option<&'b mut [u8]> { + #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { match self { &mut Ok(ref mut inner) => inner.exhume(bytes), &mut Err(ref mut inner) => inner.exhume(bytes), @@ -390,7 +398,7 @@ macro_rules! array_abomonate { Ok(()) } #[inline(always)] - unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut[u8]) -> Option<&'b mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { for element in self { let tmp = bytes; bytes = element.exhume(tmp)?; } @@ -448,7 +456,7 @@ impl Abomonation for String { Ok(()) } #[inline] - unsafe fn exhume<'a,'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { if self.len() > bytes.len() { None } else { let (mine, rest) = bytes.split_at_mut(self.len()); @@ -469,7 +477,7 @@ impl Abomonation for Vec { Ok(()) } #[inline] - unsafe fn exhume<'a,'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { // extract memory from bytes to back our vector let binary_len = self.len() * mem::size_of::(); @@ -503,7 +511,7 @@ impl Abomonation for Box { Ok(()) } #[inline] - unsafe fn exhume<'a,'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { let binary_len = mem::size_of::(); if binary_len > bytes.len() { None } else { From e97de1bd5051adc36bf53f99e34ce967d11a0b28 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Wed, 18 Sep 2019 15:03:58 +0200 Subject: [PATCH 02/18] Port unsafe_abomonate to NonNull-based interface --- src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f857aab..48d9491 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,10 +235,17 @@ macro_rules! unsafe_abomonate { $( self.$field.entomb(write)?; )* Ok(()) } + #[inline] unsafe fn exhume<'a>(self_: ::std::ptr::NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { - $( let temp = bytes; bytes = self.$field.exhume(temp)?; )* + $( + // FIXME: This (briefly) constructs an &mut _ to invalid data, which is UB. + // The proposed &raw mut operator would allow avoiding this. + let field_ptr: ::std::ptr::NonNull<_> = From::from(&mut (*self_.as_ptr()).$field); + bytes = Abomonation::exhume(field_ptr, bytes)?; + )* Some(bytes) } + #[inline] fn extent(&self) -> usize { let mut size = 0; $( size += self.$field.extent(); )* From 5fd495799695bb60f8885cb5a63e280120113c2a Mon Sep 17 00:00:00 2001 From: Hadrien G Date: Wed, 25 Sep 2019 22:53:39 +0200 Subject: [PATCH 03/18] Clarify that the tuple_abomonate macro takes types as input --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 48d9491..1d5c034 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,25 +257,25 @@ macro_rules! unsafe_abomonate { // general code for tuples (can't use '0', '1', ... as field identifiers) macro_rules! tuple_abomonate { - ( $($name:ident)+) => ( - impl<$($name: Abomonation),*> Abomonation for ($($name,)*) { + ( $($ty:ident)+) => ( + impl<$($ty: Abomonation),*> Abomonation for ($($ty,)*) { #[allow(non_snake_case)] #[inline(always)] unsafe fn entomb(&self, write: &mut WRITE) -> IOResult<()> { - let ($(ref $name,)*) = *self; - $($name.entomb(write)?;)* + let ($(ref $ty,)*) = *self; + $($ty.entomb(write)?;)* Ok(()) } #[allow(non_snake_case)] #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { - let ($(ref mut $name,)*) = *self; - $( let temp = bytes; bytes = $name.exhume(temp)?; )* + let ($(ref mut $ty,)*) = *self; + $( let temp = bytes; bytes = $ty.exhume(temp)?; )* Some(bytes) } #[allow(non_snake_case)] #[inline(always)] fn extent(&self) -> usize { let mut size = 0; - let ($(ref $name,)*) = *self; - $( size += $name.extent(); )* + let ($(ref $ty,)*) = *self; + $( size += $ty.extent(); )* size } } From b182a3877f7b4c80ad6181ecdfdaa3854fe47abe Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Wed, 18 Sep 2019 15:04:35 +0200 Subject: [PATCH 04/18] Port tuple_abomonate to NonNull-based interface --- src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1d5c034..252f654 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,12 +265,20 @@ macro_rules! tuple_abomonate { $($ty.entomb(write)?;)* Ok(()) } + #[allow(non_snake_case)] #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { - let ($(ref mut $ty,)*) = *self; - $( let temp = bytes; bytes = $ty.exhume(temp)?; )* + // FIXME: This (briefly) constructs a "ref mut" to invalid data, which is UB. + // I think avoiding this would require a cleaner way to iterate over tuple fields. + // One possibility would be a C++11-style combination of variadic generics and recursion. + let ($(ref mut $ty,)*) = *self_.as_ptr(); + $( + let field_ptr : NonNull<$ty> = From::from($ty); + bytes = $ty::exhume(field_ptr, bytes)?; + )* Some(bytes) } + #[allow(non_snake_case)] #[inline(always)] fn extent(&self) -> usize { let mut size = 0; From d4a78e4b85312d63f390246f8365d048bcfdb240 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Wed, 18 Sep 2019 15:04:58 +0200 Subject: [PATCH 05/18] Port Box abomonation to NonNull-based interface --- src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 252f654..0950f44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -525,17 +525,20 @@ impl Abomonation for Box { (**self).entomb(bytes)?; Ok(()) } + #[inline] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { let binary_len = mem::size_of::(); if binary_len > bytes.len() { None } else { let (mine, mut rest) = bytes.split_at_mut(binary_len); - std::ptr::write(self, mem::transmute(mine.as_mut_ptr() as *mut T)); - let temp = rest; rest = (**self).exhume(temp)?; + let box_target : NonNull = NonNull::new_unchecked(mine.as_mut_ptr() as *mut T); + rest = T::exhume(box_target, rest)?; + self_.as_ptr().write(Box::from_raw(box_target.as_ptr())); Some(rest) } } + #[inline] fn extent(&self) -> usize { mem::size_of::() + (&**self).extent() } From 421a486b2f11ebc5804be536b044be191598ae00 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Wed, 18 Sep 2019 19:43:53 +0200 Subject: [PATCH 06/18] Port Rust enum abomonation to NonNull-based interface --- src/lib.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0950f44..c681214 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,12 +337,17 @@ impl Abomonation for Option { } Ok(()) } + #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { - if let &mut Some(ref mut inner) = self { - let tmp = bytes; bytes = inner.exhume(tmp)?; + // FIXME: This (briefly) constructs a "ref mut" to invalid data, which is UB. + // I'm not sure if this can be fully resolved without relying on enum implementation details. + if let Some(ref mut inner) = *self_.as_ptr() { + let inner_ptr : NonNull = From::from(inner); + bytes = T::exhume(inner_ptr, bytes)?; } Some(bytes) } + #[inline] fn extent(&self) -> usize { self.as_ref().map(|inner| inner.extent()).unwrap_or(0) } @@ -356,12 +361,22 @@ impl Abomonation for Result { }; Ok(()) } + #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { - match self { - &mut Ok(ref mut inner) => inner.exhume(bytes), - &mut Err(ref mut inner) => inner.exhume(bytes), + // FIXME: This (briefly) constructs a "ref mut" to invalid data, which is UB. + // I'm not sure if this can be fully resolved without relying on enum implementation details. + match *self_.as_ptr() { + Ok(ref mut inner) => { + let inner_ptr : NonNull = From::from(inner); + T::exhume(inner_ptr, bytes) + } + Err(ref mut inner) => { + let inner_ptr : NonNull = From::from(inner); + E::exhume(inner_ptr, bytes) + } } } + #[inline] fn extent(&self) -> usize { match self { &Ok(ref inner) => inner.extent(), From 5ef07a7d460d168e3c1dd12a425fba257043c6b4 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Thu, 19 Sep 2019 13:48:14 +0200 Subject: [PATCH 07/18] Port slice-like abomonations to NonNull-based interface (and deduplicate them) --- src/lib.rs | 78 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c681214..e5750eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -424,22 +424,16 @@ macro_rules! array_abomonate { impl Abomonation for [T; $size] { #[inline(always)] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { - for element in self { element.entomb(write)?; } - Ok(()) + entomb_slice(&self[..], write) } + #[inline(always)] - unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { - for element in self { - let tmp = bytes; bytes = element.exhume(tmp)?; - } - Some(bytes) + unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { + exhume_slice(self_.as_ptr() as *mut T, $size, bytes) } + #[inline(always)] fn extent(&self) -> usize { - let mut size = 0; - for element in self { - size += element.extent(); - } - size + slice_extent(&self[..]) } } ) @@ -485,15 +479,20 @@ impl Abomonation for String { write.write_all(self.as_bytes())?; Ok(()) } + #[inline] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { - if self.len() > bytes.len() { None } + // FIXME: This (briefly) constructs an &String to invalid data, which is UB. + // I'm not sure if this can be fully resolved without relying on String implementation details. + let self_len = self_.as_ref().len(); + if self_len > bytes.len() { None } else { - let (mine, rest) = bytes.split_at_mut(self.len()); - std::ptr::write(self, String::from_raw_parts(mem::transmute(mine.as_ptr()), self.len(), self.len())); + let (mine, rest) = bytes.split_at_mut(self_len); + self_.as_ptr().write(String::from_raw_parts(mine.as_mut_ptr(), self_len, self_len)); Some(rest) } } + #[inline] fn extent(&self) -> usize { self.len() } @@ -503,33 +502,28 @@ impl Abomonation for Vec { #[inline] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { write.write_all(typed_to_bytes(&self[..]))?; - for element in self.iter() { element.entomb(write)?; } - Ok(()) + entomb_slice(&self[..], write) } + #[inline] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { - - // extract memory from bytes to back our vector - let binary_len = self.len() * mem::size_of::(); + // FIXME: This (briefly) constructs an &Vec to invalid data, which is UB. + // I'm not sure if this can be fully resolved without relying on Vec implementation details. + let self_len = self_.as_ref().len(); + let binary_len = self_len * mem::size_of::(); if binary_len > bytes.len() { None } else { let (mine, mut rest) = bytes.split_at_mut(binary_len); - let slice = std::slice::from_raw_parts_mut(mine.as_mut_ptr() as *mut T, self.len()); - std::ptr::write(self, Vec::from_raw_parts(slice.as_mut_ptr(), self.len(), self.len())); - for element in self.iter_mut() { - let temp = rest; // temp variable explains lifetimes (mysterious!) - rest = element.exhume(temp)?; - } + let first_ptr = mine.as_mut_ptr() as *mut T; + rest = exhume_slice(first_ptr, self_len, rest)?; + self_.as_ptr().write(Vec::from_raw_parts(first_ptr, self_len, self_len)); Some(rest) } } + #[inline] fn extent(&self) -> usize { - let mut sum = mem::size_of::() * self.len(); - for element in self.iter() { - sum += element.extent(); - } - sum + mem::size_of::() * self.len() + slice_extent(&self[..]) } } @@ -564,6 +558,28 @@ impl Abomonation for Box { std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::()) } +// Common subset of "entomb" for all [T]-like types +unsafe fn entomb_slice(slice: &[T], write: &mut W) -> IOResult<()> { + for element in slice { element.entomb(write)?; } + Ok(()) +} + +// Common subset of "exhume" for all [T]-like types +// (I'd gladly take a NonNull<[T]>, but it is too difficult to build raw pointers to slices) +#[inline] +unsafe fn exhume_slice<'a, T: Abomonation>(first_ptr: *mut T, length: usize, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { + for i in 0..length { + let element_ptr: NonNull = NonNull::new_unchecked(first_ptr.add(i)); + bytes = T::exhume(element_ptr, bytes)?; + } + Some(bytes) +} + +// Common subset of "extent" for all [T]-like types +fn slice_extent(slice: &[T]) -> usize { + slice.iter().map(T::extent).sum() +} + mod network { use Abomonation; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr}; From b23d2dc2acb83438eb3716255c7b27e1097a4dc9 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Thu, 19 Sep 2019 18:22:33 +0200 Subject: [PATCH 08/18] Require Debug and use assert_eq for improved test failure messages --- tests/tests.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index 340e776..3ff0dec 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,6 +1,7 @@ extern crate abomonation; use abomonation::*; +use std::fmt::Debug; #[test] fn test_array() { _test_pass(vec![[0, 1, 2]; 1024]); } #[test] fn test_nonzero() { _test_pass(vec![[std::num::NonZeroI32::new(1)]; 1024]); } @@ -38,21 +39,21 @@ fn test_phantom_data_for_non_abomonatable_type() { _test_pass(PhantomData::::default()); } -fn _test_pass(record: T) { +fn _test_pass(record: T) { let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } { let (result, rest) = unsafe { decode::(&mut bytes[..]) }.unwrap(); - assert!(&record == result); - assert!(rest.len() == 0); + assert_eq!(&record, result); + assert_eq!(rest.len(), 0); } } -fn _test_fail(record: T) { +fn _test_fail(record: T) { let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } bytes.pop(); - assert!(unsafe { decode::(&mut bytes[..]) }.is_none()); + assert_eq!(unsafe { decode::(&mut bytes[..]) }, None); } fn _test_size(record: T) { @@ -62,7 +63,7 @@ fn _test_size(record: T) { } -#[derive(Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] struct MyStruct { a: String, b: u64, @@ -82,8 +83,8 @@ fn test_macro() { // decode a &Vec<(u64, String)> from binary data if let Some((result, rest)) = unsafe { decode::(&mut bytes) } { - assert!(result == &record); - assert!(rest.len() == 0); + assert_eq!(result, &record); + assert_eq!(rest.len(), 0); } } @@ -106,10 +107,10 @@ fn test_multiple_encode_decode() { unsafe { encode(&vec![1,2,3], &mut bytes).unwrap(); } unsafe { encode(&"grawwwwrr".to_owned(), &mut bytes).unwrap(); } - let (t, r) = unsafe { decode::(&mut bytes) }.unwrap(); assert!(*t == 0); - let (t, r) = unsafe { decode::(r) }.unwrap(); assert!(*t == 7); - let (t, r) = unsafe { decode::>(r) }.unwrap(); assert!(*t == vec![1,2,3]); - let (t, _r) = unsafe { decode::(r) }.unwrap(); assert!(*t == "grawwwwrr".to_owned()); + let (t, r) = unsafe { decode::(&mut bytes) }.unwrap(); assert_eq!(*t, 0); + let (t, r) = unsafe { decode::(r) }.unwrap(); assert_eq!(*t, 7); + let (t, r) = unsafe { decode::>(r) }.unwrap(); assert_eq!(*t, vec![1,2,3]); + let (t, _r) = unsafe { decode::(r) }.unwrap(); assert_eq!(*t, "grawwwwrr".to_owned()); } #[test] @@ -125,6 +126,6 @@ fn test_net_types() { unsafe { encode(&socket_addr4, &mut bytes).unwrap(); } unsafe { encode(&socket_addr6, &mut bytes).unwrap(); } - let (t, r) = unsafe { decode::(&mut bytes) }.unwrap(); assert!(*t == socket_addr4); - let (t, _r) = unsafe { decode::(r) }.unwrap(); assert!(*t == socket_addr6); + let (t, r) = unsafe { decode::(&mut bytes) }.unwrap(); assert_eq!(*t, socket_addr4); + let (t, _r) = unsafe { decode::(r) }.unwrap(); assert_eq!(*t, socket_addr6); } From b5f7b71bdd7fdea8fa11c48a09cfdb5901b1a0c4 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Thu, 19 Sep 2019 18:23:11 +0200 Subject: [PATCH 09/18] Clarify invalid data as a source of UB --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c8565c..8afb905 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A mortifying serialization library for Rust Abomonation (spelling intentional) is a serialization library for Rust based on the very simple idea that if someone presents data for serialization it will copy those exact bits, and then follow any pointers and copy those bits, and so on. When deserializing it recovers the exact bits, and then corrects pointers to aim at the serialized forms of the chased data. -**Warning**: Abomonation should not be used on any data you care strongly about, or from any computer you value the data on. The `encode` and `decode` methods do things that may be undefined behavior, and you shouldn't stand for that. Specifically, `encode` exposes padding bytes to `memcpy`, and `decode` doesn't much respect alignment. +**Warning**: Abomonation should not be used on any data you care strongly about, or from any computer you value the data on. The `encode` and `decode` methods do things that may be undefined behavior, and you shouldn't stand for that. Specifically, `encode` exposes padding bytes to `memcpy`, and `decode` doesn't much respect alignment and may need to construct Rust references to invalid data. Please consult the [abomonation documentation](https://frankmcsherry.github.com/abomonation) for more specific information. From e4a4963073b32646169a6cc8637447ea7adf6aa4 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Thu, 19 Sep 2019 18:23:26 +0200 Subject: [PATCH 10/18] Remove outdated Abomonable notion --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8afb905..13de90c 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Be warned that these numbers are not *goodput*, but rather the total number of b ## unsafe_abomonate! -Abomonation comes with the `unsafe_abomonate!` macro implementing `Abomonation` for structs which are essentially equivalent to a tuple of other `Abomonable` types. To use the macro, you must put the `#[macro_use]` modifier before `extern crate abomonation;`. +Abomonation comes with the `unsafe_abomonate!` macro implementing `Abomonation` for structs which are essentially equivalent to a tuple of other `Abomonation` types. To use the macro, you must put the `#[macro_use]` modifier before `extern crate abomonation;`. Please note that `unsafe_abomonate!` synthesizes unsafe implementations of `Abomonation`, and it is should be considered unsafe to invoke. @@ -82,4 +82,4 @@ if let Some((result, remaining)) = unsafe { decode::(&mut bytes) } { } ``` -Be warned that implementing `Abomonable` for types can be a giant disaster and is entirely discouraged. +Be warned that implementing `Abomonation` for types can be a giant disaster and is entirely discouraged. From b8629a300d1bc71bb8e5f8f47ec81931fe2ce399 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Fri, 20 Sep 2019 01:11:12 +0200 Subject: [PATCH 11/18] Make sure that black_box does its job --- benches/bench.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/bench.rs b/benches/bench.rs index 9ce8a3c..c106228 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -40,7 +40,7 @@ fn _bench_enc(bencher: &mut Bencher, record: T) { bencher.bytes = bytes.len() as u64; bencher.iter(|| { bytes.clear(); - unsafe { encode(&record, &mut bytes).unwrap(); } + unsafe { encode(&record, &mut bytes).unwrap() } }); } From 48b2b9aa9d64cff1bd25b264fd9a8c092e30b4bb Mon Sep 17 00:00:00 2001 From: Hadrien G Date: Thu, 26 Sep 2019 07:49:52 +0200 Subject: [PATCH 12/18] Remove some strange whitespace --- benches/bench.rs | 2 -- benches/clone.rs | 2 -- benches/recycler.rs | 2 -- 3 files changed, 6 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index c106228..4492541 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -31,7 +31,6 @@ use test::Bencher; #[bench] fn vec_u_vn_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); } fn _bench_enc(bencher: &mut Bencher, record: T) { - // prepare encoded data for bencher.bytes let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } @@ -45,7 +44,6 @@ fn _bench_enc(bencher: &mut Bencher, record: T) { } fn _bench_dec(bencher: &mut Bencher, record: T) { - // prepare encoded data let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } diff --git a/benches/clone.rs b/benches/clone.rs index a040895..a5f5b2e 100644 --- a/benches/clone.rs +++ b/benches/clone.rs @@ -28,7 +28,6 @@ use test::Bencher; #[bench] fn vec_u_vn_s_cln(bencher: &mut Bencher) { _bench_cln(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); } fn _bench_e_d(bencher: &mut Bencher, record: T) { - // prepare encoded data for bencher.bytes let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } @@ -43,7 +42,6 @@ fn _bench_e_d(bencher: &mut Bencher, record: T) { } fn _bench_cln(bencher: &mut Bencher, record: T) { - // prepare encoded data let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } diff --git a/benches/recycler.rs b/benches/recycler.rs index cc9ae94..96391b5 100644 --- a/benches/recycler.rs +++ b/benches/recycler.rs @@ -28,7 +28,6 @@ use test::Bencher; // #[bench] fn vec_u_vn_s_rec(bencher: &mut Bencher) { _bench_rec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); } fn _bench_own(bencher: &mut Bencher, record: T) { - // prepare encoded data let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } @@ -43,7 +42,6 @@ fn _bench_own(bencher: &mut Bencher, record: T) { fn _bench_rec(bencher: &mut Bencher, record: T) { - // prepare encoded data let mut bytes = Vec::new(); unsafe { encode(&record, &mut bytes).unwrap(); } From 7388af5ab46724ef68398d79e5206cd08c33be81 Mon Sep 17 00:00:00 2001 From: Hadrien G Date: Thu, 26 Sep 2019 09:39:22 +0200 Subject: [PATCH 13/18] Snipe some unnecessary usage of Ok(()) --- src/lib.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e5750eb..3c1c817 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,8 +75,7 @@ pub mod abomonated; pub unsafe fn encode(typed: &T, write: &mut W) -> IOResult<()> { let slice = std::slice::from_raw_parts(mem::transmute(typed), mem::size_of::()); write.write_all(slice)?; - typed.entomb(write)?; - Ok(()) + typed.entomb(write) } /// Decodes a mutable binary slice into an immutable typed reference. @@ -262,7 +261,7 @@ macro_rules! tuple_abomonate { #[allow(non_snake_case)] #[inline(always)] unsafe fn entomb(&self, write: &mut WRITE) -> IOResult<()> { let ($(ref $ty,)*) = *self; - $($ty.entomb(write)?;)* + $( $ty.entomb(write)?; )* Ok(()) } @@ -356,10 +355,9 @@ impl Abomonation for Option { impl Abomonation for Result { #[inline(always)] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { match self { - &Ok(ref inner) => inner.entomb(write)?, - &Err(ref inner) => inner.entomb(write)?, - }; - Ok(()) + &Ok(ref inner) => inner.entomb(write), + &Err(ref inner) => inner.entomb(write), + } } #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { @@ -476,8 +474,7 @@ array_abomonate!(32); impl Abomonation for String { #[inline] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { - write.write_all(self.as_bytes())?; - Ok(()) + write.write_all(self.as_bytes()) } #[inline] @@ -531,8 +528,7 @@ impl Abomonation for Box { #[inline] unsafe fn entomb(&self, bytes: &mut W) -> IOResult<()> { bytes.write_all(std::slice::from_raw_parts(mem::transmute(&**self), mem::size_of::()))?; - (**self).entomb(bytes)?; - Ok(()) + (**self).entomb(bytes) } #[inline] From 3f847833809249459a65b57b143b2b08776bce70 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Fri, 20 Sep 2019 01:11:30 +0200 Subject: [PATCH 14/18] Don't use manual inlining where benchmarks don't show a benefit --- src/lib.rs | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3c1c817..dc1a5e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,6 @@ pub unsafe fn encode(typed: &T, write: &mut W) -> IORe /// assert!(remaining.len() == 0); /// } /// ``` -#[inline] pub unsafe fn decode(bytes: &mut [u8]) -> Option<(&T, &mut [u8])> { if bytes.len() < mem::size_of::() { None } else { @@ -139,7 +138,6 @@ pub unsafe fn decode(bytes: &mut [u8]) -> Option<(&T, &mut [u8]) /// # Safety /// /// The `measure` method is safe. It neither produces nor consults serialized representations. -#[inline] pub fn measure(typed: &T) -> usize { mem::size_of::() + typed.extent() } @@ -163,7 +161,7 @@ pub trait Abomonation { /// /// Most commonly this is owned data on the other end of pointers in `&self`. The return value /// reports any failures in writing to `write`. - #[inline(always)] unsafe fn entomb(&self, _write: &mut W) -> IOResult<()> { Ok(()) } + unsafe fn entomb(&self, _write: &mut W) -> IOResult<()> { Ok(()) } /// Recover any information for `self_` not evident from its binary representation. /// @@ -175,10 +173,10 @@ pub trait Abomonation { /// `exhume` using raw pointer operations as much as feasible. // // FIXME: Replace self_ with self once Rust has arbitrary self types - #[inline(always)] unsafe fn exhume<'a>(_self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { Some(bytes) } + unsafe fn exhume<'a>(_self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { Some(bytes) } /// Reports the number of further bytes required to entomb `self`. - #[inline(always)] fn extent(&self) -> usize { 0 } + fn extent(&self) -> usize { 0 } } /// The `unsafe_abomonate!` macro takes a type name with an optional list of fields, and implements @@ -230,12 +228,12 @@ macro_rules! unsafe_abomonate { }; ($t:ty : $($field:ident),*) => { impl Abomonation for $t { - #[inline] unsafe fn entomb(&self, write: &mut W) -> ::std::io::Result<()> { + unsafe fn entomb(&self, write: &mut W) -> ::std::io::Result<()> { $( self.$field.entomb(write)?; )* Ok(()) } - #[inline] unsafe fn exhume<'a>(self_: ::std::ptr::NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { + unsafe fn exhume<'a>(self_: ::std::ptr::NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { $( // FIXME: This (briefly) constructs an &mut _ to invalid data, which is UB. // The proposed &raw mut operator would allow avoiding this. @@ -245,7 +243,7 @@ macro_rules! unsafe_abomonate { Some(bytes) } - #[inline] fn extent(&self) -> usize { + fn extent(&self) -> usize { let mut size = 0; $( size += self.$field.extent(); )* size @@ -259,14 +257,14 @@ macro_rules! tuple_abomonate { ( $($ty:ident)+) => ( impl<$($ty: Abomonation),*> Abomonation for ($($ty,)*) { #[allow(non_snake_case)] - #[inline(always)] unsafe fn entomb(&self, write: &mut WRITE) -> IOResult<()> { + unsafe fn entomb(&self, write: &mut WRITE) -> IOResult<()> { let ($(ref $ty,)*) = *self; $( $ty.entomb(write)?; )* Ok(()) } #[allow(non_snake_case)] - #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { // FIXME: This (briefly) constructs a "ref mut" to invalid data, which is UB. // I think avoiding this would require a cleaner way to iterate over tuple fields. // One possibility would be a C++11-style combination of variadic generics and recursion. @@ -279,7 +277,7 @@ macro_rules! tuple_abomonate { } #[allow(non_snake_case)] - #[inline(always)] fn extent(&self) -> usize { + fn extent(&self) -> usize { let mut size = 0; let ($(ref $ty,)*) = *self; $( size += $ty.extent(); )* @@ -330,14 +328,14 @@ impl Abomonation for ::std::time::Duration { } impl Abomonation for PhantomData {} impl Abomonation for Option { - #[inline(always)] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { + unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { if let &Some(ref inner) = self { inner.entomb(write)?; } Ok(()) } - #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { // FIXME: This (briefly) constructs a "ref mut" to invalid data, which is UB. // I'm not sure if this can be fully resolved without relying on enum implementation details. if let Some(ref mut inner) = *self_.as_ptr() { @@ -347,20 +345,20 @@ impl Abomonation for Option { Some(bytes) } - #[inline] fn extent(&self) -> usize { + fn extent(&self) -> usize { self.as_ref().map(|inner| inner.extent()).unwrap_or(0) } } impl Abomonation for Result { - #[inline(always)] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { + unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { match self { &Ok(ref inner) => inner.entomb(write), &Err(ref inner) => inner.entomb(write), } } - #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { + unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { // FIXME: This (briefly) constructs a "ref mut" to invalid data, which is UB. // I'm not sure if this can be fully resolved without relying on enum implementation details. match *self_.as_ptr() { @@ -375,7 +373,7 @@ impl Abomonation for Result { } } - #[inline] fn extent(&self) -> usize { + fn extent(&self) -> usize { match self { &Ok(ref inner) => inner.extent(), &Err(ref inner) => inner.extent(), @@ -416,21 +414,18 @@ tuple_abomonate!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD tuple_abomonate!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD AE); tuple_abomonate!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD AE AF); - macro_rules! array_abomonate { ($size:expr) => ( impl Abomonation for [T; $size] { - #[inline(always)] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { entomb_slice(&self[..], write) } - #[inline(always)] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { exhume_slice(self_.as_ptr() as *mut T, $size, bytes) } - #[inline(always)] fn extent(&self) -> usize { + fn extent(&self) -> usize { slice_extent(&self[..]) } } @@ -472,7 +467,6 @@ array_abomonate!(31); array_abomonate!(32); impl Abomonation for String { - #[inline] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { write.write_all(self.as_bytes()) } @@ -490,13 +484,12 @@ impl Abomonation for String { } } - #[inline] fn extent(&self) -> usize { + fn extent(&self) -> usize { self.len() } } impl Abomonation for Vec { - #[inline] unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { write.write_all(typed_to_bytes(&self[..]))?; entomb_slice(&self[..], write) @@ -518,20 +511,17 @@ impl Abomonation for Vec { } } - #[inline] fn extent(&self) -> usize { mem::size_of::() * self.len() + slice_extent(&self[..]) } } impl Abomonation for Box { - #[inline] unsafe fn entomb(&self, bytes: &mut W) -> IOResult<()> { bytes.write_all(std::slice::from_raw_parts(mem::transmute(&**self), mem::size_of::()))?; (**self).entomb(bytes) } - #[inline] unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { let binary_len = mem::size_of::(); if binary_len > bytes.len() { None } @@ -544,13 +534,13 @@ impl Abomonation for Box { } } - #[inline] fn extent(&self) -> usize { + fn extent(&self) -> usize { mem::size_of::() + (&**self).extent() } } // This method currently enables undefined behavior, by exposing padding bytes. -#[inline] unsafe fn typed_to_bytes(slice: &[T]) -> &[u8] { +unsafe fn typed_to_bytes(slice: &[T]) -> &[u8] { std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::()) } @@ -587,4 +577,4 @@ mod network { impl Abomonation for SocketAddr { } impl Abomonation for SocketAddrV4 { } impl Abomonation for SocketAddrV6 { } -} \ No newline at end of file +} From cb8e2af5eb9810e4eda4156eecf33cac2d998f7b Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Fri, 20 Sep 2019 01:38:36 +0200 Subject: [PATCH 15/18] Add inline(always) where it matters in benchmarks --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index dc1a5e6..c0aa555 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ pub mod abomonated; /// } /// ``` /// -#[inline] +#[inline(always)] pub unsafe fn encode(typed: &T, write: &mut W) -> IOResult<()> { let slice = std::slice::from_raw_parts(mem::transmute(typed), mem::size_of::()); write.write_all(slice)?; From 17a03542b2c0b64d67e8859e6ac0543695fa2bc3 Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Fri, 20 Sep 2019 01:56:57 +0200 Subject: [PATCH 16/18] Avoid multiple codegen units in benchmarks --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7c2f62d..9ee1ca1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,9 @@ license = "MIT" [dev-dependencies] recycler="0.1.4" + +[profile.bench] +# Multiple codegen units speed up compilation, but make compilation output less +# deteministic and generally decrease codegen quality through worse inlining. +# Let's turn it off for benchmarking. +codegen-units = 1 From ee283b030f44b98a55bf4514fba4e26f125c988e Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Fri, 20 Sep 2019 11:24:56 +0200 Subject: [PATCH 17/18] Improve Abomonation documentation and make the trait unsafe By its very nature, Abomonation does very unsafe (and UB-ish) things. But we should strive to explain these as well as we can in the current state of the unsafe Rust formalization effort in order to reduce the potential for known misuse. --- src/lib.rs | 114 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c0aa555..a01bac7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -149,14 +149,28 @@ pub fn measure(typed: &T) -> usize { /// /// # Safety /// -/// Abomonation has no safe methods. Please do not call them. They should be called only by +/// `entomb` and `exhume` are not meant to be called directly. They should be called only by /// `encode` and `decode`, each of which impose restrictions on ownership and lifetime of the data -/// they take as input and return as output. +/// they take as input and return as output, thus improving safety. +/// +/// Not all Rust types are abomonable, and one should think carefully about the implications of +/// implementing `Abomonation` for a type. To lend itself to this exercise, a type must... +/// +/// - Provide exhaustive access to its internal representation +/// - Allow reconstruction from said representation +/// - Survive having its heap allocations being silently replaced by inline pointers to +/// the same storage block, as long as only a shared reference is observed. +/// +/// The last point is the reason why `Abomonation` only provides a shared reference to the +/// reconstructed object. Without this, it would be trivial to trigger, say, a `Box` destructor +/// that tries to call `free()` on the inner pointer. But the use of a shared reference only +/// provides minimal sanity, and for types with internal mutability (those with an `UnsafeCell` +/// inside), this precaution is insufficient. `Abomonation` is generally not safely implementable +/// for such types, but may work in particular cases like atomics. /// /// If you are concerned about safety, it may be best to avoid Abomonation all together. It does /// several things that may be undefined behavior, depending on how undefined behavior is defined. -pub trait Abomonation { - +pub unsafe trait Abomonation { /// Write any additional information about `&self` beyond its binary representation. /// /// Most commonly this is owned data on the other end of pointers in `&self`. The return value @@ -224,10 +238,10 @@ pub trait Abomonation { #[deprecated(since="0.5", note="please use the abomonation_derive crate")] macro_rules! unsafe_abomonate { ($t:ty) => { - impl Abomonation for $t { } + unsafe impl Abomonation for $t { } }; ($t:ty : $($field:ident),*) => { - impl Abomonation for $t { + unsafe impl Abomonation for $t { unsafe fn entomb(&self, write: &mut W) -> ::std::io::Result<()> { $( self.$field.entomb(write)?; )* Ok(()) @@ -255,7 +269,7 @@ macro_rules! unsafe_abomonate { // general code for tuples (can't use '0', '1', ... as field identifiers) macro_rules! tuple_abomonate { ( $($ty:ident)+) => ( - impl<$($ty: Abomonation),*> Abomonation for ($($ty,)*) { + unsafe impl<$($ty: Abomonation),*> Abomonation for ($($ty,)*) { #[allow(non_snake_case)] unsafe fn entomb(&self, write: &mut WRITE) -> IOResult<()> { let ($(ref $ty,)*) = *self; @@ -287,47 +301,47 @@ macro_rules! tuple_abomonate { ); } -impl Abomonation for u8 { } -impl Abomonation for u16 { } -impl Abomonation for u32 { } -impl Abomonation for u64 { } -impl Abomonation for u128 { } -impl Abomonation for usize { } +unsafe impl Abomonation for u8 { } +unsafe impl Abomonation for u16 { } +unsafe impl Abomonation for u32 { } +unsafe impl Abomonation for u64 { } +unsafe impl Abomonation for u128 { } +unsafe impl Abomonation for usize { } -impl Abomonation for i8 { } -impl Abomonation for i16 { } -impl Abomonation for i32 { } -impl Abomonation for i64 { } -impl Abomonation for i128 { } -impl Abomonation for isize { } +unsafe impl Abomonation for i8 { } +unsafe impl Abomonation for i16 { } +unsafe impl Abomonation for i32 { } +unsafe impl Abomonation for i64 { } +unsafe impl Abomonation for i128 { } +unsafe impl Abomonation for isize { } -impl Abomonation for NonZeroU8 { } -impl Abomonation for NonZeroU16 { } -impl Abomonation for NonZeroU32 { } -impl Abomonation for NonZeroU64 { } -impl Abomonation for NonZeroU128 { } -impl Abomonation for NonZeroUsize { } +unsafe impl Abomonation for NonZeroU8 { } +unsafe impl Abomonation for NonZeroU16 { } +unsafe impl Abomonation for NonZeroU32 { } +unsafe impl Abomonation for NonZeroU64 { } +unsafe impl Abomonation for NonZeroU128 { } +unsafe impl Abomonation for NonZeroUsize { } -impl Abomonation for NonZeroI8 { } -impl Abomonation for NonZeroI16 { } -impl Abomonation for NonZeroI32 { } -impl Abomonation for NonZeroI64 { } -impl Abomonation for NonZeroI128 { } -impl Abomonation for NonZeroIsize { } +unsafe impl Abomonation for NonZeroI8 { } +unsafe impl Abomonation for NonZeroI16 { } +unsafe impl Abomonation for NonZeroI32 { } +unsafe impl Abomonation for NonZeroI64 { } +unsafe impl Abomonation for NonZeroI128 { } +unsafe impl Abomonation for NonZeroIsize { } -impl Abomonation for f32 { } -impl Abomonation for f64 { } +unsafe impl Abomonation for f32 { } +unsafe impl Abomonation for f64 { } -impl Abomonation for bool { } -impl Abomonation for () { } +unsafe impl Abomonation for bool { } +unsafe impl Abomonation for () { } -impl Abomonation for char { } +unsafe impl Abomonation for char { } -impl Abomonation for ::std::time::Duration { } +unsafe impl Abomonation for ::std::time::Duration { } -impl Abomonation for PhantomData {} +unsafe impl Abomonation for PhantomData {} -impl Abomonation for Option { +unsafe impl Abomonation for Option { unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { if let &Some(ref inner) = self { inner.entomb(write)?; @@ -350,7 +364,7 @@ impl Abomonation for Option { } } -impl Abomonation for Result { +unsafe impl Abomonation for Result { unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { match self { &Ok(ref inner) => inner.entomb(write), @@ -416,7 +430,7 @@ tuple_abomonate!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD macro_rules! array_abomonate { ($size:expr) => ( - impl Abomonation for [T; $size] { + unsafe impl Abomonation for [T; $size] { unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { entomb_slice(&self[..], write) } @@ -466,7 +480,7 @@ array_abomonate!(30); array_abomonate!(31); array_abomonate!(32); -impl Abomonation for String { +unsafe impl Abomonation for String { unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { write.write_all(self.as_bytes()) } @@ -489,7 +503,7 @@ impl Abomonation for String { } } -impl Abomonation for Vec { +unsafe impl Abomonation for Vec { unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { write.write_all(typed_to_bytes(&self[..]))?; entomb_slice(&self[..], write) @@ -516,7 +530,7 @@ impl Abomonation for Vec { } } -impl Abomonation for Box { +unsafe impl Abomonation for Box { unsafe fn entomb(&self, bytes: &mut W) -> IOResult<()> { bytes.write_all(std::slice::from_raw_parts(mem::transmute(&**self), mem::size_of::()))?; (**self).entomb(bytes) @@ -570,11 +584,11 @@ mod network { use Abomonation; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr}; - impl Abomonation for IpAddr { } - impl Abomonation for Ipv4Addr { } - impl Abomonation for Ipv6Addr { } + unsafe impl Abomonation for IpAddr { } + unsafe impl Abomonation for Ipv4Addr { } + unsafe impl Abomonation for Ipv6Addr { } - impl Abomonation for SocketAddr { } - impl Abomonation for SocketAddrV4 { } - impl Abomonation for SocketAddrV6 { } + unsafe impl Abomonation for SocketAddr { } + unsafe impl Abomonation for SocketAddrV4 { } + unsafe impl Abomonation for SocketAddrV6 { } } From 6c12b2e05c5325e88335f7e90fe119a26f8da0fa Mon Sep 17 00:00:00 2001 From: Hadrien Grasland Date: Fri, 20 Sep 2019 11:58:05 +0200 Subject: [PATCH 18/18] Take writer by value --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a01bac7..aba9051 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,10 +72,10 @@ pub mod abomonated; /// ``` /// #[inline(always)] -pub unsafe fn encode(typed: &T, write: &mut W) -> IOResult<()> { +pub unsafe fn encode(typed: &T, mut write: W) -> IOResult<()> { let slice = std::slice::from_raw_parts(mem::transmute(typed), mem::size_of::()); write.write_all(slice)?; - typed.entomb(write) + typed.entomb(&mut write) } /// Decodes a mutable binary slice into an immutable typed reference.