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}