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