encase/core/
alignment_value.rs

1use super::SizeValue;
2use core::num::NonZeroU64;
3
4/// Helper type for alignment calculations
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub struct AlignmentValue(NonZeroU64);
7
8impl AlignmentValue {
9    pub const fn new(val: u64) -> Self {
10        if !val.is_power_of_two() {
11            panic!("Alignment must be a power of 2!");
12        }
13        // SAFETY: This is safe since 0 is not a power of 2
14        Self(unsafe { NonZeroU64::new_unchecked(val) })
15    }
16
17    /// Returns an alignment that is the smallest power of two greater than the passed in `size`
18    #[inline]
19    pub const fn from_next_power_of_two_size(size: SizeValue) -> Self {
20        match size.get().checked_next_power_of_two() {
21            None => panic!("Overflow occurred while getting the next power of 2!"),
22            Some(val) => {
23                // SAFETY: This is safe since we got the next_power_of_two
24                Self(unsafe { NonZeroU64::new_unchecked(val) })
25            }
26        }
27    }
28
29    #[inline]
30    pub const fn get(&self) -> u64 {
31        self.0.get()
32    }
33
34    /// Returns the max alignment from an array of alignments
35    pub const fn max<const N: usize>(input: [AlignmentValue; N]) -> AlignmentValue {
36        let mut max = input[0];
37        let mut i = 1;
38
39        while i < N {
40            if input[i].get() > max.get() {
41                max = input[i];
42            }
43
44            i += 1;
45        }
46
47        max
48    }
49
50    /// Returns true if `n` is a multiple of this alignment
51    #[inline]
52    pub const fn is_aligned(&self, n: u64) -> bool {
53        n % self.get() == 0
54    }
55
56    /// Returns the amount of padding needed so that `n + padding` will be a multiple of this alignment
57    #[inline]
58    pub const fn padding_needed_for(&self, n: u64) -> u64 {
59        let r = n % self.get();
60        if r > 0 {
61            self.get() - r
62        } else {
63            0
64        }
65    }
66
67    /// Will round up the given `n` so that the returned value will be a multiple of this alignment
68    #[inline]
69    pub const fn round_up(&self, n: u64) -> u64 {
70        n + self.padding_needed_for(n)
71    }
72
73    /// Will round up the given `n` so that the returned value will be a multiple of this alignment
74    #[inline]
75    pub const fn round_up_size(&self, n: SizeValue) -> SizeValue {
76        SizeValue::new(self.round_up(n.get()))
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use super::AlignmentValue;
83
84    #[test]
85    fn new() {
86        assert_eq!(4, AlignmentValue::new(4).get());
87    }
88
89    #[test]
90    #[should_panic]
91    fn new_panic() {
92        AlignmentValue::new(3);
93    }
94
95    #[test]
96    fn from_next_power_of_two_size() {
97        assert_eq!(
98            AlignmentValue::new(8),
99            AlignmentValue::from_next_power_of_two_size(super::SizeValue::new(7))
100        );
101    }
102
103    #[test]
104    #[should_panic]
105    fn from_next_power_of_two_size_panic() {
106        AlignmentValue::from_next_power_of_two_size(super::SizeValue::new(u64::MAX));
107    }
108
109    #[test]
110    fn max() {
111        assert_eq!(
112            AlignmentValue::new(32),
113            AlignmentValue::max([
114                AlignmentValue::new(2),
115                AlignmentValue::new(8),
116                AlignmentValue::new(32)
117            ])
118        );
119    }
120
121    #[test]
122    fn is_aligned() {
123        assert!(AlignmentValue::new(8).is_aligned(32));
124        assert!(!AlignmentValue::new(8).is_aligned(9));
125    }
126
127    #[test]
128    fn padding_needed_for() {
129        assert_eq!(1, AlignmentValue::new(8).padding_needed_for(7));
130        assert_eq!(16 - 9, AlignmentValue::new(8).padding_needed_for(9));
131    }
132
133    #[test]
134    fn round_up() {
135        assert_eq!(24, AlignmentValue::new(8).round_up(20));
136        assert_eq!(
137            super::SizeValue::new(16),
138            AlignmentValue::new(16).round_up_size(super::SizeValue::new(7))
139        );
140    }
141
142    #[test]
143    fn derived_traits() {
144        let alignment = AlignmentValue::new(8);
145        #[allow(clippy::clone_on_copy)]
146        let alignment_clone = alignment.clone();
147
148        assert!(alignment == alignment_clone);
149
150        assert_eq!(format!("{alignment:?}"), "AlignmentValue(8)");
151    }
152}