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