Commit 6a2c67b9 authored by jschuh's avatar jschuh Committed by Commit Bot

Split out code to be shared between CheckedNumeric and ClampedNumeric

BUG=672489

Review-Url: https://codereview.chromium.org/2931323002
Cr-Commit-Position: refs/heads/master@{#478803}
parent 96e2b7d0
...@@ -593,10 +593,12 @@ component("base") { ...@@ -593,10 +593,12 @@ component("base") {
"nix/mime_util_xdg.h", "nix/mime_util_xdg.h",
"nix/xdg_util.cc", "nix/xdg_util.cc",
"nix/xdg_util.h", "nix/xdg_util.h",
"numerics/checked_math.h",
"numerics/checked_math_impl.h",
"numerics/safe_conversions.h", "numerics/safe_conversions.h",
"numerics/safe_conversions_impl.h", "numerics/safe_conversions_impl.h",
"numerics/safe_math.h", "numerics/safe_math.h",
"numerics/safe_math_impl.h", "numerics/safe_math_shared_impl.h",
"numerics/saturated_arithmetic.h", "numerics/saturated_arithmetic.h",
"numerics/saturated_arithmetic_arm.h", "numerics/saturated_arithmetic_arm.h",
"observer_list.h", "observer_list.h",
......
This diff is collapsed.
// Copyright 2014 The Chromium Authors. All rights reserved. // Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_
#define BASE_NUMERICS_SAFE_MATH_IMPL_H_ #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
...@@ -15,33 +15,11 @@ ...@@ -15,33 +15,11 @@
#include <type_traits> #include <type_traits>
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math_shared_impl.h"
namespace base { namespace base {
namespace internal { namespace internal {
// Everything from here up to the floating point operations is portable C++,
// but it may not be fast. This code could be split based on
// platform/architecture and replaced with potentially faster implementations.
// This is used for UnsignedAbs, where we need to support floating-point
// template instantiations even though we don't actually support the operations.
// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
// so the float versions will not compile.
template <typename Numeric,
bool IsInteger = std::is_integral<Numeric>::value,
bool IsFloat = std::is_floating_point<Numeric>::value>
struct UnsignedOrFloatForSize;
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, true, false> {
using type = typename std::make_unsigned<Numeric>::type;
};
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, false, true> {
using type = Numeric;
};
// Probe for builtin math overflow support on Clang and version check on GCC. // Probe for builtin math overflow support on Clang and version check on GCC.
#if defined(__has_builtin) #if defined(__has_builtin)
#define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow))
...@@ -444,19 +422,20 @@ struct CheckedMinOp< ...@@ -444,19 +422,20 @@ struct CheckedMinOp<
// This is just boilerplate that wraps the standard floating point arithmetic. // This is just boilerplate that wraps the standard floating point arithmetic.
// A macro isn't the nicest solution, but it beats rewriting these repeatedly. // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
template <typename T, typename U> \ template <typename T, typename U> \
struct Checked##NAME##Op< \ struct Checked##NAME##Op< \
T, U, typename std::enable_if<std::is_floating_point<T>::value || \ T, U, \
std::is_floating_point<U>::value>::type> { \ typename std::enable_if<std::is_floating_point<T>::value || \
using result_type = typename MaxExponentPromotion<T, U>::type; \ std::is_floating_point<U>::value>::type> { \
template <typename V> \ using result_type = typename MaxExponentPromotion<T, U>::type; \
static bool Do(T x, U y, V* result) { \ template <typename V> \
using Promotion = typename MaxExponentPromotion<T, U>::type; \ static bool Do(T x, U y, V* result) { \
Promotion presult = x OP y; \ using Promotion = typename MaxExponentPromotion<T, U>::type; \
*result = static_cast<V>(presult); \ Promotion presult = x OP y; \
return IsValueInRangeForNumericType<V>(presult); \ *result = static_cast<V>(presult); \
} \ return IsValueInRangeForNumericType<V>(presult); \
} \
}; };
BASE_FLOAT_ARITHMETIC_OPS(Add, +) BASE_FLOAT_ARITHMETIC_OPS(Add, +)
...@@ -466,45 +445,6 @@ BASE_FLOAT_ARITHMETIC_OPS(Div, /) ...@@ -466,45 +445,6 @@ BASE_FLOAT_ARITHMETIC_OPS(Div, /)
#undef BASE_FLOAT_ARITHMETIC_OPS #undef BASE_FLOAT_ARITHMETIC_OPS
// Wrap the unary operations to allow SFINAE when instantiating integrals versus
// floating points. These don't perform any overflow checking. Rather, they
// exhibit well-defined overflow semantics and rely on the caller to detect
// if an overflow occured.
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T NegateWrapper(T value) {
using UnsignedT = typename std::make_unsigned<T>::type;
// This will compile to a NEG on Intel, and is normal negation on ARM.
return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T NegateWrapper(T value) {
return -value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
return ~value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T AbsWrapper(T value) {
return static_cast<T>(SafeUnsignedAbs(value));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T AbsWrapper(T value) {
return value < 0 ? -value : value;
}
// Floats carry around their validity state with them, but integers do not. So, // Floats carry around their validity state with them, but integers do not. So,
// we wrap the underlying value in a specialization in order to hide that detail // we wrap the underlying value in a specialization in order to hide that detail
// and expose an interface via accessors. // and expose an interface via accessors.
...@@ -523,8 +463,8 @@ struct GetNumericRepresentation { ...@@ -523,8 +463,8 @@ struct GetNumericRepresentation {
: NUMERIC_UNKNOWN); : NUMERIC_UNKNOWN);
}; };
template <typename T, NumericRepresentation type = template <typename T,
GetNumericRepresentation<T>::value> NumericRepresentation type = GetNumericRepresentation<T>::value>
class CheckedNumericState {}; class CheckedNumericState {};
// Integrals require quite a bit of additional housekeeping to manage state. // Integrals require quite a bit of additional housekeeping to manage state.
...@@ -625,17 +565,7 @@ class CheckedNumericState<T, NUMERIC_FLOATING> { ...@@ -625,17 +565,7 @@ class CheckedNumericState<T, NUMERIC_FLOATING> {
constexpr T value() const { return value_; } constexpr T value() const { return value_; }
}; };
template <template <typename, typename, typename> class M,
typename L,
typename R>
struct MathWrapper {
using math = M<typename UnderlyingType<L>::type,
typename UnderlyingType<R>::type,
void>;
using type = typename math::result_type;
};
} // namespace internal } // namespace internal
} // namespace base } // namespace base
#endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
...@@ -537,6 +537,9 @@ struct ArithmeticOrUnderlyingEnum<T, false> { ...@@ -537,6 +537,9 @@ struct ArithmeticOrUnderlyingEnum<T, false> {
template <typename T> template <typename T>
class CheckedNumeric; class CheckedNumeric;
template <typename T>
class ClampedNumeric;
template <typename T> template <typename T>
class StrictNumeric; class StrictNumeric;
...@@ -554,6 +557,16 @@ struct UnderlyingType<CheckedNumeric<T>> { ...@@ -554,6 +557,16 @@ struct UnderlyingType<CheckedNumeric<T>> {
using type = T; using type = T;
static const bool is_numeric = true; static const bool is_numeric = true;
static const bool is_checked = true; static const bool is_checked = true;
static const bool is_clamped = false;
static const bool is_strict = false;
};
template <typename T>
struct UnderlyingType<ClampedNumeric<T>> {
using type = T;
static const bool is_numeric = true;
static const bool is_checked = false;
static const bool is_clamped = true;
static const bool is_strict = false; static const bool is_strict = false;
}; };
...@@ -562,6 +575,7 @@ struct UnderlyingType<StrictNumeric<T>> { ...@@ -562,6 +575,7 @@ struct UnderlyingType<StrictNumeric<T>> {
using type = T; using type = T;
static const bool is_numeric = true; static const bool is_numeric = true;
static const bool is_checked = false; static const bool is_checked = false;
static const bool is_clamped = false;
static const bool is_strict = true; static const bool is_strict = true;
}; };
...@@ -572,6 +586,13 @@ struct IsCheckedOp { ...@@ -572,6 +586,13 @@ struct IsCheckedOp {
(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
}; };
template <typename L, typename R>
struct IsClampedOp {
static const bool value =
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
};
template <typename L, typename R> template <typename L, typename R>
struct IsStrictOp { struct IsStrictOp {
static const bool value = static const bool value =
...@@ -702,7 +723,7 @@ constexpr bool SafeCompare(const L lhs, const R rhs) { ...@@ -702,7 +723,7 @@ constexpr bool SafeCompare(const L lhs, const R rhs) {
static_cast<BigType>(static_cast<R>(rhs))) static_cast<BigType>(static_cast<R>(rhs)))
// Let the template functions figure it out for mixed types. // Let the template functions figure it out for mixed types.
: C<L, R>::Test(lhs, rhs); : C<L, R>::Test(lhs, rhs);
}; }
} // namespace internal } // namespace internal
} // namespace base } // namespace base
......
This diff is collapsed.
// Copyright 2017 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.
#ifndef BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
#include <stddef.h>
#include <stdint.h>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions.h"
namespace base {
namespace internal {
// This is used for UnsignedAbs, where we need to support floating-point
// template instantiations even though we don't actually support the operations.
// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
// so the float versions will not compile.
template <typename Numeric,
bool IsInteger = std::is_integral<Numeric>::value,
bool IsFloat = std::is_floating_point<Numeric>::value>
struct UnsignedOrFloatForSize;
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, true, false> {
using type = typename std::make_unsigned<Numeric>::type;
};
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, false, true> {
using type = Numeric;
};
// Wrap the unary operations to allow SFINAE when instantiating integrals versus
// floating points. These don't perform any overflow checking. Rather, they
// exhibit well-defined overflow semantics and rely on the caller to detect
// if an overflow occured.
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T NegateWrapper(T value) {
using UnsignedT = typename std::make_unsigned<T>::type;
// This will compile to a NEG on Intel, and is normal negation on ARM.
return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T NegateWrapper(T value) {
return -value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
return ~value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T AbsWrapper(T value) {
return static_cast<T>(SafeUnsignedAbs(value));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T AbsWrapper(T value) {
return value < 0 ? -value : value;
}
template <template <typename, typename, typename> class M,
typename L,
typename R>
struct MathWrapper {
using math = M<typename UnderlyingType<L>::type,
typename UnderlyingType<R>::type,
void>;
using type = typename math::result_type;
};
// These variadic templates work out the return types.
// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
template <template <typename, typename, typename> class M,
typename L,
typename R,
typename... Args>
struct ResultType;
template <template <typename, typename, typename> class M,
typename L,
typename R>
struct ResultType<M, L, R> {
using type = typename MathWrapper<M, L, R>::type;
};
template <template <typename, typename, typename> class M,
typename L,
typename R,
typename... Args>
struct ResultType {
using type =
typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
};
// The following macros are just boilerplate for the standard arithmetic
// operator overloads and variadic function templates. A macro isn't the nicest
// solution, but it beats rewriting these over and over again.
#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
template <typename L, typename R, typename... Args> \
CLASS##Numeric<typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
return ChkMathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, args...); \
}
#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
/* Binary arithmetic operator for all CheckedNumeric operations. */ \
template <typename L, typename R, \
typename std::enable_if<IsCheckedOp<L, R>::value>::type* = \
nullptr> \
CheckedNumeric<typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
operator OP(const L lhs, const R rhs) { \
return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
rhs); \
} \
/* Assignment arithmetic operator implementation from CheckedNumeric. */ \
template <typename L> \
template <typename R> \
CheckedNumeric<L>& CheckedNumeric<L>::operator CMP_OP(const R rhs) { \
return MathOp<CLASS##OP_NAME##Op>(rhs); \
} \
/* Variadic arithmetic functions that return CheckedNumeric. */ \
BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment