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

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

Issue 2496143003: Simplify CheckedNumeric macros (Closed)
Patch Set: nits 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 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> 120 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
121 PositionOfSignBit<T>::value); 121 PositionOfSignBit<T>::value);
122 } 122 }
123 123
124 // This wrapper undoes the standard integer promotions. 124 // This wrapper undoes the standard integer promotions.
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 // For integers less than 128-bit and floats 32-bit or larger, we have the type
131 // with the larger maximum exponent take precedence.
132 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
133
134 template <typename Lhs,
135 typename Rhs = Lhs,
136 ArithmeticPromotionCategory Promotion =
137 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
138 ? LEFT_PROMOTION
139 : RIGHT_PROMOTION>
140 struct ArithmeticPromotion;
141
142 template <typename Lhs, typename Rhs>
143 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
144 typedef Lhs type;
145 };
146
147 template <typename Lhs, typename Rhs>
148 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
149 typedef Rhs type;
150 };
151
130 // Here are the actual portable checked integer math implementations. 152 // 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 153 // 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. 154 // way to coalesce things into the CheckedNumericState specializations below.
133 155
134 template <typename T> 156 template <typename T>
135 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 157 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
136 CheckedAdd(T x, T y, T* result) { 158 CheckedAddImpl(T x, T y, T* result) {
137 // Since the value of x+y is undefined if we have a signed type, we compute 159 // 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. 160 // it using the unsigned type of the same size.
139 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 161 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
140 UnsignedDst ux = static_cast<UnsignedDst>(x); 162 UnsignedDst ux = static_cast<UnsignedDst>(x);
141 UnsignedDst uy = static_cast<UnsignedDst>(y); 163 UnsignedDst uy = static_cast<UnsignedDst>(y);
142 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); 164 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
143 *result = static_cast<T>(uresult); 165 *result = static_cast<T>(uresult);
144 // Addition is valid if the sign of (x + y) is equal to either that of x or 166 // Addition is valid if the sign of (x + y) is equal to either that of x or
145 // that of y. 167 // that of y.
146 return (std::numeric_limits<T>::is_signed) 168 return (std::numeric_limits<T>::is_signed)
147 ? HasSignBit(BinaryComplement( 169 ? HasSignBit(BinaryComplement(
148 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) 170 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))
149 : (BinaryComplement(x) >= 171 : (BinaryComplement(x) >=
150 y); // Unsigned is either valid or underflow. 172 y); // Unsigned is either valid or underflow.
151 } 173 }
152 174
175 template <typename T, typename U, typename V>
176 typename std::enable_if<std::numeric_limits<T>::is_integer &&
177 std::numeric_limits<U>::is_integer &&
178 std::numeric_limits<V>::is_integer,
179 bool>::type
180 CheckedAdd(T x, U y, V* result) {
181 using Promotion = typename ArithmeticPromotion<T, U>::type;
182 // Fail if either operand is out of range for the promoted type.
183 // TODO(jschuh): This could be made to work for a broader range of values.
184 if (!IsValueInRangeForNumericType<Promotion>(x) ||
185 !IsValueInRangeForNumericType<Promotion>(y)) {
186 return false;
187 }
188
189 Promotion presult;
190 bool is_valid = CheckedAddImpl(static_cast<Promotion>(x),
191 static_cast<Promotion>(y), &presult);
192 *result = static_cast<V>(presult);
193 return is_valid && IsValueInRangeForNumericType<V>(presult);
194 }
195
153 template <typename T> 196 template <typename T>
154 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 197 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
155 CheckedSub(T x, T y, T* result) { 198 CheckedSubImpl(T x, T y, T* result) {
156 // Since the value of x+y is undefined if we have a signed type, we compute 199 // Since the value of x+y is undefined if we have a signed type, we compute
157 // it using the unsigned type of the same size. 200 // it using the unsigned type of the same size.
158 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 201 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
159 UnsignedDst ux = static_cast<UnsignedDst>(x); 202 UnsignedDst ux = static_cast<UnsignedDst>(x);
160 UnsignedDst uy = static_cast<UnsignedDst>(y); 203 UnsignedDst uy = static_cast<UnsignedDst>(y);
161 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); 204 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
162 *result = static_cast<T>(uresult); 205 *result = static_cast<T>(uresult);
163 // Subtraction is valid if either x and y have same sign, or (x-y) and x have 206 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
164 // the same sign. 207 // the same sign.
165 return (std::numeric_limits<T>::is_signed) 208 return (std::numeric_limits<T>::is_signed)
166 ? HasSignBit(BinaryComplement( 209 ? HasSignBit(BinaryComplement(
167 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) 210 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
168 : (x >= y); 211 : (x >= y);
169 } 212 }
170 213
214 template <typename T, typename U, typename V>
215 typename std::enable_if<std::numeric_limits<T>::is_integer &&
216 std::numeric_limits<U>::is_integer &&
217 std::numeric_limits<V>::is_integer,
218 bool>::type
219 CheckedSub(T x, U y, V* result) {
220 using Promotion = typename ArithmeticPromotion<T, U>::type;
221 // Fail if either operand is out of range for the promoted type.
222 // TODO(jschuh): This could be made to work for a broader range of values.
223 if (!IsValueInRangeForNumericType<Promotion>(x) ||
224 !IsValueInRangeForNumericType<Promotion>(y)) {
225 return false;
226 }
227
228 Promotion presult;
229 bool is_valid = CheckedSubImpl(static_cast<Promotion>(x),
230 static_cast<Promotion>(y), &presult);
231 *result = static_cast<V>(presult);
232 return is_valid && IsValueInRangeForNumericType<V>(presult);
233 }
234
171 // Integer multiplication is a bit complicated. In the fast case we just 235 // Integer multiplication is a bit complicated. In the fast case we just
172 // we just promote to a twice wider type, and range check the result. In the 236 // we just promote to a twice wider type, and range check the result. In the
173 // slow case we need to manually check that the result won't be truncated by 237 // slow case we need to manually check that the result won't be truncated by
174 // checking with division against the appropriate bound. 238 // checking with division against the appropriate bound.
175 template <typename T> 239 template <typename T>
176 typename std::enable_if<std::numeric_limits<T>::is_integer && 240 typename std::enable_if<std::numeric_limits<T>::is_integer &&
177 sizeof(T) * 2 <= sizeof(uintmax_t), 241 sizeof(T) * 2 <= sizeof(uintmax_t),
178 bool>::type 242 bool>::type
179 CheckedMul(T x, T y, T* result) { 243 CheckedMulImpl(T x, T y, T* result) {
180 typedef typename TwiceWiderInteger<T>::type IntermediateType; 244 typedef typename TwiceWiderInteger<T>::type IntermediateType;
181 IntermediateType tmp = 245 IntermediateType tmp =
182 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); 246 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
183 *result = static_cast<T>(tmp); 247 *result = static_cast<T>(tmp);
184 return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID; 248 return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID;
185 } 249 }
186 250
187 template <typename T> 251 template <typename T>
188 typename std::enable_if<std::numeric_limits<T>::is_integer && 252 typename std::enable_if<std::numeric_limits<T>::is_integer &&
189 std::numeric_limits<T>::is_signed && 253 std::numeric_limits<T>::is_signed &&
190 (sizeof(T) * 2 > sizeof(uintmax_t)), 254 (sizeof(T) * 2 > sizeof(uintmax_t)),
191 bool>::type 255 bool>::type
192 CheckedMul(T x, T y, T* result) { 256 CheckedMulImpl(T x, T y, T* result) {
193 if (x && y) { 257 if (x && y) {
194 if (x > 0) { 258 if (x > 0) {
195 if (y > 0) { 259 if (y > 0) {
196 if (x > std::numeric_limits<T>::max() / y) 260 if (x > std::numeric_limits<T>::max() / y)
197 return false; 261 return false;
198 } else { 262 } else {
199 if (y < std::numeric_limits<T>::min() / x) 263 if (y < std::numeric_limits<T>::min() / x)
200 return false; 264 return false;
201 } 265 }
202 } else { 266 } else {
203 if (y > 0) { 267 if (y > 0) {
204 if (x < std::numeric_limits<T>::min() / y) 268 if (x < std::numeric_limits<T>::min() / y)
205 return false; 269 return false;
206 } else { 270 } else {
207 if (y < std::numeric_limits<T>::max() / x) 271 if (y < std::numeric_limits<T>::max() / x)
208 return false; 272 return false;
209 } 273 }
210 } 274 }
211 } 275 }
212 *result = x * y; 276 *result = x * y;
213 return true; 277 return true;
214 } 278 }
215 279
216 template <typename T> 280 template <typename T>
217 typename std::enable_if<std::numeric_limits<T>::is_integer && 281 typename std::enable_if<std::numeric_limits<T>::is_integer &&
218 !std::numeric_limits<T>::is_signed && 282 !std::numeric_limits<T>::is_signed &&
219 (sizeof(T) * 2 > sizeof(uintmax_t)), 283 (sizeof(T) * 2 > sizeof(uintmax_t)),
220 bool>::type 284 bool>::type
221 CheckedMul(T x, T y, T* result) { 285 CheckedMulImpl(T x, T y, T* result) {
222 *result = x * y; 286 *result = x * y;
223 return (y == 0 || x <= std::numeric_limits<T>::max() / y); 287 return (y == 0 || x <= std::numeric_limits<T>::max() / y);
224 } 288 }
225 289
290 template <typename T, typename U, typename V>
291 typename std::enable_if<std::numeric_limits<T>::is_integer &&
292 std::numeric_limits<U>::is_integer &&
293 std::numeric_limits<V>::is_integer,
294 bool>::type
295 CheckedMul(T x, U y, V* result) {
296 using Promotion = typename ArithmeticPromotion<T, U>::type;
297 // Fail if either operand is out of range for the promoted type.
298 // TODO(jschuh): This could be made to work for a broader range of values.
299 if (!IsValueInRangeForNumericType<Promotion>(x) ||
300 !IsValueInRangeForNumericType<Promotion>(y)) {
301 return false;
302 }
303
304 Promotion presult;
305 bool is_valid = CheckedMulImpl(static_cast<Promotion>(x),
306 static_cast<Promotion>(y), &presult);
307 *result = static_cast<V>(presult);
308 return is_valid && IsValueInRangeForNumericType<V>(presult);
309 }
310
226 // Division just requires a check for a zero denominator or an invalid negation 311 // Division just requires a check for a zero denominator or an invalid negation
227 // on signed min/-1. 312 // on signed min/-1.
228 template <typename T> 313 template <typename T>
229 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 314 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
230 CheckedDiv(T x, T y, T* result) { 315 CheckedDivImpl(T x, T y, T* result) {
231 if (y && (!std::numeric_limits<T>::is_signed || 316 if (y && (!std::numeric_limits<T>::is_signed ||
232 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { 317 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
233 *result = x / y; 318 *result = x / y;
234 return true; 319 return true;
235 } 320 }
236 return false; 321 return false;
237 } 322 }
238 323
324 template <typename T, typename U, typename V>
325 typename std::enable_if<std::numeric_limits<T>::is_integer &&
326 std::numeric_limits<U>::is_integer &&
327 std::numeric_limits<V>::is_integer,
328 bool>::type
329 CheckedDiv(T x, U y, V* result) {
330 using Promotion = typename ArithmeticPromotion<T, U>::type;
331 // Fail if either operand is out of range for the promoted type.
332 // TODO(jschuh): This could be made to work for a broader range of values.
333 if (!IsValueInRangeForNumericType<Promotion>(x) ||
334 !IsValueInRangeForNumericType<Promotion>(y)) {
335 return false;
336 }
337
338 Promotion presult;
339 bool is_valid = CheckedDivImpl(static_cast<Promotion>(x),
340 static_cast<Promotion>(y), &presult);
341 *result = static_cast<V>(presult);
342 return is_valid && IsValueInRangeForNumericType<V>(presult);
343 }
344
239 template <typename T> 345 template <typename T>
240 typename std::enable_if<std::numeric_limits<T>::is_integer && 346 typename std::enable_if<std::numeric_limits<T>::is_integer &&
241 std::numeric_limits<T>::is_signed, 347 std::numeric_limits<T>::is_signed,
242 bool>::type 348 bool>::type
243 CheckedMod(T x, T y, T* result) { 349 CheckedModImpl(T x, T y, T* result) {
244 if (y > 0) { 350 if (y > 0) {
245 *result = static_cast<T>(x % y); 351 *result = static_cast<T>(x % y);
246 return true; 352 return true;
247 } 353 }
248 return false; 354 return false;
249 } 355 }
250 356
251 template <typename T> 357 template <typename T>
252 typename std::enable_if<std::numeric_limits<T>::is_integer && 358 typename std::enable_if<std::numeric_limits<T>::is_integer &&
253 !std::numeric_limits<T>::is_signed, 359 !std::numeric_limits<T>::is_signed,
254 bool>::type 360 bool>::type
255 CheckedMod(T x, T y, T* result) { 361 CheckedModImpl(T x, T y, T* result) {
256 if (y != 0) { 362 if (y != 0) {
257 *result = static_cast<T>(x % y); 363 *result = static_cast<T>(x % y);
258 return true; 364 return true;
259 } 365 }
260 return false; 366 return false;
261 } 367 }
262 368
369 template <typename T, typename U, typename V>
370 typename std::enable_if<std::numeric_limits<T>::is_integer &&
371 std::numeric_limits<U>::is_integer &&
372 std::numeric_limits<V>::is_integer,
373 bool>::type
374 CheckedMod(T x, U y, V* result) {
375 using Promotion = typename ArithmeticPromotion<T, U>::type;
376 Promotion presult;
377 bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
378 static_cast<Promotion>(y), &presult);
379 *result = static_cast<V>(presult);
380 return is_valid && IsValueInRangeForNumericType<V>(presult);
381 }
382
263 template <typename T> 383 template <typename T>
264 typename std::enable_if<std::numeric_limits<T>::is_integer && 384 typename std::enable_if<std::numeric_limits<T>::is_integer &&
265 std::numeric_limits<T>::is_signed, 385 std::numeric_limits<T>::is_signed,
266 bool>::type 386 bool>::type
267 CheckedNeg(T value, T* result) { 387 CheckedNeg(T value, T* result) {
268 // The negation of signed min is min, so catch that one. 388 // The negation of signed min is min, so catch that one.
269 if (value != std::numeric_limits<T>::min()) { 389 if (value != std::numeric_limits<T>::min()) {
270 *result = static_cast<T>(-value); 390 *result = static_cast<T>(-value);
271 return true; 391 return true;
272 } 392 }
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
337 457
338 template <typename T> 458 template <typename T>
339 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( 459 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
340 T value, 460 T value,
341 bool*) { 461 bool*) {
342 NOTREACHED(); 462 NOTREACHED();
343 return static_cast<T>(std::abs(value)); 463 return static_cast<T>(std::abs(value));
344 } 464 }
345 465
346 // These are the floating point stubs that the compiler needs to see. 466 // These are the floating point stubs that the compiler needs to see.
347 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ 467 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
348 template <typename T> \ 468 template <typename T, typename U, typename V> \
349 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type \ 469 typename std::enable_if<std::numeric_limits<T>::is_iec559 || \
350 Checked##NAME(T, T, T*) { \ 470 std::numeric_limits<U>::is_iec559 || \
351 NOTREACHED(); \ 471 std::numeric_limits<V>::is_iec559, \
352 return static_cast<T>(false); \ 472 bool>::type Checked##NAME(T, U, V*) { \
473 NOTREACHED(); \
474 return static_cast<T>(false); \
353 } 475 }
354 476
355 BASE_FLOAT_ARITHMETIC_STUBS(Add) 477 BASE_FLOAT_ARITHMETIC_STUBS(Add)
356 BASE_FLOAT_ARITHMETIC_STUBS(Sub) 478 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
357 BASE_FLOAT_ARITHMETIC_STUBS(Mul) 479 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
358 BASE_FLOAT_ARITHMETIC_STUBS(Div) 480 BASE_FLOAT_ARITHMETIC_STUBS(Div)
359 BASE_FLOAT_ARITHMETIC_STUBS(Mod) 481 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
360 482
361 #undef BASE_FLOAT_ARITHMETIC_STUBS 483 #undef BASE_FLOAT_ARITHMETIC_STUBS
362 484
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 589
468 // Copy constructor. 590 // Copy constructor.
469 template <typename Src> 591 template <typename Src>
470 CheckedNumericState(const CheckedNumericState<Src>& rhs) 592 CheckedNumericState(const CheckedNumericState<Src>& rhs)
471 : value_(static_cast<T>(rhs.value())) {} 593 : value_(static_cast<T>(rhs.value())) {}
472 594
473 bool is_valid() const { return std::isfinite(value_); } 595 bool is_valid() const { return std::isfinite(value_); }
474 T value() const { return value_; } 596 T value() const { return value_; }
475 }; 597 };
476 598
477 // For integers less than 128-bit and floats 32-bit or larger, we have the type
478 // with the larger maximum exponent take precedence.
479 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
480
481 template <typename Lhs,
482 typename Rhs = Lhs,
483 ArithmeticPromotionCategory Promotion =
484 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
485 ? LEFT_PROMOTION
486 : RIGHT_PROMOTION>
487 struct ArithmeticPromotion;
488
489 template <typename Lhs, typename Rhs>
490 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
491 typedef Lhs type;
492 };
493
494 template <typename Lhs, typename Rhs>
495 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
496 typedef Rhs type;
497 };
498
499 // We can statically check if operations on the provided types can wrap, so we
500 // can skip the checked operations if they're not needed. So, for an integer we
501 // care if the destination type preserves the sign and is twice the width of
502 // the source.
503 template <typename T, typename Lhs, typename Rhs>
504 struct IsIntegerArithmeticSafe {
505 static const bool value = !std::numeric_limits<T>::is_iec559 &&
506 StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
507 NUMERIC_RANGE_CONTAINED &&
508 sizeof(T) >= (2 * sizeof(Lhs)) &&
509 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
510 NUMERIC_RANGE_CONTAINED &&
511 sizeof(T) >= (2 * sizeof(Rhs));
512 };
513
514 } // namespace internal 599 } // namespace internal
515 } // namespace base 600 } // namespace base
516 601
517 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 602 #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