const_map_array/
lib.rs

1/// Create an array of uninitialized elements.
2#[inline]
3#[doc(hidden)]
4pub const fn __array_uninit<T, const N: usize>() -> [::core::mem::MaybeUninit<T>; N] {
5    [const { ::core::mem::MaybeUninit::uninit() }; N] // <- is the const { ... } here necessary?
6}
7
8// TODO: Replace with stable version when stabilized.
9#[inline]
10#[doc(hidden)]
11pub const unsafe fn __array_assume_init<T, const N: usize>(
12    array: [::core::mem::MaybeUninit<T>; N],
13) -> [T; N] {
14    // SAFETY: MaybeUninit<T> and T are guaranteed to have the same layout
15    unsafe { ::core::mem::transmute_copy(&array) }
16}
17
18/// Provides access to the inner value of a ManuallyDrop<T>.
19#[inline]
20#[doc(hidden)]
21pub const fn __manually_drop_inner_ref<T>(slot: &::core::mem::ManuallyDrop<T>) -> &T {
22    // SAFETY: ManuallyDrop<T> and T are guaranteed to have the same layout
23    unsafe { ::core::mem::transmute(slot) }
24}
25
26/// Asserts that two arrays are of the same length at compile-time.
27#[inline]
28#[doc(hidden)]
29pub const fn __same_len<A, B, const N: usize>(_: &[A; N], _: &[B; N]) {}
30
31/// Asserts that any number of arrays are of the same length at compile-time and returns the length as a value.
32#[doc(hidden)]
33#[macro_export]
34macro_rules! __same_len {
35    ($a:expr, $($rest:tt)*) => {{
36        $crate::__same_len!(@impl $a, $($rest)*);
37        $a.len()
38    }};
39    (@impl $a:expr, $b:expr $(,)?) => {
40        $crate::__same_len($a, $b);
41    };
42    (@impl $a:expr, $b:expr, $($rest:tt)*) => {
43        $crate::__same_len($a, $b);
44        $crate::__same_len!(@impl $b, $($rest)*);
45    };
46}
47
48#[macro_export]
49macro_rules! const_map_array {
50    (let ($($oa_pat:pat_param),+ $(,)?) = $($rest:tt)*) => {
51        $crate::const_map_array!(@zip_outputs (($($oa_pat),*) ((o0a, o0), (o1a, o1), (o2a, o2), (o3a, o3), (o4a, o4), (o5a, o5), (o6a, o6), (o7a, o7), (o8a, o8), (o9a, o9), (o10a, o10), (o11a, o11)) => ()) ($($rest)*) => (multi_output) );
52    };
53    (let $oa_pat:pat_param = $($rest:tt)*) => {
54        $crate::const_map_array!(@zip_outputs (($oa_pat) ((o0a, o0)) => ()) ($($rest)*) => (single_output) );
55    };
56    (@zip_outputs (() ($($_:tt),*) => ($($arg:tt)*)) ($($rest:tt)*) => $($args:tt)*) => {
57        $crate::const_map_array!(@parse_inputs ($($rest)*) => $($args)* ($($arg)*));
58    };
59    (@zip_outputs (($($oa_pat:pat_param),+) () => ($($arg:tt)*)) ($($rest:tt)*) => $($args:tt)*) => {
60        compile_error!("const_map_array only supports up to 12 outputs");
61    };
62    (@zip_outputs (($oa_pat_head:pat_param $(,$oa_pat_tail:pat_param)*) (($oa_head:ident, $o_head:ident) $(,($oa_tail:ident, $o_tail:ident))*) => ($(($oa_pat:pat_param, $oa:ident, $o:ident)),*)) ($($rest:tt)*) => $($args:tt)*) => {
63        $crate::const_map_array!(@zip_outputs (($($oa_pat_tail),*) ($(($oa_tail, $o_tail)),*) => ($(($oa_pat, $oa, $o),)* ($oa_pat_head, $oa_head, $o_head))) ($($rest)*) => $($args)*);
64    };
65    (@parse_inputs (map($ia_expr:expr, |($($i_pat:pat_param),+ $(,)?)| $body:expr)) => $($args:tt)*) => {
66        $crate::const_map_array!(@zip_inputs (($($i_pat),*) ((i0, i0a), (i1, i1a), (i2, i2a), (i3, i3a), (i4, i4a), (i5, i5a), (i6, i6a), (i7, i7a), (i8, i8a), (i9, i9a), (i10, i10a), (i11, i11a)) => ()) => $($args)* (multi_input) ($ia_expr) ($body));
67    };
68    (@parse_inputs (map($ia_expr:expr, |$i_pat:pat_param| $body:expr)) => $($args:tt)*) => {
69        $crate::const_map_array!(@zip_inputs (($i_pat) ((i0, i0a)) => ()) => $($args)* (single_input) ($ia_expr) ($body));
70    };
71    (@zip_inputs (() ($($_:tt),*) => ($($arg:tt)*)) => $($args:tt)*) => {
72        $crate::const_map_array!(@call $($args)* ($($arg)*));
73    };
74    (@zip_inputs (($($i_pat:pat_param),+) () => ($($arg:tt)*)) => $($args:tt)*) => {
75        compile_error!("const_map_array only supports up to 12 inputs");
76    };
77    (@zip_inputs (($i_pat_head:pat_param $(,$i_pat_tail:pat_param)*) (($i_head:ident, $ia_head:ident) $(,($i_tail:ident, $ia_tail:ident))*) => ($(($i_pat:pat_param, $i:ident, $ia:ident)),*)) => $($args:tt)*) => {
78        $crate::const_map_array!(@zip_inputs (($($i_pat_tail),*) ($(($i_tail, $ia_tail)),*) => ($(($i_pat, $i, $ia),)* ($i_pat_head, $i_head, $ia_head))) => $($args)*);
79    };
80    (@call ($output:tt) ($(($oa_pat:pat_param, $oa:ident, $o:ident)),*) ($input:tt) ($ia_expr:expr) ($body:expr) ($(($i_pat:pat_param, $i:ident, $ia:ident)),*)) => {
81        $(
82            let mut $oa = $crate::__array_uninit();
83        )*
84
85        $crate::const_map_array!(@destructure_input $input ($ia_expr) ($($ia),*));
86        $(
87            let $ia = ::core::mem::ManuallyDrop::<[_; _]>::new($ia);
88            let $ia = $crate::__manually_drop_inner_ref(&$ia);
89        )*
90
91        let end = $crate::__same_len!($(&$oa,)* $(&$ia),*);
92        let mut index = 0;
93        #[allow(unreachable_code)] // hide noise when breaking (which you shouldn't)
94        while index < end {
95            $(
96                let $i_pat = unsafe { ::core::ptr::addr_of!($ia[index]).read() };
97            )*
98
99            let item = $body;
100            $crate::const_map_array!(@write_outputs $output ($(($oa, $o)),*) (index) (item));
101            index += 1;
102        };
103
104        assert!(index == end, "break is not allowed because a value must be written into every array element");
105
106        $(
107            let $oa_pat = unsafe { $crate::__array_assume_init($oa) };
108        )*
109    };
110    (@destructure_input single_input ($ia_expr:expr) ($ia:ident)) => {
111        let $ia = $ia_expr;
112    };
113    (@destructure_input multi_input ($ia_expr:expr) ($($ia:ident),*)) => {
114        ::const_destructure::const_destructure!(let ($($ia),*) = $ia_expr);
115    };
116    // TODO: $o is only used here, so we can zip the $o idents here in principle.
117    (@write_outputs single_output (($oa:ident, $o:ident)) ($index:ident) ($item:ident)) => {
118        $oa[$index].write($item);
119    };
120    (@write_outputs multi_output ($(($oa:ident, $o:ident)),*) ($index:ident) ($item:ident)) => {
121        ::const_destructure::const_destructure!(let ($($o),*) = $item);
122        $(
123            $oa[$index].write($o);
124        )*
125    };
126}