use crate::{
wrapping_cast, Az, CheckedAs, OverflowingAs, Round, SaturatingAs, StaticAs, WrappingAs,
};
use core::{mem, num::Wrapping};
macro_rules! bool_to_int {
($Dst:ty) => {
impl Az<$Dst> for bool {
#[inline]
fn az(self) -> $Dst {
self as $Dst
}
}
impl CheckedAs<$Dst> for bool {
#[inline]
fn checked_as(self) -> Option<$Dst> {
Some(self as $Dst)
}
}
impl SaturatingAs<$Dst> for bool {
#[inline]
fn saturating_as(self) -> $Dst {
self as $Dst
}
}
impl WrappingAs<$Dst> for bool {
#[inline]
fn wrapping_as(self) -> $Dst {
self as $Dst
}
}
impl OverflowingAs<$Dst> for bool {
#[inline]
fn overflowing_as(self) -> ($Dst, bool) {
(self as $Dst, false)
}
}
};
}
macro_rules! common {
($Src:ty => $Dst:ty) => {
impl Az<$Dst> for $Src {
#[inline]
fn az(self) -> $Dst {
let (wrapped, overflow) = self.overflowing_as();
debug_assert!(!overflow, "{} overflows", self);
let _ = overflow;
wrapped
}
}
impl CheckedAs<$Dst> for $Src {
#[inline]
fn checked_as(self) -> Option<$Dst> {
match self.overflowing_as() {
(value, false) => Some(value),
(_, true) => None,
}
}
}
impl SaturatingAs<$Dst> for $Src {
#[inline]
fn saturating_as(self) -> $Dst {
match self.overflowing_as() {
(value, false) => value,
(_, true) => {
if self > 0 {
<$Dst>::max_value()
} else {
<$Dst>::min_value()
}
}
}
}
}
impl WrappingAs<$Dst> for $Src {
#[inline]
fn wrapping_as(self) -> $Dst {
self.overflowing_as().0
}
}
};
}
macro_rules! same_signedness {
($($Src:ty),* => $Dst:ty) => { $(
common! { $Src => $Dst }
impl OverflowingAs<$Dst> for $Src {
#[inline]
fn overflowing_as(self) -> ($Dst, bool) {
let wrapped = self as $Dst;
let overflow = self != wrapped as $Src;
(wrapped, overflow)
}
}
)* };
}
macro_rules! signed_to_unsigned {
($($Src:ty),* => $Dst:ty) => { $(
common! { $Src => $Dst }
impl OverflowingAs<$Dst> for $Src {
#[inline]
fn overflowing_as(self) -> ($Dst, bool) {
let wrapped = self as $Dst;
let overflow = self < 0 || self != wrapped as $Src;
(wrapped, overflow)
}
}
)* };
}
macro_rules! unsigned_to_signed {
($($Src:ty),* => $Dst:ty) => { $(
common! { $Src => $Dst }
impl OverflowingAs<$Dst> for $Src {
#[inline]
fn overflowing_as(self) -> ($Dst, bool) {
let wrapped = self as $Dst;
let overflow = wrapped < 0 || self != wrapped as $Src;
(wrapped, overflow)
}
}
)* };
}
macro_rules! float_to_int {
($Src:ty, $ViaU:ty, $ViaI:ty => $($Dst:ty)*) => { $(
impl Az<$Dst> for $Src {
#[inline]
fn az(self) -> $Dst {
let (wrapped, overflow) = self.overflowing_as();
debug_assert!(!overflow, "{} overflows", self);
let _ = overflow;
wrapped
}
}
impl CheckedAs<$Dst> for $Src {
fn checked_as(self) -> Option<$Dst> {
let f: Float<$ViaU> = self.into();
match f.kind {
FloatKind::Nan | FloatKind::Infinite | FloatKind::Overflowing(_, true) => None,
FloatKind::Overflowing(abs, false) => {
if f.neg {
let i = abs as $ViaI;
if i == <$ViaI>::min_value() {
i.checked_as()
} else if i < 0 {
None
} else {
(-i).checked_as()
}
} else {
abs.checked_as()
}
}
}
}
}
impl SaturatingAs<$Dst> for $Src {
fn saturating_as(self) -> $Dst {
let f: Float<$ViaU> = self.into();
let saturated = if f.neg {
<$Dst>::min_value()
} else {
<$Dst>::max_value()
};
match f.kind {
FloatKind::Nan => panic!("NaN"),
FloatKind::Infinite | FloatKind::Overflowing(_, true) => saturated,
FloatKind::Overflowing(abs, false) => {
if f.neg {
let i = abs as $ViaI;
if i == <$ViaI>::min_value() {
i.saturating_as()
} else if i < 0 {
saturated
} else {
(-i).saturating_as()
}
} else {
abs.saturating_as()
}
}
}
}
}
impl WrappingAs<$Dst> for $Src {
#[inline]
fn wrapping_as(self) -> $Dst {
self.overflowing_as().0
}
}
impl OverflowingAs<$Dst> for $Src {
fn overflowing_as(self) -> ($Dst, bool) {
let f: Float<$ViaU> = self.into();
match f.kind {
FloatKind::Nan => panic!("NaN"),
FloatKind::Infinite => panic!("infinite"),
FloatKind::Overflowing(abs, overflow) => {
if f.neg {
let i = abs as $ViaI;
let (wrapped, overflow2) = if i == <$ViaI>::min_value() {
i.overflowing_as()
} else if i < 0 {
(wrapping_cast::<_, $Dst>(abs).wrapping_neg(), true)
} else {
(-i).overflowing_as()
};
(wrapped, overflow | overflow2)
} else {
let (wrapped, overflow2) = abs.overflowing_as();
(wrapped, overflow | overflow2)
}
}
}
}
}
)* };
}
float_to_int! { f32, u32, i32 => i8 i16 i32 }
float_to_int! { f32, u64, i64 => i64 }
float_to_int! { f32, u128, i128 => i128 }
#[cfg(target_pointer_width = "32")]
float_to_int! { f32, u32, i32 => isize }
#[cfg(target_pointer_width = "64")]
float_to_int! { f32, u64, i64 => isize }
float_to_int! { f32, u32, i32 => u8 u16 u32 }
float_to_int! { f32, u64, i64 => u64 }
float_to_int! { f32, u128, i128 => u128 }
#[cfg(target_pointer_width = "32")]
float_to_int! { f32, u32, i32 => usize }
#[cfg(target_pointer_width = "64")]
float_to_int! { f32, u64, i64 => usize }
float_to_int! { f64, u64, i64 => i8 i16 i32 i64 }
float_to_int! { f64, u128, i128 => i128 }
float_to_int! { f64, u64, i64 => isize }
float_to_int! { f64, u64, i64 => u8 u16 u32 u64 }
float_to_int! { f64, u128, i128 => u128 }
float_to_int! { f64, u64, i64 => usize }
float_to_int! { Round<f32>, u32, i32 => i8 i16 i32 }
float_to_int! { Round<f32>, u64, i64 => i64 }
float_to_int! { Round<f32>, u128, i128 => i128 }
#[cfg(target_pointer_width = "32")]
float_to_int! { Round<f32>, u32, i32 => isize }
#[cfg(target_pointer_width = "64")]
float_to_int! { Round<f32>, u64, i64 => isize }
float_to_int! { Round<f32>, u32, i32 => u8 u16 u32 }
float_to_int! { Round<f32>, u64, i64 => u64 }
float_to_int! { Round<f32>, u128, i128 => u128 }
#[cfg(target_pointer_width = "32")]
float_to_int! { Round<f32>, u32, i32 => usize }
#[cfg(target_pointer_width = "64")]
float_to_int! { Round<f32>, u64, i64 => usize }
float_to_int! { Round<f64>, u64, i64 => i8 i16 i32 i64 }
float_to_int! { Round<f64>, u128, i128 => i128 }
float_to_int! { Round<f64>, u64, i64 => isize }
float_to_int! { Round<f64>, u64, i64 => u8 u16 u32 u64 }
float_to_int! { Round<f64>, u128, i128 => u128 }
float_to_int! { Round<f64>, u64, i64 => usize }
macro_rules! signed {
($($Dst:ty),*) => { $(
bool_to_int! { $Dst }
same_signedness! { i8, i16, i32, i64, i128, isize => $Dst }
unsigned_to_signed! { u8, u16, u32, u64, u128, usize => $Dst }
)* };
}
macro_rules! unsigned {
($($Dst:ty),*) => { $(
bool_to_int! { $Dst }
signed_to_unsigned! { i8, i16, i32, i64, i128, isize => $Dst }
same_signedness! { u8, u16, u32, u64, u128, usize => $Dst }
)* };
}
signed! { i8, i16, i32, i64, i128, isize }
unsigned! { u8, u16, u32, u64, u128, usize }
enum FloatKind<Uns> {
Nan,
Infinite,
Overflowing(Uns, bool),
}
struct Float<Uns> {
neg: bool,
kind: FloatKind<Uns>,
}
macro_rules! from_for_float {
($Src:ty, $Uns:ty, $PREC:expr => $($Dst:ty),*) => { $(
impl From<$Src> for Float<$Dst> {
fn from(src: $Src) -> Self {
const SRC_NBITS: i32 = mem::size_of::<$Src>() as i32 * 8;
const DST_NBITS: i32 = mem::size_of::<$Dst>() as i32 * 8;
const MANT_NBITS: i32 = $PREC - 1;
const EXP_NBITS: i32 = SRC_NBITS - MANT_NBITS - 1;
const EXP_BIAS: i32 = (1 << (EXP_NBITS - 1)) - 1;
const SIGN_MASK: $Uns = !(!0 >> 1);
const MANT_MASK: $Uns = !(!0 << MANT_NBITS);
const EXP_MASK: $Uns = !(SIGN_MASK | MANT_MASK);
let u = src.to_bits();
let neg = (u & SIGN_MASK) != 0;
let biased_exp = u & EXP_MASK;
if biased_exp == EXP_MASK {
let kind = if (u & MANT_MASK) == 0 {
FloatKind::Infinite
} else {
FloatKind::Nan
};
return Float { neg, kind };
}
let shift = (biased_exp >> MANT_NBITS) as i32 - (EXP_BIAS + MANT_NBITS);
if shift < -MANT_NBITS {
let kind = FloatKind::Overflowing(0, false);
return Float { neg, kind };
}
if shift >= DST_NBITS {
let kind = FloatKind::Overflowing(0, true);
return Float { neg, kind };
}
let mut significand: $Dst = (u & MANT_MASK).into();
significand |= 1 << MANT_NBITS;
let kind = if shift < 0 {
FloatKind::Overflowing(significand >> -shift, false)
} else {
let wrapped = significand << shift;
let overflow = (wrapped >> shift) != significand;
FloatKind::Overflowing(wrapped, overflow)
};
Float { neg, kind }
}
}
impl From<Round<$Src>> for Float<$Dst> {
fn from(src: Round<$Src>) -> Self {
const SRC_NBITS: i32 = mem::size_of::<$Src>() as i32 * 8;
const DST_NBITS: i32 = mem::size_of::<$Dst>() as i32 * 8;
const MANT_NBITS: i32 = $PREC - 1;
const EXP_NBITS: i32 = SRC_NBITS - MANT_NBITS - 1;
const EXP_BIAS: i32 = (1 << (EXP_NBITS - 1)) - 1;
const SIGN_MASK: $Uns = !(!0 >> 1);
const MANT_MASK: $Uns = !(!0 << MANT_NBITS);
const EXP_MASK: $Uns = !(SIGN_MASK | MANT_MASK);
let src = src.0;
let u = src.to_bits();
let neg = (u & SIGN_MASK) != 0;
let biased_exp = u & EXP_MASK;
if biased_exp == EXP_MASK {
let kind = if (u & MANT_MASK) == 0 {
FloatKind::Infinite
} else {
FloatKind::Nan
};
return Float { neg, kind };
}
let shift = (biased_exp >> MANT_NBITS) as i32 - (EXP_BIAS + MANT_NBITS);
if shift < -MANT_NBITS - 1 {
let kind = FloatKind::Overflowing(0, false);
return Float { neg, kind };
}
if shift >= DST_NBITS {
let kind = FloatKind::Overflowing(0, true);
return Float { neg, kind };
}
let mut significand: $Dst = (u & MANT_MASK).into();
significand |= 1 << MANT_NBITS;
let kind = if shift < 0 {
let right = -shift;
let round_bit = 1 << (right - 1);
if (significand & round_bit) != 0 && (significand & (3 * round_bit - 1)) != 0 {
significand += round_bit;
}
FloatKind::Overflowing(significand >> right, false)
} else {
let wrapped = significand << shift;
let overflow = (wrapped >> shift) != significand;
FloatKind::Overflowing(wrapped, overflow)
};
Float { neg, kind }
}
}
)* };
}
from_for_float! { f32, u32, 24 => u32, u64, u128 }
from_for_float! { f64, u64, 53 => u64, u128 }
macro_rules! cross {
(fit: $($Src:ty),* => $Dst:ty) => { $(
impl StaticAs<$Dst> for $Src {
type Output = $Dst;
#[inline]
fn static_as(self) -> $Dst {
From::from(self)
}
}
)* };
(no_fit: $($Src:ty),* => $Dst:ty) => { $(
impl StaticAs<$Dst> for $Src {
type Output = ();
#[inline]
fn static_as(self) -> () {
()
}
}
)* };
(wrapping_int: $($Src:ty),* => $Dst:ty) => { $(
impl Az<Wrapping<$Dst>> for $Src {
#[inline]
fn az(self) -> Wrapping<$Dst> {
Wrapping(self.wrapping_as())
}
}
impl CheckedAs<Wrapping<$Dst>> for $Src {
#[inline]
fn checked_as(self) -> Option<Wrapping<$Dst>> {
Some(self.az())
}
}
impl StaticAs<Wrapping<$Dst>> for $Src {
type Output = Wrapping<$Dst>;
#[inline]
fn static_as(self) -> Wrapping<$Dst> {
self.az()
}
}
)* };
(wrapping_float: $($Src:ty),* => $Dst:ty) => { $(
impl Az<Wrapping<$Dst>> for $Src {
#[inline]
fn az(self) -> Wrapping<$Dst> {
Wrapping(self.wrapping_as())
}
}
impl CheckedAs<Wrapping<$Dst>> for $Src {
#[inline]
fn checked_as(self) -> Option<Wrapping<$Dst>> {
Some(self.az())
}
}
impl StaticAs<Wrapping<$Dst>> for $Src {
type Output = ();
#[inline]
fn static_as(self) -> () {
()
}
}
)* };
(fit: $($Src:ty),*; no_fit: $($LargeSrc:ty),* => $Dst:ty) => {
cross! { fit: bool, $($Src),* => $Dst }
cross! { no_fit: $($LargeSrc,)* f32, f64, Round<f32>, Round<f64> => $Dst }
cross! { wrapping_int: bool, $($Src,)* $($LargeSrc),* => $Dst }
cross! { wrapping_float: f32, f64, Round<f32>, Round<f64> => $Dst }
}
}
cross! { fit: i8; no_fit: i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize => i8 }
cross! { fit: i8, i16, u8; no_fit: i32, i64, i128, isize, u16, u32, u64, u128, usize => i16 }
cross! { fit: i8, i16, i32, u8, u16; no_fit: i64, i128, isize, u32, u64, u128, usize => i32 }
cross! { fit: i8, i16, i32, i64, u8, u16, u32; no_fit: i128, isize, u64, u128, usize => i64 }
cross! { fit: i8, i16, i32, i64, i128, u8, u16, u32, u64; no_fit: isize, u128, usize => i128 }
cross! { fit: i8, i16, isize, u8; no_fit: i32, i64, i128, u16, u32, u64, u128, usize => isize }
cross! { fit: u8; no_fit: i8, i16, i32, i64, i128, isize, u16, u32, u64, u128, usize => u8 }
cross! { fit: u8, u16; no_fit: i8, i16, i32, i64, i128, isize, u32, u64, u128, usize => u16 }
cross! { fit: u8, u16, u32; no_fit: i8, i16, i32, i64, i128, isize, u64, u128, usize => u32 }
cross! { fit: u8, u16, u32, u64; no_fit: i8, i16, i32, i64, i128, isize, u128, usize => u64 }
cross! { fit: u8, u16, u32, u64, u128; no_fit: i8, i16, i32, i64, i128, isize, usize => u128 }
cross! { fit: u8, u16, usize; no_fit: i8, i16, i32, i64, i128, isize, u32, u64, u128 => usize }