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(__native_client__) && \ 22 ((defined(__clang__) && \ 23 ((__clang_major__ > 3) || \ 24 (__clang_major__ == 3 && __clang_minor__ >= 4))) || \ 25 (defined(__GNUC__) && __GNUC__ >= 5)) 26 #include "base/numerics/safe_math_clang_gcc_impl.h" 27 #define BASE_HAS_OPTIMIZED_SAFE_MATH (1) 28 #else 29 #define BASE_HAS_OPTIMIZED_SAFE_MATH (0) 30 #endif 31 32 namespace base { 33 namespace internal { 34 35 // These are the non-functioning boilerplate implementations of the optimized 36 // safe math routines. 37 #if !BASE_HAS_OPTIMIZED_SAFE_MATH 38 template <typename T, typename U> 39 struct CheckedAddFastOp { 40 static const bool is_supported = false; 41 template <typename V> DoCheckedAddFastOp42 static constexpr bool Do(T, U, V*) { 43 // Force a compile failure if instantiated. 44 return CheckOnFailure::template HandleFailure<bool>(); 45 } 46 }; 47 48 template <typename T, typename U> 49 struct CheckedSubFastOp { 50 static const bool is_supported = false; 51 template <typename V> DoCheckedSubFastOp52 static constexpr bool Do(T, U, V*) { 53 // Force a compile failure if instantiated. 54 return CheckOnFailure::template HandleFailure<bool>(); 55 } 56 }; 57 58 template <typename T, typename U> 59 struct CheckedMulFastOp { 60 static const bool is_supported = false; 61 template <typename V> DoCheckedMulFastOp62 static constexpr bool Do(T, U, V*) { 63 // Force a compile failure if instantiated. 64 return CheckOnFailure::template HandleFailure<bool>(); 65 } 66 }; 67 68 template <typename T, typename U> 69 struct ClampedAddFastOp { 70 static const bool is_supported = false; 71 template <typename V> DoClampedAddFastOp72 static constexpr V Do(T, U) { 73 // Force a compile failure if instantiated. 74 return CheckOnFailure::template HandleFailure<V>(); 75 } 76 }; 77 78 template <typename T, typename U> 79 struct ClampedSubFastOp { 80 static const bool is_supported = false; 81 template <typename V> DoClampedSubFastOp82 static constexpr V Do(T, U) { 83 // Force a compile failure if instantiated. 84 return CheckOnFailure::template HandleFailure<V>(); 85 } 86 }; 87 88 template <typename T, typename U> 89 struct ClampedMulFastOp { 90 static const bool is_supported = false; 91 template <typename V> DoClampedMulFastOp92 static constexpr V Do(T, U) { 93 // Force a compile failure if instantiated. 94 return CheckOnFailure::template HandleFailure<V>(); 95 } 96 }; 97 98 template <typename T> 99 struct ClampedNegFastOp { 100 static const bool is_supported = false; DoClampedNegFastOp101 static constexpr T Do(T) { 102 // Force a compile failure if instantiated. 103 return CheckOnFailure::template HandleFailure<T>(); 104 } 105 }; 106 #endif // BASE_HAS_OPTIMIZED_SAFE_MATH 107 #undef BASE_HAS_OPTIMIZED_SAFE_MATH 108 109 // This is used for UnsignedAbs, where we need to support floating-point 110 // template instantiations even though we don't actually support the operations. 111 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, 112 // so the float versions will not compile. 113 template <typename Numeric, 114 bool IsInteger = std::is_integral<Numeric>::value, 115 bool IsFloat = std::is_floating_point<Numeric>::value> 116 struct UnsignedOrFloatForSize; 117 118 template <typename Numeric> 119 struct UnsignedOrFloatForSize<Numeric, true, false> { 120 using type = typename std::make_unsigned<Numeric>::type; 121 }; 122 123 template <typename Numeric> 124 struct UnsignedOrFloatForSize<Numeric, false, true> { 125 using type = Numeric; 126 }; 127 128 // Wrap the unary operations to allow SFINAE when instantiating integrals versus 129 // floating points. These don't perform any overflow checking. Rather, they 130 // exhibit well-defined overflow semantics and rely on the caller to detect 131 // if an overflow occured. 132 133 template <typename T, 134 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 135 constexpr T NegateWrapper(T value) { 136 using UnsignedT = typename std::make_unsigned<T>::type; 137 // This will compile to a NEG on Intel, and is normal negation on ARM. 138 return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); 139 } 140 141 template < 142 typename T, 143 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> 144 constexpr T NegateWrapper(T value) { 145 return -value; 146 } 147 148 template <typename T, 149 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 150 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { 151 return ~value; 152 } 153 154 template <typename T, 155 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> 156 constexpr T AbsWrapper(T value) { 157 return static_cast<T>(SafeUnsignedAbs(value)); 158 } 159 160 template < 161 typename T, 162 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> 163 constexpr T AbsWrapper(T value) { 164 return value < 0 ? -value : value; 165 } 166 167 template <template <typename, typename, typename> class M, 168 typename L, 169 typename R> 170 struct MathWrapper { 171 using math = M<typename UnderlyingType<L>::type, 172 typename UnderlyingType<R>::type, 173 void>; 174 using type = typename math::result_type; 175 }; 176 177 // These variadic templates work out the return types. 178 // TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support. 179 template <template <typename, typename, typename> class M, 180 typename L, 181 typename R, 182 typename... Args> 183 struct ResultType; 184 185 template <template <typename, typename, typename> class M, 186 typename L, 187 typename R> 188 struct ResultType<M, L, R> { 189 using type = typename MathWrapper<M, L, R>::type; 190 }; 191 192 template <template <typename, typename, typename> class M, 193 typename L, 194 typename R, 195 typename... Args> 196 struct ResultType { 197 using type = 198 typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type; 199 }; 200 201 // The following macros are just boilerplate for the standard arithmetic 202 // operator overloads and variadic function templates. A macro isn't the nicest 203 // solution, but it beats rewriting these over and over again. 204 #define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 205 template <typename L, typename R, typename... Args> \ 206 constexpr CLASS##Numeric< \ 207 typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \ 208 CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \ 209 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ 210 args...); \ 211 } 212 213 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \ 214 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 215 template <typename L, typename R, \ 216 typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ 217 nullptr> \ 218 constexpr CLASS##Numeric< \ 219 typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ 220 operator OP(const L lhs, const R rhs) { \ 221 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ 222 rhs); \ 223 } \ 224 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 225 template <typename L> \ 226 template <typename R> \ 227 constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ 228 const R rhs) { \ 229 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 230 } \ 231 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 232 BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 233 234 } // namespace internal 235 } // namespace base 236 237 #endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 238