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 Oct 1, 2019
1 parent f382b30 commit af3e52d
Show file tree
Hide file tree
Showing 2 changed files with 82 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 @@ -438,19 +438,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<'bytes, T: Abomonation<'bytes>> Abomonation<'bytes> for [T] {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
for element in self { T::entomb(element, write)?; }
Ok(())
}

unsafe fn exhume(self_: NonNull<Self>, bytes: &'bytes mut[u8]) -> Option<&'bytes 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<'bytes, T: Abomonation<'bytes>> Abomonation<'bytes> for [T; $size] {
unsafe fn entomb<W: Write>(&self, write: &mut W) -> IOResult<()> {
entomb_slice(&self[..], write)
<[T]>::entomb(&self[..], write)
}

unsafe fn exhume(self_: NonNull<Self>, bytes: &'bytes mut[u8]) -> Option<&'bytes 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 @@ -550,30 +568,64 @@ unsafe impl<'bytes> Abomonation<'bytes> for String {
}
}

unsafe impl<'bytes, T: Abomonation<'bytes>> Abomonation<'bytes> for Vec<T> {
unsafe impl<'target, 'bytes: 'target, T: Abomonation<'bytes>> Abomonation<'bytes> for &'target [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(self_: NonNull<Self>, bytes: &'bytes mut [u8]) -> Option<&'bytes 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);
Some(rest)
}

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

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

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

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

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

#[inline]
unsafe fn exhume(self_: NonNull<Self>, bytes: &'bytes mut [u8]) -> Option<&'bytes 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 (s, rest) = exhume_slice_ref(self_len, bytes)?;
self_.as_ptr().write(Vec::from_raw_parts(s.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 @@ -631,14 +683,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<'a, T: Abomonation<'a>, 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]::exhume, but building a NonNull<[T]> is currently too difficult)
#[inline]
unsafe fn exhume_slice<'bytes, T: Abomonation<'bytes>>(
first_ptr: *mut T,
Expand All @@ -652,9 +698,17 @@ unsafe fn exhume_slice<'bytes, T: Abomonation<'bytes>>(
Some(bytes)
}

// Common subset of "extent" for all [T]-like types
fn slice_extent<'a, T: Abomonation<'a>>(slice: &[T]) -> usize {
slice.iter().map(T::extent).sum()
// Common subset of "exhume" for all &[T]-like types
#[inline]
unsafe fn exhume_slice_ref<'target, 'bytes: 'target, T:Abomonation<'bytes>>(length: usize, bytes: &'bytes mut [u8]) -> Option<(&'target mut [T], &'bytes 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 &mut T-like types
Expand Down
4 changes: 4 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ gen_tests!{
&"grawwwwrr!" => (test_str_pass,
test_str_fail,
test_str_size),

&[0, 1, 2] => (test_slice_pass,
test_slice_fail,
test_slice_size),
}

fn _test_pass<'bytes, T: Abomonation<'bytes> + Debug + Eq>(
Expand Down

0 comments on commit af3e52d

Please sign in to comment.