Skip to content

Commit

Permalink
Extract slice abomonation and add some air
Browse files Browse the repository at this point in the history
  • Loading branch information
HadrienG2 committed Sep 19, 2019
1 parent d9dc38c commit dbd56e9
Showing 1 changed file with 39 additions and 27 deletions.
66 changes: 39 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,17 @@ macro_rules! unsafe_abomonate {
$( self.$field.entomb(write)?; )*
Ok(())
}

#[inline] unsafe fn exhume<'a>(mut self_: ::std::ptr::NonNull<Self>, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {
$(
// FIXME: This constructs an &mut T to invalid data, which is UB.
// 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(); )*
Expand All @@ -263,18 +265,20 @@ macro_rules! tuple_abomonate {
$($t.entomb(write)?;)*
Ok(())
}

#[allow(non_snake_case)]
#[inline(always)] unsafe fn exhume<'a>(mut self_: NonNull<Self>, mut bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {
// FIXME: This constructs a "ref mut" to invalid data, which is UB.
// 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 $t,)*) = *self_.as_mut();
$(
let field_ptr : NonNull<$t> = From::from( $t );
bytes = $t::exhume(field_ptr, bytes)?;
let field_ptr : NonNull<_> = From::from( $t );
bytes = Abomonation::exhume(field_ptr, bytes)?;
)*
Some(bytes)
}

#[allow(non_snake_case)]
#[inline(always)] fn extent(&self) -> usize {
let mut size = 0;
Expand Down Expand Up @@ -333,15 +337,17 @@ impl<T: Abomonation> Abomonation for Option<T> {
}
Ok(())
}

#[inline(always)] unsafe fn exhume<'a>(self_: NonNull<Self>, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
// FIXME: This constructs a "ref mut" to invalid data, which is UB.
// 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<T> = 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)
}
Expand All @@ -355,8 +361,9 @@ impl<T: Abomonation, E: Abomonation> Abomonation for Result<T, E> {
};
Ok(())
}

#[inline(always)] unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
// FIXME: This constructs a "ref mut" to invalid data, which is UB.
// 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) => {
Expand All @@ -369,6 +376,7 @@ impl<T: Abomonation, E: Abomonation> Abomonation for Result<T, E> {
}
}
}

#[inline] fn extent(&self) -> usize {
match self {
&Ok(ref inner) => inner.extent(),
Expand Down Expand Up @@ -418,16 +426,11 @@ impl<T: Abomonation> Abomonation for [T] {
}

#[inline(always)]
unsafe fn exhume<'a>(self_: NonNull<Self>, mut bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
// FIXME: This constructs an &[T] to invalid data, which is UB.
unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
// FIXME: This (briefly) constructs an &[T] to invalid data, which is UB.
// I'm not sure if this can be fully resolved without relying on slice implementation details.
let self_len = self_.as_ref().len();
let first_ptr = self_.as_ptr() as *mut T;
for i in 0..self_len {
let element_ptr: NonNull<T> = NonNull::new_unchecked( first_ptr.add(i) );
bytes = T::exhume( element_ptr, bytes )?;
}
Some(bytes)
exhume_slice( self_.as_ptr() as *mut T, self_len, bytes )
}

#[inline(always)] fn extent(&self) -> usize {
Expand All @@ -443,15 +446,12 @@ macro_rules! array_abomonate {
let slice: &[T] = self.as_ref();
slice.entomb(write)
}

#[inline(always)]
unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
let first_ptr = self_.as_ptr() as *mut T;
// FIXME: This (briefly) constructs an &mut [T] to invalid data, which is UB.
// Maybe we should have a way to construct a *mut [T] without going through an &mut [T]?
// For now, one can avoid this by extracting <[T]>::exhume into a lower-level building block (TODO)
let slice_ptr : NonNull<[T]> = From::from( std::slice::from_raw_parts_mut( first_ptr, $size ) );
<[T]>::exhume( slice_ptr, bytes )
exhume_slice( self_.as_ptr() as *mut T, $size, bytes )
}

#[inline(always)] fn extent(&self) -> usize {
let slice: &[T] = self.as_ref();
slice.extent()
Expand Down Expand Up @@ -500,10 +500,11 @@ impl Abomonation for String {
write.write_all(self.as_bytes())?;
Ok(())
}

#[inline]
unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {
// FIXME: This (briefly) constructs an &String to invalid data, which is UB.
// I'm not sure if this can be resolved without relying on String implementation details.
// 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 {
Expand All @@ -512,6 +513,7 @@ impl Abomonation for String {
Some(rest)
}
}

#[inline] fn extent(&self) -> usize {
self.len()
}
Expand All @@ -525,24 +527,22 @@ impl<T: Abomonation> Abomonation for Vec<T> {
slice.entomb( write )
}
#[inline]

unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {
// FIXME: This (briefly) constructs an &Vec<T> to invalid data, which is UB.
// I'm not sure it this can be resolved without relying on Vec implementation details.
// 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::<T>();
if binary_len > bytes.len() { None }
else {
let (mine, mut rest) = bytes.split_at_mut(binary_len);
let first_ptr = mine.as_mut_ptr() as *mut T;
// FIXME: This (briefly) constructs an &mut [T] to invalid data, which is UB.
// Maybe we should have a way to construct a *mut [T] without going through an &mut [T]?
// For now, one can avoid this by extracting <[T]>::exhume into a lower-level building block (TODO)
let slice_ptr : NonNull<[T]> = From::from( std::slice::from_raw_parts_mut( first_ptr, self_len ) );
rest = <[T]>::exhume( slice_ptr, rest )?;
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 slice : &[T] = self.as_ref();
Expand All @@ -557,6 +557,7 @@ impl<T: Abomonation> Abomonation for Box<T> {
(**self).entomb(write)?;
Ok(())
}

#[inline]
unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {
let binary_len = mem::size_of::<T>();
Expand All @@ -569,6 +570,7 @@ impl<T: Abomonation> Abomonation for Box<T> {
Some(rest)
}
}

#[inline] fn extent(&self) -> usize {
mem::size_of::<T>() + (&**self).extent()
}
Expand All @@ -579,6 +581,16 @@ impl<T: Abomonation> Abomonation for Box<T> {
std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}

// Originally part of `impl Abomonation for [T]`, this lower-level building block was extracted
// because it is currently too difficult to build and use an *mut [T] safely in Rust.
#[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<T> = NonNull::new_unchecked( first_ptr.add(i) );
bytes = T::exhume( element_ptr, bytes )?;
}
Some(bytes)
}

mod network {
use Abomonation;
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr};
Expand Down

0 comments on commit dbd56e9

Please sign in to comment.