/*---------------------------------------------------------------
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (C) 2003. Microsoft Corporation. All rights reserved.
SafeInt.hpp
This header implements an integer handling class designed to catch
unsafe integer operations
This header compiles properly at warning level 4.
Please read the leading comments before using the class.
Version 1.0.3
---------------------------------------------------------------*/
#ifndef SAFEINT_HPP
#define SAFEINT_HPP
#include <assert.h>
/*
* The SafeInt class is designed to have as low an overhead as possible
* while still ensuring that all integer operations are conducted safely.
* Nearly every operator has been overloaded, with a very few exceptions.
*
* A usability-safety trade-off has been made to help ensure safety. This
* requires that every operation return either a SafeInt or a bool. If we
* allowed an operator to return a base integer type T, then the following
* can happen:
*
* char i = SafeInt<char>(32) * 2 + SafeInt<char>(16) * 4;
*
* The * operators take precedence, get overloaded, return a char, and then
* you have:
*
* char i = (char)64 + (char)64; //overflow!
*
* This situation would mean that safety would depend on usage, which isn't
* acceptable. The problem that this leaves us with is that you'd like to be able
* to do something like:
*
* void* ptr = malloc(SafeInt<unsigned short>(23) * SafeInt<unsigned short>(HowMany));
*
* and have it be a safe operation. The way out of this is to use the following type of
* construct:
*
* SafeInt<int> s = 1, s1 = 2;
* int m = (s | s1).Value();
*
* A little clunky, and less programmer-friendly than would be ideal, but it is safe.
*
* One key operator that is missing is an implicit cast. The reason for
* this is that if there is an implicit cast operator, then we end up with
* an ambiguous compile-time precedence. Because of this amiguity, there
* are two methods that are provided:
*
* SafeInt::Value() - returns the value of the object as an integer
* SafeInt::Ptr() - returns the address of the internal integer
* Note - the '&' (address of) operator has been overloaded and returns
* the address of the internal integer.
*
* The SafeInt class should be used in any circumstances where ensuring
* integrity of the calculations is more important than performance. See Performance
* Notes below for additional information.
*
* Many of the conditionals will optimize out or be inlined for a release
* build (especially with /Ox), but it does have significantly more overhead,
* especially for signed numbers. If you do not _require_ negative numbers, use
* unsigned integer types - certain types of problems cannot occur, and this class
* performs most efficiently.
*
* Here's an example of when the class should ideally be used -
*
* void* AllocateMemForStructs(int StructSize, int HowMany)
* {
* SafeInt<unsigned long> s(StructSize);
*
* s *= HowMany;
*
* return malloc(s.Value());
*
* }
*
* Here's when it should NOT be used:
*
* void foo()
* {
* int i;
*
* for(i = 0; i < 0xffff; i++)
* ....
* }
*
* Error handling - a SafeInt class will throw exceptions if something
* objectionable happens. The exceptions are SafeIntException classes,
* which contain one signed long as a code (for compatability with Windows
* errors). The values that may be assigned currently are:
*
* ERROR_ARITHMETIC_OVERFLOW
* EXCEPTION_INT_DIVIDE_BY_ZERO
*
* Typical usage might be:
*
* bool foo()
* {
* SafeInt<unsigned long> s; //note that s == 0 unless set
*
* try{
* s *= 23;
* ....
* }
* catch(SafeIntException err)
* {
* //handle errors here
* }
* }
*
* Performance:
*
* Due to the highly nested nature of this class, you can expect relatively poor
* performance in unoptimized code. In tests of optimized code vs. correct inline checks
* in native code, this class has been found to take approximately 8% more CPU time,
* most of which is due to exception handling. Solutions:
*
* 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1
* (optimize for size) does not work as well.
* 2) If that 8% hit is really a serious problem, walk through the code and inline the
* exact same checks as the class uses.
* 3) Some operations are more difficult than others - avoid using signed integers, and if
* possible keep them all the same size. 64-bit integers are also expensive. Mixing
* different integer sizes and types may prove expensive. Be aware that literals are
* actually ints. For best performance, cast them to the type desired.
*
*
* Binary Operators
*
* All of the binary operators have certain assumptions built into the class design.
* This is to ensure correctness. Notes on each class of operator follow:
*
* Arithmetic Operators (*,/,+,-,%)
* There are three possible variants:
* SafeInt<T> op SafeInt<T>
* SafeInt<T> op U
* U op SafeInt<T>
*
* The SafeInt<T> op SafeInt<U> variant is explicitly not supported, and if you try to do
* this the compiler with throw the following error:
*
* error C2593: 'operator *' is ambiguous
*
* This is because the arithmetic operators are required to return a SafeInt of some type.
* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If
* you need to do this, you need to extract the value contained within one of the two using
* the SafeInt::Value() method. For example:
*
* SafeInt<T> t, result;
* SafeInt<U> u;
*
* result = t * u.Value();
*
* Comparison Operators
* Because each of these operators return type bool, mixing SafeInts of differing types is
* allowed.
*
* Shift Operators
* Shift operators always return the type on the left hand side of the operator. Mixed type
* operations are allowed because the return type is always known.
*
* Boolean Operators
* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts
* are allowed. Additionally, specific overloads exist for type bool on both sides of the
* operator.
*
* Binary Operators
* Mixed-type operations are discouraged, however some provision has been made in order to
* enable things like:
*
* SafeInt<char> c = 2;
*
* if(c & 0x02)
* ...
*
* The "0x02" is actually an int, and it needs to work. The rule is that if the non-SafeInt type
* can be cast to the type of the SafeInt, and back to the original type without losing any
* significant bits then the operation is allowed.
*
*
* Documented issues:
*
* This header compiles correctly at /W4 using VC++ 7.1 (Version 13.10.3077).
* It is strongly recommended that any code doing integer manipulation be compiled at /W4
* - there are a number of warnings which pertain to integer manipulation enabled that are
* not enabled at /W3 (default for VC++)
*
* Perf note - postfix operators are slightly more costly than prefix operators.
* Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++
*
* The comparison operator behavior in this class varies from the ANSI definition, which is
* arguably broken. As an example, consider the following:
*
* unsigned int l = 0xffffffff;
* char c = -1;
*
* if(c == l)
* printf("Why is -1 equal to 4 billion???\n");
*
* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets
* cast again to an unsigned int, losing the true value. This behavior is despite the fact that
* an _int64 exists, and the following code will yield a different (and intuitively correct)
* answer:
*
* if((_int64)c == (_int64)l))
* printf("Why is -1 equal to 4 billion???\n");
* else
* printf("Why doesn't the compiler upcast to 64-bits when needed?\n");
*
* Note that combinations with smaller integers won't display the problem - if you
* changed "unsigned int" above to "unsigned short", you'd get the right answer.
*
* 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
*
*
* Revision history:
*
* Oct 12, 2003 - Created
* Author - David LeBlanc - dleblanc@microsoft.com
*
* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson
* Dec 28, 2003 - 1.0
* added support for mixed-type operations
* thanks to vikramh
* also fixed broken _int64 multiplication section
* added extended support for mixed-type operations where possible
* Jan 28, 2004 - 1.0.1
* changed WCHAR to wchar_t
* fixed a construct in two mixed-type assignment overloads that was
* not compiling on some compilers
* Also changed name of private method to comply with standards on
* reserved names
* Thanks to Niels Dekker for the input
* Feb 12, 2004 - 1.0.2
* Minor changes to remove dependency on Windows headers
* Consistently used _int16, _int32 and _int64 to ensure
* portability
* May 10, 2003 - 1.0.3
* Corrected bug in one case of GreaterThan
*
*/
#pragma warning(push)
//this avoids warnings from the unary '-' operator being applied to unsigned numbers
//the overload itself resolves to nothing for the unsigned case
#pragma warning(disable:4146)
// conditional expression is constant - these are used intentionally
#pragma warning(disable:4127)
//use these if the compiler does not support _intXX
#ifdef NEEDS_INT_DEFINED
#define _int16 short
#define _int32 long
#define _int64 long long
#endif
/* catch these to handle errors
** Currently implemented code values:
** ERROR_ARITHMETIC_OVERFLOW
** EXCEPTION_INT_DIVIDE_BY_ZERO
*/
#ifndef ERROR_ARITHMETIC_OVERFLOW
#define ERROR_ARITHMETIC_OVERFLOW 534L
#endif
#ifndef EXCEPTION_INT_DIVIDE_BY_ZERO
#define EXCEPTION_INT_DIVIDE_BY_ZERO ((unsigned _int32)0xC0000094L)
#endif
class SafeIntException
{
public:
SafeIntException(){m_code = 0;}
SafeIntException(_int32 code)
{
m_code = code;
}
_int32 m_code;
};
template<typename T> class SafeInt
{
public:
SafeInt()
{
m_int = 0;
}
//Having a constructor for every type of int
//avoids having the compiler evade our checks when doing implicit casts -
//e.g., SafeInt<char> s = 0x7fffffff;
SafeInt(T i)
{
//always safe
m_int = i;
}
//provide explicit boolean converter
SafeInt(bool b)
{
m_int = b ? 1 : 0;
}
template <typename U> SafeInt(SafeInt<U> u)
{
*this = SafeInt<T>(u.Value());
}
template <typename U> SafeInt(U i)
{
//use signed-unsigned test on U
if(SafeInt<U>::IsSigned())
{
//U is signed
//whether T is signed or unsigned, must test range if sizeof T is smaller
//than sizeof U
//if sizeof(T) >= sizeof(U) this optimizes out, only test for negative
//for completely safe cases, optimizes to NOOP
if(sizeof(T) < sizeof(U))
{
//test size
if(i > (U)SafeInt<T>::MaxInt() || i < (U)SafeInt<T>::MinInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
else //test +/- for sizeof(T) >= sizeof(U) and T unsigned
if(!IsSigned())
{
if(i < 0)
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
}
else
{
//U is unsigned
//if sizeof T <= sizeof U AND T is signed,
//test upper bound because MaxInt(unsigned) > MaxInt(signed)
//OR
//if sizeof T < sizeof U and T is unsigned
if((IsSigned() && sizeof(T) <= sizeof(U)) ||
(!IsSigned() && sizeof(T) < sizeof(U)) )
{
if(i > (U)MaxInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
}
m_int = (T)i;
}
~SafeInt(){};
//helpful methods
//these compile down to something as efficient as macros and allow run-time testing
//of type by the developer
template <typename U> static bool IsSigned(SafeInt<U>)
{
return( (U)-1 < 0 );
}
template <typename U> static bool IsSigned(U)
{
return( (U)-1 < 0 );
}
static bool IsSigned()
{
return( (T)-1 < 0 );
}
static unsigned char BitCount(){return (sizeof(T)*8);}
template <typename U> static unsigned char BitCount(U){return (sizeof(U)*8);}
static bool Is64Bit(){return sizeof(T) == 8;}
static bool Is32Bit(){return sizeof(T) == 4;}
static bool Is16Bit(){return sizeof(T) == 2;}
static bool Is8Bit(){return sizeof(T) == 1;}
template <typename U> static bool Is64Bit(U){return sizeof(U) == 8;}
template <typename U> static bool Is32Bit(U){return sizeof(U) == 4;}
template <typename U> static bool Is16Bit(U){return sizeof(U) == 2;}
template <typename U> static bool Is8Bit(U){return sizeof(U) == 1;}
//both of the following should optimize away
static T MaxInt()
{
if(IsSigned())
{
return (T)~((T)1 << (BitCount()-1));
}
//else
return (T)(~(T)0);
}
static T MinInt()
{
if(IsSigned())
{
return (T)((T)1 << (BitCount()-1));
}
else
{
return ((T)0);
}
}
//now start overloading operators
//assignment operator
//constructors exist for all int types and will ensure safety
template <typename U> inline SafeInt<T>& operator =(U rhs)
{
//use constructor to test size
//constructor is optimized to do minimal checking based
//on whether T can contain U
*this = SafeInt<T>(rhs);
return *this;
}
inline SafeInt<T>& operator =(T rhs)
{
m_int = rhs;
return *this;
}
template <typename U> inline SafeInt<T>& operator =(SafeInt<U> rhs)
{
*this = SafeInt<T>(rhs.Value());
return *this;
}
inline SafeInt<T>& operator =(SafeInt<T> rhs)
{
m_int = rhs.m_int;
return *this;
}
//casting operator not implemented
//because it causes ambiguous compilation
//Use the methods below to gain access to the data
T Value() const {return m_int;}
//and if you need a pointer to the data
//this could be dangerous, but allows you to correctly pass
//instances of this class to APIs that take a pointer to an integer
//also see overloaded address-of operator below
T* Ptr(){return &m_int;}
const T* Ptr() const {return &m_int;}
//or if SafeInt<T>::Ptr() is inconvenient, use the overload
// operator &
//This allows you to do unsafe things!
//It is meant to allow you to more easily
//pass a SafeInt into things like ReadFile
T* operator &(){return &m_int;}
const T* operator &() const {return &m_int;}
//unary operators
bool operator !() const {return (!m_int) ? true : false;}
//operator + (unary)
//note - normally, the '+' and '-' operators will upcast to a signed int
//for T < 32 bits. This class changes behavior to preserve type
SafeInt<T> operator +(void) const { return *this; };
//unary -
SafeInt<T> operator -() const
{
if(IsSigned())
{
//corner case
if(m_int == MinInt())
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
//cast improves perf in the case of small ints
return SafeInt<T>((T)-m_int);
}
//no-op for unsigned - generates warning 4146 at warning levels 2 and above
return SafeInt<T>((T)-m_int);
}
//prefix increment operator
SafeInt<T>& operator ++()
{
if(m_int == MaxInt())
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
++m_int;
return *this;
}
//prefix decrement operator
SafeInt<T>& operator --()
{
if(m_int == MinInt())
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
--m_int;
return *this;
}
//postfix increment operator
SafeInt<T> operator ++(int) //dummy arg to comply with spec
{
if(m_int == MaxInt())
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
SafeInt<T> tmp = m_int;
m_int++;
return tmp;
}
//postfix decrement operator
SafeInt<T> operator --(int) //dummy arg to comply with spec
{
if(m_int == MinInt())
{
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
SafeInt<T> tmp = m_int;
m_int--;
return tmp;
}
//one's complement
//note - this operator will normally change size to an int
//cast in return improves perf and maintains type
SafeInt<T> operator ~() const {return SafeInt<T>((T)~m_int);}
//binary operators
//
// arithmetic binary operators
// % modulus
// * multiplication
// / division
// + addition
// - subtraction
//
// For each of the arithmetic operators, you will need to
// use them as follows:
//
// SafeInt<char> c = 2;
// SafeInt<int> i = 3;
//
// SafeInt<int> i2 = i op c.Value();
// OR
// SafeInt<char> i2 = i.Value() op c;
//
// The base problem is that if the lhs and rhs inputs are different SafeInt types
// it is not possible in this implementation to determine what type of SafeInt
// should be returned. You have to let the class know which of the two inputs
// need to be the return type by forcing the other value to the base integer type.
// The case of:
//
// SafeInt<T> i, j, k;
// i = j op k;
//
// works just fine and no unboxing is needed because the return type is not ambiguous.
//modulus
//modulus has some convenient properties -
//first, the magnitude of the return can never be
//larger than the lhs operand, and it must be the same sign
//as well. It does, however, suffer from the same promotion
//problems as comparisons, division and other operations
template <typename U>
SafeInt<T> operator %(U rhs)
{
return MixedSizeModulus(*this, rhs);
}
SafeInt<T> operator %(SafeInt<T> rhs)
{
if(rhs.Value() == 0)
throw SafeIntException(EXCEPTION_INT_DIVIDE_BY_ZERO);
//this is always safe
return SafeInt<T>((T)(m_int % rhs.Value()));
}
//modulus assignment
template <typename U>
SafeInt<T>& operator %=(U rhs)
{
*this = MixedSizeModulus(*this, rhs);
return *this;
}
template <typename U>
SafeInt<T>& operator %=(SafeInt<U> rhs)
{
*this = MixedSizeModulus(*this, rhs.Value());
return *this;
}
//multiplication
template <typename U>
SafeInt<T> operator *(U rhs)
{
return MixedSizeMultiply(*this, rhs);
}
SafeInt<T> operator *(SafeInt<T> rhs)
{
return SafeInt<T>(multiply(m_int, rhs.Value()));
}
//multiplication assignment
SafeInt<T>& operator *=(SafeInt<T> rhs)
{
m_int = multiply(rhs.m_int, m_int);
return *this;
}
template <typename U>
SafeInt<T>& operator *=(U rhs)
{
*this = MixedSizeMultiply(*this, rhs);
return *this;
}
template <typename U>
SafeInt<T>& operator *=(SafeInt<U> rhs)
{
*this = MixedSizeMultiply(*this, rhs.Value());
return *this;
}
//division
template <typename U>
SafeInt<T> operator /(U rhs)
{
return MixedSizeDivision(*this, rhs);
}
SafeInt<T> operator /(SafeInt<T> rhs)
{
return MixedSizeDivision(*this, rhs.Value());
}
//division assignment
SafeInt<T>& operator /=(SafeInt<T> i)
{
*this = MixedSizeDivision(*this, i.Value());
return *this;
}
template <typename U> SafeInt<T>& operator /=(U i)
{
*this = MixedSizeDivision(*this, i);
return *this;
}
template <typename U> SafeInt<T>& operator /=(SafeInt<U> i)
{
*this = MixedSizeDivision(*this, i.Value());
return *this;
}
//for addition and subtraction
//addition
inline SafeInt<T> operator +(SafeInt<T> rhs)
{
return SafeInt<T>(addition(m_int, rhs.Value()));
}
template <typename U>
inline SafeInt<T> operator +(U rhs)
{
return MixedSizeAddition(*this, rhs);
}
//addition assignment
SafeInt<T>& operator +=(SafeInt<T> rhs)
{
m_int = addition(m_int, rhs.m_int);
return *this;
}
template <typename U>
SafeInt<T>& operator +=(U rhs)
{
*this = MixedSizeAddition(*this, rhs);
return *this;
}
template <typename U>
SafeInt<T>& operator +=(SafeInt<U> rhs)
{
*this = MixedSizeAddition(*this, rhs.Value());
return *this;
}
//subtraction
template <typename U>
SafeInt<T> operator -(U rhs)
{
return MixedSizeSubtraction(*this, rhs);
}
SafeInt<T> operator -(SafeInt<T> rhs)
{
return SafeInt<T>(subtraction(m_int, rhs.m_int));
}
//subtraction assignment
SafeInt<T>& operator -=(SafeInt<T> rhs)
{
m_int = subtraction(m_int, rhs.m_int);
return *this;
}
template <typename U>
SafeInt<T>& operator -=(U rhs)
{
*this = MixedSizeSubtraction(*this, rhs);
return *this;
}
template <typename U>
SafeInt<T>& operator -=(SafeInt<U> rhs)
{
*this = MixedSizeSubtraction(*this, rhs.Value());
return *this;
}
//comparison operators
//additional overloads defined outside the class at the bottom of
//the header to allow for cases where the SafeInt is the rhs value
// less than
template <typename U>
bool operator <(U rhs)
{
return LessThan(m_int, rhs);
}
bool operator <(SafeInt<T> rhs)
{
return m_int < rhs.m_int;
}
//greater than or eq.
template <typename U>
bool operator >=(U rhs){return !(*this < rhs);}
bool operator >=(SafeInt<T> rhs)
{
return m_int >= rhs.Value();
}
// greater than
template <typename U>
bool operator >(U rhs)
{
return SafeInt<T>::GreaterThan(m_int, rhs);
}
bool operator >(SafeInt<T> rhs)
{
return m_int > rhs.m_int;
}
//less than or eq.
template <typename U>
bool operator <=(U rhs){return !(*this > rhs);}
//same type - easy path
bool operator <=(SafeInt<T> rhs)
{
return m_int <= rhs.Value();
}
//equality
template <typename U>
bool operator ==(U rhs){return Equals(m_int, rhs);}
//need an explicit override for type bool
bool operator ==(bool rhs)
{
return (m_int == 0 ? false : true) == rhs;
}
bool operator ==(SafeInt<T> rhs){return m_int == rhs.Value();}
//!= operators
template <typename U>
bool operator !=(U rhs){return !Equals(m_int, rhs);}
bool operator !=(bool b)
{
return (m_int == 0 ? false : true) != b;
}
bool operator !=(SafeInt<T> rhs){return m_int != rhs.Value();}
//shift operators
//Note - shift operators ALWAYS return the same type as the lhs
//specific version for SafeInt<T> not needed -
//code path is exactly the same as for SafeInt<U> as rhs
//left shift
//Also, shifting > bitcount is undefined - trap in debug
template <typename U>
SafeInt<T> operator <<(U bits)
{
if(IsSigned(bits))
assert(bits >= 0);
assert(bits < BitCount());
return SafeInt<T>((T)(m_int << bits));
}
template <typename U>
SafeInt<T> operator <<(SafeInt<U> bits)
{
if(IsSigned(bits))
assert(bits >= 0);
assert(bits < BitCount());
return SafeInt<T>((T)(m_int << bits.Value()));
}
//left shift assignment
template <typename U>
SafeInt<T>& operator <<=(U bits)
{
if(IsSigned(bits))
assert(bits >= 0);
assert(bits < BitCount());
m_int <<= bits;
return *this;
}
template <typename U>
SafeInt<T>& operator <<=(SafeInt<U> bits)
{
if(IsSigned(bits))
assert(bits.Value() >= 0);
assert(bits.Value() < BitCount());
m_int <<= bits.Value();
return *this;
}
//right shift
template <typename U>
SafeInt<T> operator >>(U bits)
{
if(IsSigned(bits))
assert(bits >= 0);
assert(bits < BitCount());
return SafeInt<T>((T)(m_int >> bits));
}
template <typename U>
SafeInt<T> operator >>(SafeInt<U> bits)
{
if(IsSigned(bits))
assert(bits >= 0);
assert(bits < BitCount());
return SafeInt<T>((T)(m_int >> bits.Value()));
}
//right shift assignment
template <typename U>
SafeInt<T>& operator >>=(U bits)
{
if(IsSigned(bits))
assert(bits >= 0);
assert(bits < BitCount());
m_int >>= bits;
return *this;
}
template <typename U>
SafeInt<T>& operator >>=(SafeInt<U> bits)
{
if(IsSigned(bits))
assert(bits.Value() >= 0);
assert(bits.Value() < BitCount());
m_int >>= bits.Value();
return *this;
}
//bitwise operators
//this only makes sense if we're dealing with the same type and size
//demand a type T, or something that fits into a type T
//bitwise &
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);
//might still be safe
//cast rhs down to a T, then back up to U
//check to see if it is equal to the original value
//this allows things like
//SafeInt<char>(2) & 4 (literal is an int) to work
if( (U)((T)rhs) == rhs)
return SafeInt<T>(m_int & (T)rhs);
throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
}
//bitwise & assignment
SafeInt<T>& operator &=(SafeInt<T> rhs)
{
m_int &= rhs.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;
}
//XOR
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);
}
//XOR 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;
}
//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())
{
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());
}
}
//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()));
}
//bitwise operators
//this only makes sense if we're dealing with the same type and size
//demand a type T, or something that fits into a type T
//bitwise &
template <typename T, typename U>
SafeInt<T> operator &(U lhs, SafeInt<T> rhs)
{
//if U can fit into T without truncating, force U to T
return SafeInt<T>(SafeInt<T>(lhs) & rhs.Value());
}
//bitwise XOR
template <typename T, typename U>
SafeInt<T> operator ^(U lhs, SafeInt<T> rhs)
{
return SafeInt<T>(SafeInt<T>(lhs) ^ rhs.Value());
}
//bitwise OR
template <typename T, typename U>
SafeInt<T> operator |(U lhs, SafeInt<T> rhs)
{
return SafeInt<T>(SafeInt<T>(lhs) | rhs.Value());
}
//logical operators
//logical OR
template <typename T>
bool operator ||(bool lhs, SafeInt<T> rhs)
{
return lhs || rhs.Value();
}
template <typename T, typename U>
bool operator ||(U lhs, SafeInt<T> rhs)
{
return lhs || rhs.Value();
}
template <typename T, typename U>
bool operator ||(SafeInt<U> lhs, SafeInt<T> rhs)
{
return lhs.Value() || rhs.Value();
}
//logical AND
template <typename T>
bool operator &&(bool lhs, SafeInt<T> rhs)
{
return lhs && rhs.Value();
}
template <typename T, typename U>
bool operator &&(U lhs, SafeInt<T> rhs)
{
return lhs && rhs.Value();
}
template <typename T, typename U>
bool operator &&(SafeInt<U> lhs, SafeInt<T> rhs)
{
return lhs.Value() && rhs.Value();
}
#pragma warning(pop)
#endif //SAFEINT_HPP