📄 safeint.hpp
字号:
if(sizeof(T) >= sizeof(U))
return SafeInt<T>((T)((T)lhs.Value()/(T)rhs));
return SafeInt<T>((T)((U)lhs.Value()/(U)rhs));
}
//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 - rhs always fits into T
return SafeInt<T>((T)(lhs.Value()/(T)rhs));
}
if(sizeof(U) < 4 && sizeof(T) < 4)
{
//even if U is bigger, upcast to int
return SafeInt<T>((T)((_int32)lhs.Value()/(_int32)rhs));
}
//now U is either 32 or 64-bit, T same size or smaller
if(sizeof(U) == 4)
{
//upcast to 64-bit
return SafeInt<T>((T)((_int64)lhs.Value()/(_int64)rhs));
}
//U is unsigned _int64
//now it matters whether lhs < 0
if(lhs.Value() < 0)
{
if(rhs > (U)SafeInt<T>::MaxInt() + 1)
return SafeInt<T>(0);
//corner case
if(lhs.Value() == SafeInt<T>::MinInt() &&
rhs == (U)SafeInt<T>::MaxInt() + 1)
{
return SafeInt<T>((T)-1);
}
//finally, rhs fits into T - just cast
return SafeInt<T>((T)(lhs.Value()/(T)rhs));
}
//lhs >= 0
//T now has to fit into U
return SafeInt<T>((T)((U)lhs.Value()/(U)rhs));
}
//now lhs is unsigned, rhs signed
if(rhs < 0)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
if(sizeof(T) >= sizeof(U))
{
//all U fits into T
return SafeInt<T>((T)(lhs.Value()/(T)rhs));
}
//now sizeof(U) > sizeof(T) and rhs > 0
//all T fits into U
return SafeInt<T>((T)((U)lhs.Value()/rhs));
}
template <typename U>
static SafeInt<T> MixedSizeAddition(SafeInt<T> lhs, U rhs)
{
if(SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
{
//easy case - just upcast U to T
if(sizeof(T) >= sizeof(U))
{
return SafeInt<T>(addition(lhs.Value(), (T)rhs));
}
//otherwise, U > T
//where possible, do the upcast inline
//this avoids range checks inside the addition call
//and additional range checks on assignment
if(sizeof(U) == 2)
{
//an int can hold any possible range of _int16 + char
//do bounds checking in constructor
if(IsSigned())
return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
//else unsigned
return SafeInt<T>((unsigned _int32)lhs.Value() + (unsigned _int32)rhs);
}
if(sizeof(U) == 4)
{
//it is possibly overkill to go to 64-bit, but other alternatives
//involve more conditionals. This is likely cheaper overall
if(IsSigned())
return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
return SafeInt<T>((unsigned _int64)lhs.Value() + (unsigned _int64)rhs);
}
//else U is an _int64
//this will have to be done the expensive way
{
return SafeInt<T>(SafeInt<U>(lhs.Value()) + rhs); //more checking here
}
}
//else mixed sign
//first consider the case of signed T, unsigned U
if(SafeInt<T>::IsSigned())
{
if(sizeof(T) > sizeof(U))
{
//piece of cake, U fits in T
return SafeInt<T>(addition(lhs.Value(), (T)rhs));
}
//else sizeof(T) <= sizeof(U)
if(sizeof(U) < 4)
{
//upcast to int, which is what the compiler normally does
//catch overflows in the constructor
return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
}
if(sizeof(U) == 4)
{
return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
}
//else U is unsigned 64-bit
//this will have to be done the expensive way
//this particular part of the problem is especially
//difficult - the result has to fit correctly into a signed int[8|16|32]
//lhs input could be negative
//rhs input greater than an _int64 MAX_INT could be legal
if(sizeof(T) < 4)
{
//test if rhs is even possibly legal
if((unsigned _int64)rhs > (unsigned _int64)SafeInt<unsigned _int16>::MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//stuff them all into an int, return
return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
}
else
if(sizeof(T) == 4)
{
//you can't possibly add more than 0xFFFFFFFF
//to a signed int and have it work, so
if((unsigned _int64)rhs > (unsigned _int64)SafeInt<unsigned _int32>::MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//now that we know that rhs fits into an unsigned _int32
//result must fit into an _int64
return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
}
else
{
//T == signed _int64
if(lhs.Value() >= 0)
{
//they both need to fit into an _int64
//given that lhs is positive, rhs must be <= _int64 MaxInt
if((unsigned _int64)rhs > (unsigned _int64)SafeInt<_int64>::MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
return SafeInt<T>(addition((T)lhs.Value(), (T)rhs));
}
//else rhs could range all the way up to a unsigned _int64 MAX_INT
//rearrange as rhs - (-lhs)
//this still works even if lhs == _int64 MIN_INT
return SafeInt<T>(rhs - (unsigned _int64)(-(lhs.Value())));
}
}
//now we have the case of unsigned T, signed U
//we just solved this problem above - since A+B == B+A
//code is largely duplicated, but there are optimizations
//remember that U could be negative
if(sizeof(T) < 4 && sizeof(U) < 4)
{
//upcast to int, which is what the compiler normally does
//catch overflows in the constructor
return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
}
if(sizeof(T) < 8 && sizeof(U) < 8)
{
//either T or U are 32-bit
//all possible combinations fit into an _int64
return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
}
if(sizeof(U) == 8 && sizeof(T) < 8)
{
//all possible values of T fit into an _int64
SafeInt<U> u(rhs);
u += (_int64)lhs.Value();
return SafeInt<T>(u.Value());
}
//else T is unsigned 64-bit
//this will have to be done the expensive way
//rhs input could be negative
if(rhs >= 0)
{
//all possible values of rhs can fit into an unsigned _int64
return SafeInt<T>(addition(lhs.Value(), (T)rhs));
}
//rhs is negative
if((T)(-rhs) > lhs.Value()) //this will never work
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
return SafeInt<T>(lhs.Value() - (T)(-rhs));
}
template <typename U>
static SafeInt<T> MixedSizeSubtraction(SafeInt<T> lhs, U rhs)
{
if(SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
{
//easy case - just upcast U to T
if(sizeof(T) >= sizeof(U))
return SafeInt<T>(subtraction(lhs.Value(), (T)rhs));
//otherwise, U > T
if(sizeof(U) == 2)
{
//an int can hold any possible range of _int16 - char
//do bounds checking in constructor
if(IsSigned())
return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
//else unsigned
return SafeInt<T>((unsigned _int32)lhs.Value() - (unsigned _int32)rhs);
}
if(sizeof(U) == 4)
{
//it is possibly overkill to go to 64-bit, but other alternatives
//involve more conditionals. This is likely cheaper overall
if(IsSigned())
return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
return SafeInt<T>((unsigned _int64)lhs.Value() - (unsigned _int64)rhs);
}
//else U is a signed or unsigned _int64
//this will have to be done the expensive way
{
return SafeInt<T>(SafeInt<U>(lhs.Value()) - rhs); //more checking here
}
}
//else mixed sign
//first consider the case of signed T, unsigned U
if(IsSigned())
{
if(sizeof(T) > sizeof(U))
{
//piece of cake, U fits in T
return SafeInt<T>(subtraction(lhs.Value(), (T)rhs));
}
//else sizeof(T) <= sizeof(U)
if(sizeof(U) < 4)
{
//upcast to int, which is what the compiler normally does
//catch overflows in the constructor
return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
}
if(sizeof(U) == 4)
{
//upcast to _int64, which is what the compiler should do
return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
}
//else U is unsigned 64-bit
//this will have to be done the expensive way
//this particular part of the problem is especially
//difficult - the result has to fit correctly into a signed int[8|16|32]
//lhs input could be negative, rhs input is always positive
if(sizeof(T) < 4)
{
//test if rhs is even possibly legal
if(rhs > (U)SafeInt<unsigned _int16>::MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//stuff them all into an int, return
return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
}
else
if(sizeof(T) == 4)
{
//you can't possibly subtract more than 0xFFFFFFFF
//from a signed int and have it work, so
if(rhs > (U)SafeInt<unsigned _int32>::MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
}
else
{
//T = signed _int64
//U = unsigned _int64
//need the size of the range between lhs.MinInt and lhs
//this is the maximum value that can be subtracted from lhs
//we're actually going to take advantage of rollover
if(rhs > (U)(lhs.Value() - SafeInt<T>::MinInt()))
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//else it has to work
return SafeInt<T>((T)(lhs.Value() - rhs));
}
}
//now we have the case of unsigned T, signed U
//easily deals with 1/4 of the problem space
if(sizeof(T) < 4 && sizeof(U) < 4)
{
//stuff them all into an int, return
return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
}
else
if(sizeof(T) < 8 && sizeof(U) < 8)
{
//handles another 5 of 16 cases
return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
}
//case of T = unsigned _int64
//handles 4 more cases
if(sizeof(T) == 8)
{
//has to work - rhs has to fit into a T
if(rhs >= 0)
return SafeInt<T>(subtraction(lhs.Value(), (T)rhs));
//rhs negative - turn into an addition
//take care to do an intermediate cast because the
//unary negation operator returns an int
//corner case of MinInt still works
return SafeInt<T>(addition(lhs.Value(), (T)((U)-rhs)));
}
//sizeof T < 8, sizeof U == 8
//maximum possible range for rhs is lhs - MinInt(T)
//because sizeof T smaller than _int64, upcast
if(rhs > (U)((_int64)lhs.Value() - (_int64)lhs.MinInt()))
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
}
template <typename U>
static SafeInt<T> MixedSizeMultiply(SafeInt<T> lhs, U rhs)
{
//what is U?
//if T is unsigned, and sizeof(T) >= sizeof(U)
//T can hold all values of U for rhs > 0
//if T is unsigned and sizeof(T) < sizeof(U)
//declare an unsigned SafeInt of same size as U
//if T is signed and sizeof(T) > sizeof(U)
//T can hold all values of U
//if T is signed and sizeof(T) <= sizeof(U)
//declare a signed SafeInt of same size as U
if(SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
{
//simple case - same signedness and U always fits in T
if(sizeof(T) >= sizeof(U))
{
return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
}
//simple case - same signedness and U bigger than T
//looks like a lot of code, but is compile-time constants
//must handle signed and unsigned in different cases - unless sizeof(U) == 8
if(sizeof(U) == 8)
{
SafeInt<U> u(rhs);
u *= lhs.m_int;
return SafeInt<T>(u.Value());
}
if(SafeInt<T>::IsSigned())
{
if(sizeof(U) < 4)
{
//the result must always fit into an int
return SafeInt<T>((_int32)((_int32)lhs.m_int * (_int32)rhs));
}
//result must fit into an _int64
return SafeInt<T>((_int64)((_int64)lhs.m_int * (_int64)rhs));
}
//else unsigned
if(sizeof(U) < 4)
{
//the result must always fit into an int
return SafeInt<T>((unsigned _int32)((unsigned _int32)lhs.m_int * (unsigned _int32)rhs));
}
//result must fit into an _int64
return SafeInt<T>((unsigned _int64)((unsigned _int64)lhs.m_int * (unsigned _int64)rhs));
}
//mixed sign - consider T is signed, U unsigned
if(SafeInt<T>::IsSigned() && !SafeInt<U>::IsSigned())
{
//if T > U, we're OK
if(sizeof(T) > sizeof(U))
{
return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
}
//else sizeof(T) <= sizeof(U) - upcast T to signed U
//otherwise, we have to make an signed next size up from U
if(sizeof(U) < 4)
{
//T is a signed char or _int16, U is unsigned _int16 or char
//this must also always fit into an int
return SafeInt<T>((_int32)((_int32)lhs.m_int * (_int32)rhs));
}
else if(sizeof(U) == 4)
{
//T is signed int or smaller, U is unsigned int
//result must fit into an _int64
return SafeInt<T>((_int64)((_int64)lhs.m_int * (_int64)rhs));
}
else
{
//U is unsigned 64-bit
//now if rhs > MaxInt(T), overflow unless lhs == 0
//or the corner case
if(rhs > (U)SafeInt<T>::MaxInt())
{
//corner case -1 * (MaxInt + 1) = MinInt
//do lhs comparison first, since is cheaper than 64-bit test
if(lhs.Value() == -1 && ((U)SafeInt<T>::MaxInt()+1) == rhs)
return SafeInt<T>(SafeInt<T>::MinInt());
if(lhs.Value() == 0)
return SafeInt<T>(0);
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//now rhs must fit into T
return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
}
}
//now mixed sign where T is unsigned, U signed
//negative numbers are always bad
//test here to avoid having to test in constructors below
//also allows some simplifying assumptions
if(rhs < 0)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
if(sizeof(T) >= sizeof(U))
{
return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
}
//U > T
//else there is no corner case, but we do have to check overflow
if(sizeof(U) < 4)
{
//both U and T can be contained in an unsigned int
return SafeInt<T>((unsigned _int32)((unsigned _int32)lhs.m_int * (unsigned _int32)rhs));
}
else if(sizeof(U) == 4)
{
//now go up to 64-bit
return SafeInt<T>((unsigned _int64)((unsigned _int64)lhs.m_int * (unsigned _int64)rhs));
}
else
{
//U = signed _int64, T = unsigned [char|_int16|int]
SafeInt<U> u(rhs);
u *= lhs.Value();
//now bounds check
if(u.Value() > (U)SafeInt<T>::MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//it has to be safe, since rhs is non-negative
return SafeInt<T>((T)u.Value());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -