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> DoCheckedAddFastOp46 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> DoCheckedSubFastOp56 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> DoCheckedMulFastOp66 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> DoClampedAddFastOp76 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> DoClampedSubFastOp86 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> DoClampedMulFastOp96 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; DoClampedNegFastOp105 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<Numeric>::value, 119 bool IsFloat = std::is_floating_point<Numeric>::value> 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, 138 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 139 constexpr T NegateWrapper(T value) { 140 using UnsignedT = typename std::make_unsigned<T>::type; 141 // This will compile to a NEG on Intel, and is normal negation on ARM. 142 return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); 143 } 144 145 template < 146 typename T, 147 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> 148 constexpr T NegateWrapper(T value) { 149 return -value; 150 } 151 152 template <typename T, 153 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 154 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { 155 return ~value; 156 } 157 158 template <typename T, 159 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 160 constexpr T AbsWrapper(T value) { 161 return static_cast<T>(SafeUnsignedAbs(value)); 162 } 163 164 template < 165 typename T, 166 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> 167 constexpr T AbsWrapper(T value) { 168 return value < 0 ? -value : value; 169 } 170 171 template <template <typename, typename, typename> class M, 172 typename L, 173 typename R> 174 struct MathWrapper { 175 using math = M<typename UnderlyingType<L>::type, 176 typename UnderlyingType<R>::type, 177 void>; 178 using type = typename math::result_type; 179 }; 180 181 // The following macros are just boilerplate for the standard arithmetic 182 // operator overloads and variadic function templates. A macro isn't the nicest 183 // solution, but it beats rewriting these over and over again. 184 #define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 185 template <typename L, typename R, typename... Args> \ 186 constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs, \ 187 const Args... args) { \ 188 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ 189 args...); \ 190 } 191 192 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \ 193 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 194 template <typename L, typename R, \ 195 typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ 196 nullptr> \ 197 constexpr CLASS##Numeric< \ 198 typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ 199 operator OP(const L lhs, const R rhs) { \ 200 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ 201 rhs); \ 202 } \ 203 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 204 template <typename L> \ 205 template <typename R> \ 206 constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ 207 const R rhs) { \ 208 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 209 } \ 210 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 211 BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 212 213 } // namespace internal 214 } // namespace base 215 216 #endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 217