vortex_buffer/
alignment.rs1use std::fmt::Display;
5
6use vortex_error::VortexError;
7use vortex_error::VortexExpect;
8use vortex_error::VortexResult;
9use vortex_error::vortex_bail;
10use vortex_error::vortex_err;
11
12#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct Alignment(u8);
17
18impl Alignment {
19 pub const MAX_UNTRUSTED: Self = Alignment::new(64 * 1024);
24
25 pub const HOST_COPY: Self = Alignment::new(256);
27
28 pub const DEFAULT_ALIGNMENT: Self = Alignment::new(256);
36
37 #[inline]
43 #[expect(clippy::cast_possible_truncation, reason = "usize has at most 64 bits")]
44 pub const fn new(align: usize) -> Self {
45 assert!(align > 0, "Alignment must be greater than 0");
46 assert!(align.is_power_of_two(), "Alignment must be a power of 2");
47 Self(align.trailing_zeros() as u8)
48 }
49
50 #[inline]
52 pub const fn none() -> Self {
53 Self::new(1)
54 }
55
56 #[inline]
68 pub const fn of<T>() -> Self {
69 Self::new(align_of::<T>())
70 }
71
72 pub const MAX: Alignment = Alignment::new(1 << (usize::BITS - 1));
74
75 #[inline]
88 pub const fn is_aligned_to(&self, other: Alignment) -> bool {
89 self.0 >= other.0
91 }
92
93 #[inline]
105 pub const fn is_offset_aligned(&self, offset: usize) -> bool {
106 offset & (self.as_usize() - 1) == 0
108 }
109
110 #[inline]
112 pub fn is_ptr_aligned<T>(&self, ptr: *const T) -> bool {
113 self.is_offset_aligned(ptr.addr())
114 }
115
116 pub fn exponent(&self) -> u8 {
118 self.0
119 }
120
121 #[inline]
128 pub const fn from_exponent(exponent: u8) -> Self {
129 assert!(
130 (exponent as u32) < usize::BITS,
131 "Alignment exponent must fit in usize"
132 );
133 Self(exponent)
134 }
135
136 #[inline]
142 pub fn try_from_exponent(exponent: u8) -> VortexResult<Self> {
143 if u32::from(exponent) >= usize::BITS {
144 vortex_bail!(
145 "Alignment exponent {exponent} is too large for a {}-bit usize",
146 usize::BITS
147 );
148 }
149 Ok(Self::new(1 << exponent))
150 }
151
152 #[inline]
158 pub fn try_from_untrusted_exponent(exponent: u8) -> VortexResult<Self> {
159 let alignment = Self::try_from_exponent(exponent)?;
160 if alignment > Self::MAX_UNTRUSTED {
161 vortex_bail!(
162 "Untrusted alignment {alignment} exceeds the {}-byte maximum",
163 Self::MAX_UNTRUSTED
164 );
165 }
166 Ok(alignment)
167 }
168
169 #[inline]
171 pub const fn as_usize(self) -> usize {
172 1 << self.0
173 }
174}
175
176impl Display for Alignment {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 write!(f, "{}", self.as_usize())
179 }
180}
181
182impl From<usize> for Alignment {
183 #[inline]
184 fn from(value: usize) -> Self {
185 Self::new(value)
186 }
187}
188
189impl From<u16> for Alignment {
190 #[inline]
191 fn from(value: u16) -> Self {
192 Self::new(usize::from(value))
193 }
194}
195
196impl From<Alignment> for usize {
197 #[inline]
198 fn from(value: Alignment) -> Self {
199 value.as_usize()
200 }
201}
202
203impl From<Alignment> for u32 {
204 #[inline]
205 fn from(value: Alignment) -> Self {
206 u32::try_from(value.as_usize()).vortex_expect("Alignment must fit into u32")
207 }
208}
209
210impl TryFrom<u32> for Alignment {
211 type Error = VortexError;
212
213 fn try_from(value: u32) -> Result<Self, Self::Error> {
214 let value = usize::try_from(value)
215 .map_err(|_| vortex_err!("Alignment must fit into usize, got {value}"))?;
216
217 if value == 0 {
218 return Err(vortex_err!("Alignment must be greater than 0"));
219 }
220 if !value.is_power_of_two() {
221 return Err(vortex_err!("Alignment must be a power of 2, got {value}"));
222 }
223
224 Ok(Self::new(value))
225 }
226}
227
228#[cfg(test)]
229mod test {
230 use super::*;
231
232 #[test]
233 #[should_panic]
234 fn alignment_zero() {
235 Alignment::new(0);
236 }
237
238 #[test]
239 fn alignment_above_u16() {
240 let alignment = Alignment::new(u16::MAX as usize + 1);
242 assert_eq!(alignment.as_usize(), 1 << 16);
243 assert_eq!(alignment, Alignment::from_exponent(16));
244 }
245
246 #[test]
247 #[should_panic]
248 fn alignment_not_power_of_two() {
249 Alignment::new(3);
250 }
251
252 #[test]
253 fn alignment_exponent() {
254 let alignment = Alignment::new(1024);
255 assert_eq!(alignment.exponent(), 10);
256 assert_eq!(Alignment::from_exponent(10), alignment);
257 }
258
259 #[test]
260 fn is_aligned_to() {
261 assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
262 assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
263 assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
264 assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
265 }
266
267 #[test]
268 fn try_from_u32() {
269 match Alignment::try_from(8u32) {
270 Ok(alignment) => assert_eq!(alignment, Alignment::new(8)),
271 Err(err) => panic!("unexpected error for valid alignment: {err}"),
272 }
273 match Alignment::try_from(1u32 << 16) {
274 Ok(alignment) => assert_eq!(alignment, Alignment::new(1 << 16)),
275 Err(err) => panic!("64KiB alignment should be valid: {err}"),
276 }
277 assert!(Alignment::try_from(0u32).is_err());
278 assert!(Alignment::try_from(3u32).is_err());
279 }
280
281 #[test]
282 fn try_from_exponent() {
283 match Alignment::try_from_exponent(10) {
284 Ok(alignment) => assert_eq!(alignment, Alignment::new(1024)),
285 Err(err) => panic!("valid exponent should succeed: {err}"),
286 }
287 assert!(Alignment::try_from_exponent(64).is_err());
290 assert!(Alignment::try_from_exponent(u8::MAX).is_err());
291 }
292
293 #[test]
294 fn try_from_untrusted_exponent() {
295 assert_eq!(
296 Alignment::try_from_untrusted_exponent(16).unwrap(),
297 Alignment::new(64 * 1024)
298 );
299 assert!(Alignment::try_from_untrusted_exponent(17).is_err());
300 assert!(Alignment::try_from_untrusted_exponent(u8::MAX).is_err());
301 }
302
303 #[test]
304 fn into_u32() {
305 let alignment = Alignment::new(64);
306 assert_eq!(u32::from(alignment), 64u32);
307 }
308}