Skip to content

Commit

Permalink
Add support for [T] and &[mut] [T]
Browse files Browse the repository at this point in the history
  • Loading branch information
HadrienG2 committed Sep 26, 2019
1 parent 72dc70d commit dababc1
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 24 deletions.
102 changes: 78 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,19 +429,37 @@ 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);

unsafe impl<T: Abomonation> Abomonation for [T] {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
for element in self { T::entomb(element, write)?; }
Ok(())
}

unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
// FIXME: This 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();
exhume_slice(self_.as_ptr() as *mut T, self_len, bytes)
}

fn extent(&self) -> usize {
self.iter().map(T::extent).sum()
}
}

macro_rules! array_abomonate {
($size:expr) => (
unsafe impl<T: Abomonation> Abomonation for [T; $size] {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
entomb_slice(&self[..], write)
<[T]>::entomb(&self[..], write)
}

unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> {
exhume_slice(self_.as_ptr() as *mut T, $size, bytes)
}

fn extent(&self) -> usize {
slice_extent(&self[..])
<[T]>::extent(&self[..])
}
}
)
Expand Down Expand Up @@ -541,30 +559,64 @@ unsafe impl Abomonation for String {
}
}

unsafe impl<T: Abomonation> Abomonation for Vec<T> {
unsafe impl<T: Abomonation> Abomonation for &'_ [T] {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
write.write_all(typed_to_bytes(&self[..]))?;
entomb_slice(&self[..], write)
<[T]>::entomb(&self[..], write)
}

#[inline]
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 (s, rest) = exhume_slice_ref(self_len, bytes)?;
self_.as_ptr().write(s.as_ref());
Some(rest)
}

fn extent(&self) -> usize {
mem::size_of::<T>() * self.len() + <[T]>::extent(&self[..])
}
}

unsafe impl<T: Abomonation> Abomonation for &'_ mut [T] {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
<&[T]>::entomb(&&self[..], write)
}

#[inline]
unsafe fn exhume<'a>(self_: NonNull<Self>, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> {
// FIXME: This (briefly) constructs an &mut [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 (mut s, rest) = exhume_slice_ref(self_len, bytes)?;
self_.as_ptr().write(s.as_mut());
Some(rest)
}

fn extent(&self) -> usize {
<&[T]>::extent(&&self[..])
}
}

unsafe impl<T: Abomonation> Abomonation for Vec<T> {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
<&[T]>::entomb(&&self[..], 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 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;
rest = exhume_slice(first_ptr, self_len, rest)?;
self_.as_ptr().write(Vec::from_raw_parts(first_ptr, self_len, self_len));
Some(rest)
}
let (mut s, rest) = exhume_slice_ref(self_len, bytes)?;
self_.as_ptr().write(Vec::from_raw_parts(s.as_mut().as_mut_ptr(), self_len, self_len));
Some(rest)
}

fn extent(&self) -> usize {
mem::size_of::<T>() * self.len() + slice_extent(&self[..])
<&[T]>::extent(&&self[..])
}
}

Expand Down Expand Up @@ -622,14 +674,8 @@ unsafe fn typed_to_bytes<T>(slice: &[T]) -> &[u8] {
std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}

// Common subset of "entomb" for all [T]-like types
unsafe fn entomb_slice<T: Abomonation, W: Write>(slice: &[T], write: &mut W) -> IOResult<()> {
for element in slice { T::entomb(element, 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)
// (I'd gladly move this to [T]::extent, but building a NonNull<[T]> is currently too difficult)
#[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 {
Expand All @@ -639,9 +685,17 @@ unsafe fn exhume_slice<'a, T: Abomonation>(first_ptr: *mut T, length: usize, mut
Some(bytes)
}

// Common subset of "extent" for all [T]-like types
fn slice_extent<T: Abomonation>(slice: &[T]) -> usize {
slice.iter().map(T::extent).sum()
// Common subset of "exhume" for all &[T]-like types
#[inline]
unsafe fn exhume_slice_ref<'a, T:Abomonation>(length: usize, bytes: &'a mut [u8]) -> Option<(NonNull<[T]>, &'a mut [u8])> {
let binary_len = length * 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;
rest = exhume_slice(first_ptr, length, rest)?;
Some((std::slice::from_raw_parts_mut(first_ptr, length).into(), rest))
}
}

// Common subset of "exhume" for all NonNull<T>-like types
Expand Down
3 changes: 3 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ use std::fmt::Debug;
#[test] fn test_vec_u_s_pass() { _test_pass(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[test] fn test_ref_u64_pass() { _test_pass(vec![Some(&42u64); 1024]); }
#[test] fn test_str_pass() { _test_pass(vec![&"grawwwwrr!"; 1024]); }
#[test] fn test_slice_pass() { _test_pass(vec![&[0, 1, 2]; 1024]); }

#[test] fn test_u64_fail() { _test_fail(vec![0u64; 1024]); }
#[test] fn test_u128_fail() { _test_fail(vec![0u128; 1024]); }
#[test] fn test_string_fail() { _test_fail(vec![format!("grawwwwrr!"); 1024]); }
#[test] fn test_vec_u_s_fail() { _test_fail(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[test] fn test_ref_u64_fail() { _test_fail(vec![Some(&42u64); 1024]); }
#[test] fn test_str_fail() { _test_fail(vec![&"grawwwwrr!"; 1024]); }
#[test] fn test_slice_fail() { _test_fail(vec![&[0, 1, 2]; 1024]); }

#[test] fn test_array_size() { _test_size(vec![[0, 1, 2]; 1024]); }
#[test] fn test_opt_vec_size() { _test_size(vec![Some(vec![0,1,2]), None]); }
Expand All @@ -37,6 +39,7 @@ use std::fmt::Debug;
#[test] fn test_vec_u_s_size() { _test_size(vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[test] fn test_ref_u64_size() { _test_size(vec![Some(&42u64); 1024]); }
#[test] fn test_str_size() { _test_size(vec![&"grawwwwrr!"; 1024]); }
#[test] fn test_slice_size() { _test_fail(vec![&[0, 1, 2]; 1024]); }

#[test]
fn test_phantom_data_for_non_abomonatable_type() {
Expand Down

0 comments on commit dababc1

Please sign in to comment.