1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 20 // Where available use builtin math overflow support on Clang and GCC. 21 #if ((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" 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 bool IsInteger = std::is_integral<Numeric>::value, 114 bool IsFloat = std::is_floating_point<Numeric>::value> 115 struct UnsignedOrFloatForSize; 116 117 template <typename Numeric> 118 struct UnsignedOrFloatForSize<Numeric, true, false> { 119 using type = typename std::make_unsigned<Numeric>::type; 120 }; 121 122 template <typename Numeric> 123 struct UnsignedOrFloatForSize<Numeric, false, true> { 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 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 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 < 141 typename T, 142 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> 143 constexpr T NegateWrapper(T value) { 144 return -value; 145 } 146 147 template <typename T, 148 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 149 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { 150 return ~value; 151 } 152 153 template <typename T, 154 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 155 constexpr T AbsWrapper(T value) { 156 return static_cast<T>(SafeUnsignedAbs(value)); 157 } 158 159 template < 160 typename T, 161 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> 162 constexpr T AbsWrapper(T value) { 163 return value < 0 ? -value : value; 164 } 165 166 template <template <typename, typename, typename> class M, 167 typename L, 168 typename R> 169 struct MathWrapper { 170 using math = M<typename UnderlyingType<L>::type, 171 typename UnderlyingType<R>::type, 172 void>; 173 using type = typename math::result_type; 174 }; 175 176 // These variadic templates work out the return types. 177 // TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support. 178 template <template <typename, typename, typename> class M, 179 typename L, 180 typename R, 181 typename... Args> 182 struct ResultType; 183 184 template <template <typename, typename, typename> class M, 185 typename L, 186 typename R> 187 struct ResultType<M, L, R> { 188 using type = typename MathWrapper<M, L, R>::type; 189 }; 190 191 template <template <typename, typename, typename> class M, 192 typename L, 193 typename R, 194 typename... Args> 195 struct ResultType { 196 using type = 197 typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type; 198 }; 199 200 // The following macros are just boilerplate for the standard arithmetic 201 // operator overloads and variadic function templates. A macro isn't the nicest 202 // solution, but it beats rewriting these over and over again. 203 #define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 204 template <typename L, typename R, typename... Args> \ 205 constexpr CLASS##Numeric< \ 206 typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \ 207 CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \ 208 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ 209 args...); \ 210 } 211 212 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \ 213 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 214 template <typename L, typename R, \ 215 typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ 216 nullptr> \ 217 constexpr CLASS##Numeric< \ 218 typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ 219 operator OP(const L lhs, const R rhs) { \ 220 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ 221 rhs); \ 222 } \ 223 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 224 template <typename L> \ 225 template <typename R> \ 226 constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ 227 const R rhs) { \ 228 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 229 } \ 230 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 231 BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 232 233 } // namespace internal 234 } // namespace base 235 236 #endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 237