Index: base/numerics/safe_conversions_impl.h |
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h |
index bdce1675700ee710ca17a39f508fce9bb3968046..f19cbfceaa748daf9b0bd4b89b68608775f5ed15 100644 |
--- a/base/numerics/safe_conversions_impl.h |
+++ b/base/numerics/safe_conversions_impl.h |
@@ -20,8 +20,6 @@ namespace internal { |
// for accurate range comparisons between floating point and integer types. |
template <typename NumericType> |
struct MaxExponent { |
- static_assert(std::is_arithmetic<NumericType>::value, |
- "Argument must be numeric."); |
static const int value = std::numeric_limits<NumericType>::is_iec559 |
? std::numeric_limits<NumericType>::max_exponent |
: (sizeof(NumericType) * CHAR_BIT + 1 - |
@@ -260,6 +258,387 @@ constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) { |
return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); |
} |
+// Integer promotion templates used by the portable checked integer arithmetic. |
+template <size_t Size, bool IsSigned> |
+struct IntegerForSizeAndSign; |
+template <> |
+struct IntegerForSizeAndSign<1, true> { |
+ typedef int8_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<1, false> { |
+ typedef uint8_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<2, true> { |
+ typedef int16_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<2, false> { |
+ typedef uint16_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<4, true> { |
+ typedef int32_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<4, false> { |
+ typedef uint32_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<8, true> { |
+ typedef int64_t type; |
+}; |
+template <> |
+struct IntegerForSizeAndSign<8, false> { |
+ typedef uint64_t type; |
+ static_assert(sizeof(uintmax_t) == 8, |
+ "Max integer size not supported for this toolchain."); |
+}; |
+ |
+// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to |
+// support 128-bit math, then the ArithmeticPromotion template below will need |
+// to be updated (or more likely replaced with a decltype expression). |
+ |
+template <typename Integer> |
+struct UnsignedIntegerForSize { |
+ typedef typename std::enable_if< |
+ std::numeric_limits<Integer>::is_integer, |
+ typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type; |
+}; |
+ |
+template <typename Integer> |
+struct SignedIntegerForSize { |
+ typedef typename std::enable_if< |
+ std::numeric_limits<Integer>::is_integer, |
+ typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type; |
+}; |
+ |
+template <typename Integer> |
+struct TwiceWiderInteger { |
+ typedef typename std::enable_if< |
+ std::numeric_limits<Integer>::is_integer, |
+ typename IntegerForSizeAndSign< |
+ sizeof(Integer) * 2, |
+ std::numeric_limits<Integer>::is_signed>::type>::type type; |
+}; |
+ |
+template <typename Integer> |
+struct PositionOfSignBit { |
+ static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, |
+ size_t>::type value = |
+ CHAR_BIT * sizeof(Integer) - 1; |
+}; |
+ |
+enum ArithmeticPromotionCategory { |
+ LEFT_PROMOTION, // Use the type of the left-hand argument. |
+ RIGHT_PROMOTION, // Use the type of the right-hand argument. |
+ MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent. |
+ BIG_ENOUGH_PROMOTION // Attempt to find a big enough type. |
+}; |
+ |
+template <ArithmeticPromotionCategory Promotion, |
+ typename Lhs, |
+ typename Rhs = Lhs> |
+struct ArithmeticPromotion; |
+ |
+template <typename Lhs, |
+ typename Rhs, |
+ ArithmeticPromotionCategory Promotion = |
+ (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) |
+ ? LEFT_PROMOTION |
+ : RIGHT_PROMOTION> |
+struct MaxExponentPromotion; |
+ |
+template <typename Lhs, typename Rhs> |
+struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { |
+ using type = Lhs; |
+}; |
+ |
+template <typename Lhs, typename Rhs> |
+struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { |
+ using type = Rhs; |
+}; |
+ |
+template <typename Lhs, |
+ typename Rhs = Lhs, |
+ bool is_intmax_type = |
+ std::is_integral< |
+ typename MaxExponentPromotion<Lhs, Rhs>::type>::value && |
+ sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) == |
+ sizeof(intmax_t), |
+ bool is_max_exponent = |
+ StaticDstRangeRelationToSrcRange< |
+ typename MaxExponentPromotion<Lhs, Rhs>::type, |
+ Lhs>::value == |
+ NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< |
+ typename MaxExponentPromotion<Lhs, Rhs>::type, |
+ Rhs>::value == NUMERIC_RANGE_CONTAINED> |
+struct BigEnoughPromotion; |
+ |
+// The side with the max exponent is big enough. |
+template <typename Lhs, typename Rhs, bool is_intmax_type> |
+struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { |
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
+ static const bool is_contained = true; |
+}; |
+ |
+// We can use a twice wider type to fit. |
+template <typename Lhs, typename Rhs> |
+struct BigEnoughPromotion<Lhs, Rhs, false, false> { |
+ using type = typename IntegerForSizeAndSign< |
+ sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) * 2, |
+ std::is_signed<Lhs>::value || std::is_signed<Rhs>::value>::type; |
+ static const bool is_contained = true; |
+}; |
+ |
+// No type is large enough. |
+template <typename Lhs, typename Rhs> |
+struct BigEnoughPromotion<Lhs, Rhs, true, false> { |
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
+ static const bool is_contained = false; |
+}; |
+ |
+// These are the supported promotion types. |
+ |
+// Use the type supporting the largest exponent. |
+template <typename Lhs, typename Rhs> |
+struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> { |
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
+ static const bool is_contained = true; |
+}; |
+ |
+// Attempt to find a big enough type. |
+template <typename Lhs, typename Rhs> |
+struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> { |
+ using type = typename BigEnoughPromotion<Lhs, Rhs>::type; |
+ static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained; |
+}; |
+ |
+// We can statically check if operations on the provided types can wrap, so we |
+// can skip the checked operations if they're not needed. So, for an integer we |
+// care if the destination type preserves the sign and is twice the width of |
+// the source. |
+template <typename T, typename Lhs, typename Rhs> |
+struct IsIntegerArithmeticSafe { |
+ static const bool value = !std::numeric_limits<T>::is_iec559 && |
+ StaticDstRangeRelationToSrcRange<T, Lhs>::value == |
+ NUMERIC_RANGE_CONTAINED && |
+ sizeof(T) >= (2 * sizeof(Lhs)) && |
+ StaticDstRangeRelationToSrcRange<T, Rhs>::value != |
+ NUMERIC_RANGE_CONTAINED && |
+ sizeof(T) >= (2 * sizeof(Rhs)); |
+}; |
+ |
+// This hacks around libstdc++ 4.6 missing stuff in type_traits. |
+#if defined(__GLIBCXX__) |
+#define PRIV_GLIBCXX_4_7_0 20120322 |
+#define PRIV_GLIBCXX_4_5_4 20120702 |
+#define PRIV_GLIBCXX_4_6_4 20121127 |
+#if (__GLIBCXX__ < PRIV_GLIBCXX_4_7_0 || __GLIBCXX__ == PRIV_GLIBCXX_4_5_4 || \ |
+ __GLIBCXX__ == PRIV_GLIBCXX_4_6_4) |
+#define PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX |
+#undef PRIV_GLIBCXX_4_7_0 |
+#undef PRIV_GLIBCXX_4_5_4 |
+#undef PRIV_GLIBCXX_4_6_4 |
+#endif |
+#endif |
+ |
+// Extracts the underlying type from an enum. |
+template <typename T, bool is_enum = std::is_enum<T>::value> |
+struct ArithmeticOrUnderlyingEnum; |
+ |
+template <typename T> |
+struct ArithmeticOrUnderlyingEnum<T, true> { |
+#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX) |
+ using type = __underlying_type(T); |
+#else |
+ using type = typename std::underlying_type<T>::type; |
+#endif |
+ static const bool value = std::is_arithmetic<type>::value; |
+}; |
+ |
+#if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX) |
+#undef PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX |
+#endif |
+ |
+template <typename T> |
+struct ArithmeticOrUnderlyingEnum<T, false> { |
+ using type = T; |
+ static const bool value = std::is_arithmetic<type>::value; |
+}; |
+ |
+// The following are helper templates used in the CheckedNumeric class. |
+template <typename T> |
+class CheckedNumeric; |
+ |
+template <typename T> |
+class StrictNumeric; |
+ |
+// Used to treat CheckedNumeric and arithmetic underlying types the same. |
+template <typename T> |
+struct UnderlyingType { |
+ using type = typename ArithmeticOrUnderlyingEnum<T>::type; |
+ static const bool is_numeric = std::is_arithmetic<type>::value; |
+ static const bool is_checked = false; |
+ static const bool is_strict = false; |
+}; |
+ |
+template <typename T> |
+struct UnderlyingType<CheckedNumeric<T>> { |
+ using type = T; |
+ static const bool is_numeric = true; |
+ static const bool is_checked = true; |
+ static const bool is_strict = false; |
+}; |
+ |
+template <typename T> |
+struct UnderlyingType<StrictNumeric<T>> { |
+ using type = T; |
+ static const bool is_numeric = true; |
+ static const bool is_checked = false; |
+ static const bool is_strict = true; |
+}; |
+ |
+template <typename L, typename R> |
+struct IsCheckedOp { |
+ static const bool value = |
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
+ (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); |
+}; |
+ |
+template <typename L, typename R> |
+struct IsStrictOp { |
+ static const bool value = |
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
+ (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); |
+}; |
+ |
+template <typename L, typename R> |
+constexpr bool IsLessImpl(const L lhs, |
+ const R rhs, |
+ const RangeConstraint l_range, |
+ const RangeConstraint r_range) { |
+ return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || |
+ (l_range == r_range && |
+ static_cast<decltype(lhs + rhs)>(lhs) < |
+ static_cast<decltype(lhs + rhs)>(rhs)); |
+} |
+ |
+template <typename L, typename R> |
+struct IsLess { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ static constexpr bool Test(const L lhs, const R rhs) { |
+ return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
+ DstRangeRelationToSrcRange<L>(rhs)); |
+ } |
+}; |
+ |
+template <typename L, typename R> |
+constexpr bool IsLessOrEqualImpl(const L lhs, |
+ const R rhs, |
+ const RangeConstraint l_range, |
+ const RangeConstraint r_range) { |
+ return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || |
+ (l_range == r_range && |
+ static_cast<decltype(lhs + rhs)>(lhs) <= |
+ static_cast<decltype(lhs + rhs)>(rhs)); |
+} |
+ |
+template <typename L, typename R> |
+struct IsLessOrEqual { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ static constexpr bool Test(const L lhs, const R rhs) { |
+ return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
+ DstRangeRelationToSrcRange<L>(rhs)); |
+ } |
+}; |
+ |
+template <typename L, typename R> |
+constexpr bool IsGreaterImpl(const L lhs, |
+ const R rhs, |
+ const RangeConstraint l_range, |
+ const RangeConstraint r_range) { |
+ return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || |
+ (l_range == r_range && |
+ static_cast<decltype(lhs + rhs)>(lhs) > |
+ static_cast<decltype(lhs + rhs)>(rhs)); |
+} |
+ |
+template <typename L, typename R> |
+struct IsGreater { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ static constexpr bool Test(const L lhs, const R rhs) { |
+ return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
+ DstRangeRelationToSrcRange<L>(rhs)); |
+ } |
+}; |
+ |
+template <typename L, typename R> |
+constexpr bool IsGreaterOrEqualImpl(const L lhs, |
+ const R rhs, |
+ const RangeConstraint l_range, |
+ const RangeConstraint r_range) { |
+ return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || |
+ (l_range == r_range && |
+ static_cast<decltype(lhs + rhs)>(lhs) >= |
+ static_cast<decltype(lhs + rhs)>(rhs)); |
+} |
+ |
+template <typename L, typename R> |
+struct IsGreaterOrEqual { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ static constexpr bool Test(const L lhs, const R rhs) { |
+ return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
+ DstRangeRelationToSrcRange<L>(rhs)); |
+ } |
+}; |
+ |
+template <typename L, typename R> |
+struct IsEqual { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ static constexpr bool Test(const L lhs, const R rhs) { |
+ return DstRangeRelationToSrcRange<R>(lhs) == |
+ DstRangeRelationToSrcRange<L>(rhs) && |
+ static_cast<decltype(lhs + rhs)>(lhs) == |
+ static_cast<decltype(lhs + rhs)>(rhs); |
+ } |
+}; |
+ |
+template <typename L, typename R> |
+struct IsNotEqual { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ static constexpr bool Test(const L lhs, const R rhs) { |
+ return DstRangeRelationToSrcRange<R>(lhs) != |
+ DstRangeRelationToSrcRange<L>(rhs) || |
+ static_cast<decltype(lhs + rhs)>(lhs) != |
+ static_cast<decltype(lhs + rhs)>(rhs); |
+ } |
+}; |
+ |
+// These perform the actual math operations on the CheckedNumerics. |
+// Binary arithmetic operations. |
+template <template <typename, typename> class C, typename L, typename R> |
+constexpr bool SafeCompare(const L lhs, const R rhs) { |
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
+ "Types must be numeric."); |
+ using Promotion = ArithmeticPromotion<BIG_ENOUGH_PROMOTION, L, R>; |
+ using BigType = typename Promotion::type; |
+ return Promotion::is_contained |
+ // Force to a larger type for speed if both are contained. |
+ ? C<BigType, BigType>::Test( |
+ static_cast<BigType>(static_cast<L>(lhs)), |
+ static_cast<BigType>(static_cast<R>(rhs))) |
+ // Let the template functions figure it out for mixed types. |
+ : C<L, R>::Test(lhs, rhs); |
+}; |
+ |
} // namespace internal |
} // namespace base |