📄 safeint.hpp
字号:
}
}
//Note - the standard is arguably broken in the case of some integer
//conversion operations
//For example, signed char a = -1 = 0xff
// unsigned int b = 0xffffffff
//if you then test if a < b, a value-preserving cast
//is made, and you're essentially testing
// (unsigned int)a < b == false
//
// I do not think this makes sense - if you perform
// a cast to an _int64, which can clearly preserve both value and signedness
// then you get a different and intuitively correct answer
// IMHO, -1 should be less than 4 billion
// If you prefer to retain the ANSI standard behavior
// insert #define ANSI_CONVERSIONS into your source
// Behavior differences occur in the following cases:
// 8, 16, and 32-bit signed int, unsigned 32-bit int
// any signed int, unsigned 64-bit int
// Note - the signed int must be negative to show the problem
template <typename U>
static bool LessThan(T lhs, U rhs)
{
#ifdef ANSI_CONVERSIONS
return lhs < rhs;
#else
return (SafeInt<U>(rhs) > lhs);
#endif
}
template <typename U>
static bool GreaterThan(T lhs, U rhs)
{
#ifdef ANSI_CONVERSIONS
return lhs > rhs;
#else
if(SafeInt<T>::IsSigned(lhs) == SafeInt<T>::IsSigned(rhs))
{
if(sizeof(T) > sizeof(U))
return (T)lhs > (T)rhs;
return (U)lhs > (U)rhs;
}
//all remaining cases are mixed sign
if((sizeof(T) < 4 && sizeof(U) < 4) ||
(SafeInt<T>::IsSigned(lhs) && sizeof(T) == 4 && sizeof(U) < 4) ||
(SafeInt<T>::IsSigned(rhs) && sizeof(U) == 4 && sizeof(T) < 4))
{
//all of these fit into an int
return (_int32)lhs > (_int32)rhs;
}
if((SafeInt<T>::IsSigned(rhs) && sizeof(U) == 8 && sizeof(T) < 8) ||
(SafeInt<T>::IsSigned(lhs) && sizeof(T) == 8 && sizeof(U) < 8))
{
//these cases all fit into an _int64
return (_int64)lhs > (_int64)rhs;
}
//for all remaining cases unsigned value is 64-bit
//corner cases - signed value is negative
if(SafeInt<T>::IsSigned(lhs) && lhs < 0)
{
//if lhs < 0, rhs unsigned
return false;
}
//2nd corner case
if(SafeInt<T>::IsSigned(rhs) && rhs < 0)
{
//rhs < 0, lhs unsigned
return true;
}
//now the signed value is positive, and must fit into a 64-bit unsigned
return (unsigned _int64)lhs > (unsigned _int64)rhs;
#endif
}
template <typename U>
static bool Equals(T lhs, U rhs)
{
#ifdef ANSI_CONVERSIONS
return lhs == rhs;
#else
if(SafeInt<T>::IsSigned(lhs) == SafeInt<T>::IsSigned(rhs))
{
if(sizeof(T) > sizeof(U))
return (T)lhs == (T)rhs;
return (U)lhs == (U)rhs;
}
//all remaining cases are mixed sign
if((sizeof(T) < 4 && sizeof(U) < 4) ||
(SafeInt<T>::IsSigned(lhs) && sizeof(T) == 4 && sizeof(U) < 4) ||
(SafeInt<T>::IsSigned(rhs) && sizeof(U) == 4 && sizeof(T) < 4))
{
//all of these fit into an int
return (_int32)lhs == (_int32)rhs;
}
if((SafeInt<T>::IsSigned(rhs) && sizeof(U) == 8 && sizeof(T) < 8) ||
(SafeInt<T>::IsSigned(lhs) && sizeof(T) == 8 && sizeof(U) < 8))
{
//these cases all fit into an _int64
return (_int64)lhs == (_int64)rhs;
}
//for all remaining cases unsigned value is 64-bit
if((SafeInt<T>::IsSigned(lhs) && lhs < 0) ||
(SafeInt<T>::IsSigned(rhs) && rhs < 0))
{
//corner case - signed value is negative
//cannot possibly be equal to the unsigned value
return false;
}
//now the signed value is positive, and must fit into a 64-bit unsigned
return (unsigned _int64)lhs == (unsigned _int64)rhs;
#endif
}
//this is almost certainly not the best optimized version of atoi,
//but it does not display a typical bug where it isn't possible to set MinInt
//and it won't allow you to overflow your integer
//This is here because it is useful, and it is an example of what
//can be done easily with SafeInt
template <typename U>
static SafeInt<T> SafeTtoI(U* input)
{
U* tmp = input;
SafeInt<T> s;
bool negative = false;
if(input == NULL || input[0] == 0)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
switch(*tmp)
{
case '-':
tmp++;
negative = true;
break;
case '+':
tmp++;
break;
}
while(*tmp != 0)
{
if(*tmp < '0' || *tmp > '9')
break;
if(s.Value() != 0)
s *= (T)10;
if(!negative)
s += (T)(*tmp - '0');
else
s -= (T)(*tmp - '0');
tmp++;
}
return s;
}
//internal helper functions
//explanation - consider 2 8-bit ints:
// 00001010
// ^10001000
// =10000010
// if result is < 0, high bit is set
//so we now have an efficient test to see if two integers
//are the same sign or opposite signs
static bool IsMixedSign(T lhs, T rhs)
{
return ((lhs ^ rhs) < 0);
}
T m_int;
};
//externally defined functions for the case of U op SafeInt<T>
template <typename T, typename U>
bool operator <(U lhs, SafeInt<T> rhs)
{
return rhs > lhs;
}
template <typename T, typename U>
bool operator <(SafeInt<U> lhs, SafeInt<T> rhs)
{
return lhs < rhs.Value();
}
//greater than
template <typename T, typename U>
bool operator >(U lhs, SafeInt<T> rhs)
{
return rhs < lhs;
}
template <typename T, typename U>
bool operator >(SafeInt<T> lhs, SafeInt<U> rhs)
{
return lhs > rhs.Value();
}
//greater than or equal
template <typename T, typename U>
bool operator >=(U lhs, SafeInt<T> rhs)
{
return rhs < lhs;
}
template <typename T, typename U>
bool operator >=(SafeInt<T> lhs, SafeInt<U> rhs)
{
return lhs >= rhs.Value();
}
//less than or equal
template <typename T, typename U>
bool operator <=(U lhs, SafeInt<T> rhs)
{
return rhs > lhs;
}
template <typename T, typename U>
bool operator <=(SafeInt<T> lhs, SafeInt<U> rhs)
{
return lhs <= rhs.Value();
}
//equality
//explicit overload for bool
template <typename T>
bool operator ==(bool lhs, SafeInt<T> rhs)
{
return lhs == (rhs.Value() == 0 ? false : true);
}
template <typename T, typename U>
bool operator ==(U lhs, SafeInt<T> rhs)
{
return rhs == lhs;
}
template <typename T, typename U>
bool operator ==(SafeInt<T> lhs, SafeInt<U> rhs)
{
return lhs == rhs.Value();
}
//not equals
template <typename T, typename U>
bool operator !=(U lhs, SafeInt<T> rhs)
{
return rhs != lhs;
}
template <typename T>
bool operator !=(bool lhs, SafeInt<T> rhs)
{
return (rhs.Value() == 0 ? false : true) != lhs;
}
template <typename T, typename U>
bool operator !=(SafeInt<T> lhs, SafeInt<U> rhs)
{
return lhs != rhs.Value();
}
//modulus
template <typename T, typename U>
SafeInt<T> operator %(U lhs, SafeInt<T> rhs)
{
//value of return depends on sign of lhs
//this one may not be safe - bounds check in constructor
//if lhs is negative and rhs is unsigned, this will throw an exception
//fast-track the simple case
if(sizeof(T) == sizeof(U) &&
SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
{
if(rhs == 0)
throw SafeIntException(EXCEPTION_INT_DIVIDE_BY_ZERO);
return SafeInt<T>((T)(lhs % rhs.Value()));
}
return SafeInt<T>( (SafeInt<U>(lhs) % rhs.Value()) );
}
//multiplication
template <typename T, typename U>
SafeInt<T> operator *(U lhs, SafeInt<T> rhs)
{
return rhs * lhs;
}
//division
template <typename T, typename U> SafeInt<T> operator /(U lhs, SafeInt<T> rhs)
{
//no easy way out - cannot make lhs into a SafeInt, then convert
//or cases of lhs unsigned, rhs < 0 become illegal which is wrong
//first test corner cases
if(rhs.Value() == 0)
throw SafeIntException(EXCEPTION_INT_DIVIDE_BY_ZERO);
//only if both are signed, check corner case
if(SafeInt<U>::IsSigned() && SafeInt<T>::IsSigned())
{
//corner case where lhs = MinInt and rhs = -1
if(lhs == SafeInt<U>::MinInt() && rhs == -1)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//it is safe to divide lhs by an arbitrary larger number
//unless T is unsigned and U is signed
//if both have the same sign, there is no problem
//though there could be an overflow - do not cast result to T
if(SafeInt<U>::IsSigned() == SafeInt<T>::IsSigned())
{
return SafeInt<T>(lhs/rhs.Value());
}
//now we have mixed sign case, which can lead to problems
//first consider T signed, U unsigned
if(SafeInt<T>::IsSigned())
{
if(sizeof(U) < sizeof(T))
{
//simply upcast to T - lhs always fits into T
return SafeInt<T>(((T)lhs)/rhs.Value());
}
if(sizeof(U) < 4 && sizeof(T) < 4)
{
//even if U is bigger, upcast to int
return SafeInt<T>((_int32)lhs/(_int32)rhs.Value());
}
//now U is either 32 or 64-bit, T same size or smaller
if(sizeof(U) == 4)
{
//upcast to 64-bit
return SafeInt<T>((_int64)lhs/(_int64)rhs.Value());
}
//U is unsigned _int64
//now it matters whether rhs < 0
if(rhs.Value() < 0)
{
U tmp = lhs/((U)-rhs.Value());
if(tmp == (U)SafeInt<T>::MaxInt()+1)
return SafeInt<T>(SafeInt<T>::MinInt());
//else tmp is too big, or can be negated
return SafeInt<T>(-(SafeInt<T>(tmp)));
}
//rhs >= 0
//T now has to fit into U
return SafeInt<T>((U)lhs/(U)rhs.Value());
}
//now lhs is signed, rhs unsigned - return must be unsigned
if(lhs < 0)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
if(sizeof(T) >= sizeof(U))
{
//all U fits into T
return SafeInt<T>((T)lhs/rhs.Value());
}
//now sizeof(U) > sizeof(T) and rhs >= 0
//all T fits into U
return SafeInt<T>(lhs/(U)rhs.Value());
}
//addition
template <typename T, typename U>
SafeInt<T> operator +(U lhs, SafeInt<T> rhs)
{
return rhs + lhs;
}
//subtraction
template <typename T, typename U>
SafeInt<T> operator -(U lhs, SafeInt<T> rhs)
{
if(rhs.IsSigned())
{
if(SafeInt<U>::IsSigned())
{
//both are signed
if(sizeof(T) >= sizeof(U))
return SafeInt<T>(lhs) - rhs;
//else
return SafeInt<T>(SafeInt<U>(lhs) - rhs.Value());
}
//lhs is unsigned, rhs is signed
if(sizeof(T) > sizeof(U))
return SafeInt<T>(lhs) - rhs;
//U is >= T - not all values of U fit into T
if(sizeof(U) < 4)
{
//upcast to int
return SafeInt<T>((_int32)lhs - (_int32)rhs.Value());
}
if(sizeof(U) == 4)
{
//upcast to _int64
return SafeInt<T>((_int64)lhs - (_int64)rhs.Value());
}
if(sizeof(U) == 8)
{
//lhs - unsigned _int64
//rhs - signed int - any size
if(sizeof(T) < 8)
{
//if this is true, the result can never fit into T
if(lhs > ((U)2 * (U)SafeInt<T>::MaxInt()))
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//everything has to be able to fit into an _int64
return SafeInt<T>((_int64)lhs - (_int64)rhs.Value());
}
//now rhs is _int64
//if rhs is > 0, upcast to U
if(rhs.Value() >= 0)
return SafeInt<T>(SafeInt<U>(lhs) - rhs.Value());
//if rhs < 0, treat as addition
//unboxing everything maintains correctness, even if rhs = MinInt
return SafeInt<T>(SafeInt<U>(lhs) + SafeInt<U>((U)((T)-rhs.Value())) );
}
}
//T is unsigned
//this means that if lhs < 0, result is an error
if(SafeInt<T>::IsSigned(lhs) && lhs < 0)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//whether T is signed or not, it is non-negative
//if U fits in T, no problem
if(sizeof(T) >= sizeof(U))
{
return SafeInt<T>(lhs) - rhs;
}
//sizeof(T) < sizeof(U)
//T fits in U, whether U is signed or unsigned
return SafeInt<T>(SafeInt<U>(lhs) - rhs.Value());
}
//shift operators
//NOTE - shift operators always return the type of the lhs argument
//left shift
template <typename T, typename U>
SafeInt<U> operator <<(U lhs, SafeInt<T> bits)
{
if(bits.IsSigned())
assert(bits.Value() >= 0);
assert(bits.Value() < SafeInt<U>::BitCount());
return SafeInt<U>((U)(lhs << bits.Value()));
}
//right shift
template <typename T, typename U>
SafeInt<U> operator >>(U lhs, SafeInt<T> bits)
{
if(bits.IsSigned())
assert(bits.Value() >= 0);
assert(bits.Value() < SafeInt<U>::BitCount());
return SafeInt<U>((U)(lhs >> bits.Value()));
}
//bi
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -