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