Skip to main content

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//! * [`checked_align_up`]
6//! * [`align_up`]
7//! * [`is_aligned_to`]
8//!
9//! [`align_down`]: Align::align_down
10//! [`align_up`]: Align::align_up
11//! [`checked_align_up`]: Align::checked_align_up
12//! [`is_aligned_to`]: Align::is_aligned_to
13//!
14//! This crate is based on work from the [`x86_64`] crate, but is available for all architectures and all unsigned integer types.
15//!
16//! [`x86_64`]: https://docs.rs/x86_64
17//!
18//! # Examples
19//!
20//! ```
21//! use align_address::Align;
22//!
23//! assert_eq!(123_u64.align_up(2), 124);
24//! ```
25
26#![no_std]
27#![forbid(unsafe_code)]
28
29/// An adress that can be aligned.
30pub trait Align<A = Self>: Copy + PartialEq {
31    /// Align address downwards.
32    ///
33    /// Returns the greatest `x` with alignment `align` so that `x <= addr`.
34    ///
35    /// Panics if the alignment is not a power of two.
36    #[must_use = "this returns the result of the operation, \
37                  without modifying the original"]
38    #[track_caller]
39    fn align_down(self, align: A) -> Self;
40
41    /// Checked align address upwards.
42    ///
43    /// Returns the smallest `x` with alignment `align` so that `x >= addr`.
44    ///
45    /// Returns `None` if an overflow occurs.
46    ///
47    /// Panics if the alignment is not a power of two.
48    #[must_use = "this returns the result of the operation, \
49                  without modifying the original"]
50    #[track_caller]
51    fn checked_align_up(self, align: A) -> Option<Self>;
52
53    /// Align address upwards.
54    ///
55    /// Returns the smallest `x` with alignment `align` so that `x >= addr`.
56    ///
57    /// Panics if the alignment is not a power of two or if an overflow occurs.
58    #[must_use = "this returns the result of the operation, \
59                  without modifying the original"]
60    #[inline]
61    #[track_caller]
62    fn align_up(self, align: A) -> Self {
63        self.checked_align_up(align)
64            .expect("attempt to add with overflow")
65    }
66
67    /// Checks whether the address has the demanded alignment.
68    #[allow(clippy::wrong_self_convention)]
69    #[must_use]
70    #[inline]
71    #[track_caller]
72    fn is_aligned_to(self, align: A) -> bool {
73        self.align_down(align) == self
74    }
75}
76
77macro_rules! align_impl {
78    ($u:ty, $align_down:ident, $checked_align_up:ident, $align_up:ident, $is_aligned_to:ident) => {
79        /// Align address downwards.
80        ///
81        /// Returns the greatest `x` with alignment `align` so that `x <= addr`.
82        ///
83        /// Panics if the alignment is not a power of two.
84        ///
85        /// This is a `const` version of [`Align::align_down`].
86        // Adapted from `x86_64`
87        #[must_use = "this returns the result of the operation, \
88                      without modifying the original"]
89        #[inline]
90        #[track_caller]
91        pub const fn $align_down(addr: $u, align: $u) -> $u {
92            assert!(align.is_power_of_two(), "`align` must be a power of two");
93            addr & !(align - 1)
94        }
95
96        /// Checked align address upwards.
97        ///
98        /// Returns the smallest `x` with alignment `align` so that `x >= addr`.
99        ///
100        /// Returns `None` if an overflow occurs.
101        ///
102        /// Panics if the alignment is not a power of two.
103        ///
104        /// This is a `const` version of [`Align::checked_align_up`].
105        // Adapted from `x86_64`
106        #[must_use = "this returns the result of the operation, \
107                      without modifying the original"]
108        #[inline]
109        #[track_caller]
110        pub const fn $checked_align_up(addr: $u, align: $u) -> Option<$u> {
111            assert!(align.is_power_of_two(), "`align` must be a power of two");
112            let align_mask = align - 1;
113            if addr & align_mask == 0 {
114                // already aligned
115                return Some(addr);
116            }
117
118            (addr | align_mask).checked_add(1)
119        }
120
121        /// Align address upwards.
122        ///
123        /// Returns the smallest `x` with alignment `align` so that `x >= addr`.
124        ///
125        /// Panics if the alignment is not a power of two or if an overflow occurs.
126        ///
127        /// This is a `const` version of [`Align::align_up`].
128        // Adapted from `x86_64`
129        #[must_use = "this returns the result of the operation, \
130                      without modifying the original"]
131        #[inline]
132        #[track_caller]
133        pub const fn $align_up(addr: $u, align: $u) -> $u {
134            // FIXME: Replace with .expect, once `Option::expect` is const.
135            if let Some(aligned) = $checked_align_up(addr, align) {
136                aligned
137            } else {
138                panic!("attempt to add with overflow")
139            }
140        }
141
142        /// Checks whether the address has the demanded alignment.
143        ///
144        /// This is a `const` version of [`Align::is_aligned_to`].
145        #[must_use]
146        #[inline]
147        #[track_caller]
148        pub const fn $is_aligned_to(addr: $u, align: $u) -> bool {
149            $align_down(addr, align) == addr
150        }
151
152        impl Align for $u {
153            #[inline]
154            fn align_down(self, align: Self) -> Self {
155                $align_down(self, align)
156            }
157
158            #[inline]
159            fn checked_align_up(self, align: Self) -> Option<Self> {
160                $checked_align_up(self, align)
161            }
162        }
163    };
164}
165
166align_impl!(
167    u8,
168    u8_align_down,
169    u8_checked_align_up,
170    u8_align_up,
171    u8_is_aligned_to
172);
173align_impl!(
174    u16,
175    u16_align_down,
176    u16_checked_align_up,
177    u16_align_up,
178    u16_is_aligned_to
179);
180align_impl!(
181    u32,
182    u32_align_down,
183    u32_checked_align_up,
184    u32_align_up,
185    u32_is_aligned_to
186);
187align_impl!(
188    u64,
189    u64_align_down,
190    u64_checked_align_up,
191    u64_align_up,
192    u64_is_aligned_to
193);
194align_impl!(
195    u128,
196    u128_align_down,
197    u128_checked_align_up,
198    u128_align_up,
199    u128_is_aligned_to
200);
201align_impl!(
202    usize,
203    usize_align_down,
204    usize_checked_align_up,
205    usize_align_up,
206    usize_is_aligned_to
207);
208
209// Adapted from `x86_64`
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    macro_rules! test_checked_align_up_impl {
215        ($u:ty, $checked_align_up:ident, $test_checked_align_up:ident) => {
216            #[test]
217            fn $test_checked_align_up() {
218                // align 1
219                assert_eq!($checked_align_up(0, 1), Some(0));
220                assert_eq!($checked_align_up(123, 1), Some(123));
221                assert_eq!($checked_align_up(<$u>::MAX, 1), Some(<$u>::MAX));
222                // align 2
223                assert_eq!($checked_align_up(0, 2), Some(0));
224                assert_eq!($checked_align_up(123, 2), Some(124));
225                assert_eq!($checked_align_up(<$u>::MAX - 1, 2), Some(<$u>::MAX - 1));
226                // address 0
227                assert_eq!($checked_align_up(0, 128), Some(0));
228                assert_eq!($checked_align_up(0, 1), Some(0));
229                assert_eq!($checked_align_up(0, 2), Some(0));
230                assert_eq!(
231                    $checked_align_up(0, <$u>::MAX & 1 << (<$u>::BITS - 1)),
232                    Some(0)
233                );
234                // address MAX
235                assert_eq!($checked_align_up(<$u>::MAX, 2), None);
236            }
237        };
238    }
239
240    test_checked_align_up_impl!(u8, u8_checked_align_up, test_u8_checked_align_up);
241    test_checked_align_up_impl!(u16, u16_checked_align_up, test_u16_checked_align_up);
242    test_checked_align_up_impl!(u32, u32_checked_align_up, test_u32_checked_align_up);
243    test_checked_align_up_impl!(u64, u64_checked_align_up, test_u64_checked_align_up);
244    test_checked_align_up_impl!(u128, u128_checked_align_up, test_u128_checked_align_up);
245    test_checked_align_up_impl!(usize, usize_checked_align_up, test_usize_checked_align_up);
246}