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

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

Issue 2480953002: Make Checked* functions better align with GCC/Clang intrinsics (Closed)
Patch Set: nits + compile fix Created 4 years, 1 month 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
« no previous file with comments | « base/numerics/safe_math.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 88
89 template <typename Integer> 89 template <typename Integer>
90 struct PositionOfSignBit { 90 struct PositionOfSignBit {
91 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, 91 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer,
92 size_t>::type value = 92 size_t>::type value =
93 CHAR_BIT * sizeof(Integer) - 1; 93 CHAR_BIT * sizeof(Integer) - 1;
94 }; 94 };
95 95
96 // This is used for UnsignedAbs, where we need to support floating-point 96 // This is used for UnsignedAbs, where we need to support floating-point
97 // template instantiations even though we don't actually support the operations. 97 // template instantiations even though we don't actually support the operations.
98 // However, there is no corresponding implementation of e.g. CheckedUnsignedAbs, 98 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
99 // so the float versions will not compile. 99 // so the float versions will not compile.
100 template <typename Numeric, 100 template <typename Numeric,
101 bool IsInteger = std::numeric_limits<Numeric>::is_integer, 101 bool IsInteger = std::numeric_limits<Numeric>::is_integer,
102 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> 102 bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
103 struct UnsignedOrFloatForSize; 103 struct UnsignedOrFloatForSize;
104 104
105 template <typename Numeric> 105 template <typename Numeric>
106 struct UnsignedOrFloatForSize<Numeric, true, false> { 106 struct UnsignedOrFloatForSize<Numeric, true, false> {
107 typedef typename UnsignedIntegerForSize<Numeric>::type type; 107 typedef typename UnsignedIntegerForSize<Numeric>::type type;
108 }; 108 };
(...skipping 16 matching lines...) Expand all
125 template <typename T> 125 template <typename T>
126 constexpr T BinaryComplement(T x) { 126 constexpr T BinaryComplement(T x) {
127 return static_cast<T>(~x); 127 return static_cast<T>(~x);
128 } 128 }
129 129
130 // Here are the actual portable checked integer math implementations. 130 // Here are the actual portable checked integer math implementations.
131 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean 131 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
132 // way to coalesce things into the CheckedNumericState specializations below. 132 // way to coalesce things into the CheckedNumericState specializations below.
133 133
134 template <typename T> 134 template <typename T>
135 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type 135 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
136 CheckedAdd(T x, T y, bool* validity) { 136 CheckedAdd(T x, T y, T* result) {
137 // Since the value of x+y is undefined if we have a signed type, we compute 137 // Since the value of x+y is undefined if we have a signed type, we compute
138 // it using the unsigned type of the same size. 138 // it using the unsigned type of the same size.
139 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 139 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
140 UnsignedDst ux = static_cast<UnsignedDst>(x); 140 UnsignedDst ux = static_cast<UnsignedDst>(x);
141 UnsignedDst uy = static_cast<UnsignedDst>(y); 141 UnsignedDst uy = static_cast<UnsignedDst>(y);
142 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); 142 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
143 *result = static_cast<T>(uresult);
143 // Addition is valid if the sign of (x + y) is equal to either that of x or 144 // Addition is valid if the sign of (x + y) is equal to either that of x or
144 // that of y. 145 // that of y.
145 if (std::numeric_limits<T>::is_signed) { 146 return (std::numeric_limits<T>::is_signed)
146 *validity = HasSignBit(BinaryComplement( 147 ? HasSignBit(BinaryComplement(
147 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))); 148 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))
148 } else { // Unsigned is either valid or overflow. 149 : (BinaryComplement(x) >=
149 *validity = BinaryComplement(x) >= y; 150 y); // Unsigned is either valid or underflow.
150 }
151 return static_cast<T>(uresult);
152 } 151 }
153 152
154 template <typename T> 153 template <typename T>
155 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type 154 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
156 CheckedSub(T x, T y, bool* validity) { 155 CheckedSub(T x, T y, T* result) {
157 // Since the value of x+y is undefined if we have a signed type, we compute 156 // Since the value of x+y is undefined if we have a signed type, we compute
158 // it using the unsigned type of the same size. 157 // it using the unsigned type of the same size.
159 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 158 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
160 UnsignedDst ux = static_cast<UnsignedDst>(x); 159 UnsignedDst ux = static_cast<UnsignedDst>(x);
161 UnsignedDst uy = static_cast<UnsignedDst>(y); 160 UnsignedDst uy = static_cast<UnsignedDst>(y);
162 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); 161 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
162 *result = static_cast<T>(uresult);
163 // Subtraction is valid if either x and y have same sign, or (x-y) and x have 163 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
164 // the same sign. 164 // the same sign.
165 if (std::numeric_limits<T>::is_signed) { 165 return (std::numeric_limits<T>::is_signed)
166 *validity = HasSignBit( 166 ? HasSignBit(BinaryComplement(
167 BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))); 167 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
168 } else { // Unsigned is either valid or underflow. 168 : (x >= y);
169 *validity = x >= y;
170 }
171 return static_cast<T>(uresult);
172 } 169 }
173 170
174 // Integer multiplication is a bit complicated. In the fast case we just 171 // Integer multiplication is a bit complicated. In the fast case we just
175 // we just promote to a twice wider type, and range check the result. In the 172 // we just promote to a twice wider type, and range check the result. In the
176 // slow case we need to manually check that the result won't be truncated by 173 // slow case we need to manually check that the result won't be truncated by
177 // checking with division against the appropriate bound. 174 // checking with division against the appropriate bound.
178 template <typename T> 175 template <typename T>
179 typename std::enable_if<std::numeric_limits<T>::is_integer && 176 typename std::enable_if<std::numeric_limits<T>::is_integer &&
180 sizeof(T) * 2 <= sizeof(uintmax_t), 177 sizeof(T) * 2 <= sizeof(uintmax_t),
181 T>::type 178 bool>::type
182 CheckedMul(T x, T y, bool* validity) { 179 CheckedMul(T x, T y, T* result) {
183 typedef typename TwiceWiderInteger<T>::type IntermediateType; 180 typedef typename TwiceWiderInteger<T>::type IntermediateType;
184 IntermediateType tmp = 181 IntermediateType tmp =
185 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); 182 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
186 *validity = DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID; 183 *result = static_cast<T>(tmp);
187 return static_cast<T>(tmp); 184 return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID;
188 } 185 }
189 186
190 template <typename T> 187 template <typename T>
191 typename std::enable_if<std::numeric_limits<T>::is_integer && 188 typename std::enable_if<std::numeric_limits<T>::is_integer &&
192 std::numeric_limits<T>::is_signed && 189 std::numeric_limits<T>::is_signed &&
193 (sizeof(T) * 2 > sizeof(uintmax_t)), 190 (sizeof(T) * 2 > sizeof(uintmax_t)),
194 T>::type 191 bool>::type
195 CheckedMul(T x, T y, bool* validity) { 192 CheckedMul(T x, T y, T* result) {
196 // If either side is zero then the result will be zero. 193 if (x && y) {
197 if (!x || !y) { 194 if (x > 0) {
198 *validity = true; 195 if (y > 0) {
199 return static_cast<T>(0); 196 if (x > std::numeric_limits<T>::max() / y)
200 } 197 return false;
201 if (x > 0) { 198 } else {
202 if (y > 0) { 199 if (y < std::numeric_limits<T>::min() / x)
203 *validity = x <= std::numeric_limits<T>::max() / y; 200 return false;
201 }
204 } else { 202 } else {
205 *validity = y >= std::numeric_limits<T>::min() / x; 203 if (y > 0) {
206 } 204 if (x < std::numeric_limits<T>::min() / y)
207 } else { 205 return false;
208 if (y > 0) { 206 } else {
209 *validity = x >= std::numeric_limits<T>::min() / y; 207 if (y < std::numeric_limits<T>::max() / x)
210 } else { 208 return false;
211 *validity = y >= std::numeric_limits<T>::max() / x; 209 }
212 } 210 }
213 } 211 }
214 return static_cast<T>(*validity ? x * y : 0); 212 *result = x * y;
213 return true;
215 } 214 }
216 215
217 template <typename T> 216 template <typename T>
218 typename std::enable_if<std::numeric_limits<T>::is_integer && 217 typename std::enable_if<std::numeric_limits<T>::is_integer &&
219 !std::numeric_limits<T>::is_signed && 218 !std::numeric_limits<T>::is_signed &&
220 (sizeof(T) * 2 > sizeof(uintmax_t)), 219 (sizeof(T) * 2 > sizeof(uintmax_t)),
221 T>::type 220 bool>::type
222 CheckedMul(T x, T y, bool* validity) { 221 CheckedMul(T x, T y, T* result) {
223 *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y); 222 *result = x * y;
224 return static_cast<T>(*validity ? x * y : 0); 223 return (y == 0 || x <= std::numeric_limits<T>::max() / y);
225 } 224 }
226 225
227 // Division just requires a check for a zero denominator or an invalid negation 226 // Division just requires a check for a zero denominator or an invalid negation
228 // on signed min/-1. 227 // on signed min/-1.
229 template <typename T> 228 template <typename T>
230 T CheckedDiv(T x, 229 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
231 T y, 230 CheckedDiv(T x, T y, T* result) {
232 bool* validity, 231 if (y && (!std::numeric_limits<T>::is_signed ||
233 typename std::enable_if<std::numeric_limits<T>::is_integer, 232 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
234 int>::type = 0) { 233 *result = x / y;
235 if ((y == 0) || 234 return true;
236 (std::numeric_limits<T>::is_signed &&
237 x == std::numeric_limits<T>::min() && y == static_cast<T>(-1))) {
238 *validity = false;
239 return static_cast<T>(0);
240 } 235 }
241 236 return false;
242 *validity = true;
243 return static_cast<T>(x / y);
244 } 237 }
245 238
246 template <typename T> 239 template <typename T>
247 typename std::enable_if<std::numeric_limits<T>::is_integer && 240 typename std::enable_if<std::numeric_limits<T>::is_integer &&
248 std::numeric_limits<T>::is_signed, 241 std::numeric_limits<T>::is_signed,
249 T>::type 242 bool>::type
250 CheckedMod(T x, T y, bool* validity) { 243 CheckedMod(T x, T y, T* result) {
251 *validity = y > 0; 244 if (y > 0) {
252 return static_cast<T>(*validity ? x % y : 0); 245 *result = static_cast<T>(x % y);
246 return true;
247 }
248 return false;
253 } 249 }
254 250
255 template <typename T> 251 template <typename T>
256 typename std::enable_if<std::numeric_limits<T>::is_integer && 252 typename std::enable_if<std::numeric_limits<T>::is_integer &&
257 !std::numeric_limits<T>::is_signed, 253 !std::numeric_limits<T>::is_signed,
258 T>::type 254 bool>::type
259 CheckedMod(T x, T y, bool* validity) { 255 CheckedMod(T x, T y, T* result) {
260 *validity = y != 0; 256 if (y != 0) {
261 return static_cast<T>(*validity ? x % y : 0); 257 *result = static_cast<T>(x % y);
258 return true;
259 }
260 return false;
262 } 261 }
263 262
264 template <typename T> 263 template <typename T>
265 typename std::enable_if<std::numeric_limits<T>::is_integer && 264 typename std::enable_if<std::numeric_limits<T>::is_integer &&
266 std::numeric_limits<T>::is_signed, 265 std::numeric_limits<T>::is_signed,
267 T>::type 266 bool>::type
268 CheckedNeg(T value, bool* validity) { 267 CheckedNeg(T value, T* result) {
269 *validity = value != std::numeric_limits<T>::min();
270 // The negation of signed min is min, so catch that one. 268 // The negation of signed min is min, so catch that one.
271 return static_cast<T>(*validity ? -value : 0); 269 if (value != std::numeric_limits<T>::min()) {
270 *result = static_cast<T>(-value);
271 return true;
272 }
273 return false;
272 } 274 }
273 275
274 template <typename T> 276 template <typename T>
275 typename std::enable_if<std::numeric_limits<T>::is_integer && 277 typename std::enable_if<std::numeric_limits<T>::is_integer &&
276 !std::numeric_limits<T>::is_signed, 278 !std::numeric_limits<T>::is_signed,
277 T>::type 279 bool>::type
278 CheckedNeg(T value, bool* validity) { 280 CheckedNeg(T value, T* result) {
279 // The only legal unsigned negation is zero. 281 if (!value) { // The only legal unsigned negation is zero.
280 *validity = !value; 282 *result = static_cast<T>(0);
281 return static_cast<T>( 283 return true;
282 *validity ? -static_cast<typename SignedIntegerForSize<T>::type>(value) 284 }
283 : 0); 285 return false;
284 } 286 }
285 287
286 template <typename T> 288 template <typename T>
287 typename std::enable_if<std::numeric_limits<T>::is_integer && 289 typename std::enable_if<std::numeric_limits<T>::is_integer &&
288 std::numeric_limits<T>::is_signed, 290 std::numeric_limits<T>::is_signed,
289 T>::type 291 bool>::type
290 CheckedAbs(T value, bool* validity) { 292 CheckedAbs(T value, T* result) {
291 *validity = value != std::numeric_limits<T>::min(); 293 if (value != std::numeric_limits<T>::min()) {
292 return static_cast<T>(*validity ? std::abs(value) : 0); 294 *result = std::abs(value);
295 return true;
296 }
297 return false;
293 } 298 }
294 299
295 template <typename T> 300 template <typename T>
296 typename std::enable_if<std::numeric_limits<T>::is_integer && 301 typename std::enable_if<std::numeric_limits<T>::is_integer &&
297 !std::numeric_limits<T>::is_signed, 302 !std::numeric_limits<T>::is_signed,
298 T>::type 303 bool>::type
299 CheckedAbs(T value, bool* validity) { 304 CheckedAbs(T value, T* result) {
300 // T is unsigned, so |value| must already be positive. 305 // T is unsigned, so |value| must already be positive.
301 *validity = true; 306 *result = value;
302 return value; 307 return true;
303 } 308 }
304 309
305 template <typename T> 310 template <typename T>
306 typename std::enable_if<std::numeric_limits<T>::is_integer && 311 typename std::enable_if<std::numeric_limits<T>::is_integer &&
307 std::numeric_limits<T>::is_signed, 312 std::numeric_limits<T>::is_signed,
308 typename UnsignedIntegerForSize<T>::type>::type 313 typename UnsignedIntegerForSize<T>::type>::type
309 CheckedUnsignedAbs(T value) { 314 SafeUnsignedAbs(T value) {
310 typedef typename UnsignedIntegerForSize<T>::type UnsignedT; 315 typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
311 return value == std::numeric_limits<T>::min() 316 return value == std::numeric_limits<T>::min()
312 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1 317 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
313 : static_cast<UnsignedT>(std::abs(value)); 318 : static_cast<UnsignedT>(std::abs(value));
314 } 319 }
315 320
316 template <typename T> 321 template <typename T>
317 typename std::enable_if<std::numeric_limits<T>::is_integer && 322 typename std::enable_if<std::numeric_limits<T>::is_integer &&
318 !std::numeric_limits<T>::is_signed, 323 !std::numeric_limits<T>::is_signed,
319 T>::type 324 T>::type
320 CheckedUnsignedAbs(T value) { 325 SafeUnsignedAbs(T value) {
321 // T is unsigned, so |value| must already be positive. 326 // T is unsigned, so |value| must already be positive.
322 return static_cast<T>(value); 327 return static_cast<T>(value);
323 } 328 }
324 329
325 // These are the floating point stubs that the compiler needs to see. Only the 330 template <typename T>
326 // negation operation is ever called. 331 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
327 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ 332 T value,
328 template <typename T> \ 333 bool*) {
329 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \ 334 NOTREACHED();
330 Checked##NAME(T, T, bool*) { \ 335 return static_cast<T>(-value);
331 NOTREACHED(); \ 336 }
332 return static_cast<T>(0); \ 337
338 template <typename T>
339 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
340 T value,
341 bool*) {
342 NOTREACHED();
343 return static_cast<T>(std::abs(value));
344 }
345
346 // These are the floating point stubs that the compiler needs to see.
347 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
348 template <typename T> \
349 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type \
350 Checked##NAME(T, T, T*) { \
351 NOTREACHED(); \
352 return static_cast<T>(false); \
333 } 353 }
334 354
335 BASE_FLOAT_ARITHMETIC_STUBS(Add) 355 BASE_FLOAT_ARITHMETIC_STUBS(Add)
336 BASE_FLOAT_ARITHMETIC_STUBS(Sub) 356 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
337 BASE_FLOAT_ARITHMETIC_STUBS(Mul) 357 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
338 BASE_FLOAT_ARITHMETIC_STUBS(Div) 358 BASE_FLOAT_ARITHMETIC_STUBS(Div)
339 BASE_FLOAT_ARITHMETIC_STUBS(Mod) 359 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
340 360
341 #undef BASE_FLOAT_ARITHMETIC_STUBS 361 #undef BASE_FLOAT_ARITHMETIC_STUBS
342 362
343 template <typename T> 363 template <typename T>
344 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( 364 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type
345 T value, 365 CheckedNeg(T value, T* result) {
346 bool*) { 366 *result = static_cast<T>(-value);
347 return static_cast<T>(-value); 367 return true;
348 } 368 }
349 369
350 template <typename T> 370 template <typename T>
351 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( 371 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type
352 T value, 372 CheckedAbs(T value, T* result) {
353 bool*) { 373 *result = static_cast<T>(std::abs(value));
354 return static_cast<T>(std::abs(value)); 374 return true;
355 } 375 }
356 376
357 // Floats carry around their validity state with them, but integers do not. So, 377 // Floats carry around their validity state with them, but integers do not. So,
358 // we wrap the underlying value in a specialization in order to hide that detail 378 // we wrap the underlying value in a specialization in order to hide that detail
359 // and expose an interface via accessors. 379 // and expose an interface via accessors.
360 enum NumericRepresentation { 380 enum NumericRepresentation {
361 NUMERIC_INTEGER, 381 NUMERIC_INTEGER,
362 NUMERIC_FLOATING, 382 NUMERIC_FLOATING,
363 NUMERIC_UNKNOWN 383 NUMERIC_UNKNOWN
364 }; 384 };
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 sizeof(T) >= (2 * sizeof(Lhs)) && 508 sizeof(T) >= (2 * sizeof(Lhs)) &&
489 StaticDstRangeRelationToSrcRange<T, Rhs>::value != 509 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
490 NUMERIC_RANGE_CONTAINED && 510 NUMERIC_RANGE_CONTAINED &&
491 sizeof(T) >= (2 * sizeof(Rhs)); 511 sizeof(T) >= (2 * sizeof(Rhs));
492 }; 512 };
493 513
494 } // namespace internal 514 } // namespace internal
495 } // namespace base 515 } // namespace base
496 516
497 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 517 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW
« no previous file with comments | « base/numerics/safe_math.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698