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