diff --git a/src/derive/array.rs b/src/derive/array.rs index 49dc6b2..eac6f83 100644 --- a/src/derive/array.rs +++ b/src/derive/array.rs @@ -1,6 +1,7 @@ use crate::coder::{Buffer, Decoder, Encoder, Result, View}; use crate::consume::mul_length; use crate::derive::{Decode, Encode}; +use crate::fast::{FastSlice, FastVec, Unaligned}; use std::mem::MaybeUninit; use std::num::NonZeroUsize; @@ -15,6 +16,23 @@ impl Default for ArrayEncoder { } impl Encoder<[T; N]> for ArrayEncoder { + fn as_primitive(&mut self) -> Option<&mut FastVec<[T; N]>> { + // FastVec doesn't work on ZST. + if N == 0 { + return None; + } + self.0.as_primitive().map(|v| { + debug_assert!(v.len() % N == 0); + // Safety: FastVec uses pointers for len/cap unlike Vec, so casting to FastVec<[T; N]> + // is safe as long as `v.len() % N == 0`. This will always be the case since we only + // encode in chunks of N. + // NOTE: If panics occurs during ArrayEncoder::encode and Buffer is reused, this + // invariant can be violated. Luckily primitive encoders never panic. + // TODO std::mem::take Buffer while encoding to avoid corrupted buffers. + unsafe { std::mem::transmute(v) } + }) + } + #[inline(always)] fn encode(&mut self, array: &[T; N]) { // TODO use encode_vectored if N is large enough. @@ -59,6 +77,14 @@ impl<'a, T: Decode<'a>, const N: usize> View<'a> for ArrayDecoder<'a, T, N> { } impl<'a, T: Decode<'a>, const N: usize> Decoder<'a, [T; N]> for ArrayDecoder<'a, T, N> { + fn as_primitive(&mut self) -> Option<&mut FastSlice>> { + self.0.as_primitive().map(|s| { + // Safety: FastSlice doesn't have a length unlike slice, so casting to FastSlice<[T; N]> + // is safe. N == 0 case is also safe for the same reason. + unsafe { std::mem::transmute(s) } + }) + } + #[inline(always)] fn decode_in_place(&mut self, out: &mut MaybeUninit<[T; N]>) { // Safety: Equivalent to nightly MaybeUninit::transpose. @@ -68,3 +94,22 @@ impl<'a, T: Decode<'a>, const N: usize> Decoder<'a, [T; N]> for ArrayDecoder<'a, } } } + +#[cfg(test)] +mod tests { + #[test] + fn test_empty_array() { + type T = [u8; 0]; + let empty_array = T::default(); + crate::decode::(&crate::encode(&empty_array)).unwrap(); + crate::decode::>(&crate::encode(&vec![empty_array; 100])).unwrap(); + } + + fn bench_data() -> Vec> { + crate::random_data::(125) + .into_iter() + .map(|n| (0..n / 16).map(|_| [0, 0, 255]).collect()) + .collect() + } + crate::bench_encode_decode!(u8_array_vecs: Vec>); +} diff --git a/src/fast.rs b/src/fast.rs index 4b8e000..e582b47 100644 --- a/src/fast.rs +++ b/src/fast.rs @@ -67,7 +67,7 @@ impl From> for FastVec { } impl FastVec { - fn len(&self) -> usize { + pub fn len(&self) -> usize { sub_ptr(self.end, self.start) } @@ -212,7 +212,7 @@ impl<'a, T: Copy, const N: usize> PushUnchecked for FastArrayVec<'a, T, N> { pub struct FastSlice<'a, T> { ptr: *const T, #[cfg(debug_assertions)] - len: usize, // TODO could store end ptr to allow Debug and as_slice. + end: *const T, // Uses pointer instead of len to permit &mut FastSlice -> &mut FastSlice<[T; N]> cast. _spooky: PhantomData<&'a T>, } @@ -233,7 +233,7 @@ impl<'a, T> From<&'a [T]> for FastSlice<'a, T> { Self { ptr: slice.as_ptr(), #[cfg(debug_assertions)] - len: slice.len(), + end: slice.as_ptr_range().end, _spooky: PhantomData, } } @@ -247,7 +247,7 @@ impl<'a, T> FastSlice<'a, T> { Self { ptr, #[cfg(debug_assertions)] - len, + end: ptr.wrapping_add(len), _spooky: PhantomData, } } @@ -256,9 +256,7 @@ impl<'a, T> FastSlice<'a, T> { #[inline(always)] pub unsafe fn next_unchecked_as_ptr(&mut self) -> *const T { #[cfg(debug_assertions)] - { - self.len = self.len.checked_sub(1).unwrap(); - } + assert!((self.ptr.wrapping_add(1) as usize) <= self.end as usize); let p = self.ptr; self.ptr = self.ptr.add(1); p @@ -267,9 +265,7 @@ impl<'a, T> FastSlice<'a, T> { #[inline(always)] pub unsafe fn advance(&mut self, n: usize) { #[cfg(debug_assertions)] - { - self.len = self.len.checked_sub(n).unwrap(); - } + assert!((self.ptr.wrapping_add(n) as usize) <= self.end as usize); self.ptr = self.ptr.add(n); } @@ -307,9 +303,7 @@ impl<'a, T: Copy> NextUnchecked<'a, T> for FastSlice<'a, T> { #[inline(always)] unsafe fn next_unchecked(&mut self) -> T { #[cfg(debug_assertions)] - { - self.len = self.len.checked_sub(1).unwrap(); - } + assert!((self.ptr.wrapping_add(1) as usize) <= self.end as usize); let t = *self.ptr; self.ptr = self.ptr.add(1); t @@ -318,9 +312,7 @@ impl<'a, T: Copy> NextUnchecked<'a, T> for FastSlice<'a, T> { #[inline(always)] unsafe fn chunk_unchecked(&mut self, length: usize) -> &'a [T] { #[cfg(debug_assertions)] - { - self.len = self.len.checked_sub(length).unwrap(); - } + assert!((self.ptr.wrapping_add(length) as usize) <= self.end as usize); let slice = std::slice::from_raw_parts(self.ptr, length); self.ptr = self.ptr.add(length); slice @@ -373,7 +365,7 @@ impl<'borrowed, T> CowSlice<'borrowed, T> { 'borrowed: 'me, { #[cfg(debug_assertions)] - assert_eq!(self.slice.len, len); + assert_eq!(self.slice.ptr.wrapping_add(len), self.slice.end); std::slice::from_raw_parts(self.slice.ptr, len) }