| Index: base/safe_numerics.h
|
| ===================================================================
|
| --- base/safe_numerics.h (revision 245077)
|
| +++ base/safe_numerics.h (working copy)
|
| @@ -1,4 +1,4 @@
|
| -// Copyright 2013 The Chromium Authors. All rights reserved.
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| @@ -8,128 +8,55 @@
|
| #include <limits>
|
|
|
| #include "base/logging.h"
|
| +#include "base/safe_numerics_impl.h"
|
|
|
| namespace base {
|
| -namespace internal {
|
|
|
| -template <bool SameSize, bool DestLarger,
|
| - bool DestIsSigned, bool SourceIsSigned>
|
| -struct IsValidNumericCastImpl;
|
| +// Convenience function that returns true if the supplied value is in range
|
| +// for the destination type.
|
| +template <typename Dst, typename Src>
|
| +inline bool IsValueInRangeForNumericType(Src value) {
|
| + return internal::RangeCheck<Dst>(value) == internal::TYPE_VALID;
|
| +}
|
|
|
| -#define BASE_NUMERIC_CAST_CASE_SPECIALIZATION(A, B, C, D, Code) \
|
| -template <> struct IsValidNumericCastImpl<A, B, C, D> { \
|
| - template <class Source, class DestBounds> static inline bool Test( \
|
| - Source source, DestBounds min, DestBounds max) { \
|
| - return Code; \
|
| - } \
|
| +// checked_numeric_cast<> is 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.
|
| +template <typename Dst, typename Src>
|
| +inline Dst checked_numeric_cast(Src value) {
|
| + CHECK(IsValueInRangeForNumericType<Dst>(value));
|
| + return static_cast<Dst>(value);
|
| }
|
|
|
| -#define BASE_NUMERIC_CAST_CASE_SAME_SIZE(DestSigned, SourceSigned, Code) \
|
| - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
|
| - true, true, DestSigned, SourceSigned, Code); \
|
| - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
|
| - true, false, DestSigned, SourceSigned, Code)
|
| +// saturated_cast<> is analogous to static_cast<> for numeric types, except
|
| +// that the specified numeric conversion will saturate rather than overflow or
|
| +// underflow. NaN assignment to an integral will trigger a CHECK condition.
|
| +template <typename Dst, typename Src>
|
| +inline Dst saturated_cast(Src value) {
|
| + // Optimization for floating point values, which already saturate.
|
| + if (std::numeric_limits<Dst>::is_iec559)
|
| + return static_cast<Dst>(value);
|
|
|
| -#define BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(DestSigned, SourceSigned, Code) \
|
| - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
|
| - false, false, DestSigned, SourceSigned, Code); \
|
| + switch (internal::RangeCheck<Dst>(value)) {
|
| + case internal::TYPE_VALID:
|
| + return static_cast<Dst>(value);
|
|
|
| -#define BASE_NUMERIC_CAST_CASE_DEST_LARGER(DestSigned, SourceSigned, Code) \
|
| - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
|
| - false, true, DestSigned, SourceSigned, Code); \
|
| + case internal::TYPE_UNDERFLOW:
|
| + return std::numeric_limits<Dst>::min();
|
|
|
| -// The three top level cases are:
|
| -// - Same size
|
| -// - Source larger
|
| -// - Dest larger
|
| -// And for each of those three cases, we handle the 4 different possibilities
|
| -// of signed and unsigned. This gives 12 cases to handle, which we enumerate
|
| -// below.
|
| -//
|
| -// The last argument in each of the macros is the actual comparison code. It
|
| -// has three arguments available, source (the value), and min/max which are
|
| -// the ranges of the destination.
|
| + case internal::TYPE_OVERFLOW:
|
| + return std::numeric_limits<Dst>::max();
|
|
|
| + // Should fail only on attempting to assign NaN to a saturated integer.
|
| + case internal::TYPE_INVALID:
|
| + CHECK(false);
|
| + return std::numeric_limits<Dst>::max();
|
| + }
|
|
|
| -// These are the cases where both types have the same size.
|
| -
|
| -// Both signed.
|
| -BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, true, true);
|
| -// Both unsigned.
|
| -BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, false, true);
|
| -// Dest unsigned, Source signed.
|
| -BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, true, source >= 0);
|
| -// Dest signed, Source unsigned.
|
| -// This cast is OK because Dest's max must be less than Source's.
|
| -BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, false,
|
| - source <= static_cast<Source>(max));
|
| -
|
| -
|
| -// These are the cases where Source is larger.
|
| -
|
| -// Both unsigned.
|
| -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, false, source <= max);
|
| -// Both signed.
|
| -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, true,
|
| - source >= min && source <= max);
|
| -// Dest is unsigned, Source is signed.
|
| -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, true,
|
| - source >= 0 && source <= max);
|
| -// Dest is signed, Source is unsigned.
|
| -// This cast is OK because Dest's max must be less than Source's.
|
| -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, false,
|
| - source <= static_cast<Source>(max));
|
| -
|
| -
|
| -// These are the cases where Dest is larger.
|
| -
|
| -// Both unsigned.
|
| -BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, false, true);
|
| -// Both signed.
|
| -BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, true, true);
|
| -// Dest is unsigned, Source is signed.
|
| -BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, true, source >= 0);
|
| -// Dest is signed, Source is unsigned.
|
| -BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, false, true);
|
| -
|
| -#undef BASE_NUMERIC_CAST_CASE_SPECIALIZATION
|
| -#undef BASE_NUMERIC_CAST_CASE_SAME_SIZE
|
| -#undef BASE_NUMERIC_CAST_CASE_SOURCE_LARGER
|
| -#undef BASE_NUMERIC_CAST_CASE_DEST_LARGER
|
| -
|
| -
|
| -// The main test for whether the conversion will under or overflow.
|
| -template <class Dest, class Source>
|
| -inline bool IsValidNumericCast(Source source) {
|
| - typedef std::numeric_limits<Source> SourceLimits;
|
| - typedef std::numeric_limits<Dest> DestLimits;
|
| - COMPILE_ASSERT(SourceLimits::is_specialized, argument_must_be_numeric);
|
| - COMPILE_ASSERT(SourceLimits::is_integer, argument_must_be_integral);
|
| - COMPILE_ASSERT(DestLimits::is_specialized, result_must_be_numeric);
|
| - COMPILE_ASSERT(DestLimits::is_integer, result_must_be_integral);
|
| -
|
| - return IsValidNumericCastImpl<
|
| - sizeof(Dest) == sizeof(Source),
|
| - (sizeof(Dest) > sizeof(Source)),
|
| - DestLimits::is_signed,
|
| - SourceLimits::is_signed>::Test(
|
| - source,
|
| - DestLimits::min(),
|
| - DestLimits::max());
|
| + NOTREACHED();
|
| + return static_cast<Dst>(value);
|
| }
|
|
|
| -} // namespace internal
|
| -
|
| -// checked_numeric_cast<> is analogous to static_cast<> for numeric types,
|
| -// except that it CHECKs that the specified numeric conversion will not
|
| -// overflow or underflow. Floating point arguments are not currently allowed
|
| -// (this is COMPILE_ASSERTd), though this could be supported if necessary.
|
| -template <class Dest, class Source>
|
| -inline Dest checked_numeric_cast(Source source) {
|
| - CHECK(internal::IsValidNumericCast<Dest>(source));
|
| - return static_cast<Dest>(source);
|
| -}
|
| -
|
| } // namespace base
|
|
|
| #endif // BASE_SAFE_NUMERICS_H_
|
|
|