Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: base/numerics/clamped_math_impl.h

Issue 2945433003: Add ClampedNumeric templates (Closed)
Patch Set: post-review fixes Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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_CLAMPED_MATH_IMPL_H_
6 #define 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 "base/numerics/checked_math.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/numerics/safe_math_shared_impl.h"
20
21 namespace base {
22 namespace internal {
23
24 // This provides a small optimization that generates more compact code when one
25 // of the components in an operation is a compile-time constant.
26 template <typename T>
27 constexpr bool IsValueConstantAndNegative(const T v) {
28 #if defined(__clang__) || defined(__GNUC__)
29 return __builtin_constant_p(v) ? IsValueNegative(v) : false;
30 #else
31 return false;
32 #endif
33 }
34
35 template <typename T, typename U, class Enable = void>
36 struct ClampedAddOp {};
37
38 template <typename T, typename U>
39 struct ClampedAddOp<T,
40 U,
41 typename std::enable_if<std::is_integral<T>::value &&
42 std::is_integral<U>::value>::type> {
43 using result_type = typename MaxExponentPromotion<T, U>::type;
44 template <typename V = result_type>
45 static V Do(T x, U y) {
46 V result;
47 return CheckedAddOp<T, U>::Do(x, y, &result)
48 ? result
49 : (as_unsigned(std::numeric_limits<V>::max()) +
50 (IsValueConstantAndNegative(x) || IsValueNegative(y)));
dcheng 2017/06/30 07:39:31 This is glorious and terrible. P.S. I think these
jschuh 2017/07/01 06:32:00 Done.
51 }
52 };
53
54 template <typename T, typename U, class Enable = void>
55 struct ClampedSubOp {};
56
57 template <typename T, typename U>
58 struct ClampedSubOp<T,
59 U,
60 typename std::enable_if<std::is_integral<T>::value &&
61 std::is_integral<U>::value>::type> {
62 using result_type = typename MaxExponentPromotion<T, U>::type;
63 template <typename V = result_type>
64 static V Do(T x, U y) {
65 V result;
66 return CheckedSubOp<T, U>::Do(x, y, &result)
67 ? result
68 : (as_unsigned(std::numeric_limits<V>::max()) +
69 (IsValueConstantAndNegative(x) || !IsValueNegative(y)));
dcheng 2017/06/30 07:39:31 Ditto.
jschuh 2017/07/01 06:31:59 Done.
70 }
71 };
72
73 template <typename T, typename U, class Enable = void>
74 struct ClampedMulOp {};
75
76 template <typename T, typename U>
77 struct ClampedMulOp<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 V Do(T x, U y) {
84 V result;
85 return CheckedMulOp<T, U>::Do(x, y, &result)
86 ? result
87 : as_unsigned(std::numeric_limits<V>::max()) +
88 (IsValueNegative(x) ^ IsValueNegative(y));
89 }
90 };
91
92 template <typename T, typename U, class Enable = void>
93 struct ClampedDivOp {};
94
95 template <typename T, typename U>
96 struct ClampedDivOp<T,
97 U,
98 typename std::enable_if<std::is_integral<T>::value &&
99 std::is_integral<U>::value>::type> {
100 using result_type = typename MaxExponentPromotion<T, U>::type;
101 template <typename V = result_type>
102 static V Do(T x, U y) {
103 V result;
104 return CheckedDivOp<T, U>::Do(x, y, &result)
105 ? result
106 : as_unsigned(std::numeric_limits<V>::max()) +
107 (IsValueNegative(x) ^ IsValueNegative(y));
108 }
109 };
110
111 template <typename T, typename U, class Enable = void>
112 struct ClampedModOp {};
113
114 template <typename T, typename U>
115 struct ClampedModOp<T,
116 U,
117 typename std::enable_if<std::is_integral<T>::value &&
118 std::is_integral<U>::value>::type> {
119 using result_type = typename MaxExponentPromotion<T, U>::type;
120 template <typename V = result_type>
121 static V Do(T x, U y) {
122 V result;
123 return CheckedModOp<T, U>::Do(x, y, &result) ? result : x;
124 }
125 };
126
127 template <typename T, typename U, class Enable = void>
128 struct ClampedLshOp {};
129
130 // Left shift. Non-zero values saturate in the direction of the sign. A zero
131 // shifted by any value always results in zero.
132 // Note: This class template supports left shifting negative values.
133 template <typename T, typename U>
134 struct ClampedLshOp<T,
135 U,
136 typename std::enable_if<std::is_integral<T>::value &&
137 std::is_integral<U>::value>::type> {
138 using result_type = T;
139 template <typename V = result_type>
140 static V Do(T x, U shift) {
141 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
dcheng 2017/06/30 07:39:30 Oh... I guess this static assert is supposed to pr
142 V result = x;
143 return (shift < std::numeric_limits<T>::digits &&
144 CheckedMulOp<T, T>::Do(x, T(1) << shift, &result))
145 ? result
146 : (x ? as_unsigned(std::numeric_limits<V>::max()) +
147 IsValueNegative(x)
148 : 0);
149 }
150 };
151
152 template <typename T, typename U, class Enable = void>
153 struct ClampedRshOp {};
154
155 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
156 template <typename T, typename U>
157 struct ClampedRshOp<T,
158 U,
159 typename std::enable_if<std::is_integral<T>::value &&
160 std::is_integral<U>::value>::type> {
161 using result_type = T;
162 template <typename V = result_type>
163 static V Do(T x, U shift) {
164 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
165 return shift < IntegerBitsPlusSign<T>::value
166 ? saturated_cast<V>(x >> shift)
167 : as_unsigned(V(0)) - IsValueNegative(x);
168 }
169 };
170
171 template <typename T, typename U, class Enable = void>
172 struct ClampedAndOp {};
173
174 template <typename T, typename U>
175 struct ClampedAndOp<T,
176 U,
177 typename std::enable_if<std::is_integral<T>::value &&
178 std::is_integral<U>::value>::type> {
179 using result_type = typename std::make_unsigned<
180 typename MaxExponentPromotion<T, U>::type>::type;
181 template <typename V>
182 static constexpr V Do(T x, U y) {
183 return static_cast<result_type>(x) & static_cast<result_type>(y);
184 }
185 };
186
187 template <typename T, typename U, class Enable = void>
188 struct ClampedOrOp {};
189
190 // For simplicity we promote to unsigned integers.
191 template <typename T, typename U>
192 struct ClampedOrOp<T,
193 U,
194 typename std::enable_if<std::is_integral<T>::value &&
195 std::is_integral<U>::value>::type> {
196 using result_type = typename std::make_unsigned<
197 typename MaxExponentPromotion<T, U>::type>::type;
198 template <typename V>
199 static constexpr V Do(T x, U y) {
200 return static_cast<result_type>(x) | static_cast<result_type>(y);
201 }
202 };
203
204 template <typename T, typename U, class Enable = void>
205 struct ClampedXorOp {};
206
207 // For simplicity we support only unsigned integers.
208 template <typename T, typename U>
209 struct ClampedXorOp<T,
210 U,
211 typename std::enable_if<std::is_integral<T>::value &&
212 std::is_integral<U>::value>::type> {
213 using result_type = typename std::make_unsigned<
214 typename MaxExponentPromotion<T, U>::type>::type;
215 template <typename V>
216 static constexpr V Do(T x, U y) {
217 return static_cast<result_type>(x) ^ static_cast<result_type>(y);
218 }
219 };
220
221 template <typename T, typename U, class Enable = void>
222 struct ClampedMaxOp {};
223
224 template <typename T, typename U>
225 struct ClampedMaxOp<
226 T,
227 U,
228 typename std::enable_if<std::is_arithmetic<T>::value &&
229 std::is_arithmetic<U>::value>::type> {
230 using result_type = typename MaxExponentPromotion<T, U>::type;
231 template <typename V = result_type>
232 static constexpr V Do(T x, U y) {
233 return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
234 : saturated_cast<V>(y);
235 }
236 };
237
238 template <typename T, typename U, class Enable = void>
239 struct ClampedMinOp {};
240
241 template <typename T, typename U>
242 struct ClampedMinOp<
243 T,
244 U,
245 typename std::enable_if<std::is_arithmetic<T>::value &&
246 std::is_arithmetic<U>::value>::type> {
247 using result_type = typename LowestValuePromotion<T, U>::type;
248 template <typename V = result_type>
249 static constexpr V Do(T x, U y) {
250 return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
251 : saturated_cast<V>(y);
252 }
253 };
254
255 // This is just boilerplate that wraps the standard floating point arithmetic.
256 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
257 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
258 template <typename T, typename U> \
259 struct Clamped##NAME##Op< \
260 T, U, \
261 typename std::enable_if<std::is_floating_point<T>::value || \
262 std::is_floating_point<U>::value>::type> { \
263 using result_type = typename MaxExponentPromotion<T, U>::type; \
264 template <typename V = result_type> \
265 static constexpr V Do(T x, U y) { \
266 return saturated_cast<V>(x OP y); \
267 } \
268 };
269
270 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
271 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
272 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
273 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
274
275 #undef BASE_FLOAT_ARITHMETIC_OPS
276
277 } // namespace internal
278 } // namespace base
279
280 #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698