Chromium Code Reviews| 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 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 static const bool is_contained = true; | 219 static const bool is_contained = true; |
| 220 }; | 220 }; |
| 221 | 221 |
| 222 // Attempt to find a big enough type. | 222 // Attempt to find a big enough type. |
| 223 template <typename Lhs, typename Rhs> | 223 template <typename Lhs, typename Rhs> |
| 224 struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> { | 224 struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> { |
| 225 using type = typename BigEnoughPromotion<Lhs, Rhs>::type; | 225 using type = typename BigEnoughPromotion<Lhs, Rhs>::type; |
| 226 static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained; | 226 static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained; |
| 227 }; | 227 }; |
| 228 | 228 |
| 229 // We can statically check if operations on the provided types can wrap, so we | |
| 230 // can skip the checked operations if they're not needed. So, for an integer we | |
| 231 // care if the destination type preserves the sign and is twice the width of | |
| 232 // the source. | |
| 233 template <typename T, typename Lhs, typename Rhs> | |
| 234 struct IsIntegerArithmeticSafe { | |
| 235 static const bool value = !std::numeric_limits<T>::is_iec559 && | |
| 236 StaticDstRangeRelationToSrcRange<T, Lhs>::value == | |
| 237 NUMERIC_RANGE_CONTAINED && | |
| 238 sizeof(T) >= (2 * sizeof(Lhs)) && | |
| 239 StaticDstRangeRelationToSrcRange<T, Rhs>::value != | |
| 240 NUMERIC_RANGE_CONTAINED && | |
| 241 sizeof(T) >= (2 * sizeof(Rhs)); | |
| 242 }; | |
| 243 | |
| 229 // Here are the actual portable checked integer math implementations. | 244 // Here are the actual portable checked integer math implementations. |
| 230 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean | 245 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean |
| 231 // way to coalesce things into the CheckedNumericState specializations below. | 246 // way to coalesce things into the CheckedNumericState specializations below. |
| 232 | 247 |
| 233 template <typename T> | 248 template <typename T> |
| 234 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 249 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
| 235 CheckedAddImpl(T x, T y, T* result) { | 250 CheckedAddImpl(T x, T y, T* result) { |
| 236 // Since the value of x+y is undefined if we have a signed type, we compute | 251 // Since the value of x+y is undefined if we have a signed type, we compute |
| 237 // it using the unsigned type of the same size. | 252 // it using the unsigned type of the same size. |
| 238 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 253 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 250 } | 265 } |
| 251 | 266 |
| 252 template <typename T, typename U, typename V> | 267 template <typename T, typename U, typename V> |
| 253 typename std::enable_if<std::numeric_limits<T>::is_integer && | 268 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 254 std::numeric_limits<U>::is_integer && | 269 std::numeric_limits<U>::is_integer && |
| 255 std::numeric_limits<V>::is_integer, | 270 std::numeric_limits<V>::is_integer, |
| 256 bool>::type | 271 bool>::type |
| 257 CheckedAdd(T x, U y, V* result) { | 272 CheckedAdd(T x, U y, V* result) { |
| 258 using Promotion = | 273 using Promotion = |
| 259 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 274 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
| 275 Promotion presult; | |
| 260 // Fail if either operand is out of range for the promoted type. | 276 // Fail if either operand is out of range for the promoted type. |
| 261 // TODO(jschuh): This could be made to work for a broader range of values. | 277 // TODO(jschuh): This could be made to work for a broader range of values. |
| 262 if (!IsValueInRangeForNumericType<Promotion>(x) || | 278 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
| 263 !IsValueInRangeForNumericType<Promotion>(y)) { | 279 IsValueInRangeForNumericType<Promotion>(y); |
| 264 return false; | 280 |
| 281 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | |
| 282 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); | |
| 283 } else { | |
| 284 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), | |
|
Tom Sepez
2016/11/16 21:16:08
Do we maybe perform an operation here that we woul
jschuh
2016/11/16 21:22:28
The Impl functions should all be safe from undefin
| |
| 285 static_cast<Promotion>(y), &presult); | |
| 265 } | 286 } |
| 266 | |
| 267 Promotion presult; | |
| 268 bool is_valid = CheckedAddImpl(static_cast<Promotion>(x), | |
| 269 static_cast<Promotion>(y), &presult); | |
| 270 *result = static_cast<V>(presult); | 287 *result = static_cast<V>(presult); |
| 271 return is_valid && IsValueInRangeForNumericType<V>(presult); | 288 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 272 } | 289 } |
| 273 | 290 |
| 274 template <typename T> | 291 template <typename T> |
| 275 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 292 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
| 276 CheckedSubImpl(T x, T y, T* result) { | 293 CheckedSubImpl(T x, T y, T* result) { |
| 277 // Since the value of x+y is undefined if we have a signed type, we compute | 294 // Since the value of x+y is undefined if we have a signed type, we compute |
| 278 // it using the unsigned type of the same size. | 295 // it using the unsigned type of the same size. |
| 279 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 296 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 290 } | 307 } |
| 291 | 308 |
| 292 template <typename T, typename U, typename V> | 309 template <typename T, typename U, typename V> |
| 293 typename std::enable_if<std::numeric_limits<T>::is_integer && | 310 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 294 std::numeric_limits<U>::is_integer && | 311 std::numeric_limits<U>::is_integer && |
| 295 std::numeric_limits<V>::is_integer, | 312 std::numeric_limits<V>::is_integer, |
| 296 bool>::type | 313 bool>::type |
| 297 CheckedSub(T x, U y, V* result) { | 314 CheckedSub(T x, U y, V* result) { |
| 298 using Promotion = | 315 using Promotion = |
| 299 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 316 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
| 317 Promotion presult; | |
| 300 // Fail if either operand is out of range for the promoted type. | 318 // Fail if either operand is out of range for the promoted type. |
| 301 // TODO(jschuh): This could be made to work for a broader range of values. | 319 // TODO(jschuh): This could be made to work for a broader range of values. |
| 302 if (!IsValueInRangeForNumericType<Promotion>(x) || | 320 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
| 303 !IsValueInRangeForNumericType<Promotion>(y)) { | 321 IsValueInRangeForNumericType<Promotion>(y); |
| 304 return false; | 322 |
| 323 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | |
| 324 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); | |
| 325 } else { | |
| 326 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), | |
| 327 static_cast<Promotion>(y), &presult); | |
| 305 } | 328 } |
| 306 | |
| 307 Promotion presult; | |
| 308 bool is_valid = CheckedSubImpl(static_cast<Promotion>(x), | |
| 309 static_cast<Promotion>(y), &presult); | |
| 310 *result = static_cast<V>(presult); | 329 *result = static_cast<V>(presult); |
| 311 return is_valid && IsValueInRangeForNumericType<V>(presult); | 330 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 312 } | 331 } |
| 313 | 332 |
| 314 // Integer multiplication is a bit complicated. In the fast case we just | 333 // Integer multiplication is a bit complicated. In the fast case we just |
| 315 // we just promote to a twice wider type, and range check the result. In the | 334 // we just promote to a twice wider type, and range check the result. In the |
| 316 // slow case we need to manually check that the result won't be truncated by | 335 // slow case we need to manually check that the result won't be truncated by |
| 317 // checking with division against the appropriate bound. | 336 // checking with division against the appropriate bound. |
| 318 template <typename T> | 337 template <typename T> |
| 319 typename std::enable_if<std::numeric_limits<T>::is_integer && | 338 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 367 } | 386 } |
| 368 | 387 |
| 369 template <typename T, typename U, typename V> | 388 template <typename T, typename U, typename V> |
| 370 typename std::enable_if<std::numeric_limits<T>::is_integer && | 389 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 371 std::numeric_limits<U>::is_integer && | 390 std::numeric_limits<U>::is_integer && |
| 372 std::numeric_limits<V>::is_integer, | 391 std::numeric_limits<V>::is_integer, |
| 373 bool>::type | 392 bool>::type |
| 374 CheckedMul(T x, U y, V* result) { | 393 CheckedMul(T x, U y, V* result) { |
| 375 using Promotion = | 394 using Promotion = |
| 376 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 395 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
| 396 Promotion presult; | |
| 377 // Fail if either operand is out of range for the promoted type. | 397 // Fail if either operand is out of range for the promoted type. |
| 378 // TODO(jschuh): This could be made to work for a broader range of values. | 398 // TODO(jschuh): This could be made to work for a broader range of values. |
| 379 if (!IsValueInRangeForNumericType<Promotion>(x) || | 399 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
| 380 !IsValueInRangeForNumericType<Promotion>(y)) { | 400 IsValueInRangeForNumericType<Promotion>(y); |
| 381 return false; | 401 |
| 402 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | |
| 403 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); | |
| 404 } else { | |
| 405 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), | |
| 406 static_cast<Promotion>(y), &presult); | |
| 382 } | 407 } |
| 383 | |
| 384 Promotion presult; | |
| 385 bool is_valid = CheckedMulImpl(static_cast<Promotion>(x), | |
| 386 static_cast<Promotion>(y), &presult); | |
| 387 *result = static_cast<V>(presult); | 408 *result = static_cast<V>(presult); |
| 388 return is_valid && IsValueInRangeForNumericType<V>(presult); | 409 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 389 } | 410 } |
| 390 | 411 |
| 391 // Division just requires a check for a zero denominator or an invalid negation | 412 // Division just requires a check for a zero denominator or an invalid negation |
| 392 // on signed min/-1. | 413 // on signed min/-1. |
| 393 template <typename T> | 414 template <typename T> |
| 394 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 415 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
| 395 CheckedDivImpl(T x, T y, T* result) { | 416 CheckedDivImpl(T x, T y, T* result) { |
| 396 if (y && (!std::numeric_limits<T>::is_signed || | 417 if (y && (!std::numeric_limits<T>::is_signed || |
| 397 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { | 418 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { |
| 398 *result = x / y; | 419 *result = x / y; |
| 399 return true; | 420 return true; |
| 400 } | 421 } |
| 401 return false; | 422 return false; |
| 402 } | 423 } |
| 403 | 424 |
| 404 template <typename T, typename U, typename V> | 425 template <typename T, typename U, typename V> |
| 405 typename std::enable_if<std::numeric_limits<T>::is_integer && | 426 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 406 std::numeric_limits<U>::is_integer && | 427 std::numeric_limits<U>::is_integer && |
| 407 std::numeric_limits<V>::is_integer, | 428 std::numeric_limits<V>::is_integer, |
| 408 bool>::type | 429 bool>::type |
| 409 CheckedDiv(T x, U y, V* result) { | 430 CheckedDiv(T x, U y, V* result) { |
| 410 using Promotion = | 431 using Promotion = |
| 411 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 432 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
| 433 Promotion presult; | |
| 412 // Fail if either operand is out of range for the promoted type. | 434 // 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. | 435 // TODO(jschuh): This could be made to work for a broader range of values. |
| 414 if (!IsValueInRangeForNumericType<Promotion>(x) || | 436 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
| 415 !IsValueInRangeForNumericType<Promotion>(y)) { | 437 IsValueInRangeForNumericType<Promotion>(y); |
| 416 return false; | 438 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), |
| 417 } | 439 static_cast<Promotion>(y), &presult); |
| 418 | |
| 419 Promotion presult; | |
| 420 bool is_valid = CheckedDivImpl(static_cast<Promotion>(x), | |
| 421 static_cast<Promotion>(y), &presult); | |
| 422 *result = static_cast<V>(presult); | 440 *result = static_cast<V>(presult); |
| 423 return is_valid && IsValueInRangeForNumericType<V>(presult); | 441 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 424 } | 442 } |
| 425 | 443 |
| 426 template <typename T> | 444 template <typename T> |
| 427 typename std::enable_if<std::numeric_limits<T>::is_integer && | 445 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 428 std::numeric_limits<T>::is_signed, | 446 std::numeric_limits<T>::is_signed, |
| 429 bool>::type | 447 bool>::type |
| 430 CheckedModImpl(T x, T y, T* result) { | 448 CheckedModImpl(T x, T y, T* result) { |
| 431 if (y > 0) { | 449 if (y > 0) { |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 675 : value_(static_cast<T>(rhs.value())) {} | 693 : value_(static_cast<T>(rhs.value())) {} |
| 676 | 694 |
| 677 bool is_valid() const { return std::isfinite(value_); } | 695 bool is_valid() const { return std::isfinite(value_); } |
| 678 T value() const { return value_; } | 696 T value() const { return value_; } |
| 679 }; | 697 }; |
| 680 | 698 |
| 681 } // namespace internal | 699 } // namespace internal |
| 682 } // namespace base | 700 } // namespace base |
| 683 | 701 |
| 684 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 702 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
| OLD | NEW |