xref: /aosp_15_r20/external/pdfium/third_party/base/numerics/clamped_math_impl.h (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
6 #define THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <climits>
12 #include <cmath>
13 #include <cstdlib>
14 #include <limits>
15 #include <type_traits>
16 
17 #include "third_party/base/numerics/checked_math.h"
18 #include "third_party/base/numerics/safe_conversions.h"
19 #include "third_party/base/numerics/safe_math_shared_impl.h"
20 
21 namespace pdfium {
22 namespace base {
23 namespace internal {
24 
25 template <typename T,
26           typename std::enable_if<std::is_integral<T>::value &&
27                                   std::is_signed<T>::value>::type* = nullptr>
SaturatedNegWrapper(T value)28 constexpr T SaturatedNegWrapper(T value) {
29   return IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported
30              ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
31                     ? NegateWrapper(value)
32                     : std::numeric_limits<T>::max())
33              : ClampedNegFastOp<T>::Do(value);
34 }
35 
36 template <typename T,
37           typename std::enable_if<std::is_integral<T>::value &&
38                                   !std::is_signed<T>::value>::type* = nullptr>
SaturatedNegWrapper(T value)39 constexpr T SaturatedNegWrapper(T value) {
40   return T(0);
41 }
42 
43 template <
44     typename T,
45     typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
SaturatedNegWrapper(T value)46 constexpr T SaturatedNegWrapper(T value) {
47   return -value;
48 }
49 
50 template <typename T,
51           typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
SaturatedAbsWrapper(T value)52 constexpr T SaturatedAbsWrapper(T value) {
53   // The calculation below is a static identity for unsigned types, but for
54   // signed integer types it provides a non-branching, saturated absolute value.
55   // This works because SafeUnsignedAbs() returns an unsigned type, which can
56   // represent the absolute value of all negative numbers of an equal-width
57   // integer type. The call to IsValueNegative() then detects overflow in the
58   // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
59   // a signed integer value. If it is the overflow case, we end up subtracting
60   // one from the unsigned result, thus saturating to numeric_limits<T>::max().
61   return static_cast<T>(
62       SafeUnsignedAbs(value) -
63       IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value))));
64 }
65 
66 template <
67     typename T,
68     typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
SaturatedAbsWrapper(T value)69 constexpr T SaturatedAbsWrapper(T value) {
70   return value < 0 ? -value : value;
71 }
72 
73 template <typename T, typename U, class Enable = void>
74 struct ClampedAddOp {};
75 
76 template <typename T, typename U>
77 struct ClampedAddOp<T,
78                     U,
79                     typename std::enable_if<std::is_integral<T>::value &&
80                                             std::is_integral<U>::value>::type> {
81   using result_type = typename MaxExponentPromotion<T, U>::type;
82   template <typename V = result_type>
83   static constexpr V Do(T x, U y) {
84     if (!IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported)
85       return ClampedAddFastOp<T, U>::template Do<V>(x, y);
86 
87     static_assert(std::is_same<V, result_type>::value ||
88                       IsTypeInRangeForNumericType<U, V>::value,
89                   "The saturation result cannot be determined from the "
90                   "provided types.");
91     const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
92     V result = {};
93     return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
94                ? result
95                : saturated;
96   }
97 };
98 
99 template <typename T, typename U, class Enable = void>
100 struct ClampedSubOp {};
101 
102 template <typename T, typename U>
103 struct ClampedSubOp<T,
104                     U,
105                     typename std::enable_if<std::is_integral<T>::value &&
106                                             std::is_integral<U>::value>::type> {
107   using result_type = typename MaxExponentPromotion<T, U>::type;
108   template <typename V = result_type>
109   static constexpr V Do(T x, U y) {
110     if (!IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported)
111       return ClampedSubFastOp<T, U>::template Do<V>(x, y);
112 
113     static_assert(std::is_same<V, result_type>::value ||
114                       IsTypeInRangeForNumericType<U, V>::value,
115                   "The saturation result cannot be determined from the "
116                   "provided types.");
117     const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
118     V result = {};
119     return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
120                ? result
121                : saturated;
122   }
123 };
124 
125 template <typename T, typename U, class Enable = void>
126 struct ClampedMulOp {};
127 
128 template <typename T, typename U>
129 struct ClampedMulOp<T,
130                     U,
131                     typename std::enable_if<std::is_integral<T>::value &&
132                                             std::is_integral<U>::value>::type> {
133   using result_type = typename MaxExponentPromotion<T, U>::type;
134   template <typename V = result_type>
135   static constexpr V Do(T x, U y) {
136     if (!IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported)
137       return ClampedMulFastOp<T, U>::template Do<V>(x, y);
138 
139     V result = {};
140     const V saturated =
141         CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
142     return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
143                ? result
144                : saturated;
145   }
146 };
147 
148 template <typename T, typename U, class Enable = void>
149 struct ClampedDivOp {};
150 
151 template <typename T, typename U>
152 struct ClampedDivOp<T,
153                     U,
154                     typename std::enable_if<std::is_integral<T>::value &&
155                                             std::is_integral<U>::value>::type> {
156   using result_type = typename MaxExponentPromotion<T, U>::type;
157   template <typename V = result_type>
158   static constexpr V Do(T x, U y) {
159     V result = {};
160     if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
161       return result;
162     // Saturation goes to max, min, or NaN (if x is zero).
163     return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
164              : SaturationDefaultLimits<V>::NaN();
165   }
166 };
167 
168 template <typename T, typename U, class Enable = void>
169 struct ClampedModOp {};
170 
171 template <typename T, typename U>
172 struct ClampedModOp<T,
173                     U,
174                     typename std::enable_if<std::is_integral<T>::value &&
175                                             std::is_integral<U>::value>::type> {
176   using result_type = typename MaxExponentPromotion<T, U>::type;
177   template <typename V = result_type>
178   static constexpr V Do(T x, U y) {
179     V result = {};
180     return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
181                ? result
182                : x;
183   }
184 };
185 
186 template <typename T, typename U, class Enable = void>
187 struct ClampedLshOp {};
188 
189 // Left shift. Non-zero values saturate in the direction of the sign. A zero
190 // shifted by any value always results in zero.
191 template <typename T, typename U>
192 struct ClampedLshOp<T,
193                     U,
194                     typename std::enable_if<std::is_integral<T>::value &&
195                                             std::is_integral<U>::value>::type> {
196   using result_type = T;
197   template <typename V = result_type>
198   static constexpr V Do(T x, U shift) {
199     static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
200     if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
201       // Shift as unsigned to avoid undefined behavior.
202       V result = static_cast<V>(as_unsigned(x) << shift);
203       // If the shift can be reversed, we know it was valid.
204       if (BASE_NUMERICS_LIKELY(result >> shift == x))
205         return result;
206     }
207     return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
208   }
209 };
210 
211 template <typename T, typename U, class Enable = void>
212 struct ClampedRshOp {};
213 
214 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
215 template <typename T, typename U>
216 struct ClampedRshOp<T,
217                     U,
218                     typename std::enable_if<std::is_integral<T>::value &&
219                                             std::is_integral<U>::value>::type> {
220   using result_type = T;
221   template <typename V = result_type>
222   static constexpr V Do(T x, U shift) {
223     static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
224     // Signed right shift is odd, because it saturates to -1 or 0.
225     const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
226     return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
227                ? saturated_cast<V>(x >> shift)
228                : saturated;
229   }
230 };
231 
232 template <typename T, typename U, class Enable = void>
233 struct ClampedAndOp {};
234 
235 template <typename T, typename U>
236 struct ClampedAndOp<T,
237                     U,
238                     typename std::enable_if<std::is_integral<T>::value &&
239                                             std::is_integral<U>::value>::type> {
240   using result_type = typename std::make_unsigned<
241       typename MaxExponentPromotion<T, U>::type>::type;
242   template <typename V>
243   static constexpr V Do(T x, U y) {
244     return static_cast<result_type>(x) & static_cast<result_type>(y);
245   }
246 };
247 
248 template <typename T, typename U, class Enable = void>
249 struct ClampedOrOp {};
250 
251 // For simplicity we promote to unsigned integers.
252 template <typename T, typename U>
253 struct ClampedOrOp<T,
254                    U,
255                    typename std::enable_if<std::is_integral<T>::value &&
256                                            std::is_integral<U>::value>::type> {
257   using result_type = typename std::make_unsigned<
258       typename MaxExponentPromotion<T, U>::type>::type;
259   template <typename V>
260   static constexpr V Do(T x, U y) {
261     return static_cast<result_type>(x) | static_cast<result_type>(y);
262   }
263 };
264 
265 template <typename T, typename U, class Enable = void>
266 struct ClampedXorOp {};
267 
268 // For simplicity we support only unsigned integers.
269 template <typename T, typename U>
270 struct ClampedXorOp<T,
271                     U,
272                     typename std::enable_if<std::is_integral<T>::value &&
273                                             std::is_integral<U>::value>::type> {
274   using result_type = typename std::make_unsigned<
275       typename MaxExponentPromotion<T, U>::type>::type;
276   template <typename V>
277   static constexpr V Do(T x, U y) {
278     return static_cast<result_type>(x) ^ static_cast<result_type>(y);
279   }
280 };
281 
282 template <typename T, typename U, class Enable = void>
283 struct ClampedMaxOp {};
284 
285 template <typename T, typename U>
286 struct ClampedMaxOp<
287     T,
288     U,
289     typename std::enable_if<std::is_arithmetic<T>::value &&
290                             std::is_arithmetic<U>::value>::type> {
291   using result_type = typename MaxExponentPromotion<T, U>::type;
292   template <typename V = result_type>
293   static constexpr V Do(T x, U y) {
294     return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
295                                        : saturated_cast<V>(y);
296   }
297 };
298 
299 template <typename T, typename U, class Enable = void>
300 struct ClampedMinOp {};
301 
302 template <typename T, typename U>
303 struct ClampedMinOp<
304     T,
305     U,
306     typename std::enable_if<std::is_arithmetic<T>::value &&
307                             std::is_arithmetic<U>::value>::type> {
308   using result_type = typename LowestValuePromotion<T, U>::type;
309   template <typename V = result_type>
310   static constexpr V Do(T x, U y) {
311     return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
312                                     : saturated_cast<V>(y);
313   }
314 };
315 
316 // This is just boilerplate that wraps the standard floating point arithmetic.
317 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
318 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
319   template <typename T, typename U>                                      \
320   struct Clamped##NAME##Op<                                              \
321       T, U,                                                              \
322       typename std::enable_if<std::is_floating_point<T>::value ||        \
323                               std::is_floating_point<U>::value>::type> { \
324     using result_type = typename MaxExponentPromotion<T, U>::type;       \
325     template <typename V = result_type>                                  \
326     static constexpr V Do(T x, U y) {                                    \
327       return saturated_cast<V>(x OP y);                                  \
328     }                                                                    \
329   };
330 
331 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
332 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
333 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
334 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
335 
336 #undef BASE_FLOAT_ARITHMETIC_OPS
337 
338 }  // namespace internal
339 }  // namespace base
340 }  // namespace pdfium
341 
342 #endif  // THIRD_PARTY_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
343