/*--------------------------------------------------------------- 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 /* * 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(32) * 2 + SafeInt(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(23) * SafeInt(HowMany)); * * and have it be a safe operation. The way out of this is to use the following type of * construct: * * SafeInt 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 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 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 op SafeInt * SafeInt op U * U op SafeInt * * The SafeInt op SafeInt 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, result; * SafeInt 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 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 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 s = 0x7fffffff; SafeInt(T i) { //always safe m_int = i; } //provide explicit boolean converter SafeInt(bool b) { m_int = b ? 1 : 0; } template SafeInt(SafeInt u) { *this = SafeInt(u.Value()); } template SafeInt(U i) { //use signed-unsigned test on U if(SafeInt::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::MaxInt() || i < (U)SafeInt::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 static bool IsSigned(SafeInt) { return( (U)-1 < 0 ); } template static bool IsSigned(U) { return( (U)-1 < 0 ); } static bool IsSigned() { return( (T)-1 < 0 ); } static unsigned char BitCount(){return (sizeof(T)*8);} template 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 static bool Is64Bit(U){return sizeof(U) == 8;} template static bool Is32Bit(U){return sizeof(U) == 4;} template static bool Is16Bit(U){return sizeof(U) == 2;} template 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 inline SafeInt& operator =(U rhs) { //use constructor to test size //constructor is optimized to do minimal checking based //on whether T can contain U *this = SafeInt(rhs); return *this; } inline SafeInt& operator =(T rhs) { m_int = rhs; return *this; } template inline SafeInt& operator =(SafeInt rhs) { *this = SafeInt(rhs.Value()); return *this; } inline SafeInt& operator =(SafeInt 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::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 operator +(void) const { return *this; }; //unary - SafeInt 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)-m_int); } //no-op for unsigned - generates warning 4146 at warning levels 2 and above return SafeInt((T)-m_int); } //prefix increment operator SafeInt& operator ++() { if(m_int == MaxInt()) { throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } ++m_int; return *this; } //prefix decrement operator SafeInt& operator --() { if(m_int == MinInt()) { throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } --m_int; return *this; } //postfix increment operator SafeInt operator ++(int) //dummy arg to comply with spec { if(m_int == MaxInt()) { throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } SafeInt tmp = m_int; m_int++; return tmp; } //postfix decrement operator SafeInt operator --(int) //dummy arg to comply with spec { if(m_int == MinInt()) { throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } SafeInt 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 operator ~() const {return SafeInt((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 c = 2; // SafeInt i = 3; // // SafeInt i2 = i op c.Value(); // OR // SafeInt 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 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 SafeInt operator %(U rhs) { return MixedSizeModulus(*this, rhs); } SafeInt operator %(SafeInt rhs) { if(rhs.Value() == 0) throw SafeIntException(EXCEPTION_INT_DIVIDE_BY_ZERO); //this is always safe return SafeInt((T)(m_int % rhs.Value())); } //modulus assignment template SafeInt& operator %=(U rhs) { *this = MixedSizeModulus(*this, rhs); return *this; } template SafeInt& operator %=(SafeInt rhs) { *this = MixedSizeModulus(*this, rhs.Value()); return *this; } //multiplication template SafeInt operator *(U rhs) { return MixedSizeMultiply(*this, rhs); } SafeInt operator *(SafeInt rhs) { return SafeInt(multiply(m_int, rhs.Value())); } //multiplication assignment SafeInt& operator *=(SafeInt rhs) { m_int = multiply(rhs.m_int, m_int); return *this; } template SafeInt& operator *=(U rhs) { *this = MixedSizeMultiply(*this, rhs); return *this; } template SafeInt& operator *=(SafeInt rhs) { *this = MixedSizeMultiply(*this, rhs.Value()); return *this; } //division template SafeInt operator /(U rhs) { return MixedSizeDivision(*this, rhs); } SafeInt operator /(SafeInt rhs) { return MixedSizeDivision(*this, rhs.Value()); } //division assignment SafeInt& operator /=(SafeInt i) { *this = MixedSizeDivision(*this, i.Value()); return *this; } template SafeInt& operator /=(U i) { *this = MixedSizeDivision(*this, i); return *this; } template SafeInt& operator /=(SafeInt i) { *this = MixedSizeDivision(*this, i.Value()); return *this; } //for addition and subtraction //addition inline SafeInt operator +(SafeInt rhs) { return SafeInt(addition(m_int, rhs.Value())); } template inline SafeInt operator +(U rhs) { return MixedSizeAddition(*this, rhs); } //addition assignment SafeInt& operator +=(SafeInt rhs) { m_int = addition(m_int, rhs.m_int); return *this; } template SafeInt& operator +=(U rhs) { *this = MixedSizeAddition(*this, rhs); return *this; } template SafeInt& operator +=(SafeInt rhs) { *this = MixedSizeAddition(*this, rhs.Value()); return *this; } //subtraction template SafeInt operator -(U rhs) { return MixedSizeSubtraction(*this, rhs); } SafeInt operator -(SafeInt rhs) { return SafeInt(subtraction(m_int, rhs.m_int)); } //subtraction assignment SafeInt& operator -=(SafeInt rhs) { m_int = subtraction(m_int, rhs.m_int); return *this; } template SafeInt& operator -=(U rhs) { *this = MixedSizeSubtraction(*this, rhs); return *this; } template SafeInt& operator -=(SafeInt 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 bool operator <(U rhs) { return LessThan(m_int, rhs); } bool operator <(SafeInt rhs) { return m_int < rhs.m_int; } //greater than or eq. template bool operator >=(U rhs){return !(*this < rhs);} bool operator >=(SafeInt rhs) { return m_int >= rhs.Value(); } // greater than template bool operator >(U rhs) { return SafeInt::GreaterThan(m_int, rhs); } bool operator >(SafeInt rhs) { return m_int > rhs.m_int; } //less than or eq. template bool operator <=(U rhs){return !(*this > rhs);} //same type - easy path bool operator <=(SafeInt rhs) { return m_int <= rhs.Value(); } //equality template 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 rhs){return m_int == rhs.Value();} //!= operators template bool operator !=(U rhs){return !Equals(m_int, rhs);} bool operator !=(bool b) { return (m_int == 0 ? false : true) != b; } bool operator !=(SafeInt rhs){return m_int != rhs.Value();} //shift operators //Note - shift operators ALWAYS return the same type as the lhs //specific version for SafeInt not needed - //code path is exactly the same as for SafeInt as rhs //left shift //Also, shifting > bitcount is undefined - trap in debug template SafeInt operator <<(U bits) { if(IsSigned(bits)) assert(bits >= 0); assert(bits < BitCount()); return SafeInt((T)(m_int << bits)); } template SafeInt operator <<(SafeInt bits) { if(IsSigned(bits)) assert(bits >= 0); assert(bits < BitCount()); return SafeInt((T)(m_int << bits.Value())); } //left shift assignment template SafeInt& operator <<=(U bits) { if(IsSigned(bits)) assert(bits >= 0); assert(bits < BitCount()); m_int <<= bits; return *this; } template SafeInt& operator <<=(SafeInt bits) { if(IsSigned(bits)) assert(bits.Value() >= 0); assert(bits.Value() < BitCount()); m_int <<= bits.Value(); return *this; } //right shift template SafeInt operator >>(U bits) { if(IsSigned(bits)) assert(bits >= 0); assert(bits < BitCount()); return SafeInt((T)(m_int >> bits)); } template SafeInt operator >>(SafeInt bits) { if(IsSigned(bits)) assert(bits >= 0); assert(bits < BitCount()); return SafeInt((T)(m_int >> bits.Value())); } //right shift assignment template SafeInt& operator >>=(U bits) { if(IsSigned(bits)) assert(bits >= 0); assert(bits < BitCount()); m_int >>= bits; return *this; } template SafeInt& operator >>=(SafeInt 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 operator &(SafeInt rhs) { return SafeInt((T)(m_int & rhs.m_int)); } template SafeInt operator &(U rhs) { //if U can fit into T without truncating, force U to T if(sizeof(U) <= sizeof(T)) return SafeInt(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(2) & 4 (literal is an int) to work if( (U)((T)rhs) == rhs) return SafeInt(m_int & (T)rhs); throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } //bitwise & assignment SafeInt& operator &=(SafeInt rhs) { m_int &= rhs.m_int; return *this; } template SafeInt& operator &=(U rhs) { *this = *this & rhs; return *this; } template SafeInt& operator &=(SafeInt rhs) { *this = *this & rhs.Value(); return *this; } //XOR SafeInt operator ^(SafeInt rhs) { return SafeInt((T)(m_int ^ rhs.m_int)); } template SafeInt operator ^(U rhs) { //if U can fit into T without truncating, force U to T if(sizeof(U) <= sizeof(T)) return SafeInt(m_int ^ (T)rhs); if( (U)((T)rhs) == rhs) return SafeInt(m_int ^ (T)rhs); throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } //XOR assignment SafeInt& operator ^=(SafeInt i) { m_int ^= i.m_int; return *this; } template SafeInt& operator ^=(U rhs) { *this = *this ^ rhs; return *this; } template SafeInt& operator ^=(SafeInt rhs) { *this = *this ^ rhs.Value(); return *this; } //bitwise OR SafeInt operator |(SafeInt rhs) { return SafeInt((T)(m_int | rhs.m_int)); } template SafeInt operator |(U rhs) { //if U can fit into T without truncating, force U to T if(sizeof(U) <= sizeof(T)) return SafeInt(m_int | (T)rhs); if( (U)((T)rhs) == rhs) return SafeInt(m_int | (T)rhs); throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } //bitwise OR assignment SafeInt& operator |=(SafeInt i) { m_int |= i.m_int; return *this; } template SafeInt& operator |=(U rhs) { *this = *this | rhs; return *this; } template SafeInt& operator |=(SafeInt 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, SafeInt - internal // SafeInt, U - internal // SafeInt, bool - internal // bool, SafeInt - external // U, SafeInt - external // SafeInt, SafeInt - external //logical OR bool operator ||(SafeInt rhs) { return m_int || rhs.Value(); } template bool operator ||(U rhs) { return m_int || rhs; } bool operator ||(bool rhs) { return m_int || rhs; } //logical && bool operator &&(SafeInt rhs) { return m_int && rhs.Value(); } template bool operator &&(U rhs) { return m_int && rhs; } bool operator &&(bool rhs) { return m_int && rhs; } //miscellaneous helper functions SafeInt Min(SafeInt test, SafeInt floor = SafeInt::MinInt()) const { T tmp = test.Value() < m_int ? test.Value() : m_int; return tmp < floor ? floor : tmp; } SafeInt Max(SafeInt test, SafeInt upper = SafeInt::MaxInt()) const { T tmp = test.Value() > m_int ? test.Value() : m_int; return tmp > upper ? upper : tmp; } void Swap( SafeInt& with ) { T temp( m_int ); m_int = with.m_int; with.m_int = temp; } static SafeInt SafeAtoI(const char* input) { return SafeTtoI(input); } static SafeInt 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 static SafeInt MixedSizeModulus(SafeInt 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::IsSigned() && sizeof(U) <= 4 && SafeInt::IsSigned()) { if(rhs < 0) return SafeInt((_int64)lhs.Value() % (_int64)rhs); } //second problem case //T signed <=32-bit, U unsigned 32-bit if(sizeof(U) == 4 && !SafeInt::IsSigned() && sizeof(T) <= 4 && SafeInt::IsSigned()) { if(lhs.Value() < 0) return SafeInt((_int64)lhs.Value() % (_int64)rhs); } //third problem case //T unsigned 64-bit, U signed if(sizeof(T) == 8 && !SafeInt::IsSigned() && SafeInt::IsSigned()) { if(rhs < 0) { //return must be positive return SafeInt((T)lhs.Value() % (T)(-rhs)); } //else it must be safe to cast U to T return SafeInt((T)lhs.Value() % (T)rhs); } //fourth problem case //T signed, U unsigned 64-bit if(sizeof(U) == 8 && !SafeInt::IsSigned() && SafeInt::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)( ((U)(-lhs.Value())) % (U)rhs)); } return SafeInt((U)lhs.Value() % rhs); } //else no problem return SafeInt(lhs.Value() % rhs); } template static SafeInt MixedSizeDivision(SafeInt 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::IsSigned() && SafeInt::IsSigned()) { //corner case where lhs = MinInt and rhs = -1 if(lhs == SafeInt::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::IsSigned() == SafeInt::IsSigned()) { if(sizeof(T) >= sizeof(U)) return SafeInt((T)((T)lhs.Value()/(T)rhs)); return SafeInt((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::IsSigned()) { if(sizeof(U) < sizeof(T)) { //simply upcast to T - rhs always fits into T return SafeInt((T)(lhs.Value()/(T)rhs)); } if(sizeof(U) < 4 && sizeof(T) < 4) { //even if U is bigger, upcast to int return SafeInt((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)((_int64)lhs.Value()/(_int64)rhs)); } //U is unsigned _int64 //now it matters whether lhs < 0 if(lhs.Value() < 0) { if(rhs > (U)SafeInt::MaxInt() + 1) return SafeInt(0); //corner case if(lhs.Value() == SafeInt::MinInt() && rhs == (U)SafeInt::MaxInt() + 1) { return SafeInt((T)-1); } //finally, rhs fits into T - just cast return SafeInt((T)(lhs.Value()/(T)rhs)); } //lhs >= 0 //T now has to fit into U return SafeInt((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)(lhs.Value()/(T)rhs)); } //now sizeof(U) > sizeof(T) and rhs > 0 //all T fits into U return SafeInt((T)((U)lhs.Value()/rhs)); } template static SafeInt MixedSizeAddition(SafeInt lhs, U rhs) { if(SafeInt::IsSigned() == SafeInt::IsSigned()) { //easy case - just upcast U to T if(sizeof(T) >= sizeof(U)) { return SafeInt(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((_int32)lhs.Value() + (_int32)rhs); //else unsigned return SafeInt((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((_int64)lhs.Value() + (_int64)rhs); return SafeInt((unsigned _int64)lhs.Value() + (unsigned _int64)rhs); } //else U is an _int64 //this will have to be done the expensive way { return SafeInt(SafeInt(lhs.Value()) + rhs); //more checking here } } //else mixed sign //first consider the case of signed T, unsigned U if(SafeInt::IsSigned()) { if(sizeof(T) > sizeof(U)) { //piece of cake, U fits in T return SafeInt(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((_int32)lhs.Value() + (_int32)rhs); } if(sizeof(U) == 4) { return SafeInt((_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::MaxInt()) throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); //stuff them all into an int, return return SafeInt((_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::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((_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(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(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((_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((_int64)lhs.Value() + (_int64)rhs); } if(sizeof(U) == 8 && sizeof(T) < 8) { //all possible values of T fit into an _int64 SafeInt u(rhs); u += (_int64)lhs.Value(); return SafeInt(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(addition(lhs.Value(), (T)rhs)); } //rhs is negative if((T)(-rhs) > lhs.Value()) //this will never work throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); return SafeInt(lhs.Value() - (T)(-rhs)); } template static SafeInt MixedSizeSubtraction(SafeInt lhs, U rhs) { if(SafeInt::IsSigned() == SafeInt::IsSigned()) { //easy case - just upcast U to T if(sizeof(T) >= sizeof(U)) return SafeInt(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((_int32)lhs.Value() - (_int32)rhs); //else unsigned return SafeInt((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((_int64)lhs.Value() - (_int64)rhs); return SafeInt((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(SafeInt(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(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((_int32)lhs.Value() - (_int32)rhs); } if(sizeof(U) == 4) { //upcast to _int64, which is what the compiler should do return SafeInt((_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::MaxInt()) throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); //stuff them all into an int, return return SafeInt((_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::MaxInt()) throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); return SafeInt((_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::MinInt())) throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); //else it has to work return SafeInt((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((_int32)lhs.Value() - (_int32)rhs); } else if(sizeof(T) < 8 && sizeof(U) < 8) { //handles another 5 of 16 cases return SafeInt((_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(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(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((_int64)lhs.Value() - (_int64)rhs); } template static SafeInt MixedSizeMultiply(SafeInt 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::IsSigned() == SafeInt::IsSigned()) { //simple case - same signedness and U always fits in T if(sizeof(T) >= sizeof(U)) { return SafeInt(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(rhs); u *= lhs.m_int; return SafeInt(u.Value()); } if(SafeInt::IsSigned()) { if(sizeof(U) < 4) { //the result must always fit into an int return SafeInt((_int32)((_int32)lhs.m_int * (_int32)rhs)); } //result must fit into an _int64 return SafeInt((_int64)((_int64)lhs.m_int * (_int64)rhs)); } //else unsigned if(sizeof(U) < 4) { //the result must always fit into an int return SafeInt((unsigned _int32)((unsigned _int32)lhs.m_int * (unsigned _int32)rhs)); } //result must fit into an _int64 return SafeInt((unsigned _int64)((unsigned _int64)lhs.m_int * (unsigned _int64)rhs)); } //mixed sign - consider T is signed, U unsigned if(SafeInt::IsSigned() && !SafeInt::IsSigned()) { //if T > U, we're OK if(sizeof(T) > sizeof(U)) { return SafeInt(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((_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((_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::MaxInt()) { //corner case -1 * (MaxInt + 1) = MinInt //do lhs comparison first, since is cheaper than 64-bit test if(lhs.Value() == -1 && ((U)SafeInt::MaxInt()+1) == rhs) return SafeInt(SafeInt::MinInt()); if(lhs.Value() == 0) return SafeInt(0); throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); } //now rhs must fit into T return SafeInt(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(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((unsigned _int32)((unsigned _int32)lhs.m_int * (unsigned _int32)rhs)); } else if(sizeof(U) == 4) { //now go up to 64-bit return SafeInt((unsigned _int64)((unsigned _int64)lhs.m_int * (unsigned _int64)rhs)); } else { //U = signed _int64, T = unsigned [char|_int16|int] SafeInt u(rhs); u *= lhs.Value(); //now bounds check if(u.Value() > (U)SafeInt::MaxInt()) throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); //it has to be safe, since rhs is non-negative return SafeInt((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 static bool LessThan(T lhs, U rhs) { #ifdef ANSI_CONVERSIONS return lhs < rhs; #else return (SafeInt(rhs) > lhs); #endif } template static bool GreaterThan(T lhs, U rhs) { #ifdef ANSI_CONVERSIONS return lhs > rhs; #else if(SafeInt::IsSigned(lhs) == SafeInt::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::IsSigned(lhs) && sizeof(T) == 4 && sizeof(U) < 4) || (SafeInt::IsSigned(rhs) && sizeof(U) == 4 && sizeof(T) < 4)) { //all of these fit into an int return (_int32)lhs > (_int32)rhs; } if((SafeInt::IsSigned(rhs) && sizeof(U) == 8 && sizeof(T) < 8) || (SafeInt::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::IsSigned(lhs) && lhs < 0) { //if lhs < 0, rhs unsigned return false; } //2nd corner case if(SafeInt::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 static bool Equals(T lhs, U rhs) { #ifdef ANSI_CONVERSIONS return lhs == rhs; #else if(SafeInt::IsSigned(lhs) == SafeInt::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::IsSigned(lhs) && sizeof(T) == 4 && sizeof(U) < 4) || (SafeInt::IsSigned(rhs) && sizeof(U) == 4 && sizeof(T) < 4)) { //all of these fit into an int return (_int32)lhs == (_int32)rhs; } if((SafeInt::IsSigned(rhs) && sizeof(U) == 8 && sizeof(T) < 8) || (SafeInt::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::IsSigned(lhs) && lhs < 0) || (SafeInt::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 static SafeInt SafeTtoI(U* input) { U* tmp = input; SafeInt 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 template bool operator <(U lhs, SafeInt rhs) { return rhs > lhs; } template bool operator <(SafeInt lhs, SafeInt rhs) { return lhs < rhs.Value(); } //greater than template bool operator >(U lhs, SafeInt rhs) { return rhs < lhs; } template bool operator >(SafeInt lhs, SafeInt rhs) { return lhs > rhs.Value(); } //greater than or equal template bool operator >=(U lhs, SafeInt rhs) { return rhs < lhs; } template bool operator >=(SafeInt lhs, SafeInt rhs) { return lhs >= rhs.Value(); } //less than or equal template bool operator <=(U lhs, SafeInt rhs) { return rhs > lhs; } template bool operator <=(SafeInt lhs, SafeInt rhs) { return lhs <= rhs.Value(); } //equality //explicit overload for bool template bool operator ==(bool lhs, SafeInt rhs) { return lhs == (rhs.Value() == 0 ? false : true); } template bool operator ==(U lhs, SafeInt rhs) { return rhs == lhs; } template bool operator ==(SafeInt lhs, SafeInt rhs) { return lhs == rhs.Value(); } //not equals template bool operator !=(U lhs, SafeInt rhs) { return rhs != lhs; } template bool operator !=(bool lhs, SafeInt rhs) { return (rhs.Value() == 0 ? false : true) != lhs; } template bool operator !=(SafeInt lhs, SafeInt rhs) { return lhs != rhs.Value(); } //modulus template SafeInt operator %(U lhs, SafeInt 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::IsSigned() == SafeInt::IsSigned()) { if(rhs == 0) throw SafeIntException(EXCEPTION_INT_DIVIDE_BY_ZERO); return SafeInt((T)(lhs % rhs.Value())); } return SafeInt( (SafeInt(lhs) % rhs.Value()) ); } //multiplication template SafeInt operator *(U lhs, SafeInt rhs) { return rhs * lhs; } //division template SafeInt operator /(U lhs, SafeInt 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::IsSigned() && SafeInt::IsSigned()) { //corner case where lhs = MinInt and rhs = -1 if(lhs == SafeInt::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::IsSigned() == SafeInt::IsSigned()) { return SafeInt(lhs/rhs.Value()); } //now we have mixed sign case, which can lead to problems //first consider T signed, U unsigned if(SafeInt::IsSigned()) { if(sizeof(U) < sizeof(T)) { //simply upcast to T - lhs always fits into T return SafeInt(((T)lhs)/rhs.Value()); } if(sizeof(U) < 4 && sizeof(T) < 4) { //even if U is bigger, upcast to int return SafeInt((_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((_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::MaxInt()+1) return SafeInt(SafeInt::MinInt()); //else tmp is too big, or can be negated return SafeInt(-(SafeInt(tmp))); } //rhs >= 0 //T now has to fit into U return SafeInt((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)lhs/rhs.Value()); } //now sizeof(U) > sizeof(T) and rhs >= 0 //all T fits into U return SafeInt(lhs/(U)rhs.Value()); } //addition template SafeInt operator +(U lhs, SafeInt rhs) { return rhs + lhs; } //subtraction template SafeInt operator -(U lhs, SafeInt rhs) { if(rhs.IsSigned()) { if(SafeInt::IsSigned()) { //both are signed if(sizeof(T) >= sizeof(U)) return SafeInt(lhs) - rhs; //else return SafeInt(SafeInt(lhs) - rhs.Value()); } //lhs is unsigned, rhs is signed if(sizeof(T) > sizeof(U)) return SafeInt(lhs) - rhs; //U is >= T - not all values of U fit into T if(sizeof(U) < 4) { //upcast to int return SafeInt((_int32)lhs - (_int32)rhs.Value()); } if(sizeof(U) == 4) { //upcast to _int64 return SafeInt((_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::MaxInt())) throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW); //everything has to be able to fit into an _int64 return SafeInt((_int64)lhs - (_int64)rhs.Value()); } //now rhs is _int64 //if rhs is > 0, upcast to U if(rhs.Value() >= 0) return SafeInt(SafeInt(lhs) - rhs.Value()); //if rhs < 0, treat as addition //unboxing everything maintains correctness, even if rhs = MinInt return SafeInt(SafeInt(lhs) + SafeInt((U)((T)-rhs.Value())) ); } } //T is unsigned //this means that if lhs < 0, result is an error if(SafeInt::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(lhs) - rhs; } //sizeof(T) < sizeof(U) //T fits in U, whether U is signed or unsigned return SafeInt(SafeInt(lhs) - rhs.Value()); } //shift operators //NOTE - shift operators always return the type of the lhs argument //left shift template SafeInt operator <<(U lhs, SafeInt bits) { if(bits.IsSigned()) assert(bits.Value() >= 0); assert(bits.Value() < SafeInt::BitCount()); return SafeInt((U)(lhs << bits.Value())); } //right shift template SafeInt operator >>(U lhs, SafeInt bits) { if(bits.IsSigned()) assert(bits.Value() >= 0); assert(bits.Value() < SafeInt::BitCount()); return SafeInt((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 SafeInt operator &(U lhs, SafeInt rhs) { //if U can fit into T without truncating, force U to T return SafeInt(SafeInt(lhs) & rhs.Value()); } //bitwise XOR template SafeInt operator ^(U lhs, SafeInt rhs) { return SafeInt(SafeInt(lhs) ^ rhs.Value()); } //bitwise OR template SafeInt operator |(U lhs, SafeInt rhs) { return SafeInt(SafeInt(lhs) | rhs.Value()); } //logical operators //logical OR template bool operator ||(bool lhs, SafeInt rhs) { return lhs || rhs.Value(); } template bool operator ||(U lhs, SafeInt rhs) { return lhs || rhs.Value(); } template bool operator ||(SafeInt lhs, SafeInt rhs) { return lhs.Value() || rhs.Value(); } //logical AND template bool operator &&(bool lhs, SafeInt rhs) { return lhs && rhs.Value(); } template bool operator &&(U lhs, SafeInt rhs) { return lhs && rhs.Value(); } template bool operator &&(SafeInt lhs, SafeInt rhs) { return lhs.Value() && rhs.Value(); } #pragma warning(pop) #endif //SAFEINT_HPP