Bug 732875 - 2/8 - Let CheckedInt support the 3 families of integer types: stdint, standard integer types, and PR types - r=jwalden

This commit is contained in:
Benoit Jacob 2012-05-14 15:50:19 -04:00
Родитель c170d6091a
Коммит 534e463c32
2 изменённых файлов: 344 добавлений и 258 удалений

Просмотреть файл

@ -42,6 +42,8 @@
#include "prtypes.h"
#include <mozilla/Assertions.h>
#include <climits>
namespace mozilla {
@ -55,80 +57,121 @@ namespace CheckedInt_internal {
* helps much with twice_bigger_type.
*/
/*** Step 1: manually record information for all the types that we want to support
/*** Step 1: manually record supported types
***
*** What's nontrivial here is that there are different families of integer types: plain integer types, stdint types,
*** and PR types. It is merrily undefined which types from one family may be just typedefs for a type from another family.
***
*** For example, on GCC 4.6, aside from the 10 'standard types' (including long long types), the only other type
*** that isn't just a typedef for some of them, is int8_t.
***/
struct unsupported_type {};
template<typename T> struct integer_type_manually_recorded_info
{
enum { is_supported = 0 };
typedef unsupported_type twice_bigger_type;
typedef unsupported_type unsigned_type;
template<typename integer_type> struct is_supported_pass_3 {
enum { value = 0 };
};
template<typename integer_type> struct is_supported_pass_2 {
enum { value = is_supported_pass_3<integer_type>::value };
};
template<typename integer_type> struct is_supported {
enum { value = is_supported_pass_2<integer_type>::value };
};
template<> struct is_supported<int8_t> { enum { value = 1 }; };
template<> struct is_supported<uint8_t> { enum { value = 1 }; };
template<> struct is_supported<int16_t> { enum { value = 1 }; };
template<> struct is_supported<uint16_t> { enum { value = 1 }; };
template<> struct is_supported<int32_t> { enum { value = 1 }; };
template<> struct is_supported<uint32_t> { enum { value = 1 }; };
template<> struct is_supported<int64_t> { enum { value = 1 }; };
template<> struct is_supported<uint64_t> { enum { value = 1 }; };
#define CHECKEDINT_REGISTER_SUPPORTED_TYPE(T,_twice_bigger_type,_unsigned_type) \
template<> struct integer_type_manually_recorded_info<T> \
{ \
enum { is_supported = 1 }; \
typedef _twice_bigger_type twice_bigger_type; \
typedef _unsigned_type unsigned_type; \
static void TYPE_NOT_SUPPORTED_BY_CheckedInt() {} \
};
template<> struct is_supported_pass_2<char> { enum { value = 1 }; };
template<> struct is_supported_pass_2<unsigned char> { enum { value = 1 }; };
template<> struct is_supported_pass_2<short> { enum { value = 1 }; };
template<> struct is_supported_pass_2<unsigned short> { enum { value = 1 }; };
template<> struct is_supported_pass_2<int> { enum { value = 1 }; };
template<> struct is_supported_pass_2<unsigned int> { enum { value = 1 }; };
template<> struct is_supported_pass_2<long> { enum { value = 1 }; };
template<> struct is_supported_pass_2<unsigned long> { enum { value = 1 }; };
template<> struct is_supported_pass_2<long long> { enum { value = 1 }; };
template<> struct is_supported_pass_2<unsigned long long> { enum { value = 1 }; };
// Type Twice Bigger Type Unsigned Type
CHECKEDINT_REGISTER_SUPPORTED_TYPE(int8_t, int16_t, uint8_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint8_t, uint16_t, uint8_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(int16_t, int32_t, uint16_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint16_t, uint32_t, uint16_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(int32_t, int64_t, uint32_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint32_t, uint64_t, uint32_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(int64_t, unsupported_type, uint64_t)
CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint64_t, unsupported_type, uint64_t)
template<> struct is_supported_pass_3<PRInt8> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRUint8> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRInt16> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRUint16> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRInt32> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRUint32> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRInt64> { enum { value = 1 }; };
template<> struct is_supported_pass_3<PRUint64> { enum { value = 1 }; };
/*** Step 2: record some info about a given integer type,
*** including whether it is supported, whether a twice bigger integer type
*** is supported, what that twice bigger type is, and some stuff as found
*** in std::numeric_limits (which we don't use because PRInt.. types may
*** not support it, if they are defined directly from compiler built-in types).
*** We use function names min_value() and max_value() instead of min() and max()
*** because of stupid min/max macros in Windows headers.
/*** Step 2: some integer-traits kind of stuff. We're doing our own thing here rather than
*** relying on std::numeric_limits mostly for historical reasons (we still support PR integer types
*** which might still be different types e.g. typedefs for some built-in types). Eventually, a patch
*** replacing some of that by std::numeric_limits should be welcome.
***/
template<typename T> struct is_unsupported_type { enum { answer = 0 }; };
template<> struct is_unsupported_type<unsupported_type> { enum { answer = 1 }; };
template<int size, bool signedness> struct stdint_type_for_size_and_signedness {};
template<> struct stdint_type_for_size_and_signedness<1, true> { typedef int8_t type; };
template<> struct stdint_type_for_size_and_signedness<1, false> { typedef uint8_t type; };
template<> struct stdint_type_for_size_and_signedness<2, true> { typedef int16_t type; };
template<> struct stdint_type_for_size_and_signedness<2, false> { typedef uint16_t type; };
template<> struct stdint_type_for_size_and_signedness<4, true> { typedef int32_t type; };
template<> struct stdint_type_for_size_and_signedness<4, false> { typedef uint32_t type; };
template<> struct stdint_type_for_size_and_signedness<8, true> { typedef int64_t type; };
template<> struct stdint_type_for_size_and_signedness<8, false> { typedef uint64_t type; };
template<typename T> struct integer_traits
template<typename integer_type> struct unsigned_type {
typedef typename stdint_type_for_size_and_signedness<sizeof(integer_type), false>::type type;
};
template<typename integer_type> struct is_signed {
enum { value = integer_type(-1) <= integer_type(0) };
};
template<typename integer_type, int size=sizeof(integer_type)>
struct twice_bigger_type {
typedef typename stdint_type_for_size_and_signedness<
sizeof(integer_type) * 2,
is_signed<integer_type>::value
>::type type;
};
template<typename integer_type>
struct twice_bigger_type<integer_type, 8> {
typedef unsupported_type type;
};
template<typename integer_type> struct position_of_sign_bit
{
typedef typename integer_type_manually_recorded_info<T>::twice_bigger_type twice_bigger_type;
typedef typename integer_type_manually_recorded_info<T>::unsigned_type unsigned_type;
enum {
is_supported = integer_type_manually_recorded_info<T>::is_supported,
twice_bigger_type_is_supported
= is_unsupported_type<
typename integer_type_manually_recorded_info<T>::twice_bigger_type
>::answer ? 0 : 1,
size = sizeof(T),
position_of_sign_bit = CHAR_BIT * size - 1,
is_signed = (T(-1) > T(0)) ? 0 : 1
value = CHAR_BIT * sizeof(integer_type) - 1
};
};
static T min_value()
template<typename integer_type> struct min_value
{
static integer_type value()
{
// bitwise ops may return a larger type, that's why we cast explicitly to T
// in C++, left bit shifts on signed values is undefined by the standard unless the shifted value is representable.
// notice that signed-to-unsigned conversions are always well-defined in the standard,
// as the value congruent to 2^n as expected. By contrast, unsigned-to-signed is only well-defined if the value is
// representable.
return is_signed ? T(unsigned_type(1) << position_of_sign_bit) : T(0);
return is_signed<integer_type>::value
? integer_type(typename unsigned_type<integer_type>::type(1) << position_of_sign_bit<integer_type>::value)
: integer_type(0);
}
};
static T max_value()
template<typename integer_type> struct max_value
{
static integer_type value()
{
return ~min_value();
return ~min_value<integer_type>::value();
}
};
@ -144,8 +187,7 @@ template<typename T> inline T has_sign_bit(T x)
// notice that signed-to-unsigned conversions are always well-defined in the standard,
// as the value congruent modulo 2^n as expected. By contrast, unsigned-to-signed is only well-defined if the value is
// representable. Here the unsigned-to-signed conversion is OK because the value (the result of the shift) is 0 or 1.
typedef typename integer_traits<T>::unsigned_type unsigned_T;
return T(unsigned_T(x) >> integer_traits<T>::position_of_sign_bit);
return T(typename unsigned_type<T>::type(x) >> position_of_sign_bit<T>::value);
}
template<typename T> inline T binary_complement(T x)
@ -154,8 +196,8 @@ template<typename T> inline T binary_complement(T x)
}
template<typename T, typename U,
bool is_T_signed = integer_traits<T>::is_signed,
bool is_U_signed = integer_traits<U>::is_signed>
bool is_T_signed = is_signed<T>::value,
bool is_U_signed = is_signed<U>::value>
struct is_in_range_impl {};
template<typename T, typename U>
@ -163,8 +205,8 @@ struct is_in_range_impl<T, U, true, true>
{
static T run(U x)
{
return (x <= integer_traits<T>::max_value()) &&
(x >= integer_traits<T>::min_value());
return (x <= max_value<T>::value()) &&
(x >= min_value<T>::value());
}
};
@ -173,7 +215,7 @@ struct is_in_range_impl<T, U, false, false>
{
static T run(U x)
{
return x <= integer_traits<T>::max_value();
return x <= max_value<T>::value();
}
};
@ -185,7 +227,7 @@ struct is_in_range_impl<T, U, true, false>
if (sizeof(T) > sizeof(U))
return 1;
else
return x <= U(integer_traits<T>::max_value());
return x <= U(max_value<T>::value());
}
};
@ -197,7 +239,7 @@ struct is_in_range_impl<T, U, false, true>
if (sizeof(T) >= sizeof(U))
return x >= 0;
else
return (x >= 0) && (x <= U(integer_traits<T>::max_value()));
return (x >= 0) && (x <= U(max_value<T>::value()));
}
};
@ -208,27 +250,27 @@ template<typename T, typename U> inline T is_in_range(U x)
template<typename T> inline T is_add_valid(T x, T y, T result)
{
return integer_traits<T>::is_signed ?
// addition is valid if the sign of x+y is equal to either that of x or that of y.
// Beware! These bitwise operations can return a larger integer type, if T was a
// small type like int8, so we explicitly cast to T.
has_sign_bit(binary_complement(T((result^x) & (result^y))))
:
binary_complement(x) >= y;
return is_signed<T>::value ?
// addition is valid if the sign of x+y is equal to either that of x or that of y.
// Beware! These bitwise operations can return a larger integer type, if T was a
// small type like int8, so we explicitly cast to T.
has_sign_bit(binary_complement(T((result^x) & (result^y))))
:
binary_complement(x) >= y;
}
template<typename T> inline T is_sub_valid(T x, T y, T result)
{
return integer_traits<T>::is_signed ?
// substraction is valid if either x and y have same sign, or x-y and x have same sign
has_sign_bit(binary_complement(T((result^x) & (x^y))))
:
x >= y;
return is_signed<T>::value ?
// substraction is valid if either x and y have same sign, or x-y and x have same sign
has_sign_bit(binary_complement(T((result^x) & (x^y))))
:
x >= y;
}
template<typename T,
bool is_signed = integer_traits<T>::is_signed,
bool twice_bigger_type_is_supported = integer_traits<T>::twice_bigger_type_is_supported>
bool is_signed = is_signed<T>::value,
bool twice_bigger_type_is_supported = is_supported<typename twice_bigger_type<T>::type>::value>
struct is_mul_valid_impl {};
template<typename T, bool is_signed>
@ -236,7 +278,7 @@ struct is_mul_valid_impl<T, is_signed, true>
{
static T run(T x, T y)
{
typedef typename integer_traits<T>::twice_bigger_type twice_bigger_type;
typedef typename twice_bigger_type<T>::type twice_bigger_type;
twice_bigger_type product = twice_bigger_type(x) * twice_bigger_type(y);
return is_in_range<T>(product);
}
@ -247,21 +289,21 @@ struct is_mul_valid_impl<T, true, false>
{
static T run(T x, T y)
{
const T max_value = integer_traits<T>::max_value();
const T min_value = integer_traits<T>::min_value();
const T max = max_value<T>::value();
const T min = min_value<T>::value();
if (x == 0 || y == 0) return true;
if (x > 0) {
if (y > 0)
return x <= max_value / y;
return x <= max / y;
else
return y >= min_value / x;
return y >= min / x;
} else {
if (y > 0)
return x >= min_value / y;
return x >= min / y;
else
return y >= max_value / x;
return y >= max / x;
}
}
};
@ -271,9 +313,9 @@ struct is_mul_valid_impl<T, false, false>
{
static T run(T x, T y)
{
const T max_value = integer_traits<T>::max_value();
const T max = max_value<T>::value();
if (x == 0 || y == 0) return true;
return x <= max_value / y;
return x <= max / y;
}
};
@ -284,15 +326,15 @@ template<typename T> inline T is_mul_valid(T x, T y, T /*result not used*/)
template<typename T> inline T is_div_valid(T x, T y)
{
return integer_traits<T>::is_signed ?
// keep in mind that min/-1 is invalid because abs(min)>max
(y != 0) && (x != integer_traits<T>::min_value() || y != T(-1))
:
y != 0;
return is_signed<T>::value ?
// keep in mind that min/-1 is invalid because abs(min)>max
(y != 0) && (x != min_value<T>::value() || y != T(-1))
:
y != 0;
}
// this is just to shut up msvc warnings about negating unsigned ints.
template<typename T, bool is_signed = integer_traits<T>::is_signed>
template<typename T, bool is_signed = is_signed<T>::value>
struct opposite_if_signed_impl
{
static T run(T x) { return -x; }
@ -374,8 +416,7 @@ protected:
template<typename U>
CheckedInt(U value, T isValid) : mValue(value), mIsValid(isValid)
{
CheckedInt_internal::integer_type_manually_recorded_info<T>
::TYPE_NOT_SUPPORTED_BY_CheckedInt();
MOZ_STATIC_ASSERT(CheckedInt_internal::is_supported<T>::value, "This type is not supported by CheckedInt");
}
public:
@ -392,15 +433,13 @@ public:
: mValue(T(value)),
mIsValid(CheckedInt_internal::is_in_range<T>(value))
{
CheckedInt_internal::integer_type_manually_recorded_info<T>
::TYPE_NOT_SUPPORTED_BY_CheckedInt();
MOZ_STATIC_ASSERT(CheckedInt_internal::is_supported<T>::value, "This type is not supported by CheckedInt");
}
/** Constructs a valid checked integer with initial value 0 */
CheckedInt() : mValue(0), mIsValid(1)
{
CheckedInt_internal::integer_type_manually_recorded_info<T>
::TYPE_NOT_SUPPORTED_BY_CheckedInt();
MOZ_STATIC_ASSERT(CheckedInt_internal::is_supported<T>::value, "This type is not supported by CheckedInt");
}
/** \returns the actual value */

Просмотреть файл

@ -45,12 +45,13 @@ namespace CheckedInt_test {
using namespace mozilla::CheckedInt_internal;
using mozilla::CheckedInt;
int g_integer_types_tested = 0;
int g_tests_passed = 0;
int g_tests_failed = 0;
void verify_impl_function(bool x, bool expected,
const char* file, int line,
int T_size, bool T_is_signed)
int T_size, bool T_is_T_signed)
{
if (x == expected) {
g_tests_passed++;
@ -58,7 +59,7 @@ void verify_impl_function(bool x, bool expected,
g_tests_failed++;
std::cerr << "Test failed at " << file << ":" << line;
std::cerr << " with T a ";
if(T_is_signed)
if(T_is_T_signed)
std::cerr << "signed";
else
std::cerr << "unsigned";
@ -67,7 +68,7 @@ void verify_impl_function(bool x, bool expected,
}
#define VERIFY_IMPL(x, expected) \
verify_impl_function((x), (expected), __FILE__, __LINE__, sizeof(T), integer_traits<T>::is_signed)
verify_impl_function((x), (expected), __FILE__, __LINE__, sizeof(T), is_signed<T>::value)
#define VERIFY(x) VERIFY_IMPL(x, true)
#define VERIFY_IS_FALSE(x) VERIFY_IMPL(x, false)
@ -80,12 +81,11 @@ struct test_twice_bigger_type
{
static void run()
{
VERIFY(integer_traits<T>::twice_bigger_type_is_supported);
VERIFY(sizeof(typename integer_traits<T>::twice_bigger_type)
== 2 * sizeof(T));
VERIFY(bool(integer_traits<
typename integer_traits<T>::twice_bigger_type
>::is_signed) == bool(integer_traits<T>::is_signed));
VERIFY(is_supported<typename twice_bigger_type<T>::type>::value);
VERIFY(sizeof(typename twice_bigger_type<T>::type)
== 2 * sizeof(T));
VERIFY(bool(is_signed<typename twice_bigger_type<T>::type>::value)
== bool(is_signed<T>::value));
}
};
@ -94,7 +94,7 @@ struct test_twice_bigger_type<T, 8>
{
static void run()
{
VERIFY_IS_FALSE(integer_traits<T>::twice_bigger_type_is_supported);
VERIFY_IS_FALSE(is_supported<typename twice_bigger_type<T>::type>::value);
}
};
@ -103,45 +103,44 @@ template<typename T>
void test()
{
static bool already_run = false;
if (already_run) {
g_tests_failed++;
std::cerr << "You already tested this type. Copy/paste typo??" << std::endl;
// integer types from different families may just be typedefs for types from other families.
// e.g. int32_t might be just a typedef for int. No point re-running the same tests then.
if (already_run)
return;
}
already_run = true;
g_integer_types_tested++;
VERIFY(integer_traits<T>::is_supported);
VERIFY(integer_traits<T>::size == sizeof(T));
enum{ is_signed = integer_traits<T>::is_signed };
VERIFY(bool(is_signed) == !bool(T(-1) > T(0)));
VERIFY(is_supported<T>::value);
enum{ is_T_signed = is_signed<T>::value };
VERIFY(bool(is_T_signed) == !bool(T(-1) > T(0)));
test_twice_bigger_type<T>::run();
typedef typename integer_traits<T>::unsigned_type unsigned_T;
typedef typename unsigned_type<T>::type unsigned_T;
VERIFY(sizeof(unsigned_T) == sizeof(T));
VERIFY(integer_traits<unsigned_T>::is_signed == false);
VERIFY(is_signed<unsigned_T>::value == false);
CheckedInt<T> max_value(integer_traits<T>::max_value());
CheckedInt<T> min_value(integer_traits<T>::min_value());
const CheckedInt<T> max(max_value<T>::value());
const CheckedInt<T> min(min_value<T>::value());
// check min_value() and max_value(), since they are custom implementations and a mistake there
// check min() and max(), since they are custom implementations and a mistake there
// could potentially NOT be caught by any other tests... while making everything wrong!
T bit = 1;
for(unsigned int i = 0; i < sizeof(T) * CHAR_BIT - 1; i++)
{
VERIFY((min_value.value() & bit) == 0);
VERIFY((min.value() & bit) == 0);
bit <<= 1;
}
VERIFY((min_value.value() & bit) == (is_signed ? bit : T(0)));
VERIFY(max_value.value() == T(~(min_value.value())));
VERIFY((min.value() & bit) == (is_T_signed ? bit : T(0)));
VERIFY(max.value() == T(~(min.value())));
CheckedInt<T> zero(0);
CheckedInt<T> one(1);
CheckedInt<T> two(2);
CheckedInt<T> three(3);
CheckedInt<T> four(4);
const CheckedInt<T> zero(0);
const CheckedInt<T> one(1);
const CheckedInt<T> two(2);
const CheckedInt<T> three(3);
const CheckedInt<T> four(4);
/* addition / substraction checks */
@ -153,77 +152,77 @@ void test()
VERIFY_IS_VALID(one+one);
VERIFY(one+one == two);
CheckedInt<T> max_value_minus_one = max_value - one;
CheckedInt<T> max_value_minus_two = max_value - two;
VERIFY_IS_VALID(max_value_minus_one);
VERIFY_IS_VALID(max_value_minus_two);
VERIFY_IS_VALID(max_value_minus_one + one);
VERIFY_IS_VALID(max_value_minus_two + one);
VERIFY_IS_VALID(max_value_minus_two + two);
VERIFY(max_value_minus_one + one == max_value);
VERIFY(max_value_minus_two + one == max_value_minus_one);
VERIFY(max_value_minus_two + two == max_value);
const CheckedInt<T> max_minus_one = max - one;
const CheckedInt<T> max_minus_two = max - two;
VERIFY_IS_VALID(max_minus_one);
VERIFY_IS_VALID(max_minus_two);
VERIFY_IS_VALID(max_minus_one + one);
VERIFY_IS_VALID(max_minus_two + one);
VERIFY_IS_VALID(max_minus_two + two);
VERIFY(max_minus_one + one == max);
VERIFY(max_minus_two + one == max_minus_one);
VERIFY(max_minus_two + two == max);
VERIFY_IS_VALID(max_value + zero);
VERIFY_IS_VALID(max_value - zero);
VERIFY_IS_INVALID(max_value + one);
VERIFY_IS_INVALID(max_value + two);
VERIFY_IS_INVALID(max_value + max_value_minus_one);
VERIFY_IS_INVALID(max_value + max_value);
VERIFY_IS_VALID(max + zero);
VERIFY_IS_VALID(max - zero);
VERIFY_IS_INVALID(max + one);
VERIFY_IS_INVALID(max + two);
VERIFY_IS_INVALID(max + max_minus_one);
VERIFY_IS_INVALID(max + max);
CheckedInt<T> min_value_plus_one = min_value + one;
CheckedInt<T> min_value_plus_two = min_value + two;
VERIFY_IS_VALID(min_value_plus_one);
VERIFY_IS_VALID(min_value_plus_two);
VERIFY_IS_VALID(min_value_plus_one - one);
VERIFY_IS_VALID(min_value_plus_two - one);
VERIFY_IS_VALID(min_value_plus_two - two);
VERIFY(min_value_plus_one - one == min_value);
VERIFY(min_value_plus_two - one == min_value_plus_one);
VERIFY(min_value_plus_two - two == min_value);
const CheckedInt<T> min_plus_one = min + one;
const CheckedInt<T> min_plus_two = min + two;
VERIFY_IS_VALID(min_plus_one);
VERIFY_IS_VALID(min_plus_two);
VERIFY_IS_VALID(min_plus_one - one);
VERIFY_IS_VALID(min_plus_two - one);
VERIFY_IS_VALID(min_plus_two - two);
VERIFY(min_plus_one - one == min);
VERIFY(min_plus_two - one == min_plus_one);
VERIFY(min_plus_two - two == min);
CheckedInt<T> min_value_minus_one = min_value - one;
VERIFY_IS_VALID(min_value + zero);
VERIFY_IS_VALID(min_value - zero);
VERIFY_IS_INVALID(min_value - one);
VERIFY_IS_INVALID(min_value - two);
VERIFY_IS_INVALID(min_value - min_value_minus_one);
VERIFY_IS_VALID(min_value - min_value);
const CheckedInt<T> min_minus_one = min - one;
VERIFY_IS_VALID(min + zero);
VERIFY_IS_VALID(min - zero);
VERIFY_IS_INVALID(min - one);
VERIFY_IS_INVALID(min - two);
VERIFY_IS_INVALID(min - min_minus_one);
VERIFY_IS_VALID(min - min);
CheckedInt<T> max_value_over_two = max_value / two;
VERIFY_IS_VALID(max_value_over_two + max_value_over_two);
VERIFY_IS_VALID(max_value_over_two + one);
VERIFY((max_value_over_two + one) - one == max_value_over_two);
VERIFY_IS_VALID(max_value_over_two - max_value_over_two);
VERIFY(max_value_over_two - max_value_over_two == zero);
const CheckedInt<T> max_over_two = max / two;
VERIFY_IS_VALID(max_over_two + max_over_two);
VERIFY_IS_VALID(max_over_two + one);
VERIFY((max_over_two + one) - one == max_over_two);
VERIFY_IS_VALID(max_over_two - max_over_two);
VERIFY(max_over_two - max_over_two == zero);
CheckedInt<T> min_value_over_two = min_value / two;
VERIFY_IS_VALID(min_value_over_two + min_value_over_two);
VERIFY_IS_VALID(min_value_over_two + one);
VERIFY((min_value_over_two + one) - one == min_value_over_two);
VERIFY_IS_VALID(min_value_over_two - min_value_over_two);
VERIFY(min_value_over_two - min_value_over_two == zero);
const CheckedInt<T> min_over_two = min / two;
VERIFY_IS_VALID(min_over_two + min_over_two);
VERIFY_IS_VALID(min_over_two + one);
VERIFY((min_over_two + one) - one == min_over_two);
VERIFY_IS_VALID(min_over_two - min_over_two);
VERIFY(min_over_two - min_over_two == zero);
VERIFY_IS_INVALID(min_value - one);
VERIFY_IS_INVALID(min_value - two);
VERIFY_IS_INVALID(min - one);
VERIFY_IS_INVALID(min - two);
if (is_signed) {
VERIFY_IS_INVALID(min_value + min_value);
VERIFY_IS_INVALID(min_value_over_two + min_value_over_two + min_value_over_two);
VERIFY_IS_INVALID(zero - min_value + min_value);
VERIFY_IS_INVALID(one - min_value + min_value);
if (is_T_signed) {
VERIFY_IS_INVALID(min + min);
VERIFY_IS_INVALID(min_over_two + min_over_two + min_over_two);
VERIFY_IS_INVALID(zero - min + min);
VERIFY_IS_INVALID(one - min + min);
}
/* unary operator- checks */
CheckedInt<T> neg_one = -one;
CheckedInt<T> neg_two = -two;
const CheckedInt<T> neg_one = -one;
const CheckedInt<T> neg_two = -two;
if (is_signed) {
VERIFY_IS_VALID(-max_value);
VERIFY_IS_VALID(-max_value - one);
if (is_T_signed) {
VERIFY_IS_VALID(-max);
VERIFY_IS_VALID(-max - one);
VERIFY_IS_VALID(neg_one);
VERIFY_IS_VALID(-max_value + neg_one);
VERIFY_IS_VALID(-max + neg_one);
VERIFY_IS_VALID(neg_one + one);
VERIFY(neg_one + one == zero);
VERIFY_IS_VALID(neg_two);
@ -248,77 +247,77 @@ void test()
VERIFY_IS_VALID(two*two);
VERIFY(two*two == four);
VERIFY_IS_INVALID(max_value * max_value);
VERIFY_IS_INVALID(max_value_over_two * max_value);
VERIFY_IS_INVALID(max_value_over_two * max_value_over_two);
VERIFY_IS_INVALID(max * max);
VERIFY_IS_INVALID(max_over_two * max);
VERIFY_IS_INVALID(max_over_two * max_over_two);
CheckedInt<T> max_value_approx_sqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2)));
const CheckedInt<T> max_approx_sqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2)));
VERIFY_IS_VALID(max_value_approx_sqrt);
VERIFY_IS_VALID(max_value_approx_sqrt * two);
VERIFY_IS_INVALID(max_value_approx_sqrt * max_value_approx_sqrt);
VERIFY_IS_INVALID(max_value_approx_sqrt * max_value_approx_sqrt * max_value_approx_sqrt);
VERIFY_IS_VALID(max_approx_sqrt);
VERIFY_IS_VALID(max_approx_sqrt * two);
VERIFY_IS_INVALID(max_approx_sqrt * max_approx_sqrt);
VERIFY_IS_INVALID(max_approx_sqrt * max_approx_sqrt * max_approx_sqrt);
if (is_signed) {
VERIFY_IS_INVALID(min_value * min_value);
VERIFY_IS_INVALID(min_value_over_two * min_value);
VERIFY_IS_INVALID(min_value_over_two * min_value_over_two);
if (is_T_signed) {
VERIFY_IS_INVALID(min * min);
VERIFY_IS_INVALID(min_over_two * min);
VERIFY_IS_INVALID(min_over_two * min_over_two);
CheckedInt<T> min_value_approx_sqrt = -max_value_approx_sqrt;
const CheckedInt<T> min_approx_sqrt = -max_approx_sqrt;
VERIFY_IS_VALID(min_value_approx_sqrt);
VERIFY_IS_VALID(min_value_approx_sqrt * two);
VERIFY_IS_INVALID(min_value_approx_sqrt * max_value_approx_sqrt);
VERIFY_IS_INVALID(min_value_approx_sqrt * min_value_approx_sqrt);
VERIFY_IS_VALID(min_approx_sqrt);
VERIFY_IS_VALID(min_approx_sqrt * two);
VERIFY_IS_INVALID(min_approx_sqrt * max_approx_sqrt);
VERIFY_IS_INVALID(min_approx_sqrt * min_approx_sqrt);
}
// make sure to check all 4 paths in signed multiplication validity check.
// test positive * positive
VERIFY_IS_VALID(max_value * one);
VERIFY(max_value * one == max_value);
VERIFY_IS_INVALID(max_value * two);
VERIFY_IS_VALID(max_value_over_two * two);
VERIFY((max_value_over_two + max_value_over_two) == (max_value_over_two * two));
VERIFY_IS_VALID(max * one);
VERIFY(max * one == max);
VERIFY_IS_INVALID(max * two);
VERIFY_IS_VALID(max_over_two * two);
VERIFY((max_over_two + max_over_two) == (max_over_two * two));
if (is_signed) {
if (is_T_signed) {
// test positive * negative
VERIFY_IS_VALID(max_value * neg_one);
VERIFY_IS_VALID(-max_value);
VERIFY(max_value * neg_one == -max_value);
VERIFY_IS_VALID(one * min_value);
VERIFY_IS_INVALID(max_value * neg_two);
VERIFY_IS_VALID(max_value_over_two * neg_two);
VERIFY_IS_VALID(two * min_value_over_two);
VERIFY_IS_VALID((max_value_over_two + one) * neg_two);
VERIFY_IS_INVALID((max_value_over_two + two) * neg_two);
VERIFY_IS_INVALID(two * (min_value_over_two - one));
VERIFY_IS_VALID(max * neg_one);
VERIFY_IS_VALID(-max);
VERIFY(max * neg_one == -max);
VERIFY_IS_VALID(one * min);
VERIFY_IS_INVALID(max * neg_two);
VERIFY_IS_VALID(max_over_two * neg_two);
VERIFY_IS_VALID(two * min_over_two);
VERIFY_IS_VALID((max_over_two + one) * neg_two);
VERIFY_IS_INVALID((max_over_two + two) * neg_two);
VERIFY_IS_INVALID(two * (min_over_two - one));
// test negative * positive
VERIFY_IS_VALID(min_value * one);
VERIFY_IS_VALID(min_value_plus_one * one);
VERIFY_IS_INVALID(min_value * two);
VERIFY_IS_VALID(min_value_over_two * two);
VERIFY(min_value_over_two * two == min_value);
VERIFY_IS_INVALID((min_value_over_two - one) * neg_two);
VERIFY_IS_INVALID(neg_two * max_value);
VERIFY_IS_VALID(min_value_over_two * two);
VERIFY(min_value_over_two * two == min_value);
VERIFY_IS_VALID(neg_two * max_value_over_two);
VERIFY_IS_INVALID((min_value_over_two - one) * two);
VERIFY_IS_VALID(neg_two * (max_value_over_two + one));
VERIFY_IS_INVALID(neg_two * (max_value_over_two + two));
VERIFY_IS_VALID(min * one);
VERIFY_IS_VALID(min_plus_one * one);
VERIFY_IS_INVALID(min * two);
VERIFY_IS_VALID(min_over_two * two);
VERIFY(min_over_two * two == min);
VERIFY_IS_INVALID((min_over_two - one) * neg_two);
VERIFY_IS_INVALID(neg_two * max);
VERIFY_IS_VALID(min_over_two * two);
VERIFY(min_over_two * two == min);
VERIFY_IS_VALID(neg_two * max_over_two);
VERIFY_IS_INVALID((min_over_two - one) * two);
VERIFY_IS_VALID(neg_two * (max_over_two + one));
VERIFY_IS_INVALID(neg_two * (max_over_two + two));
// test negative * negative
VERIFY_IS_INVALID(min_value * neg_one);
VERIFY_IS_VALID(min_value_plus_one * neg_one);
VERIFY(min_value_plus_one * neg_one == max_value);
VERIFY_IS_INVALID(min_value * neg_two);
VERIFY_IS_INVALID(min_value_over_two * neg_two);
VERIFY_IS_INVALID(neg_one * min_value);
VERIFY_IS_VALID(neg_one * min_value_plus_one);
VERIFY(neg_one * min_value_plus_one == max_value);
VERIFY_IS_INVALID(neg_two * min_value);
VERIFY_IS_INVALID(neg_two * min_value_over_two);
VERIFY_IS_INVALID(min * neg_one);
VERIFY_IS_VALID(min_plus_one * neg_one);
VERIFY(min_plus_one * neg_one == max);
VERIFY_IS_INVALID(min * neg_two);
VERIFY_IS_INVALID(min_over_two * neg_two);
VERIFY_IS_INVALID(neg_one * min);
VERIFY_IS_VALID(neg_one * min_plus_one);
VERIFY(neg_one * min_plus_one == max);
VERIFY_IS_INVALID(neg_two * min);
VERIFY_IS_INVALID(neg_two * min_over_two);
}
/* division checks */
@ -336,23 +335,23 @@ void test()
VERIFY_IS_INVALID(one / zero);
VERIFY_IS_INVALID(two / zero);
VERIFY_IS_INVALID(neg_one / zero);
VERIFY_IS_INVALID(max_value / zero);
VERIFY_IS_INVALID(min_value / zero);
VERIFY_IS_INVALID(max / zero);
VERIFY_IS_INVALID(min / zero);
if (is_signed) {
// check that min_value / -1 is invalid
VERIFY_IS_INVALID(min_value / neg_one);
if (is_T_signed) {
// check that min / -1 is invalid
VERIFY_IS_INVALID(min / neg_one);
// check that the test for div by -1 isn't banning other numerators than min_value
// check that the test for div by -1 isn't banning other numerators than min
VERIFY_IS_VALID(one / neg_one);
VERIFY_IS_VALID(zero / neg_one);
VERIFY_IS_VALID(neg_one / neg_one);
VERIFY_IS_VALID(max_value / neg_one);
VERIFY_IS_VALID(max / neg_one);
}
/* check that invalidity is correctly preserved by arithmetic ops */
CheckedInt<T> some_invalid = max_value + max_value;
const CheckedInt<T> some_invalid = max + max;
VERIFY_IS_INVALID(some_invalid + zero);
VERIFY_IS_INVALID(some_invalid - zero);
VERIFY_IS_INVALID(zero + some_invalid);
@ -413,19 +412,19 @@ void test()
#define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \
{ \
bool is_U_signed = integer_traits<U>::is_signed; \
bool is_U_signed = is_signed<U>::value; \
VERIFY_IS_VALID(CheckedInt<T>(U(0))); \
VERIFY_IS_VALID(CheckedInt<T>(U(1))); \
VERIFY_IS_VALID(CheckedInt<T>(U(100))); \
if (is_U_signed) \
VERIFY_IS_VALID_IF(CheckedInt<T>(U(-1)), is_signed); \
VERIFY_IS_VALID_IF(CheckedInt<T>(U(-1)), is_T_signed); \
if (sizeof(U) > sizeof(T)) \
VERIFY_IS_INVALID(CheckedInt<T>(U(integer_traits<T>::max_value())+1)); \
VERIFY_IS_VALID_IF(CheckedInt<T>(integer_traits<U>::max_value()), \
(sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (is_U_signed || !is_signed)))); \
VERIFY_IS_VALID_IF(CheckedInt<T>(integer_traits<U>::min_value()), \
VERIFY_IS_INVALID(CheckedInt<T>(U(max_value<T>::value())+1)); \
VERIFY_IS_VALID_IF(CheckedInt<T>(max_value<U>::value()), \
(sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (is_U_signed || !is_T_signed)))); \
VERIFY_IS_VALID_IF(CheckedInt<T>(min_value<U>::value()), \
is_U_signed == false ? 1 : \
bool(is_signed) == false ? 0 : \
bool(is_T_signed) == false ? 0 : \
sizeof(T) >= sizeof(U)); \
}
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int8_t)
@ -437,6 +436,33 @@ void test()
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int64_t)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint64_t)
typedef unsigned char unsigned_char;
typedef unsigned short unsigned_short;
typedef unsigned int unsigned_int;
typedef unsigned long unsigned_long;
typedef long long long_long;
typedef unsigned long long unsigned_long_long;
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(char)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsigned_char)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(short)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsigned_short)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsigned_int)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(long)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsigned_long)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(long_long)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsigned_long_long)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt8)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint8)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt16)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint16)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt32)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint32)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt64)
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint64)
/* Test increment/decrement operators */
CheckedInt<T> x, y;
@ -456,13 +482,13 @@ void test()
y = --x;
VERIFY(x == zero);
VERIFY(y == zero);
x = max_value;
x = max;
VERIFY_IS_VALID(x++);
x = max_value;
x = max;
VERIFY_IS_INVALID(++x);
x = min_value;
x = min;
VERIFY_IS_VALID(x--);
x = min_value;
x = min;
VERIFY_IS_INVALID(--x);
}
@ -479,10 +505,31 @@ int main()
CheckedInt_test::test<int64_t>();
CheckedInt_test::test<uint64_t>();
CheckedInt_test::test<char>();
CheckedInt_test::test<unsigned char>();
CheckedInt_test::test<short>();
CheckedInt_test::test<unsigned short>();
CheckedInt_test::test<int>();
CheckedInt_test::test<unsigned int>();
CheckedInt_test::test<long>();
CheckedInt_test::test<unsigned long>();
CheckedInt_test::test<long long>();
CheckedInt_test::test<unsigned long long>();
CheckedInt_test::test<PRInt8>();
CheckedInt_test::test<PRUint8>();
CheckedInt_test::test<PRInt16>();
CheckedInt_test::test<PRUint16>();
CheckedInt_test::test<PRInt32>();
CheckedInt_test::test<PRUint32>();
CheckedInt_test::test<PRInt64>();
std::cerr << CheckedInt_test::g_tests_failed << " tests failed, "
<< CheckedInt_test::g_tests_passed << " tests passed out of "
<< CheckedInt_test::g_tests_failed + CheckedInt_test::g_tests_passed
<< " tests." << std::endl;
<< " tests, covering " << CheckedInt_test::g_integer_types_tested
<< " distinct integer types." << std::endl;
return CheckedInt_test::g_tests_failed > 0;
return CheckedInt_test::g_tests_failed > 0
|| CheckedInt_test::g_integer_types_tested < 8;
}