1 use crate::{DeviceSize, NonZeroDeviceSize}; 2 use std::{ 3 cmp::Ordering, 4 error::Error, 5 fmt::{Debug, Display, Formatter, Result as FmtResult}, 6 hash::{Hash, Hasher}, 7 mem::{self, align_of, size_of}, 8 }; 9 10 /// Vulkan analog of std's [`Alignment`], stored as a [`DeviceSize`] that is guaranteed to be a 11 /// valid Vulkan alignment. 12 /// 13 /// [`Alignment`]: std::ptr::Alignment 14 #[derive(Clone, Copy, PartialEq, Eq)] 15 #[repr(transparent)] 16 pub struct DeviceAlignment(AlignmentEnum); 17 18 const _: () = assert!(size_of::<DeviceAlignment>() == size_of::<DeviceSize>()); 19 const _: () = assert!(align_of::<DeviceAlignment>() == align_of::<DeviceSize>()); 20 21 impl DeviceAlignment { 22 /// The smallest possible alignment, 1. 23 pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0); 24 25 /// The largest possible alignment, 2<sup>63</sup>. 26 pub const MAX: Self = Self(AlignmentEnum::_Align1Shl63); 27 28 /// Returns the alignment for a type. 29 #[inline] of<T>() -> Self30 pub const fn of<T>() -> Self { 31 #[cfg(any( 32 target_pointer_width = "64", 33 target_pointer_width = "32", 34 target_pointer_width = "16", 35 ))] 36 { 37 const _: () = assert!(size_of::<DeviceSize>() >= size_of::<usize>()); 38 39 // SAFETY: rustc guarantees that the alignment of types is a power of two. 40 unsafe { DeviceAlignment::new_unchecked(align_of::<T>() as DeviceSize) } 41 } 42 } 43 44 /// Tries to create a `DeviceAlignment` from a [`DeviceSize`], returning [`None`] if it's not a 45 /// power of two. 46 #[inline] new(alignment: DeviceSize) -> Option<Self>47 pub const fn new(alignment: DeviceSize) -> Option<Self> { 48 if alignment.is_power_of_two() { 49 Some(unsafe { DeviceAlignment::new_unchecked(alignment) }) 50 } else { 51 None 52 } 53 } 54 55 /// Creates a `DeviceAlignment` from a [`DeviceSize`] without checking if it's a power of two. 56 /// 57 /// # Safety 58 /// 59 /// - `alignment` must be a power of two, which also means it must be non-zero. 60 #[inline] new_unchecked(alignment: DeviceSize) -> Self61 pub const unsafe fn new_unchecked(alignment: DeviceSize) -> Self { 62 debug_assert!(alignment.is_power_of_two()); 63 64 unsafe { mem::transmute::<DeviceSize, DeviceAlignment>(alignment) } 65 } 66 67 /// Returns the alignment as a [`DeviceSize`]. 68 #[inline] as_devicesize(self) -> DeviceSize69 pub const fn as_devicesize(self) -> DeviceSize { 70 self.0 as DeviceSize 71 } 72 73 /// Returns the alignment as a [`NonZeroDeviceSize`]. 74 #[inline] as_nonzero(self) -> NonZeroDeviceSize75 pub const fn as_nonzero(self) -> NonZeroDeviceSize { 76 // SAFETY: All the discriminants are non-zero. 77 unsafe { NonZeroDeviceSize::new_unchecked(self.as_devicesize()) } 78 } 79 80 /// Returns the base-2 logarithm of the alignment. 81 #[inline] log2(self) -> u3282 pub const fn log2(self) -> u32 { 83 self.as_nonzero().trailing_zeros() 84 } 85 86 // TODO: Replace with `Ord::max` once its constness is stabilized. 87 #[inline(always)] max(self, other: Self) -> Self88 pub(crate) const fn max(self, other: Self) -> Self { 89 if self.as_devicesize() >= other.as_devicesize() { 90 self 91 } else { 92 other 93 } 94 } 95 } 96 97 impl Debug for DeviceAlignment { fmt(&self, f: &mut Formatter<'_>) -> FmtResult98 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 99 write!(f, "{:?} (1 << {:?})", self.as_nonzero(), self.log2()) 100 } 101 } 102 103 impl Default for DeviceAlignment { 104 #[inline] default() -> Self105 fn default() -> Self { 106 DeviceAlignment::MIN 107 } 108 } 109 110 impl TryFrom<NonZeroDeviceSize> for DeviceAlignment { 111 type Error = TryFromIntError; 112 113 #[inline] try_from(alignment: NonZeroDeviceSize) -> Result<Self, Self::Error>114 fn try_from(alignment: NonZeroDeviceSize) -> Result<Self, Self::Error> { 115 if alignment.is_power_of_two() { 116 Ok(unsafe { DeviceAlignment::new_unchecked(alignment.get()) }) 117 } else { 118 Err(TryFromIntError) 119 } 120 } 121 } 122 123 impl TryFrom<DeviceSize> for DeviceAlignment { 124 type Error = TryFromIntError; 125 126 #[inline] try_from(alignment: DeviceSize) -> Result<Self, Self::Error>127 fn try_from(alignment: DeviceSize) -> Result<Self, Self::Error> { 128 DeviceAlignment::new(alignment).ok_or(TryFromIntError) 129 } 130 } 131 132 impl From<DeviceAlignment> for NonZeroDeviceSize { 133 #[inline] from(alignment: DeviceAlignment) -> Self134 fn from(alignment: DeviceAlignment) -> Self { 135 alignment.as_nonzero() 136 } 137 } 138 139 impl From<DeviceAlignment> for DeviceSize { 140 #[inline] from(alignment: DeviceAlignment) -> Self141 fn from(alignment: DeviceAlignment) -> Self { 142 alignment.as_devicesize() 143 } 144 } 145 146 // This is a false-positive, the underlying values that this impl and the derived `PartialEq` work 147 // with are the same. 148 #[allow(clippy::derived_hash_with_manual_eq)] 149 impl Hash for DeviceAlignment { 150 #[inline] hash<H: Hasher>(&self, state: &mut H)151 fn hash<H: Hasher>(&self, state: &mut H) { 152 self.as_nonzero().hash(state); 153 } 154 } 155 156 impl PartialOrd for DeviceAlignment { 157 #[inline] partial_cmp(&self, other: &Self) -> Option<Ordering>158 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 159 self.as_nonzero().partial_cmp(&other.as_nonzero()) 160 } 161 } 162 163 impl Ord for DeviceAlignment { 164 #[inline] cmp(&self, other: &Self) -> Ordering165 fn cmp(&self, other: &Self) -> Ordering { 166 self.as_nonzero().cmp(&other.as_nonzero()) 167 } 168 } 169 170 #[derive(Clone, Copy, PartialEq, Eq)] 171 #[repr(u64)] 172 enum AlignmentEnum { 173 _Align1Shl0 = 1 << 0, 174 _Align1Shl1 = 1 << 1, 175 _Align1Shl2 = 1 << 2, 176 _Align1Shl3 = 1 << 3, 177 _Align1Shl4 = 1 << 4, 178 _Align1Shl5 = 1 << 5, 179 _Align1Shl6 = 1 << 6, 180 _Align1Shl7 = 1 << 7, 181 _Align1Shl8 = 1 << 8, 182 _Align1Shl9 = 1 << 9, 183 _Align1Shl10 = 1 << 10, 184 _Align1Shl11 = 1 << 11, 185 _Align1Shl12 = 1 << 12, 186 _Align1Shl13 = 1 << 13, 187 _Align1Shl14 = 1 << 14, 188 _Align1Shl15 = 1 << 15, 189 _Align1Shl16 = 1 << 16, 190 _Align1Shl17 = 1 << 17, 191 _Align1Shl18 = 1 << 18, 192 _Align1Shl19 = 1 << 19, 193 _Align1Shl20 = 1 << 20, 194 _Align1Shl21 = 1 << 21, 195 _Align1Shl22 = 1 << 22, 196 _Align1Shl23 = 1 << 23, 197 _Align1Shl24 = 1 << 24, 198 _Align1Shl25 = 1 << 25, 199 _Align1Shl26 = 1 << 26, 200 _Align1Shl27 = 1 << 27, 201 _Align1Shl28 = 1 << 28, 202 _Align1Shl29 = 1 << 29, 203 _Align1Shl30 = 1 << 30, 204 _Align1Shl31 = 1 << 31, 205 _Align1Shl32 = 1 << 32, 206 _Align1Shl33 = 1 << 33, 207 _Align1Shl34 = 1 << 34, 208 _Align1Shl35 = 1 << 35, 209 _Align1Shl36 = 1 << 36, 210 _Align1Shl37 = 1 << 37, 211 _Align1Shl38 = 1 << 38, 212 _Align1Shl39 = 1 << 39, 213 _Align1Shl40 = 1 << 40, 214 _Align1Shl41 = 1 << 41, 215 _Align1Shl42 = 1 << 42, 216 _Align1Shl43 = 1 << 43, 217 _Align1Shl44 = 1 << 44, 218 _Align1Shl45 = 1 << 45, 219 _Align1Shl46 = 1 << 46, 220 _Align1Shl47 = 1 << 47, 221 _Align1Shl48 = 1 << 48, 222 _Align1Shl49 = 1 << 49, 223 _Align1Shl50 = 1 << 50, 224 _Align1Shl51 = 1 << 51, 225 _Align1Shl52 = 1 << 52, 226 _Align1Shl53 = 1 << 53, 227 _Align1Shl54 = 1 << 54, 228 _Align1Shl55 = 1 << 55, 229 _Align1Shl56 = 1 << 56, 230 _Align1Shl57 = 1 << 57, 231 _Align1Shl58 = 1 << 58, 232 _Align1Shl59 = 1 << 59, 233 _Align1Shl60 = 1 << 60, 234 _Align1Shl61 = 1 << 61, 235 _Align1Shl62 = 1 << 62, 236 _Align1Shl63 = 1 << 63, 237 } 238 239 /// Error that can happen when trying to convert an integer to a `DeviceAlignment`. 240 #[derive(Clone, Debug, PartialEq, Eq)] 241 pub struct TryFromIntError; 242 243 impl Error for TryFromIntError {} 244 245 impl Display for TryFromIntError { fmt(&self, f: &mut Formatter<'_>) -> FmtResult246 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 247 f.write_str("attempted to convert a non-power-of-two integer to a `DeviceAlignment`") 248 } 249 } 250