diff --git a/src/from_fn.rs b/src/from_fn.rs index b10f87d..0edde10 100644 --- a/src/from_fn.rs +++ b/src/from_fn.rs @@ -24,6 +24,7 @@ where /// /// Propagates the `E` type returned from the provided `F` in the event of error. pub fn try_from_fn(f: impl FnMut(usize) -> Result) -> Result { + core::convert::identity(U::__CHECK_INVARIANT); let mut array = Array::, U>::uninit(); try_from_fn_erased(array.0.as_mut(), f)?; diff --git a/src/lib.rs b/src/lib.rs index 4c8e037..28c2212 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,7 +107,7 @@ use core::{ ptr, slice::{self, Iter, IterMut}, }; -use typenum::{Diff, Sum}; +use typenum::{Diff, Sum, Unsigned}; #[cfg(feature = "zeroize")] use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -207,7 +207,7 @@ where U: Add, Sum: ArraySize, { - self.into_iter().chain(other.into_iter()).collect() + self.into_iter().chain(other).collect() } /// Splits `self` at index `N` in two arrays. @@ -223,7 +223,7 @@ where unsafe { let array = ManuallyDrop::new(self); let head = ptr::read(array.as_ptr().cast()); - let tail = ptr::read(array.as_ptr().add(N::USIZE).cast()); + let tail = ptr::read(array.as_ptr().add(::USIZE).cast()); (head, tail) } } @@ -239,7 +239,7 @@ where unsafe { let array_ptr = self.as_ptr(); let head = &*array_ptr.cast(); - let tail = &*array_ptr.add(N::USIZE).cast(); + let tail = &*array_ptr.add(::USIZE).cast(); (head, tail) } } @@ -255,7 +255,7 @@ where unsafe { let array_ptr = self.as_mut_ptr(); let head = &mut *array_ptr.cast(); - let tail = &mut *array_ptr.add(N::USIZE).cast(); + let tail = &mut *array_ptr.add(::USIZE).cast(); (head, tail) } } @@ -268,12 +268,16 @@ where #[allow(clippy::arithmetic_side_effects)] #[inline] pub fn slice_as_chunks(buf: &[T]) -> (&[Self], &[T]) { - assert_ne!(U::USIZE, 0, "chunk size must be non-zero"); + assert_ne!( + ::USIZE, + 0, + "chunk size must be non-zero" + ); // Arithmetic safety: we have checked that `N::USIZE` is not zero, thus // division always returns correct result. `tail_pos` can not be bigger than `buf.len()`, // thus overflow on multiplication and underflow on substraction are impossible. - let chunks_len = buf.len() / U::USIZE; - let tail_pos = U::USIZE * chunks_len; + let chunks_len = buf.len() / ::USIZE; + let tail_pos = ::USIZE * chunks_len; let tail_len = buf.len() - tail_pos; unsafe { let ptr = buf.as_ptr(); @@ -291,12 +295,16 @@ where #[allow(clippy::arithmetic_side_effects)] #[inline] pub fn slice_as_chunks_mut(buf: &mut [T]) -> (&mut [Self], &mut [T]) { - assert_ne!(U::USIZE, 0, "chunk size must be non-zero"); + assert_ne!( + ::USIZE, + 0, + "chunk size must be non-zero" + ); // Arithmetic safety: we have checked that `N::USIZE` is not zero, thus // division always returns correct result. `tail_pos` can not be bigger than `buf.len()`, // thus overflow on multiplication and underflow on substraction are impossible. - let chunks_len = buf.len() / U::USIZE; - let tail_pos = U::USIZE * chunks_len; + let chunks_len = buf.len() / ::USIZE; + let tail_pos = ::USIZE * chunks_len; let tail_len = buf.len() - tail_pos; unsafe { let ptr = buf.as_mut_ptr(); @@ -605,6 +613,7 @@ where { #[inline] fn from(arr: [T; N]) -> Array { + core::convert::identity(U::__CHECK_INVARIANT); Array(arr) } } @@ -766,7 +775,8 @@ where #[inline] fn try_from(slice: &'a [T]) -> Result, TryFromSliceError> { - <&'a Self>::try_from(slice).map(Clone::clone) + core::convert::identity(U::__CHECK_INVARIANT); + <&'a Self>::try_from(slice).cloned() } } @@ -778,6 +788,7 @@ where #[inline] fn try_from(slice: &'a [T]) -> Result { + core::convert::identity(U::__CHECK_INVARIANT); check_slice_length::(slice)?; // SAFETY: `Array` is a `repr(transparent)` newtype for a core @@ -794,6 +805,7 @@ where #[inline] fn try_from(slice: &'a mut [T]) -> Result { + core::convert::identity(U::__CHECK_INVARIANT); check_slice_length::(slice)?; // SAFETY: `Array` is a `repr(transparent)` newtype for a core @@ -825,9 +837,12 @@ where /// Generate a [`TryFromSliceError`] if the slice doesn't match the given length. #[cfg_attr(debug_assertions, allow(clippy::panic_in_result_fn))] fn check_slice_length(slice: &[T]) -> Result<(), TryFromSliceError> { - debug_assert_eq!(Array::<(), U>::default().len(), U::USIZE); + debug_assert_eq!( + Array::<(), U>::default().len(), + ::USIZE + ); - if slice.len() != U::USIZE { + if slice.len() != ::USIZE { // Hack: `TryFromSliceError` lacks a public constructor <&[T; 1]>::try_from([].as_slice())?; diff --git a/src/sizes.rs b/src/sizes.rs index 28888eb..d5f1ec9 100644 --- a/src/sizes.rs +++ b/src/sizes.rs @@ -22,6 +22,7 @@ macro_rules! impl_array_sizes { ($($len:expr => $ty:ident),+ $(,)?) => { $( unsafe impl ArraySize for $ty { + type Size = $ty; type ArrayType = [T; $len]; } diff --git a/src/traits.rs b/src/traits.rs index d5824a9..91a50a9 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,6 +3,7 @@ use crate::Array; use core::{ borrow::{Borrow, BorrowMut}, + mem::size_of, ops::{Index, IndexMut, Range}, }; use typenum::Unsigned; @@ -13,17 +14,23 @@ use typenum::Unsigned; /// # Safety /// /// `ArrayType` MUST be an array with a number of elements exactly equal to -/// [`Unsigned::USIZE`]. Breaking this requirement will cause undefined behavior. -/// -/// NOTE: This trait is effectively sealed and can not be implemented by third-party crates. -/// It is implemented only for a number of types defined in [`typenum::consts`]. -pub unsafe trait ArraySize: Unsigned { +/// [`Size::USIZE`](Unsigned::USIZE). Breaking this requirement will cause undefined behavior. +pub unsafe trait ArraySize: Sized + 'static { + #[doc(hidden)] + const __CHECK_INVARIANT: () = { + let a = ::USIZE; + let b = size_of::>(); + assert!(a == b, "ArraySize invariant violated"); + }; + + /// The size underlying the array. + type Size: Unsigned; + /// Array type which corresponds to this size. /// /// This is always defined to be `[T; N]` where `N` is the same as - /// [`ArraySize::USIZE`][`typenum::Unsigned::USIZE`]. - type ArrayType: AssocArraySize - + AsRef<[T]> + /// [`ArraySize::Size::USIZE`][`typenum::Unsigned::USIZE`]. + type ArrayType: AsRef<[T]> + AsMut<[T]> + Borrow<[T]> + BorrowMut<[T]> diff --git a/tests/custom_size.rs b/tests/custom_size.rs new file mode 100644 index 0000000..5c52cc7 --- /dev/null +++ b/tests/custom_size.rs @@ -0,0 +1,23 @@ +use hybrid_array::Array; + +struct Size54321; + +// This macro constructs a UInt type from a sequence of bits. The bits are interpreted as the +// little-endian representation of the integer in question. For example, uint!(1 1 0 1 0 0 1) is +// U75 (not U105). +macro_rules! uint { + () => { typenum::UTerm }; + (0 $($bs:tt)*) => { typenum::UInt< uint!($($bs)*), typenum::B0 > }; + (1 $($bs:tt)*) => { typenum::UInt< uint!($($bs)*), typenum::B1 > }; +} + +unsafe impl hybrid_array::ArraySize for Size54321 { + type Size = uint!(1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1); + type ArrayType = [T; 54321]; +} + +#[test] +fn from_fn() { + let array = Array::::from_fn(|n| (n + 1) as u8); + assert_eq!(array.as_slice().len(), 54321); +}