vortex_buffer/
alignment.rs1use std::fmt::Display;
5use std::ops::Deref;
6
7use vortex_error::VortexError;
8use vortex_error::VortexExpect;
9use vortex_error::vortex_err;
10
11#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct Alignment(usize);
16
17impl Alignment {
18 pub const HOST_COPY: Self = Alignment::new(256);
20
21 pub const DEFAULT_ALIGNMENT: Self = Alignment::new(256);
29
30 #[inline]
36 pub const fn new(align: usize) -> Self {
37 assert!(align > 0, "Alignment must be greater than 0");
38 assert!(align.is_power_of_two(), "Alignment must be a power of 2");
39 Self(align)
40 }
41
42 #[inline]
44 pub const fn none() -> Self {
45 Self::new(1)
46 }
47
48 #[inline]
60 pub const fn of<T>() -> Self {
61 Self::new(align_of::<T>())
62 }
63
64 pub const MAX: Alignment = Alignment::new(1 << (usize::BITS - 1));
66
67 #[inline]
80 pub const fn is_aligned_to(&self, other: Alignment) -> bool {
81 self.0 >= other.0
83 }
84
85 #[inline]
97 pub const fn is_offset_aligned(&self, offset: usize) -> bool {
98 offset & (self.0 - 1) == 0
100 }
101
102 #[inline]
104 pub fn is_ptr_aligned<T>(&self, ptr: *const T) -> bool {
105 self.is_offset_aligned(ptr.addr())
106 }
107
108 pub fn exponent(&self) -> u8 {
110 u8::try_from(self.0.trailing_zeros())
111 .vortex_expect("alignment is a power of 2 within usize, so its exponent fits in u8")
112 }
113
114 #[inline]
120 pub const fn from_exponent(exponent: u8) -> Self {
121 Self::new(1 << exponent)
122 }
123}
124
125impl Display for Alignment {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 write!(f, "{}", self.0)
128 }
129}
130
131impl Deref for Alignment {
132 type Target = usize;
133
134 #[inline]
135 fn deref(&self) -> &Self::Target {
136 &self.0
137 }
138}
139
140impl From<usize> for Alignment {
141 #[inline]
142 fn from(value: usize) -> Self {
143 Self::new(value)
144 }
145}
146
147impl From<u16> for Alignment {
148 #[inline]
149 fn from(value: u16) -> Self {
150 Self::new(usize::from(value))
151 }
152}
153
154impl From<Alignment> for usize {
155 #[inline]
156 fn from(value: Alignment) -> Self {
157 value.0
158 }
159}
160
161impl From<Alignment> for u32 {
162 #[inline]
163 fn from(value: Alignment) -> Self {
164 u32::try_from(value.0).vortex_expect("Alignment must fit into u32")
165 }
166}
167
168impl TryFrom<u32> for Alignment {
169 type Error = VortexError;
170
171 fn try_from(value: u32) -> Result<Self, Self::Error> {
172 let value = usize::try_from(value)
173 .map_err(|_| vortex_err!("Alignment must fit into usize, got {value}"))?;
174
175 if value == 0 {
176 return Err(vortex_err!("Alignment must be greater than 0"));
177 }
178 if !value.is_power_of_two() {
179 return Err(vortex_err!("Alignment must be a power of 2, got {value}"));
180 }
181
182 Ok(Self(value))
183 }
184}
185
186#[cfg(test)]
187mod test {
188 use super::*;
189
190 #[test]
191 #[should_panic]
192 fn alignment_zero() {
193 Alignment::new(0);
194 }
195
196 #[test]
197 fn alignment_above_u16() {
198 let alignment = Alignment::new(u16::MAX as usize + 1);
200 assert_eq!(*alignment, 1 << 16);
201 assert_eq!(alignment, Alignment::from_exponent(16));
202 }
203
204 #[test]
205 #[should_panic]
206 fn alignment_not_power_of_two() {
207 Alignment::new(3);
208 }
209
210 #[test]
211 fn alignment_exponent() {
212 let alignment = Alignment::new(1024);
213 assert_eq!(alignment.exponent(), 10);
214 assert_eq!(Alignment::from_exponent(10), alignment);
215 }
216
217 #[test]
218 fn is_aligned_to() {
219 assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
220 assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
221 assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
222 assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
223 }
224
225 #[test]
226 fn try_from_u32() {
227 match Alignment::try_from(8u32) {
228 Ok(alignment) => assert_eq!(alignment, Alignment::new(8)),
229 Err(err) => panic!("unexpected error for valid alignment: {err}"),
230 }
231 match Alignment::try_from(1u32 << 16) {
232 Ok(alignment) => assert_eq!(alignment, Alignment::new(1 << 16)),
233 Err(err) => panic!("64KiB alignment should be valid: {err}"),
234 }
235 assert!(Alignment::try_from(0u32).is_err());
236 assert!(Alignment::try_from(3u32).is_err());
237 }
238
239 #[test]
240 fn into_u32() {
241 let alignment = Alignment::new(64);
242 assert_eq!(u32::from(alignment), 64u32);
243 }
244}