ab_io_type/alignment.rs
1//! Alignment of a type or value, in its own module so that only its constructors can create it
2
3use crate::trivial_type::TrivialType;
4use core::hint;
5
6/// Alignment of a type or value.
7///
8/// It is stored as its power of two, so the compiler knows that the alignment in bytes is a power
9/// of two, and code that uses it can compute padding with plain `%` that compiles to bit masks.
10/// Alignments compare like their values in bytes.
11#[derive(Debug, Copy, Clone)]
12#[derive_const(PartialEq, Eq, PartialOrd, Ord)]
13pub struct Alignment {
14 /// Power of two of the alignment, at most the power of [`Alignment::MAX`]
15 pub mut(self) power: u8,
16}
17
18impl Alignment {
19 /// The largest alignment supported, 16 bytes.
20 ///
21 /// No [`TrivialType`] has a larger alignment.
22 pub const MAX: Self = {
23 let Ok(power) = u8::try_from(16u8.ilog2()) else {
24 unreachable!();
25 };
26 Self { power }
27 };
28
29 /// Alignment from its power of two, `None` if it would be larger than [`Alignment::MAX`]
30 #[inline(always)]
31 pub const fn from_power(power: u8) -> Option<Self> {
32 if power > Self::MAX.power {
33 return None;
34 }
35
36 Some(Self { power })
37 }
38
39 /// Alignment of `T` in metadata, see [`TrivialType::ALIGNMENT`].
40 ///
41 /// [`TrivialType`] requires an alignment of at most [`Alignment::MAX`], and a call doesn't
42 /// compile for an implementation that breaks this requirement. It also doesn't compile when
43 /// `T::ALIGNMENT` is smaller than `align_of::<T>()`.
44 #[inline(always)]
45 pub const fn of<T>() -> Self
46 where
47 T: TrivialType,
48 {
49 const {
50 assert!(
51 align_of::<T>() <= usize::from(T::ALIGNMENT.get()),
52 "Alignment of the type is larger than `TrivialType::ALIGNMENT`"
53 );
54 T::ALIGNMENT
55 }
56 }
57
58 /// The alignment in bytes
59 #[inline(always)]
60 pub const fn get(self) -> u8 {
61 // SAFETY: The field is only set in this module (`mut(self)`), by the definition of `MAX`
62 // and by `from_power()`, which rejects powers larger than that of `MAX`. This tells the
63 // compiler that the alignment is at most `Alignment::MAX`, also when it was read from
64 // memory.
65 unsafe {
66 hint::assert_unchecked(self.power <= Self::MAX.power);
67 }
68
69 1 << self.power
70 }
71
72 /// Whether `slice` starts at an address aligned to `self`
73 #[inline(always)]
74 pub fn is_slice_aligned<T>(self, slice: &[T]) -> bool
75 where
76 T: TrivialType,
77 {
78 let alignment = usize::from(self.get());
79 slice.as_ptr().addr().is_multiple_of(alignment)
80 }
81}