use std::fmt::Display;
use std::ops::Deref;
use vortex_error::VortexError;
use vortex_error::VortexExpect;
use vortex_error::vortex_err;
pub const ALIGNMENT_TO_HOST_COPY: Alignment = Alignment::new(256);
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Alignment(usize);
impl Alignment {
#[inline]
pub const fn new(align: usize) -> Self {
assert!(align > 0, "Alignment must be greater than 0");
assert!(align <= u16::MAX as usize, "Alignment must fit into u16");
assert!(align.is_power_of_two(), "Alignment must be a power of 2");
Self(align)
}
#[inline]
pub const fn none() -> Self {
Self::new(1)
}
#[inline]
pub const fn of<T>() -> Self {
Self::new(align_of::<T>())
}
#[inline]
pub fn is_aligned_to(&self, other: Alignment) -> bool {
self.0.trailing_zeros() >= other.0.trailing_zeros()
}
pub fn exponent(&self) -> u8 {
u8::try_from(self.0.trailing_zeros())
.vortex_expect("alignment fits into u16, so exponent fits in u7")
}
#[inline]
pub const fn from_exponent(exponent: u8) -> Self {
Self::new(1 << exponent)
}
}
impl Display for Alignment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for Alignment {
type Target = usize;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<usize> for Alignment {
#[inline]
fn from(value: usize) -> Self {
Self::new(value)
}
}
impl From<u16> for Alignment {
#[inline]
fn from(value: u16) -> Self {
Self::new(usize::from(value))
}
}
impl From<Alignment> for usize {
#[inline]
fn from(value: Alignment) -> Self {
value.0
}
}
impl From<Alignment> for u16 {
#[inline]
fn from(value: Alignment) -> Self {
u16::try_from(value.0).vortex_expect("Alignment must fit into u16")
}
}
impl From<Alignment> for u32 {
#[inline]
fn from(value: Alignment) -> Self {
u32::try_from(value.0).vortex_expect("Alignment must fit into u32")
}
}
impl TryFrom<u32> for Alignment {
type Error = VortexError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
let value = usize::try_from(value)
.map_err(|_| vortex_err!("Alignment must fit into usize, got {value}"))?;
if value == 0 {
return Err(vortex_err!("Alignment must be greater than 0"));
}
if value > u16::MAX as usize {
return Err(vortex_err!("Alignment must fit into u16, got {value}"));
}
if !value.is_power_of_two() {
return Err(vortex_err!("Alignment must be a power of 2, got {value}"));
}
Ok(Self(value))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[should_panic]
fn alignment_zero() {
Alignment::new(0);
}
#[test]
#[should_panic]
fn alignment_overflow() {
Alignment::new(u16::MAX as usize + 1);
}
#[test]
#[should_panic]
fn alignment_not_power_of_two() {
Alignment::new(3);
}
#[test]
fn alignment_exponent() {
let alignment = Alignment::new(1024);
assert_eq!(alignment.exponent(), 10);
assert_eq!(Alignment::from_exponent(10), alignment);
}
#[test]
fn is_aligned_to() {
assert!(Alignment::new(1).is_aligned_to(Alignment::new(1)));
assert!(Alignment::new(2).is_aligned_to(Alignment::new(1)));
assert!(Alignment::new(4).is_aligned_to(Alignment::new(1)));
assert!(!Alignment::new(1).is_aligned_to(Alignment::new(2)));
}
#[test]
fn try_from_u32() {
match Alignment::try_from(8u32) {
Ok(alignment) => assert_eq!(alignment, Alignment::new(8)),
Err(err) => panic!("unexpected error for valid alignment: {err}"),
}
assert!(Alignment::try_from(0u32).is_err());
assert!(Alignment::try_from(3u32).is_err());
}
#[test]
fn into_u32() {
let alignment = Alignment::new(64);
assert_eq!(u32::from(alignment), 64u32);
}
}