blob: cbd96cbf20a18607adb11a8d45eb8986f9fa092d [file] [log] [blame]
// 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 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) {
// TODO(jschuh) Make this "constexpr if" once we're C++17.
if (ClampedAddFastOp<T, U>::is_supported)
return ClampedAddFastOp<T, U>::template Do<V>(x, y);
V result;
return CheckedAddOp<T, U>::Do(x, y, &result)
? result
// Prefer a compile-time constant (if we have one).
: GetMaxOrMin<V>(IsCompileTimeConstant(x) ? IsValueNegative(x)
: IsValueNegative(y));
}
};
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) {
// 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);
V result;
return CheckedSubOp<T, U>::Do(x, y, &result)
? result
// Prefer a compile-time constant (if we have one).
: GetMaxOrMin<V>(IsCompileTimeConstant(x) ? IsValueNegative(x)
: !IsValueNegative(y));
}
};
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) {
// 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;
return CheckedMulOp<T, U>::Do(x, y, &result)
? result
: GetMaxOrMin<V>(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 = SaturationDefaultLimits<V>::NaN();
return !x || CheckedDivOp<T, U>::Do(x, y, &result)
? result
: GetMaxOrMin<V>(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.");
V result = x;
return (shift < std::numeric_limits<T>::digits &&
CheckedMulOp<T, T>::Do(x, T(1) << shift, &result))
? result
: (x ? GetMaxOrMin<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 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)
// Signed right shift is odd, because it saturates to -1 or 0.
: 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_