vortex_buffer/
alignment.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt::Display;
5use std::ops::Deref;
6
7use vortex_error::VortexExpect;
8
9/// The alignment of a buffer.
10///
11/// This type is a wrapper around `usize` that ensures the alignment is a power of 2 and fits into
12/// a `u16`.
13#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Alignment(usize);
15
16impl Alignment {
17    /// Create a new alignment.
18    ///
19    /// ## Panics
20    ///
21    /// Panics if `align` is not a power of 2, or is greater than `u16::MAX`.
22    #[inline]
23    pub const fn new(align: usize) -> Self {
24        assert!(align > 0, "Alignment must be greater than 0");
25        assert!(align <= u16::MAX as usize, "Alignment must fit into u16");
26        assert!(align.is_power_of_two(), "Alignment must be a power of 2");
27        Self(align)
28    }
29
30    /// Create a new 1-byte alignment.
31    #[inline]
32    pub const fn none() -> Self {
33        Self::new(1)
34    }
35
36    /// Create an alignment from the alignment of a type `T`.
37    ///
38    /// ## Example
39    ///
40    /// ```
41    /// use vortex_buffer::Alignment;
42    ///
43    /// assert_eq!(Alignment::new(4), Alignment::of::<i32>());
44    /// assert_eq!(Alignment::new(8), Alignment::of::<i64>());
45    /// assert_eq!(Alignment::new(16), Alignment::of::<u128>());
46    /// ```
47    #[inline]
48    pub const fn of<T>() -> Self {
49        Self::new(align_of::<T>())
50    }
51
52    /// Check if this alignment is a "larger" than another alignment.
53    ///
54    /// ## Example
55    ///
56    /// ```
57    /// use vortex_buffer::Alignment;
58    ///
59    /// let a = Alignment::new(4);
60    /// let b = Alignment::new(2);
61    /// assert!(a.is_aligned_to(b));
62    /// assert!(!b.is_aligned_to(a));
63    /// ```
64    #[inline]
65    pub fn is_aligned_to(&self, other: Alignment) -> bool {
66        // Since we know alignments are powers of 2, we can compare them by checking if the number
67        // of trailing zeros in the binary representation of the alignment is greater or equal.
68        self.0.trailing_zeros() >= other.0.trailing_zeros()
69    }
70
71    /// Returns the log2 of the alignment.
72    pub fn exponent(&self) -> u8 {
73        u8::try_from(self.0.trailing_zeros())
74            .vortex_expect("alignment fits into u16, so exponent fits in u7")
75    }
76
77    /// Create from the log2 exponent of the alignment.
78    ///
79    /// ## Panics
80    ///
81    /// Panics if `alignment` is not a power of 2, or is greater than `u16::MAX`.
82    #[inline]
83    pub const fn from_exponent(exponent: u8) -> Self {
84        Self::new(1 << exponent)
85    }
86}
87
88impl Display for Alignment {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        write!(f, "{}", self.0)
91    }
92}
93
94impl Deref for Alignment {
95    type Target = usize;
96
97    #[inline]
98    fn deref(&self) -> &Self::Target {
99        &self.0
100    }
101}
102
103impl From<usize> for Alignment {
104    #[inline]
105    fn from(value: usize) -> Self {
106        Self::new(value)
107    }
108}
109
110impl From<u16> for Alignment {
111    #[inline]
112    fn from(value: u16) -> Self {
113        Self::new(usize::from(value))
114    }
115}
116
117impl From<Alignment> for usize {
118    #[inline]
119    fn from(value: Alignment) -> Self {
120        value.0
121    }
122}
123
124impl From<Alignment> for u16 {
125    #[inline]
126    fn from(value: Alignment) -> Self {
127        u16::try_from(value.0).vortex_expect("Alignment must fit into u16")
128    }
129}
130
131#[cfg(test)]
132mod test {
133    use super::*;
134
135    #[test]
136    #[should_panic]
137    fn alignment_zero() {
138        Alignment::new(0);
139    }
140
141    #[test]
142    #[should_panic]
143    fn alignment_overflow() {
144        Alignment::new(u16::MAX as usize + 1);
145    }
146
147    #[test]
148    #[should_panic]
149    fn alignment_not_power_of_two() {
150        Alignment::new(3);
151    }
152
153    #[test]
154    fn alignment_exponent() {
155        let alignment = Alignment::new(1024);
156        assert_eq!(alignment.exponent(), 10);
157        assert_eq!(Alignment::from_exponent(10), alignment);
158    }
159
160    #[test]
161    fn is_aligned_to() {
162        assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
163        assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
164        assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
165        assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
166    }
167}