diff options
Diffstat (limited to 'base/numerics/safe_numerics_unittest.cc')
-rw-r--r-- | base/numerics/safe_numerics_unittest.cc | 207 |
1 files changed, 195 insertions, 12 deletions
diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc index 6f9a966c01..cb63ad0d08 100644 --- a/base/numerics/safe_numerics_unittest.cc +++ b/base/numerics/safe_numerics_unittest.cc @@ -2,22 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) -#include <mmintrin.h> -#endif +#include <stddef.h> #include <stdint.h> #include <limits> +#include <type_traits> #include "base/compiler_specific.h" #include "base/numerics/safe_conversions.h" #include "base/numerics/safe_math.h" #include "base/template_util.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) +#include <mmintrin.h> +#endif + using std::numeric_limits; using base::CheckedNumeric; using base::checked_cast; +using base::IsValueInRangeForNumericType; +using base::IsValueNegative; using base::SizeT; using base::StrictNumeric; using base::saturated_cast; @@ -27,7 +33,7 @@ using base::internal::RANGE_VALID; using base::internal::RANGE_INVALID; using base::internal::RANGE_OVERFLOW; using base::internal::RANGE_UNDERFLOW; -using base::enable_if; +using base::internal::SignedIntegerForSize; // These tests deliberately cause arithmetic overflows. If the compiler is // aggressive enough, it can const fold these overflows. Disable warnings about @@ -36,6 +42,26 @@ using base::enable_if; #pragma warning(disable:4756) #endif +// This is a helper function for finding the maximum value in Src that can be +// wholy represented as the destination floating-point type. +template <typename Dst, typename Src> +Dst GetMaxConvertibleToFloat() { + typedef numeric_limits<Dst> DstLimits; + typedef numeric_limits<Src> SrcLimits; + static_assert(SrcLimits::is_specialized, "Source must be numeric."); + static_assert(DstLimits::is_specialized, "Destination must be numeric."); + CHECK(DstLimits::is_iec559); + + if (SrcLimits::digits <= DstLimits::digits && + MaxExponent<Src>::value <= MaxExponent<Dst>::value) + return SrcLimits::max(); + Src max = SrcLimits::max() / 2 + (SrcLimits::is_integer ? 1 : 0); + while (max != static_cast<Src>(static_cast<Dst>(max))) { + max /= 2; + } + return static_cast<Dst>(max); +} + // Helper macros to wrap displaying the conversion types and line numbers. #define TEST_EXPECTED_VALIDITY(expected, actual) \ EXPECT_EQ(expected, CheckedNumeric<Dst>(actual).validity()) \ @@ -53,9 +79,9 @@ template <typename Dst> static void TestSpecializedArithmetic( const char* dst, int line, - typename enable_if< - numeric_limits<Dst>::is_integer&& numeric_limits<Dst>::is_signed, - int>::type = 0) { + typename std::enable_if<numeric_limits<Dst>::is_integer && + numeric_limits<Dst>::is_signed, + int>::type = 0) { typedef numeric_limits<Dst> DstLimits; TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, -CheckedNumeric<Dst>(DstLimits::min())); @@ -109,9 +135,9 @@ template <typename Dst> static void TestSpecializedArithmetic( const char* dst, int line, - typename enable_if< - numeric_limits<Dst>::is_integer && !numeric_limits<Dst>::is_signed, - int>::type = 0) { + typename std::enable_if<numeric_limits<Dst>::is_integer && + !numeric_limits<Dst>::is_signed, + int>::type = 0) { typedef numeric_limits<Dst> DstLimits; TEST_EXPECTED_VALIDITY(RANGE_VALID, -CheckedNumeric<Dst>(DstLimits::min())); TEST_EXPECTED_VALIDITY(RANGE_VALID, @@ -122,6 +148,13 @@ static void TestSpecializedArithmetic( CheckedNumeric<Dst>(DstLimits::min()) - 1); TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2); TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2); + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()).UnsignedAbs()); + TEST_EXPECTED_VALIDITY( + RANGE_VALID, + CheckedNumeric<typename SignedIntegerForSize<Dst>::type>( + std::numeric_limits<typename SignedIntegerForSize<Dst>::type>::min()) + .UnsignedAbs()); // Modulus is legal only for integers. TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1); @@ -142,7 +175,7 @@ template <typename Dst> void TestSpecializedArithmetic( const char* dst, int line, - typename enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) { + typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) { typedef numeric_limits<Dst> DstLimits; TEST_EXPECTED_VALIDITY(RANGE_VALID, -CheckedNumeric<Dst>(DstLimits::min())); @@ -317,7 +350,6 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> { "Comparison must be sign preserving and value preserving"); const CheckedNumeric<Dst> checked_dst = SrcLimits::max(); - ; TEST_EXPECTED_VALIDITY(RANGE_VALID, checked_dst); if (MaxExponent<Dst>::value > MaxExponent<Src>::value) { if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1) { @@ -370,6 +402,18 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> { TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); + if (DstLimits::is_integer) { + if (SrcLimits::digits < DstLimits::digits) { + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, + static_cast<Src>(DstLimits::max())); + } else { + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max())); + } + TEST_EXPECTED_RANGE( + RANGE_VALID, + static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>())); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min())); + } } else if (SrcLimits::is_signed) { TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1)); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); @@ -428,6 +472,18 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> { TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); + if (DstLimits::is_integer) { + if (SrcLimits::digits < DstLimits::digits) { + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, + static_cast<Src>(DstLimits::max())); + } else { + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max())); + } + TEST_EXPECTED_RANGE( + RANGE_VALID, + static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>())); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min())); + } } else { TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); } @@ -578,6 +634,18 @@ TEST(SafeNumerics, CastTests) { EXPECT_TRUE(CheckedNumeric<int>(StrictNumeric<unsigned>(1U)).IsValid()); EXPECT_FALSE(CheckedNumeric<unsigned>(StrictNumeric<int>(-1)).IsValid()); + EXPECT_TRUE(IsValueNegative(-1)); + EXPECT_TRUE(IsValueNegative(numeric_limits<int>::min())); + EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::min())); + EXPECT_TRUE(IsValueNegative(-numeric_limits<double>::max())); + EXPECT_FALSE(IsValueNegative(0)); + EXPECT_FALSE(IsValueNegative(1)); + EXPECT_FALSE(IsValueNegative(0u)); + EXPECT_FALSE(IsValueNegative(1u)); + EXPECT_FALSE(IsValueNegative(numeric_limits<int>::max())); + EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::max())); + EXPECT_FALSE(IsValueNegative(numeric_limits<double>::max())); + // These casts and coercions will fail to compile: // EXPECT_EQ(0, strict_cast<int>(static_cast<size_t>(0))); // EXPECT_EQ(0, strict_cast<size_t>(static_cast<int>(0))); @@ -598,5 +666,120 @@ TEST(SafeNumerics, CastTests) { EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity); EXPECT_EQ(numeric_limits<int>::min(), saturated_cast<int>(double_small_int)); EXPECT_EQ(numeric_limits<int>::max(), saturated_cast<int>(double_large_int)); + + float not_a_number = std::numeric_limits<float>::infinity() - + std::numeric_limits<float>::infinity(); + EXPECT_TRUE(std::isnan(not_a_number)); + EXPECT_EQ(0, saturated_cast<int>(not_a_number)); } +#if GTEST_HAS_DEATH_TEST + +TEST(SafeNumerics, SaturatedCastChecks) { + float not_a_number = std::numeric_limits<float>::infinity() - + std::numeric_limits<float>::infinity(); + EXPECT_TRUE(std::isnan(not_a_number)); + EXPECT_DEATH((saturated_cast<int, base::SaturatedCastNaNBehaviorCheck>( + not_a_number)), ""); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(SafeNumerics, IsValueInRangeForNumericType) { + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(2)); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0xffffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0xffffffff))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000000))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000001))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>( + std::numeric_limits<int32_t>::min())); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>( + std::numeric_limits<int64_t>::min())); + + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(2)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffff)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffffu)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0x80000000u)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0xffffffffu)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x80000000))); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0xffffffff))); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x100000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>( + std::numeric_limits<int32_t>::min())); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>( + static_cast<int64_t>(std::numeric_limits<int32_t>::min()))); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>( + static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>( + std::numeric_limits<int64_t>::min())); + + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(2)); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0xffffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0xffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000001))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>( + std::numeric_limits<int32_t>::min())); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(INT64_C(-1))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>( + std::numeric_limits<int64_t>::min())); + + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(2)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffff)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x80000000u)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0xffffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x80000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0xffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x100000000))); + EXPECT_TRUE( + IsValueInRangeForNumericType<int64_t>(INT64_C(0x7fffffffffffffff))); + EXPECT_TRUE( + IsValueInRangeForNumericType<int64_t>(UINT64_C(0x7fffffffffffffff))); + EXPECT_FALSE( + IsValueInRangeForNumericType<int64_t>(UINT64_C(0x8000000000000000))); + EXPECT_FALSE( + IsValueInRangeForNumericType<int64_t>(UINT64_C(0xffffffffffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>( + std::numeric_limits<int32_t>::min())); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>( + static_cast<int64_t>(std::numeric_limits<int32_t>::min()))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>( + std::numeric_limits<int64_t>::min())); +} + +TEST(SafeNumerics, CompoundNumericOperations) { + CheckedNumeric<int> a = 1; + CheckedNumeric<int> b = 2; + CheckedNumeric<int> c = 3; + CheckedNumeric<int> d = 4; + a += b; + EXPECT_EQ(3, a.ValueOrDie()); + a -= c; + EXPECT_EQ(0, a.ValueOrDie()); + d /= b; + EXPECT_EQ(2, d.ValueOrDie()); + d *= d; + EXPECT_EQ(4, d.ValueOrDie()); + + CheckedNumeric<int> too_large = std::numeric_limits<int>::max(); + EXPECT_TRUE(too_large.IsValid()); + too_large += d; + EXPECT_FALSE(too_large.IsValid()); + too_large -= d; + EXPECT_FALSE(too_large.IsValid()); + too_large /= d; + EXPECT_FALSE(too_large.IsValid()); +} |