const_generic_alignment/
lib.rs

1#![no_std]
2#![cfg_attr(feature = "freeze", feature(freeze))]
3#![cfg_attr(feature = "unstable_docs", feature(doc_auto_cfg))]
4
5#[cfg(feature = "freeze")]
6use core::marker::Freeze;
7use core::{
8    borrow::{Borrow, BorrowMut},
9    fmt,
10    hash::Hash,
11    mem::MaybeUninit,
12    panic::{RefUnwindSafe, UnwindSafe},
13};
14
15/// A wrapper type with the specified alignment.
16///
17/// This type implements most relevant traits if the wrapped type does,
18/// including [`Default`], [`Ord`], [`Copy`], [`Send`], [`Sync`], [`Unpin`],
19/// etc.
20///
21/// With the `bytemuck` optional feature enabled, this type implements the
22#[cfg_attr(feature = "bytemuck", doc = "[`bytemuck::AnyBitPattern`]")]
23#[cfg_attr(
24    not(feature = "bytemuck"),
25    doc = "[`bytemuck::AnyBitPattern`](https://docs.rs/bytemuck/latest/bytemuck/trait.AnyBitPattern.html)"
26)]
27/// trait if the wrapped type does. (It cannot in general implement `Pod`,
28/// because there may be padding.)
29///
30/// With the `freeze` optional feature enabled, this type implements the
31/// unstable [`core::marker::Freeze`] trait if the wrapped type does.
32///
33/// # Layout
34///
35/// The wrapped field `inner` is at offset `0`, and the size and alignment of
36/// `Self` are minimal to meet the wrapped field's size and alignment, and to
37/// meet the requested alignment, under Rust's normal `size % align == 0` rule.
38///
39/// This is *not* `repr(transparent)`.
40#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41#[repr(C)]
42pub struct Aligned<T: ?Sized, const ALIGNMENT: usize>
43where
44    Alignment<ALIGNMENT>: SupportedAlignment,
45{
46    pub align: AlignedZst<ALIGNMENT>,
47    pub inner: T,
48}
49
50impl<T: ?Sized + Hash, const ALIGNMENT: usize> Hash for Aligned<T, ALIGNMENT>
51where
52    Alignment<ALIGNMENT>: SupportedAlignment,
53{
54    #[inline]
55    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
56        T::hash(&self.inner, state)
57    }
58}
59
60impl<T: ?Sized, const ALIGNMENT: usize> AsRef<T> for Aligned<T, ALIGNMENT>
61where
62    Alignment<ALIGNMENT>: SupportedAlignment,
63{
64    #[inline]
65    fn as_ref(&self) -> &T {
66        &self.inner
67    }
68}
69
70impl<T: ?Sized, const ALIGNMENT: usize> AsMut<T> for Aligned<T, ALIGNMENT>
71where
72    Alignment<ALIGNMENT>: SupportedAlignment,
73{
74    #[inline]
75    fn as_mut(&mut self) -> &mut T {
76        &mut self.inner
77    }
78}
79
80impl<T: ?Sized, const ALIGNMENT: usize> Borrow<T> for Aligned<T, ALIGNMENT>
81where
82    Alignment<ALIGNMENT>: SupportedAlignment,
83{
84    #[inline]
85    fn borrow(&self) -> &T {
86        &self.inner
87    }
88}
89
90impl<T: ?Sized, const ALIGNMENT: usize> BorrowMut<T> for Aligned<T, ALIGNMENT>
91where
92    Alignment<ALIGNMENT>: SupportedAlignment,
93{
94    #[inline]
95    fn borrow_mut(&mut self) -> &mut T {
96        &mut self.inner
97    }
98}
99
100impl<T, const ALIGNMENT: usize> Aligned<T, ALIGNMENT>
101where
102    Alignment<ALIGNMENT>: SupportedAlignment,
103{
104    /// Creates a new `Aligned` wrapper containing the given value.
105    pub const fn new(value: T) -> Self {
106        Self { align: AlignedZst::new(), inner: value }
107    }
108
109    /// Consumes this `Aligned` wrapper, returning the wrapped value.
110    pub fn into_inner(self) -> T {
111        let Self { inner, .. } = self;
112        inner
113    }
114}
115
116// Safety: The only fields are an inhabited ZST and `T`, which are both
117// `Zeroable`. Padding is not relevant for `Zeroable`.
118#[cfg(feature = "bytemuck")]
119unsafe impl<T, const ALIGNMENT: usize> bytemuck::Zeroable
120    for Aligned<T, ALIGNMENT>
121where
122    T: bytemuck::Zeroable,
123    Alignment<ALIGNMENT>: SupportedAlignment,
124{
125}
126
127// Safety: The only fields are an inhabited ZST and `T`, which are both
128// `AnyBitPattern`. Padding is not relevant for `AnyBitPattern`.
129#[cfg(feature = "bytemuck")]
130unsafe impl<T, const ALIGNMENT: usize> bytemuck::AnyBitPattern
131    for Aligned<T, ALIGNMENT>
132where
133    T: bytemuck::AnyBitPattern,
134    Alignment<ALIGNMENT>: SupportedAlignment,
135{
136}
137
138/// A Zero-sized type with the specified alignment.
139///
140/// To facilitate deriving traits on types containing this type, this type
141/// implements most relevant traits, including [`Default`], [`Ord`], [`Copy`],
142/// [`Send`], [`Sync`], [`Unpin`], etc.
143///
144/// With the `bytemuck` optional feature enabled, this type implements the
145#[cfg_attr(feature = "bytemuck", doc = "[`bytemuck::Pod`]")]
146#[cfg_attr(
147    not(feature = "bytemuck"),
148    doc = "[`bytemuck::Pod`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html)"
149)]
150/// trait.
151///
152/// With the `freeze` optional feature enabled, this type implements the
153/// unstable [`core::marker::Freeze`] trait.
154#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[repr(transparent)]
156pub struct AlignedZst<const ALIGNMENT: usize>
157where
158    Alignment<ALIGNMENT>: SupportedAlignment,
159{
160    inner: <Alignment<ALIGNMENT> as SupportedAlignment>::AlignedZst,
161}
162
163impl<const ALIGNMENT: usize> AlignedZst<ALIGNMENT>
164where
165    Alignment<ALIGNMENT>: SupportedAlignment,
166{
167    /// Creates a new `AlignedZst`.
168    ///
169    /// This does the same thing as `<Self as Default>::default()`, but is
170    /// usable in `const`.
171    pub const fn new() -> Self {
172        // Safety: This is an inhabited ZST.
173        unsafe { MaybeUninit::uninit().assume_init() }
174    }
175}
176
177impl<const ALIGNMENT: usize> fmt::Debug for AlignedZst<ALIGNMENT>
178where
179    Alignment<ALIGNMENT>: SupportedAlignment,
180{
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        write!(f, "AlignedZst<{}> {{ .. }}", ALIGNMENT)
183    }
184}
185
186// Safety: This is an inhabited ZST.
187#[cfg(feature = "bytemuck")]
188unsafe impl<const ALIGNMENT: usize> bytemuck::Zeroable for AlignedZst<ALIGNMENT> where
189    Alignment<ALIGNMENT>: SupportedAlignment
190{
191}
192
193// Safety: This is an inhabited ZST.
194#[cfg(feature = "bytemuck")]
195unsafe impl<const ALIGNMENT: usize> bytemuck::Pod for AlignedZst<ALIGNMENT> where
196    Alignment<ALIGNMENT>: SupportedAlignment
197{
198}
199
200mod sealed {
201    pub trait Sealed {}
202}
203
204impl<const N: usize> sealed::Sealed for Alignment<N> {}
205
206/// Marker type to specify the alignment of [`AlignedZst`] in the type system.
207///
208/// For valid alignments `N`, `Alignment<N>` implements [`SupportedAlignment`].
209#[non_exhaustive]
210pub struct Alignment<const N: usize>;
211
212/// Statically guarantees that an alignment is supported
213///
214/// This trait is *sealed*: the list of implementors below is total. Users do
215/// not have the ability to mark additional `Alignment<N>` values as supported.
216/// Only alignments supported by rustc/LLVM are constructable, i.e. powers of
217/// two from 1 to `2^29` (or `2^15` on 16-bit targets).
218pub unsafe trait SupportedAlignment: sealed::Sealed {
219    cfg_if::cfg_if! {
220        if #[cfg(feature = "freeze")] {
221            /// Safety: This must be an inhabited ZST with the specified alignment.
222            #[doc(hidden)]
223            type AlignedZst: Sized + Default + Copy + Ord + Hash + Send + Sync + Unpin + UnwindSafe + RefUnwindSafe + Freeze + 'static;
224        } else {
225            /// Safety: This must be an inhabited ZST with the specified alignment.
226            #[doc(hidden)]
227            type AlignedZst: Sized + Default + Copy + Ord + Hash + Send + Sync + Unpin + UnwindSafe + RefUnwindSafe + 'static;
228        }
229    }
230}
231
232macro_rules! make_zsts {
233    ( $($typename:ident = $alignment:literal;)* ) => { $(
234        /// Helper type used only as a field in [`AlignedZst`].
235        ///
236        #[doc = concat!("Is a ZST with alignment ", stringify!($alignment), ".")]
237        #[doc(hidden)]
238        #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
239        #[repr(align($alignment))]
240        pub struct $typename;
241
242        unsafe impl SupportedAlignment for Alignment<$alignment> {
243            type AlignedZst = $typename;
244        }
245     )* };
246}
247
248make_zsts! {
249    Align1 = 1;
250    Align2 = 2;
251    Align4 = 4;
252    Align8 = 8;
253    Align16 = 16;
254    Align32 = 32;
255    Align64 = 64;
256    Align128 = 128;
257    Align256 = 256;
258    Align512 = 512;
259    Align1024 = 1024;
260    Align2048 = 2048;
261    Align4096 = 4096;
262    Align8192 = 8192;
263    Align16384 = 16384;
264    Align32768 = 32768;
265}
266#[cfg(not(target_pointer_width = "16"))]
267make_zsts! {
268    Align65536 = 65536;
269    Align131072 = 131072;
270    Align262144 = 262144;
271    Align524288 = 524288;
272    Align1048576 = 1048576;
273    Align2097152 = 2097152;
274    Align4194304 = 4194304;
275    Align8388608 = 8388608;
276    Align16777216 = 16777216;
277    Align33554432 = 33554432;
278    Align67108864 = 67108864;
279    Align134217728 = 134217728;
280    Align268435456 = 268435456;
281    Align536870912 = 536870912;
282}
283
284#[cfg(test)]
285mod tests {
286    extern crate std;
287
288    use crate::{Aligned, AlignedZst, Alignment, SupportedAlignment};
289
290    #[test]
291    fn it_works() {
292        #[derive(Default, Clone, Copy)]
293        struct AlignedThing<const N: usize, T>
294        where
295            Alignment<N>: SupportedAlignment,
296        {
297            _aligned: AlignedZst<N>,
298            value: T,
299        }
300
301        assert_eq!(core::mem::align_of::<AlignedThing<64, u8>>(), 64);
302        assert_eq!(
303            core::mem::align_of::<AlignedThing<4, u64>>(),
304            core::cmp::max(4, core::mem::align_of::<u64>())
305        );
306        assert_eq!(
307            core::mem::align_of::<AlignedThing<536870912, u8>>(),
308            536870912
309        );
310
311        let x: [AlignedThing<64, u8>; 2] =
312            [AlignedThing { value: 42, _aligned: Default::default() }; 2];
313        assert_eq!(
314            (&x[1].value as *const _ as usize)
315                - (&x[0].value as *const _ as usize),
316            64
317        );
318    }
319
320    #[test]
321    fn wrapper_works() {
322        assert_eq!(core::mem::align_of::<Aligned<u8, 64>>(), 64);
323        assert_eq!(
324            core::mem::align_of::<Aligned<u64, 4>>(),
325            core::cmp::max(4, core::mem::align_of::<u64>())
326        );
327        assert_eq!(core::mem::align_of::<Aligned<u8, 536870912>>(), 536870912);
328
329        let x: [Aligned<u8, 64>; 2] = [Aligned::new(42); 2];
330        assert_eq!(
331            (&x[1].inner as *const _ as usize)
332                - (&x[0].inner as *const _ as usize),
333            64
334        );
335    }
336}