diff --git a/src/lib.rs b/src/lib.rs index 1a65ffd..01c0d1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -429,11 +429,29 @@ 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 Abomonation for [T] { + unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { + for element in self { T::entomb(element, write)?; } + Ok(()) + } + + unsafe fn exhume<'a>(self_: NonNull, 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 Abomonation for [T; $size] { unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { - entomb_slice(&self[..], write) + <[T]>::entomb(&self[..], write) } unsafe fn exhume<'a>(self_: NonNull, bytes: &'a mut[u8]) -> Option<&'a mut [u8]> { @@ -441,7 +459,7 @@ macro_rules! array_abomonate { } fn extent(&self) -> usize { - slice_extent(&self[..]) + <[T]>::extent(&self[..]) } } ) @@ -541,10 +559,50 @@ unsafe impl Abomonation for String { } } -unsafe impl Abomonation for Vec { +unsafe impl Abomonation for &'_ [T] { unsafe fn entomb(&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, 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::() * self.len() + <[T]>::extent(&self[..]) + } +} + +unsafe impl Abomonation for &'_ mut [T] { + unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { + <&[T]>::entomb(&&self[..], write) + } + + #[inline] + unsafe fn exhume<'a>(self_: NonNull, 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 Abomonation for Vec { + unsafe fn entomb(&self, write: &mut W) -> IOResult<()> { + <&[T]>::entomb(&&self[..], write) } #[inline] @@ -552,19 +610,13 @@ unsafe impl Abomonation for Vec { // 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 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::() * self.len() + slice_extent(&self[..]) + <&[T]>::extent(&&self[..]) } } @@ -622,14 +674,8 @@ unsafe fn typed_to_bytes(slice: &[T]) -> &[u8] { 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 { 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 { @@ -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(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::(); + 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-like types diff --git a/tests/tests.rs b/tests/tests.rs index cb733e3..c8f4b98 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -17,6 +17,7 @@ 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]); } @@ -24,6 +25,7 @@ use std::fmt::Debug; #[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]); } @@ -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() {