|  | // 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 { | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value && | 
|  | std::is_signed<T>::value>::type* = nullptr> | 
|  | constexpr T SaturatedNegWrapper(T value) { | 
|  | return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported | 
|  | ? (NegateWrapper(value) != std::numeric_limits<T>::lowest() | 
|  | ? NegateWrapper(value) | 
|  | : std::numeric_limits<T>::max()) | 
|  | : ClampedNegFastOp<T>::Do(value); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value && | 
|  | !std::is_signed<T>::value>::type* = nullptr> | 
|  | constexpr T SaturatedNegWrapper(T value) { | 
|  | return T(0); | 
|  | } | 
|  |  | 
|  | template < | 
|  | typename T, | 
|  | typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> | 
|  | constexpr T SaturatedNegWrapper(T value) { | 
|  | return -value; | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> | 
|  | constexpr T SaturatedAbsWrapper(T value) { | 
|  | // The calculation below is a static identity for unsigned types, but for | 
|  | // signed integer types it provides a non-branching, saturated absolute value. | 
|  | // This works because SafeUnsignedAbs() returns an unsigned type, which can | 
|  | // represent the absolute value of all negative numbers of an equal-width | 
|  | // integer type. The call to IsValueNegative() then detects overflow in the | 
|  | // special case of numeric_limits<T>::min(), by evaluating the bit pattern as | 
|  | // a signed integer value. If it is the overflow case, we end up subtracting | 
|  | // one from the unsigned result, thus saturating to numeric_limits<T>::max(). | 
|  | return static_cast<T>(SafeUnsignedAbs(value) - | 
|  | IsValueNegative<T>(SafeUnsignedAbs(value))); | 
|  | } | 
|  |  | 
|  | template < | 
|  | typename T, | 
|  | typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> | 
|  | constexpr T SaturatedAbsWrapper(T value) { | 
|  | return value < 0 ? -value : value; | 
|  | } | 
|  |  | 
|  | 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 constexpr V Do(T x, U y) { | 
|  | if (ClampedAddFastOp<T, U>::is_supported) | 
|  | return ClampedAddFastOp<T, U>::template Do<V>(x, y); | 
|  |  | 
|  | static_assert(std::is_same<V, result_type>::value || | 
|  | IsTypeInRangeForNumericType<U, V>::value, | 
|  | "The saturation result cannot be determined from the " | 
|  | "provided types."); | 
|  | const V saturated = CommonMaxOrMin<V>(IsValueNegative(y)); | 
|  | V result = {}; | 
|  | return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result))) | 
|  | ? result | 
|  | : saturated; | 
|  | } | 
|  | }; | 
|  |  | 
|  | 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 constexpr V Do(T x, U y) { | 
|  | // TODO(jschuh) Make this "constexpr if" once we're C++17. | 
|  | if (ClampedSubFastOp<T, U>::is_supported) | 
|  | return ClampedSubFastOp<T, U>::template Do<V>(x, y); | 
|  |  | 
|  | static_assert(std::is_same<V, result_type>::value || | 
|  | IsTypeInRangeForNumericType<U, V>::value, | 
|  | "The saturation result cannot be determined from the " | 
|  | "provided types."); | 
|  | const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y)); | 
|  | V result = {}; | 
|  | return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result))) | 
|  | ? result | 
|  | : saturated; | 
|  | } | 
|  | }; | 
|  |  | 
|  | 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 constexpr V Do(T x, U y) { | 
|  | // TODO(jschuh) Make this "constexpr if" once we're C++17. | 
|  | if (ClampedMulFastOp<T, U>::is_supported) | 
|  | return ClampedMulFastOp<T, U>::template Do<V>(x, y); | 
|  |  | 
|  | V result = {}; | 
|  | const V saturated = | 
|  | CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); | 
|  | return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result))) | 
|  | ? result | 
|  | : saturated; | 
|  | } | 
|  | }; | 
|  |  | 
|  | 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 constexpr V Do(T x, U y) { | 
|  | V result = {}; | 
|  | if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result)))) | 
|  | return result; | 
|  | // Saturation goes to max, min, or NaN (if x is zero). | 
|  | return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)) | 
|  | : SaturationDefaultLimits<V>::NaN(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | 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 constexpr V Do(T x, U y) { | 
|  | V result = {}; | 
|  | return BASE_NUMERICS_LIKELY((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. | 
|  | 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 constexpr V Do(T x, U shift) { | 
|  | static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | 
|  | if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) { | 
|  | // Shift as unsigned to avoid undefined behavior. | 
|  | V result = static_cast<V>(as_unsigned(x) << shift); | 
|  | // If the shift can be reversed, we know it was valid. | 
|  | if (BASE_NUMERICS_LIKELY(result >> shift == x)) | 
|  | return result; | 
|  | } | 
|  | return x ? CommonMaxOrMin<V>(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 constexpr V Do(T x, U shift) { | 
|  | static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | 
|  | // Signed right shift is odd, because it saturates to -1 or 0. | 
|  | const V saturated = as_unsigned(V(0)) - IsValueNegative(x); | 
|  | return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value) | 
|  | ? saturated_cast<V>(x >> shift) | 
|  | : saturated; | 
|  | } | 
|  | }; | 
|  |  | 
|  | 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_ |