2895 lines
94 KiB
C++
2895 lines
94 KiB
C++
/***
|
|
*safeint_internal.h - Internal details for SafeInt (see safeint.h)
|
|
*
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* Private internal details for SafeInt.
|
|
* The constructs and functions in Microsoft::Utilities::details are not
|
|
* meant to be used by external code and can change at any time.
|
|
*
|
|
****/
|
|
|
|
#pragma once
|
|
|
|
#include <crtdbg.h>
|
|
|
|
#pragma pack(push, _CRT_PACKING)
|
|
|
|
namespace msl
|
|
{
|
|
|
|
namespace utilities
|
|
{
|
|
|
|
namespace details
|
|
{
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4702)
|
|
|
|
template < typename T > class NumericType;
|
|
|
|
template <> class NumericType<bool> { public: enum{ isBool = true, isFloat = false, isInt = false }; };
|
|
template <> class NumericType<char> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<unsigned char> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<signed char> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<short> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<unsigned short> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
#if _NATIVE_WCHAR_T_DEFINED
|
|
template <> class NumericType<wchar_t> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
#endif /* _NATIVE_WCHAR_T_DEFINED */
|
|
template <> class NumericType<int> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<unsigned int> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<long> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<unsigned long> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<__int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<unsigned __int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; };
|
|
template <> class NumericType<float> { public: enum{ isBool = false, isFloat = true, isInt = false }; };
|
|
template <> class NumericType<double> { public: enum{ isBool = false, isFloat = true, isInt = false }; };
|
|
template <> class NumericType<long double> { public: enum{ isBool = false, isFloat = true, isInt = false }; };
|
|
// Catch-all for anything not supported
|
|
template < typename T > class NumericType { public: enum{ isBool = false, isFloat = false, isInt = false }; };
|
|
|
|
|
|
template < typename T > class IntTraits
|
|
{
|
|
public:
|
|
_STATIC_ASSERT( NumericType<T>::isInt || NumericType<T>::isBool );
|
|
enum
|
|
{
|
|
#pragma warning(suppress:4804)
|
|
isSigned = ( (T)(-1) < 0 ),
|
|
is64Bit = ( sizeof(T) == 8 ),
|
|
is32Bit = ( sizeof(T) == 4 ),
|
|
is16Bit = ( sizeof(T) == 2 ),
|
|
is8Bit = ( sizeof(T) == 1 ),
|
|
isLT32Bit = ( sizeof(T) < 4 ),
|
|
isLT64Bit = ( sizeof(T) < 8 ),
|
|
isInt8 = ( sizeof(T) == 1 && isSigned ),
|
|
isUint8 = ( sizeof(T) == 1 && !isSigned ),
|
|
isInt16 = ( sizeof(T) == 2 && isSigned ),
|
|
isUint16 = ( sizeof(T) == 2 && !isSigned ),
|
|
isInt32 = ( sizeof(T) == 4 && isSigned ),
|
|
isUint32 = ( sizeof(T) == 4 && !isSigned ),
|
|
isInt64 = ( sizeof(T) == 8 && isSigned ),
|
|
isUint64 = ( sizeof(T) == 8 && !isSigned ),
|
|
bitCount = ( sizeof(T)*8 ),
|
|
#pragma warning(suppress:4804)
|
|
isBool = NumericType<T>::isBool
|
|
};
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4310)
|
|
const static T maxInt = isSigned ? ((T)~((T)1 << (T)(bitCount-1))) : ((T)(~(T)0));
|
|
const static T minInt = isSigned ? ((T)((T)1 << (T)(bitCount-1))) : ((T)0);
|
|
#pragma warning(pop)
|
|
};
|
|
|
|
// this is strictly internal and not to be used as a policy in SafeInt<>
|
|
struct SafeIntErrorPolicy_NoThrow
|
|
{
|
|
static void SafeIntOnOverflow()
|
|
{
|
|
}
|
|
|
|
static void SafeIntOnDivZero()
|
|
{
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class SafeIntCompare
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
isBothSigned = (IntTraits< T >::isSigned && IntTraits< U >::isSigned),
|
|
isBothUnsigned = (!IntTraits< T >::isSigned && !IntTraits< U >::isSigned),
|
|
isLikeSigned = (IntTraits< T >::isSigned == IntTraits< U >::isSigned),
|
|
isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) ||
|
|
(IntTraits< T >::isSigned && sizeof(T) > sizeof(U))),
|
|
isBothLT32Bit = (IntTraits< T >::isLT32Bit && IntTraits< U >::isLT32Bit),
|
|
isBothLT64Bit = (IntTraits< T >::isLT64Bit && IntTraits< U >::isLT64Bit)
|
|
};
|
|
};
|
|
|
|
template < typename U > class SafeIntCompare< float, U >
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
isBothSigned = IntTraits< U >::isSigned,
|
|
isBothUnsigned = false,
|
|
isLikeSigned = IntTraits< U >::isSigned,
|
|
isCastOK = true
|
|
};
|
|
};
|
|
|
|
template < typename U > class SafeIntCompare< double, U >
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
isBothSigned = IntTraits< U >::isSigned,
|
|
isBothUnsigned = false,
|
|
isLikeSigned = IntTraits< U >::isSigned,
|
|
isCastOK = true
|
|
};
|
|
};
|
|
|
|
template < typename U > class SafeIntCompare< long double, U >
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
isBothSigned = IntTraits< U >::isSigned,
|
|
isBothUnsigned = false,
|
|
isLikeSigned = IntTraits< U >::isSigned,
|
|
isCastOK = true
|
|
};
|
|
};
|
|
|
|
//all of the arithmetic operators can be solved by the same code within
|
|
//each of these regions without resorting to compile-time constant conditionals
|
|
//most operators collapse the problem into less than the 22 zones, but this is used
|
|
//as the first cut
|
|
//using this also helps ensure that we handle all of the possible cases correctly
|
|
|
|
template < typename T, typename U > class IntRegion
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
//unsigned-unsigned zone
|
|
IntZone_UintLT32_UintLT32 = SafeIntCompare< T,U >::isBothUnsigned && SafeIntCompare< T,U >::isBothLT32Bit,
|
|
IntZone_Uint32_UintLT64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit,
|
|
IntZone_UintLT32_Uint32 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit,
|
|
IntZone_Uint64_Uint = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is64Bit,
|
|
IntZone_UintLT64_Uint64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit,
|
|
//unsigned-signed
|
|
IntZone_UintLT32_IntLT32 = !IntTraits< T >::isSigned && IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit,
|
|
IntZone_Uint32_IntLT64 = IntTraits< T >::isUint32 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit,
|
|
IntZone_UintLT32_Int32 = !IntTraits< T >::isSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::isInt32,
|
|
IntZone_Uint64_Int = IntTraits< T >::isUint64 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit,
|
|
IntZone_UintLT64_Int64 = !IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isInt64,
|
|
IntZone_Uint64_Int64 = IntTraits< T >::isUint64 && IntTraits< U >::isInt64,
|
|
//signed-signed
|
|
IntZone_IntLT32_IntLT32 = SafeIntCompare< T,U >::isBothSigned && SafeIntCompare< T, U >::isBothLT32Bit,
|
|
IntZone_Int32_IntLT64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit,
|
|
IntZone_IntLT32_Int32 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit,
|
|
IntZone_Int64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isInt64 && IntTraits< U >::isInt64,
|
|
IntZone_Int64_Int = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is64Bit && IntTraits< U >::isLT64Bit,
|
|
IntZone_IntLT64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit,
|
|
//signed-unsigned
|
|
IntZone_IntLT32_UintLT32 = IntTraits< T >::isSigned && !IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit,
|
|
IntZone_Int32_UintLT32 = IntTraits< T >::isInt32 && !IntTraits< U >::isSigned && IntTraits< U >::isLT32Bit,
|
|
IntZone_IntLT64_Uint32 = IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isUint32,
|
|
IntZone_Int64_UintLT64 = IntTraits< T >::isInt64 && !IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit,
|
|
IntZone_Int_Uint64 = IntTraits< T >::isSigned && IntTraits< U >::isUint64 && IntTraits< T >::isLT64Bit,
|
|
IntZone_Int64_Uint64 = IntTraits< T >::isInt64 && IntTraits< U >::isUint64
|
|
};
|
|
};
|
|
|
|
// useful function to help with getting the magnitude of a negative number
|
|
enum AbsMethod
|
|
{
|
|
AbsMethodInt,
|
|
AbsMethodInt64,
|
|
AbsMethodNoop
|
|
};
|
|
|
|
template < typename T >
|
|
class GetAbsMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
method = IntTraits< T >::isLT64Bit && IntTraits< T >::isSigned ? AbsMethodInt :
|
|
IntTraits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop
|
|
};
|
|
};
|
|
|
|
template < typename T, int Method = GetAbsMethod< T >::method > class AbsValueHelper;
|
|
|
|
template < typename T > class AbsValueHelper < T, AbsMethodInt >
|
|
{
|
|
public:
|
|
static unsigned __int32 Abs( T t ) throw()
|
|
{
|
|
_ASSERTE( t < 0 );
|
|
return (unsigned __int32)-t;
|
|
}
|
|
};
|
|
|
|
template < typename T > class AbsValueHelper < T, AbsMethodInt64 >
|
|
{
|
|
public:
|
|
static unsigned __int64 Abs( T t ) throw()
|
|
{
|
|
_ASSERTE( t < 0 );
|
|
return (unsigned __int64)-t;
|
|
}
|
|
};
|
|
|
|
template < typename T > class AbsValueHelper < T, AbsMethodNoop >
|
|
{
|
|
public:
|
|
static T Abs( T t ) throw()
|
|
{
|
|
// Why are you calling Abs on an unsigned number ???
|
|
_ASSERTE( ("AbsValueHelper::Abs should not be called with an unsigned integer type", 0) );
|
|
return t;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename E, bool fSigned > class NegationHelper;
|
|
|
|
template < typename T, typename E > class NegationHelper < T, E, true > // Signed
|
|
{
|
|
public:
|
|
static SafeIntError Negative( T t, T& ret )
|
|
{
|
|
// corner case
|
|
if( t != IntTraits< T >::minInt )
|
|
{
|
|
// cast prevents unneeded checks in the case of small ints
|
|
ret = -t;
|
|
return SafeIntNoError;
|
|
}
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
|
|
template < typename T, typename E > class NegationHelper < T, E, false > // unsigned
|
|
{
|
|
public:
|
|
static SafeIntError Negative( T t, T& ret ) throw()
|
|
{
|
|
_SAFEINT_UNSIGNED_NEGATION_BEHAVIOR();
|
|
|
|
#pragma warning(suppress:4127)
|
|
_ASSERTE( !IntTraits<T>::isLT32Bit );
|
|
|
|
#pragma warning(suppress:4146)
|
|
ret = -t;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
//core logic to determine casting behavior
|
|
enum CastMethod
|
|
{
|
|
CastOK = 0,
|
|
CastCheckLTZero,
|
|
CastCheckGTMax,
|
|
CastCheckMinMaxUnsigned,
|
|
CastCheckMinMaxSigned,
|
|
CastFromFloat,
|
|
CastToBool,
|
|
CastFromBool
|
|
};
|
|
|
|
template < typename ToType, typename FromType >
|
|
class GetCastMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
method = ( IntTraits< FromType >::isBool &&
|
|
!IntTraits< ToType >::isBool ) ? CastFromBool :
|
|
|
|
( !IntTraits< FromType >::isBool &&
|
|
IntTraits< ToType >::isBool ) ? CastToBool :
|
|
( NumericType< FromType >::isFloat &&
|
|
!NumericType< ToType >::isFloat ) ? CastFromFloat :
|
|
|
|
( SafeIntCompare< ToType, FromType >::isCastOK ||
|
|
( NumericType< ToType >::isFloat &&
|
|
!NumericType< FromType >::isFloat ) ) ? CastOK :
|
|
|
|
( ( IntTraits< ToType >::isSigned &&
|
|
!IntTraits< FromType >::isSigned &&
|
|
sizeof( FromType ) >= sizeof( ToType ) ) ||
|
|
( SafeIntCompare< ToType, FromType >::isBothUnsigned &&
|
|
sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax :
|
|
|
|
( !IntTraits< ToType >::isSigned &&
|
|
IntTraits< FromType >::isSigned &&
|
|
sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero :
|
|
|
|
( !IntTraits< ToType >::isSigned ) ? CastCheckMinMaxUnsigned
|
|
: CastCheckMinMaxSigned
|
|
};
|
|
};
|
|
|
|
template < typename T, typename U, typename E,
|
|
int Method = GetCastMethod< T, U >::method > class SafeCastHelper;
|
|
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastOK >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( U u, T& t ) throw()
|
|
{
|
|
t = (T)u;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
// special case floats and doubles
|
|
// tolerate loss of precision
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastFromFloat >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( U u, T& t )
|
|
{
|
|
if( u <= (U)IntTraits< T >::maxInt &&
|
|
u >= (U)IntTraits< T >::minInt )
|
|
{
|
|
t = (T)u;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
// Match on any method where a bool is cast to type T
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastFromBool >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( bool b, T& t ) throw()
|
|
{
|
|
t = (T)( b ? 1 : 0 );
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastToBool >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( T t, bool& b ) throw()
|
|
{
|
|
b = !!t;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckLTZero >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( U u, T& t )
|
|
{
|
|
if( u < 0 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
t = (T)u;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckGTMax >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( U u, T& t )
|
|
{
|
|
if( u > IntTraits< T >::maxInt )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
t = (T)u;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckMinMaxUnsigned >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( U u, T& t )
|
|
{
|
|
// U is signed - T could be either signed or unsigned
|
|
if( u > IntTraits< T >::maxInt || u < 0 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
t = (T)u;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckMinMaxSigned >
|
|
{
|
|
public:
|
|
static SafeIntError Cast( U u, T& t )
|
|
{
|
|
// T, U are signed
|
|
if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
t = (T)u;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
//core logic to determine whether a comparison is valid, or needs special treatment
|
|
enum ComparisonMethod
|
|
{
|
|
ComparisonMethod_Ok = 0,
|
|
ComparisonMethod_CastInt,
|
|
ComparisonMethod_CastInt64,
|
|
ComparisonMethod_UnsignedT,
|
|
ComparisonMethod_UnsignedU
|
|
};
|
|
|
|
template < typename T, typename U >
|
|
class ValidComparison
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
#if _SAFEINT_USE_ANSI_CONVERSIONS
|
|
method = ComparisonMethod_Ok
|
|
#else /* _SAFEINT_USE_ANSI_CONVERSIONS */
|
|
method = ( ( SafeIntCompare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok :
|
|
( ( IntTraits< T >::isSigned && sizeof(T) < 8 && sizeof(U) < 4 ) ||
|
|
( IntTraits< U >::isSigned && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt :
|
|
( ( IntTraits< T >::isSigned && sizeof(U) < 8 ) ||
|
|
( IntTraits< U >::isSigned && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 :
|
|
( !IntTraits< T >::isSigned ) ? ComparisonMethod_UnsignedT :
|
|
ComparisonMethod_UnsignedU )
|
|
#endif /* _SAFEINT_USE_ANSI_CONVERSIONS */
|
|
};
|
|
};
|
|
|
|
template <typename T, typename U, int Method = ValidComparison< T, U >::method > class EqualityTest;
|
|
|
|
template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok >
|
|
{
|
|
public:
|
|
static bool IsEquals( const T t, const U u ) throw() { return ( t == u ); }
|
|
};
|
|
|
|
template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt >
|
|
{
|
|
public:
|
|
static bool IsEquals( const T t, const U u ) throw() { return ( (int)t == (int)u ); }
|
|
};
|
|
|
|
template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 >
|
|
{
|
|
public:
|
|
static bool IsEquals( const T t, const U u ) throw() { return ( (__int64)t == (__int64)u ); }
|
|
};
|
|
|
|
template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT >
|
|
{
|
|
public:
|
|
static bool IsEquals( const T t, const U u ) throw()
|
|
{
|
|
//one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
|
|
if( u < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//else safe to cast to type T
|
|
return ( t == (T)u );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU>
|
|
{
|
|
public:
|
|
static bool IsEquals( const T t, const U u ) throw()
|
|
{
|
|
//one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
|
|
if( t < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//else safe to cast to type U
|
|
return ( (U)t == u );
|
|
}
|
|
};
|
|
|
|
template <typename T, typename U, int Method = ValidComparison< T, U >::method > class GreaterThanTest;
|
|
|
|
template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok >
|
|
{
|
|
public:
|
|
static bool GreaterThan( const T t, const U u ) throw() { return ( t > u ); }
|
|
};
|
|
|
|
template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt >
|
|
{
|
|
public:
|
|
static bool GreaterThan( const T t, const U u ) throw() { return ( (int)t > (int)u ); }
|
|
};
|
|
|
|
template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 >
|
|
{
|
|
public:
|
|
static bool GreaterThan( const T t, const U u ) throw() { return ( (__int64)t > (__int64)u ); }
|
|
};
|
|
|
|
template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT >
|
|
{
|
|
public:
|
|
static bool GreaterThan( const T t, const U u ) throw()
|
|
{
|
|
// one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
|
|
if( u < 0 )
|
|
{
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
// else safe to cast to type T
|
|
return ( t > (T)u );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU >
|
|
{
|
|
public:
|
|
static bool GreaterThan( const T t, const U u ) throw()
|
|
{
|
|
// one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller
|
|
if( t < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// else safe to cast to type U
|
|
return ( (U)t > u );
|
|
}
|
|
};
|
|
|
|
// Modulus is simpler than comparison, but follows much the same logic
|
|
// using this set of functions, it can't fail except in a div 0 situation
|
|
template <typename T, typename U, typename E, int Method = ValidComparison< T, U >::method > class ModulusHelper;
|
|
|
|
template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_Ok>
|
|
{
|
|
public:
|
|
static SafeIntError Modulus( const T& t, const U& u, T& result )
|
|
{
|
|
if(u == 0)
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
// trap corner case
|
|
#pragma warning(suppress:4127)
|
|
if( IntTraits< U >::isSigned )
|
|
{
|
|
if(u == -1)
|
|
{
|
|
result = 0;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
result = (T)(t % u);
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_CastInt>
|
|
{
|
|
public:
|
|
static SafeIntError Modulus( const T& t, const U& u, T& result )
|
|
{
|
|
if(u == 0)
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
// trap corner case
|
|
#pragma warning(suppress:4127)
|
|
if( IntTraits< U >::isSigned )
|
|
{
|
|
if(u == -1)
|
|
{
|
|
result = 0;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
result = (T)(t % u);
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_CastInt64>
|
|
{
|
|
public:
|
|
static SafeIntError Modulus( const T& t, const U& u, T& result )
|
|
{
|
|
if(u == 0)
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
#pragma warning(suppress:4127)
|
|
if(IntTraits< U >::isSigned && u == -1)
|
|
{
|
|
result = 0;
|
|
}
|
|
else
|
|
{
|
|
result = (T)((__int64)t % (__int64)u);
|
|
}
|
|
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
// T is unsigned __int64, U is any signed int
|
|
template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_UnsignedT>
|
|
{
|
|
public:
|
|
static SafeIntError Modulus( const T& t, const U& u, T& result )
|
|
{
|
|
if(u == 0)
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
// u could be negative - if so, need to convert to positive
|
|
// casts below are always safe due to the way modulus works
|
|
if(u < 0)
|
|
{
|
|
result = (T)(t % AbsValueHelper< U >::Abs(u));
|
|
}
|
|
else
|
|
{
|
|
result = (T)(t % u);
|
|
}
|
|
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
// U is unsigned __int64, T any signed int
|
|
template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_UnsignedU>
|
|
{
|
|
public:
|
|
static SafeIntError Modulus( const T& t, const U& u, T& result )
|
|
{
|
|
if(u == 0)
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
//t could be negative - if so, need to convert to positive
|
|
if(t < 0)
|
|
{
|
|
result = -(T)( AbsValueHelper< T >::Abs( t ) % u );
|
|
}
|
|
else
|
|
{
|
|
result = (T)((T)t % u);
|
|
}
|
|
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
//core logic to determine method to check multiplication
|
|
enum MultiplicationState
|
|
{
|
|
MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit
|
|
MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit
|
|
MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit
|
|
MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller
|
|
MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller
|
|
MultiplicationState_Uint64Uint64, // Both are unsigned int64
|
|
MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32
|
|
MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64
|
|
MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit
|
|
MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64
|
|
MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32
|
|
MultiplicationState_Int64Int64, // lhs int64, rhs int64
|
|
MultiplicationState_Int64Int, // lhs int64, rhs int32
|
|
MultiplicationState_IntUint64, // lhs int, rhs unsigned int64
|
|
MultiplicationState_IntInt64, // lhs int, rhs int64
|
|
MultiplicationState_Int64Uint64, // lhs int64, rhs uint64
|
|
MultiplicationState_Error
|
|
};
|
|
|
|
template < typename T, typename U >
|
|
class MultiplicationMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
// unsigned-unsigned
|
|
method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint :
|
|
(IntRegion< T,U >::IntZone_Uint32_UintLT64 ||
|
|
IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 :
|
|
SafeIntCompare< T,U >::isBothUnsigned &&
|
|
IntTraits< T >::isUint64 && IntTraits< U >::isUint64 ? MultiplicationState_Uint64Uint64 :
|
|
(IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint :
|
|
(IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 :
|
|
// unsigned-signed
|
|
(IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt :
|
|
(IntRegion< T,U >::IntZone_Uint32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 :
|
|
(IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int :
|
|
(IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 :
|
|
(IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 :
|
|
// signed-signed
|
|
(IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt :
|
|
(IntRegion< T,U >::IntZone_Int32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 :
|
|
(IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 :
|
|
(IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int :
|
|
(IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 :
|
|
// signed-unsigned
|
|
(IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt :
|
|
(IntRegion< T,U >::IntZone_Int32_UintLT32 ||
|
|
IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 :
|
|
(IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint :
|
|
(IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 :
|
|
(IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 :
|
|
MultiplicationState_Error ) )
|
|
};
|
|
};
|
|
|
|
template <typename T, typename U, typename E, int Method = MultiplicationMethod< T, U >::method > class MultiplicationHelper;
|
|
|
|
template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastInt>
|
|
{
|
|
public:
|
|
//accepts signed, both less than 32-bit
|
|
static SafeIntError Multiply( const T& t, const U& u, T& ret )
|
|
{
|
|
int tmp = t * u;
|
|
|
|
if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastUint >
|
|
{
|
|
public:
|
|
//accepts unsigned, both less than 32-bit
|
|
static SafeIntError Multiply( const T& t, const U& u, T& ret )
|
|
{
|
|
unsigned int tmp = t * u;
|
|
|
|
if( tmp > IntTraits< T >::maxInt )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastInt64>
|
|
{
|
|
public:
|
|
//mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less
|
|
static SafeIntError Multiply( const T& t, const U& u, T& ret )
|
|
{
|
|
__int64 tmp = (__int64)t * (__int64)u;
|
|
|
|
if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastUint64>
|
|
{
|
|
public:
|
|
//both unsigned where at least one argument is 32-bit, and both are 32-bit or less
|
|
static SafeIntError Multiply( const T& t, const U& u, T& ret )
|
|
{
|
|
unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u;
|
|
|
|
if(tmp > (unsigned __int64)IntTraits< T >::maxInt)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
// T = left arg and return type
|
|
// U = right arg
|
|
template < typename T, typename U, typename E > class LargeIntRegMultiply;
|
|
|
|
template< typename E > class LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64& ret )
|
|
{
|
|
unsigned __int32 aHigh, aLow, bHigh, bLow;
|
|
|
|
// Consider that a*b can be broken up into:
|
|
// (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow)
|
|
// => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow)
|
|
// Note - same approach applies for 128 bit math on a 64-bit system
|
|
|
|
aHigh = (unsigned __int32)(a >> 32);
|
|
aLow = (unsigned __int32)a;
|
|
bHigh = (unsigned __int32)(b >> 32);
|
|
bLow = (unsigned __int32)b;
|
|
|
|
ret = 0;
|
|
|
|
if(aHigh == 0)
|
|
{
|
|
if(bHigh != 0)
|
|
{
|
|
ret = (unsigned __int64)aLow * (unsigned __int64)bHigh;
|
|
}
|
|
}
|
|
else if(bHigh == 0)
|
|
{
|
|
if(aHigh != 0)
|
|
{
|
|
ret = (unsigned __int64)aHigh * (unsigned __int64)bLow;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
if(ret != 0)
|
|
{
|
|
unsigned __int64 tmp;
|
|
|
|
if((unsigned __int32)(ret >> 32) != 0)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret <<= 32;
|
|
tmp = (unsigned __int64)aLow * (unsigned __int64)bLow;
|
|
ret += tmp;
|
|
|
|
if(ret < tmp)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
ret = (unsigned __int64)aLow * (unsigned __int64)bLow;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template< typename E > class LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const unsigned __int64& a, unsigned __int32 b, unsigned __int64& ret )
|
|
{
|
|
unsigned __int32 aHigh, aLow;
|
|
|
|
// Consider that a*b can be broken up into:
|
|
// (aHigh * 2^32 + aLow) * b
|
|
// => (aHigh * b * 2^32) + (aLow * b)
|
|
|
|
aHigh = (unsigned __int32)(a >> 32);
|
|
aLow = (unsigned __int32)a;
|
|
|
|
ret = 0;
|
|
|
|
if(aHigh != 0)
|
|
{
|
|
ret = (unsigned __int64)aHigh * (unsigned __int64)b;
|
|
|
|
unsigned __int64 tmp;
|
|
|
|
if((unsigned __int32)(ret >> 32) != 0)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret <<= 32;
|
|
tmp = (unsigned __int64)aLow * (unsigned __int64)b;
|
|
ret += tmp;
|
|
|
|
if(ret < tmp)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
ret = (unsigned __int64)aLow * (unsigned __int64)b;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template< typename E > class LargeIntRegMultiply< unsigned __int64, signed __int32, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const unsigned __int64& a, signed __int32 b, unsigned __int64& ret )
|
|
{
|
|
if( b < 0 && a != 0 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
return LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::RegMultiply(a, (unsigned __int32)b, ret);
|
|
}
|
|
};
|
|
|
|
template< typename E > class LargeIntRegMultiply< unsigned __int64, signed __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const unsigned __int64& a, signed __int64 b, unsigned __int64& ret )
|
|
{
|
|
if( b < 0 && a != 0 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
return LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >::RegMultiply(a, (unsigned __int64)b, ret);
|
|
}
|
|
};
|
|
|
|
template< typename E > class LargeIntRegMultiply< signed __int32, unsigned __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( signed __int32 a, const unsigned __int64& b, signed __int32& ret )
|
|
{
|
|
unsigned __int32 bHigh, bLow;
|
|
bool fIsNegative = false;
|
|
|
|
// Consider that a*b can be broken up into:
|
|
// (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow)
|
|
// => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow)
|
|
|
|
bHigh = (unsigned __int32)(b >> 32);
|
|
bLow = (unsigned __int32)b;
|
|
|
|
ret = 0;
|
|
|
|
if(bHigh != 0 && a != 0)
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
if( a < 0 )
|
|
{
|
|
a = -a;
|
|
fIsNegative = true;
|
|
}
|
|
|
|
unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow;
|
|
|
|
|
|
if( !fIsNegative )
|
|
{
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt )
|
|
{
|
|
ret = (signed __int32)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 )
|
|
{
|
|
ret = -( (signed __int32)tmp );
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( unsigned __int32 a, const unsigned __int64& b, unsigned __int32& ret )
|
|
{
|
|
// Consider that a*b can be broken up into:
|
|
// (bHigh * 2^32 + bLow) * a
|
|
// => (bHigh * a * 2^32) + (bLow * a)
|
|
// In this case, the result must fit into 32-bits
|
|
// If bHigh != 0 && a != 0, immediate error.
|
|
|
|
if( (unsigned __int32)(b >> 32) != 0 && a != 0 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
unsigned __int64 tmp = b * (unsigned __int64)a;
|
|
|
|
if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
ret = (unsigned __int32)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< unsigned __int32, signed __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( unsigned __int32 a, const signed __int64& b, unsigned __int32& ret )
|
|
{
|
|
if( b < 0 && a != 0 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
return LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >::RegMultiply( a, (unsigned __int64)b, ret );
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< signed __int64, signed __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const signed __int64& a, const signed __int64& b, signed __int64& ret )
|
|
{
|
|
bool aNegative = false;
|
|
bool bNegative = false;
|
|
|
|
unsigned __int64 tmp;
|
|
__int64 a1 = a;
|
|
__int64 b1 = b;
|
|
|
|
if( a1 < 0 )
|
|
{
|
|
aNegative = true;
|
|
a1 = -a1;
|
|
}
|
|
|
|
if( b1 < 0 )
|
|
{
|
|
bNegative = true;
|
|
b1 = -b1;
|
|
}
|
|
|
|
if( LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >::
|
|
RegMultiply( (unsigned __int64)a1, (unsigned __int64)b1, (unsigned __int64)tmp ) == SafeIntNoError )
|
|
{
|
|
// The unsigned multiplication didn't overflow
|
|
if( aNegative ^ bNegative )
|
|
{
|
|
// Result must be negative
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt )
|
|
{
|
|
ret = -(signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Result must be positive
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt )
|
|
{
|
|
ret = (signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< signed __int64, unsigned __int32, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const signed __int64& a, unsigned __int32 b, signed __int64& ret )
|
|
{
|
|
bool aNegative = false;
|
|
unsigned __int64 tmp;
|
|
__int64 a1 = a;
|
|
|
|
if( a1 < 0 )
|
|
{
|
|
aNegative = true;
|
|
a1 = -a1;
|
|
}
|
|
|
|
if( LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::RegMultiply( (unsigned __int64)a1, b, tmp ) == SafeIntNoError )
|
|
{
|
|
// The unsigned multiplication didn't overflow
|
|
if( aNegative )
|
|
{
|
|
// Result must be negative
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt )
|
|
{
|
|
ret = -(signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Result must be positive
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt )
|
|
{
|
|
ret = (signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< signed __int64, signed __int32, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const signed __int64& a, signed __int32 b, signed __int64& ret )
|
|
{
|
|
bool aNegative = false;
|
|
bool bNegative = false;
|
|
|
|
unsigned __int64 tmp;
|
|
__int64 a1 = a;
|
|
__int64 b1 = b;
|
|
|
|
if( a1 < 0 )
|
|
{
|
|
aNegative = true;
|
|
a1 = -a1;
|
|
}
|
|
|
|
if( b1 < 0 )
|
|
{
|
|
bNegative = true;
|
|
b1 = -b1;
|
|
}
|
|
|
|
if( LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::
|
|
RegMultiply( (unsigned __int64)a1, (unsigned __int32)b1, (unsigned __int64)tmp ) == SafeIntNoError )
|
|
{
|
|
// The unsigned multiplication didn't overflow
|
|
if( aNegative ^ bNegative )
|
|
{
|
|
// Result must be negative
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt )
|
|
{
|
|
ret = -(signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Result must be positive
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt )
|
|
{
|
|
ret = (signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< signed __int32, signed __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( signed __int32 a, const signed __int64& b, signed __int32& ret )
|
|
{
|
|
bool aNegative = false;
|
|
bool bNegative = false;
|
|
|
|
unsigned __int32 tmp;
|
|
__int64 b1 = b;
|
|
|
|
if( a < 0 )
|
|
{
|
|
aNegative = true;
|
|
a = -a;
|
|
}
|
|
|
|
if( b1 < 0 )
|
|
{
|
|
bNegative = true;
|
|
b1 = -b1;
|
|
}
|
|
|
|
if( LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >::
|
|
RegMultiply( (unsigned __int32)a, (unsigned __int64)b1, tmp ) == SafeIntNoError )
|
|
{
|
|
// The unsigned multiplication didn't overflow
|
|
if( aNegative ^ bNegative )
|
|
{
|
|
// Result must be negative
|
|
if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt )
|
|
{
|
|
#pragma warning(suppress:4146)
|
|
ret = -tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Result must be positive
|
|
if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt )
|
|
{
|
|
ret = (signed __int32)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class LargeIntRegMultiply< signed __int64, unsigned __int64, E >
|
|
{
|
|
public:
|
|
static SafeIntError RegMultiply( const signed __int64& a, const unsigned __int64& b, signed __int64& ret )
|
|
{
|
|
bool aNegative = false;
|
|
|
|
unsigned __int64 tmp;
|
|
__int64 a1 = a;
|
|
|
|
if( a1 < 0 )
|
|
{
|
|
aNegative = true;
|
|
a1 = -a1;
|
|
}
|
|
|
|
if( LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >::
|
|
RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, tmp ) == SafeIntNoError )
|
|
{
|
|
// The unsigned multiplication didn't overflow
|
|
if( aNegative )
|
|
{
|
|
// Result must be negative
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt )
|
|
{
|
|
ret = -((signed __int64)tmp);
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Result must be positive
|
|
if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt )
|
|
{
|
|
ret = (signed __int64)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class MultiplicationHelper< unsigned __int64, unsigned __int64, E, MultiplicationState_Uint64Uint64 >
|
|
{
|
|
public:
|
|
static SafeIntError Multiply( const unsigned __int64& t, const unsigned __int64& u, unsigned __int64& ret )
|
|
{
|
|
return LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >::RegMultiply( t, u, ret );
|
|
}
|
|
};
|
|
|
|
template < typename U, typename E > class MultiplicationHelper<unsigned __int64, U, E, MultiplicationState_Uint64Uint >
|
|
{
|
|
public:
|
|
//U is any unsigned int 32-bit or less
|
|
static SafeIntError Multiply( const unsigned __int64& t, const U& u, unsigned __int64& ret )
|
|
{
|
|
return LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::RegMultiply( t, (unsigned __int32)u, ret );
|
|
}
|
|
};
|
|
|
|
// converse of the previous function
|
|
template < typename T, typename E > class MultiplicationHelper< T, unsigned __int64, E, MultiplicationState_UintUint64 >
|
|
{
|
|
public:
|
|
// T is any unsigned int up to 32-bit
|
|
static SafeIntError Multiply( const T& t, const unsigned __int64& u, T& ret )
|
|
{
|
|
unsigned __int32 tmp;
|
|
|
|
if( LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >::RegMultiply( t, u, tmp ) == SafeIntNoError &&
|
|
SafeCastHelper< T, unsigned __int32, E >::Cast(tmp, ret) == SafeIntNoError )
|
|
{
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename E > class MultiplicationHelper< unsigned __int64, U, E, MultiplicationState_Uint64Int >
|
|
{
|
|
public:
|
|
//U is any signed int, up to 64-bit
|
|
static SafeIntError Multiply(const unsigned __int64& t, const U& u, unsigned __int64& ret)
|
|
{
|
|
return LargeIntRegMultiply< unsigned __int64, signed __int32, E >::RegMultiply(t, (signed __int32)u, ret);
|
|
}
|
|
};
|
|
|
|
template < typename E > class MultiplicationHelper<unsigned __int64, __int64, E, MultiplicationState_Uint64Int64 >
|
|
{
|
|
public:
|
|
static SafeIntError Multiply(const unsigned __int64& t, const __int64& u, unsigned __int64& ret)
|
|
{
|
|
return LargeIntRegMultiply< unsigned __int64, __int64, E >::RegMultiply(t, u, ret);
|
|
}
|
|
};
|
|
|
|
template < typename T, typename E > class MultiplicationHelper< T, __int64, E, MultiplicationState_UintInt64 >
|
|
{
|
|
public:
|
|
//T is unsigned up to 32-bit
|
|
static SafeIntError Multiply( const T& t, const __int64& u, T& ret )
|
|
{
|
|
unsigned __int32 tmp;
|
|
|
|
if( LargeIntRegMultiply< unsigned __int32, __int64, E >::RegMultiply( (unsigned __int32)t, u, tmp ) == SafeIntNoError &&
|
|
SafeCastHelper< T, unsigned __int32, E >::Cast( tmp, ret ) == SafeIntNoError )
|
|
{
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename E > class MultiplicationHelper<__int64, U, E, MultiplicationState_Int64Uint >
|
|
{
|
|
public:
|
|
//U is unsigned up to 32-bit
|
|
static SafeIntError Multiply( const __int64& t, const U& u, __int64& ret )
|
|
{
|
|
return LargeIntRegMultiply< __int64, unsigned __int32, E >::RegMultiply( t, (unsigned __int32)u, ret );
|
|
}
|
|
};
|
|
|
|
template < typename E > class MultiplicationHelper<__int64, __int64, E, MultiplicationState_Int64Int64 >
|
|
{
|
|
public:
|
|
static SafeIntError Multiply( const __int64& t, const __int64& u, __int64& ret )
|
|
{
|
|
return LargeIntRegMultiply< __int64, __int64, E >::RegMultiply( t, u, ret );
|
|
}
|
|
};
|
|
|
|
template < typename U, typename E > class MultiplicationHelper<__int64, U, E, MultiplicationState_Int64Int>
|
|
{
|
|
public:
|
|
//U is signed up to 32-bit
|
|
static SafeIntError Multiply( const __int64& t, U u, __int64& ret )
|
|
{
|
|
return LargeIntRegMultiply< __int64, __int32, E >::RegMultiply( t, (__int32)u, ret );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename E > class MultiplicationHelper< T, unsigned __int64, E, MultiplicationState_IntUint64 >
|
|
{
|
|
public:
|
|
//T is signed up to 32-bit
|
|
static SafeIntError Multiply(T t, const unsigned __int64& u, T& ret)
|
|
{
|
|
__int32 tmp;
|
|
|
|
if( LargeIntRegMultiply< __int32, unsigned __int64, E >::RegMultiply( (__int32)t, u, tmp ) == SafeIntNoError &&
|
|
SafeCastHelper< T, __int32, E >::Cast( tmp, ret ) == SafeIntNoError )
|
|
{
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class MultiplicationHelper<__int64, unsigned __int64, E, MultiplicationState_Int64Uint64>
|
|
{
|
|
public:
|
|
//U is signed up to 32-bit
|
|
static SafeIntError Multiply( const __int64& t, const unsigned __int64& u, __int64& ret )
|
|
{
|
|
return LargeIntRegMultiply< __int64, unsigned __int64, E >::RegMultiply( t, u, ret );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename E > class MultiplicationHelper< T, __int64, E, MultiplicationState_IntInt64>
|
|
{
|
|
public:
|
|
//T is signed, up to 32-bit
|
|
static SafeIntError Multiply( T t, const __int64& u, T& ret )
|
|
{
|
|
__int32 tmp;
|
|
|
|
if( LargeIntRegMultiply< __int32, __int64, E >::RegMultiply( (__int32)t, u, tmp ) == SafeIntNoError &&
|
|
SafeCastHelper< T, __int32, E >::Cast( tmp, ret ) == SafeIntNoError )
|
|
{
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
enum DivisionState
|
|
{
|
|
DivisionState_OK,
|
|
DivisionState_UnsignedSigned,
|
|
DivisionState_SignedUnsigned32,
|
|
DivisionState_SignedUnsigned64,
|
|
DivisionState_SignedUnsigned,
|
|
DivisionState_SignedSigned
|
|
};
|
|
|
|
template < typename T, typename U > class DivisionMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
method = (SafeIntCompare< T, U >::isBothUnsigned ? DivisionState_OK :
|
|
(!IntTraits< T >::isSigned && IntTraits< U >::isSigned) ? DivisionState_UnsignedSigned :
|
|
(IntTraits< T >::isSigned &&
|
|
IntTraits< U >::isUint32 &&
|
|
IntTraits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 :
|
|
(IntTraits< T >::isSigned && IntTraits< U >::isUint64) ? DivisionState_SignedUnsigned64 :
|
|
(IntTraits< T >::isSigned && !IntTraits< U >::isSigned) ? DivisionState_SignedUnsigned :
|
|
DivisionState_SignedSigned)
|
|
};
|
|
};
|
|
|
|
template < typename T, typename U, typename E, int Method = DivisionMethod< T, U >::method > class DivisionHelper;
|
|
|
|
template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_OK >
|
|
{
|
|
public:
|
|
static SafeIntError Divide( const T& t, const U& u, T& result )
|
|
{
|
|
if( u == 0 )
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
result = (T)( t/u );
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_UnsignedSigned>
|
|
{
|
|
public:
|
|
static SafeIntError Divide( const T& t, const U& u, T& result )
|
|
{
|
|
if( u > 0 )
|
|
{
|
|
result = (T)( t/u );
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
if( u == 0 )
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
// it is always an error to try and divide an unsigned number by a negative signed number
|
|
// unless u is bigger than t
|
|
if( AbsValueHelper< U >::Abs( u ) > t )
|
|
{
|
|
result = 0;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_SignedUnsigned32 >
|
|
{
|
|
public:
|
|
static SafeIntError Divide( const T& t, const U& u, T& result )
|
|
{
|
|
if( u == 0 )
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
// Test for t > 0
|
|
// If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors
|
|
// As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional
|
|
if( t > 0 )
|
|
result = (T)( t/u );
|
|
else
|
|
result = (T)( (__int64)t/(__int64)u );
|
|
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename E > class DivisionHelper< T, unsigned __int64, E, DivisionState_SignedUnsigned64 >
|
|
{
|
|
public:
|
|
static SafeIntError Divide( const T& t, const unsigned __int64& u, T& result )
|
|
{
|
|
if( u == 0 )
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
if( u <= (unsigned __int64)IntTraits< T >::maxInt )
|
|
{
|
|
// Else u can safely be cast to T
|
|
#pragma warning(suppress:4127)
|
|
if( sizeof( T ) < sizeof( __int64 ) )
|
|
result = (T)( (int)t/(int)u );
|
|
else
|
|
result = (T)((__int64)t/(__int64)u);
|
|
}
|
|
else // Corner case
|
|
if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt )
|
|
{
|
|
// Min int divided by it's own magnitude is -1
|
|
result = -1;
|
|
}
|
|
else
|
|
{
|
|
result = 0;
|
|
}
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_SignedUnsigned>
|
|
{
|
|
public:
|
|
// T is any signed, U is unsigned and smaller than 32-bit
|
|
// In this case, standard operator casting is correct
|
|
static SafeIntError Divide( const T& t, const U& u, T& result )
|
|
{
|
|
if( u == 0 )
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
result = (T)( t/u );
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_SignedSigned>
|
|
{
|
|
public:
|
|
static SafeIntError Divide( const T& t, const U& u, T& result )
|
|
{
|
|
if( u == 0 )
|
|
{
|
|
E::SafeIntOnDivZero();
|
|
return SafeIntDivideByZero;
|
|
}
|
|
|
|
// Must test for corner case
|
|
if( t == IntTraits< T >::minInt && u == -1 )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
result = (T)( t/u );
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
enum AdditionState
|
|
{
|
|
AdditionState_CastIntCheckMax,
|
|
AdditionState_CastUintCheckOverflow,
|
|
AdditionState_CastUintCheckOverflowMax,
|
|
AdditionState_CastUint64CheckOverflow,
|
|
AdditionState_CastUint64CheckOverflowMax,
|
|
AdditionState_CastIntCheckMinMax,
|
|
AdditionState_CastInt64CheckMinMax,
|
|
AdditionState_CastInt64CheckMax,
|
|
AdditionState_CastUint64CheckMinMax,
|
|
AdditionState_CastUint64CheckMinMax2,
|
|
AdditionState_CastInt64CheckOverflow,
|
|
AdditionState_CastInt64CheckOverflowMinMax,
|
|
AdditionState_CastInt64CheckOverflowMax,
|
|
AdditionState_ManualCheckInt64Uint64,
|
|
AdditionState_ManualCheck,
|
|
AdditionState_Error
|
|
};
|
|
|
|
template< typename T, typename U >
|
|
class AdditionMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
//unsigned-unsigned
|
|
method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax :
|
|
(IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow :
|
|
(IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax :
|
|
(IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow :
|
|
(IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax :
|
|
//unsigned-signed
|
|
(IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Uint32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Uint64_Int ||
|
|
IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckMinMax :
|
|
(IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckMinMax2 :
|
|
//signed-signed
|
|
(IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Int32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Int64_Int ||
|
|
IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow :
|
|
(IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowMinMax :
|
|
//signed-unsigned
|
|
(IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax :
|
|
(IntRegion< T,U >::IntZone_Int32_UintLT32 ||
|
|
IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax :
|
|
(IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax :
|
|
(IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 :
|
|
(IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck :
|
|
AdditionState_Error)
|
|
};
|
|
};
|
|
|
|
template < typename T, typename U, typename E, int Method = AdditionMethod< T, U >::method > class AdditionHelper;
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastIntCheckMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
//16-bit or less unsigned addition
|
|
__int32 tmp = lhs + rhs;
|
|
|
|
if( tmp <= (__int32)IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUintCheckOverflow >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// 32-bit or less - both are unsigned
|
|
unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs;
|
|
|
|
//we added didn't get smaller
|
|
if( tmp >= lhs )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUintCheckOverflowMax>
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// 32-bit or less - both are unsigned
|
|
unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs;
|
|
|
|
// We added and it didn't get smaller or exceed maxInt
|
|
if( tmp >= lhs && tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckOverflow>
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs unsigned __int64, rhs unsigned
|
|
unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
|
|
|
|
// We added and it didn't get smaller
|
|
if(tmp >= lhs)
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckOverflowMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
//lhs unsigned __int64, rhs unsigned
|
|
unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
|
|
|
|
// We added and it didn't get smaller
|
|
if( tmp >= lhs && tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastIntCheckMinMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// 16-bit or less - one or both are signed
|
|
__int32 tmp = lhs + rhs;
|
|
|
|
if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4702)
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckMinMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// 32-bit or less - one or both are signed
|
|
__int64 tmp = (__int64)lhs + (__int64)rhs;
|
|
|
|
if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
// return E::SafeIntOnOverflow2();
|
|
}
|
|
};
|
|
#pragma warning(pop)
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// 32-bit or less - lhs signed, rhs unsigned
|
|
__int64 tmp = (__int64)lhs + (__int64)rhs;
|
|
|
|
if( tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckMinMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is unsigned __int64, rhs signed
|
|
unsigned __int64 tmp;
|
|
|
|
if( rhs < 0 )
|
|
{
|
|
// So we're effectively subtracting
|
|
tmp = AbsValueHelper< U >::Abs( rhs );
|
|
|
|
if( tmp <= lhs )
|
|
{
|
|
result = lhs - tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// now we know that rhs can be safely cast into an unsigned __int64
|
|
tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
|
|
|
|
// We added and it did not become smaller
|
|
if( tmp >= lhs )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckMinMax2>
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is unsigned and < 64-bit, rhs signed __int64
|
|
if( rhs < 0 )
|
|
{
|
|
if( lhs >= (unsigned __int64)( -rhs ) )//negation is safe, since rhs is 64-bit
|
|
{
|
|
result = (T)( lhs + rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// now we know that rhs can be safely cast into an unsigned __int64
|
|
unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs;
|
|
|
|
// special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff
|
|
// it is not possible for the operation above to overflow, so just check max
|
|
if( tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckOverflow>
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is signed __int64, rhs signed
|
|
__int64 tmp = (__int64)lhs + (__int64)rhs;
|
|
|
|
if( lhs >= 0 )
|
|
{
|
|
// mixed sign cannot overflow
|
|
if( rhs >= 0 && tmp < lhs )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// lhs negative
|
|
if( rhs < 0 && tmp > lhs )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
}
|
|
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckOverflowMinMax>
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
//rhs is signed __int64, lhs signed
|
|
__int64 tmp;
|
|
|
|
if( AdditionHelper< __int64, __int64, E, AdditionState_CastInt64CheckOverflow >::
|
|
Addition( (__int64)lhs, (__int64)rhs, tmp ) == SafeIntNoError &&
|
|
tmp <= IntTraits< T >::maxInt &&
|
|
tmp >= IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckOverflowMax >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
//lhs is signed __int64, rhs unsigned < 64-bit
|
|
__int64 tmp = lhs + (__int64)rhs;
|
|
|
|
if( tmp >= lhs )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class AdditionHelper < __int64, unsigned __int64, E, AdditionState_ManualCheckInt64Uint64 >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) throw()
|
|
{
|
|
// rhs is unsigned __int64, lhs __int64
|
|
__int64 tmp = lhs + (__int64)rhs;
|
|
|
|
if( tmp >= lhs )
|
|
{
|
|
result = tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_ManualCheck >
|
|
{
|
|
public:
|
|
static SafeIntError Addition( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// rhs is unsigned __int64, lhs signed, 32-bit or less
|
|
|
|
if( (unsigned __int32)( rhs >> 32 ) == 0 )
|
|
{
|
|
// Now it just happens to work out that the standard behavior does what we want
|
|
// Adding explicit casts to show exactly what's happening here
|
|
__int32 tmp = (__int32)( (unsigned __int32)rhs + (unsigned __int32)lhs );
|
|
|
|
if( tmp >= lhs &&
|
|
SafeCastHelper< T, __int32, E >::Cast( tmp, result ) == SafeIntNoError )
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
enum SubtractionState
|
|
{
|
|
SubtractionState_BothUnsigned,
|
|
SubtractionState_CastIntCheckMinMax,
|
|
SubtractionState_CastIntCheckMin,
|
|
SubtractionState_CastInt64CheckMinMax,
|
|
SubtractionState_CastInt64CheckMin,
|
|
SubtractionState_Uint64Int,
|
|
SubtractionState_UintInt64,
|
|
SubtractionState_Int64Int,
|
|
SubtractionState_IntInt64,
|
|
SubtractionState_Int64Uint,
|
|
SubtractionState_IntUint64,
|
|
SubtractionState_Int64Uint64,
|
|
// states for SubtractionMethod2
|
|
SubtractionState_BothUnsigned2,
|
|
SubtractionState_CastIntCheckMinMax2,
|
|
SubtractionState_CastInt64CheckMinMax2,
|
|
SubtractionState_Uint64Int2,
|
|
SubtractionState_UintInt642,
|
|
SubtractionState_Int64Int2,
|
|
SubtractionState_IntInt642,
|
|
SubtractionState_Int64Uint2,
|
|
SubtractionState_IntUint642,
|
|
SubtractionState_Int64Uint642,
|
|
SubtractionState_Error
|
|
};
|
|
|
|
template < typename T, typename U > class SubtractionMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
// unsigned-unsigned
|
|
method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 ||
|
|
(IntRegion< T,U >::IntZone_Uint32_UintLT64) ||
|
|
(IntRegion< T,U >::IntZone_UintLT32_Uint32) ||
|
|
(IntRegion< T,U >::IntZone_Uint64_Uint) ||
|
|
(IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned :
|
|
// unsigned-signed
|
|
(IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Uint32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Uint64_Int ||
|
|
IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int :
|
|
(IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 :
|
|
// signed-signed
|
|
(IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Int32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckMinMax :
|
|
(IntRegion< T,U >::IntZone_Int64_Int ||
|
|
IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int :
|
|
(IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 :
|
|
// signed-unsigned
|
|
(IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin :
|
|
(IntRegion< T,U >::IntZone_Int32_UintLT32 ||
|
|
IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin :
|
|
(IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint :
|
|
(IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 :
|
|
(IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 :
|
|
SubtractionState_Error)
|
|
};
|
|
};
|
|
|
|
// this is for the case of U - SafeInt< T, E >
|
|
template < typename T, typename U > class SubtractionMethod2
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
// unsigned-unsigned
|
|
method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 ||
|
|
(IntRegion< T,U >::IntZone_Uint32_UintLT64) ||
|
|
(IntRegion< T,U >::IntZone_UintLT32_Uint32) ||
|
|
(IntRegion< T,U >::IntZone_Uint64_Uint) ||
|
|
(IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 :
|
|
// unsigned-signed
|
|
(IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax2 :
|
|
(IntRegion< T,U >::IntZone_Uint32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckMinMax2 :
|
|
(IntRegion< T,U >::IntZone_Uint64_Int ||
|
|
IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 :
|
|
(IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 :
|
|
// signed-signed
|
|
(IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax2 :
|
|
(IntRegion< T,U >::IntZone_Int32_IntLT64 ||
|
|
IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckMinMax2 :
|
|
(IntRegion< T,U >::IntZone_Int64_Int ||
|
|
IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 :
|
|
(IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 :
|
|
// signed-unsigned
|
|
(IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMinMax2 :
|
|
(IntRegion< T,U >::IntZone_Int32_UintLT32 ||
|
|
IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMinMax2 :
|
|
(IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 :
|
|
(IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 :
|
|
(IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 :
|
|
SubtractionState_Error)
|
|
};
|
|
};
|
|
|
|
template < typename T, typename U, typename E, int Method = SubtractionMethod< T, U >::method > class SubtractionHelper;
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_BothUnsigned >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// both are unsigned - easy case
|
|
if( rhs <= lhs )
|
|
{
|
|
result = (T)( lhs - rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_BothUnsigned2 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, U& result )
|
|
{
|
|
// both are unsigned - easy case
|
|
// Except we do have to check for overflow - lhs could be larger than result can hold
|
|
if( rhs <= lhs )
|
|
{
|
|
T tmp = (T)(lhs - rhs);
|
|
return SafeCastHelper< U, T, E>::Cast( tmp, result);
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastIntCheckMinMax >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// both values are 16-bit or less
|
|
// rhs is signed, so could end up increasing or decreasing
|
|
__int32 tmp = lhs - rhs;
|
|
|
|
if( SafeCastHelper< T, __int32, E >::Cast( tmp, result ) == SafeIntNoError )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_CastIntCheckMinMax2 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// both values are 16-bit or less
|
|
// rhs is signed, so could end up increasing or decreasing
|
|
__int32 tmp = lhs - rhs;
|
|
|
|
return SafeCastHelper< T, __int32, E >::Cast( tmp, result );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastIntCheckMin >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// both values are 16-bit or less
|
|
// rhs is unsigned - check only minimum
|
|
__int32 tmp = lhs - rhs;
|
|
|
|
if( tmp >= (__int32)IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastInt64CheckMinMax >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// both values are 32-bit or less
|
|
// rhs is signed, so could end up increasing or decreasing
|
|
__int64 tmp = (__int64)lhs - (__int64)rhs;
|
|
|
|
return SafeCastHelper< T, __int64, E >::Cast( tmp, result );
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_CastInt64CheckMinMax2 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// both values are 32-bit or less
|
|
// rhs is signed, so could end up increasing or decreasing
|
|
__int64 tmp = (__int64)lhs - (__int64)rhs;
|
|
|
|
return SafeCastHelper< T, __int64, E >::Cast( tmp, result );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastInt64CheckMin >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// both values are 32-bit or less
|
|
// rhs is unsigned - check only minimum
|
|
__int64 tmp = (__int64)lhs - (__int64)rhs;
|
|
|
|
if( tmp >= (__int64)IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_Uint64Int >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is an unsigned __int64, rhs signed
|
|
// must first see if rhs is positive or negative
|
|
if( rhs >= 0 )
|
|
{
|
|
if( (unsigned __int64)rhs <= lhs )
|
|
{
|
|
result = (T)( lhs - (unsigned __int64)rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we're now effectively adding
|
|
T tmp = lhs + AbsValueHelper< U >::Abs( rhs );
|
|
|
|
if(tmp >= lhs)
|
|
{
|
|
result = tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_Uint64Int2 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// U is unsigned __int64, T is signed
|
|
if( rhs < 0 )
|
|
{
|
|
// treat this as addition
|
|
unsigned __int64 tmp;
|
|
|
|
tmp = lhs + (unsigned __int64)AbsValueHelper< T >::Abs( rhs );
|
|
|
|
// must check for addition overflow and max
|
|
if( tmp >= lhs && tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works
|
|
{
|
|
// result is negative
|
|
// implies that lhs must fit into T, and result cannot overflow
|
|
// Also allows us to drop to 32-bit math, which is faster on a 32-bit system
|
|
result = (T)lhs - (T)rhs;
|
|
return SafeIntNoError;
|
|
}
|
|
else
|
|
{
|
|
// result is positive
|
|
unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs;
|
|
|
|
if( tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_UintInt64 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is an unsigned int32 or smaller, rhs signed __int64
|
|
// must first see if rhs is positive or negative
|
|
if( rhs >= 0 )
|
|
{
|
|
if( (unsigned __int64)rhs <= lhs )
|
|
{
|
|
result = (T)( lhs - (T)rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we're now effectively adding
|
|
// since lhs is 32-bit, and rhs cannot exceed 2^63
|
|
// this addition cannot overflow
|
|
unsigned __int64 tmp = lhs + (unsigned __int64)( -rhs ); // negation safe
|
|
|
|
// but we could exceed MaxInt
|
|
if(tmp <= IntTraits< T >::maxInt)
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_UintInt642 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// U unsigned 32-bit or less, T __int64
|
|
if( rhs >= 0 )
|
|
{
|
|
// overflow not possible
|
|
result = (T)( (__int64)lhs - rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
else
|
|
{
|
|
// we effectively have an addition
|
|
// which cannot overflow internally
|
|
unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs );
|
|
|
|
if( tmp <= (unsigned __int64)IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_Int64Int >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is an __int64, rhs signed (up to 64-bit)
|
|
// we have essentially 4 cases:
|
|
//
|
|
// 1) lhs positive, rhs positive - overflow not possible
|
|
// 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error
|
|
// 3) lhs negative, rhs positive - check result <= lhs
|
|
// 4) lhs negative, rhs negative - overflow not possible
|
|
|
|
__int64 tmp = lhs - rhs;
|
|
|
|
// Note - ideally, we can order these so that true conditionals
|
|
// lead to success, which enables better pipelining
|
|
// It isn't practical here
|
|
if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2
|
|
( rhs >= 0 && tmp > lhs ) ) // condition 3
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_Int64Int2 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// lhs __int64, rhs any signed int (including __int64)
|
|
__int64 tmp = lhs - rhs;
|
|
|
|
// we have essentially 4 cases:
|
|
//
|
|
// 1) lhs positive, rhs positive - overflow not possible in tmp
|
|
// 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error
|
|
// 3) lhs negative, rhs positive - check result <= lhs
|
|
// 4) lhs negative, rhs negative - overflow not possible in tmp
|
|
|
|
if( lhs >= 0 )
|
|
{
|
|
// if both positive, overflow to negative not possible
|
|
// which is why we'll explicitly check maxInt, and not call SafeCast
|
|
#pragma warning(suppress:4127)
|
|
if( ( IntTraits< T >::isLT64Bit && tmp > IntTraits< T >::maxInt ) ||
|
|
( rhs < 0 && tmp < lhs ) )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// lhs negative
|
|
#pragma warning(suppress:4127)
|
|
if( ( IntTraits< T >::isLT64Bit && tmp < IntTraits< T >::minInt) ||
|
|
( rhs >=0 && tmp > lhs ) )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
}
|
|
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_IntInt64 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is a 32-bit int or less, rhs __int64
|
|
// we have essentially 4 cases:
|
|
//
|
|
// lhs positive, rhs positive - rhs could be larger than lhs can represent
|
|
// lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int
|
|
// lhs negative, rhs positive - check tmp <= lhs and tmp < min int
|
|
// lhs negative, rhs negative - addition cannot internally overflow, check against max
|
|
|
|
__int64 tmp = (__int64)lhs - rhs;
|
|
|
|
if( lhs >= 0 )
|
|
{
|
|
// first case
|
|
if( rhs >= 0 )
|
|
{
|
|
if( tmp >= IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// second case
|
|
if( tmp >= lhs && tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// lhs < 0
|
|
// third case
|
|
if( rhs >= 0 )
|
|
{
|
|
if( tmp <= lhs && tmp >= IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// fourth case
|
|
if( tmp <= IntTraits< T >::maxInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_IntInt642 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// lhs is any signed int32 or smaller, rhs is int64
|
|
__int64 tmp = (__int64)lhs - rhs;
|
|
|
|
if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) ||
|
|
( rhs > 0 && tmp > lhs ) )
|
|
{
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
//else OK
|
|
}
|
|
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_Int64Uint >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is a 64-bit int, rhs unsigned int32 or smaller
|
|
|
|
__int64 tmp = lhs - (__int64)rhs;
|
|
|
|
if( tmp <= lhs )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_Int64Uint2 >
|
|
{
|
|
public:
|
|
// lhs is __int64, rhs is unsigned 32-bit or smaller
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
__int64 tmp = lhs - (__int64)rhs;
|
|
|
|
if( tmp <= IntTraits< T >::maxInt && tmp >= IntTraits< T >::minInt )
|
|
{
|
|
result = (T)tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_IntUint64 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const T& lhs, const U& rhs, T& result )
|
|
{
|
|
// lhs is any signed int, rhs unsigned int64
|
|
// check against available range
|
|
|
|
// We need the absolute value of IntTraits< T >::minInt
|
|
// This will give it to us without extraneous compiler warnings
|
|
const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1;
|
|
|
|
if( lhs < 0 )
|
|
{
|
|
if( rhs <= AbsMinIntT - AbsValueHelper< T >::Abs( lhs ) )
|
|
{
|
|
result = (T)( lhs - rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( rhs <= AbsMinIntT + (unsigned __int64)lhs )
|
|
{
|
|
result = (T)( lhs - rhs );
|
|
return SafeIntNoError;
|
|
}
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_IntUint642 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const U& lhs, const T& rhs, T& result )
|
|
{
|
|
// We run into upcasting problems on comparison - needs 2 checks
|
|
if( lhs >= 0 && (T)lhs >= rhs )
|
|
{
|
|
result = (T)((U)lhs - (U)rhs);
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class SubtractionHelper< __int64, unsigned __int64, E, SubtractionState_Int64Uint64 >
|
|
{
|
|
public:
|
|
static SafeIntError Subtract( const __int64& lhs, const unsigned __int64& rhs, __int64& result )
|
|
{
|
|
// if we subtract, and it gets larger, there's a problem
|
|
__int64 tmp = lhs - (__int64)rhs;
|
|
|
|
if( tmp <= lhs )
|
|
{
|
|
result = tmp;
|
|
return SafeIntNoError;
|
|
}
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
template < typename E > class SubtractionHelper< __int64, unsigned __int64, E, SubtractionState_Int64Uint642 >
|
|
{
|
|
public:
|
|
// If lhs is negative, immediate problem - return must be positive, and subtracting only makes it
|
|
// get smaller. If rhs > lhs, then it would also go negative, which is the other case
|
|
static SafeIntError Subtract( const __int64& lhs, const unsigned __int64& rhs, unsigned __int64& result )
|
|
{
|
|
if( lhs >= 0 && (unsigned __int64)lhs >= rhs )
|
|
{
|
|
result = (unsigned __int64)lhs - rhs;
|
|
return SafeIntNoError;
|
|
}
|
|
|
|
E::SafeIntOnOverflow();
|
|
return SafeIntArithmeticOverflow;
|
|
}
|
|
};
|
|
|
|
enum BinaryState
|
|
{
|
|
BinaryState_OK,
|
|
BinaryState_Int8,
|
|
BinaryState_Int16,
|
|
BinaryState_Int32
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryMethod
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
// If both operands are unsigned OR
|
|
// return type is smaller than rhs OR
|
|
// return type is larger and rhs is unsigned
|
|
// Then binary operations won't produce unexpected results
|
|
method = ( sizeof( T ) <= sizeof( U ) ||
|
|
SafeIntCompare< T, U >::isBothUnsigned ||
|
|
!IntTraits< U >::isSigned ) ? BinaryState_OK :
|
|
IntTraits< U >::isInt8 ? BinaryState_Int8 :
|
|
IntTraits< U >::isInt16 ? BinaryState_Int16
|
|
: BinaryState_Int32
|
|
};
|
|
};
|
|
|
|
template < typename T, typename U, int Method = BinaryMethod< T, U >::method > class BinaryAndHelper;
|
|
|
|
template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK >
|
|
{
|
|
public:
|
|
static T And( T lhs, U rhs ){ return (T)( lhs & rhs ); }
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 >
|
|
{
|
|
public:
|
|
static T And( T lhs, U rhs )
|
|
{
|
|
// cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs & rhs ) == ( lhs & (unsigned __int8)rhs ) );
|
|
return (T)( lhs & (unsigned __int8)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 >
|
|
{
|
|
public:
|
|
static T And( T lhs, U rhs )
|
|
{
|
|
//cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs & rhs ) == ( lhs & (unsigned __int16)rhs ) );
|
|
return (T)( lhs & (unsigned __int16)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 >
|
|
{
|
|
public:
|
|
static T And( T lhs, U rhs )
|
|
{
|
|
//cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs & rhs ) == ( lhs & (unsigned __int32)rhs ) );
|
|
return (T)( lhs & (unsigned __int32)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U, int Method = BinaryMethod< T, U >::method > class BinaryOrHelper;
|
|
|
|
template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK >
|
|
{
|
|
public:
|
|
static T Or( T lhs, U rhs ){ return (T)( lhs | rhs ); }
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 >
|
|
{
|
|
public:
|
|
static T Or( T lhs, U rhs )
|
|
{
|
|
//cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs | rhs ) == ( lhs | (unsigned __int8)rhs ) );
|
|
return (T)( lhs | (unsigned __int8)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 >
|
|
{
|
|
public:
|
|
static T Or( T lhs, U rhs )
|
|
{
|
|
//cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs | rhs ) == ( lhs | (unsigned __int16)rhs ) );
|
|
return (T)( lhs | (unsigned __int16)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 >
|
|
{
|
|
public:
|
|
static T Or( T lhs, U rhs )
|
|
{
|
|
//cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs | rhs ) == ( lhs | (unsigned __int32)rhs ) );
|
|
return (T)( lhs | (unsigned __int32)rhs );
|
|
}
|
|
};
|
|
|
|
template <typename T, typename U, int Method = BinaryMethod< T, U >::method > class BinaryXorHelper;
|
|
|
|
template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK >
|
|
{
|
|
public:
|
|
static T Xor( T lhs, U rhs ){ return (T)( lhs ^ rhs ); }
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 >
|
|
{
|
|
public:
|
|
static T Xor( T lhs, U rhs )
|
|
{
|
|
// cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int8)rhs ) );
|
|
return (T)( lhs ^ (unsigned __int8)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 >
|
|
{
|
|
public:
|
|
static T Xor( T lhs, U rhs )
|
|
{
|
|
// cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int16)rhs ) );
|
|
return (T)( lhs ^ (unsigned __int16)rhs );
|
|
}
|
|
};
|
|
|
|
template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 >
|
|
{
|
|
public:
|
|
static T Xor( T lhs, U rhs )
|
|
{
|
|
// cast forces sign extension to be zeros
|
|
_SAFEINT_BINARY_ASSERT( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int32)rhs ) );
|
|
return (T)( lhs ^ (unsigned __int32)rhs );
|
|
}
|
|
};
|
|
|
|
#pragma warning(pop)
|
|
} // namespace details
|
|
|
|
} // namespace utilities
|
|
|
|
} // namespace msl
|
|
|
|
#pragma pack(pop)
|