tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

clamped_math_impl.h (11471B)


      1 // Copyright 2017 The Chromium Authors
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
      6 #define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
      7 
      8 #include <stddef.h>
      9 #include <stdint.h>
     10 
     11 #include <climits>
     12 #include <cmath>
     13 #include <cstdlib>
     14 #include <limits>
     15 #include <type_traits>
     16 
     17 #include "base/numerics/checked_math.h"
     18 #include "base/numerics/safe_conversions.h"
     19 #include "base/numerics/safe_math_shared_impl.h"
     20 
     21 namespace base {
     22 namespace internal {
     23 
     24 template <
     25    typename T,
     26    std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>>* = nullptr>
     27 constexpr T SaturatedNegWrapper(T value) {
     28  return IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported
     29             ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
     30                    ? NegateWrapper(value)
     31                    : std::numeric_limits<T>::max())
     32             : ClampedNegFastOp<T>::Do(value);
     33 }
     34 
     35 template <
     36    typename T,
     37    std::enable_if_t<std::is_integral_v<T> && !std::is_signed_v<T>>* = nullptr>
     38 constexpr T SaturatedNegWrapper(T value) {
     39  return T(0);
     40 }
     41 
     42 template <typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr>
     43 constexpr T SaturatedNegWrapper(T value) {
     44  return -value;
     45 }
     46 
     47 template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
     48 constexpr T SaturatedAbsWrapper(T value) {
     49  // The calculation below is a static identity for unsigned types, but for
     50  // signed integer types it provides a non-branching, saturated absolute value.
     51  // This works because SafeUnsignedAbs() returns an unsigned type, which can
     52  // represent the absolute value of all negative numbers of an equal-width
     53  // integer type. The call to IsValueNegative() then detects overflow in the
     54  // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
     55  // a signed integer value. If it is the overflow case, we end up subtracting
     56  // one from the unsigned result, thus saturating to numeric_limits<T>::max().
     57  return static_cast<T>(
     58      SafeUnsignedAbs(value) -
     59      IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value))));
     60 }
     61 
     62 template <typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr>
     63 constexpr T SaturatedAbsWrapper(T value) {
     64  return value < 0 ? -value : value;
     65 }
     66 
     67 template <typename T, typename U, class Enable = void>
     68 struct ClampedAddOp {};
     69 
     70 template <typename T, typename U>
     71 struct ClampedAddOp<
     72    T,
     73    U,
     74    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
     75  using result_type = typename MaxExponentPromotion<T, U>::type;
     76  template <typename V = result_type>
     77  static constexpr V Do(T x, U y) {
     78    if (!IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported)
     79      return ClampedAddFastOp<T, U>::template Do<V>(x, y);
     80 
     81    static_assert(std::is_same_v<V, result_type> ||
     82                      IsTypeInRangeForNumericType<U, V>::value,
     83                  "The saturation result cannot be determined from the "
     84                  "provided types.");
     85    const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
     86    V result = {};
     87    return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
     88               ? result
     89               : saturated;
     90  }
     91 };
     92 
     93 template <typename T, typename U, class Enable = void>
     94 struct ClampedSubOp {};
     95 
     96 template <typename T, typename U>
     97 struct ClampedSubOp<
     98    T,
     99    U,
    100    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    101  using result_type = typename MaxExponentPromotion<T, U>::type;
    102  template <typename V = result_type>
    103  static constexpr V Do(T x, U y) {
    104    if (!IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported)
    105      return ClampedSubFastOp<T, U>::template Do<V>(x, y);
    106 
    107    static_assert(std::is_same_v<V, result_type> ||
    108                      IsTypeInRangeForNumericType<U, V>::value,
    109                  "The saturation result cannot be determined from the "
    110                  "provided types.");
    111    const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
    112    V result = {};
    113    return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
    114               ? result
    115               : saturated;
    116  }
    117 };
    118 
    119 template <typename T, typename U, class Enable = void>
    120 struct ClampedMulOp {};
    121 
    122 template <typename T, typename U>
    123 struct ClampedMulOp<
    124    T,
    125    U,
    126    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    127  using result_type = typename MaxExponentPromotion<T, U>::type;
    128  template <typename V = result_type>
    129  static constexpr V Do(T x, U y) {
    130    if (!IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported)
    131      return ClampedMulFastOp<T, U>::template Do<V>(x, y);
    132 
    133    V result = {};
    134    const V saturated =
    135        CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
    136    return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
    137               ? result
    138               : saturated;
    139  }
    140 };
    141 
    142 template <typename T, typename U, class Enable = void>
    143 struct ClampedDivOp {};
    144 
    145 template <typename T, typename U>
    146 struct ClampedDivOp<
    147    T,
    148    U,
    149    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    150  using result_type = typename MaxExponentPromotion<T, U>::type;
    151  template <typename V = result_type>
    152  static constexpr V Do(T x, U y) {
    153    V result = {};
    154    if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
    155      return result;
    156    // Saturation goes to max, min, or NaN (if x is zero).
    157    return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
    158             : SaturationDefaultLimits<V>::NaN();
    159  }
    160 };
    161 
    162 template <typename T, typename U, class Enable = void>
    163 struct ClampedModOp {};
    164 
    165 template <typename T, typename U>
    166 struct ClampedModOp<
    167    T,
    168    U,
    169    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    170  using result_type = typename MaxExponentPromotion<T, U>::type;
    171  template <typename V = result_type>
    172  static constexpr V Do(T x, U y) {
    173    V result = {};
    174    return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
    175               ? result
    176               : x;
    177  }
    178 };
    179 
    180 template <typename T, typename U, class Enable = void>
    181 struct ClampedLshOp {};
    182 
    183 // Left shift. Non-zero values saturate in the direction of the sign. A zero
    184 // shifted by any value always results in zero.
    185 template <typename T, typename U>
    186 struct ClampedLshOp<
    187    T,
    188    U,
    189    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    190  using result_type = T;
    191  template <typename V = result_type>
    192  static constexpr V Do(T x, U shift) {
    193    static_assert(!std::is_signed_v<U>, "Shift value must be unsigned.");
    194    if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
    195      // Shift as unsigned to avoid undefined behavior.
    196      V result = static_cast<V>(as_unsigned(x) << shift);
    197      // If the shift can be reversed, we know it was valid.
    198      if (BASE_NUMERICS_LIKELY(result >> shift == x))
    199        return result;
    200    }
    201    return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
    202  }
    203 };
    204 
    205 template <typename T, typename U, class Enable = void>
    206 struct ClampedRshOp {};
    207 
    208 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
    209 template <typename T, typename U>
    210 struct ClampedRshOp<
    211    T,
    212    U,
    213    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    214  using result_type = T;
    215  template <typename V = result_type>
    216  static constexpr V Do(T x, U shift) {
    217    static_assert(!std::is_signed_v<U>, "Shift value must be unsigned.");
    218    // Signed right shift is odd, because it saturates to -1 or 0.
    219    const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
    220    return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
    221               ? saturated_cast<V>(x >> shift)
    222               : saturated;
    223  }
    224 };
    225 
    226 template <typename T, typename U, class Enable = void>
    227 struct ClampedAndOp {};
    228 
    229 template <typename T, typename U>
    230 struct ClampedAndOp<
    231    T,
    232    U,
    233    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    234  using result_type = typename std::make_unsigned<
    235      typename MaxExponentPromotion<T, U>::type>::type;
    236  template <typename V>
    237  static constexpr V Do(T x, U y) {
    238    return static_cast<result_type>(x) & static_cast<result_type>(y);
    239  }
    240 };
    241 
    242 template <typename T, typename U, class Enable = void>
    243 struct ClampedOrOp {};
    244 
    245 // For simplicity we promote to unsigned integers.
    246 template <typename T, typename U>
    247 struct ClampedOrOp<
    248    T,
    249    U,
    250    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    251  using result_type = typename std::make_unsigned<
    252      typename MaxExponentPromotion<T, U>::type>::type;
    253  template <typename V>
    254  static constexpr V Do(T x, U y) {
    255    return static_cast<result_type>(x) | static_cast<result_type>(y);
    256  }
    257 };
    258 
    259 template <typename T, typename U, class Enable = void>
    260 struct ClampedXorOp {};
    261 
    262 // For simplicity we support only unsigned integers.
    263 template <typename T, typename U>
    264 struct ClampedXorOp<
    265    T,
    266    U,
    267    std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>> {
    268  using result_type = typename std::make_unsigned<
    269      typename MaxExponentPromotion<T, U>::type>::type;
    270  template <typename V>
    271  static constexpr V Do(T x, U y) {
    272    return static_cast<result_type>(x) ^ static_cast<result_type>(y);
    273  }
    274 };
    275 
    276 template <typename T, typename U, class Enable = void>
    277 struct ClampedMaxOp {};
    278 
    279 template <typename T, typename U>
    280 struct ClampedMaxOp<
    281    T,
    282    U,
    283    std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>>> {
    284  using result_type = typename MaxExponentPromotion<T, U>::type;
    285  template <typename V = result_type>
    286  static constexpr V Do(T x, U y) {
    287    return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
    288                                       : saturated_cast<V>(y);
    289  }
    290 };
    291 
    292 template <typename T, typename U, class Enable = void>
    293 struct ClampedMinOp {};
    294 
    295 template <typename T, typename U>
    296 struct ClampedMinOp<
    297    T,
    298    U,
    299    std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>>> {
    300  using result_type = typename LowestValuePromotion<T, U>::type;
    301  template <typename V = result_type>
    302  static constexpr V Do(T x, U y) {
    303    return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
    304                                    : saturated_cast<V>(y);
    305  }
    306 };
    307 
    308 // This is just boilerplate that wraps the standard floating point arithmetic.
    309 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
    310 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                                 \
    311  template <typename T, typename U>                                         \
    312  struct Clamped##NAME##Op<T, U,                                            \
    313                           std::enable_if_t<std::is_floating_point_v<T> ||  \
    314                                            std::is_floating_point_v<U>>> { \
    315    using result_type = typename MaxExponentPromotion<T, U>::type;          \
    316    template <typename V = result_type>                                     \
    317    static constexpr V Do(T x, U y) {                                       \
    318      return saturated_cast<V>(x OP y);                                     \
    319    }                                                                       \
    320  };
    321 
    322 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
    323 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
    324 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
    325 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
    326 
    327 #undef BASE_FLOAT_ARITHMETIC_OPS
    328 
    329 }  // namespace internal
    330 }  // namespace base
    331 
    332 #endif  // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_