OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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_CHECKED_MATH_IMPL_H_ | 5 #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
6 #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 6 #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
7 | 7 |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 #include <stdint.h> | 9 #include <stdint.h> |
10 | 10 |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 (IntegerBitsPlusSign<__typeof__(x * y)>::value == | 175 (IntegerBitsPlusSign<__typeof__(x * y)>::value == |
176 IntegerBitsPlusSign<intptr_t>::value && | 176 IntegerBitsPlusSign<intptr_t>::value && |
177 std::is_signed<T>::value == std::is_signed<U>::value); | 177 std::is_signed<T>::value == std::is_signed<U>::value); |
178 #else | 178 #else |
179 static const bool kUseMaxInt = true; | 179 static const bool kUseMaxInt = true; |
180 #endif | 180 #endif |
181 if (kUseMaxInt) | 181 if (kUseMaxInt) |
182 return !__builtin_mul_overflow(x, y, result); | 182 return !__builtin_mul_overflow(x, y, result); |
183 #endif | 183 #endif |
184 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; | 184 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; |
| 185 // Verify the destination type can hold the result (always true for 0). |
| 186 if (!(IsValueInRangeForNumericType<Promotion>(x) && |
| 187 IsValueInRangeForNumericType<Promotion>(y)) && |
| 188 x && y) { |
| 189 return false; |
| 190 } |
185 Promotion presult; | 191 Promotion presult; |
186 // Fail if either operand is out of range for the promoted type. | 192 bool is_valid = true; |
187 // TODO(jschuh): This could be made to work for a broader range of values. | |
188 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | |
189 IsValueInRangeForNumericType<Promotion>(y); | |
190 | |
191 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { | 193 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
192 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); | 194 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
193 } else { | 195 } else { |
194 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), | 196 is_valid = CheckedMulImpl(static_cast<Promotion>(x), |
195 static_cast<Promotion>(y), &presult); | 197 static_cast<Promotion>(y), &presult); |
196 } | 198 } |
197 *result = static_cast<V>(presult); | 199 *result = static_cast<V>(presult); |
198 return is_valid && IsValueInRangeForNumericType<V>(presult); | 200 return is_valid && IsValueInRangeForNumericType<V>(presult); |
199 } | 201 } |
200 }; | 202 }; |
201 | 203 |
202 // Avoid poluting the namespace once we're done with the macro. | 204 // Avoid poluting the namespace once we're done with the macro. |
203 #undef USE_OVERFLOW_BUILTINS | 205 #undef USE_OVERFLOW_BUILTINS |
204 | 206 |
205 // Division just requires a check for a zero denominator or an invalid negation | 207 // Division just requires a check for a zero denominator or an invalid negation |
(...skipping 29 matching lines...) Expand all Loading... |
235 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), | 237 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), |
236 static_cast<Promotion>(y), &presult); | 238 static_cast<Promotion>(y), &presult); |
237 *result = static_cast<V>(presult); | 239 *result = static_cast<V>(presult); |
238 return is_valid && IsValueInRangeForNumericType<V>(presult); | 240 return is_valid && IsValueInRangeForNumericType<V>(presult); |
239 } | 241 } |
240 }; | 242 }; |
241 | 243 |
242 template <typename T> | 244 template <typename T> |
243 bool CheckedModImpl(T x, T y, T* result) { | 245 bool CheckedModImpl(T x, T y, T* result) { |
244 static_assert(std::is_integral<T>::value, "Type must be integral"); | 246 static_assert(std::is_integral<T>::value, "Type must be integral"); |
245 if (y > 0) { | 247 if (y) { |
246 *result = static_cast<T>(x % y); | 248 *result = static_cast<T>(x % y); |
247 return true; | 249 return true; |
248 } | 250 } |
249 return false; | 251 return false; |
250 } | 252 } |
251 | 253 |
252 template <typename T, typename U, class Enable = void> | 254 template <typename T, typename U, class Enable = void> |
253 struct CheckedModOp {}; | 255 struct CheckedModOp {}; |
254 | 256 |
255 template <typename T, typename U> | 257 template <typename T, typename U> |
(...skipping 20 matching lines...) Expand all Loading... |
276 // of bits in the promoted type are undefined. Shifts of negative values | 278 // of bits in the promoted type are undefined. Shifts of negative values |
277 // are undefined. Otherwise it is defined when the result fits. | 279 // are undefined. Otherwise it is defined when the result fits. |
278 template <typename T, typename U> | 280 template <typename T, typename U> |
279 struct CheckedLshOp<T, | 281 struct CheckedLshOp<T, |
280 U, | 282 U, |
281 typename std::enable_if<std::is_integral<T>::value && | 283 typename std::enable_if<std::is_integral<T>::value && |
282 std::is_integral<U>::value>::type> { | 284 std::is_integral<U>::value>::type> { |
283 using result_type = T; | 285 using result_type = T; |
284 template <typename V> | 286 template <typename V> |
285 static bool Do(T x, U shift, V* result) { | 287 static bool Do(T x, U shift, V* result) { |
286 using ShiftType = typename std::make_unsigned<T>::type; | |
287 static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value; | |
288 const ShiftType real_shift = static_cast<ShiftType>(shift); | |
289 // Signed shift is not legal on negative values. | 288 // Signed shift is not legal on negative values. |
290 if (!IsValueNegative(x) && real_shift < kBitWidth) { | 289 if (!IsValueNegative(x) && |
| 290 as_unsigned(shift) < IntegerBitsPlusSign<T>::value) { |
291 // Just use a multiplication because it's easy. | 291 // Just use a multiplication because it's easy. |
292 // TODO(jschuh): This could probably be made more efficient. | 292 // TODO(jschuh): This could probably be made more efficient. |
293 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) | 293 if (!std::is_signed<T>::value || |
294 return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result); | 294 as_unsigned(shift) < std::numeric_limits<T>::digits) |
| 295 return CheckedMulOp<T, T>::Do(x, T(1) << shift, result); |
| 296 *result = 0; |
295 return !x; // Special case zero for a full width signed shift. | 297 return !x; // Special case zero for a full width signed shift. |
296 } | 298 } |
297 return false; | 299 return false; |
298 } | 300 } |
299 }; | 301 }; |
300 | 302 |
301 template <typename T, typename U, class Enable = void> | 303 template <typename T, typename U, class Enable = void> |
302 struct CheckedRshOp {}; | 304 struct CheckedRshOp {}; |
303 | 305 |
304 // Right shift. Shifts less than 0 or greater than or equal to the number | 306 // Right shift. Shifts less than 0 or greater than or equal to the number |
305 // of bits in the promoted type are undefined. Otherwise, it is always defined, | 307 // of bits in the promoted type are undefined. Otherwise, it is always defined, |
306 // but a right shift of a negative value is implementation-dependent. | 308 // but a right shift of a negative value is implementation-dependent. |
307 template <typename T, typename U> | 309 template <typename T, typename U> |
308 struct CheckedRshOp<T, | 310 struct CheckedRshOp<T, |
309 U, | 311 U, |
310 typename std::enable_if<std::is_integral<T>::value && | 312 typename std::enable_if<std::is_integral<T>::value && |
311 std::is_integral<U>::value>::type> { | 313 std::is_integral<U>::value>::type> { |
312 using result_type = T; | 314 using result_type = T; |
313 template <typename V> | 315 template <typename V> |
314 static bool Do(T x, U shift, V* result) { | 316 static bool Do(T x, U shift, V* result) { |
315 // Use the type conversion push negative values out of range. | 317 // Use the type conversion push negative values out of range. |
316 using ShiftType = typename std::make_unsigned<T>::type; | 318 if (as_unsigned(shift) < IntegerBitsPlusSign<T>::value) { |
317 if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { | |
318 T tmp = x >> shift; | 319 T tmp = x >> shift; |
319 *result = static_cast<V>(tmp); | 320 *result = static_cast<V>(tmp); |
320 return IsValueInRangeForNumericType<V>(tmp); | 321 return IsValueInRangeForNumericType<V>(tmp); |
321 } | 322 } |
322 return false; | 323 return false; |
323 } | 324 } |
324 }; | 325 }; |
325 | 326 |
326 template <typename T, typename U, class Enable = void> | 327 template <typename T, typename U, class Enable = void> |
327 struct CheckedAndOp {}; | 328 struct CheckedAndOp {}; |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
564 return value_ <= std::numeric_limits<T>::max() && | 565 return value_ <= std::numeric_limits<T>::max() && |
565 value_ >= std::numeric_limits<T>::lowest(); | 566 value_ >= std::numeric_limits<T>::lowest(); |
566 } | 567 } |
567 constexpr T value() const { return value_; } | 568 constexpr T value() const { return value_; } |
568 }; | 569 }; |
569 | 570 |
570 } // namespace internal | 571 } // namespace internal |
571 } // namespace base | 572 } // namespace base |
572 | 573 |
573 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 574 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
OLD | NEW |