• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_
6 #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
7 
8 // IWYU pragma: private, include "base/numerics/checked_math.h"
9 
10 #include <stdint.h>
11 
12 #include <cmath>
13 #include <concepts>
14 #include <limits>
15 #include <type_traits>
16 
17 #include "base/numerics/safe_conversions.h"
18 #include "base/numerics/safe_math_shared_impl.h"  // IWYU pragma: export
19 
20 namespace base {
21 namespace internal {
22 
23 template <typename T>
CheckedAddImpl(T x,T y,T * result)24 constexpr bool CheckedAddImpl(T x, T y, T* result) {
25   static_assert(std::is_integral_v<T>, "Type must be integral");
26   // Since the value of x+y is undefined if we have a signed type, we compute
27   // it using the unsigned type of the same size.
28   using UnsignedDst = typename std::make_unsigned<T>::type;
29   using SignedDst = typename std::make_signed<T>::type;
30   const UnsignedDst ux = static_cast<UnsignedDst>(x);
31   const UnsignedDst uy = static_cast<UnsignedDst>(y);
32   const UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
33   // Addition is valid if the sign of (x + y) is equal to either that of x or
34   // that of y.
35   if (std::is_signed_v<T>
36           ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) < 0
37           : uresult < uy) {  // Unsigned is either valid or underflow.
38     return false;
39   }
40   *result = static_cast<T>(uresult);
41   return true;
42 }
43 
44 template <typename T, typename U>
45 struct CheckedAddOp {};
46 
47 template <typename T, typename U>
48   requires(std::integral<T> && std::integral<U>)
49 struct CheckedAddOp<T, U> {
50   using result_type = MaxExponentPromotion<T, U>;
51   template <typename V>
52   static constexpr bool Do(T x, U y, V* result) {
53     if constexpr (CheckedAddFastOp<T, U>::is_supported)
54       return CheckedAddFastOp<T, U>::Do(x, y, result);
55 
56     // Double the underlying type up to a full machine word.
57     using FastPromotion = FastIntegerArithmeticPromotion<T, U>;
58     using Promotion =
59         std::conditional_t<(kIntegerBitsPlusSign<FastPromotion> >
60                             kIntegerBitsPlusSign<intptr_t>),
61                            BigEnoughPromotion<T, U>, FastPromotion>;
62     // Fail if either operand is out of range for the promoted type.
63     // TODO(jschuh): This could be made to work for a broader range of values.
64     if (!IsValueInRangeForNumericType<Promotion>(x) ||
65         !IsValueInRangeForNumericType<Promotion>(y)) [[unlikely]] {
66       return false;
67     }
68 
69     Promotion presult = {};
70     bool is_valid = true;
71     if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
72       presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
73     } else {
74       is_valid = CheckedAddImpl(static_cast<Promotion>(x),
75                                 static_cast<Promotion>(y), &presult);
76     }
77     if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
78       return false;
79     *result = static_cast<V>(presult);
80     return true;
81   }
82 };
83 
84 template <typename T>
85 constexpr bool CheckedSubImpl(T x, T y, T* result) {
86   static_assert(std::is_integral_v<T>, "Type must be integral");
87   // Since the value of x+y is undefined if we have a signed type, we compute
88   // it using the unsigned type of the same size.
89   using UnsignedDst = typename std::make_unsigned<T>::type;
90   using SignedDst = typename std::make_signed<T>::type;
91   const UnsignedDst ux = static_cast<UnsignedDst>(x);
92   const UnsignedDst uy = static_cast<UnsignedDst>(y);
93   const UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
94   // Subtraction is valid if either x and y have same sign, or (x-y) and x have
95   // the same sign.
96   if (std::is_signed_v<T>
97           ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) < 0
98           : x < y) {
99     return false;
100   }
101   *result = static_cast<T>(uresult);
102   return true;
103 }
104 
105 template <typename T, typename U>
106 struct CheckedSubOp {};
107 
108 template <typename T, typename U>
109   requires(std::integral<T> && std::integral<U>)
110 struct CheckedSubOp<T, U> {
111   using result_type = MaxExponentPromotion<T, U>;
112   template <typename V>
113   static constexpr bool Do(T x, U y, V* result) {
114     if constexpr (CheckedSubFastOp<T, U>::is_supported)
115       return CheckedSubFastOp<T, U>::Do(x, y, result);
116 
117     // Double the underlying type up to a full machine word.
118     using FastPromotion = FastIntegerArithmeticPromotion<T, U>;
119     using Promotion =
120         std::conditional_t<(kIntegerBitsPlusSign<FastPromotion> >
121                             kIntegerBitsPlusSign<intptr_t>),
122                            BigEnoughPromotion<T, U>, FastPromotion>;
123     // Fail if either operand is out of range for the promoted type.
124     // TODO(jschuh): This could be made to work for a broader range of values.
125     if (!IsValueInRangeForNumericType<Promotion>(x) ||
126         !IsValueInRangeForNumericType<Promotion>(y)) [[unlikely]] {
127       return false;
128     }
129 
130     Promotion presult = {};
131     bool is_valid = true;
132     if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
133       presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
134     } else {
135       is_valid = CheckedSubImpl(static_cast<Promotion>(x),
136                                 static_cast<Promotion>(y), &presult);
137     }
138     if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
139       return false;
140     *result = static_cast<V>(presult);
141     return true;
142   }
143 };
144 
145 template <typename T>
146 constexpr bool CheckedMulImpl(T x, T y, T* result) {
147   static_assert(std::is_integral_v<T>, "Type must be integral");
148   // Since the value of x*y is potentially undefined if we have a signed type,
149   // we compute it using the unsigned type of the same size.
150   using UnsignedDst = typename std::make_unsigned<T>::type;
151   using SignedDst = typename std::make_signed<T>::type;
152   const UnsignedDst ux = SafeUnsignedAbs(x);
153   const UnsignedDst uy = SafeUnsignedAbs(y);
154   const UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
155   const bool is_negative =
156       std::is_signed_v<T> && static_cast<SignedDst>(x ^ y) < 0;
157   // We have a fast out for unsigned identity or zero on the second operand.
158   // After that it's an unsigned overflow check on the absolute value, with
159   // a +1 bound for a negative result.
160   if (uy > UnsignedDst(!std::is_signed_v<T> || is_negative) &&
161       ux > (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy) {
162     return false;
163   }
164   *result = static_cast<T>(is_negative ? 0 - uresult : uresult);
165   return true;
166 }
167 
168 template <typename T, typename U>
169 struct CheckedMulOp {};
170 
171 template <typename T, typename U>
172   requires(std::integral<T> && std::integral<U>)
173 struct CheckedMulOp<T, U> {
174   using result_type = MaxExponentPromotion<T, U>;
175   template <typename V>
176   static constexpr bool Do(T x, U y, V* result) {
177     if constexpr (CheckedMulFastOp<T, U>::is_supported)
178       return CheckedMulFastOp<T, U>::Do(x, y, result);
179 
180     using Promotion = FastIntegerArithmeticPromotion<T, U>;
181     // Verify the destination type can hold the result (always true for 0).
182     if ((!IsValueInRangeForNumericType<Promotion>(x) ||
183          !IsValueInRangeForNumericType<Promotion>(y)) &&
184         x && y) [[unlikely]] {
185       return false;
186     }
187 
188     Promotion presult = {};
189     bool is_valid = true;
190     if constexpr (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
191       // The fast op may be available with the promoted type.
192       // The casts here are safe because of the "value in range" conditional
193       // above.
194       is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(
195           static_cast<Promotion>(x), static_cast<Promotion>(y), &presult);
196     } else if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
197       presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
198     } else {
199       is_valid = CheckedMulImpl(static_cast<Promotion>(x),
200                                 static_cast<Promotion>(y), &presult);
201     }
202     if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
203       return false;
204     *result = static_cast<V>(presult);
205     return true;
206   }
207 };
208 
209 // Division just requires a check for a zero denominator or an invalid negation
210 // on signed min/-1.
211 template <typename T, typename U>
212 struct CheckedDivOp {};
213 
214 template <typename T, typename U>
215   requires(std::integral<T> && std::integral<U>)
216 struct CheckedDivOp<T, U> {
217   using result_type = MaxExponentPromotion<T, U>;
218   template <typename V>
219   static constexpr bool Do(T x, U y, V* result) {
220     if (!y) [[unlikely]] {
221       return false;
222     }
223 
224     // The overflow check can be compiled away if we don't have the exact
225     // combination of types needed to trigger this case.
226     using Promotion = BigEnoughPromotion<T, U>;
227     if (std::is_signed_v<T> && std::is_signed_v<U> &&
228         kIsTypeInRangeForNumericType<T, Promotion> &&
229         static_cast<Promotion>(x) == std::numeric_limits<Promotion>::lowest() &&
230         y == static_cast<U>(-1)) [[unlikely]] {
231       return false;
232     }
233 
234     // This branch always compiles away if the above branch wasn't removed.
235     if ((!IsValueInRangeForNumericType<Promotion>(x) ||
236          !IsValueInRangeForNumericType<Promotion>(y)) &&
237         x) [[unlikely]] {
238       return false;
239     }
240 
241     const Promotion presult = Promotion(x) / Promotion(y);
242     if (!IsValueInRangeForNumericType<V>(presult))
243       return false;
244     *result = static_cast<V>(presult);
245     return true;
246   }
247 };
248 
249 template <typename T, typename U>
250 struct CheckedModOp {};
251 
252 template <typename T, typename U>
253   requires(std::integral<T> && std::integral<U>)
254 struct CheckedModOp<T, U> {
255   using result_type = MaxExponentPromotion<T, U>;
256   template <typename V>
257   static constexpr bool Do(T x, U y, V* result) {
258     if (!y) [[unlikely]] {
259       return false;
260     }
261 
262     using Promotion = BigEnoughPromotion<T, U>;
263     if (std::is_signed_v<T> && std::is_signed_v<U> &&
264         kIsTypeInRangeForNumericType<T, Promotion> &&
265         static_cast<Promotion>(x) == std::numeric_limits<Promotion>::lowest() &&
266         y == static_cast<U>(-1)) [[unlikely]] {
267       *result = 0;
268       return true;
269     }
270 
271     const Promotion presult =
272         static_cast<Promotion>(x) % static_cast<Promotion>(y);
273     if (!IsValueInRangeForNumericType<V>(presult))
274       return false;
275     *result = static_cast<Promotion>(presult);
276     return true;
277   }
278 };
279 
280 template <typename T, typename U>
281 struct CheckedLshOp {};
282 
283 // Left shift. Shifts less than 0 or greater than or equal to the number
284 // of bits in the promoted type are undefined. Shifts of negative values
285 // are undefined. Otherwise it is defined when the result fits.
286 template <typename T, typename U>
287   requires(std::integral<T> && std::integral<U>)
288 struct CheckedLshOp<T, U> {
289   using result_type = T;
290   template <typename V>
291   static constexpr bool Do(T x, U shift, V* result) {
292     // Disallow negative numbers and verify the shift is in bounds.
293     if (!IsValueNegative(x) &&
294         as_unsigned(shift) < as_unsigned(std::numeric_limits<T>::digits))
295         [[likely]] {
296       // Shift as unsigned to avoid undefined behavior.
297       *result = static_cast<V>(as_unsigned(x) << shift);
298       // If the shift can be reversed, we know it was valid.
299       return *result >> shift == x;
300     }
301 
302     // Handle the legal corner-case of a full-width signed shift of zero.
303     if (!std::is_signed_v<T> || x ||
304         as_unsigned(shift) != as_unsigned(std::numeric_limits<T>::digits)) {
305       return false;
306     }
307     *result = 0;
308     return true;
309   }
310 };
311 
312 template <typename T, typename U>
313 struct CheckedRshOp {};
314 
315 // Right shift. Shifts less than 0 or greater than or equal to the number
316 // of bits in the promoted type are undefined. Otherwise, it is always defined,
317 // but a right shift of a negative value is implementation-dependent.
318 template <typename T, typename U>
319   requires(std::integral<T> && std::integral<U>)
320 struct CheckedRshOp<T, U> {
321   using result_type = T;
322   template <typename V>
323   static constexpr bool Do(T x, U shift, V* result) {
324     // Use sign conversion to push negative values out of range.
325     if (as_unsigned(shift) >= kIntegerBitsPlusSign<T>) [[unlikely]] {
326       return false;
327     }
328 
329     const T tmp = x >> shift;
330     if (!IsValueInRangeForNumericType<V>(tmp))
331       return false;
332     *result = static_cast<V>(tmp);
333     return true;
334   }
335 };
336 
337 template <typename T, typename U>
338 struct CheckedAndOp {};
339 
340 // For simplicity we support only unsigned integer results.
341 template <typename T, typename U>
342   requires(std::integral<T> && std::integral<U>)
343 struct CheckedAndOp<T, U> {
344   using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
345   template <typename V>
346   static constexpr bool Do(T x, U y, V* result) {
347     const result_type tmp =
348         static_cast<result_type>(x) & static_cast<result_type>(y);
349     if (!IsValueInRangeForNumericType<V>(tmp))
350       return false;
351     *result = static_cast<V>(tmp);
352     return true;
353   }
354 };
355 
356 template <typename T, typename U>
357 struct CheckedOrOp {};
358 
359 // For simplicity we support only unsigned integers.
360 template <typename T, typename U>
361   requires(std::integral<T> && std::integral<U>)
362 struct CheckedOrOp<T, U> {
363   using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
364   template <typename V>
365   static constexpr bool Do(T x, U y, V* result) {
366     const result_type tmp =
367         static_cast<result_type>(x) | static_cast<result_type>(y);
368     if (!IsValueInRangeForNumericType<V>(tmp))
369       return false;
370     *result = static_cast<V>(tmp);
371     return true;
372   }
373 };
374 
375 template <typename T, typename U>
376 struct CheckedXorOp {};
377 
378 // For simplicity we support only unsigned integers.
379 template <typename T, typename U>
380   requires(std::integral<T> && std::integral<U>)
381 struct CheckedXorOp<T, U> {
382   using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
383   template <typename V>
384   static constexpr bool Do(T x, U y, V* result) {
385     const result_type tmp =
386         static_cast<result_type>(x) ^ static_cast<result_type>(y);
387     if (!IsValueInRangeForNumericType<V>(tmp))
388       return false;
389     *result = static_cast<V>(tmp);
390     return true;
391   }
392 };
393 
394 // Max doesn't really need to be implemented this way because it can't fail,
395 // but it makes the code much cleaner to use the MathOp wrappers.
396 template <typename T, typename U>
397 struct CheckedMaxOp {};
398 
399 template <typename T, typename U>
400   requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
401 struct CheckedMaxOp<T, U> {
402   using result_type = MaxExponentPromotion<T, U>;
403   template <typename V>
404   static constexpr bool Do(T x, U y, V* result) {
405     const result_type tmp = IsGreater<T, U>::Test(x, y)
406                                 ? static_cast<result_type>(x)
407                                 : static_cast<result_type>(y);
408     if (!IsValueInRangeForNumericType<V>(tmp))
409       return false;
410     *result = static_cast<V>(tmp);
411     return true;
412   }
413 };
414 
415 // Min doesn't really need to be implemented this way because it can't fail,
416 // but it makes the code much cleaner to use the MathOp wrappers.
417 template <typename T, typename U>
418 struct CheckedMinOp {};
419 
420 template <typename T, typename U>
421   requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
422 struct CheckedMinOp<T, U> {
423   using result_type = LowestValuePromotion<T, U>;
424   template <typename V>
425   static constexpr bool Do(T x, U y, V* result) {
426     const result_type tmp = IsLess<T, U>::Test(x, y)
427                                 ? static_cast<result_type>(x)
428                                 : static_cast<result_type>(y);
429     if (!IsValueInRangeForNumericType<V>(tmp))
430       return false;
431     *result = static_cast<V>(tmp);
432     return true;
433   }
434 };
435 
436 // This is just boilerplate that wraps the standard floating point arithmetic.
437 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
438 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
439   template <typename T, typename U>                                      \
440     requires(std::is_floating_point_v<T> || std::is_floating_point_v<U>) \
441   struct Checked##NAME##Op<T, U> {                                       \
442     using result_type = MaxExponentPromotion<T, U>;                      \
443     template <typename V>                                                \
444     static constexpr bool Do(T x, U y, V* result) {                      \
445       const result_type presult = x OP y;                                \
446       if (!IsValueInRangeForNumericType<V>(presult))                     \
447         return false;                                                    \
448       *result = static_cast<V>(presult);                                 \
449       return true;                                                       \
450     }                                                                    \
451   };
452 
453 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
454 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
455 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
456 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
457 
458 #undef BASE_FLOAT_ARITHMETIC_OPS
459 
460 // Floats carry around their validity state with them, but integers do not. So,
461 // we wrap the underlying value in a specialization in order to hide that detail
462 // and expose an interface via accessors.
463 enum NumericRepresentation {
464   NUMERIC_INTEGER,
465   NUMERIC_FLOATING,
466   NUMERIC_UNKNOWN
467 };
468 
469 template <typename NumericType>
470 struct GetNumericRepresentation {
471   static const NumericRepresentation value =
472       std::is_integral_v<NumericType>
473           ? NUMERIC_INTEGER
474           : (std::is_floating_point_v<NumericType> ? NUMERIC_FLOATING
475                                                    : NUMERIC_UNKNOWN);
476 };
477 
478 template <typename T,
479           NumericRepresentation type = GetNumericRepresentation<T>::value>
480 class CheckedNumericState {};
481 
482 // Integrals require quite a bit of additional housekeeping to manage state.
483 template <typename T>
484 class CheckedNumericState<T, NUMERIC_INTEGER> {
485  public:
486   template <typename Src = int>
487   constexpr explicit CheckedNumericState(Src value = 0, bool is_valid = true)
488       : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
489         value_(WellDefinedConversionOrZero(value, is_valid_)) {
490     static_assert(std::is_arithmetic_v<Src>, "Argument must be numeric.");
491   }
492 
493   template <typename Src>
494   constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
495       : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
496 
497   constexpr bool is_valid() const { return is_valid_; }
498 
499   constexpr T value() const { return value_; }
500 
501  private:
502   // Ensures that a type conversion does not trigger undefined behavior.
503   template <typename Src>
504   static constexpr T WellDefinedConversionOrZero(Src value, bool is_valid) {
505     using SrcType = typename internal::UnderlyingType<Src>::type;
506     return (std::is_integral_v<SrcType> || is_valid) ? static_cast<T>(value)
507                                                      : 0;
508   }
509 
510   // is_valid_ precedes value_ because member initializers in the constructors
511   // are evaluated in field order, and is_valid_ must be read when initializing
512   // value_.
513   bool is_valid_;
514   T value_;
515 };
516 
517 // Floating points maintain their own validity, but need translation wrappers.
518 template <typename T>
519 class CheckedNumericState<T, NUMERIC_FLOATING> {
520  public:
521   template <typename Src = double>
522   constexpr explicit CheckedNumericState(Src value = 0.0, bool is_valid = true)
523       : value_(WellDefinedConversionOrNaN(
524             value,
525             is_valid && IsValueInRangeForNumericType<T>(value))) {}
526 
527   template <typename Src>
528   constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
529       : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
530 
531   constexpr bool is_valid() const {
532     // Written this way because std::isfinite is not constexpr before C++23.
533     // TODO(C++23): Use `std::isfinite()` unconditionally.
534     return std::is_constant_evaluated()
535                ? value_ <= std::numeric_limits<T>::max() &&
536                      value_ >= std::numeric_limits<T>::lowest()
537                : std::isfinite(value_);
538   }
539 
540   constexpr T value() const { return value_; }
541 
542  private:
543   // Ensures that a type conversion does not trigger undefined behavior.
544   template <typename Src>
545   static constexpr T WellDefinedConversionOrNaN(Src value, bool is_valid) {
546     using SrcType = typename internal::UnderlyingType<Src>::type;
547     return (kStaticDstRangeRelationToSrcRange<T, SrcType> ==
548                 NumericRangeRepresentation::kContained ||
549             is_valid)
550                ? static_cast<T>(value)
551                : std::numeric_limits<T>::quiet_NaN();
552   }
553 
554   T value_;
555 };
556 
557 }  // namespace internal
558 }  // namespace base
559 
560 #endif  // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
561