@ -42,33 +42,32 @@
namespace Safe
namespace Safe
{
{
/*!
/*!
* @ brief Helper struct s for providing integer overflow checks .
* @ brief Helper function s for providing integer overflow checks .
*
*
* This namespace contains the internal helper structs fallback_add _overflow
* This namespace contains internal helper functions fallback_ $ op _overflow
* and builtin_ add_overflow. Both have a public static member function add
* and builtin_ $ op_overflow ( where $ op is an arithmetic operation like add ,
* with the following interface :
* subtract, etc . ) . Both provide the following interface :
*
*
* bool add( T summand_1 , T summand_2 , T & result )
* bool fallback/ builtin_ $ op_overflow ( T first , T second , T & result ) ;
*
*
* where T is the type over which the struct is templated .
* where T is an integer type .
*
*
* The function performs a check whether the addition summand_1 + summand_2
* Each function performs checks whether first $ op second can be safely
* can be performed without an overflow . If the operation would overflow ,
* performed without overflows . If yes , the result is saved in result and
* true is returned and the addition is not performed if it would result in
* false is returned . Otherwise true is returned and the contents of result
* undefined behavior . If no overflow occurs , the sum is saved in result and
* are unspecified .
* false is returned .
*
*
* fallback_ add _overflow implements a portable but slower overflow check .
* fallback_ $ op _overflow implements a portable but slower overflow check .
* builtin_ add _overflow uses compiler builtins ( when available ) and should
* builtin_ $ op _overflow uses compiler builtins ( when available ) and should
* be considerably faster. As builtins are not available for all types ,
* be faster. As builtins are not available for all types ,
* builtin_ add_overflow falls back to fallback_add _overflow when no builtin
* builtin_ $ op_overflow falls back to fallback_ $ op _overflow when no builtin
* is available .
* is available .
*/
*/
namespace Internal
namespace Internal
{
{
/*!
/*!
* @ brief Helper struct to determine whether a type is signed or unsigned
* @ brief Helper struct to determine whether a type is signed or unsigned
*
* This struct is a backport of std : : is_signed from C + + 11. It has a public
* This struct is a backport of std : : is_signed from C + + 11. It has a public
* enum with the property VALUE which is true when the type is signed or
* enum with the property VALUE which is true when the type is signed or
* false if it is unsigned .
* false if it is unsigned .
@ -103,145 +102,111 @@ namespace Safe
} ;
} ;
/*!
/*!
* @ brief Fallback overflow checker , specialized via SFINAE
* @ brief Check the addition of two numbers for overflows for signed
* integer types larger than int or with the same size as int .
*
*
* This struct implements a ' fallback ' addition with an overflow check ,
* This function performs a check if summand_1 + summand_2 would
* i . e . it does not rely on compiler intrinsics . It is specialized via
* overflow and returns true in that case . If no overflow occurs ,
* SFINAE for signed and unsigned integer types and provides a public
* the sum is saved in result and false is returned .
* static member function add .
*
*/
* @ return true on overflow , false on no overflow
template < typename T , typename = void >
*
struct fallback_add_overflow ;
* @ param [ in ] summand_1 , summand_2 The summands with are added
* @ param [ out ] result Result of the addition , only populated when no
/*!
* overflow occurs .
* @ brief Overload of fallback_add_overflow for signed integer types
*
* larger then int or with the same size as int
* Further information :
* https : //wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
*/
*/
template < typename T >
template < typename T >
struct fallback_add_overflow < T , typename enable_if < is_signed < T > : : VALUE & & sizeof ( T ) > = sizeof ( int ) > : : type >
typename enable_if < is_signed < T > : : VALUE & & sizeof ( T ) > = sizeof ( int ) , bool > : : type fallback_add_overflow (
T summand_1 , T summand_2 , T & result )
{
{
/*!
if ( ( ( summand_2 > = 0 ) & & ( summand_1 > std : : numeric_limits < T > : : max ( ) - summand_2 ) ) | |
* @ brief Adds the two summands only if no overflow occurs
( ( summand_2 < 0 ) & & ( summand_1 < std : : numeric_limits < T > : : min ( ) - summand_2 ) ) ) {
*
return true ;
* This function performs a check if summand_1 + summand_2 would
} else {
* overflow and returns true in that case . If no overflow occurs ,
result = summand_1 + summand_2 ;
* the sum is saved in result and false is returned .
return false ;
*
* @ return true on overflow , false on no overflow
*
* The check for an overflow is performed before the addition to
* ensure that no undefined behavior occurs . The value in result is
* only valid when the function returns false .
*
* Further information :
* https : //wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
*/
static bool add ( T summand_1 , T summand_2 , T & result )
{
if ( ( ( summand_2 > = 0 ) & & ( summand_1 > std : : numeric_limits < T > : : max ( ) - summand_2 ) ) | |
( ( summand_2 < 0 ) & & ( summand_1 < std : : numeric_limits < T > : : min ( ) - summand_2 ) ) ) {
return true ;
} else {
result = summand_1 + summand_2 ;
return false ;
}
}
}
} ;
}
/*!
/*!
* @ brief Overload of fallback_add_overflow for signed integers smaller
* @ brief Check the addition of two numbers for overflows for signed
* then int .
* integer types smaller than int .
*
* This function adds summand_1 and summand_2 exploiting integer
* promotion rules , thereby not causing undefined behavior . The
* result is checked against the limits of T and true is returned if
* they are exceeded . Otherwise the sum is saved in result and false
* is returned .
*
* @ return true on overflow , false on no overflow
*
* @ param [ in ] summand_1 , summand_2 The summands with are added
* @ param [ out ] result Result of the addition , only populated when no
* overflow occurs .
*
* Further information :
* https : //wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules
*/
*/
template < typename T >
template < typename T >
struct fallback_add_overflow < T , typename enable_if < is_signed < T > : : VALUE & & sizeof ( T ) < sizeof ( int ) > : : type >
typename enable_if < is_signed < T > : : VALUE & & sizeof ( T ) < sizeof ( int ) , bool > : : type fallback_add_overflow (
T summand_1 , T summand_2 , T & result )
{
{
/*!
const int res = summand_1 + summand_2 ;
* @ brief Adds the two summands only if no overflow occurs
if ( ( res > std : : numeric_limits < T > : : max ( ) ) | | ( res < std : : numeric_limits < T > : : min ( ) ) ) {
*
return true ;
* This function adds summand_1 and summand_2 exploiting integer
} else {
* promotion rules , thereby not causing undefined behavior . The
result = static_cast < T > ( res ) ;
* result is checked against the limits of T and true is returned if
return false ;
* they are exceeded . Otherwise the sum is saved in result and false
* is returned .
*
* @ return true on overflow , false on no overflow
*
* The value in result is only valid when the function returns
* false .
*
* Further information :
* https : //wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules
*/
static bool add ( T summand_1 , T summand_2 , T & result )
{
const int res = summand_1 + summand_2 ;
if ( ( res > std : : numeric_limits < T > : : max ( ) ) | | ( res < std : : numeric_limits < T > : : min ( ) ) ) {
return true ;
} else {
result = static_cast < T > ( res ) ;
return false ;
}
}
}
} ;
}
/*!
/*!
* @ brief Overload of fallback_add_overflow for unsigned integers
* @ brief Check the addition of two numbers for overflows for unsigned
* integer types .
*
* This function adds summand_1 and summand_2 and checks after that if
* the operation overflowed . Since these are unsigned integers , no
* undefined behavior is invoked .
*
* @ return true on overflow , false on no overflow
*
* @ param [ in ] summand_1 , summand_2 The summands with are added
* @ param [ out ] result Result of the addition
*
* Further information :
* https : //wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
*/
*/
template < typename T >
template < typename T >
struct fallback_add_overflow < T , typename enable_if < ! is_signed < T > : : VALUE > : : type >
typename enable_if < ! is_signed < T > : : VALUE , bool > : : type fallback_add_overflow ( T summand_1 , T summand_2 , T & result )
{
{
/*!
result = summand_1 + summand_2 ;
* @ brief Adds the two summands only if no overflow occurs
return result < summand_1 ;
*
}
* This function performs a check if summand_1 + summand_2 would
* overflow and returns true in that case . If no overflow occurs ,
* the sum is saved in result and false is returned .
*
* @ return true on overflow , false on no overflow
*
* Further information :
* https : //wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
*/
static bool add ( T summand_1 , T summand_2 , T & result )
{
result = summand_1 + summand_2 ;
return result < summand_1 ;
}
} ;
/*!
/*!
* @ brief Overflow checker using compiler intrinsics
* @ brief Overflow addition check using compiler intrinsics .
*
*
* This struct provides an add function with the same interface &
* This function behaves exactly like fallback_add_overflow ( ) but it
* behavior as fallback_add_overload : : add but it relies on compiler
* relies on compiler intrinsics instead . This version should be faster
* intrinsics instead . This version should be considerably faster than
* than the fallback version as it can fully utilize available CPU
* the fallback version as it can fully utilize available CPU
* instructions & the compiler ' s diagnostic .
* instructions & the compiler ' s diagnostic .
*
*
* However , as some compilers don ' t provide intrinsics for certain
* However , as some compilers don ' t provide intrinsics for certain
* types , the default implementation of add is the version from fallback .
* types , the default implementation is the version from fallback .
*
*
* The struct is explicitly specialized for each type via # ifdefs for
* This function is fully specialized for each compiler .
* each compiler .
*/
*/
template < typename T >
template < typename T >
struct builtin_add_overflow
bool builtin_add_overflow ( T summand_1 , T summand_2 , T & result )
{
{
/*!
return fallback_add_overflow ( summand_1 , summand_2 , result ) ;
* @ brief Add summand_1 and summand_2 and check for overflows .
}
*
* This is the default add ( ) function that uses
* fallback_add_overflow < T > : : add ( ) . All specializations must have
* exactly the same interface and behave the same way .
*/
static inline bool add ( T summand_1 , T summand_2 , T & result )
{
return fallback_add_overflow < T > : : add ( summand_1 , summand_2 , result ) ;
}
} ;
# if defined(__GNUC__) || defined(__clang__)
# if defined(__GNUC__) || defined(__clang__)
# if __GNUC__ >= 5
# if __GNUC__ >= 5 || __clang_major__ >= 3
/*!
/*!
* This macro pastes a specialization of builtin_add_overflow using gcc ' s &
* This macro pastes a specialization of builtin_add_overflow using gcc ' s &
@ -253,14 +218,13 @@ namespace Safe
* The intrinsics are documented here :
* The intrinsics are documented here :
* https : //gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#Integer-Overflow-Builtins
* https : //gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#Integer-Overflow-Builtins
*/
*/
# define SPECIALIZE_builtin_add_overflow(type, builtin_name) \
# define SPECIALIZE_builtin_add_overflow(type, builtin_name) \
template < > \
/* Full specialization of builtin_add_overflow for type using the */ \
struct builtin_add_overflow < type > \
/* builtin_name intrinsic */ \
{ \
template < > \
static inline bool add ( type summand_1 , type summand_2 , type & result ) \
bool builtin_add_overflow < type > ( type summand_1 , type summand_2 , type & result ) \
{ \
{ \
return builtin_name ( summand_1 , summand_2 , & result ) ; \
return builtin_name ( summand_1 , summand_2 , & result ) ; \
} \
}
}
SPECIALIZE_builtin_add_overflow ( int , __builtin_sadd_overflow ) ;
SPECIALIZE_builtin_add_overflow ( int , __builtin_sadd_overflow ) ;
@ -272,7 +236,7 @@ namespace Safe
SPECIALIZE_builtin_add_overflow ( unsigned long long , __builtin_uaddll_overflow ) ;
SPECIALIZE_builtin_add_overflow ( unsigned long long , __builtin_uaddll_overflow ) ;
# undef SPECIALIZE_builtin_add_overflow
# undef SPECIALIZE_builtin_add_overflow
# endif
# endif // __GNUC__ >= 5 || __clang_major >= 3
# elif defined(_MSC_VER)
# elif defined(_MSC_VER)
@ -289,14 +253,11 @@ namespace Safe
* The intrinsics are documented here :
* The intrinsics are documented here :
* https : //msdn.microsoft.com/en-us/library/windows/desktop/ff516460(v=vs.85).aspx
* https : //msdn.microsoft.com/en-us/library/windows/desktop/ff516460(v=vs.85).aspx
*/
*/
# define SPECIALIZE_builtin_add_overflow_WIN(type, builtin_name) \
# define SPECIALIZE_builtin_add_overflow_WIN(type, builtin_name) \
template < > \
template < > \
struct builtin_add_overflow < type > \
bool builtin_add_overflow ( type summand_1 , type summand_2 , type & result ) \
{ \
{ \
static inline bool add ( type summand_1 , type summand_2 , type & result ) \
return builtin_name ( summand_1 , summand_2 , & result ) ! = S_OK ; \
{ \
return builtin_name ( summand_1 , summand_2 , & result ) ! = S_OK ; \
} \
}
}
SPECIALIZE_builtin_add_overflow_WIN ( unsigned int , UIntAdd ) ;
SPECIALIZE_builtin_add_overflow_WIN ( unsigned int , UIntAdd ) ;
@ -305,7 +266,7 @@ namespace Safe
# undef SPECIALIZE_builtin_add_overflow_WIN
# undef SPECIALIZE_builtin_add_overflow_WIN
# endif
# endif // defined(_MSC_VER)
} // namespace Internal
} // namespace Internal
@ -332,7 +293,7 @@ namespace Safe
T add ( T summand_1 , T summand_2 )
T add ( T summand_1 , T summand_2 )
{
{
T res = 0 ;
T res = 0 ;
if ( Internal : : builtin_add_overflow < T > : : add ( summand_1 , summand_2 , res ) ) {
if ( Internal : : builtin_add_overflow ( summand_1 , summand_2 , res ) ) {
throw std : : overflow_error ( " Overflow in addition " ) ;
throw std : : overflow_error ( " Overflow in addition " ) ;
}
}
return res ;
return res ;