Fix and cleanup safe spec; cryptonomex/graphene#10
This commit is contained in:
parent
2ef7583f90
commit
0391665471
3 changed files with 179 additions and 138 deletions
|
|
@ -35,7 +35,8 @@ namespace fc
|
|||
udt_error_code = 17,
|
||||
aes_error_code = 18,
|
||||
overflow_code = 19,
|
||||
underflow_code = 20
|
||||
underflow_code = 20,
|
||||
divide_by_zero_code = 21
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -289,6 +290,7 @@ namespace fc
|
|||
FC_DECLARE_EXCEPTION( aes_exception, aes_error_code, "AES error" );
|
||||
FC_DECLARE_EXCEPTION( overflow_exception, overflow_code, "Integer Overflow" );
|
||||
FC_DECLARE_EXCEPTION( underflow_exception, underflow_code, "Integer Underflow" );
|
||||
FC_DECLARE_EXCEPTION( divide_by_zero_exception, divide_by_zero_code, "Integer Divide By Zero" );
|
||||
|
||||
std::string except_str();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,126 +13,164 @@ namespace fc {
|
|||
*
|
||||
* It can only be used on built-in types. In particular,
|
||||
* safe<uint128_t> is buggy and should not be used.
|
||||
*
|
||||
* Implemented using spec from:
|
||||
* https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
|
||||
*/
|
||||
template<typename T>
|
||||
struct safe
|
||||
{
|
||||
T value = 0;
|
||||
|
||||
template<typename O>
|
||||
safe( O o ):value(o){}
|
||||
safe(){}
|
||||
safe( const safe& o ):value(o.value){}
|
||||
|
||||
static safe max()
|
||||
{ return std::numeric_limits<T>::max(); }
|
||||
static safe min()
|
||||
{ return std::numeric_limits<T>::min(); }
|
||||
|
||||
safe& operator += ( const safe& b )
|
||||
{
|
||||
if( b.value > 0 && value > (std::numeric_limits<T>::max() - b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (*this)(b) );
|
||||
if( b.value < 0 && value < (std::numeric_limits<T>::min() - b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (*this)(b) );
|
||||
value += b.value;
|
||||
return *this;
|
||||
return std::numeric_limits<T>::min();
|
||||
}
|
||||
static safe max()
|
||||
{
|
||||
return std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
friend safe operator + ( const safe& a, const safe& b )
|
||||
{
|
||||
if( b.value > 0 && a.value > std::numeric_limits<T>::max() - b.value ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
if( b.value < 0 && a.value < std::numeric_limits<T>::min() - b.value ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) );
|
||||
if( b.value > 0 && a.value > (std::numeric_limits<T>::max() - b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
if( b.value < 0 && a.value < (std::numeric_limits<T>::min() - b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) );
|
||||
return safe( a.value + b.value );
|
||||
}
|
||||
safe& operator *= ( safe v ) { value *= v.value; return *this; }
|
||||
safe& operator /= ( safe v ) { FC_ASSERT(v.value != 0); value /= v.value; return *this; }
|
||||
safe& operator -= ( const safe& b ) { return *this += safe(-b.value); }
|
||||
friend safe operator - ( const safe& a, const safe& b )
|
||||
{
|
||||
if( b.value > 0 && a.value < (std::numeric_limits<T>::min() + b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) );
|
||||
if( b.value < 0 && a.value > (std::numeric_limits<T>::max() + b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
return safe( a.value - b.value );
|
||||
}
|
||||
|
||||
friend safe operator * ( const safe& a, const safe& b )
|
||||
{
|
||||
if( a.value > 0 )
|
||||
{
|
||||
if( b.value > 0 )
|
||||
{
|
||||
if( a.value > (std::numeric_limits<T>::max() / b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( b.value < (std::numeric_limits<T>::min() / a.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( b.value > 0 )
|
||||
{
|
||||
if( a.value < (std::numeric_limits<T>::min() / b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( a.value != 0 && b.value < (std::numeric_limits<T>::max() / a.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
}
|
||||
}
|
||||
|
||||
return safe( a.value * b.value );
|
||||
}
|
||||
|
||||
friend safe operator / ( const safe& a, const safe& b )
|
||||
{
|
||||
if( b.value == 0 ) FC_CAPTURE_AND_THROW( divide_by_zero_exception, (a)(b) );
|
||||
if( a.value == std::numeric_limits<T>::min() && b.value == -1 ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
return safe( a.value / b.value );
|
||||
}
|
||||
friend safe operator % ( const safe& a, const safe& b )
|
||||
{
|
||||
if( b.value == 0 ) FC_CAPTURE_AND_THROW( divide_by_zero_exception, (a)(b) );
|
||||
if( a.value == std::numeric_limits<T>::min() && b.value == -1 ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) );
|
||||
return safe( a.value % b.value );
|
||||
}
|
||||
|
||||
safe operator - ()const
|
||||
{
|
||||
if( value == std::numeric_limits<T>::min() )
|
||||
FC_CAPTURE_AND_THROW( overflow_exception, (value) );
|
||||
if( value == std::numeric_limits<T>::min() ) FC_CAPTURE_AND_THROW( overflow_exception, (*this) );
|
||||
return safe( -value );
|
||||
}
|
||||
|
||||
safe operator++(int) { safe bak = *this; *this += 1; return bak; }
|
||||
safe& operator++() { return *this += 1; }
|
||||
safe operator--(int) { safe bak = *this; *this -= 1; return bak; }
|
||||
safe& operator--() { return *this -= 1; }
|
||||
|
||||
friend safe operator - ( const safe& a, const safe& b )
|
||||
safe& operator += ( const safe& b )
|
||||
{
|
||||
safe tmp(a); tmp -= b; return tmp;
|
||||
value = (*this + b).value;
|
||||
return *this;
|
||||
}
|
||||
safe& operator -= ( const safe& b )
|
||||
{
|
||||
value = (*this - b).value;
|
||||
return *this;
|
||||
}
|
||||
safe& operator *= ( const safe& b )
|
||||
{
|
||||
value = (*this * b).value;
|
||||
return *this;
|
||||
}
|
||||
safe& operator /= ( const safe& b )
|
||||
{
|
||||
value = (*this / b).value;
|
||||
return *this;
|
||||
}
|
||||
safe& operator %= ( const safe& b )
|
||||
{
|
||||
value = (*this % b).value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
safe& operator++()
|
||||
{
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
safe operator++( int )
|
||||
{
|
||||
safe bak = *this;
|
||||
*this += 1;
|
||||
return bak;
|
||||
}
|
||||
|
||||
safe& operator--()
|
||||
{
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
safe operator--( int )
|
||||
{
|
||||
safe bak = *this;
|
||||
*this -= 1;
|
||||
return bak;
|
||||
}
|
||||
|
||||
friend bool operator == ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value == b.value;
|
||||
}
|
||||
friend bool operator == ( const safe& a, const T& b )
|
||||
{
|
||||
return a.value == b;
|
||||
}
|
||||
friend bool operator == ( const T& a, const safe& b )
|
||||
{
|
||||
return a == b.value;
|
||||
}
|
||||
friend bool operator != ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value != b.value;
|
||||
}
|
||||
friend bool operator != ( const safe& a, const T& b )
|
||||
{
|
||||
return a.value != b;
|
||||
}
|
||||
friend bool operator != ( const T& a, const safe& b )
|
||||
{
|
||||
return a != b.value;
|
||||
}
|
||||
friend bool operator < ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value < b.value;
|
||||
}
|
||||
friend bool operator < ( const safe& a, const T& b )
|
||||
{
|
||||
return a.value < b;
|
||||
}
|
||||
friend bool operator < ( const T& a, const safe& b )
|
||||
{
|
||||
return a < b.value;
|
||||
}
|
||||
friend bool operator > ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value > b.value;
|
||||
}
|
||||
friend bool operator > ( const safe& a, const T& b )
|
||||
|
||||
friend bool operator != ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value > b;
|
||||
}
|
||||
friend bool operator > ( const T& a, const safe& b )
|
||||
{
|
||||
return a > b.value;
|
||||
}
|
||||
friend bool operator >= ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value >= b.value;
|
||||
}
|
||||
friend bool operator >= ( const safe& a, const T& b )
|
||||
{
|
||||
return a.value >= b;
|
||||
}
|
||||
friend bool operator >= ( const T& a, const safe& b )
|
||||
{
|
||||
return a >= b.value;
|
||||
return !(a == b);
|
||||
}
|
||||
friend bool operator <= ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value <= b.value;
|
||||
return !(a > b );
|
||||
}
|
||||
friend bool operator <= ( const safe& a, const T& b )
|
||||
friend bool operator >= ( const safe& a, const safe& b )
|
||||
{
|
||||
return a.value <= b;
|
||||
return !(a < b);
|
||||
}
|
||||
friend bool operator <= ( const T& a, const safe& b )
|
||||
{
|
||||
return a <= b.value;
|
||||
}
|
||||
T value = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace fc
|
|||
(aes_exception)
|
||||
(overflow_exception)
|
||||
(underflow_exception)
|
||||
(divide_by_zero_exception)
|
||||
)
|
||||
|
||||
namespace detail
|
||||
|
|
|
|||
Loading…
Reference in a new issue