diff --git a/src/cast.rs b/src/cast.rs index 2d7fe19a..ebe53902 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -236,35 +236,81 @@ macro_rules! impl_to_primitive_float_to_float { ) } +macro_rules! impl_to_primitive_float_to_signed_int { + ($SrcT:ident, $DstT:ident, $slf:expr) => ({ + let t = $slf.trunc(); + if t >= $DstT::min_value() as $SrcT && t <= $DstT::max_value() as $SrcT { + Some($slf as $DstT) + } else { + None + } + }) +} + +macro_rules! impl_to_primitive_float_to_unsigned_int { + ($SrcT:ident, $DstT:ident, $slf:expr) => ({ + let t = $slf.trunc(); + if t >= $DstT::min_value() as $SrcT && t <= $DstT::max_value() as $SrcT { + Some($slf as $DstT) + } else { + None + } + }) +} + macro_rules! impl_to_primitive_float { ($T:ident) => ( impl ToPrimitive for $T { #[inline] - fn to_isize(&self) -> Option { Some(*self as isize) } + fn to_isize(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, isize, *self) + } #[inline] - fn to_i8(&self) -> Option { Some(*self as i8) } + fn to_i8(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i8, *self) + } #[inline] - fn to_i16(&self) -> Option { Some(*self as i16) } + fn to_i16(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i16, *self) + } #[inline] - fn to_i32(&self) -> Option { Some(*self as i32) } + fn to_i32(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i32, *self) + } #[inline] - fn to_i64(&self) -> Option { Some(*self as i64) } + fn to_i64(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i64, *self) + } #[inline] - fn to_usize(&self) -> Option { Some(*self as usize) } + fn to_usize(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, usize, *self) + } #[inline] - fn to_u8(&self) -> Option { Some(*self as u8) } + fn to_u8(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u8, *self) + } #[inline] - fn to_u16(&self) -> Option { Some(*self as u16) } + fn to_u16(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u16, *self) + } #[inline] - fn to_u32(&self) -> Option { Some(*self as u32) } + fn to_u32(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u32, *self) + } #[inline] - fn to_u64(&self) -> Option { Some(*self as u64) } + fn to_u64(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u64, *self) + } #[inline] - fn to_f32(&self) -> Option { impl_to_primitive_float_to_float!($T, f32, *self) } + fn to_f32(&self) -> Option { + impl_to_primitive_float_to_float!($T, f32, *self) + } #[inline] - fn to_f64(&self) -> Option { impl_to_primitive_float_to_float!($T, f64, *self) } + fn to_f64(&self) -> Option { + impl_to_primitive_float_to_float!($T, f64, *self) + } } ) } @@ -589,3 +635,60 @@ fn as_primitive() { let x: u8 = (768i16).as_(); assert_eq!(x, 0); } + +#[test] +fn float_to_integer_checks_overflow() { + // This will overflow an i32 + let source: f64 = 1.0e+123f64; + + // Expect the overflow to be caught + assert_eq!(cast::(source), None); +} + +#[test] +fn cast_to_int_checks_overflow() { + let big_f: f64 = 1.0e123; + let normal_f: f64 = 1.0; + let small_f: f64 = -1.0e123; + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + + assert_eq!(Some(normal_f as isize), cast::(normal_f)); + assert_eq!(Some(normal_f as i8), cast::(normal_f)); + assert_eq!(Some(normal_f as i16), cast::(normal_f)); + assert_eq!(Some(normal_f as i32), cast::(normal_f)); + assert_eq!(Some(normal_f as i64), cast::(normal_f)); + + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); +} + +#[test] +fn cast_to_unsigned_int_checks_overflow() { + let big_f: f64 = 1.0e123; + let normal_f: f64 = 1.0; + let small_f: f64 = -1.0e123; + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + + assert_eq!(Some(normal_f as usize), cast::(normal_f)); + assert_eq!(Some(normal_f as u8), cast::(normal_f)); + assert_eq!(Some(normal_f as u16), cast::(normal_f)); + assert_eq!(Some(normal_f as u32), cast::(normal_f)); + assert_eq!(Some(normal_f as u64), cast::(normal_f)); + + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); +}