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