diff --git a/SafeInt.hpp b/SafeInt.hpp index 4b4e629..429c96c 100644 --- a/SafeInt.hpp +++ b/SafeInt.hpp @@ -5374,6 +5374,41 @@ template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int } }; +template < typename U, int signed > class bits_not_negative; + +// Signed case +template < typename U > class bits_not_negative < U, true > +{ +public: + SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool value(U bits) + { + return bits >= 0; + } +}; + +template < typename U > class bits_not_negative < U, false > +{ +public: + SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 static bool value(U) + { + return true; + } +}; + +template < typename T, typename U > +SAFE_INT_NODISCARD SAFEINT_CONSTEXPR14 bool valid_bitcount(U bits) +{ + if (bits_not_negative::is_signed>::value(bits)) + { + if (bits < (int)safeint_internal::int_traits< T >::bitCount) + { + return true; + } + } + + return false; +} + /***************** External functions ****************************************/ // External functions that can be used where you only need to check one operation @@ -5988,87 +6023,100 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // code path is exactly the same as for SafeInt< U, E > as rhs // Left shift - // Also, shifting > bitcount is undefined - trap in debug - #define ShiftAssert(x) SAFEINT_ASSERT(x) template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E > operator <<( U bits ) const { - ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); - ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + return SafeInt< T, E >((T)(m_int << bits)); + } - return SafeInt< T, E >( (T)( m_int << bits ) ); + E::SafeIntOnOverflow(); } template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const { - ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); - - return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + if (valid_bitcount(bits)) + { + return SafeInt< T, E >((T)(m_int << (U)bits)); + } + E::SafeIntOnOverflow(); } // Left shift assignment template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator <<=( U bits ) { - ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); - ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + m_int <<= bits; + return *this; + } - m_int <<= bits; - return *this; + E::SafeIntOnOverflow(); } template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) { - ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + m_int <<= (U)bits; + return *this; + } - m_int <<= (U)bits; - return *this; + E::SafeIntOnOverflow(); } // Right shift template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E > operator >>( U bits ) const { - ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); - ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + return SafeInt< T, E >((T)(m_int >> bits)); + } - return SafeInt< T, E >( (T)( m_int >> bits ) ); + E::SafeIntOnOverflow(); } template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const { - ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + return SafeInt< T, E >((T)(m_int >> (U)bits)); + } - return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + E::SafeIntOnOverflow(); } // Right shift assignment template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator >>=( U bits ) { - ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); - ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + m_int >>= bits; + return *this; + } - m_int >>= bits; - return *this; + E::SafeIntOnOverflow(); } template < typename U > - SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + SAFEINT_CONSTEXPR14 SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) { - ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + if (valid_bitcount(bits)) + { + m_int >>= (U)bits; + return *this; + } - m_int >>= (U)bits; - return *this; + E::SafeIntOnOverflow(); } // Bitwise operators @@ -6918,20 +6966,24 @@ SAFEINT_CONSTEXPR14 T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW template < typename T, typename U, typename E > SAFEINT_CONSTEXPR14 SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW { - ShiftAssert( !std::numeric_limits< T >::is_signed || (T)bits >= 0 ); - ShiftAssert( (T)bits < (int)safeint_internal::int_traits< U >::bitCount ); + if (valid_bitcount(bits)) + { + return SafeInt< U, E >((U)(lhs << (T)bits)); + } - return SafeInt< U, E >( (U)( lhs << (T)bits ) ); + E::SafeIntOnOverflow(); } // Right shift template < typename T, typename U, typename E > SAFEINT_CONSTEXPR14 SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW { - ShiftAssert( !std::numeric_limits< T >::is_signed || (T)bits >= 0 ); - ShiftAssert( (T)bits < (int)safeint_internal::int_traits< U >::bitCount ); + if (valid_bitcount(bits)) + { + return SafeInt< U, E >((U)(lhs >> (T)bits)); + } - return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); + E::SafeIntOnOverflow(); } // Bitwise operators diff --git a/helpfile.md b/helpfile.md index a9a4b40..7a5fe59 100644 --- a/helpfile.md +++ b/helpfile.md @@ -324,7 +324,7 @@ The shift operators have the following signatures: SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) Note: -Shift operations where the right argument is larger than the number of bits in the left argument type minus 1 is implementation-defined behavior. SafeInt will attempt to help you avoid this by causing an assert in debug builds. If you prefer to have a stronger reaction, create a custom SAFEINT_ASSERT that throws or causes a fault in both debug and release builds. Shifting by a negative bit count also doesn't make sense, and is also trapped. +Shift operations where the right argument is larger than the number of bits in the left argument type minus 1 is implementation-defined behavior. Undefined behavior, such as shifting by a negative count or more bits than the type has will throw an exception. ### Methods SafeInt has a a small number of methods that help with common problems, such as aligning a value, getting a pointer to the raw integer type, and helping to manage pointer arithmetic.