xref: /aosp_15_r20/external/cronet/base/numerics/safe_conversions_impl.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 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_SAFE_CONVERSIONS_IMPL_H_
6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
7 
8 #include <stdint.h>
9 
10 #include <concepts>
11 #include <limits>
12 #include <type_traits>
13 
14 #if defined(__GNUC__) || defined(__clang__)
15 #define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
16 #define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
17 #else
18 #define BASE_NUMERICS_LIKELY(x) (x)
19 #define BASE_NUMERICS_UNLIKELY(x) (x)
20 #endif
21 
22 namespace base {
23 namespace internal {
24 
25 // The std library doesn't provide a binary max_exponent for integers, however
26 // we can compute an analog using std::numeric_limits<>::digits.
27 template <typename NumericType>
28 struct MaxExponent {
29   static const int value = std::is_floating_point_v<NumericType>
30                                ? std::numeric_limits<NumericType>::max_exponent
31                                : std::numeric_limits<NumericType>::digits + 1;
32 };
33 
34 // The number of bits (including the sign) in an integer. Eliminates sizeof
35 // hacks.
36 template <typename NumericType>
37 struct IntegerBitsPlusSign {
38   static const int value =
39       std::numeric_limits<NumericType>::digits + std::is_signed_v<NumericType>;
40 };
41 
42 // Helper templates for integer manipulations.
43 
44 template <typename Integer>
45 struct PositionOfSignBit {
46   static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
47 };
48 
49 // Determines if a numeric value is negative without throwing compiler
50 // warnings on: unsigned(value) < 0.
51 template <typename T>
requires(std::is_arithmetic_v<T> && std::is_signed_v<T>)52   requires(std::is_arithmetic_v<T> && std::is_signed_v<T>)
53 constexpr bool IsValueNegative(T value) {
54   return value < 0;
55 }
56 
57 template <typename T>
requires(std::is_arithmetic_v<T> && std::is_unsigned_v<T>)58   requires(std::is_arithmetic_v<T> && std::is_unsigned_v<T>)
59 constexpr bool IsValueNegative(T) {
60   return false;
61 }
62 
63 // This performs a fast negation, returning a signed value. It works on unsigned
64 // arguments, but probably doesn't do what you want for any unsigned value
65 // larger than max / 2 + 1 (i.e. signed min cast to unsigned).
66 template <typename T>
ConditionalNegate(T x,bool is_negative)67 constexpr typename std::make_signed<T>::type ConditionalNegate(
68     T x,
69     bool is_negative) {
70   static_assert(std::is_integral_v<T>, "Type must be integral");
71   using SignedT = typename std::make_signed<T>::type;
72   using UnsignedT = typename std::make_unsigned<T>::type;
73   return static_cast<SignedT>((static_cast<UnsignedT>(x) ^
74                                static_cast<UnsignedT>(-SignedT(is_negative))) +
75                               is_negative);
76 }
77 
78 // This performs a safe, absolute value via unsigned overflow.
79 template <typename T>
SafeUnsignedAbs(T value)80 constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
81   static_assert(std::is_integral_v<T>, "Type must be integral");
82   using UnsignedT = typename std::make_unsigned<T>::type;
83   return IsValueNegative(value)
84              ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
85              : static_cast<UnsignedT>(value);
86 }
87 
88 // TODO(jschuh): Switch to std::is_constant_evaluated() once C++20 is supported.
89 // Alternately, the usage could be restructured for "consteval if" in C++23.
90 #define IsConstantEvaluated() (__builtin_is_constant_evaluated())
91 
92 // TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict
93 // some accelerated runtime paths to release builds until this can be forced
94 // with consteval support in C++20 or C++23.
95 #if defined(NDEBUG)
96 constexpr bool kEnableAsmCode = true;
97 #else
98 constexpr bool kEnableAsmCode = false;
99 #endif
100 
101 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
102 // Also used in a constexpr template to trigger a compilation failure on
103 // an error condition.
104 struct CheckOnFailure {
105   template <typename T>
HandleFailureCheckOnFailure106   static T HandleFailure() {
107 #if defined(_MSC_VER)
108     __debugbreak();
109 #elif defined(__GNUC__) || defined(__clang__)
110     __builtin_trap();
111 #else
112     ((void)(*(volatile char*)0 = 0));
113 #endif
114     return T();
115   }
116 };
117 
118 enum IntegerRepresentation {
119   INTEGER_REPRESENTATION_UNSIGNED,
120   INTEGER_REPRESENTATION_SIGNED
121 };
122 
123 // A range for a given nunmeric Src type is contained for a given numeric Dst
124 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
125 // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
126 // We implement this as template specializations rather than simple static
127 // comparisons to ensure type correctness in our comparisons.
128 enum NumericRangeRepresentation {
129   NUMERIC_RANGE_NOT_CONTAINED,
130   NUMERIC_RANGE_CONTAINED
131 };
132 
133 // Helper templates to statically determine if our destination type can contain
134 // maximum and minimum values represented by the source type.
135 
136 template <typename Dst,
137           typename Src,
138           IntegerRepresentation DstSign = std::is_signed_v<Dst>
139                                               ? INTEGER_REPRESENTATION_SIGNED
140                                               : INTEGER_REPRESENTATION_UNSIGNED,
141           IntegerRepresentation SrcSign = std::is_signed_v<Src>
142                                               ? INTEGER_REPRESENTATION_SIGNED
143                                               : INTEGER_REPRESENTATION_UNSIGNED>
144 struct StaticDstRangeRelationToSrcRange;
145 
146 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
147 // larger.
148 template <typename Dst, typename Src, IntegerRepresentation Sign>
149 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
150   static const NumericRangeRepresentation value =
151       MaxExponent<Dst>::value >= MaxExponent<Src>::value
152           ? NUMERIC_RANGE_CONTAINED
153           : NUMERIC_RANGE_NOT_CONTAINED;
154 };
155 
156 // Unsigned to signed: Dst is guaranteed to contain source only if its range is
157 // larger.
158 template <typename Dst, typename Src>
159 struct StaticDstRangeRelationToSrcRange<Dst,
160                                         Src,
161                                         INTEGER_REPRESENTATION_SIGNED,
162                                         INTEGER_REPRESENTATION_UNSIGNED> {
163   static const NumericRangeRepresentation value =
164       MaxExponent<Dst>::value > MaxExponent<Src>::value
165           ? NUMERIC_RANGE_CONTAINED
166           : NUMERIC_RANGE_NOT_CONTAINED;
167 };
168 
169 // Signed to unsigned: Dst cannot be statically determined to contain Src.
170 template <typename Dst, typename Src>
171 struct StaticDstRangeRelationToSrcRange<Dst,
172                                         Src,
173                                         INTEGER_REPRESENTATION_UNSIGNED,
174                                         INTEGER_REPRESENTATION_SIGNED> {
175   static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
176 };
177 
178 // This class wraps the range constraints as separate booleans so the compiler
179 // can identify constants and eliminate unused code paths.
180 class RangeCheck {
181  public:
182   constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
183       : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
184   constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}
185   constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
186   constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
187   constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
188   constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
189   constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
190   constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
191   constexpr bool operator==(const RangeCheck rhs) const {
192     return is_underflow_ == rhs.is_underflow_ &&
193            is_overflow_ == rhs.is_overflow_;
194   }
195   constexpr bool operator!=(const RangeCheck rhs) const {
196     return !(*this == rhs);
197   }
198 
199  private:
200   // Do not change the order of these member variables. The integral conversion
201   // optimization depends on this exact order.
202   const bool is_underflow_;
203   const bool is_overflow_;
204 };
205 
206 // The following helper template addresses a corner case in range checks for
207 // conversion from a floating-point type to an integral type of smaller range
208 // but larger precision (e.g. float -> unsigned). The problem is as follows:
209 //   1. Integral maximum is always one less than a power of two, so it must be
210 //      truncated to fit the mantissa of the floating point. The direction of
211 //      rounding is implementation defined, but by default it's always IEEE
212 //      floats, which round to nearest and thus result in a value of larger
213 //      magnitude than the integral value.
214 //      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
215 //                                   // is 4294967295u.
216 //   2. If the floating point value is equal to the promoted integral maximum
217 //      value, a range check will erroneously pass.
218 //      Example: (4294967296f <= 4294967295u) // This is true due to a precision
219 //                                            // loss in rounding up to float.
220 //   3. When the floating point value is then converted to an integral, the
221 //      resulting value is out of range for the target integral type and
222 //      thus is implementation defined.
223 //      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
224 // To fix this bug we manually truncate the maximum value when the destination
225 // type is an integral of larger precision than the source floating-point type,
226 // such that the resulting maximum is represented exactly as a floating point.
227 template <typename Dst, typename Src, template <typename> class Bounds>
228 struct NarrowingRange {
229   using SrcLimits = std::numeric_limits<Src>;
230   using DstLimits = typename std::numeric_limits<Dst>;
231 
232   // Computes the mask required to make an accurate comparison between types.
233   static const int kShift =
234       (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
235        SrcLimits::digits < DstLimits::digits)
236           ? (DstLimits::digits - SrcLimits::digits)
237           : 0;
238 
239   template <typename T>
240     requires(std::integral<T>)
241   // Masks out the integer bits that are beyond the precision of the
242   // intermediate type used for comparison.
243   static constexpr T Adjust(T value) {
244     static_assert(std::is_same_v<T, Dst>, "");
245     static_assert(kShift < DstLimits::digits, "");
246     using UnsignedDst = typename std::make_unsigned_t<T>;
247     return static_cast<T>(ConditionalNegate(
248         SafeUnsignedAbs(value) & ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),
249         IsValueNegative(value)));
250   }
251 
252   template <typename T>
253     requires(std::floating_point<T>)
254   static constexpr T Adjust(T value) {
255     static_assert(std::is_same_v<T, Dst>, "");
256     static_assert(kShift == 0, "");
257     return value;
258   }
259 
260   static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
261   static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
262 };
263 
264 template <typename Dst,
265           typename Src,
266           template <typename>
267           class Bounds,
268           IntegerRepresentation DstSign = std::is_signed_v<Dst>
269                                               ? INTEGER_REPRESENTATION_SIGNED
270                                               : INTEGER_REPRESENTATION_UNSIGNED,
271           IntegerRepresentation SrcSign = std::is_signed_v<Src>
272                                               ? INTEGER_REPRESENTATION_SIGNED
273                                               : INTEGER_REPRESENTATION_UNSIGNED,
274           NumericRangeRepresentation DstRange =
275               StaticDstRangeRelationToSrcRange<Dst, Src>::value>
276 struct DstRangeRelationToSrcRangeImpl;
277 
278 // The following templates are for ranges that must be verified at runtime. We
279 // split it into checks based on signedness to avoid confusing casts and
280 // compiler warnings on signed an unsigned comparisons.
281 
282 // Same sign narrowing: The range is contained for normal limits.
283 template <typename Dst,
284           typename Src,
285           template <typename>
286           class Bounds,
287           IntegerRepresentation DstSign,
288           IntegerRepresentation SrcSign>
289 struct DstRangeRelationToSrcRangeImpl<Dst,
290                                       Src,
291                                       Bounds,
292                                       DstSign,
293                                       SrcSign,
294                                       NUMERIC_RANGE_CONTAINED> {
295   static constexpr RangeCheck Check(Src value) {
296     using SrcLimits = std::numeric_limits<Src>;
297     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
298     return RangeCheck(
299         static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
300             static_cast<Dst>(value) >= DstLimits::lowest(),
301         static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
302             static_cast<Dst>(value) <= DstLimits::max());
303   }
304 };
305 
306 // Signed to signed narrowing: Both the upper and lower boundaries may be
307 // exceeded for standard limits.
308 template <typename Dst, typename Src, template <typename> class Bounds>
309 struct DstRangeRelationToSrcRangeImpl<Dst,
310                                       Src,
311                                       Bounds,
312                                       INTEGER_REPRESENTATION_SIGNED,
313                                       INTEGER_REPRESENTATION_SIGNED,
314                                       NUMERIC_RANGE_NOT_CONTAINED> {
315   static constexpr RangeCheck Check(Src value) {
316     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
317     return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
318   }
319 };
320 
321 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
322 // standard limits.
323 template <typename Dst, typename Src, template <typename> class Bounds>
324 struct DstRangeRelationToSrcRangeImpl<Dst,
325                                       Src,
326                                       Bounds,
327                                       INTEGER_REPRESENTATION_UNSIGNED,
328                                       INTEGER_REPRESENTATION_UNSIGNED,
329                                       NUMERIC_RANGE_NOT_CONTAINED> {
330   static constexpr RangeCheck Check(Src value) {
331     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
332     return RangeCheck(
333         DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
334         value <= DstLimits::max());
335   }
336 };
337 
338 // Unsigned to signed: Only the upper bound can be exceeded for standard limits.
339 template <typename Dst, typename Src, template <typename> class Bounds>
340 struct DstRangeRelationToSrcRangeImpl<Dst,
341                                       Src,
342                                       Bounds,
343                                       INTEGER_REPRESENTATION_SIGNED,
344                                       INTEGER_REPRESENTATION_UNSIGNED,
345                                       NUMERIC_RANGE_NOT_CONTAINED> {
346   static constexpr RangeCheck Check(Src value) {
347     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
348     using Promotion = decltype(Src() + Dst());
349     return RangeCheck(DstLimits::lowest() <= Dst(0) ||
350                           static_cast<Promotion>(value) >=
351                               static_cast<Promotion>(DstLimits::lowest()),
352                       static_cast<Promotion>(value) <=
353                           static_cast<Promotion>(DstLimits::max()));
354   }
355 };
356 
357 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
358 // and any negative value exceeds the lower boundary for standard limits.
359 template <typename Dst, typename Src, template <typename> class Bounds>
360 struct DstRangeRelationToSrcRangeImpl<Dst,
361                                       Src,
362                                       Bounds,
363                                       INTEGER_REPRESENTATION_UNSIGNED,
364                                       INTEGER_REPRESENTATION_SIGNED,
365                                       NUMERIC_RANGE_NOT_CONTAINED> {
366   static constexpr RangeCheck Check(Src value) {
367     using SrcLimits = std::numeric_limits<Src>;
368     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
369     using Promotion = decltype(Src() + Dst());
370     bool ge_zero = false;
371     // Converting floating-point to integer will discard fractional part, so
372     // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
373     if (std::is_floating_point_v<Src>) {
374       ge_zero = value > Src(-1);
375     } else {
376       ge_zero = value >= Src(0);
377     }
378     return RangeCheck(
379         ge_zero && (DstLimits::lowest() == 0 ||
380                     static_cast<Dst>(value) >= DstLimits::lowest()),
381         static_cast<Promotion>(SrcLimits::max()) <=
382                 static_cast<Promotion>(DstLimits::max()) ||
383             static_cast<Promotion>(value) <=
384                 static_cast<Promotion>(DstLimits::max()));
385   }
386 };
387 
388 // Simple wrapper for statically checking if a type's range is contained.
389 template <typename Dst, typename Src>
390 struct IsTypeInRangeForNumericType {
391   static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
392                             NUMERIC_RANGE_CONTAINED;
393 };
394 
395 template <typename Dst,
396           template <typename> class Bounds = std::numeric_limits,
397           typename Src>
398 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
399   static_assert(std::is_arithmetic_v<Src>, "Argument must be numeric.");
400   static_assert(std::is_arithmetic_v<Dst>, "Result must be numeric.");
401   static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
402   return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
403 }
404 
405 // Integer promotion templates used by the portable checked integer arithmetic.
406 template <size_t Size, bool IsSigned>
407 struct IntegerForDigitsAndSign;
408 
409 #define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
410   template <>                                                   \
411   struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
412                                  std::is_signed_v<I>> {         \
413     using type = I;                                             \
414   }
415 
416 INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
417 INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
418 INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
419 INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
420 INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
421 INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
422 INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
423 INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
424 #undef INTEGER_FOR_DIGITS_AND_SIGN
425 
426 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
427 // support 128-bit math, then the ArithmeticPromotion template below will need
428 // to be updated (or more likely replaced with a decltype expression).
429 static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
430               "Max integer size not supported for this toolchain.");
431 
432 template <typename Integer, bool IsSigned = std::is_signed_v<Integer>>
433 struct TwiceWiderInteger {
434   using type =
435       typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
436                                        IsSigned>::type;
437 };
438 
439 enum ArithmeticPromotionCategory {
440   LEFT_PROMOTION,  // Use the type of the left-hand argument.
441   RIGHT_PROMOTION  // Use the type of the right-hand argument.
442 };
443 
444 // Determines the type that can represent the largest positive value.
445 template <typename Lhs,
446           typename Rhs,
447           ArithmeticPromotionCategory Promotion =
448               (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
449                   ? LEFT_PROMOTION
450                   : RIGHT_PROMOTION>
451 struct MaxExponentPromotion;
452 
453 template <typename Lhs, typename Rhs>
454 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
455   using type = Lhs;
456 };
457 
458 template <typename Lhs, typename Rhs>
459 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
460   using type = Rhs;
461 };
462 
463 // Determines the type that can represent the lowest arithmetic value.
464 template <typename Lhs,
465           typename Rhs,
466           ArithmeticPromotionCategory Promotion =
467               std::is_signed_v<Lhs>
468                   ? (std::is_signed_v<Rhs>
469                          ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
470                                 ? LEFT_PROMOTION
471                                 : RIGHT_PROMOTION)
472                          : LEFT_PROMOTION)
473                   : (std::is_signed_v<Rhs>
474                          ? RIGHT_PROMOTION
475                          : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
476                                 ? LEFT_PROMOTION
477                                 : RIGHT_PROMOTION))>
478 struct LowestValuePromotion;
479 
480 template <typename Lhs, typename Rhs>
481 struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
482   using type = Lhs;
483 };
484 
485 template <typename Lhs, typename Rhs>
486 struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
487   using type = Rhs;
488 };
489 
490 // Determines the type that is best able to represent an arithmetic result.
491 template <
492     typename Lhs,
493     typename Rhs = Lhs,
494     bool is_intmax_type =
495         std::is_integral_v<typename MaxExponentPromotion<Lhs, Rhs>::type> &&
496         IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
497                 value == IntegerBitsPlusSign<intmax_t>::value,
498     bool is_max_exponent = StaticDstRangeRelationToSrcRange<
499                                typename MaxExponentPromotion<Lhs, Rhs>::type,
500                                Lhs>::value == NUMERIC_RANGE_CONTAINED &&
501                            StaticDstRangeRelationToSrcRange<
502                                typename MaxExponentPromotion<Lhs, Rhs>::type,
503                                Rhs>::value == NUMERIC_RANGE_CONTAINED>
504 struct BigEnoughPromotion;
505 
506 // The side with the max exponent is big enough.
507 template <typename Lhs, typename Rhs, bool is_intmax_type>
508 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
509   using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
510   static const bool is_contained = true;
511 };
512 
513 // We can use a twice wider type to fit.
514 template <typename Lhs, typename Rhs>
515 struct BigEnoughPromotion<Lhs, Rhs, false, false> {
516   using type =
517       typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
518                                  std::is_signed_v<Lhs> ||
519                                      std::is_signed_v<Rhs>>::type;
520   static const bool is_contained = true;
521 };
522 
523 // No type is large enough.
524 template <typename Lhs, typename Rhs>
525 struct BigEnoughPromotion<Lhs, Rhs, true, false> {
526   using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
527   static const bool is_contained = false;
528 };
529 
530 // We can statically check if operations on the provided types can wrap, so we
531 // can skip the checked operations if they're not needed. So, for an integer we
532 // care if the destination type preserves the sign and is twice the width of
533 // the source.
534 template <typename T, typename Lhs, typename Rhs = Lhs>
535 struct IsIntegerArithmeticSafe {
536   static const bool value =
537       !std::is_floating_point_v<T> && !std::is_floating_point_v<Lhs> &&
538       !std::is_floating_point_v<Rhs> &&
539       std::is_signed_v<T> >= std::is_signed_v<Lhs> &&
540       IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
541       std::is_signed_v<T> >= std::is_signed_v<Rhs> &&
542       IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
543 };
544 
545 // Promotes to a type that can represent any possible result of a binary
546 // arithmetic operation with the source types.
547 template <typename Lhs, typename Rhs>
548 struct FastIntegerArithmeticPromotion {
549   using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
550   static const bool is_contained = false;
551 };
552 
553 template <typename Lhs, typename Rhs>
554   requires(IsIntegerArithmeticSafe<
555            std::conditional_t<std::is_signed_v<Lhs> || std::is_signed_v<Rhs>,
556                               intmax_t,
557                               uintmax_t>,
558            typename MaxExponentPromotion<Lhs, Rhs>::type>::value)
559 struct FastIntegerArithmeticPromotion<Lhs, Rhs> {
560   using type =
561       typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
562                                  std::is_signed_v<Lhs> ||
563                                      std::is_signed_v<Rhs>>::type;
564   static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
565   static const bool is_contained = true;
566 };
567 
568 // Extracts the underlying type from an enum.
569 template <typename T>
570 struct ArithmeticOrUnderlyingEnum {
571   using type = T;
572   static const bool value = std::is_arithmetic_v<type>;
573 };
574 
575 template <typename T>
576   requires(std::is_enum_v<T>)
577 struct ArithmeticOrUnderlyingEnum<T> {
578   using type = typename std::underlying_type<T>::type;
579   static const bool value = std::is_arithmetic_v<type>;
580 };
581 
582 // The following are helper templates used in the CheckedNumeric class.
583 template <typename T>
584 class CheckedNumeric;
585 
586 template <typename T>
587 class ClampedNumeric;
588 
589 template <typename T>
590 class StrictNumeric;
591 
592 // Used to treat CheckedNumeric and arithmetic underlying types the same.
593 template <typename T>
594 struct UnderlyingType {
595   using type = typename ArithmeticOrUnderlyingEnum<T>::type;
596   static const bool is_numeric = std::is_arithmetic_v<type>;
597   static const bool is_checked = false;
598   static const bool is_clamped = false;
599   static const bool is_strict = false;
600 };
601 
602 template <typename T>
603 struct UnderlyingType<CheckedNumeric<T>> {
604   using type = T;
605   static const bool is_numeric = true;
606   static const bool is_checked = true;
607   static const bool is_clamped = false;
608   static const bool is_strict = false;
609 };
610 
611 template <typename T>
612 struct UnderlyingType<ClampedNumeric<T>> {
613   using type = T;
614   static const bool is_numeric = true;
615   static const bool is_checked = false;
616   static const bool is_clamped = true;
617   static const bool is_strict = false;
618 };
619 
620 template <typename T>
621 struct UnderlyingType<StrictNumeric<T>> {
622   using type = T;
623   static const bool is_numeric = true;
624   static const bool is_checked = false;
625   static const bool is_clamped = false;
626   static const bool is_strict = true;
627 };
628 
629 template <typename L, typename R>
630 struct IsCheckedOp {
631   static const bool value =
632       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
633       (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
634 };
635 
636 template <typename L, typename R>
637 struct IsClampedOp {
638   static const bool value =
639       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
640       (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
641       !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
642 };
643 
644 template <typename L, typename R>
645 struct IsStrictOp {
646   static const bool value =
647       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
648       (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
649       !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
650       !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
651 };
652 
653 // as_signed<> returns the supplied integral value (or integral castable
654 // Numeric template) cast as a signed integral of equivalent precision.
655 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
656 template <typename Src>
657 constexpr typename std::make_signed<
658     typename base::internal::UnderlyingType<Src>::type>::type
659 as_signed(const Src value) {
660   static_assert(std::is_integral_v<decltype(as_signed(value))>,
661                 "Argument must be a signed or unsigned integer type.");
662   return static_cast<decltype(as_signed(value))>(value);
663 }
664 
665 // as_unsigned<> returns the supplied integral value (or integral castable
666 // Numeric template) cast as an unsigned integral of equivalent precision.
667 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
668 template <typename Src>
669 constexpr typename std::make_unsigned<
670     typename base::internal::UnderlyingType<Src>::type>::type
671 as_unsigned(const Src value) {
672   static_assert(std::is_integral_v<decltype(as_unsigned(value))>,
673                 "Argument must be a signed or unsigned integer type.");
674   return static_cast<decltype(as_unsigned(value))>(value);
675 }
676 
677 template <typename L, typename R>
678 constexpr bool IsLessImpl(const L lhs,
679                           const R rhs,
680                           const RangeCheck l_range,
681                           const RangeCheck r_range) {
682   return l_range.IsUnderflow() || r_range.IsOverflow() ||
683          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
684                                     static_cast<decltype(lhs + rhs)>(rhs));
685 }
686 
687 template <typename L, typename R>
688 struct IsLess {
689   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
690                 "Types must be numeric.");
691   static constexpr bool Test(const L lhs, const R rhs) {
692     return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
693                       DstRangeRelationToSrcRange<L>(rhs));
694   }
695 };
696 
697 template <typename L, typename R>
698 constexpr bool IsLessOrEqualImpl(const L lhs,
699                                  const R rhs,
700                                  const RangeCheck l_range,
701                                  const RangeCheck r_range) {
702   return l_range.IsUnderflow() || r_range.IsOverflow() ||
703          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
704                                     static_cast<decltype(lhs + rhs)>(rhs));
705 }
706 
707 template <typename L, typename R>
708 struct IsLessOrEqual {
709   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
710                 "Types must be numeric.");
711   static constexpr bool Test(const L lhs, const R rhs) {
712     return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
713                              DstRangeRelationToSrcRange<L>(rhs));
714   }
715 };
716 
717 template <typename L, typename R>
718 constexpr bool IsGreaterImpl(const L lhs,
719                              const R rhs,
720                              const RangeCheck l_range,
721                              const RangeCheck r_range) {
722   return l_range.IsOverflow() || r_range.IsUnderflow() ||
723          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
724                                     static_cast<decltype(lhs + rhs)>(rhs));
725 }
726 
727 template <typename L, typename R>
728 struct IsGreater {
729   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
730                 "Types must be numeric.");
731   static constexpr bool Test(const L lhs, const R rhs) {
732     return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
733                          DstRangeRelationToSrcRange<L>(rhs));
734   }
735 };
736 
737 template <typename L, typename R>
738 constexpr bool IsGreaterOrEqualImpl(const L lhs,
739                                     const R rhs,
740                                     const RangeCheck l_range,
741                                     const RangeCheck r_range) {
742   return l_range.IsOverflow() || r_range.IsUnderflow() ||
743          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
744                                     static_cast<decltype(lhs + rhs)>(rhs));
745 }
746 
747 template <typename L, typename R>
748 struct IsGreaterOrEqual {
749   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
750                 "Types must be numeric.");
751   static constexpr bool Test(const L lhs, const R rhs) {
752     return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
753                                 DstRangeRelationToSrcRange<L>(rhs));
754   }
755 };
756 
757 template <typename L, typename R>
758 struct IsEqual {
759   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
760                 "Types must be numeric.");
761   static constexpr bool Test(const L lhs, const R rhs) {
762     return DstRangeRelationToSrcRange<R>(lhs) ==
763                DstRangeRelationToSrcRange<L>(rhs) &&
764            static_cast<decltype(lhs + rhs)>(lhs) ==
765                static_cast<decltype(lhs + rhs)>(rhs);
766   }
767 };
768 
769 template <typename L, typename R>
770 struct IsNotEqual {
771   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
772                 "Types must be numeric.");
773   static constexpr bool Test(const L lhs, const R rhs) {
774     return DstRangeRelationToSrcRange<R>(lhs) !=
775                DstRangeRelationToSrcRange<L>(rhs) ||
776            static_cast<decltype(lhs + rhs)>(lhs) !=
777                static_cast<decltype(lhs + rhs)>(rhs);
778   }
779 };
780 
781 // These perform the actual math operations on the CheckedNumerics.
782 // Binary arithmetic operations.
783 template <template <typename, typename> class C, typename L, typename R>
784 constexpr bool SafeCompare(const L lhs, const R rhs) {
785   static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>,
786                 "Types must be numeric.");
787   using Promotion = BigEnoughPromotion<L, R>;
788   using BigType = typename Promotion::type;
789   return Promotion::is_contained
790              // Force to a larger type for speed if both are contained.
791              ? C<BigType, BigType>::Test(
792                    static_cast<BigType>(static_cast<L>(lhs)),
793                    static_cast<BigType>(static_cast<R>(rhs)))
794              // Let the template functions figure it out for mixed types.
795              : C<L, R>::Test(lhs, rhs);
796 }
797 
798 template <typename Dst, typename Src>
799 constexpr bool IsMaxInRangeForNumericType() {
800   return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
801                                           std::numeric_limits<Src>::max());
802 }
803 
804 template <typename Dst, typename Src>
805 constexpr bool IsMinInRangeForNumericType() {
806   return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
807                                        std::numeric_limits<Src>::lowest());
808 }
809 
810 template <typename Dst, typename Src>
811 constexpr Dst CommonMax() {
812   return !IsMaxInRangeForNumericType<Dst, Src>()
813              ? Dst(std::numeric_limits<Dst>::max())
814              : Dst(std::numeric_limits<Src>::max());
815 }
816 
817 template <typename Dst, typename Src>
818 constexpr Dst CommonMin() {
819   return !IsMinInRangeForNumericType<Dst, Src>()
820              ? Dst(std::numeric_limits<Dst>::lowest())
821              : Dst(std::numeric_limits<Src>::lowest());
822 }
823 
824 // This is a wrapper to generate return the max or min for a supplied type.
825 // If the argument is false, the returned value is the maximum. If true the
826 // returned value is the minimum.
827 template <typename Dst, typename Src = Dst>
828 constexpr Dst CommonMaxOrMin(bool is_min) {
829   return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
830 }
831 
832 }  // namespace internal
833 }  // namespace base
834 
835 #endif  // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
836