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

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

Issue 2516153002: Move the remaining CheckedNumeric logic out of the macro (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 | « 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 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> 122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
123 PositionOfSignBit<T>::value); 123 PositionOfSignBit<T>::value);
124 } 124 }
125 125
126 // This wrapper undoes the standard integer promotions. 126 // This wrapper undoes the standard integer promotions.
127 template <typename T> 127 template <typename T>
128 constexpr T BinaryComplement(T x) { 128 constexpr T BinaryComplement(T x) {
129 return static_cast<T>(~x); 129 return static_cast<T>(~x);
130 } 130 }
131 131
132 // Return if a numeric value is negative regardless of type.
133 template <typename T,
134 typename std::enable_if<std::is_arithmetic<T>::value &&
135 std::is_signed<T>::value>::type* = nullptr>
136 constexpr bool IsNegative(T x) {
137 return x < 0;
138 }
139
140 template <typename T,
141 typename std::enable_if<std::is_arithmetic<T>::value &&
142 !std::is_signed<T>::value>::type* = nullptr>
143 constexpr bool IsNegative(T x) {
144 return false;
145 }
146
147 enum ArithmeticPromotionCategory { 132 enum ArithmeticPromotionCategory {
148 LEFT_PROMOTION, // Use the type of the left-hand argument. 133 LEFT_PROMOTION, // Use the type of the left-hand argument.
149 RIGHT_PROMOTION, // Use the type of the right-hand argument. 134 RIGHT_PROMOTION, // Use the type of the right-hand argument.
150 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent. 135 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent.
151 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type. 136 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type.
152 }; 137 };
153 138
154 template <ArithmeticPromotionCategory Promotion, 139 template <ArithmeticPromotionCategory Promotion,
155 typename Lhs, 140 typename Lhs,
156 typename Rhs = Lhs> 141 typename Rhs = Lhs>
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 static const bool is_contained = true; 191 static const bool is_contained = true;
207 }; 192 };
208 193
209 // No type is large enough. 194 // No type is large enough.
210 template <typename Lhs, typename Rhs> 195 template <typename Lhs, typename Rhs>
211 struct BigEnoughPromotion<Lhs, Rhs, true, false> { 196 struct BigEnoughPromotion<Lhs, Rhs, true, false> {
212 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 197 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
213 static const bool is_contained = false; 198 static const bool is_contained = false;
214 }; 199 };
215 200
216 // These are the four supported promotion types. 201 // These are the supported promotion types.
217
218 // Use the type of the left-hand argument.
219 template <typename Lhs, typename Rhs>
220 struct ArithmeticPromotion<LEFT_PROMOTION, Lhs, Rhs> {
221 using type = Lhs;
222 static const bool is_contained = true;
223 };
224
225 // Use the type of the right-hand argument.
226 template <typename Lhs, typename Rhs>
227 struct ArithmeticPromotion<RIGHT_PROMOTION, Lhs, Rhs> {
228 using type = Rhs;
229 static const bool is_contained = true;
230 };
231 202
232 // Use the type supporting the largest exponent. 203 // Use the type supporting the largest exponent.
233 template <typename Lhs, typename Rhs> 204 template <typename Lhs, typename Rhs>
234 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> { 205 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> {
235 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 206 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
236 static const bool is_contained = true; 207 static const bool is_contained = true;
237 }; 208 };
238 209
239 // Attempt to find a big enough type. 210 // Attempt to find a big enough type.
240 template <typename Lhs, typename Rhs> 211 template <typename Lhs, typename Rhs>
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 *result = static_cast<T>(uresult); 254 *result = static_cast<T>(uresult);
284 // Addition is valid if the sign of (x + y) is equal to either that of x or 255 // Addition is valid if the sign of (x + y) is equal to either that of x or
285 // that of y. 256 // that of y.
286 return (std::numeric_limits<T>::is_signed) 257 return (std::numeric_limits<T>::is_signed)
287 ? HasSignBit(BinaryComplement( 258 ? HasSignBit(BinaryComplement(
288 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) 259 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))
289 : (BinaryComplement(x) >= 260 : (BinaryComplement(x) >=
290 y); // Unsigned is either valid or underflow. 261 y); // Unsigned is either valid or underflow.
291 } 262 }
292 263
293 template <typename T, typename U, typename V> 264 template <typename T, typename U, class Enable = void>
294 typename std::enable_if<std::numeric_limits<T>::is_integer && 265 struct CheckedAdd {};
295 std::numeric_limits<U>::is_integer && 266
296 std::numeric_limits<V>::is_integer, 267 template <typename T, typename U>
297 bool>::type 268 struct CheckedAdd<
298 CheckedAdd(T x, U y, V* result) { 269 T,
270 U,
271 typename std::enable_if<std::numeric_limits<T>::is_integer &&
272 std::numeric_limits<U>::is_integer>::type> {
273 using result_type =
274 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
275 template <typename V>
276 static bool Op(T x, U y, V* result) {
299 #if USE_OVERFLOW_BUILTINS 277 #if USE_OVERFLOW_BUILTINS
300 return !__builtin_add_overflow(x, y, result); 278 return !__builtin_add_overflow(x, y, result);
301 #else 279 #else
302 using Promotion = 280 using Promotion =
303 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 281 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
304 Promotion presult; 282 Promotion presult;
305 // Fail if either operand is out of range for the promoted type. 283 // Fail if either operand is out of range for the promoted type.
306 // TODO(jschuh): This could be made to work for a broader range of values. 284 // TODO(jschuh): This could be made to work for a broader range of values.
307 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 285 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
308 IsValueInRangeForNumericType<Promotion>(y); 286 IsValueInRangeForNumericType<Promotion>(y);
309 287
310 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { 288 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
311 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); 289 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
312 } else { 290 } else {
313 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), 291 is_valid &= CheckedAddImpl(static_cast<Promotion>(x),
314 static_cast<Promotion>(y), &presult); 292 static_cast<Promotion>(y), &presult);
293 }
294 *result = static_cast<V>(presult);
295 return is_valid && IsValueInRangeForNumericType<V>(presult);
296 #endif
315 } 297 }
316 *result = static_cast<V>(presult); 298 };
317 return is_valid && IsValueInRangeForNumericType<V>(presult);
318 #endif
319 }
320 299
321 template <typename T> 300 template <typename T>
322 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 301 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
323 CheckedSubImpl(T x, T y, T* result) { 302 CheckedSubImpl(T x, T y, T* result) {
324 // Since the value of x+y is undefined if we have a signed type, we compute 303 // Since the value of x+y is undefined if we have a signed type, we compute
325 // it using the unsigned type of the same size. 304 // it using the unsigned type of the same size.
326 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 305 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
327 UnsignedDst ux = static_cast<UnsignedDst>(x); 306 UnsignedDst ux = static_cast<UnsignedDst>(x);
328 UnsignedDst uy = static_cast<UnsignedDst>(y); 307 UnsignedDst uy = static_cast<UnsignedDst>(y);
329 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); 308 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
330 *result = static_cast<T>(uresult); 309 *result = static_cast<T>(uresult);
331 // Subtraction is valid if either x and y have same sign, or (x-y) and x have 310 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
332 // the same sign. 311 // the same sign.
333 return (std::numeric_limits<T>::is_signed) 312 return (std::numeric_limits<T>::is_signed)
334 ? HasSignBit(BinaryComplement( 313 ? HasSignBit(BinaryComplement(
335 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) 314 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
336 : (x >= y); 315 : (x >= y);
337 } 316 }
338 317
339 template <typename T, typename U, typename V> 318 template <typename T, typename U, class Enable = void>
340 typename std::enable_if<std::numeric_limits<T>::is_integer && 319 struct CheckedSub {};
341 std::numeric_limits<U>::is_integer && 320
342 std::numeric_limits<V>::is_integer, 321 template <typename T, typename U>
343 bool>::type 322 struct CheckedSub<
344 CheckedSub(T x, U y, V* result) { 323 T,
324 U,
325 typename std::enable_if<std::numeric_limits<T>::is_integer &&
326 std::numeric_limits<U>::is_integer>::type> {
327 using result_type =
328 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
329 template <typename V>
330 static bool Op(T x, U y, V* result) {
345 #if USE_OVERFLOW_BUILTINS 331 #if USE_OVERFLOW_BUILTINS
346 return !__builtin_sub_overflow(x, y, result); 332 return !__builtin_sub_overflow(x, y, result);
347 #else 333 #else
348 using Promotion = 334 using Promotion =
349 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 335 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
350 Promotion presult; 336 Promotion presult;
351 // Fail if either operand is out of range for the promoted type. 337 // Fail if either operand is out of range for the promoted type.
352 // TODO(jschuh): This could be made to work for a broader range of values. 338 // TODO(jschuh): This could be made to work for a broader range of values.
353 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 339 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
354 IsValueInRangeForNumericType<Promotion>(y); 340 IsValueInRangeForNumericType<Promotion>(y);
355 341
356 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { 342 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
357 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); 343 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
358 } else { 344 } else {
359 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), 345 is_valid &= CheckedSubImpl(static_cast<Promotion>(x),
360 static_cast<Promotion>(y), &presult); 346 static_cast<Promotion>(y), &presult);
347 }
348 *result = static_cast<V>(presult);
349 return is_valid && IsValueInRangeForNumericType<V>(presult);
350 #endif
361 } 351 }
362 *result = static_cast<V>(presult); 352 };
363 return is_valid && IsValueInRangeForNumericType<V>(presult);
364 #endif
365 }
366 353
367 // Integer multiplication is a bit complicated. In the fast case we just 354 // Integer multiplication is a bit complicated. In the fast case we just
368 // we just promote to a twice wider type, and range check the result. In the 355 // we just promote to a twice wider type, and range check the result. In the
369 // slow case we need to manually check that the result won't be truncated by 356 // slow case we need to manually check that the result won't be truncated by
370 // checking with division against the appropriate bound. 357 // checking with division against the appropriate bound.
371 template <typename T> 358 template <typename T>
372 typename std::enable_if<std::numeric_limits<T>::is_integer && 359 typename std::enable_if<std::numeric_limits<T>::is_integer &&
373 sizeof(T) * 2 <= sizeof(uintmax_t), 360 sizeof(T) * 2 <= sizeof(uintmax_t),
374 bool>::type 361 bool>::type
375 CheckedMulImpl(T x, T y, T* result) { 362 CheckedMulImpl(T x, T y, T* result) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 template <typename T> 399 template <typename T>
413 typename std::enable_if<std::numeric_limits<T>::is_integer && 400 typename std::enable_if<std::numeric_limits<T>::is_integer &&
414 !std::numeric_limits<T>::is_signed && 401 !std::numeric_limits<T>::is_signed &&
415 (sizeof(T) * 2 > sizeof(uintmax_t)), 402 (sizeof(T) * 2 > sizeof(uintmax_t)),
416 bool>::type 403 bool>::type
417 CheckedMulImpl(T x, T y, T* result) { 404 CheckedMulImpl(T x, T y, T* result) {
418 *result = x * y; 405 *result = x * y;
419 return (y == 0 || x <= std::numeric_limits<T>::max() / y); 406 return (y == 0 || x <= std::numeric_limits<T>::max() / y);
420 } 407 }
421 408
422 template <typename T, typename U, typename V> 409 template <typename T, typename U, class Enable = void>
423 typename std::enable_if<std::numeric_limits<T>::is_integer && 410 struct CheckedMul {};
424 std::numeric_limits<U>::is_integer && 411
425 std::numeric_limits<V>::is_integer, 412 template <typename T, typename U>
426 bool>::type 413 struct CheckedMul<
427 CheckedMul(T x, U y, V* result) { 414 T,
415 U,
416 typename std::enable_if<std::numeric_limits<T>::is_integer &&
417 std::numeric_limits<U>::is_integer>::type> {
418 using result_type =
419 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
420 template <typename V>
421 static bool Op(T x, U y, V* result) {
428 #if USE_OVERFLOW_BUILTINS 422 #if USE_OVERFLOW_BUILTINS
429 #if defined(__clang__) 423 #if defined(__clang__)
430 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can 424 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
431 // support full-width, mixed-sign multiply builtins. https://crbug.com/613003 425 // support full-width, mixed-sign multiply builtins.
432 static const bool kUseMaxInt = 426 // https://crbug.com/613003
433 sizeof(__typeof__(x * y)) < sizeof(intptr_t) || 427 static const bool kUseMaxInt =
434 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && 428 sizeof(__typeof__(x * y)) < sizeof(intptr_t) ||
435 std::is_signed<T>::value == std::is_signed<U>::value); 429 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) &&
430 std::is_signed<T>::value == std::is_signed<U>::value);
436 #else 431 #else
437 static const bool kUseMaxInt = true; 432 static const bool kUseMaxInt = true;
438 #endif 433 #endif
439 if (kUseMaxInt) 434 if (kUseMaxInt)
440 return !__builtin_mul_overflow(x, y, result); 435 return !__builtin_mul_overflow(x, y, result);
441 #endif 436 #endif
442 using Promotion = 437 using Promotion =
443 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 438 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
444 Promotion presult; 439 Promotion presult;
445 // Fail if either operand is out of range for the promoted type. 440 // Fail if either operand is out of range for the promoted type.
446 // TODO(jschuh): This could be made to work for a broader range of values. 441 // TODO(jschuh): This could be made to work for a broader range of values.
447 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 442 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
448 IsValueInRangeForNumericType<Promotion>(y); 443 IsValueInRangeForNumericType<Promotion>(y);
449 444
450 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { 445 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
451 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); 446 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
452 } else { 447 } else {
453 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), 448 is_valid &= CheckedMulImpl(static_cast<Promotion>(x),
454 static_cast<Promotion>(y), &presult); 449 static_cast<Promotion>(y), &presult);
450 }
451 *result = static_cast<V>(presult);
452 return is_valid && IsValueInRangeForNumericType<V>(presult);
455 } 453 }
456 *result = static_cast<V>(presult); 454 };
457 return is_valid && IsValueInRangeForNumericType<V>(presult);
458 }
459 455
460 // Avoid poluting the namespace once we're done with the macro. 456 // Avoid poluting the namespace once we're done with the macro.
461 #undef USE_OVERFLOW_BUILTINS 457 #undef USE_OVERFLOW_BUILTINS
462 458
463 // Division just requires a check for a zero denominator or an invalid negation 459 // Division just requires a check for a zero denominator or an invalid negation
464 // on signed min/-1. 460 // on signed min/-1.
465 template <typename T> 461 template <typename T>
466 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 462 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
467 CheckedDivImpl(T x, T y, T* result) { 463 CheckedDivImpl(T x, T y, T* result) {
468 if (y && (!std::numeric_limits<T>::is_signed || 464 if (y && (!std::numeric_limits<T>::is_signed ||
469 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { 465 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
470 *result = x / y; 466 *result = x / y;
471 return true; 467 return true;
472 } 468 }
473 return false; 469 return false;
474 } 470 }
475 471
476 template <typename T, typename U, typename V> 472 template <typename T, typename U, class Enable = void>
477 typename std::enable_if<std::numeric_limits<T>::is_integer && 473 struct CheckedDiv {};
478 std::numeric_limits<U>::is_integer && 474
479 std::numeric_limits<V>::is_integer, 475 template <typename T, typename U>
480 bool>::type 476 struct CheckedDiv<
481 CheckedDiv(T x, U y, V* result) { 477 T,
482 using Promotion = 478 U,
483 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 479 typename std::enable_if<std::numeric_limits<T>::is_integer &&
484 Promotion presult; 480 std::numeric_limits<U>::is_integer>::type> {
485 // Fail if either operand is out of range for the promoted type. 481 using result_type =
486 // TODO(jschuh): This could be made to work for a broader range of values. 482 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
487 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 483 template <typename V>
488 IsValueInRangeForNumericType<Promotion>(y); 484 static bool Op(T x, U y, V* result) {
489 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), 485 using Promotion =
490 static_cast<Promotion>(y), &presult); 486 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
491 *result = static_cast<V>(presult); 487 Promotion presult;
492 return is_valid && IsValueInRangeForNumericType<V>(presult); 488 // Fail if either operand is out of range for the promoted type.
493 } 489 // TODO(jschuh): This could be made to work for a broader range of values.
490 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
491 IsValueInRangeForNumericType<Promotion>(y);
492 is_valid &= CheckedDivImpl(static_cast<Promotion>(x),
493 static_cast<Promotion>(y), &presult);
494 *result = static_cast<V>(presult);
495 return is_valid && IsValueInRangeForNumericType<V>(presult);
496 }
497 };
494 498
495 template <typename T> 499 template <typename T>
496 typename std::enable_if<std::numeric_limits<T>::is_integer && 500 typename std::enable_if<std::numeric_limits<T>::is_integer &&
497 std::numeric_limits<T>::is_signed, 501 std::numeric_limits<T>::is_signed,
498 bool>::type 502 bool>::type
499 CheckedModImpl(T x, T y, T* result) { 503 CheckedModImpl(T x, T y, T* result) {
500 if (y > 0) { 504 if (y > 0) {
501 *result = static_cast<T>(x % y); 505 *result = static_cast<T>(x % y);
502 return true; 506 return true;
503 } 507 }
504 return false; 508 return false;
505 } 509 }
506 510
507 template <typename T> 511 template <typename T>
508 typename std::enable_if<std::numeric_limits<T>::is_integer && 512 typename std::enable_if<std::numeric_limits<T>::is_integer &&
509 !std::numeric_limits<T>::is_signed, 513 !std::numeric_limits<T>::is_signed,
510 bool>::type 514 bool>::type
511 CheckedModImpl(T x, T y, T* result) { 515 CheckedModImpl(T x, T y, T* result) {
512 if (y != 0) { 516 if (y != 0) {
513 *result = static_cast<T>(x % y); 517 *result = static_cast<T>(x % y);
514 return true; 518 return true;
515 } 519 }
516 return false; 520 return false;
517 } 521 }
518 522
519 template <typename T, typename U, typename V> 523 template <typename T, typename U, class Enable = void>
520 typename std::enable_if<std::numeric_limits<T>::is_integer && 524 struct CheckedMod {};
521 std::numeric_limits<U>::is_integer && 525
522 std::numeric_limits<V>::is_integer, 526 template <typename T, typename U>
523 bool>::type 527 struct CheckedMod<
524 CheckedMod(T x, U y, V* result) { 528 T,
525 using Promotion = 529 U,
526 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 530 typename std::enable_if<std::numeric_limits<T>::is_integer &&
527 Promotion presult; 531 std::numeric_limits<U>::is_integer>::type> {
528 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), 532 using result_type =
529 static_cast<Promotion>(y), &presult); 533 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
530 *result = static_cast<V>(presult); 534 template <typename V>
531 return is_valid && IsValueInRangeForNumericType<V>(presult); 535 static bool Op(T x, U y, V* result) {
532 } 536 using Promotion =
537 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
538 Promotion presult;
539 bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
540 static_cast<Promotion>(y), &presult);
541 *result = static_cast<V>(presult);
542 return is_valid && IsValueInRangeForNumericType<V>(presult);
543 }
544 };
545
546 template <typename T, typename U, class Enable = void>
547 struct CheckedLeftShift {};
533 548
534 // Left shift. Shifts less than 0 or greater than or equal to the number 549 // Left shift. Shifts less than 0 or greater than or equal to the number
535 // of bits in the promoted type are undefined. Shifts of negative values 550 // of bits in the promoted type are undefined. Shifts of negative values
536 // are undefined. Otherwise it is defined when the result fits. 551 // are undefined. Otherwise it is defined when the result fits.
537 template <typename T, typename U, typename V> 552 template <typename T, typename U>
538 typename std::enable_if<std::numeric_limits<T>::is_integer && 553 struct CheckedLeftShift<
539 std::numeric_limits<U>::is_integer && 554 T,
540 std::numeric_limits<V>::is_integer, 555 U,
541 bool>::type 556 typename std::enable_if<std::numeric_limits<T>::is_integer &&
542 CheckedLeftShift(T x, U shift, V* result) { 557 std::numeric_limits<U>::is_integer>::type> {
543 using ShiftType = typename UnsignedIntegerForSize<T>::type; 558 using result_type = T;
544 static const ShiftType kBitWidth = CHAR_BIT * sizeof(T); 559 template <typename V>
545 const ShiftType real_shift = static_cast<ShiftType>(shift); 560 static bool Op(T x, U shift, V* result) {
546 // Signed shift is not legal on negative values. 561 using ShiftType = typename UnsignedIntegerForSize<T>::type;
547 if (!IsNegative(x) && real_shift < kBitWidth) { 562 static const ShiftType kBitWidth = CHAR_BIT * sizeof(T);
548 // Just use a multiplication because it's easy. 563 const ShiftType real_shift = static_cast<ShiftType>(shift);
549 // TODO(jschuh): This could probably be made more efficient. 564 // Signed shift is not legal on negative values.
550 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) 565 if (!IsValueNegative(x) && real_shift < kBitWidth) {
Tom Sepez 2016/11/21 17:19:52 nit: while we're at it, these might read easier if
jschuh 2016/11/21 17:43:25 I'm going to leave it for now, because when I look
551 return CheckedMul(x, static_cast<T>(1) << shift, result); 566 // Just use a multiplication because it's easy.
552 return !x; // Special case zero for a full width signed shift. 567 // TODO(jschuh): This could probably be made more efficient.
568 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1)
569 return CheckedMul<T, T>::Op(x, static_cast<T>(1) << shift, result);
570 return !x; // Special case zero for a full width signed shift.
571 }
572 return false;
553 } 573 }
554 return false; 574 };
555 } 575
576 template <typename T, typename U, class Enable = void>
577 struct CheckedRightShift {};
556 578
557 // Right shift. Shifts less than 0 or greater than or equal to the number 579 // Right shift. Shifts less than 0 or greater than or equal to the number
558 // of bits in the promoted type are undefined. Otherwise, it is always defined, 580 // of bits in the promoted type are undefined. Otherwise, it is always defined,
559 // but a right shift of a negative value is implementation-dependent. 581 // but a right shift of a negative value is implementation-dependent.
560 template <typename T, typename U, typename V> 582 template <typename T, typename U>
561 typename std::enable_if<std::numeric_limits<T>::is_integer && 583 struct CheckedRightShift<
562 std::numeric_limits<U>::is_integer && 584 T,
563 std::numeric_limits<V>::is_integer, 585 U,
564 bool>::type 586 typename std::enable_if<std::numeric_limits<T>::is_integer &&
565 CheckedRightShift(T x, U shift, V* result) { 587 std::numeric_limits<U>::is_integer>::type> {
566 // Use the type conversion push negative values out of range. 588 using result_type = T;
567 using ShiftType = typename UnsignedIntegerForSize<T>::type; 589 template <typename V = result_type>
568 if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) { 590 static bool Op(T x, U shift, V* result) {
569 T tmp = x >> shift; 591 // Use the type conversion push negative values out of range.
570 *result = static_cast<V>(tmp); 592 using ShiftType = typename UnsignedIntegerForSize<T>::type;
571 return IsValueInRangeForNumericType<unsigned>(tmp); 593 if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) {
594 T tmp = x >> shift;
595 *result = static_cast<V>(tmp);
596 return IsValueInRangeForNumericType<V>(tmp);
597 }
598 return false;
572 } 599 }
573 return false; 600 };
574 }
575 601
576 template <typename T> 602 template <typename T>
577 typename std::enable_if<std::numeric_limits<T>::is_integer && 603 typename std::enable_if<std::numeric_limits<T>::is_integer &&
578 std::numeric_limits<T>::is_signed, 604 std::numeric_limits<T>::is_signed,
579 bool>::type 605 bool>::type
580 CheckedNeg(T value, T* result) { 606 CheckedNeg(T value, T* result) {
581 // The negation of signed min is min, so catch that one. 607 // The negation of signed min is min, so catch that one.
582 if (value != std::numeric_limits<T>::min()) { 608 if (value != std::numeric_limits<T>::min()) {
583 *result = static_cast<T>(-value); 609 *result = static_cast<T>(-value);
584 return true; 610 return true;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
633 659
634 template <typename T> 660 template <typename T>
635 typename std::enable_if<std::numeric_limits<T>::is_integer && 661 typename std::enable_if<std::numeric_limits<T>::is_integer &&
636 !std::numeric_limits<T>::is_signed, 662 !std::numeric_limits<T>::is_signed,
637 T>::type 663 T>::type
638 SafeUnsignedAbs(T value) { 664 SafeUnsignedAbs(T value) {
639 // T is unsigned, so |value| must already be positive. 665 // T is unsigned, so |value| must already be positive.
640 return static_cast<T>(value); 666 return static_cast<T>(value);
641 } 667 }
642 668
643 template <typename T> 669 // This is just boilerplate that wraps the standard floating point arithmetic.
Tom Sepez 2016/11/21 17:19:52 Maybe this belongs in the safe_math.h file along w
jschuh 2016/11/21 17:43:25 Nah, this is the file with all of the integer impl
644 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( 670 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
645 T value, 671 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
646 bool*) { 672 template <typename T, typename U> \
647 NOTREACHED(); 673 struct Checked##NAME<T, U, typename std::enable_if< \
648 return static_cast<T>(-value); 674 std::numeric_limits<T>::is_iec559 || \
649 } 675 std::numeric_limits<U>::is_iec559>::type> { \
676 using result_type = \
677 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \
678 template <typename V> \
679 static bool Op(T x, U y, V* result) { \
680 using Promotion = \
681 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \
682 Promotion presult = x OP y; \
683 *result = static_cast<V>(presult); \
684 return IsValueInRangeForNumericType<V>(presult); \
685 } \
686 };
650 687
651 template <typename T> 688 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
652 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( 689 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
653 T value, 690 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
654 bool*) { 691 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
655 NOTREACHED();
656 return static_cast<T>(std::abs(value));
657 }
658 692
659 // These are the floating point stubs that the compiler needs to see. 693 #undef BASE_FLOAT_ARITHMETIC_OPS
660 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
661 template <typename T, typename U, typename V> \
662 typename std::enable_if<std::numeric_limits<T>::is_iec559 || \
663 std::numeric_limits<U>::is_iec559 || \
664 std::numeric_limits<V>::is_iec559, \
665 bool>::type Checked##NAME(T, U, V*) { \
666 NOTREACHED(); \
667 return static_cast<T>(false); \
668 }
669
670 BASE_FLOAT_ARITHMETIC_STUBS(Add)
671 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
672 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
673 BASE_FLOAT_ARITHMETIC_STUBS(Div)
674 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
675
676 #undef BASE_FLOAT_ARITHMETIC_STUBS
677 694
678 template <typename T> 695 template <typename T>
679 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type 696 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type
680 CheckedNeg(T value, T* result) { 697 CheckedNeg(T value, T* result) {
681 *result = static_cast<T>(-value); 698 *result = static_cast<T>(-value);
682 return true; 699 return true;
683 } 700 }
684 701
685 template <typename T> 702 template <typename T>
686 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type 703 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 : value_(static_cast<T>(rhs.value())) {} 803 : value_(static_cast<T>(rhs.value())) {}
787 804
788 bool is_valid() const { return std::isfinite(value_); } 805 bool is_valid() const { return std::isfinite(value_); }
789 T value() const { return value_; } 806 T value() const { return value_; }
790 }; 807 };
791 808
792 } // namespace internal 809 } // namespace internal
793 } // namespace base 810 } // namespace base
794 811
795 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 812 #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