Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(280)

Unified Diff: base/numerics/safe_conversions.h

Issue 2528243002: Fix silent truncations when extracting values from CheckedNumeric (Closed)
Patch Set: compile fix Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/numerics/safe_conversions.h
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 4b9129dfffe94329c954bc1842069e7e5bd1e919..71408102eabc4843493cf46e086129c766dcce8e 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -9,12 +9,40 @@
#include <cassert>
#include <limits>
+#include <ostream>
#include <type_traits>
#include "base/numerics/safe_conversions_impl.h"
namespace base {
+// The following are helper constexpr template functions and classes for safely
+// performing a range of conversions, assignments, and tests:
+//
+// checked_cast<> - Analogous to static_cast<> for numeric types, except
+// that it CHECKs that the specified numeric conversion will not overflow
+// or underflow. NaN source will always trigger a CHECK.
+// The default CHECK triggers a crash, but the handler can be overriden.
+// saturated_cast<> - Analogous to static_cast<> for numeric types, except
+// that it returns a saturated result when the specified numeric conversion
+// would otherwise overflow or underflow. An NaN source returns 0 by
+// default, but can be overridden to return a different result.
+// strict_cast<> - Analogous to static_cast<> for numeric types, except that
+// it will cause a compile failure if the destination type is not large
+// enough to contain any value in the source type. It performs no runtime
+// checking and thus introduces no runtime overhead.
+// IsValueInRangeForNumericType<>() - A convenience function that returns true
+// if the type supplied to the template parameter can represent the value
+// passed as an argument to the function.
+// IsValueNegative<>() - A convenience function that will accept any arithmetic
+// type as an argument and will return whether the value is less than zero.
+// Unsigned types always return false.
+// StrictNumeric<> - A wrapper type that performs assignments and copies via
+// the strict_cast<> template, and can perform valid arithmetic comparisons
+// across any range of arithmetic types. StrictNumeric is the return type
+// for values extracted from a CheckedNumeric class instance. The raw
+// arithmetic value is extracted via static_cast to the underlying type.
+
// Convenience function that returns true if the supplied value is in range
// for the destination type.
template <typename Dst, typename Src>
@@ -63,8 +91,9 @@ template <typename Dst,
constexpr Dst checked_cast(Src value) {
// This throws a compile-time error on evaluating the constexpr if it can be
// determined at compile-time as failing, otherwise it will CHECK at runtime.
- return IsValueInRangeForNumericType<Dst>(value)
- ? static_cast<Dst>(value)
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return IsValueInRangeForNumericType<Dst, SrcType>(value)
+ ? static_cast<Dst>(static_cast<SrcType>(value))
: CheckHandler::template HandleFailure<Dst>();
}
@@ -91,7 +120,6 @@ constexpr Dst saturated_cast_impl(const Src value,
? std::numeric_limits<Dst>::max()
: NaNHandler::template HandleFailure<Dst>()));
}
-} // namespace internal
// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate rather than overflow or
@@ -101,10 +129,13 @@ template <typename Dst,
class NaNHandler = SaturatedCastNaNBehaviorReturnZero,
typename Src>
constexpr Dst saturated_cast(Src value) {
+ using SrcType = typename UnderlyingType<Src>::type;
return std::numeric_limits<Dst>::is_iec559
- ? static_cast<Dst>(value) // Floating point optimization.
+ ? static_cast<Dst>(
+ static_cast<SrcType>(value)) // Floating point optimization.
: internal::saturated_cast_impl<Dst, NaNHandler>(
- value, internal::DstRangeRelationToSrcRange<Dst>(value));
+ value,
+ internal::DstRangeRelationToSrcRange<Dst, SrcType>(value));
}
// strict_cast<> is analogous to static_cast<> for numeric types, except that
@@ -112,22 +143,41 @@ constexpr Dst saturated_cast(Src value) {
// to contain any value in the source type. It performs no runtime checking.
template <typename Dst, typename Src>
constexpr Dst strict_cast(Src value) {
- static_assert(std::numeric_limits<Src>::is_specialized,
- "Argument must be numeric.");
+ using SrcType = typename UnderlyingType<Src>::type;
+ static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
static_assert(std::numeric_limits<Dst>::is_specialized,
"Result must be numeric.");
- static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
- internal::NUMERIC_RANGE_CONTAINED),
- "The numeric conversion is out of range for this type. You "
- "should probably use one of the following conversion "
- "mechanisms on the value you want to pass:\n"
- "- base::checked_cast\n"
- "- base::saturated_cast\n"
- "- base::CheckedNumeric");
-
- return static_cast<Dst>(value);
+
+ // If you got here from a compiler error, it's because you tried to assign
+ // from a source type to a destination type that has insufficient range.
+ // The solution may be to change the destination type you're assigning to,
+ // and use one large enough to represent the source.
+ // Alternatively, you may be better served with the checked_cast<> or
+ // saturated_cast<> template functions for your particular use case.
+ static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
+ NUMERIC_RANGE_CONTAINED,
+ "The source type is out of range for the destination type. "
+ "Please see strict_cast<> comments for more information.");
+
+ return static_cast<Dst>(static_cast<SrcType>(value));
}
+// Some wrappers to statically check that a type is in range.
+template <typename Dst, typename Src, class Enable = void>
+struct IsNumericRangeContained {
+ static const bool value = false;
+};
+
+template <typename Dst, typename Src>
+struct IsNumericRangeContained<
+ Dst,
+ Src,
+ typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
+ ArithmeticOrUnderlyingEnum<Src>::value>::type> {
+ static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ NUMERIC_RANGE_CONTAINED;
+};
+
// StrictNumeric implements compile time range checking between numeric types by
// wrapping assignment operations in a strict_cast. This class is intended to be
// used for function arguments and return types, to ensure the destination type
@@ -153,19 +203,62 @@ class StrictNumeric {
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to StrictNumerics to make them easier to use.
template <typename Src>
- constexpr StrictNumeric(Src value)
+ constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
: value_(strict_cast<T>(value)) {}
- // The numeric cast operator basically handles all the magic.
- template <typename Dst>
+ // If you got here from a compiler error, it's because you tried to assign
+ // from a source type to a destination type that has insufficient range.
+ // The solution may be to change the destination type you're assigning to,
+ // and use one large enough to represent the source.
+ // If you're assigning from a CheckedNumeric<> class, you may be able to use
+ // the AssignIfValid() member function, specify a narrower destination type to
+ // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
+ // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
+ // If you've encountered an _ambiguous overload_ you can use a static_cast<>
+ // to explicitly cast the result to the destination type.
+ // If none of that works, you may be better served with the checked_cast<> or
+ // saturated_cast<> template functions for your particular use case.
+ template <typename Dst,
+ typename std::enable_if<
+ IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
constexpr operator Dst() const {
- return strict_cast<Dst>(value_);
+ return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
}
private:
const T value_;
};
+// Overload the ostream output operator to make logging work nicely.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+
+#define STRICT_COMPARISON_OP(NAME, OP) \
+ template <typename L, typename R, \
+ typename std::enable_if< \
+ internal::IsStrictOp<L, R>::value>::type* = nullptr> \
+ constexpr bool operator OP(const L lhs, const R rhs) { \
+ return SafeCompare<NAME, typename UnderlyingType<L>::type, \
+ typename UnderlyingType<R>::type>(lhs, rhs); \
+ }
+
+STRICT_COMPARISON_OP(IsLess, <);
+STRICT_COMPARISON_OP(IsLessOrEqual, <=);
+STRICT_COMPARISON_OP(IsGreater, >);
+STRICT_COMPARISON_OP(IsGreaterOrEqual, >=);
+STRICT_COMPARISON_OP(IsEqual, ==);
+STRICT_COMPARISON_OP(IsNotEqual, !=);
+
+#undef STRICT_COMPARISON_OP
+};
+
+using internal::strict_cast;
+using internal::saturated_cast;
+using internal::StrictNumeric;
+
// Explicitly make a shorter size_t typedef for convenience.
typedef StrictNumeric<size_t> SizeT;
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698