align_address/
lib.rs

1//! This crate provides functions for aligning addresses.
2//!
3//! [`Align`] is implemented for all unsigned integers and provides methods for:
4//! * [`align_down`]
5//! * [`align_up`]
6//! * [`is_aligned_to`]
7//!
8//! [`align_down`]: Align::align_down
9//! [`align_up`]: Align::align_up
10//! [`is_aligned_to`]: Align::is_aligned_to
11//!
12//! This crate is based on work from the [`x86_64`] crate, but is available for all architectures and all unsigned integer types.
13//!
14//! [`x86_64`]: https://docs.rs/x86_64
15//!
16//! # Examples
17//!
18//! ```
19//! use align_address::Align;
20//!
21//! assert_eq!(123_u64.align_up(2), 124);
22//! ```
23
24#![no_std]
25#![forbid(unsafe_code)]
26
27/// An adress that can be aligned.
28pub trait Align<A = Self>: Copy + PartialEq {
29    /// Align address downwards.
30    ///
31    /// Returns the greatest `x` with alignment `align` so that `x <= addr`.
32    ///
33    /// Panics if the alignment is not a power of two.
34    fn align_down(self, align: A) -> Self;
35
36    /// Align address upwards.
37    ///
38    /// Returns the smallest `x` with alignment `align` so that `x >= addr`.
39    ///
40    /// Panics if the alignment is not a power of two or if an overflow occurs.
41    fn align_up(self, align: A) -> Self;
42
43    /// Checks whether the address has the demanded alignment.
44    #[allow(clippy::wrong_self_convention)]
45    #[inline]
46    fn is_aligned_to(self, align: A) -> bool {
47        self.align_down(align) == self
48    }
49}
50
51macro_rules! align_impl {
52    ($u:ty, $align_down:ident, $align_up:ident, $is_aligned_to:ident) => {
53        /// Align address downwards.
54        ///
55        /// Returns the greatest `x` with alignment `align` so that `x <= addr`.
56        ///
57        /// Panics if the alignment is not a power of two.
58        ///
59        /// This is a `const` version of [`Align::align_down`].
60        // Adapted from `x86_64`
61        #[inline]
62        pub const fn $align_down(addr: $u, align: $u) -> $u {
63            assert!(align.is_power_of_two(), "`align` must be a power of two");
64            addr & !(align - 1)
65        }
66
67        /// Align address upwards.
68        ///
69        /// Returns the smallest `x` with alignment `align` so that `x >= addr`.
70        ///
71        /// Panics if the alignment is not a power of two or if an overflow occurs.
72        ///
73        /// This is a `const` version of [`Align::align_up`].
74        // Adapted from `x86_64`
75        #[inline]
76        pub const fn $align_up(addr: $u, align: $u) -> $u {
77            assert!(align.is_power_of_two(), "`align` must be a power of two");
78            let align_mask = align - 1;
79            if addr & align_mask == 0 {
80                addr // already aligned
81            } else {
82                // FIXME: Replace with .expect, once `Option::expect` is const.
83                if let Some(aligned) = (addr | align_mask).checked_add(1) {
84                    aligned
85                } else {
86                    panic!("attempt to add with overflow")
87                }
88            }
89        }
90
91        /// Checks whether the address has the demanded alignment.
92        ///
93        /// This is a `const` version of [`Align::is_aligned_to`].
94        #[inline]
95        pub const fn $is_aligned_to(addr: $u, align: $u) -> bool {
96            $align_down(addr, align) == addr
97        }
98
99        impl Align for $u {
100            #[inline]
101            fn align_down(self, align: Self) -> Self {
102                $align_down(self, align)
103            }
104
105            #[inline]
106            fn align_up(self, align: Self) -> Self {
107                $align_up(self, align)
108            }
109        }
110    };
111}
112
113align_impl!(u8, u8_align_down, u8_align_up, u8_is_aligned_to);
114align_impl!(u16, u16_align_down, u16_align_up, u16_is_aligned_to);
115align_impl!(u32, u32_align_down, u32_align_up, u32_is_aligned_to);
116align_impl!(u64, u64_align_down, u64_align_up, u64_is_aligned_to);
117align_impl!(u128, u128_align_down, u128_align_up, u128_is_aligned_to);
118align_impl!(usize, usize_align_down, usize_align_up, usize_is_aligned_to);
119
120// Adapted from `x86_64`
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    macro_rules! test_align_up_impl {
126        ($u:ty, $align_up:ident, $test_align_up:ident) => {
127            #[test]
128            fn $test_align_up() {
129                // align 1
130                assert_eq!($align_up(0, 1), 0);
131                assert_eq!($align_up(123, 1), 123);
132                assert_eq!($align_up(<$u>::MAX, 1), <$u>::MAX);
133                // align 2
134                assert_eq!($align_up(0, 2), 0);
135                assert_eq!($align_up(123, 2), 124);
136                assert_eq!($align_up(<$u>::MAX - 1, 2), <$u>::MAX - 1);
137                // address 0
138                assert_eq!($align_up(0, 128), 0);
139                assert_eq!($align_up(0, 1), 0);
140                assert_eq!($align_up(0, 2), 0);
141                assert_eq!($align_up(0, <$u>::MAX & 1 << (<$u>::BITS - 1)), 0);
142            }
143        };
144    }
145
146    test_align_up_impl!(u8, u8_align_up, test_u8_align_up);
147    test_align_up_impl!(u16, u16_align_up, test_u16_align_up);
148    test_align_up_impl!(u32, u32_align_up, test_u32_align_up);
149    test_align_up_impl!(u64, u64_align_up, test_u64_align_up);
150    test_align_up_impl!(u128, u128_align_up, test_u128_align_up);
151    test_align_up_impl!(usize, usize_align_up, test_usize_align_up);
152
153    macro_rules! test_align_up_overflow_impl {
154        ($u:ty, $test_align_up_overflow:ident, $two:expr) => {
155            #[test]
156            #[should_panic]
157            fn $test_align_up_overflow() {
158                <$u>::MAX.align_up($two);
159            }
160        };
161    }
162
163    test_align_up_overflow_impl!(u8, test_u8_align_up_overflow, 2);
164    test_align_up_overflow_impl!(u16, test_u16_align_up_overflow, 2);
165    test_align_up_overflow_impl!(u32, test_u32_align_up_overflow, 2);
166    test_align_up_overflow_impl!(u64, test_u64_align_up_overflow, 2);
167    test_align_up_overflow_impl!(u128, test_u128_align_up_overflow, 2);
168    test_align_up_overflow_impl!(usize, test_usize_align_up_overflow, 2);
169}