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