#ifndef BOOST_NUMERIC_SAFE_RANGE_HPP #define BOOST_NUMERIC_SAFE_RANGE_HPP // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // Copyright (c) 2012 Robert Ramey // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include "numeric.hpp" #include "safe_compare.hpp" #include "safe_cast.hpp" #include "overflow.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // we could have used decltype and auto for C++11 but we've decided // to use boost/typeof to be compatible with older compilers #include namespace boost { namespace numeric { namespace detail { //////////////////////////////////////////////////// // layer 0 - detect overflows / alteration at the // atomic operation level taking care to work around // otherwise undetect alterations in integers due // to machine architecture. Note presumption of twos // complement integer arithmetic ////////////////////////// // addition implementation template struct check_addition_overflow{}; // both arguments unsigned template<> struct check_addition_overflow { template static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u) { BOOST_AUTO_TPL(tmp, t + u); if(boost::numeric::safe_compare::less_than(tmp, t) || boost::numeric::safe_compare::less_than(tmp, u)) overflow("safe range addition result overflow"); return tmp; } }; // T unsigned, U signed template<> struct check_addition_overflow { template static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u){ typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(boost::numeric::is_unsigned::value){ if(u < 0) overflow("safe range right operand value altered"); return check_addition_overflow::add( t, static_cast::type>(u) ); } else{ if(u > 0){ result_type tmp = t + u; if(tmp <= 0) overflow("safe range addition result overflow"); return t + u; } } return t + u; } }; // T signed, U unsigned template<> struct check_addition_overflow { template static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u){ typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(boost::numeric::is_unsigned::value){ if(t < 0) overflow("safe range left operand value altered"); return check_addition_overflow::add( static_cast::type>(t), u ); } else{ if(t > 0){ result_type tmp = t + u; if(tmp < 0) overflow("safe range addition result overflow"); } } BOOST_AUTO_TPL(tmp, t + u); return tmp; } }; // both arguments signed template<> struct check_addition_overflow { template static BOOST_TYPEOF_TPL(T() + U()) add(const T & t, const U & u){ typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(t > 0 && u > 0){ result_type tmp = t + u; if(tmp < 0) overflow("safe range addition result overflow"); return tmp; } if(t < 0 && u < 0){ result_type tmp = t + u; if(tmp >= 0) overflow("safe range addition result underflow"); return tmp; } return t + u; } }; ///////////////////////////// // subtraction implementation template struct check_subtraction_overflow{}; // both arguments signed template<> struct check_subtraction_overflow { template static BOOST_TYPEOF_TPL(T() + U()) subtract(const T & t, const U & u){ auto tmp = t - u; if(t > 0 && u < 0){ if(tmp < 0) overflow("safe range subtraction result overflow"); } if(t < 0 && u > 0) if(tmp >= 0){ overflow("safe range subtraction result underflow"); } return tmp; } }; // both arguments unsigned template<> struct check_subtraction_overflow { template static BOOST_TYPEOF_TPL(T() + U()) subtract(const T & t, const U & u) { if(safe_compare::less_than(t, u)) overflow("safe range subtraction unsigned difference less than zero"); return t - u; } }; // T unsigned, U signed template<> struct check_subtraction_overflow { template static BOOST_TYPEOF_TPL(T() + U()) subtract(const T & t, const U & u){ typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(boost::numeric::is_unsigned::value){ if(u < 0) overflow("safe range left operand value altered"); // u >= 0 if(u > t) overflow("unsigned result is negative"); } // result is signed return t - u; } }; // T signed, U unsigned template<> struct check_subtraction_overflow { template static BOOST_TYPEOF_TPL(T() + U()) subtract(const T & t, const U & u){ typedef BOOST_TYPEOF_TPL(T() + U()) result_type; if(boost::numeric::is_unsigned::value){ return check_subtraction_overflow::subtract( safe_cast(t), safe_cast(u) ); } // result is signed return check_subtraction_overflow::subtract( t, safe_cast(u) ); } }; //////////////////////////////// // multiplication implementation template BOOST_TYPEOF_TPL(T() * U()) check_multiplication_overflow(const T & t, const U & u){ typedef BOOST_TYPEOF_TPL(T() * U()) result_type; char const * const msg = "safe range multiplication overflow"; // presume that size of uintmax_t and intmax_t are the same typedef bits available_bits; if(multiply_result_bits::value <= boost::numeric::bits::value) return t * u; if(multiply_result_bits::value <= available_bits::value){ typedef typename multiply_result_type::type temp_type; temp_type tmp = static_cast(t) * temp_type(u); // the following works for both positive and negative results // and for both signed and unsigned numbers if(tmp > boost::integer_traits::const_max) boost::numeric::overflow(msg); if(tmp < boost::integer_traits::const_min) boost::numeric::overflow(msg); return static_cast(tmp); } // when the there is no native type which can hold the product // use multible precision // t is factored as (a << temp_bits) + b // u is factored as (c << temp_bits) + d // so we use multi-precision: // a + b // c + d // ----- // bd // ad // cb // ac // ----- // .. if(boost::numeric::is_unsigned::value && (t < 0 || u < 0)) overflow("conversion of negative value to unsigned"); if(t == 1) return u; if(u == 1) return t; result_type rt = t; if(rt < 0){ rt = ~rt + 1; // address if(rt < 0) overflow("overflow of negative value"); } result_type ru = u; if(ru < 0){ ru = ~ru + 1; // address if(ru < 0) overflow("overflow of negative value"); } // check positive values for overflow // t is factored as (a << temp_bits) + b // u is factored as (c << temp_bits) + d // so we use multi-precision: // a + b // c + d // ----- // bd // ad // cb // ac // ----- // .. typedef boost::uintmax_t accumulator_type; const int temp_bits = bits::value / 2; typedef typename boost::uint_t::least temp_type; temp_type a = (static_cast(rt) >> temp_bits); temp_type c = (static_cast(ru) >> temp_bits); if(0 != a && 0 != c) overflow(msg); temp_type b = static_cast(rt); if((static_cast(b) * static_cast(c) >> temp_bits) > 0) overflow(msg); temp_type d = static_cast(ru); if(0 != (static_cast(a) * static_cast(d) >> temp_bits)) overflow(msg); return t * u; } template BOOST_TYPEOF_TPL(T() / U()) check_division_overflow(const T & t, const U & u){ if(0 == u) overflow("divide by zero"); if(boost::numeric::is_signed::value){ // t unsigned, u signed if(boost::numeric::is_unsigned::value){ if(u < 0){ overflow("conversion of negative value to unsigned"); } } else{ // both signed // pathological case: change sign on negative number so it overflows if(t == boost::integer_traits::const_min && u == -1) overflow("overflow in result"); } } // both unsigned // t signed, u unsigned return t / u; } template BOOST_TYPEOF_TPL(T() / U()) check_modulus_overflow(const T & t, const U & u){ if(0 == u) overflow("modulus divide by zero"); if(boost::numeric::is_signed::value){ // t unsigned, u signed if(boost::numeric::is_unsigned::value){ if(u < 0){ overflow("conversion of negative value to unsigned"); } } else{ // both signed // pathological case: change sign on negative number so it overflows if(t == boost::integer_traits::const_min && u == -1) overflow("overflow in result"); } } // both unsigned // t signed, u unsigned return t % u; } } // detail template< class Stored, class Derived > class safe_range_base { Derived & derived() { return static_cast(*this); } const Derived & derived() const { return static_cast(*this); } template Stored validate(const T & t) const { return derived().validate(t); } Stored m_t; protected: //////////////////////////////////////////////////////////// // constructors // default constructor safe_range_base() {} // copy constructor safe_range_base(const safe_range_base & t) : m_t(t.m_t) {} template safe_range_base(const T & t) { // verify that this is convertible to the storable type BOOST_STATIC_ASSERT(( boost::is_convertible::value )); validate(t); m_t = static_cast(t); } typedef Stored stored_type; public: ///////////////////////////////////////////////////////////////// // modification binary operators template Derived & operator=(const T & rhs){ m_t = validate(rhs); return derived(); } template Derived & operator+=(const T & rhs){ *this = *this + rhs; return derived(); } template Derived & operator-=(const T & rhs){ *this = *this - rhs; return derived(); } template Derived & operator*=(const T & rhs){ *this = *this * rhs; return derived(); } template Derived & operator/=(const T & rhs){ *this = *this / rhs; return derived(); } template Derived & operator%=(const T & rhs){ *this = *this % rhs; return derived(); } template Derived & operator|=(const T & rhs){ *this = *this | rhs; return derived(); } template Derived & operator&=(const T & rhs){ *this = *this & rhs; return derived(); } template Derived & operator^=(const T & rhs){ *this = *this * rhs; return derived(); } template Derived & operator>>=(const T & rhs){ *this = *this >> rhs; return derived(); } template Derived & operator<<=(const T & rhs){ *this = *this << rhs; return derived(); } // unary operators Derived operator++(){ *this = *this + 1; return derived(); } Derived operator--(){ *this = *this - 1; return derived(); } Derived operator++(int){ Derived rvalue = *this; m_t = validate(*this + 1); return rvalue; } Derived & operator--(int){ Derived rvalue = *this; m_t = validate(*this - 1); return rvalue; } Derived operator-() const { return validate( check_unary_negation_overflow(m_t) ); } Derived operator~() const { return validate(~m_t); } ///////////////////////////////////////////////////////////////// // comparison operators template bool operator<(const U & rhs) const { return safe_compare::less_than(m_t, rhs); } template bool operator>(const U & rhs) const { return safe_compare::greater_than(m_t, rhs); } template bool operator==(const U & rhs) const { return safe_compare::equal(m_t, rhs); } template bool inline operator!=(const U & rhs) const { return ! safe_compare::equal(m_t,rhs); } template bool inline operator>=(const U & rhs) const { return ! safe_compare::less_than(m_t, rhs); } template bool inline operator<=(const U & rhs) const { return ! safe_compare::greater_than(m_t, rhs); } ///////////////////////////////////////////////////////////////// // addition // case 1 - no overflow possible template struct no_addition_overflow_possible : public boost::mpl::and_< typename boost::mpl::greater< typename boost::mpl::sizeof_< BOOST_TYPEOF_TPL(Stored() - U()) >, typename boost::mpl::max< boost::mpl::sizeof_, boost::mpl::sizeof_ >::type >, boost::numeric::is_signed > {}; template typename boost::enable_if< no_addition_overflow_possible, BOOST_TYPEOF_TPL(Stored() - U()) >::type inline operator+(const U & rhs) const { return m_t + rhs; } // case 2 - overflow possible - must be checked at run time template typename boost::disable_if< no_addition_overflow_possible, BOOST_TYPEOF_TPL(Stored() - U()) >::type inline operator+(const U & rhs) const { return detail::check_addition_overflow< boost::numeric::is_signed::value, boost::numeric::is_signed::value >::add(m_t, rhs); } ///////////////////////////////////////////////////////////////// // subtraction template struct no_subtraction_overflow_possible : public boost::mpl::and_< typename boost::mpl::greater< typename boost::mpl::sizeof_< BOOST_TYPEOF_TPL(Stored() - U()) >, typename boost::mpl::max< boost::mpl::sizeof_, boost::mpl::sizeof_ >::type >, boost::numeric::is_signed > {}; // case 1 - no overflow possible template typename boost::enable_if< no_subtraction_overflow_possible, BOOST_TYPEOF_TPL(Stored() - U()) >::type inline operator-(const U & rhs) const { return m_t - rhs; } template typename boost::disable_if< no_subtraction_overflow_possible, BOOST_TYPEOF_TPL(Stored() - U()) >::type inline operator-(const U & rhs) const { return detail::check_subtraction_overflow< boost::numeric::is_signed::value, boost::numeric::is_signed::value >::subtract(m_t, safe_cast(rhs)); } ///////////////////////////////////////////////////////////////// // multiplication template BOOST_TYPEOF_TPL(U() * Stored()) inline operator*(const U & rhs) const { return detail::check_multiplication_overflow(m_t, rhs); } ///////////////////////////////////////////////////////////////// // division template BOOST_TYPEOF_TPL(U() / Stored()) inline operator/(const U & rhs) const { return detail::check_division_overflow(m_t, rhs); } ///////////////////////////////////////////////////////////////// // modulus template BOOST_TYPEOF_TPL(Stored() % U()) inline operator%(const U & rhs) const { if(0 == rhs) throw std::domain_error("Divide by zero"); return detail::check_modulus_overflow(m_t, rhs); } ///////////////////////////////////////////////////////////////// // logical operators template BOOST_TYPEOF_TPL(Stored() | U()) inline operator|(const U & rhs) const { // verify that U is an integer type BOOST_STATIC_ASSERT_MSG( std::numeric_limits::is_integer, "right hand side is not an integer type" ); return m_t | rhs; } template BOOST_TYPEOF_TPL(Stored() & U()) inline operator&(const U & rhs) const { // verify that U is an integer type BOOST_STATIC_ASSERT_MSG( std::numeric_limits::is_integer, "right hand side is not an integer type" ); return m_t & rhs; } template BOOST_TYPEOF_TPL(Stored() ^ U()) inline operator^(const U & rhs) const { // verify that U is an integer type BOOST_STATIC_ASSERT_MSG( std::numeric_limits::is_integer, "right hand side is not an integer type" ); return m_t ^ rhs; } template Stored inline operator>>(const U & rhs) const { // verify that U is an integer type BOOST_STATIC_ASSERT_MSG( std::numeric_limits::is_integer, "right hand side is not an integer type" ); if(m_t < 0) overflow("right shift of negative number undefined"); typedef BOOST_TYPEOF_TPL(Stored() >> U()) result_type; if(rhs > boost::numeric::bits::value) overflow("conversion of negative value to unsigned"); return m_t >> rhs; } template Stored inline operator<<(const U & rhs) const { // verify that U is an integer type BOOST_STATIC_ASSERT_MSG( std::numeric_limits::is_integer, "right hand side is not an integer type" ); if(m_t < 0) overflow("right shift of negative number undefined"); typedef BOOST_TYPEOF_TPL(Stored() >> U()) result_type; if(rhs > boost::numeric::bits::value) overflow("conversion of negative value to unsigned"); return m_t << rhs; } //////////////////////////////// // unary negation implementation Stored operator-(){ // this makes no sense for unsigned types BOOST_STATIC_ASSERT((boost::numeric::is_signed::value)); // the most common situation would be doing something like // boost::uint8_t x = -128; // ... // --x; if(boost::integer_traits::const_max == m_t) overflow("safe range unary negation overflow"); return -m_t; } ///////////////////////////////////////////////////////////////// // casting operators for intrinsic integers operator stored_type () const { return m_t; } }; ///////////////////////////////////////////////////////////////// // Note: the following global operators will be only found via // argument dependent lookup. So they won't conflict any // other global operators for types in namespaces other than // boost::numeric // These should catch things like U < safe_range_base<...> and implement them // as safe_range_base<...> >= U which should be handled above. ///////////////////////////////////////////////////////////////// // binary operators // comparison operators template typename boost::enable_if< boost::is_integral, bool >::type operator<(const T & lhs, const safe_range_base & rhs) { return rhs > lhs; } template typename boost::enable_if< boost::is_integral, bool >::type inline operator>(const T & lhs, const safe_range_base & rhs) { return rhs < lhs; } template typename boost::enable_if< boost::is_integral, bool >::type inline operator==(const T & lhs, const safe_range_base & rhs) { return rhs == lhs; } template typename boost::enable_if< boost::is_integral, bool >::type inline operator!=(const T & lhs, const safe_range_base & rhs) { return rhs != rhs; } template typename boost::enable_if< boost::is_integral, bool >::type inline operator>=(const T & lhs, const safe_range_base & rhs) { return rhs <= lhs; } template typename boost::enable_if< boost::is_integral, bool >::type inline operator<=(const T & lhs, const safe_range_base & rhs) { return rhs >= lhs; } // addition template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() + Stored()) >::type inline operator+(const T & lhs, const safe_range_base & rhs) { return rhs + lhs; } // subtraction template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() - Stored()) >::type inline operator-(const T & lhs, const safe_range_base & rhs) { BOOST_TYPEOF_TPL(T() - Stored()) tmp = rhs - lhs; return - tmp; } // multiplication template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() * Stored()) >::type inline operator*(const T & lhs, const safe_range_base & rhs) { return rhs * lhs; } // division // special case - possible overflow template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() / Stored()) >::type inline operator/(const T & lhs, const safe_range_base & rhs) { if(safe_compare::equal(0, rhs)) throw std::domain_error("Divide by zero"); return static_cast< BOOST_TYPEOF_TPL(T() / Stored()) >(lhs / static_cast(rhs)); } // modulus template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() % Stored()) >::type inline operator%(const T & lhs, const safe_range_base & rhs) { if(safe_compare::equal(0, rhs)) throw std::domain_error("Divide by zero"); return static_cast< BOOST_TYPEOF_TPL(T() % Stored()) >(lhs % static_cast(rhs)); } // logical operators template typename boost::enable_if< boost::is_integral, typename multiply_result_type::type >::type inline operator|(const T & lhs, const safe_range_base & rhs) { return rhs | lhs; } template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() & Stored()) >::type inline operator&(const T & lhs, const safe_range_base & rhs) { return rhs & lhs; } template typename boost::enable_if< boost::is_integral, BOOST_TYPEOF_TPL(T() ^ Stored()) >::type inline operator^(const T & lhs, const safe_range_base & rhs) { return rhs ^ lhs; } ///////////////////////////////////////////////////////////////// // higher level types implemented in terms of safe_range_base namespace detail { template< boost::intmax_t MIN, boost::intmax_t MAX > struct signed_stored_type { // double check that MIN < MAX typedef typename boost::int_t< boost::mpl::max< typename boost::numeric::detail::log, typename boost::numeric::detail::log >::type::value >::least type; }; template< boost::uintmax_t MIN, boost::uintmax_t MAX > struct unsigned_stored_type { // calculate max(abs(MIN, MAX)) typedef typename boost::uint_t< boost::mpl::max< typename boost::numeric::detail::ulog, typename boost::numeric::detail::ulog >::type::value >::least type; }; } // detail ///////////////////////////////////////////////////////////////// // safe_signed_range template< boost::intmax_t MIN, boost::intmax_t MAX > class safe_signed_range : public safe_range_base< typename detail::signed_stored_type::type, safe_signed_range > { BOOST_STATIC_ASSERT_MSG( MIN < MAX, "minimum must be less than maximum" ); public: typedef typename boost::numeric::safe_range_base< typename detail::signed_stored_type::type, safe_signed_range > base; typedef typename detail::signed_stored_type::type stored_type; template stored_type validate(const T & t) const { if(safe_compare::less_than(t, MIN) || safe_compare::greater_than(t, MAX)) overflow("safe range out of range"); return static_cast(t); } safe_signed_range() : base() {} template safe_signed_range(const T & t) : base(t) {} }; template< boost::intmax_t MIN, boost::intmax_t MAX > std::ostream & operator<<(std::ostream & os, const safe_signed_range & t){ return os << static_cast::stored_type &>(t); } template< boost::intmax_t MIN, boost::intmax_t MAX > std::istream & operator>>(std::istream & is, safe_signed_range & t){ typename safe_signed_range::stored_type tx; is >> tx; t = tx; return is; } ///////////////////////////////////////////////////////////////// // safe_unsigned_range template< boost::uintmax_t MIN, boost::uintmax_t MAX > class safe_unsigned_range : public safe_range_base< typename detail::unsigned_stored_type::type, safe_unsigned_range > { BOOST_STATIC_ASSERT_MSG( MIN < MAX, "minimum must be less than maximum" ); public: typedef typename boost::numeric::safe_range_base< typename detail::unsigned_stored_type::type, safe_unsigned_range > base; typedef typename detail::unsigned_stored_type::type stored_type; template stored_type validate(const T & t) const { if(safe_compare::less_than(t, MIN) || safe_compare::greater_than(t, MAX)) overflow("safe range out of range"); return static_cast(t); } safe_unsigned_range(){} template safe_unsigned_range(const T & t) : base(t) {} }; template< boost::uintmax_t MIN, boost::uintmax_t MAX > std::ostream & operator<<(std::ostream & os, const safe_unsigned_range & t){ return os << static_cast::stored_type &>(t); } template< boost::uintmax_t MIN, boost::uintmax_t MAX > std::istream & operator>>(std::istream & is, safe_unsigned_range & t){ typename safe_unsigned_range::stored_type tx; is >> tx; t = tx; return is; } } // numeric } // boost #endif // BOOST_NUMERIC_SAFE_RANGE_HPP