use crate::{
internal::{self, something_went_wrong},
AnyBitPattern, NoUninit,
};
pub unsafe trait CheckedBitPattern: Copy {
type Bits: AnyBitPattern;
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool;
}
unsafe impl<T: AnyBitPattern> CheckedBitPattern for T {
type Bits = T;
#[inline(always)]
fn is_valid_bit_pattern(_bits: &T) -> bool {
true
}
}
unsafe impl CheckedBitPattern for char {
type Bits = u32;
#[inline]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
core::char::from_u32(*bits).is_some()
}
}
unsafe impl CheckedBitPattern for bool {
type Bits = u8;
#[inline]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
match *bits {
0 | 1 => true,
_ => false,
}
}
}
macro_rules! impl_checked_for_nonzero {
($($nonzero:ty: $primitive:ty),* $(,)?) => {
$(
unsafe impl CheckedBitPattern for $nonzero {
type Bits = $primitive;
#[inline]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits != 0
}
}
)*
};
}
impl_checked_for_nonzero! {
core::num::NonZeroU8: u8,
core::num::NonZeroI8: i8,
core::num::NonZeroU16: u16,
core::num::NonZeroI16: i16,
core::num::NonZeroU32: u32,
core::num::NonZeroI32: i32,
core::num::NonZeroU64: u64,
core::num::NonZeroI64: i64,
core::num::NonZeroI128: i128,
core::num::NonZeroU128: u128,
core::num::NonZeroUsize: usize,
core::num::NonZeroIsize: isize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CheckedCastError {
PodCastError(crate::PodCastError),
InvalidBitPattern,
}
#[cfg(not(target_arch = "spirv"))]
impl core::fmt::Display for CheckedCastError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(feature = "extern_crate_std")]
#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_std")))]
impl std::error::Error for CheckedCastError {}
#[cfg(all(feature = "impl_core_error", not(feature = "extern_crate_std")))]
impl core::error::Error for CheckedCastError {}
impl From<crate::PodCastError> for CheckedCastError {
fn from(err: crate::PodCastError) -> CheckedCastError {
CheckedCastError::PodCastError(err)
}
}
#[inline]
pub fn try_from_bytes<T: CheckedBitPattern>(
s: &[u8],
) -> Result<&T, CheckedCastError> {
let pod = crate::try_from_bytes(s)?;
if <T as CheckedBitPattern>::is_valid_bit_pattern(pod) {
Ok(unsafe { &*(pod as *const <T as CheckedBitPattern>::Bits as *const T) })
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_from_bytes_mut<T: CheckedBitPattern + NoUninit>(
s: &mut [u8],
) -> Result<&mut T, CheckedCastError> {
let pod = unsafe { internal::try_from_bytes_mut(s) }?;
if <T as CheckedBitPattern>::is_valid_bit_pattern(pod) {
Ok(unsafe { &mut *(pod as *mut <T as CheckedBitPattern>::Bits as *mut T) })
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_pod_read_unaligned<T: CheckedBitPattern>(
bytes: &[u8],
) -> Result<T, CheckedCastError> {
let pod = crate::try_pod_read_unaligned(bytes)?;
if <T as CheckedBitPattern>::is_valid_bit_pattern(&pod) {
Ok(unsafe { transmute!(pod) })
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_cast<A: NoUninit, B: CheckedBitPattern>(
a: A,
) -> Result<B, CheckedCastError> {
let pod = crate::try_cast(a)?;
if <B as CheckedBitPattern>::is_valid_bit_pattern(&pod) {
Ok(unsafe { transmute!(pod) })
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_cast_ref<A: NoUninit, B: CheckedBitPattern>(
a: &A,
) -> Result<&B, CheckedCastError> {
let pod = crate::try_cast_ref(a)?;
if <B as CheckedBitPattern>::is_valid_bit_pattern(pod) {
Ok(unsafe { &*(pod as *const <B as CheckedBitPattern>::Bits as *const B) })
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_cast_mut<
A: NoUninit + AnyBitPattern,
B: CheckedBitPattern + NoUninit,
>(
a: &mut A,
) -> Result<&mut B, CheckedCastError> {
let pod = unsafe { internal::try_cast_mut(a) }?;
if <B as CheckedBitPattern>::is_valid_bit_pattern(pod) {
Ok(unsafe { &mut *(pod as *mut <B as CheckedBitPattern>::Bits as *mut B) })
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_cast_slice<A: NoUninit, B: CheckedBitPattern>(
a: &[A],
) -> Result<&[B], CheckedCastError> {
let pod = crate::try_cast_slice(a)?;
if pod.iter().all(|pod| <B as CheckedBitPattern>::is_valid_bit_pattern(pod)) {
Ok(unsafe {
core::slice::from_raw_parts(pod.as_ptr() as *const B, pod.len())
})
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
pub fn try_cast_slice_mut<
A: NoUninit + AnyBitPattern,
B: CheckedBitPattern + NoUninit,
>(
a: &mut [A],
) -> Result<&mut [B], CheckedCastError> {
let pod = unsafe { internal::try_cast_slice_mut(a) }?;
if pod.iter().all(|pod| <B as CheckedBitPattern>::is_valid_bit_pattern(pod)) {
Ok(unsafe {
core::slice::from_raw_parts_mut(pod.as_mut_ptr() as *mut B, pod.len())
})
} else {
Err(CheckedCastError::InvalidBitPattern)
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn from_bytes<T: CheckedBitPattern>(s: &[u8]) -> &T {
match try_from_bytes(s) {
Ok(t) => t,
Err(e) => something_went_wrong("from_bytes", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn from_bytes_mut<T: NoUninit + CheckedBitPattern>(s: &mut [u8]) -> &mut T {
match try_from_bytes_mut(s) {
Ok(t) => t,
Err(e) => something_went_wrong("from_bytes_mut", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn pod_read_unaligned<T: CheckedBitPattern>(bytes: &[u8]) -> T {
match try_pod_read_unaligned(bytes) {
Ok(t) => t,
Err(e) => something_went_wrong("pod_read_unaligned", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn cast<A: NoUninit, B: CheckedBitPattern>(a: A) -> B {
match try_cast(a) {
Ok(t) => t,
Err(e) => something_went_wrong("cast", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn cast_mut<
A: NoUninit + AnyBitPattern,
B: NoUninit + CheckedBitPattern,
>(
a: &mut A,
) -> &mut B {
match try_cast_mut(a) {
Ok(t) => t,
Err(e) => something_went_wrong("cast_mut", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn cast_ref<A: NoUninit, B: CheckedBitPattern>(a: &A) -> &B {
match try_cast_ref(a) {
Ok(t) => t,
Err(e) => something_went_wrong("cast_ref", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn cast_slice<A: NoUninit, B: CheckedBitPattern>(a: &[A]) -> &[B] {
match try_cast_slice(a) {
Ok(t) => t,
Err(e) => something_went_wrong("cast_slice", e),
}
}
#[inline]
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn cast_slice_mut<
A: NoUninit + AnyBitPattern,
B: NoUninit + CheckedBitPattern,
>(
a: &mut [A],
) -> &mut [B] {
match try_cast_slice_mut(a) {
Ok(t) => t,
Err(e) => something_went_wrong("cast_slice_mut", e),
}
}