Chromium Code Reviews| Index: base/numerics/clamped_math_impl.h |
| diff --git a/base/numerics/clamped_math_impl.h b/base/numerics/clamped_math_impl.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ec5192578777c98836b55bddc7198ac1930c1eef |
| --- /dev/null |
| +++ b/base/numerics/clamped_math_impl.h |
| @@ -0,0 +1,280 @@ |
| +// 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_CLAMPED_MATH_IMPL_H_ |
| +#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ |
| + |
| +#include <stddef.h> |
| +#include <stdint.h> |
| + |
| +#include <climits> |
| +#include <cmath> |
| +#include <cstdlib> |
| +#include <limits> |
| +#include <type_traits> |
| + |
| +#include "base/numerics/checked_math.h" |
| +#include "base/numerics/safe_conversions.h" |
| +#include "base/numerics/safe_math_shared_impl.h" |
| + |
| +namespace base { |
| +namespace internal { |
| + |
| +// This provides a small optimization that generates more compact code when one |
| +// of the components in an operation is a compile-time constant. |
| +template <typename T> |
| +constexpr bool IsValueConstantAndNegative(const T v) { |
| +#if defined(__clang__) || defined(__GNUC__) |
| + return __builtin_constant_p(v) ? IsValueNegative(v) : false; |
| +#else |
| + return false; |
| +#endif |
| +} |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedAddOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedAddOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename MaxExponentPromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static V Do(T x, U y) { |
| + V result; |
| + return CheckedAddOp<T, U>::Do(x, y, &result) |
| + ? result |
| + : (as_unsigned(std::numeric_limits<V>::max()) + |
| + (IsValueConstantAndNegative(x) || IsValueNegative(y))); |
|
dcheng
2017/06/30 07:39:31
This is glorious and terrible.
P.S. I think these
jschuh
2017/07/01 06:32:00
Done.
|
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedSubOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedSubOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename MaxExponentPromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static V Do(T x, U y) { |
| + V result; |
| + return CheckedSubOp<T, U>::Do(x, y, &result) |
| + ? result |
| + : (as_unsigned(std::numeric_limits<V>::max()) + |
| + (IsValueConstantAndNegative(x) || !IsValueNegative(y))); |
|
dcheng
2017/06/30 07:39:31
Ditto.
jschuh
2017/07/01 06:31:59
Done.
|
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedMulOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedMulOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename MaxExponentPromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static V Do(T x, U y) { |
| + V result; |
| + return CheckedMulOp<T, U>::Do(x, y, &result) |
| + ? result |
| + : as_unsigned(std::numeric_limits<V>::max()) + |
| + (IsValueNegative(x) ^ IsValueNegative(y)); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedDivOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedDivOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename MaxExponentPromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static V Do(T x, U y) { |
| + V result; |
| + return CheckedDivOp<T, U>::Do(x, y, &result) |
| + ? result |
| + : as_unsigned(std::numeric_limits<V>::max()) + |
| + (IsValueNegative(x) ^ IsValueNegative(y)); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedModOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedModOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename MaxExponentPromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static V Do(T x, U y) { |
| + V result; |
| + return CheckedModOp<T, U>::Do(x, y, &result) ? result : x; |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedLshOp {}; |
| + |
| +// Left shift. Non-zero values saturate in the direction of the sign. A zero |
| +// shifted by any value always results in zero. |
| +// Note: This class template supports left shifting negative values. |
| +template <typename T, typename U> |
| +struct ClampedLshOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = T; |
| + template <typename V = result_type> |
| + static V Do(T x, U shift) { |
| + static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); |
|
dcheng
2017/06/30 07:39:30
Oh... I guess this static assert is supposed to pr
|
| + V result = x; |
| + return (shift < std::numeric_limits<T>::digits && |
| + CheckedMulOp<T, T>::Do(x, T(1) << shift, &result)) |
| + ? result |
| + : (x ? as_unsigned(std::numeric_limits<V>::max()) + |
| + IsValueNegative(x) |
| + : 0); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedRshOp {}; |
| + |
| +// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. |
| +template <typename T, typename U> |
| +struct ClampedRshOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = T; |
| + template <typename V = result_type> |
| + static V Do(T x, U shift) { |
| + static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); |
| + return shift < IntegerBitsPlusSign<T>::value |
| + ? saturated_cast<V>(x >> shift) |
| + : as_unsigned(V(0)) - IsValueNegative(x); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedAndOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedAndOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename std::make_unsigned< |
| + typename MaxExponentPromotion<T, U>::type>::type; |
| + template <typename V> |
| + static constexpr V Do(T x, U y) { |
| + return static_cast<result_type>(x) & static_cast<result_type>(y); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedOrOp {}; |
| + |
| +// For simplicity we promote to unsigned integers. |
| +template <typename T, typename U> |
| +struct ClampedOrOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename std::make_unsigned< |
| + typename MaxExponentPromotion<T, U>::type>::type; |
| + template <typename V> |
| + static constexpr V Do(T x, U y) { |
| + return static_cast<result_type>(x) | static_cast<result_type>(y); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedXorOp {}; |
| + |
| +// For simplicity we support only unsigned integers. |
| +template <typename T, typename U> |
| +struct ClampedXorOp<T, |
| + U, |
| + typename std::enable_if<std::is_integral<T>::value && |
| + std::is_integral<U>::value>::type> { |
| + using result_type = typename std::make_unsigned< |
| + typename MaxExponentPromotion<T, U>::type>::type; |
| + template <typename V> |
| + static constexpr V Do(T x, U y) { |
| + return static_cast<result_type>(x) ^ static_cast<result_type>(y); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedMaxOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedMaxOp< |
| + T, |
| + U, |
| + typename std::enable_if<std::is_arithmetic<T>::value && |
| + std::is_arithmetic<U>::value>::type> { |
| + using result_type = typename MaxExponentPromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static constexpr V Do(T x, U y) { |
| + return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) |
| + : saturated_cast<V>(y); |
| + } |
| +}; |
| + |
| +template <typename T, typename U, class Enable = void> |
| +struct ClampedMinOp {}; |
| + |
| +template <typename T, typename U> |
| +struct ClampedMinOp< |
| + T, |
| + U, |
| + typename std::enable_if<std::is_arithmetic<T>::value && |
| + std::is_arithmetic<U>::value>::type> { |
| + using result_type = typename LowestValuePromotion<T, U>::type; |
| + template <typename V = result_type> |
| + static constexpr V Do(T x, U y) { |
| + return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) |
| + : saturated_cast<V>(y); |
| + } |
| +}; |
| + |
| +// This is just boilerplate that wraps the standard floating point arithmetic. |
| +// A macro isn't the nicest solution, but it beats rewriting these repeatedly. |
| +#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ |
| + template <typename T, typename U> \ |
| + struct Clamped##NAME##Op< \ |
| + T, U, \ |
| + typename std::enable_if<std::is_floating_point<T>::value || \ |
| + std::is_floating_point<U>::value>::type> { \ |
| + using result_type = typename MaxExponentPromotion<T, U>::type; \ |
| + template <typename V = result_type> \ |
| + static constexpr V Do(T x, U y) { \ |
| + return saturated_cast<V>(x OP y); \ |
| + } \ |
| + }; |
| + |
| +BASE_FLOAT_ARITHMETIC_OPS(Add, +) |
| +BASE_FLOAT_ARITHMETIC_OPS(Sub, -) |
| +BASE_FLOAT_ARITHMETIC_OPS(Mul, *) |
| +BASE_FLOAT_ARITHMETIC_OPS(Div, /) |
| + |
| +#undef BASE_FLOAT_ARITHMETIC_OPS |
| + |
| +} // namespace internal |
| +} // namespace base |
| + |
| +#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ |