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