use core::{i128, i16, i32, i64, i8, isize};
use core::{u128, u16, u32, u64, u8, usize};
pub trait SaturatingCast {
#[inline]
fn saturating_cast<T>(self) -> T
where
Self: SaturatingElement<T>,
{
SaturatingElement::as_element(self)
}
}
impl SaturatingCast for u8 {}
impl SaturatingCast for u16 {}
impl SaturatingCast for u32 {}
impl SaturatingCast for u64 {}
impl SaturatingCast for u128 {}
impl SaturatingCast for usize {}
impl SaturatingCast for i8 {}
impl SaturatingCast for i16 {}
impl SaturatingCast for i32 {}
impl SaturatingCast for i64 {}
impl SaturatingCast for i128 {}
impl SaturatingCast for isize {}
pub trait SaturatingElement<T>: Copy {
fn as_element(self) -> T;
}
macro_rules! impl_lossless_casts {
($src: ty => $($target: ty),*) => {$(
impl SaturatingElement<$target> for $src {
#[inline]
fn as_element(self) -> $target {
<$target>::from(self)
}
}
)*};
}
macro_rules! impl_uint_clamp_to_max_bound {
($src: ty => $($target: ty),*) => {$(
impl SaturatingElement<$target> for $src {
#[inline]
fn as_element(self) -> $target {
self.min(<$target>::MAX as $src) as $target
}
}
)*};
}
impl_lossless_casts!(u8 => u8, u16, u32, u64, u128, usize);
impl_lossless_casts!(u8 => i16, i32, i64, i128, isize);
impl_uint_clamp_to_max_bound!(u8 => i8);
impl_lossless_casts!(u16 => u16, u32, u64, u128, usize);
impl_lossless_casts!(u16 => i32, i64, i128);
impl_uint_clamp_to_max_bound!(u16 => u8);
impl_uint_clamp_to_max_bound!(u16 => i8, i16, isize);
impl_lossless_casts!(u32 => u32, u64, u128);
impl_lossless_casts!(u32 => i64, i128);
impl_uint_clamp_to_max_bound!(u32 => u8, u16, usize);
impl_uint_clamp_to_max_bound!(u32 => i8, i16, i32, isize);
impl_lossless_casts!(u64 => u64, u128);
impl_lossless_casts!(u64 => i128);
impl_uint_clamp_to_max_bound!(u64 => u8, u16, u32, usize);
impl_uint_clamp_to_max_bound!(u64 => i8, i16, i32, i64, isize);
impl_lossless_casts!(u128 => u128);
impl_uint_clamp_to_max_bound!(u128 => u8, u16, u32, u64, usize);
impl_uint_clamp_to_max_bound!(u128 => i8, i16, i32, i64, i128, isize);
impl_lossless_casts!(usize => usize);
impl_uint_clamp_to_max_bound!(usize => u8, u16, u32, u64, u128);
impl_uint_clamp_to_max_bound!(usize => i8, i16, i32, i64, i128, isize);
macro_rules! impl_int_clamp_to_smaller {
($src: ty => $($target: ty),*) => {$(
impl SaturatingElement<$target> for $src {
#[inline]
fn as_element(self) -> $target {
self.max(<$target>::MIN as $src).min(<$target>::MAX as $src) as $target
}
}
)*};
}
macro_rules! impl_int_clamp_to_zero_with_larger_uint_max {
($src: ty => $($target: ty),*) => {$(
impl SaturatingElement<$target> for $src {
#[inline]
fn as_element(self) -> $target {
self.max(0) as $target
}
}
)*};
}
macro_rules! impl_int_isize_usize {
($src: ty) => {
#[cfg(target_pointer_width = "16")]
impl SaturatingElement<isize> for $src {
#[inline]
fn as_element(self) -> isize {
self.saturating_cast::<i16>() as isize
}
}
#[cfg(target_pointer_width = "32")]
impl SaturatingElement<isize> for $src {
#[inline]
fn as_element(self) -> isize {
self.saturating_cast::<i32>() as isize
}
}
#[cfg(target_pointer_width = "64")]
impl SaturatingElement<isize> for $src {
#[inline]
fn as_element(self) -> isize {
self.saturating_cast::<i64>() as isize
}
}
#[cfg(target_pointer_width = "16")]
impl SaturatingElement<usize> for $src {
#[inline]
fn as_element(self) -> usize {
self.saturating_cast::<u16>() as usize
}
}
#[cfg(target_pointer_width = "32")]
impl SaturatingElement<usize> for $src {
#[inline]
fn as_element(self) -> usize {
self.saturating_cast::<u32>() as usize
}
}
#[cfg(target_pointer_width = "64")]
impl SaturatingElement<usize> for $src {
#[inline]
fn as_element(self) -> usize {
self.saturating_cast::<u64>() as usize
}
}
};
}
macro_rules! impl_isize_casts {
($($target: ty),*) => {$(
#[cfg(target_pointer_width = "16")]
impl SaturatingElement<$target> for isize {
#[inline]
fn as_element(self) -> $target {
(self as i16).saturating_cast::<$target>()
}
}
#[cfg(target_pointer_width = "32")]
impl SaturatingElement<$target> for isize {
#[inline]
fn as_element(self) -> $target {
(self as i32).saturating_cast::<$target>()
}
}
#[cfg(target_pointer_width = "64")]
impl SaturatingElement<$target> for isize {
#[inline]
fn as_element(self) -> $target {
(self as i64).saturating_cast::<$target>()
}
}
)*};
}
impl_lossless_casts!(i8 => i8, i16, i32, i64, i128, isize);
impl_int_clamp_to_zero_with_larger_uint_max!(i8 => u8, u16, u32, u64, u128, usize);
impl_lossless_casts!(i16 => i16, i32, i64, i128, isize);
impl_int_clamp_to_smaller!(i16 => i8);
impl_int_clamp_to_zero_with_larger_uint_max!(i16 => u16, u32, u64, u128, usize);
impl_int_clamp_to_smaller!(i16 => u8);
impl_lossless_casts!(i32 => i32, i64, i128);
impl_int_clamp_to_smaller!(i32 => i8, i16);
impl_int_clamp_to_zero_with_larger_uint_max!(i32 => u32, u64, u128);
impl_int_clamp_to_smaller!(i32 => u8, u16);
impl_int_isize_usize!(i32);
impl_lossless_casts!(i64 => i64, i128);
impl_int_clamp_to_smaller!(i64 => i8, i16, i32);
impl_int_clamp_to_zero_with_larger_uint_max!(i64 => u64, u128);
impl_int_clamp_to_smaller!(i64 => u8, u16, u32);
impl_int_isize_usize!(i64);
impl_lossless_casts!(i128 => i128);
impl_int_clamp_to_smaller!(i128 => i8, i16, i32, i64);
impl_int_clamp_to_zero_with_larger_uint_max!(i128 => u128);
impl_int_clamp_to_smaller!(i128 => u8, u16, u32, u64);
impl_int_isize_usize!(i128);
impl_lossless_casts!(isize => isize);
impl_int_clamp_to_smaller!(isize => i8, i16);
impl_isize_casts!(i32, i64, i128);
impl_int_clamp_to_zero_with_larger_uint_max!(isize => u64, u128, usize);
impl_int_clamp_to_smaller!(isize => u8, u16);
impl_isize_casts!(u32);
#[cfg(test)]
mod casts {
use crate::SaturatingCast;
use core::{i128, i16, i32, i64, i8, isize};
use core::{u128, u16, u32, u64, u8, usize};
macro_rules! impl_test_all_casts {
($src: ty => $($target: ty),*) => {$(
let _: $target = <$src>::MIN.saturating_cast::<$target>();
)*};
}
#[test]
fn u8_all_casts() {
impl_test_all_casts!(u8 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(u8 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn u16_all_casts() {
impl_test_all_casts!(u16 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(u16 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn u32_all_casts() {
impl_test_all_casts!(u32 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(u32 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn u64_all_casts() {
impl_test_all_casts!(u64 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(u64 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn u128_all_casts() {
impl_test_all_casts!(u128 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(u128 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn usize_all_casts() {
impl_test_all_casts!(usize => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(usize => i8, i16, i32, i64, i128, isize);
}
#[test]
fn i8_all_casts() {
impl_test_all_casts!(i8 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(i8 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn i16_all_casts() {
impl_test_all_casts!(i16 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(i16 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn i32_all_casts() {
impl_test_all_casts!(i32 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(i32 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn i64_all_casts() {
impl_test_all_casts!(i64 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(i64 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn i128_all_casts() {
impl_test_all_casts!(i128 => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(i128 => i8, i16, i32, i64, i128, isize);
}
#[test]
fn isize_all_casts() {
impl_test_all_casts!(isize => u8, u16, u32, u64, u128, usize);
impl_test_all_casts!(isize => i8, i16, i32, i64, i128, isize);
}
}