vortex_buffer/
alignment.rs1use std::fmt::Display;
5use std::ops::Deref;
6
7use vortex_error::VortexError;
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_error::vortex_err;
12
13#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Alignment(usize);
18
19impl Alignment {
20 pub const HOST_COPY: Self = Alignment::new(256);
22
23 pub const DEFAULT_ALIGNMENT: Self = Alignment::new(256);
31
32 #[inline]
38 pub const fn new(align: usize) -> Self {
39 assert!(align > 0, "Alignment must be greater than 0");
40 assert!(align.is_power_of_two(), "Alignment must be a power of 2");
41 Self(align)
42 }
43
44 #[inline]
46 pub const fn none() -> Self {
47 Self::new(1)
48 }
49
50 #[inline]
62 pub const fn of<T>() -> Self {
63 Self::new(align_of::<T>())
64 }
65
66 pub const MAX: Alignment = Alignment::new(1 << (usize::BITS - 1));
68
69 #[inline]
82 pub const fn is_aligned_to(&self, other: Alignment) -> bool {
83 self.0 >= other.0
85 }
86
87 #[inline]
99 pub const fn is_offset_aligned(&self, offset: usize) -> bool {
100 offset & (self.0 - 1) == 0
102 }
103
104 #[inline]
106 pub fn is_ptr_aligned<T>(&self, ptr: *const T) -> bool {
107 self.is_offset_aligned(ptr.addr())
108 }
109
110 pub fn exponent(&self) -> u8 {
112 u8::try_from(self.0.trailing_zeros())
113 .vortex_expect("alignment is a power of 2 within usize, so its exponent fits in u8")
114 }
115
116 #[inline]
122 pub const fn from_exponent(exponent: u8) -> Self {
123 Self::new(1 << exponent)
124 }
125
126 #[inline]
132 pub fn try_from_exponent(exponent: u8) -> VortexResult<Self> {
133 if u32::from(exponent) >= usize::BITS {
134 vortex_bail!(
135 "Alignment exponent {exponent} is too large for a {}-bit usize",
136 usize::BITS
137 );
138 }
139 Ok(Self::new(1 << exponent))
140 }
141}
142
143impl Display for Alignment {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(f, "{}", self.0)
146 }
147}
148
149impl Deref for Alignment {
150 type Target = usize;
151
152 #[inline]
153 fn deref(&self) -> &Self::Target {
154 &self.0
155 }
156}
157
158impl From<usize> for Alignment {
159 #[inline]
160 fn from(value: usize) -> Self {
161 Self::new(value)
162 }
163}
164
165impl From<u16> for Alignment {
166 #[inline]
167 fn from(value: u16) -> Self {
168 Self::new(usize::from(value))
169 }
170}
171
172impl From<Alignment> for usize {
173 #[inline]
174 fn from(value: Alignment) -> Self {
175 value.0
176 }
177}
178
179impl From<Alignment> for u32 {
180 #[inline]
181 fn from(value: Alignment) -> Self {
182 u32::try_from(value.0).vortex_expect("Alignment must fit into u32")
183 }
184}
185
186impl TryFrom<u32> for Alignment {
187 type Error = VortexError;
188
189 fn try_from(value: u32) -> Result<Self, Self::Error> {
190 let value = usize::try_from(value)
191 .map_err(|_| vortex_err!("Alignment must fit into usize, got {value}"))?;
192
193 if value == 0 {
194 return Err(vortex_err!("Alignment must be greater than 0"));
195 }
196 if !value.is_power_of_two() {
197 return Err(vortex_err!("Alignment must be a power of 2, got {value}"));
198 }
199
200 Ok(Self(value))
201 }
202}
203
204#[cfg(test)]
205mod test {
206 use super::*;
207
208 #[test]
209 #[should_panic]
210 fn alignment_zero() {
211 Alignment::new(0);
212 }
213
214 #[test]
215 fn alignment_above_u16() {
216 let alignment = Alignment::new(u16::MAX as usize + 1);
218 assert_eq!(*alignment, 1 << 16);
219 assert_eq!(alignment, Alignment::from_exponent(16));
220 }
221
222 #[test]
223 #[should_panic]
224 fn alignment_not_power_of_two() {
225 Alignment::new(3);
226 }
227
228 #[test]
229 fn alignment_exponent() {
230 let alignment = Alignment::new(1024);
231 assert_eq!(alignment.exponent(), 10);
232 assert_eq!(Alignment::from_exponent(10), alignment);
233 }
234
235 #[test]
236 fn is_aligned_to() {
237 assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
238 assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
239 assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
240 assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
241 }
242
243 #[test]
244 fn try_from_u32() {
245 match Alignment::try_from(8u32) {
246 Ok(alignment) => assert_eq!(alignment, Alignment::new(8)),
247 Err(err) => panic!("unexpected error for valid alignment: {err}"),
248 }
249 match Alignment::try_from(1u32 << 16) {
250 Ok(alignment) => assert_eq!(alignment, Alignment::new(1 << 16)),
251 Err(err) => panic!("64KiB alignment should be valid: {err}"),
252 }
253 assert!(Alignment::try_from(0u32).is_err());
254 assert!(Alignment::try_from(3u32).is_err());
255 }
256
257 #[test]
258 fn try_from_exponent() {
259 match Alignment::try_from_exponent(10) {
260 Ok(alignment) => assert_eq!(alignment, Alignment::new(1024)),
261 Err(err) => panic!("valid exponent should succeed: {err}"),
262 }
263 assert!(Alignment::try_from_exponent(64).is_err());
266 assert!(Alignment::try_from_exponent(u8::MAX).is_err());
267 }
268
269 #[test]
270 fn into_u32() {
271 let alignment = Alignment::new(64);
272 assert_eq!(u32::from(alignment), 64u32);
273 }
274}