use crate::{DeviceSize, NonZeroDeviceSize}; use std::{ cmp::Ordering, error::Error, fmt::{Debug, Display, Formatter, Result as FmtResult}, hash::{Hash, Hasher}, mem::{self, align_of, size_of}, }; /// Vulkan analog of std's [`Alignment`], stored as a [`DeviceSize`] that is guaranteed to be a /// valid Vulkan alignment. /// /// [`Alignment`]: std::ptr::Alignment #[derive(Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct DeviceAlignment(AlignmentEnum); const _: () = assert!(size_of::() == size_of::()); const _: () = assert!(align_of::() == align_of::()); impl DeviceAlignment { /// The smallest possible alignment, 1. pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0); /// The largest possible alignment, 263. pub const MAX: Self = Self(AlignmentEnum::_Align1Shl63); /// Returns the alignment for a type. #[inline] pub const fn of() -> Self { #[cfg(any( target_pointer_width = "64", target_pointer_width = "32", target_pointer_width = "16", ))] { const _: () = assert!(size_of::() >= size_of::()); // SAFETY: rustc guarantees that the alignment of types is a power of two. unsafe { DeviceAlignment::new_unchecked(align_of::() as DeviceSize) } } } /// Tries to create a `DeviceAlignment` from a [`DeviceSize`], returning [`None`] if it's not a /// power of two. #[inline] pub const fn new(alignment: DeviceSize) -> Option { if alignment.is_power_of_two() { Some(unsafe { DeviceAlignment::new_unchecked(alignment) }) } else { None } } /// Creates a `DeviceAlignment` from a [`DeviceSize`] without checking if it's a power of two. /// /// # Safety /// /// - `alignment` must be a power of two, which also means it must be non-zero. #[inline] pub const unsafe fn new_unchecked(alignment: DeviceSize) -> Self { debug_assert!(alignment.is_power_of_two()); unsafe { mem::transmute::(alignment) } } /// Returns the alignment as a [`DeviceSize`]. #[inline] pub const fn as_devicesize(self) -> DeviceSize { self.0 as DeviceSize } /// Returns the alignment as a [`NonZeroDeviceSize`]. #[inline] pub const fn as_nonzero(self) -> NonZeroDeviceSize { // SAFETY: All the discriminants are non-zero. unsafe { NonZeroDeviceSize::new_unchecked(self.as_devicesize()) } } /// Returns the base-2 logarithm of the alignment. #[inline] pub const fn log2(self) -> u32 { self.as_nonzero().trailing_zeros() } // TODO: Replace with `Ord::max` once its constness is stabilized. #[inline(always)] pub(crate) const fn max(self, other: Self) -> Self { if self.as_devicesize() >= other.as_devicesize() { self } else { other } } } impl Debug for DeviceAlignment { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { write!(f, "{:?} (1 << {:?})", self.as_nonzero(), self.log2()) } } impl Default for DeviceAlignment { #[inline] fn default() -> Self { DeviceAlignment::MIN } } impl TryFrom for DeviceAlignment { type Error = TryFromIntError; #[inline] fn try_from(alignment: NonZeroDeviceSize) -> Result { if alignment.is_power_of_two() { Ok(unsafe { DeviceAlignment::new_unchecked(alignment.get()) }) } else { Err(TryFromIntError) } } } impl TryFrom for DeviceAlignment { type Error = TryFromIntError; #[inline] fn try_from(alignment: DeviceSize) -> Result { DeviceAlignment::new(alignment).ok_or(TryFromIntError) } } impl From for NonZeroDeviceSize { #[inline] fn from(alignment: DeviceAlignment) -> Self { alignment.as_nonzero() } } impl From for DeviceSize { #[inline] fn from(alignment: DeviceAlignment) -> Self { alignment.as_devicesize() } } // This is a false-positive, the underlying values that this impl and the derived `PartialEq` work // with are the same. #[allow(clippy::derived_hash_with_manual_eq)] impl Hash for DeviceAlignment { #[inline] fn hash(&self, state: &mut H) { self.as_nonzero().hash(state); } } impl PartialOrd for DeviceAlignment { #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.as_nonzero().partial_cmp(&other.as_nonzero()) } } impl Ord for DeviceAlignment { #[inline] fn cmp(&self, other: &Self) -> Ordering { self.as_nonzero().cmp(&other.as_nonzero()) } } #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u64)] enum AlignmentEnum { _Align1Shl0 = 1 << 0, _Align1Shl1 = 1 << 1, _Align1Shl2 = 1 << 2, _Align1Shl3 = 1 << 3, _Align1Shl4 = 1 << 4, _Align1Shl5 = 1 << 5, _Align1Shl6 = 1 << 6, _Align1Shl7 = 1 << 7, _Align1Shl8 = 1 << 8, _Align1Shl9 = 1 << 9, _Align1Shl10 = 1 << 10, _Align1Shl11 = 1 << 11, _Align1Shl12 = 1 << 12, _Align1Shl13 = 1 << 13, _Align1Shl14 = 1 << 14, _Align1Shl15 = 1 << 15, _Align1Shl16 = 1 << 16, _Align1Shl17 = 1 << 17, _Align1Shl18 = 1 << 18, _Align1Shl19 = 1 << 19, _Align1Shl20 = 1 << 20, _Align1Shl21 = 1 << 21, _Align1Shl22 = 1 << 22, _Align1Shl23 = 1 << 23, _Align1Shl24 = 1 << 24, _Align1Shl25 = 1 << 25, _Align1Shl26 = 1 << 26, _Align1Shl27 = 1 << 27, _Align1Shl28 = 1 << 28, _Align1Shl29 = 1 << 29, _Align1Shl30 = 1 << 30, _Align1Shl31 = 1 << 31, _Align1Shl32 = 1 << 32, _Align1Shl33 = 1 << 33, _Align1Shl34 = 1 << 34, _Align1Shl35 = 1 << 35, _Align1Shl36 = 1 << 36, _Align1Shl37 = 1 << 37, _Align1Shl38 = 1 << 38, _Align1Shl39 = 1 << 39, _Align1Shl40 = 1 << 40, _Align1Shl41 = 1 << 41, _Align1Shl42 = 1 << 42, _Align1Shl43 = 1 << 43, _Align1Shl44 = 1 << 44, _Align1Shl45 = 1 << 45, _Align1Shl46 = 1 << 46, _Align1Shl47 = 1 << 47, _Align1Shl48 = 1 << 48, _Align1Shl49 = 1 << 49, _Align1Shl50 = 1 << 50, _Align1Shl51 = 1 << 51, _Align1Shl52 = 1 << 52, _Align1Shl53 = 1 << 53, _Align1Shl54 = 1 << 54, _Align1Shl55 = 1 << 55, _Align1Shl56 = 1 << 56, _Align1Shl57 = 1 << 57, _Align1Shl58 = 1 << 58, _Align1Shl59 = 1 << 59, _Align1Shl60 = 1 << 60, _Align1Shl61 = 1 << 61, _Align1Shl62 = 1 << 62, _Align1Shl63 = 1 << 63, } /// Error that can happen when trying to convert an integer to a `DeviceAlignment`. #[derive(Clone, Debug, PartialEq, Eq)] pub struct TryFromIntError; impl Error for TryFromIntError {} impl Display for TryFromIntError { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.write_str("attempted to convert a non-power-of-two integer to a `DeviceAlignment`") } }