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]
123 pub const fn from_exponent(exponent: u8) -> Self {
124 assert!(
125 (exponent as u32) < usize::BITS,
126 "Alignment exponent must fit in usize"
127 );
128 Self::new(1 << exponent)
129 }
130
131 #[inline]
137 pub fn try_from_exponent(exponent: u8) -> VortexResult<Self> {
138 if u32::from(exponent) >= usize::BITS {
139 vortex_bail!(
140 "Alignment exponent {exponent} is too large for a {}-bit usize",
141 usize::BITS
142 );
143 }
144 Ok(Self::new(1 << exponent))
145 }
146}
147
148impl Display for Alignment {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 write!(f, "{}", self.0)
151 }
152}
153
154impl Deref for Alignment {
155 type Target = usize;
156
157 #[inline]
158 fn deref(&self) -> &Self::Target {
159 &self.0
160 }
161}
162
163impl From<usize> for Alignment {
164 #[inline]
165 fn from(value: usize) -> Self {
166 Self::new(value)
167 }
168}
169
170impl From<u16> for Alignment {
171 #[inline]
172 fn from(value: u16) -> Self {
173 Self::new(usize::from(value))
174 }
175}
176
177impl From<Alignment> for usize {
178 #[inline]
179 fn from(value: Alignment) -> Self {
180 value.0
181 }
182}
183
184impl From<Alignment> for u32 {
185 #[inline]
186 fn from(value: Alignment) -> Self {
187 u32::try_from(value.0).vortex_expect("Alignment must fit into u32")
188 }
189}
190
191impl TryFrom<u32> for Alignment {
192 type Error = VortexError;
193
194 fn try_from(value: u32) -> Result<Self, Self::Error> {
195 let value = usize::try_from(value)
196 .map_err(|_| vortex_err!("Alignment must fit into usize, got {value}"))?;
197
198 if value == 0 {
199 return Err(vortex_err!("Alignment must be greater than 0"));
200 }
201 if !value.is_power_of_two() {
202 return Err(vortex_err!("Alignment must be a power of 2, got {value}"));
203 }
204
205 Ok(Self(value))
206 }
207}
208
209#[cfg(test)]
210mod test {
211 use super::*;
212
213 #[test]
214 #[should_panic]
215 fn alignment_zero() {
216 Alignment::new(0);
217 }
218
219 #[test]
220 fn alignment_above_u16() {
221 let alignment = Alignment::new(u16::MAX as usize + 1);
223 assert_eq!(*alignment, 1 << 16);
224 assert_eq!(alignment, Alignment::from_exponent(16));
225 }
226
227 #[test]
228 #[should_panic]
229 fn alignment_not_power_of_two() {
230 Alignment::new(3);
231 }
232
233 #[test]
234 fn alignment_exponent() {
235 let alignment = Alignment::new(1024);
236 assert_eq!(alignment.exponent(), 10);
237 assert_eq!(Alignment::from_exponent(10), alignment);
238 }
239
240 #[test]
241 fn is_aligned_to() {
242 assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
243 assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
244 assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
245 assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
246 }
247
248 #[test]
249 fn try_from_u32() {
250 match Alignment::try_from(8u32) {
251 Ok(alignment) => assert_eq!(alignment, Alignment::new(8)),
252 Err(err) => panic!("unexpected error for valid alignment: {err}"),
253 }
254 match Alignment::try_from(1u32 << 16) {
255 Ok(alignment) => assert_eq!(alignment, Alignment::new(1 << 16)),
256 Err(err) => panic!("64KiB alignment should be valid: {err}"),
257 }
258 assert!(Alignment::try_from(0u32).is_err());
259 assert!(Alignment::try_from(3u32).is_err());
260 }
261
262 #[test]
263 fn try_from_exponent() {
264 match Alignment::try_from_exponent(10) {
265 Ok(alignment) => assert_eq!(alignment, Alignment::new(1024)),
266 Err(err) => panic!("valid exponent should succeed: {err}"),
267 }
268 assert!(Alignment::try_from_exponent(64).is_err());
271 assert!(Alignment::try_from_exponent(u8::MAX).is_err());
272 }
273
274 #[test]
275 fn into_u32() {
276 let alignment = Alignment::new(64);
277 assert_eq!(u32::from(alignment), 64u32);
278 }
279}