xref: /aosp_15_r20/external/libchrome/base/numerics/safe_math_shared_impl.h (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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