generic_simd/
alignment.rs

1//! Alignment helpers.
2
3#[cfg(all(feature = "alloc", not(feature = "std")))]
4extern crate alloc;
5
6#[cfg(all(feature = "alloc", not(feature = "std")))]
7use alloc::{
8    alloc::{alloc, Layout},
9    boxed::Box,
10};
11
12#[cfg(feature = "std")]
13use std::alloc::{alloc, Layout};
14
15use crate::{
16    arch, scalar,
17    vector::{width, VectorOf},
18};
19
20#[repr(C)]
21#[derive(Copy, Clone)]
22struct Vectors<Token: arch::Token, Scalar: scalar::ScalarExt<Token>>(
23    VectorOf<Scalar, width::W1, Token>,
24    VectorOf<Scalar, width::W2, Token>,
25    VectorOf<Scalar, width::W4, Token>,
26    VectorOf<Scalar, width::W8, Token>,
27);
28
29macro_rules! max_alignment {
30    { $first:path, $($rest:path,)* } => {
31
32        #[doc(hidden)]
33        #[repr(C)]
34        #[derive(Copy, Clone)]
35        pub struct AllVectors<Scalar: scalar::ScalarExt<$first> $(+ scalar::ScalarExt<$rest>)*>(
36            Vectors<$first, Scalar>,
37            $(
38            Vectors<$rest, Scalar>,
39            )*
40        );
41
42        /// Allocate a boxed slice of scalars with maximum possible vector alignment for a
43        /// particular scalar on the current architecture.
44        ///
45        /// # Panics
46        /// Panics if `count` is 0 or memory allocation fails.
47        #[cfg(any(feature = "std", feature = "alloc"))]
48        pub fn allocate_max_aligned_slice<Scalar: Default + scalar::ScalarExt<$first> $(+ scalar::ScalarExt<$rest>)*>(count: usize) -> Box<[Scalar]> {
49            allocate_aligned_slice::<AllVectors<Scalar>, Scalar>(count)
50        }
51    }
52}
53
54crate::call_macro_with_tokens! { max_alignment }
55
56/// Aligns a value to another type's alignment.
57#[repr(C)]
58pub struct Aligned<AlignTo, T> {
59    alignment: [AlignTo; 0],
60    value: T,
61}
62
63impl<AlignTo, T> Aligned<AlignTo, T> {
64    pub fn new(value: T) -> Self {
65        Self {
66            alignment: [],
67            value,
68        }
69    }
70}
71
72impl<AlignTo, T> core::ops::Deref for Aligned<AlignTo, T> {
73    type Target = T;
74
75    fn deref(&self) -> &Self::Target {
76        &self.value
77    }
78}
79
80impl<AlignTo, T> core::ops::DerefMut for Aligned<AlignTo, T> {
81    fn deref_mut(&mut self) -> &mut Self::Target {
82        &mut self.value
83    }
84}
85
86impl<AlignTo: Copy, T: Copy> Copy for Aligned<AlignTo, T> {}
87
88impl<AlignTo, T: Clone> Clone for Aligned<AlignTo, T> {
89    fn clone(&self) -> Self {
90        Self::new(self.value.clone())
91    }
92}
93
94impl<AlignTo, T: Default> Default for Aligned<AlignTo, T> {
95    fn default() -> Self {
96        Self::new(T::default())
97    }
98}
99
100impl<AlignTo, T: core::fmt::Debug> core::fmt::Debug for Aligned<AlignTo, T> {
101    #[inline]
102    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103        f.debug_tuple("Aligned").field(&self.value).finish()
104    }
105}
106
107impl<AlignTo, T: core::cmp::PartialEq> core::cmp::PartialEq for Aligned<AlignTo, T> {
108    #[inline]
109    fn eq(&self, other: &Self) -> bool {
110        self.value.eq(&other.value)
111    }
112}
113
114impl<AlignTo, T: core::cmp::Eq> core::cmp::Eq for Aligned<AlignTo, T> {}
115
116impl<AlignTo, T: core::cmp::PartialOrd> core::cmp::PartialOrd for Aligned<AlignTo, T> {
117    #[inline]
118    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
119        self.value.partial_cmp(&other.value)
120    }
121}
122
123impl<AlignTo, T: core::cmp::Ord> core::cmp::Ord for Aligned<AlignTo, T> {
124    #[inline]
125    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
126        self.value.cmp(&other.value)
127    }
128}
129
130impl<AlignTo, T: core::hash::Hash> core::hash::Hash for Aligned<AlignTo, T> {
131    #[inline]
132    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
133        self.value.hash(hasher)
134    }
135}
136
137/// Allocate a boxed slice of `count` `T`s aligned to the `AlignTo` type.
138///
139/// # Panics
140/// Panics if `count` is 0 or memory allocation fails.
141#[cfg(any(feature = "std", feature = "alloc"))]
142pub fn allocate_aligned_slice<AlignTo, T: Default>(count: usize) -> Box<[T]> {
143    assert!(count > 0, "size must be nonzero");
144    let layout = Layout::from_size_align(
145        count * core::mem::size_of::<T>(),
146        core::cmp::max(core::mem::align_of::<AlignTo>(), core::mem::align_of::<T>()),
147    )
148    .unwrap();
149    unsafe {
150        let ptr = alloc(layout) as *mut T;
151        assert!(!ptr.is_null());
152        for i in 0..count {
153            ptr.add(i).write(T::default());
154        }
155        Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr, count))
156    }
157}
158
159/// Aligns a type to the maximum possible vector alignment for a particular scalar on the current
160/// architecture.
161pub type MaxAligned<Scalar, T> = Aligned<AllVectors<Scalar>, T>;
162
163#[cfg(test)]
164mod test {
165    use super::*;
166
167    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
168    #[test]
169    fn check_x86() {
170        type Foo = [f32; 8];
171        type AlignedFoo = MaxAligned<f32, Foo>;
172        assert_eq!(core::mem::align_of::<AlignedFoo>(), 32);
173    }
174}