safe_math_shared_impl.h (7719B)
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_SAFE_MATH_SHARED_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <cassert> 12 #include <climits> 13 #include <cmath> 14 #include <cstdlib> 15 #include <limits> 16 #include <type_traits> 17 18 #include "base/numerics/safe_conversions.h" 19 #include "build/build_config.h" 20 21 #if BUILDFLAG(IS_ASMJS) 22 // Optimized safe math instructions are incompatible with asmjs. 23 #define BASE_HAS_OPTIMIZED_SAFE_MATH (0) 24 // Where available use builtin math overflow support on Clang and GCC. 25 #elif !defined(__native_client__) && \ 26 ((defined(__clang__) && \ 27 ((__clang_major__ > 3) || \ 28 (__clang_major__ == 3 && __clang_minor__ >= 4))) || \ 29 (defined(__GNUC__) && __GNUC__ >= 5)) 30 #include "base/numerics/safe_math_clang_gcc_impl.h" 31 #define BASE_HAS_OPTIMIZED_SAFE_MATH (1) 32 #else 33 #define BASE_HAS_OPTIMIZED_SAFE_MATH (0) 34 #endif 35 36 namespace base { 37 namespace internal { 38 39 // These are the non-functioning boilerplate implementations of the optimized 40 // safe math routines. 41 #if !BASE_HAS_OPTIMIZED_SAFE_MATH 42 template <typename T, typename U> 43 struct CheckedAddFastOp { 44 static const bool is_supported = false; 45 template <typename V> 46 static constexpr bool Do(T, U, V*) { 47 // Force a compile failure if instantiated. 48 return CheckOnFailure::template HandleFailure<bool>(); 49 } 50 }; 51 52 template <typename T, typename U> 53 struct CheckedSubFastOp { 54 static const bool is_supported = false; 55 template <typename V> 56 static constexpr bool Do(T, U, V*) { 57 // Force a compile failure if instantiated. 58 return CheckOnFailure::template HandleFailure<bool>(); 59 } 60 }; 61 62 template <typename T, typename U> 63 struct CheckedMulFastOp { 64 static const bool is_supported = false; 65 template <typename V> 66 static constexpr bool Do(T, U, V*) { 67 // Force a compile failure if instantiated. 68 return CheckOnFailure::template HandleFailure<bool>(); 69 } 70 }; 71 72 template <typename T, typename U> 73 struct ClampedAddFastOp { 74 static const bool is_supported = false; 75 template <typename V> 76 static constexpr V Do(T, U) { 77 // Force a compile failure if instantiated. 78 return CheckOnFailure::template HandleFailure<V>(); 79 } 80 }; 81 82 template <typename T, typename U> 83 struct ClampedSubFastOp { 84 static const bool is_supported = false; 85 template <typename V> 86 static constexpr V Do(T, U) { 87 // Force a compile failure if instantiated. 88 return CheckOnFailure::template HandleFailure<V>(); 89 } 90 }; 91 92 template <typename T, typename U> 93 struct ClampedMulFastOp { 94 static const bool is_supported = false; 95 template <typename V> 96 static constexpr V Do(T, U) { 97 // Force a compile failure if instantiated. 98 return CheckOnFailure::template HandleFailure<V>(); 99 } 100 }; 101 102 template <typename T> 103 struct ClampedNegFastOp { 104 static const bool is_supported = false; 105 static constexpr T Do(T) { 106 // Force a compile failure if instantiated. 107 return CheckOnFailure::template HandleFailure<T>(); 108 } 109 }; 110 #endif // BASE_HAS_OPTIMIZED_SAFE_MATH 111 #undef BASE_HAS_OPTIMIZED_SAFE_MATH 112 113 // This is used for UnsignedAbs, where we need to support floating-point 114 // template instantiations even though we don't actually support the operations. 115 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, 116 // so the float versions will not compile. 117 template <typename Numeric, 118 bool IsInteger = std::is_integral_v<Numeric>, 119 bool IsFloat = std::is_floating_point_v<Numeric>> 120 struct UnsignedOrFloatForSize; 121 122 template <typename Numeric> 123 struct UnsignedOrFloatForSize<Numeric, true, false> { 124 using type = typename std::make_unsigned<Numeric>::type; 125 }; 126 127 template <typename Numeric> 128 struct UnsignedOrFloatForSize<Numeric, false, true> { 129 using type = Numeric; 130 }; 131 132 // Wrap the unary operations to allow SFINAE when instantiating integrals versus 133 // floating points. These don't perform any overflow checking. Rather, they 134 // exhibit well-defined overflow semantics and rely on the caller to detect 135 // if an overflow occurred. 136 137 template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr> 138 constexpr T NegateWrapper(T value) { 139 using UnsignedT = typename std::make_unsigned<T>::type; 140 // This will compile to a NEG on Intel, and is normal negation on ARM. 141 return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); 142 } 143 144 template <typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr> 145 constexpr T NegateWrapper(T value) { 146 return -value; 147 } 148 149 template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr> 150 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { 151 return ~value; 152 } 153 154 template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr> 155 constexpr T AbsWrapper(T value) { 156 return static_cast<T>(SafeUnsignedAbs(value)); 157 } 158 159 template <typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr> 160 constexpr T AbsWrapper(T value) { 161 return value < 0 ? -value : value; 162 } 163 164 template <template <typename, typename, typename> class M, 165 typename L, 166 typename R> 167 struct MathWrapper { 168 using math = M<typename UnderlyingType<L>::type, 169 typename UnderlyingType<R>::type, 170 void>; 171 using type = typename math::result_type; 172 }; 173 174 // The following macros are just boilerplate for the standard arithmetic 175 // operator overloads and variadic function templates. A macro isn't the nicest 176 // solution, but it beats rewriting these over and over again. 177 #define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 178 template <typename L, typename R, typename... Args> \ 179 constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs, \ 180 const Args... args) { \ 181 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ 182 args...); \ 183 } 184 185 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \ 186 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 187 template <typename L, typename R, \ 188 std::enable_if_t<Is##CLASS##Op<L, R>::value>* = nullptr> \ 189 constexpr CLASS##Numeric< \ 190 typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ 191 operator OP(const L lhs, const R rhs) { \ 192 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ 193 rhs); \ 194 } \ 195 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 196 template <typename L> \ 197 template <typename R> \ 198 constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ 199 const R rhs) { \ 200 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 201 } \ 202 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 203 BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 204 205 } // namespace internal 206 } // namespace base 207 208 #endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_