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