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; | |
jschuh
2017/06/26 20:26:50
The only functional change here is supporting the
| |
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> |
256 struct CheckedModOp<T, | 258 struct CheckedModOp<T, |
257 U, | 259 U, |
258 typename std::enable_if<std::is_integral<T>::value && | 260 typename std::enable_if<std::is_integral<T>::value && |
259 std::is_integral<U>::value>::type> { | 261 std::is_integral<U>::value>::type> { |
260 using result_type = typename MaxExponentPromotion<T, U>::type; | 262 using result_type = typename MaxExponentPromotion<T, U>::type; |
261 template <typename V> | 263 template <typename V> |
262 static bool Do(T x, U y, V* result) { | 264 static bool Do(T x, U y, V* result) { |
263 using Promotion = typename BigEnoughPromotion<T, U>::type; | 265 using Promotion = typename BigEnoughPromotion<T, U>::type; |
264 Promotion presult; | 266 Promotion presult; |
265 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), | 267 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), |
266 static_cast<Promotion>(y), &presult); | 268 static_cast<Promotion>(y), &presult); |
267 *result = static_cast<V>(presult); | 269 *result = static_cast<V>(presult); |
268 return is_valid && IsValueInRangeForNumericType<V>(presult); | 270 return is_valid && IsValueInRangeForNumericType<V>(presult); |
269 } | 271 } |
270 }; | 272 }; |
271 | 273 |
272 template <typename T, typename U, class Enable = void> | 274 template <typename T, typename U, class Enable = void> |
273 struct CheckedLshOp {}; | 275 struct CheckedLshOp {}; |
274 | 276 |
275 // Left shift. Shifts less than 0 or greater than or equal to the number | |
276 // of bits in the promoted type are undefined. Shifts of negative values | |
277 // are undefined. Otherwise it is defined when the result fits. | |
278 template <typename T, typename U> | 277 template <typename T, typename U> |
279 struct CheckedLshOp<T, | 278 struct CheckedLshOp<T, |
280 U, | 279 U, |
281 typename std::enable_if<std::is_integral<T>::value && | 280 typename std::enable_if<std::is_integral<T>::value && |
282 std::is_integral<U>::value>::type> { | 281 std::is_integral<U>::value>::type> { |
283 using result_type = T; | 282 using result_type = T; |
284 template <typename V> | 283 template <typename V> |
285 static bool Do(T x, U shift, V* result) { | 284 static bool Do(T x, U shift, V* result) { |
286 using ShiftType = typename std::make_unsigned<T>::type; | 285 if (!IsValueNegative(x) && |
287 static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value; | 286 as_unsigned(shift) < std::numeric_limits<T>::digits) { |
288 const ShiftType real_shift = static_cast<ShiftType>(shift); | |
289 // Signed shift is not legal on negative values. | |
290 if (!IsValueNegative(x) && real_shift < kBitWidth) { | |
291 // Just use a multiplication because it's easy. | 287 // Just use a multiplication because it's easy. |
292 // TODO(jschuh): This could probably be made more efficient. | 288 // TODO(jschuh): This could probably be made more efficient. |
293 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) | 289 return CheckedMulOp<T, T>::Do(x, T(1) << shift, result); |
294 return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result); | |
295 return !x; // Special case zero for a full width signed shift. | |
296 } | 290 } |
297 return false; | 291 // The standard defines only the result of the shift, rather than the |
292 // values of source operands. So, shifting zero by any amount is legal. | |
293 // Otherwise, we overflowed or have an undefined operation. | |
294 return !x ? (*result = V(0)), true : false; | |
jschuh
2017/06/26 20:26:50
Stumbled on a spec error in how I was handling zer
dcheng
2017/06/28 07:25:06
Acknowledged--though for future CLs, it would be n
jschuh
2017/06/28 12:32:37
Weird. I'm looking at it now and you're right. May
| |
298 } | 295 } |
299 }; | 296 }; |
300 | 297 |
301 template <typename T, typename U, class Enable = void> | 298 template <typename T, typename U, class Enable = void> |
302 struct CheckedRshOp {}; | 299 struct CheckedRshOp {}; |
303 | 300 |
304 // 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, | |
306 // but a right shift of a negative value is implementation-dependent. | |
307 template <typename T, typename U> | 301 template <typename T, typename U> |
308 struct CheckedRshOp<T, | 302 struct CheckedRshOp<T, |
309 U, | 303 U, |
310 typename std::enable_if<std::is_integral<T>::value && | 304 typename std::enable_if<std::is_integral<T>::value && |
311 std::is_integral<U>::value>::type> { | 305 std::is_integral<U>::value>::type> { |
312 using result_type = T; | 306 using result_type = T; |
313 template <typename V> | 307 template <typename V> |
314 static bool Do(T x, U shift, V* result) { | 308 static bool Do(T x, U shift, V* result) { |
315 // Use the type conversion push negative values out of range. | 309 // Use the type conversion push negative values out of range. |
316 using ShiftType = typename std::make_unsigned<T>::type; | 310 using ShiftType = typename std::make_unsigned<T>::type; |
317 if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { | 311 if (!x || static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { |
jschuh
2017/06/26 20:26:50
Stumbled on a spec error in how I was handling zer
dcheng
2017/06/28 07:25:06
Ditto.
jschuh
2017/06/28 12:32:37
Yup.
| |
318 T tmp = x >> shift; | 312 T tmp = x >> shift; |
319 *result = static_cast<V>(tmp); | 313 *result = static_cast<V>(tmp); |
320 return IsValueInRangeForNumericType<V>(tmp); | 314 return IsValueInRangeForNumericType<V>(tmp); |
321 } | 315 } |
322 return false; | 316 return false; |
323 } | 317 } |
324 }; | 318 }; |
325 | 319 |
326 template <typename T, typename U, class Enable = void> | 320 template <typename T, typename U, class Enable = void> |
327 struct CheckedAndOp {}; | 321 struct CheckedAndOp {}; |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
564 return value_ <= std::numeric_limits<T>::max() && | 558 return value_ <= std::numeric_limits<T>::max() && |
565 value_ >= std::numeric_limits<T>::lowest(); | 559 value_ >= std::numeric_limits<T>::lowest(); |
566 } | 560 } |
567 constexpr T value() const { return value_; } | 561 constexpr T value() const { return value_; } |
568 }; | 562 }; |
569 | 563 |
570 } // namespace internal | 564 } // namespace internal |
571 } // namespace base | 565 } // namespace base |
572 | 566 |
573 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 567 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
OLD | NEW |