diff --git a/src/int.rs b/src/int.rs index 4f9221f..18ce873 100644 --- a/src/int.rs +++ b/src/int.rs @@ -21,6 +21,8 @@ pub trait PrimInt + CheckedSub + CheckedMul + CheckedDiv + + CheckedShl + + CheckedShr + Saturating { /// Returns the number of ones in the binary representation of `self`. diff --git a/src/ops/checked.rs b/src/ops/checked.rs index 45e6716..6527b5b 100644 --- a/src/ops/checked.rs +++ b/src/ops/checked.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, Sub, Mul, Div}; +use std::ops::{Add, Sub, Mul, Div, Shl, Shr}; /// Performs addition that returns `None` instead of wrapping around on /// overflow. @@ -90,3 +90,54 @@ checked_impl!(CheckedDiv, checked_div, i32); checked_impl!(CheckedDiv, checked_div, i64); checked_impl!(CheckedDiv, checked_div, isize); +/// Performs a left shift that returns `None` on overflow. +pub trait CheckedShl: Sized + Shl { + /// Shifts a number to the left, checking for overflow. If overflow happens, `None` is + /// returned. + fn checked_shl(&self, rhs: &RHS) -> Option; +} + +macro_rules! checked_shift_impl { + ($trait_name:ident, $method:ident, $rhs:ty, $t:ty) => { + impl $trait_name<$rhs> for $t { + #[inline] + fn $method(&self, rhs: &$rhs) -> Option<$t> { + // Note the cast to `u32` here: The standard library is somewhat inconsistent here. + // The `Shl` and `Shr` trait are generic over the right-hand side `T`, but + // the checked versions of the shifts all operate on `u32`. + + // TODO: Maybe we should use a conversion that can fail here. This would allow us + // to catch the case where `rhs` exceeds the `u32` accepted by the stdlib, and + // return a `None` instead. + <$t>::$method(*self, *rhs as u32) + } + } + } +} + +macro_rules! checked_shift_impl_all { + ($trait_name:ident, $method:ident, $($t:ty)*) => ($( + checked_shift_impl! { $trait_name, $method, u8 , $t } + checked_shift_impl! { $trait_name, $method, u16 , $t } + checked_shift_impl! { $trait_name, $method, u32 , $t } + checked_shift_impl! { $trait_name, $method, u64 , $t } + checked_shift_impl! { $trait_name, $method, usize, $t } + + checked_shift_impl! { $trait_name, $method, i8 , $t } + checked_shift_impl! { $trait_name, $method, i16 , $t } + checked_shift_impl! { $trait_name, $method, i32 , $t } + checked_shift_impl! { $trait_name, $method, i64 , $t } + checked_shift_impl! { $trait_name, $method, isize, $t } + )*) +} + +checked_shift_impl_all!(CheckedShl, checked_shl, u8 u16 u32 u64 usize i8 i16 i32 i64 isize); + +/// Performs a right shift that returns `None` on overflow. +pub trait CheckedShr: Sized + Shr { + /// Shifts a number to the left, checking for overflow. If overflow happens, `None` is + /// returned. + fn checked_shr(&self, rhs: &RHS) -> Option; +} + +checked_shift_impl_all!(CheckedShr, checked_shr, u8 u16 u32 u64 usize i8 i16 i32 i64 isize);