align_ext/
lib.rs

1// SPDX-License-Identifier: MPL-2.0
2
3#![cfg_attr(not(test), no_std)]
4
5/// An extension trait for Rust integer types, including `u8`, `u16`, `u32`,
6/// `u64`, and `usize`, to provide methods to make integers aligned to a
7/// power of two.
8pub trait AlignExt {
9    /// Returns whether the number is a power of two
10    fn is_power_of_two(&self) -> bool;
11
12    /// Returns to the smallest number that is greater than or equal to
13    /// `self` and is a multiple of the given power of two.
14    ///
15    /// The method panics if `power_of_two` is not a
16    /// power of two or is smaller than 2 or the calculation overflows
17    /// because `self` is too large.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use crate::align_ext::AlignExt;
23    /// assert_eq!(12usize.align_up(2), 12);
24    /// assert_eq!(12usize.align_up(4), 12);
25    /// assert_eq!(12usize.align_up(8), 16);
26    /// assert_eq!(12usize.align_up(16), 16);
27    /// ```
28    fn align_up(self, power_of_two: Self) -> Self;
29
30    /// Returns to the greatest number that is smaller than or equal to
31    /// `self` and is a multiple of the given power of two.
32    ///
33    /// The method panics if `power_of_two` is not a
34    /// power of two or is smaller than 2 or the calculation overflows
35    /// because `self` is too large. In release mode,
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use crate::align_ext::AlignExt;
41    /// assert_eq!(12usize.align_down(2), 12);
42    /// assert_eq!(12usize.align_down(4), 12);
43    /// assert_eq!(12usize.align_down(8), 8);
44    /// assert_eq!(12usize.align_down(16), 0);
45    /// ```
46    fn align_down(self, power_of_two: Self) -> Self;
47}
48
49macro_rules! impl_align_ext {
50    ($( $uint_type:ty ),+,) => {
51        $(
52            impl AlignExt for $uint_type {
53                #[inline]
54                fn is_power_of_two(&self) -> bool {
55                    (*self != 0) && ((*self & (*self - 1)) == 0)
56                }
57
58                #[inline]
59                fn align_up(self, align: Self) -> Self {
60                    assert!(align.is_power_of_two() && align >= 2);
61                    self.checked_add(align - 1).unwrap() & !(align - 1)
62                }
63
64                #[inline]
65                fn align_down(self, align: Self) -> Self {
66                    assert!(align.is_power_of_two() && align >= 2);
67                    self & !(align - 1)
68                }
69            }
70        )*
71    }
72}
73
74impl_align_ext! {
75    u8,
76    u16,
77    u32,
78    u64,
79    usize,
80}
81
82#[cfg(test)]
83mod test {
84    use super::*;
85
86    #[test]
87    fn test_align_up() {
88        let input_ns = [0usize, 1, 2, 9, 15, 21, 32, 47, 50];
89        let input_as = [2usize, 2, 2, 2, 4, 4, 8, 8, 8];
90        let output_ns = [0usize, 2, 2, 10, 16, 24, 32, 48, 56];
91
92        for i in 0..input_ns.len() {
93            let n = input_ns[i];
94            let a = input_as[i];
95            let n2 = output_ns[i];
96            assert!(n.align_up(a) == n2);
97        }
98    }
99
100    #[test]
101    fn test_align_down() {
102        let input_ns = [0usize, 1, 2, 9, 15, 21, 32, 47, 50];
103        let input_as = [2usize, 2, 2, 2, 4, 4, 8, 8, 8];
104        let output_ns = [0usize, 0, 2, 8, 12, 20, 32, 40, 48];
105
106        for i in 0..input_ns.len() {
107            let n = input_ns[i];
108            let a = input_as[i];
109            let n2 = output_ns[i];
110            assert!(n.align_down(a) == n2);
111        }
112    }
113}