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 MAX_UNTRUSTED: Self = Alignment::new(64 * 1024);
25
26 pub const HOST_COPY: Self = Alignment::new(256);
28
29 pub const DEFAULT_ALIGNMENT: Self = Alignment::new(256);
37
38 #[inline]
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)
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.0 - 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 u8::try_from(self.0.trailing_zeros())
119 .vortex_expect("alignment is a power of 2 within usize, so its exponent fits in u8")
120 }
121
122 #[inline]
129 pub const fn from_exponent(exponent: u8) -> Self {
130 assert!(
131 (exponent as u32) < usize::BITS,
132 "Alignment exponent must fit in usize"
133 );
134 Self::new(1 << exponent)
135 }
136
137 #[inline]
143 pub fn try_from_exponent(exponent: u8) -> VortexResult<Self> {
144 if u32::from(exponent) >= usize::BITS {
145 vortex_bail!(
146 "Alignment exponent {exponent} is too large for a {}-bit usize",
147 usize::BITS
148 );
149 }
150 Ok(Self::new(1 << exponent))
151 }
152
153 #[inline]
159 pub fn try_from_untrusted_exponent(exponent: u8) -> VortexResult<Self> {
160 let alignment = Self::try_from_exponent(exponent)?;
161 if alignment > Self::MAX_UNTRUSTED {
162 vortex_bail!(
163 "Untrusted alignment {alignment} exceeds the {}-byte maximum",
164 Self::MAX_UNTRUSTED
165 );
166 }
167 Ok(alignment)
168 }
169}
170
171impl Display for Alignment {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 write!(f, "{}", self.0)
174 }
175}
176
177impl Deref for Alignment {
178 type Target = usize;
179
180 #[inline]
181 fn deref(&self) -> &Self::Target {
182 &self.0
183 }
184}
185
186impl From<usize> for Alignment {
187 #[inline]
188 fn from(value: usize) -> Self {
189 Self::new(value)
190 }
191}
192
193impl From<u16> for Alignment {
194 #[inline]
195 fn from(value: u16) -> Self {
196 Self::new(usize::from(value))
197 }
198}
199
200impl From<Alignment> for usize {
201 #[inline]
202 fn from(value: Alignment) -> Self {
203 value.0
204 }
205}
206
207impl From<Alignment> for u32 {
208 #[inline]
209 fn from(value: Alignment) -> Self {
210 u32::try_from(value.0).vortex_expect("Alignment must fit into u32")
211 }
212}
213
214impl TryFrom<u32> for Alignment {
215 type Error = VortexError;
216
217 fn try_from(value: u32) -> Result<Self, Self::Error> {
218 let value = usize::try_from(value)
219 .map_err(|_| vortex_err!("Alignment must fit into usize, got {value}"))?;
220
221 if value == 0 {
222 return Err(vortex_err!("Alignment must be greater than 0"));
223 }
224 if !value.is_power_of_two() {
225 return Err(vortex_err!("Alignment must be a power of 2, got {value}"));
226 }
227
228 Ok(Self(value))
229 }
230}
231
232#[cfg(test)]
233mod test {
234 use super::*;
235
236 #[test]
237 #[should_panic]
238 fn alignment_zero() {
239 Alignment::new(0);
240 }
241
242 #[test]
243 fn alignment_above_u16() {
244 let alignment = Alignment::new(u16::MAX as usize + 1);
246 assert_eq!(*alignment, 1 << 16);
247 assert_eq!(alignment, Alignment::from_exponent(16));
248 }
249
250 #[test]
251 #[should_panic]
252 fn alignment_not_power_of_two() {
253 Alignment::new(3);
254 }
255
256 #[test]
257 fn alignment_exponent() {
258 let alignment = Alignment::new(1024);
259 assert_eq!(alignment.exponent(), 10);
260 assert_eq!(Alignment::from_exponent(10), alignment);
261 }
262
263 #[test]
264 fn is_aligned_to() {
265 assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
266 assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
267 assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
268 assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
269 }
270
271 #[test]
272 fn try_from_u32() {
273 match Alignment::try_from(8u32) {
274 Ok(alignment) => assert_eq!(alignment, Alignment::new(8)),
275 Err(err) => panic!("unexpected error for valid alignment: {err}"),
276 }
277 match Alignment::try_from(1u32 << 16) {
278 Ok(alignment) => assert_eq!(alignment, Alignment::new(1 << 16)),
279 Err(err) => panic!("64KiB alignment should be valid: {err}"),
280 }
281 assert!(Alignment::try_from(0u32).is_err());
282 assert!(Alignment::try_from(3u32).is_err());
283 }
284
285 #[test]
286 fn try_from_exponent() {
287 match Alignment::try_from_exponent(10) {
288 Ok(alignment) => assert_eq!(alignment, Alignment::new(1024)),
289 Err(err) => panic!("valid exponent should succeed: {err}"),
290 }
291 assert!(Alignment::try_from_exponent(64).is_err());
294 assert!(Alignment::try_from_exponent(u8::MAX).is_err());
295 }
296
297 #[test]
298 fn try_from_untrusted_exponent() {
299 assert_eq!(
300 Alignment::try_from_untrusted_exponent(16).unwrap(),
301 Alignment::new(64 * 1024)
302 );
303 assert!(Alignment::try_from_untrusted_exponent(17).is_err());
304 assert!(Alignment::try_from_untrusted_exponent(u8::MAX).is_err());
305 }
306
307 #[test]
308 fn into_u32() {
309 let alignment = Alignment::new(64);
310 assert_eq!(u32::from(alignment), 64u32);
311 }
312}