generic_array_storage/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std] // <-- yeah, in case you wondered - there you are, feel free to use it
3
4use core::fmt::Debug;
5
6use generic_array::{ArrayLength, GenericArray, IntoArrayLength, functional::FunctionalSequence};
7use nalgebra::{
8    DefaultAllocator, IsContiguous, Matrix, OMatrix, Owned, RawStorage, RawStorageMut, Scalar,
9    Storage, allocator::Allocator,
10};
11
12mod conv;
13pub use conv::Conv;
14
15/// A stack-allocated storage, of [`typenum`]-backed col-major two dimensional array
16///
17/// This struct is transparent and completely public, since it has nothing to hide! Note that [`GenericArray`] is transparent itself, so this struct effectively has the same layout as a two-dimensional array of the corresponding size.
18#[repr(transparent)]
19pub struct GenericArrayStorage<T, R: Conv, C: Conv>(
20    pub GenericArray<GenericArray<T, R::TNum>, C::TNum>,
21);
22
23impl<T: Debug, R: Conv, C: Conv> Debug for GenericArrayStorage<T, R, C> {
24    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25        <GenericArray<GenericArray<T, R::TNum>, C::TNum> as Debug>::fmt(&self.0, f)
26    }
27}
28
29impl<T, R: Conv, C: Conv> Clone for GenericArrayStorage<T, R, C>
30where
31    T: Clone,
32    GenericArray<GenericArray<T, R::TNum>, C::TNum>: Clone,
33{
34    fn clone(&self) -> Self {
35        Self(self.0.clone())
36    }
37}
38
39impl<T, R: Conv, C: Conv> Copy for GenericArrayStorage<T, R, C>
40where
41    T: Copy,
42    <R::TNum as ArrayLength>::ArrayType<T>: Copy,
43    <C::TNum as ArrayLength>::ArrayType<GenericArray<T, R::TNum>>: Copy,
44{
45}
46
47impl<T, R: Conv, C: Conv> AsRef<[T]> for GenericArrayStorage<T, R, C> {
48    fn as_ref(&self) -> &[T] {
49        GenericArray::slice_from_chunks(&self.0)
50    }
51}
52
53impl<T, R: Conv, C: Conv> AsMut<[T]> for GenericArrayStorage<T, R, C> {
54    fn as_mut(&mut self) -> &mut [T] {
55        GenericArray::slice_from_chunks_mut(&mut self.0)
56    }
57}
58
59#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
60unsafe impl<T, R: Conv, C: Conv> RawStorage<T, R::Nalg, C::Nalg> for GenericArrayStorage<T, R, C> {
61    type RStride = nalgebra::U1;
62
63    type CStride = R::Nalg;
64
65    fn ptr(&self) -> *const T {
66        if self.0.is_empty() {
67            core::ptr::NonNull::<T>::dangling().as_ptr()
68        } else {
69            self.0.as_ptr().cast()
70        }
71    }
72
73    fn shape(&self) -> (R::Nalg, C::Nalg) {
74        (R::new_nalg(), C::new_nalg())
75    }
76
77    fn strides(&self) -> (Self::RStride, Self::CStride) {
78        (nalgebra::U1, R::new_nalg())
79    }
80
81    fn is_contiguous(&self) -> bool {
82        true
83    }
84
85    unsafe fn as_slice_unchecked(&self) -> &[T] {
86        self.as_ref()
87    }
88}
89
90#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
91unsafe impl<T, R: Conv, C: Conv> RawStorageMut<T, R::Nalg, C::Nalg>
92    for GenericArrayStorage<T, R, C>
93{
94    fn ptr_mut(&mut self) -> *mut T {
95        if self.0.is_empty() {
96            core::ptr::NonNull::<T>::dangling().as_ptr()
97        } else {
98            self.0.as_mut_ptr().cast()
99        }
100    }
101
102    unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
103        // SAFETY: see struct's doc - it's layout is guaranteed to be like that of a two-dimensional array
104        self.as_mut()
105    }
106}
107
108#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
109unsafe impl<T: Scalar, R: Conv, C: Conv> Storage<T, R::Nalg, C::Nalg>
110    for GenericArrayStorage<T, R, C>
111where
112    nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<R::Nalg, C::Nalg>,
113{
114    fn into_owned(self) -> Owned<T, R::Nalg, C::Nalg>
115    where
116        nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<R::Nalg, C::Nalg>,
117    {
118        nalgebra::DefaultAllocator::allocate_from_iterator(
119            R::new_nalg(),
120            C::new_nalg(),
121            self.0.into_iter().flatten(),
122        )
123    }
124
125    fn clone_owned(&self) -> Owned<T, R::Nalg, C::Nalg>
126    where
127        nalgebra::DefaultAllocator: nalgebra::allocator::Allocator<R::Nalg, C::Nalg>,
128    {
129        self.clone().into_owned()
130    }
131
132    fn forget_elements(self) {
133        core::mem::forget(self);
134    }
135}
136
137#[allow(unsafe_code, reason = "nalgebra storage traits are unsafe")]
138unsafe impl<R: Conv, C: Conv, T: nalgebra::Scalar> IsContiguous for GenericArrayStorage<T, R, C> {}
139
140/// Alias to [`nalgebra::Matrix`], completely "hiding" `const usize`s away. See crate's documentation on how this is possible.
141pub type GenericMatrix<T, R, C> =
142    nalgebra::Matrix<T, <R as Conv>::Nalg, <C as Conv>::Nalg, GenericArrayStorage<T, R, C>>;
143
144type TNum<const N: usize> = typenum::Const<N>;
145
146impl<T, const AR: usize, const AC: usize, R, C> From<[[T; AR]; AC]> for GenericArrayStorage<T, R, C>
147where
148    TNum<AR>: IntoArrayLength,
149    TNum<AC>: IntoArrayLength,
150    R: Conv<TNum = <TNum<AR> as IntoArrayLength>::ArrayLength>,
151    C: Conv<TNum = <TNum<AC> as IntoArrayLength>::ArrayLength>,
152{
153    fn from(value: [[T; AR]; AC]) -> Self {
154        let tnum_array: GenericArray<
155            GenericArray<T, <TNum<AR> as IntoArrayLength>::ArrayLength>,
156            <TNum<AC> as IntoArrayLength>::ArrayLength,
157        > = GenericArray::from_array(value.map(GenericArray::from_array));
158        Self(tnum_array)
159    }
160}
161
162impl<T, const AR: usize, const AC: usize, R, C> From<GenericArrayStorage<T, R, C>> for [[T; AR]; AC]
163where
164    TNum<AR>: IntoArrayLength,
165    TNum<AC>: IntoArrayLength,
166    R: Conv<TNum = <TNum<AR> as IntoArrayLength>::ArrayLength>,
167    C: Conv<TNum = <TNum<AC> as IntoArrayLength>::ArrayLength>,
168{
169    fn from(GenericArrayStorage(data): GenericArrayStorage<T, R, C>) -> Self {
170        data.map(GenericArray::into_array).into_array()
171    }
172}
173
174/// [`GenericMatrix`]-conversion trait intended for core arrays and regular [`nalgebra`] matrices
175pub trait GenericMatrixFromExt<R: Conv, C: Conv> {
176    /// Type of the elements.
177    ///
178    /// This an associated type for the simple reason that is can be such.
179    type T;
180
181    /// Creates [`GenericMatrix`] from core Rust array.
182    fn into_generic_matrix(self) -> GenericMatrix<Self::T, R, C>;
183}
184
185impl<T, const AR: usize, const AC: usize, R, C> GenericMatrixFromExt<R, C> for [[T; AR]; AC]
186where
187    TNum<AR>: IntoArrayLength,
188    TNum<AC>: IntoArrayLength,
189    R: Conv<TNum = <TNum<AR> as IntoArrayLength>::ArrayLength>,
190    C: Conv<TNum = <TNum<AC> as IntoArrayLength>::ArrayLength>,
191{
192    type T = T;
193
194    fn into_generic_matrix(self) -> GenericMatrix<Self::T, R, C> {
195        GenericMatrix::from_data(self.into())
196    }
197}
198
199impl<T, R, C> GenericMatrixFromExt<R, C> for OMatrix<T, R::Nalg, C::Nalg>
200where
201    T: Scalar,
202    R: Conv,
203    C: Conv,
204    DefaultAllocator: Allocator<R::Nalg, C::Nalg>,
205{
206    type T = T;
207
208    fn into_generic_matrix(self) -> GenericMatrix<Self::T, R, C> {
209        let (rows, rest) = GenericArray::<_, R::TNum>::chunks_from_slice(self.as_slice());
210        debug_assert!(rest.is_empty(), "Should be no leftover");
211        let arr = GenericArray::<_, C::TNum>::from_slice(rows);
212        let storage = GenericArrayStorage(arr.clone());
213        GenericMatrix::from_data(storage)
214    }
215}
216
217/// Conv trait defining [`GenericMatrix`] conversions.
218pub trait GenericMatrixExt {
219    /// Type of the elements.
220    ///
221    /// This an associated type for the simple reason that is can be such.
222    type T: Scalar;
223
224    /// Type defining rows count
225    type R: Conv;
226
227    /// Type defining column count
228    type C: Conv;
229
230    /// Converts [`GenericMatrix`] into regular [`nalgebra`] matrix, backed by core array (it's opaque about that though)
231    fn into_regular_matrix(
232        self,
233    ) -> OMatrix<Self::T, <Self::R as Conv>::Nalg, <Self::C as Conv>::Nalg>
234    where
235        nalgebra::DefaultAllocator:
236            nalgebra::allocator::Allocator<<Self::R as Conv>::Nalg, <Self::C as Conv>::Nalg>;
237
238    /// Changes type of [`GenericMatrix`] to a different row and column count descriptors.
239    fn conv<
240        NewR: Conv<TNum = <Self::R as Conv>::TNum>,
241        NewC: Conv<TNum = <Self::C as Conv>::TNum>,
242    >(
243        self,
244    ) -> GenericMatrix<Self::T, NewR, NewC>;
245}
246
247impl<T: Scalar, R: Conv, C: Conv> GenericMatrixExt for GenericMatrix<T, R, C> {
248    type T = T;
249
250    type R = R;
251
252    type C = C;
253
254    fn into_regular_matrix(
255        self,
256    ) -> OMatrix<Self::T, <Self::R as Conv>::Nalg, <Self::C as Conv>::Nalg>
257    where
258        DefaultAllocator: Allocator<<Self::R as Conv>::Nalg, <Self::C as Conv>::Nalg>,
259    {
260        Matrix::from_data(DefaultAllocator::allocate_from_iterator(
261            <Self::R as Conv>::new_nalg(),
262            <Self::C as Conv>::new_nalg(),
263            self.data.0.into_iter().flatten(),
264        ))
265    }
266
267    fn conv<
268        NewR: Conv<TNum = <Self::R as Conv>::TNum>,
269        NewC: Conv<TNum = <Self::C as Conv>::TNum>,
270    >(
271        self,
272    ) -> GenericMatrix<Self::T, NewR, NewC> {
273        GenericMatrix::from_data(GenericArrayStorage(self.data.0))
274    }
275}
276
277#[cfg(test)]
278mod tests;