//! Trait definitions. use crate::Array; use core::{ borrow::{Borrow, BorrowMut}, fmt::Debug, ops::{Index, IndexMut, Range}, }; use typenum::Unsigned; /// Trait which associates a [`usize`] size and `ArrayType` with a /// `typenum`-provided [`Unsigned`] integer. /// /// # 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`]. #[diagnostic::on_unimplemented(note = "size may not be supported (see RustCrypto/hybrid-array#66)")] pub unsafe trait ArraySize: Unsigned + Debug { /// 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]> + AsMut<[T]> + Borrow<[T]> + BorrowMut<[T]> + From> + Index + Index> + IndexMut + IndexMut> + Into> + IntoIterator; } /// Associates an [`ArraySize`] with a given type. Can be used to accept `[T; N]` const generic /// arguments and convert to [`Array`] internally. /// /// This trait is also the magic glue that makes the [`ArrayN`][`crate::ArrayN`] type alias work. /// /// # Example /// /// ``` /// use hybrid_array::{ArrayN, AssocArraySize}; /// /// pub fn example(bytes: &[u8; N]) /// where /// [u8; N]: AssocArraySize + AsRef> /// { /// // _arrayn is ArrayN /// let _arrayn = bytes.as_ref(); /// } /// ``` pub trait AssocArraySize: Sized { /// Size of an array type, expressed as a [`typenum`]-based [`ArraySize`]. type Size: ArraySize; } impl AssocArraySize for Array where U: ArraySize, { type Size = U; } /// Obtain an `&Array` reference for a given type. /// /// This provides functionality equivalent to `AsRef` or `Borrow`, but is deliberately /// implemented as its own trait both so it can leverage [`AssocArraySize`] to determine the /// array size, and also to avoid inference problems that occur when third party impls of traits /// like [`AsRef`] and [`Borrow`] are added to `[T; N]`. /// /// # Usage with `[T; N]` /// /// ``` /// use hybrid_array::{Array, ArraySize, AsArrayRef}; /// /// pub fn getn_hybrid(arr: &Array, n: usize) -> &T { /// &arr[2] /// } /// /// pub fn getn_generic(arr: &[T; N], n: usize) -> &T /// where /// [T; N]: AsArrayRef /// { /// getn_hybrid(arr.as_array_ref(), n) /// } /// /// let array = [0u8, 1, 2, 3]; /// let x = getn_generic(&array, 2); /// assert_eq!(x, &2); /// ``` pub trait AsArrayRef: AssocArraySize { /// Converts this type into an immutable [`Array`] reference. fn as_array_ref(&self) -> &Array; } /// Obtain a `&mut Array` reference for a given type. /// /// Companion trait to [`AsArrayRef`] for mutable references, equivalent to [`AsMut`] or /// [`BorrowMut`]. pub trait AsArrayMut: AsArrayRef { /// Converts this type into a mutable [`Array`] reference. fn as_array_mut(&mut self) -> &mut Array; } impl AsArrayRef for Array where U: ArraySize, { fn as_array_ref(&self) -> &Self { self } } impl AsArrayMut for Array where U: ArraySize, { fn as_array_mut(&mut self) -> &mut Self { self } } impl AsArrayRef for [T; N] where Self: AssocArraySize, U: ArraySize = Self>, { fn as_array_ref(&self) -> &Array { self.into() } } impl AsArrayMut for [T; N] where Self: AssocArraySize, U: ArraySize = Self>, { fn as_array_mut(&mut self) -> &mut Array { self.into() } } /// Extension trait for `[T]` providing methods for working with [`Array`]. pub trait SliceExt: sealed::Sealed { /// Get a reference to an array from a slice, if the slice is exactly the size of the array. /// /// Returns `None` if the slice's length is not exactly equal to the array size. fn as_hybrid_array(&self) -> Option<&Array>; /// Get a mutable reference to an array from a slice, if the slice is exactly the size of the /// array. /// /// Returns `None` if the slice's length is not exactly equal to the array size. fn as_mut_hybrid_array(&mut self) -> Option<&mut Array>; /// Splits the shared slice into a slice of `U`-element arrays, starting at the beginning /// of the slice, and a remainder slice with length strictly less than `U`. /// /// # Panics /// If `U` is 0. fn as_hybrid_chunks(&self) -> (&[Array], &[T]); /// Splits the exclusive slice into a slice of `U`-element arrays, starting at the beginning /// of the slice, and a remainder slice with length strictly less than `U`. /// /// # Panics /// If `U` is 0. fn as_hybrid_chunks_mut(&mut self) -> (&mut [Array], &mut [T]); } impl SliceExt for [T] { fn as_hybrid_array(&self) -> Option<&Array> { Array::slice_as_array(self) } fn as_mut_hybrid_array(&mut self) -> Option<&mut Array> { Array::slice_as_mut_array(self) } fn as_hybrid_chunks(&self) -> (&[Array], &[T]) { Array::slice_as_chunks(self) } fn as_hybrid_chunks_mut(&mut self) -> (&mut [Array], &mut [T]) { Array::slice_as_chunks_mut(self) } } impl sealed::Sealed for [T] {} mod sealed { pub trait Sealed {} } #[cfg(test)] mod tests { use super::{AsArrayMut, AsArrayRef, SliceExt}; use crate::{ Array, sizes::{U2, U3}, }; type A = Array; #[test] fn core_as_array_ref() { assert_eq!([1, 2, 3].as_array_ref(), &Array([1, 2, 3])); } #[test] fn core_as_array_mut() { assert_eq!([1, 2, 3].as_array_mut(), &Array([1, 2, 3])); } #[test] fn hybrid_as_array_ref() { assert_eq!(A::from([1, 2]).as_array_ref(), &Array([1, 2])); } #[test] fn hybrid_as_array_mut() { assert_eq!(A::from([1, 2]).as_array_mut(), &Array([1, 2])); } #[test] fn slice_as_hybrid_array() { assert_eq!([1, 2].as_hybrid_array::(), None); assert_eq!([1, 2, 3].as_hybrid_array::(), Some(&Array([1, 2, 3]))); assert_eq!([1, 2, 3, 4].as_hybrid_array::(), None); } #[test] fn slice_as_mut_hybrid_array() { assert_eq!([1, 2].as_mut_hybrid_array::(), None); assert_eq!( [1, 2, 3].as_mut_hybrid_array::(), Some(&mut Array([1, 2, 3])) ); assert_eq!([1, 2, 3, 4].as_mut_hybrid_array::(), None); } #[test] fn slice_as_hybrid_chunks() { let (slice_empty, rem_empty): (&[A], &[u8]) = [].as_hybrid_chunks::(); assert!(slice_empty.is_empty()); assert!(rem_empty.is_empty()); let (slice_one, rem_one) = [1].as_hybrid_chunks::(); assert!(slice_one.is_empty()); assert_eq!(rem_one, &[1]); let (slice_aligned, rem_aligned) = [1u8, 2].as_hybrid_chunks::(); assert_eq!(slice_aligned, &[Array([1u8, 2])]); assert_eq!(rem_aligned, b""); let (slice_unaligned, rem_unaligned) = [1u8, 2, 3].as_hybrid_chunks::(); assert_eq!(slice_unaligned, &[Array([1u8, 2])]); assert_eq!(rem_unaligned, &[3]); } #[test] fn slice_as_hybrid_chunks_mut() { let (slice_empty, rem_empty): (&mut [A], &mut [u8]) = [].as_hybrid_chunks_mut::(); assert!(slice_empty.is_empty()); assert!(rem_empty.is_empty()); let mut arr1 = [1]; let (slice_one, rem_one) = arr1.as_hybrid_chunks_mut::(); assert!(slice_one.is_empty()); assert_eq!(rem_one, &[1]); let mut arr2 = [1u8, 2]; let (slice_aligned, rem_aligned) = arr2.as_hybrid_chunks_mut::(); assert_eq!(slice_aligned, &mut [Array([1u8, 2])]); assert_eq!(rem_aligned, b""); let mut arr3 = [1u8, 2, 3]; let (slice_unaligned, rem_unaligned) = arr3.as_hybrid_chunks_mut::(); assert_eq!(slice_unaligned, &mut [Array([1u8, 2])]); assert_eq!(rem_unaligned, &mut [3]); } }