boxarray/
lib.rs

1//! Safe way to allocate and initialize nested arrays directly on the heap inside a `Box`.
2//!
3//! ## Usage
4//!
5//! In order to initialize a Boxed nested-array, simply call the `boxarray` function and give it the value (here `v`) to initialize with:
6//! ```
7//!   let v = 7.0;
8//!   let a: Box<[[[f64; 3]; 2]; 4]> = boxarray::boxarray(v);
9//! ```
10//!
11//! The initialization can also be done with a function that takes the coordinates in nested tuples as arguments by using `boxarray_` instead:
12//! ```
13//!   let f = |((((), i), j), k)| (i+j*k) as usize;
14//!   let a: Box<[[[usize; 3]; 2]; 4]> = boxarray::boxarray_(f);
15//! ```
16use std::{
17    alloc::{alloc_zeroed, Layout},
18    mem::transmute,
19};
20
21mod private {
22    use std::marker::PhantomData;
23
24    /// Type-level list of const generic usize.
25    pub trait CUList {
26        type CoordType;
27    }
28    /// Type operator that return the CoordType of o CUList, which is a type representing nested tuple of usize, where the number of nesting is the same as the number of array nesting the CUList represent.
29    pub type CoordType<A> = <A as CUList>::CoordType;
30
31    /// Value constructor for `CUList`. Represend a single value not in an array.
32    pub struct Value {}
33    impl CUList for Value {
34        type CoordType = ();
35    }
36
37    /// Array constructor for `CUList`. Represent the outter-most array that contains the other nested arrays and its own size.
38    pub struct Array<L: CUList, const N: usize> {
39        _l: PhantomData<L>,
40    }
41    impl<L: CUList, const N: usize> CUList for Array<L, N> {
42        type CoordType = (L::CoordType, usize);
43    }
44
45    /// Convert the impl type to a value of type `T`.
46    pub trait Reify<T> {
47        fn reify() -> T;
48    }
49
50    impl Reify<usize> for Value {
51        fn reify() -> usize {
52            0
53        }
54    }
55    impl<L: CUList + Reify<usize>, const N: usize> Reify<usize> for Array<L, N> {
56        fn reify() -> usize {
57            1 + L::reify()
58        }
59    }
60
61    impl Reify<CoordType<Value>> for Value {
62        fn reify() -> CoordType<Value> {
63            ()
64        }
65    }
66    impl<L: CUList + Reify<CoordType<L>>, const N: usize> Reify<CoordType<Array<L, N>>>
67        for Array<L, N>
68    {
69        fn reify() -> CoordType<Array<L, N>> {
70            (L::reify(), N)
71        }
72    }
73
74    /// Product for recursive types
75    pub trait Product<T> {
76        fn product() -> T;
77    }
78    impl Product<usize> for Value {
79        fn product() -> usize {
80            1
81        }
82    }
83    impl<L: CUList + Product<usize>, const N: usize> Product<usize> for Array<L, N> {
84        fn product() -> usize {
85            N * L::product()
86        }
87    }
88
89    pub trait IndexCoord<L: CUList> {
90        fn coords(i: usize) -> CoordType<L>;
91    }
92    impl IndexCoord<Value> for Value {
93        fn coords(_: usize) -> CoordType<Value> {
94            ()
95        }
96    }
97    impl<L: CUList + IndexCoord<L> + Product<usize>, const N: usize> IndexCoord<Array<L, N>>
98        for Array<L, N>
99    {
100        fn coords(i: usize) -> CoordType<Array<L, N>> {
101            let prod = L::product();
102
103            (L::coords(i % prod), i / prod)
104        }
105    }
106
107    /// Constrains valid nested arrays.
108    pub trait Arrays<E, L: CUList> {}
109    impl<E> Arrays<E, Value> for E {}
110    impl<E, L: CUList, A: Arrays<E, L>, const N: usize> Arrays<E, Array<L, N>> for [A; N] {}
111}
112use private::*;
113pub use private::{Array, Value};
114
115/// The `boxarray` function allow to allocate nested arrays directly on the heap inside a `Box` and initialize it with a constant value of type `E`.
116///
117/// # Examples
118///
119/// Zero-size array (i.e. a simple value)
120/// ```
121/// fn signle_array() {
122///     let a: Box<u32> = boxarray::boxarray(1);
123///     assert_eq!(*a, 1u32);
124/// }
125/// ```
126///
127/// Single array.
128/// ```
129/// fn signle_array() {
130///     let a: Box<[u32; 10]> = boxarray::boxarray(1);
131///     assert_eq!(*a, [1u32; 10]);
132/// }
133/// ```
134///
135/// Nested array.
136/// ```
137/// fn nested_array() {
138///     let a: Box<[[[f64; 10]; 2]; 4]> = boxarray::boxarray(7.0);
139///     assert_eq!(*a, [[[7f64; 10]; 2]; 4]);
140/// }
141/// ```
142///
143/// Zero sized type.
144/// ```
145/// fn zero_sized_type() {
146///     #[derive(Clone, Copy, Debug, PartialEq, Eq)]
147///     struct ZST;
148///     let a: Box<[[[ZST; 10]; 2]; 4]> = boxarray::boxarray(ZST);
149///     assert_eq!(*a, [[[ZST; 10]; 2]; 4]);
150/// }
151/// ```
152///
153/// If the type of the value to initialize with does not correspond, a compiler will be raised.
154/// ```compile_fail
155/// fn nested_array_wrong_type() {
156///     let a: Box<[[[f64; 10]; 2]; 4]> = boxarray::boxarray(7.0f32);
157/// }
158/// ```
159///
160/// If the type to initialize is not only composed of nested arrays, a compiler will be raised.
161/// ```compile_fail
162/// fn nested_array_wrong_type() {
163///     let a: Box<[[([f64; 10], [f64; 10]); 2]; 4]> = boxarray::boxarray(7.0);
164/// }
165/// ```
166///
167pub fn boxarray<E: Clone, L: CUList, A: Arrays<E, L>>(e: E) -> Box<A> {
168    unsafe {
169        let ptr = alloc_zeroed(Layout::new::<A>());
170        let se = std::mem::size_of::<E>();
171        if se != 0 {
172            let st = std::mem::size_of::<A>();
173            let n = st / se;
174            let arr: *mut E = transmute(ptr);
175            for i in 0..n {
176                *arr.add(i) = e.clone();
177            }
178        }
179        Box::from_raw(std::mem::transmute(ptr))
180    }
181}
182
183/// Same as `boxarray` but use a fonction that takes nested tuples of `usize` as coordinates and return a value of type `E` to initialize every cells.
184///
185/// # Examples
186///
187/// Zero-size array (i.e. a simple value)
188/// ```
189/// fn signle_array() {
190///     let a: Box<u32> = boxarray::boxarray_(|()| 1);
191///     assert_eq!(*a, 1u32);
192/// }
193/// ```
194///
195/// Single array.
196/// ```
197/// fn signle_array() {
198///     let a: Box<[u32; 4]> = boxarray::boxarray_(|((),i)| i as  u32);
199///     assert_eq!(*a, [0,1,2,3]);
200/// }
201/// ```
202///
203/// Nested array.
204/// ```
205/// fn nested_array() {
206///     let a: Box<[[[i32; 3]; 2]; 4]> = boxarray::boxarray_(|((((),i),j),k)| (i+j*k) as i32);
207///     let mut sol = [[[0i32; 3]; 2]; 4];
208///     for k in 0..4 {
209///         for j in 0..2 {
210///             for i in 0..3 {
211///                 sol[k][j][i] = (i+j*k) as i32;
212///             }
213///         }
214///     }
215///     assert_eq!(*a, sol);
216/// }
217/// ```
218///
219/// Fails to compile when the number of coordinates are not the same as the dimension of the nested arrays.
220/// ```compile_fail
221/// fn nested_array() {
222///     let a: Box<[[[i32; 3]; 2]; 4]> = boxarray::boxarray_(|(((),i),j)| i as i32);
223/// }
224/// ```
225/// ```compile_fail
226/// fn nested_array() {
227///     let a: Box<[[[i32; 3]; 2]; 4]> = boxarray::boxarray_(|(((((),i),j),k),l)| i as i32);
228/// }
229/// ```
230///
231pub fn boxarray_<E, L: CUList + IndexCoord<L>, A: Arrays<E, L>, F: Fn(CoordType<L>) -> E>(
232    f: F,
233) -> Box<A> {
234    unsafe {
235        let ptr = alloc_zeroed(Layout::new::<A>());
236        let se = std::mem::size_of::<E>();
237        if se != 0 {
238            let st = std::mem::size_of::<A>();
239            let n = st / se;
240            let arr: *mut E = transmute(ptr);
241            for i in 0..n {
242                *arr.add(i) = f(L::coords(i));
243            }
244        }
245        Box::from_raw(std::mem::transmute(ptr))
246    }
247}