macro_rules! impl_meta {
($to:ty, $from:ty) => (
concat!(
"# Saturating From `",
stringify!($from),
"`\n",
"This method will safely recast any `",
stringify!($from),
"` into a `",
stringify!($to),
"`, capping the values at `0` or `",
stringify!($to),
"::MAX` to prevent overflow or wrapping."
)
);
}
macro_rules! unsigned_to_unsigned {
($meta:expr, $from:ty, $to:ty, $MAX:literal) => (
impl SaturatingFrom<$from> for $to {
#[doc = $meta]
fn saturating_from(src: $from) -> Self {
if src >= $MAX { $MAX }
else { src as Self }
}
}
);
($meta:expr, $from:ty, $to:ty) => (
impl SaturatingFrom<$from> for $to {
#[doc = $meta]
fn saturating_from(src: $from) -> Self { src as Self }
}
);
($to:ty, $MAX:literal, ($($from:ty),+)) => (
$( unsigned_to_unsigned!(impl_meta!($to, $from), $from, $to, $MAX); )+
);
($to:ty, ($($from:ty),+)) => (
$( unsigned_to_unsigned!(impl_meta!($to, $from), $from, $to); )+
);
}
macro_rules! signed_to_unsigned {
($meta:expr, $from:ty, $to:ty, $MAX:literal) => (
impl SaturatingFrom<$from> for $to {
#[doc = $meta]
fn saturating_from(src: $from) -> Self {
if src >= $MAX { Self::MAX }
else if src > 0 { src as Self }
else { 0 }
}
}
);
($meta:expr, $from:ty, $to:ty) => (
impl SaturatingFrom<$from> for $to {
#[doc = $meta]
fn saturating_from(src: $from) -> Self {
if src > 0 { src as Self }
else { 0 }
}
}
);
($to:ty, $MAX:literal, ($($from:ty),+)) => (
$( signed_to_unsigned!(impl_meta!($to, $from), $from, $to, $MAX); )+
);
($to:ty, ($($from:ty),+)) => (
$( signed_to_unsigned!(impl_meta!($to, $from), $from, $to); )+
);
}
macro_rules! float_to_unsigned {
($meta:expr, $from:ty, $to:ty, $MAX:literal) => (
impl SaturatingFrom<$from> for $to {
#[doc = $meta]
fn saturating_from(src: $from) -> Self {
if src <= 0.0 { 0 }
else if src >= $MAX { Self::MAX }
else { src as Self }
}
}
);
($meta:expr, $from:ty, $to:ty) => (
impl SaturatingFrom<$from> for $to {
#[doc = $meta]
fn saturating_from(src: $from) -> Self {
if src > 0.0 { src as Self }
else { 0 }
}
}
);
($to:ty, $MAX:literal, ($($from:ty),+)) => (
$( float_to_unsigned!(impl_meta!($to, $from), $from, $to, $MAX); )+
);
($to:ty, ($($from:ty),+)) => (
$( float_to_unsigned!(impl_meta!($to, $from), $from, $to); )+
);
}
pub trait SaturatingFrom<T> {
fn saturating_from(src: T) -> Self;
}
unsigned_to_unsigned!(u8, 255, (u16, u32, u64, u128, usize));
unsigned_to_unsigned!(u16, 65_535, (u32, u64, u128, usize));
unsigned_to_unsigned!(u32, 4_294_967_295, (u64, u128)); unsigned_to_unsigned!(u64, 18_446_744_073_709_551_615, (u128)); unsigned_to_unsigned!(u128, (usize));
#[cfg(any(target_pointer_width = "16", target_pointer_width="32"))] unsigned_to_unsigned!(u32, (usize));
#[cfg(any(target_pointer_width = "64", target_pointer_width="128"))] unsigned_to_unsigned!(u32, 4_294_967_295, (usize));
#[cfg(not(target_pointer_width = "128"))] unsigned_to_unsigned!(u64, (usize));
#[cfg(target_pointer_width = "128")] unsigned_to_unsigned!(u64, 18_446_744_073_709_551_615, (usize));
#[cfg(target_pointer_width = "16")] unsigned_to_unsigned!(usize, 65_535, (u32, u64, u128));
#[cfg(target_pointer_width = "32")] unsigned_to_unsigned!(usize, (u32));
#[cfg(target_pointer_width = "32")] unsigned_to_unsigned!(usize, 4_294_967_295, (u64, u128));
#[cfg(target_pointer_width = "64")] unsigned_to_unsigned!(usize, (u32, u64));
#[cfg(target_pointer_width = "64")] unsigned_to_unsigned!(usize, 18_446_744_073_709_551_615, (u128));
#[cfg(target_pointer_width = "128")]
unsigned_to_unsigned!(usize, (u32, u64, u128));
signed_to_unsigned!(u8, (i8));
signed_to_unsigned!(u16, (i8, i16));
signed_to_unsigned!(u32, (i8, i16, i32));
signed_to_unsigned!(u64, (i8, i16, i32, i64));
signed_to_unsigned!(u128, (i8, i16, i32, i64, i128, isize));
signed_to_unsigned!(usize, (i8, i16, isize));
signed_to_unsigned!(u8, 255, (i16, i32, i64, i128, isize));
signed_to_unsigned!(u16, 65_535, (i32, i64, i128, isize));
signed_to_unsigned!(u32, 4_294_967_295, (i64, i128));
signed_to_unsigned!(u64, 18_446_744_073_709_551_615, (i128));
#[cfg(any(target_pointer_width = "16", target_pointer_width="32"))]
signed_to_unsigned!(u32, (isize));
#[cfg(any(target_pointer_width = "64", target_pointer_width="128"))]
signed_to_unsigned!(u32, 4_294_967_295, (isize));
#[cfg(not(target_pointer_width = "128"))]
signed_to_unsigned!(u64, (isize));
#[cfg(target_pointer_width = "128")]
signed_to_unsigned!(u64, 18_446_744_073_709_551_615, (isize));
#[cfg(target_pointer_width = "16")]
signed_to_unsigned!(usize, 65_535, (i32, i64, i128));
#[cfg(target_pointer_width = "32")]
signed_to_unsigned!(usize, (i32));
#[cfg(target_pointer_width = "32")]
signed_to_unsigned!(usize, 4_294_967_295, (i64, i128));
#[cfg(target_pointer_width = "64")]
signed_to_unsigned!(usize, (i32, i64));
#[cfg(target_pointer_width = "64")]
signed_to_unsigned!(usize, 18_446_744_073_709_551_615, (i128));
#[cfg(target_pointer_width = "128")]
signed_to_unsigned!(usize, (i32, i64, i128));
float_to_unsigned!(u8, 255.0, (f32, f64));
float_to_unsigned!(u16, 65_535.0, (f32, f64));
float_to_unsigned!(u32, 4_294_967_295.0, (f32, f64));
float_to_unsigned!(u64, (f32));
float_to_unsigned!(u64, 18_446_744_073_709_551_615.0, (f64));
float_to_unsigned!(u128, (f32, f64));
#[cfg(target_pointer_width = "16")]
float_to_unsigned!(usize, 65_535.0, (f32, f64));
#[cfg(target_pointer_width = "32")]
float_to_unsigned!(usize, 4_294_967_295.0, (f32, f64));
#[cfg(target_pointer_width = "64")]
float_to_unsigned!(usize, (f32));
#[cfg(target_pointer_width = "64")]
float_to_unsigned!(usize, 18_446_744_073_709_551_615.0, (f64));
#[cfg(target_pointer_width = "128")]
float_to_unsigned!(usize, (f32, f64));
#[cfg(test)]
mod tests {
use super::*;
use num_traits::cast::AsPrimitive;
#[cfg(not(miri))]
const SAMPLE_SIZE: usize = 1_000_000;
#[cfg(miri)]
const SAMPLE_SIZE: usize = 1000;
macro_rules! test_impl {
($type:ty, ($($val:expr),+)) => (
$( assert_eq!(<$type>::saturating_from($val), <$type>::MIN); )+
);
($type:ty) => {
test_impl!($type, (-1_i8, -1_i16, -1_i32, -1_i64, -1_i128, -1_isize));
test_impl!($type, (0_i8, 0_i16, 0_i32, 0_i64, 0_i128, 0_isize));
test_impl!($type, (0_f32, f32::NEG_INFINITY, f32::NAN));
test_impl!($type, (0_f64, f64::NEG_INFINITY, f64::NAN));
};
}
macro_rules! test_impl_max {
($type:ty, ($($from:ty),+)) => (
$( assert_eq!(<$type>::saturating_from(<$from>::MAX), <$type>::MAX); )+
);
($type:ty) => (
assert_eq!(<$type>::saturating_from(f32::INFINITY), <$type>::MAX);
assert_eq!(<$type>::saturating_from(f64::INFINITY), <$type>::MAX);
);
}
macro_rules! test_impl_range {
($type:ty, ($($from:ty),+)) => {
for i in 0..=<$type>::MAX {
$( assert_eq!(<$type>::saturating_from(i as $from), i); )+
}
};
}
macro_rules! test_impl_subrange {
($type:ty:$fn:ident, ($($from:ty),+)) => {
let rng = fastrand::Rng::new();
for i in std::iter::repeat_with(|| rng.$fn(..)).take(SAMPLE_SIZE) {
$(
let test: $from = i.as_();
let test2: $type = test.as_();
if test2 == i {
assert_eq!(<$type>::saturating_from(test), i);
}
)+
}
};
}
#[test]
fn t_impls() {
test_impl!(u8, (0_u16, 0_u32, 0_u64, 0_u128, 0_usize));
test_impl!(u8);
test_impl!(u16, (0_u32, 0_u64, 0_u128, 0_usize));
test_impl!(u16);
test_impl!(u32, (0_u64, 0_u128, 0_usize));
test_impl!(u32);
test_impl!(u64, (0_u128, 0_usize));
test_impl!(u64);
test_impl!(u128, (0_usize));
test_impl!(u128);
test_impl!(usize, (0_u32, 0_u64, 0_u128));
test_impl!(usize);
}
#[test]
fn t_u8_from() {
test_impl_range!(u8, (u16, u32, u64, u128, usize, f32, f64));
test_impl_max!(u8, (u16, u32, u64, u128, usize, f32, f64));
test_impl_max!(u8); }
#[test]
fn t_u16_from() {
test_impl_range!(u16, (u32, u64, u128, usize, f32, f64));
test_impl_max!(u16, (u32, u64, u128, usize, f32, f64));
test_impl_max!(u16); }
#[test]
fn t_u32_from() {
test_impl_subrange!(u32:u32, (u64, u128, f32, f64));
test_impl_max!(u32, (u64, u128));
test_impl_max!(u32); }
#[test]
fn t_u64_from() {
test_impl_subrange!(u64:u64, (u128));
test_impl_max!(u64, (u128));
test_impl_max!(u64); }
#[test]
#[ignore]
fn t_u64_from_float() {
test_impl_subrange!(u64:u64, (f32, f64));
}
#[cfg(target_pointer_width = "16")]
#[test]
fn t_usize_from() {
assert_eq!(std::mem::size_of::<u16>(), std::mem::size_of::<usize>());
test_impl_range!(usize, (u32, u64, u128, f32, f64));
test_impl_max!(usize, (u32, u64, u128));
test_impl_max!(usize); }
#[cfg(target_pointer_width = "32")]
#[test]
fn t_usize_from() {
assert_eq!(std::mem::size_of::<u32>(), std::mem::size_of::<usize>());
test_impl_subrange!(usize:usize, (u32, u64, u128, f32, f64));
test_impl_max!(usize, (u32, u64, u128));
test_impl_max!(u32, (usize));
test_impl_max!(usize); }
#[cfg(target_pointer_width = "64")]
#[test]
fn t_usize_from() {
assert_eq!(std::mem::size_of::<u64>(), std::mem::size_of::<usize>());
test_impl_subrange!(usize:usize, (u64, u128));
assert_eq!(u32::saturating_from(usize::MAX), u32::MAX);
test_impl_max!(u64, (usize));
test_impl_max!(usize); }
#[cfg(target_pointer_width = "128")]
#[test]
fn t_usize_from() {
assert_eq!(std::mem::size_of::<u128>(), std::mem::size_of::<usize>());
test_impl_subrange!(usize:usize, (u128));
assert_eq!(u32::saturating_from(usize::MAX), u32::MAX);
assert_eq!(u64::saturating_from(usize::MAX), u64::MAX);
test_impl_max!(u128, (usize));
test_impl_max!(u128); test_impl_max!(usize); }
#[cfg(any(target_pointer_width = "64", target_pointer_width = "128"))]
#[test]
#[ignore]
fn t_usize_from_float() {
test_impl_subrange!(usize:usize, (f32, f64));
}
}