Skip to main content

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    use alloc::format;
84
85    #[test]
86    fn new() {
87        assert_eq!(4, AlignmentValue::new(4).get());
88    }
89
90    #[test]
91    #[should_panic]
92    fn new_panic() {
93        AlignmentValue::new(3);
94    }
95
96    #[test]
97    fn from_next_power_of_two_size() {
98        assert_eq!(
99            AlignmentValue::new(8),
100            AlignmentValue::from_next_power_of_two_size(super::SizeValue::new(7))
101        );
102    }
103
104    #[test]
105    #[should_panic]
106    fn from_next_power_of_two_size_panic() {
107        AlignmentValue::from_next_power_of_two_size(super::SizeValue::new(u64::MAX));
108    }
109
110    #[test]
111    fn max() {
112        assert_eq!(
113            AlignmentValue::new(32),
114            AlignmentValue::max([
115                AlignmentValue::new(2),
116                AlignmentValue::new(8),
117                AlignmentValue::new(32)
118            ])
119        );
120    }
121
122    #[test]
123    fn is_aligned() {
124        assert!(AlignmentValue::new(8).is_aligned(32));
125        assert!(!AlignmentValue::new(8).is_aligned(9));
126    }
127
128    #[test]
129    fn padding_needed_for() {
130        assert_eq!(1, AlignmentValue::new(8).padding_needed_for(7));
131        assert_eq!(16 - 9, AlignmentValue::new(8).padding_needed_for(9));
132    }
133
134    #[test]
135    fn round_up() {
136        assert_eq!(24, AlignmentValue::new(8).round_up(20));
137        assert_eq!(
138            super::SizeValue::new(16),
139            AlignmentValue::new(16).round_up_size(super::SizeValue::new(7))
140        );
141    }
142
143    #[test]
144    fn derived_traits() {
145        let alignment = AlignmentValue::new(8);
146        #[allow(clippy::clone_on_copy)]
147        let alignment_clone = alignment.clone();
148
149        assert!(alignment == alignment_clone);
150
151        assert_eq!(format!("{alignment:?}"), "AlignmentValue(8)");
152    }
153}