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

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

Issue 2510793004: Performance optimizations for Checked(Add|Sub|Mul|Div) (Closed)
Patch Set: nit 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 | « no previous file | no next file » | 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 208 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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_
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698