vortex_buffer/
alignment.rs

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