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