OLD | NEW |
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 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
249 struct IsIntegerArithmeticSafe { | 249 struct IsIntegerArithmeticSafe { |
250 static const bool value = !std::numeric_limits<T>::is_iec559 && | 250 static const bool value = !std::numeric_limits<T>::is_iec559 && |
251 StaticDstRangeRelationToSrcRange<T, Lhs>::value == | 251 StaticDstRangeRelationToSrcRange<T, Lhs>::value == |
252 NUMERIC_RANGE_CONTAINED && | 252 NUMERIC_RANGE_CONTAINED && |
253 sizeof(T) >= (2 * sizeof(Lhs)) && | 253 sizeof(T) >= (2 * sizeof(Lhs)) && |
254 StaticDstRangeRelationToSrcRange<T, Rhs>::value != | 254 StaticDstRangeRelationToSrcRange<T, Rhs>::value != |
255 NUMERIC_RANGE_CONTAINED && | 255 NUMERIC_RANGE_CONTAINED && |
256 sizeof(T) >= (2 * sizeof(Rhs)); | 256 sizeof(T) >= (2 * sizeof(Rhs)); |
257 }; | 257 }; |
258 | 258 |
| 259 // Probe for builtin math overflow support on Clang and version check on GCC. |
| 260 #if defined(__has_builtin) |
| 261 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) |
| 262 #elif defined(__GNUC__) |
| 263 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) |
| 264 #else |
| 265 #define USE_OVERFLOW_BUILTINS (0) |
| 266 #endif |
| 267 |
259 // Here are the actual portable checked integer math implementations. | 268 // Here are the actual portable checked integer math implementations. |
260 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean | 269 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean |
261 // way to coalesce things into the CheckedNumericState specializations below. | 270 // way to coalesce things into the CheckedNumericState specializations below. |
262 | 271 |
263 template <typename T> | 272 template <typename T> |
264 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 273 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
265 CheckedAddImpl(T x, T y, T* result) { | 274 CheckedAddImpl(T x, T y, T* result) { |
266 // Since the value of x+y is undefined if we have a signed type, we compute | 275 // Since the value of x+y is undefined if we have a signed type, we compute |
267 // it using the unsigned type of the same size. | 276 // it using the unsigned type of the same size. |
268 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 277 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
269 UnsignedDst ux = static_cast<UnsignedDst>(x); | 278 UnsignedDst ux = static_cast<UnsignedDst>(x); |
270 UnsignedDst uy = static_cast<UnsignedDst>(y); | 279 UnsignedDst uy = static_cast<UnsignedDst>(y); |
271 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); | 280 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); |
272 *result = static_cast<T>(uresult); | 281 *result = static_cast<T>(uresult); |
273 // Addition is valid if the sign of (x + y) is equal to either that of x or | 282 // Addition is valid if the sign of (x + y) is equal to either that of x or |
274 // that of y. | 283 // that of y. |
275 return (std::numeric_limits<T>::is_signed) | 284 return (std::numeric_limits<T>::is_signed) |
276 ? HasSignBit(BinaryComplement( | 285 ? HasSignBit(BinaryComplement( |
277 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) | 286 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) |
278 : (BinaryComplement(x) >= | 287 : (BinaryComplement(x) >= |
279 y); // Unsigned is either valid or underflow. | 288 y); // Unsigned is either valid or underflow. |
280 } | 289 } |
281 | 290 |
282 template <typename T, typename U, typename V> | 291 template <typename T, typename U, typename V> |
283 typename std::enable_if<std::numeric_limits<T>::is_integer && | 292 typename std::enable_if<std::numeric_limits<T>::is_integer && |
284 std::numeric_limits<U>::is_integer && | 293 std::numeric_limits<U>::is_integer && |
285 std::numeric_limits<V>::is_integer, | 294 std::numeric_limits<V>::is_integer, |
286 bool>::type | 295 bool>::type |
287 CheckedAdd(T x, U y, V* result) { | 296 CheckedAdd(T x, U y, V* result) { |
| 297 #if USE_OVERFLOW_BUILTINS |
| 298 return !__builtin_add_overflow(x, y, result); |
| 299 #else |
288 using Promotion = | 300 using Promotion = |
289 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 301 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
290 Promotion presult; | 302 Promotion presult; |
291 // Fail if either operand is out of range for the promoted type. | 303 // Fail if either operand is out of range for the promoted type. |
292 // TODO(jschuh): This could be made to work for a broader range of values. | 304 // TODO(jschuh): This could be made to work for a broader range of values. |
293 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 305 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
294 IsValueInRangeForNumericType<Promotion>(y); | 306 IsValueInRangeForNumericType<Promotion>(y); |
295 | 307 |
296 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | 308 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { |
297 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); | 309 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); |
298 } else { | 310 } else { |
299 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), | 311 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), |
300 static_cast<Promotion>(y), &presult); | 312 static_cast<Promotion>(y), &presult); |
301 } | 313 } |
302 *result = static_cast<V>(presult); | 314 *result = static_cast<V>(presult); |
303 return is_valid && IsValueInRangeForNumericType<V>(presult); | 315 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 316 #endif |
304 } | 317 } |
305 | 318 |
306 template <typename T> | 319 template <typename T> |
307 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 320 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
308 CheckedSubImpl(T x, T y, T* result) { | 321 CheckedSubImpl(T x, T y, T* result) { |
309 // Since the value of x+y is undefined if we have a signed type, we compute | 322 // Since the value of x+y is undefined if we have a signed type, we compute |
310 // it using the unsigned type of the same size. | 323 // it using the unsigned type of the same size. |
311 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 324 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
312 UnsignedDst ux = static_cast<UnsignedDst>(x); | 325 UnsignedDst ux = static_cast<UnsignedDst>(x); |
313 UnsignedDst uy = static_cast<UnsignedDst>(y); | 326 UnsignedDst uy = static_cast<UnsignedDst>(y); |
314 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); | 327 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); |
315 *result = static_cast<T>(uresult); | 328 *result = static_cast<T>(uresult); |
316 // Subtraction is valid if either x and y have same sign, or (x-y) and x have | 329 // Subtraction is valid if either x and y have same sign, or (x-y) and x have |
317 // the same sign. | 330 // the same sign. |
318 return (std::numeric_limits<T>::is_signed) | 331 return (std::numeric_limits<T>::is_signed) |
319 ? HasSignBit(BinaryComplement( | 332 ? HasSignBit(BinaryComplement( |
320 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) | 333 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) |
321 : (x >= y); | 334 : (x >= y); |
322 } | 335 } |
323 | 336 |
324 template <typename T, typename U, typename V> | 337 template <typename T, typename U, typename V> |
325 typename std::enable_if<std::numeric_limits<T>::is_integer && | 338 typename std::enable_if<std::numeric_limits<T>::is_integer && |
326 std::numeric_limits<U>::is_integer && | 339 std::numeric_limits<U>::is_integer && |
327 std::numeric_limits<V>::is_integer, | 340 std::numeric_limits<V>::is_integer, |
328 bool>::type | 341 bool>::type |
329 CheckedSub(T x, U y, V* result) { | 342 CheckedSub(T x, U y, V* result) { |
| 343 #if USE_OVERFLOW_BUILTINS |
| 344 return !__builtin_sub_overflow(x, y, result); |
| 345 #else |
330 using Promotion = | 346 using Promotion = |
331 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 347 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
332 Promotion presult; | 348 Promotion presult; |
333 // Fail if either operand is out of range for the promoted type. | 349 // Fail if either operand is out of range for the promoted type. |
334 // TODO(jschuh): This could be made to work for a broader range of values. | 350 // TODO(jschuh): This could be made to work for a broader range of values. |
335 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 351 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
336 IsValueInRangeForNumericType<Promotion>(y); | 352 IsValueInRangeForNumericType<Promotion>(y); |
337 | 353 |
338 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | 354 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { |
339 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); | 355 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); |
340 } else { | 356 } else { |
341 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), | 357 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), |
342 static_cast<Promotion>(y), &presult); | 358 static_cast<Promotion>(y), &presult); |
343 } | 359 } |
344 *result = static_cast<V>(presult); | 360 *result = static_cast<V>(presult); |
345 return is_valid && IsValueInRangeForNumericType<V>(presult); | 361 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 362 #endif |
346 } | 363 } |
347 | 364 |
348 // Integer multiplication is a bit complicated. In the fast case we just | 365 // Integer multiplication is a bit complicated. In the fast case we just |
349 // we just promote to a twice wider type, and range check the result. In the | 366 // we just promote to a twice wider type, and range check the result. In the |
350 // slow case we need to manually check that the result won't be truncated by | 367 // slow case we need to manually check that the result won't be truncated by |
351 // checking with division against the appropriate bound. | 368 // checking with division against the appropriate bound. |
352 template <typename T> | 369 template <typename T> |
353 typename std::enable_if<std::numeric_limits<T>::is_integer && | 370 typename std::enable_if<std::numeric_limits<T>::is_integer && |
354 sizeof(T) * 2 <= sizeof(uintmax_t), | 371 sizeof(T) * 2 <= sizeof(uintmax_t), |
355 bool>::type | 372 bool>::type |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
399 *result = x * y; | 416 *result = x * y; |
400 return (y == 0 || x <= std::numeric_limits<T>::max() / y); | 417 return (y == 0 || x <= std::numeric_limits<T>::max() / y); |
401 } | 418 } |
402 | 419 |
403 template <typename T, typename U, typename V> | 420 template <typename T, typename U, typename V> |
404 typename std::enable_if<std::numeric_limits<T>::is_integer && | 421 typename std::enable_if<std::numeric_limits<T>::is_integer && |
405 std::numeric_limits<U>::is_integer && | 422 std::numeric_limits<U>::is_integer && |
406 std::numeric_limits<V>::is_integer, | 423 std::numeric_limits<V>::is_integer, |
407 bool>::type | 424 bool>::type |
408 CheckedMul(T x, U y, V* result) { | 425 CheckedMul(T x, U y, V* result) { |
| 426 #if USE_OVERFLOW_BUILTINS |
| 427 #if defined(__clang__) |
| 428 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can |
| 429 // support full-width, mixed-sign multiply builtins. https://crbug.com/613003 |
| 430 static const bool kUseMaxInt = |
| 431 sizeof(__typeof__(x * y)) < sizeof(intptr_t) || |
| 432 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && |
| 433 std::is_signed<T>::value == std::is_signed<U>::value); |
| 434 #else |
| 435 static const bool kUseMaxInt = true; |
| 436 #endif |
| 437 if (kUseMaxInt) |
| 438 return !__builtin_mul_overflow(x, y, result); |
| 439 #endif |
409 using Promotion = | 440 using Promotion = |
410 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 441 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
411 Promotion presult; | 442 Promotion presult; |
412 // Fail if either operand is out of range for the promoted type. | 443 // Fail if either operand is out of range for the promoted type. |
413 // TODO(jschuh): This could be made to work for a broader range of values. | 444 // TODO(jschuh): This could be made to work for a broader range of values. |
414 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 445 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
415 IsValueInRangeForNumericType<Promotion>(y); | 446 IsValueInRangeForNumericType<Promotion>(y); |
416 | 447 |
417 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | 448 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { |
418 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); | 449 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
419 } else { | 450 } else { |
420 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), | 451 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), |
421 static_cast<Promotion>(y), &presult); | 452 static_cast<Promotion>(y), &presult); |
422 } | 453 } |
423 *result = static_cast<V>(presult); | 454 *result = static_cast<V>(presult); |
424 return is_valid && IsValueInRangeForNumericType<V>(presult); | 455 return is_valid && IsValueInRangeForNumericType<V>(presult); |
425 } | 456 } |
426 | 457 |
| 458 // Avoid poluting the namespace once we're done with the macro. |
| 459 #undef USE_OVERFLOW_BUILTINS |
| 460 |
427 // Division just requires a check for a zero denominator or an invalid negation | 461 // Division just requires a check for a zero denominator or an invalid negation |
428 // on signed min/-1. | 462 // on signed min/-1. |
429 template <typename T> | 463 template <typename T> |
430 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 464 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
431 CheckedDivImpl(T x, T y, T* result) { | 465 CheckedDivImpl(T x, T y, T* result) { |
432 if (y && (!std::numeric_limits<T>::is_signed || | 466 if (y && (!std::numeric_limits<T>::is_signed || |
433 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { | 467 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { |
434 *result = x / y; | 468 *result = x / y; |
435 return true; | 469 return true; |
436 } | 470 } |
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
750 : value_(static_cast<T>(rhs.value())) {} | 784 : value_(static_cast<T>(rhs.value())) {} |
751 | 785 |
752 bool is_valid() const { return std::isfinite(value_); } | 786 bool is_valid() const { return std::isfinite(value_); } |
753 T value() const { return value_; } | 787 T value() const { return value_; } |
754 }; | 788 }; |
755 | 789 |
756 } // namespace internal | 790 } // namespace internal |
757 } // namespace base | 791 } // namespace base |
758 | 792 |
759 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 793 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
OLD | NEW |