diff options
Diffstat (limited to 'src/memory/alignment.rs')
-rw-r--r-- | src/memory/alignment.rs | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/memory/alignment.rs b/src/memory/alignment.rs new file mode 100644 index 0000000..290240b --- /dev/null +++ b/src/memory/alignment.rs @@ -0,0 +1,249 @@ +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::<DeviceAlignment>() == size_of::<DeviceSize>()); +const _: () = assert!(align_of::<DeviceAlignment>() == align_of::<DeviceSize>()); + +impl DeviceAlignment { + /// The smallest possible alignment, 1. + pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0); + + /// The largest possible alignment, 2<sup>63</sup>. + pub const MAX: Self = Self(AlignmentEnum::_Align1Shl63); + + /// Returns the alignment for a type. + #[inline] + pub const fn of<T>() -> Self { + #[cfg(any( + target_pointer_width = "64", + target_pointer_width = "32", + target_pointer_width = "16", + ))] + { + const _: () = assert!(size_of::<DeviceSize>() >= size_of::<usize>()); + + // SAFETY: rustc guarantees that the alignment of types is a power of two. + unsafe { DeviceAlignment::new_unchecked(align_of::<T>() 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<Self> { + 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::<DeviceSize, DeviceAlignment>(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<NonZeroDeviceSize> for DeviceAlignment { + type Error = TryFromIntError; + + #[inline] + fn try_from(alignment: NonZeroDeviceSize) -> Result<Self, Self::Error> { + if alignment.is_power_of_two() { + Ok(unsafe { DeviceAlignment::new_unchecked(alignment.get()) }) + } else { + Err(TryFromIntError) + } + } +} + +impl TryFrom<DeviceSize> for DeviceAlignment { + type Error = TryFromIntError; + + #[inline] + fn try_from(alignment: DeviceSize) -> Result<Self, Self::Error> { + DeviceAlignment::new(alignment).ok_or(TryFromIntError) + } +} + +impl From<DeviceAlignment> for NonZeroDeviceSize { + #[inline] + fn from(alignment: DeviceAlignment) -> Self { + alignment.as_nonzero() + } +} + +impl From<DeviceAlignment> 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<H: Hasher>(&self, state: &mut H) { + self.as_nonzero().hash(state); + } +} + +impl PartialOrd for DeviceAlignment { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + 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`") + } +} |