?? safeint.hpp
字號:
SafeInt<T>& operator ^=(SafeInt<U> rhs)
{
*this = *this ^ rhs.Value();
return *this;
}
//bitwise OR
SafeInt<T> operator |(SafeInt<T> rhs)
{
return SafeInt<T>((T)(m_int | rhs.m_int));
}
template <typename U>
SafeInt<T> operator |(U rhs)
{
//if U can fit into T without truncating, force U to T
if(sizeof(U) <= sizeof(T))
return SafeInt<T>(m_int | (T)rhs);
if( (U)((T)rhs) == rhs)
return SafeInt<T>(m_int | (T)rhs);
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//bitwise OR assignment
SafeInt<T>& operator |=(SafeInt<T> i)
{
m_int |= i.m_int;
return *this;
}
template <typename U>
SafeInt<T>& operator |=(U rhs)
{
*this = *this | rhs;
return *this;
}
template <typename U>
SafeInt<T>& operator |=(SafeInt<U> rhs)
{
*this = *this | rhs.Value();
return *this;
}
//logical operators
//logical operators are like comparison operators
//because the return value is the same regardless of
//what type is on the RHS or the LHS
//and as it turns out, we need some overloads
//bool constructor has a little overhead
//possible combinations:
// SafeInt<T>, SafeInt<T> - internal
// SafeInt<T>, U - internal
// SafeInt<T>, bool - internal
// bool, SafeInt<T> - external
// U, SafeInt<T> - external
// SafeInt<U>, SafeInt<T> - external
//logical OR
bool operator ||(SafeInt<T> rhs)
{
return m_int || rhs.Value();
}
template <typename U>
bool operator ||(U rhs)
{
return m_int || rhs;
}
bool operator ||(bool rhs)
{
return m_int || rhs;
}
//logical &&
bool operator &&(SafeInt<T> rhs)
{
return m_int && rhs.Value();
}
template <typename U>
bool operator &&(U rhs)
{
return m_int && rhs;
}
bool operator &&(bool rhs)
{
return m_int && rhs;
}
//miscellaneous helper functions
SafeInt<T> Min(SafeInt<T> test, SafeInt<T> floor = SafeInt<T>::MinInt()) const
{
T tmp = test.Value() < m_int ? test.Value() : m_int;
return tmp < floor ? floor : tmp;
}
SafeInt<T> Max(SafeInt<T> test, SafeInt<T> upper = SafeInt<T>::MaxInt()) const
{
T tmp = test.Value() > m_int ? test.Value() : m_int;
return tmp > upper ? upper : tmp;
}
void Swap( SafeInt<T>& with )
{
T temp( m_int );
m_int = with.m_int;
with.m_int = temp;
}
static SafeInt<T> SafeAtoI(const char* input)
{
return SafeTtoI(input);
}
static SafeInt<T> SafeWtoI(const wchar_t* input)
{
return SafeTtoI(input);
}
private:
//note - this looks complex, but most of the conditionals
//are constant and optimize away
//for example, a signed 64-bit check collapses to:
/*
if(lhs == 0 || rhs == 0)
return 0;
if(MaxInt()/+lhs < +rhs)
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//ok
return lhs * rhs;
Which ought to inline nicely
*/
static T multiply(T lhs, T rhs)
{
if(Is64Bit())
{
//fast track this one - and avoid DIV_0 below
if(lhs == 0 || rhs == 0)
return 0;
//we're 64 bit - slow, but the only way to do it
if(IsSigned())
{
if(!IsMixedSign(lhs, rhs))
{
//both positive or both negative
//result will be positive, check for lhs * rhs > MaxInt
if(lhs > 0)
{
//both positive
if(MaxInt()/lhs < rhs)
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
}
else
{
//both negative
//comparison gets tricky unless we force it to positive
//EXCEPT that -MinInt is undefined - can't be done
//And MinInt always has a greater magnitude than MaxInt
if(lhs == MinInt() || rhs == MinInt())
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
if(MaxInt()/(-lhs) < (-rhs) )
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
}
}
else
{
//mixed sign - this case is difficult
//test case is lhs * rhs < MinInt => overflow
//if lhs < 0 (implies rhs > 0),
//lhs < MinInt/rhs is the correct test
//else if lhs > 0
//rhs < MinInt/lhs is the correct test
//avoid dividing MinInt by a negative number,
//because MinInt/-1 is a corner case
if(lhs < 0)
{
if(lhs < MinInt()/rhs)
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
}
else
{
if(rhs < MinInt()/lhs)
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
}
}
//ok
return lhs * rhs;
}
else
{
//unsigned, easy case
if(MaxInt()/lhs < rhs)
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//ok
return lhs * rhs;
}
}
else if(Is32Bit())
{
//we're 32-bit
if(IsSigned())
{
signed _int64 tmp = (signed _int64)lhs * (signed _int64)rhs;
//upper 33 bits must be the same
//most common case is likely that both are positive - test first
if( (tmp & 0xffffffff80000000LL) == 0 ||
(tmp & 0xffffffff80000000LL) == 0xffffffff80000000LL)
{
//this is OK
return (T)tmp;
}
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
else
{
unsigned _int64 tmp = (unsigned _int64)lhs * (unsigned _int64)rhs;
if (tmp & 0xffffffff00000000ULL) //overflow
{
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
return (T)tmp;
}
}
else if(Is16Bit())
{
//16-bit
if(IsSigned())
{
signed _int32 tmp = (signed _int32)lhs * (signed _int32)rhs;
//upper 17 bits must be the same
//most common case is likely that both are positive - test first
if( (tmp & 0xffff8000) == 0 || (tmp & 0xffff8000) == 0xffff8000)
{
//this is OK
return (T)tmp;
}
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
else
{
unsigned _int32 tmp = (unsigned _int32)lhs * (unsigned _int32)rhs;
if (tmp & 0xffff0000) //overflow
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
return (T)tmp;
}
}
else //8-bit
{
assert(Is8Bit());
if(IsSigned())
{
signed _int16 tmp = (signed _int16)lhs * (signed _int16)rhs;
//upper 9 bits must be the same
//most common case is likely that both are positive - test first
if( (tmp & 0xff80) == 0 || (tmp & 0xff80) == 0xff80)
{
//this is OK
return (T)tmp;
}
//overflow
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
else
{
unsigned _int16 tmp = ((unsigned _int16)lhs) * ((unsigned _int16)rhs);
if (tmp & 0xff00) //overflow
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
return (T)tmp;
}
}
}
static inline T addition(T lhs, T rhs)
{
if(IsSigned())
{
//test for +/- combo
if(!IsMixedSign(lhs, rhs))
{
//either two negatives, or 2 positives
if(rhs < 0)
{
//two negatives
if(lhs < (T)(MinInt() - rhs)) //remember rhs < 0
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//ok
}
else
{
//two positives
if((T)(MaxInt() - lhs) < rhs)
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//OK
}
}
//else overflow not possible
return lhs + rhs;
}
else //unsigned
{
if((T)(MaxInt() - lhs) < rhs)
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
return (lhs + rhs);
}
}
static T subtraction(T lhs, T rhs)
{
if(IsSigned())
{
if(IsMixedSign(lhs, rhs)) //test for +/- combo
{
//mixed positive and negative
//two cases - +X - -Y => X + Y - check for overflow against MaxInt()
// -X - +Y - check for overflow against MinInt()
if(lhs >= 0) //first case
{
//test is X - -Y > MaxInt()
//equivalent to X > MaxInt() - |Y|
//Y == MinInt() creates special case
//Even 0 - MinInt() can't be done
//note that the special case collapses into the general case, due to the fact
//MaxInt() - MinInt() == -1, and lhs is non-negative
if(lhs > (T)(MaxInt() + rhs)) //remember that rhs is negative
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//fall through to return value
}
else
{
//second case
//test is -X - Y < MinInt()
//or -X < MinInt() + Y
//we do not have the same issues because abs(MinInt()) > MaxInt()
if(lhs < (T)(MinInt() + rhs))
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//fall through to return value
}
}
// else
//both negative, or both positive
//no possible overflow
return (lhs - rhs);
}
else
{
//easy unsigned case
if(lhs < rhs)
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
return (lhs - rhs);
}
}
template <typename U>
static SafeInt<T> MixedSizeModulus(SafeInt<T> lhs, U rhs)
{
//this is a simpler case than other arithmetic operations
//first, sign of return must be same as sign of lhs
//next, magnitude of return can never be larger than lhs
//always test this:
if(rhs == 0)
throw SafeIntException(EXCEPTION_INT_DIVIDE_BY_ZERO);
//problem cases are:
//either T or U 32 or 64-bit unsigned, other is signed
//signed value must be negative to create problem
//first problem case
//T unsigned 32-bit, U signed
if(sizeof(T) == 4 && !SafeInt<T>::IsSigned() &&
sizeof(U) <= 4 && SafeInt<U>::IsSigned())
{
if(rhs < 0)
return SafeInt<T>((_int64)lhs.Value() % (_int64)rhs);
}
//second problem case
//T signed <=32-bit, U unsigned 32-bit
if(sizeof(U) == 4 && !SafeInt<U>::IsSigned() &&
sizeof(T) <= 4 && SafeInt<T>::IsSigned())
{
if(lhs.Value() < 0)
return SafeInt<T>((_int64)lhs.Value() % (_int64)rhs);
}
//third problem case
//T unsigned 64-bit, U signed
if(sizeof(T) == 8 && !SafeInt<T>::IsSigned() &&
SafeInt<U>::IsSigned())
{
if(rhs < 0)
{
//return must be positive
return SafeInt<T>((T)lhs.Value() % (T)(-rhs));
}
//else it must be safe to cast U to T
return SafeInt<T>((T)lhs.Value() % (T)rhs);
}
//fourth problem case
//T signed, U unsigned 64-bit
if(sizeof(U) == 8 && !SafeInt<U>::IsSigned() &&
SafeInt<T>::IsSigned())
{
if(lhs.Value() < 0)
{
//first cast -lhs to U - must fit
//modulus operation returns type U, must fit into T (2nd cast to T)
//negation forces to int, re-cast to T
return SafeInt<T>((T)-(T)( ((U)(-lhs.Value())) % (U)rhs));
}
return SafeInt<T>((U)lhs.Value() % rhs);
}
//else no problem
return SafeInt<T>(lhs.Value() % rhs);
}
template <typename U>
static SafeInt<T> MixedSizeDivision(SafeInt<T> lhs, U rhs)
{
//first test corner cases
if(rhs == 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<T>::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
if(SafeInt<U>::IsSigned() == SafeInt<T>::IsSigned())
{
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -