Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
safe_conversions_impl.h
Go to the documentation of this file.
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 <cstdint>
9#include <limits>
10#include <type_traits>
11
12#if defined(__GNUC__) || defined(__clang__)
13#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
14#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
15#else
16#define BASE_NUMERICS_LIKELY(x) (x)
17#define BASE_NUMERICS_UNLIKELY(x) (x)
18#endif
19
20namespace base {
21namespace 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.
25template <typename NumericType>
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.
34template <typename NumericType>
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
42template <typename Integer>
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.
49template <typename T,
50 typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
51constexpr bool IsValueNegative(T value) {
52 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
53 return value < 0;
54}
55
56template <typename T,
57 typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
58constexpr 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).
66template <typename T>
67constexpr 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>(
74 (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
75}
76
77// This performs a safe, absolute value via unsigned overflow.
78template <typename T>
79constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
80 static_assert(std::is_integral<T>::value, "Type must be integral");
81 using UnsignedT = typename std::make_unsigned<T>::type;
83 ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
84 : static_cast<UnsignedT>(value);
85}
86
87// This allows us to switch paths on known compile-time constants.
88#if defined(__clang__) || defined(__GNUC__)
89constexpr bool CanDetectCompileTimeConstant() {
90 return true;
91}
92template <typename T>
93constexpr bool IsCompileTimeConstant(const T v) {
94 return __builtin_constant_p(v);
95}
96#else
98 return false;
99}
100template <typename T>
101constexpr bool IsCompileTimeConstant(const T) {
102 return false;
103}
104#endif
105template <typename T>
106constexpr bool MustTreatAsConstexpr(const T v) {
107 // Either we can't detect a compile-time constant, and must always use the
108 // constexpr path, or we know we have a compile-time constant.
110}
111
112// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
113// Also used in a constexpr template to trigger a compilation failure on
114// an error condition.
116 template <typename T>
117 static T HandleFailure() {
118#if defined(_MSC_VER)
119 __debugbreak();
120#elif defined(__GNUC__) || defined(__clang__)
121 __builtin_trap();
122#else
123 ((void)(*(volatile char*)0 = 0));
124#endif
125 return T();
126 }
127};
128
133
134// A range for a given nunmeric Src type is contained for a given numeric Dst
135// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
136// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
137// We implement this as template specializations rather than simple static
138// comparisons to ensure type correctness in our comparisons.
143
144// Helper templates to statically determine if our destination type can contain
145// maximum and minimum values represented by the source type.
146
147template <typename Dst,
148 typename Src,
149 IntegerRepresentation DstSign = std::is_signed<Dst>::value
152 IntegerRepresentation SrcSign = std::is_signed<Src>::value
156
157// Same sign: Dst is guaranteed to contain Src only if its range is equal or
158// larger.
159template <typename Dst, typename Src, IntegerRepresentation Sign>
166
167// Unsigned to signed: Dst is guaranteed to contain source only if its range is
168// larger.
169template <typename Dst, typename Src>
179
180// Signed to unsigned: Dst cannot be statically determined to contain Src.
181template <typename Dst, typename Src>
188
189// This class wraps the range constraints as separate booleans so the compiler
190// can identify constants and eliminate unused code paths.
192 public:
193 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
194 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
195 constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
196 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
197 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
198 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
199 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
200 constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
201 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
202 constexpr bool operator==(const RangeCheck rhs) const {
203 return is_underflow_ == rhs.is_underflow_ &&
204 is_overflow_ == rhs.is_overflow_;
205 }
206 constexpr bool operator!=(const RangeCheck rhs) const {
207 return !(*this == rhs);
208 }
209
210 private:
211 // Do not change the order of these member variables. The integral conversion
212 // optimization depends on this exact order.
213 const bool is_underflow_;
214 const bool is_overflow_;
215};
216
217// The following helper template addresses a corner case in range checks for
218// conversion from a floating-point type to an integral type of smaller range
219// but larger precision (e.g. float -> unsigned). The problem is as follows:
220// 1. Integral maximum is always one less than a power of two, so it must be
221// truncated to fit the mantissa of the floating point. The direction of
222// rounding is implementation defined, but by default it's always IEEE
223// floats, which round to nearest and thus result in a value of larger
224// magnitude than the integral value.
225// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
226// // is 4294967295u.
227// 2. If the floating point value is equal to the promoted integral maximum
228// value, a range check will erroneously pass.
229// Example: (4294967296f <= 4294967295u) // This is true due to a precision
230// // loss in rounding up to float.
231// 3. When the floating point value is then converted to an integral, the
232// resulting value is out of range for the target integral type and
233// thus is implementation defined.
234// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
235// To fix this bug we manually truncate the maximum value when the destination
236// type is an integral of larger precision than the source floating-point type,
237// such that the resulting maximum is represented exactly as a floating point.
238template <typename Dst, typename Src, template <typename> class Bounds>
240 using SrcLimits = std::numeric_limits<Src>;
241 using DstLimits = typename std::numeric_limits<Dst>;
242
243 // Computes the mask required to make an accurate comparison between types.
244 static const int kShift =
246 SrcLimits::digits < DstLimits::digits)
247 ? (DstLimits::digits - SrcLimits::digits)
248 : 0;
249 template <
250 typename T,
251 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
252
253 // Masks out the integer bits that are beyond the precision of the
254 // intermediate type used for comparison.
255 static constexpr T Adjust(T value) {
256 static_assert(std::is_same<T, Dst>::value, "");
257 static_assert(kShift < DstLimits::digits, "");
258 return static_cast<T>(
259 ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
261 }
262
263 template <typename T,
264 typename std::enable_if<std::is_floating_point<T>::value>::type* =
265 nullptr>
266 static constexpr T Adjust(T value) {
267 static_assert(std::is_same<T, Dst>::value, "");
268 static_assert(kShift == 0, "");
269 return value;
270 }
271
272 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
273 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
274};
275
276template <typename Dst,
277 typename Src,
278 template <typename>
279 class Bounds,
280 IntegerRepresentation DstSign = std::is_signed<Dst>::value
283 IntegerRepresentation SrcSign = std::is_signed<Src>::value
287 StaticDstRangeRelationToSrcRange<Dst, Src>::value>
289
290// The following templates are for ranges that must be verified at runtime. We
291// split it into checks based on signedness to avoid confusing casts and
292// compiler warnings on signed an unsigned comparisons.
293
294// Same sign narrowing: The range is contained for normal limits.
295template <typename Dst,
296 typename Src,
297 template <typename>
298 class Bounds,
299 IntegerRepresentation DstSign,
300 IntegerRepresentation SrcSign>
302 Src,
303 Bounds,
304 DstSign,
305 SrcSign,
307 static constexpr RangeCheck Check(Src value) {
308 using SrcLimits = std::numeric_limits<Src>;
309 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
310 return RangeCheck(
311 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
312 static_cast<Dst>(value) >= DstLimits::lowest(),
313 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
314 static_cast<Dst>(value) <= DstLimits::max());
315 }
316};
317
318// Signed to signed narrowing: Both the upper and lower boundaries may be
319// exceeded for standard limits.
320template <typename Dst, typename Src, template <typename> class Bounds>
322 Src,
323 Bounds,
327 static constexpr RangeCheck Check(Src value) {
328 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
329 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
330 }
331};
332
333// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
334// standard limits.
335template <typename Dst, typename Src, template <typename> class Bounds>
337 Src,
338 Bounds,
342 static constexpr RangeCheck Check(Src value) {
343 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
344 return RangeCheck(
345 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
346 value <= DstLimits::max());
347 }
348};
349
350// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
351template <typename Dst, typename Src, template <typename> class Bounds>
353 Src,
354 Bounds,
358 static constexpr RangeCheck Check(Src value) {
359 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
360 using Promotion = decltype(Src() + Dst());
361 return RangeCheck(DstLimits::lowest() <= Dst(0) ||
362 static_cast<Promotion>(value) >=
363 static_cast<Promotion>(DstLimits::lowest()),
364 static_cast<Promotion>(value) <=
365 static_cast<Promotion>(DstLimits::max()));
366 }
367};
368
369// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
370// and any negative value exceeds the lower boundary for standard limits.
371template <typename Dst, typename Src, template <typename> class Bounds>
373 Src,
374 Bounds,
378 static constexpr RangeCheck Check(Src value) {
379 using SrcLimits = std::numeric_limits<Src>;
380 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
381 using Promotion = decltype(Src() + Dst());
382 return RangeCheck(
383 value >= Src(0) && (DstLimits::lowest() == 0 ||
384 static_cast<Dst>(value) >= DstLimits::lowest()),
385 static_cast<Promotion>(SrcLimits::max()) <=
386 static_cast<Promotion>(DstLimits::max()) ||
387 static_cast<Promotion>(value) <=
388 static_cast<Promotion>(DstLimits::max()));
389 }
390};
391
392// Simple wrapper for statically checking if a type's range is contained.
393template <typename Dst, typename Src>
398
399template <typename Dst,
400 template <typename> class Bounds = std::numeric_limits,
401 typename Src>
403 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
404 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
405 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
407}
408
409// Integer promotion templates used by the portable checked integer arithmetic.
410template <size_t Size, bool IsSigned>
412
413#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
414 template <> \
415 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
416 std::is_signed<I>::value> { \
417 using type = I; \
418 }
419
428#undef INTEGER_FOR_DIGITS_AND_SIGN
429
430// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
431// support 128-bit math, then the ArithmeticPromotion template below will need
432// to be updated (or more likely replaced with a decltype expression).
433static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
434 "Max integer size not supported for this toolchain.");
435
436template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
442
444 LEFT_PROMOTION, // Use the type of the left-hand argument.
445 RIGHT_PROMOTION // Use the type of the right-hand argument.
447
448// Determines the type that can represent the largest positive value.
449template <typename Lhs,
450 typename Rhs,
456
457template <typename Lhs, typename Rhs>
459 using type = Lhs;
460};
461
462template <typename Lhs, typename Rhs>
464 using type = Rhs;
465};
466
467// Determines the type that can represent the lowest arithmetic value.
468template <typename Lhs,
469 typename Rhs,
471 std::is_signed<Lhs>::value
472 ? (std::is_signed<Rhs>::value
477 : (std::is_signed<Rhs>::value
479 : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
481 : RIGHT_PROMOTION))>
483
484template <typename Lhs, typename Rhs>
486 using type = Lhs;
487};
488
489template <typename Lhs, typename Rhs>
491 using type = Rhs;
492};
493
494// Determines the type that is best able to represent an arithmetic result.
495template <
496 typename Lhs,
497 typename Rhs = Lhs,
498 bool is_intmax_type =
499 std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
502 bool is_max_exponent =
505 Lhs>::value ==
508 Rhs>::value == NUMERIC_RANGE_CONTAINED>
510
511// The side with the max exponent is big enough.
512template <typename Lhs, typename Rhs, bool is_intmax_type>
513struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
515 static const bool is_contained = true;
516};
517
518// We can use a twice wider type to fit.
519template <typename Lhs, typename Rhs>
520struct BigEnoughPromotion<Lhs, Rhs, false, false> {
521 using type =
523 std::is_signed<Lhs>::value ||
524 std::is_signed<Rhs>::value>::type;
525 static const bool is_contained = true;
526};
527
528// No type is large enough.
529template <typename Lhs, typename Rhs>
530struct BigEnoughPromotion<Lhs, Rhs, true, false> {
532 static const bool is_contained = false;
533};
534
535// We can statically check if operations on the provided types can wrap, so we
536// can skip the checked operations if they're not needed. So, for an integer we
537// care if the destination type preserves the sign and is twice the width of
538// the source.
539template <typename T, typename Lhs, typename Rhs = Lhs>
541 static const bool value =
542 !std::is_floating_point<T>::value &&
543 !std::is_floating_point<Lhs>::value &&
544 !std::is_floating_point<Rhs>::value &&
545 std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
547 std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
549};
550
551// Promotes to a type that can represent any possible result of a binary
552// arithmetic operation with the source types.
553template <typename Lhs,
554 typename Rhs,
555 bool is_promotion_possible = IsIntegerArithmeticSafe<
556 typename std::conditional<std::is_signed<Lhs>::value ||
557 std::is_signed<Rhs>::value,
558 intmax_t,
559 uintmax_t>::type,
562
563template <typename Lhs, typename Rhs>
565 using type =
567 std::is_signed<Lhs>::value ||
568 std::is_signed<Rhs>::value>::type;
570 static const bool is_contained = true;
571};
572
573template <typename Lhs, typename Rhs>
574struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
576 static const bool is_contained = false;
577};
578
579// Extracts the underlying type from an enum.
580template <typename T, bool is_enum = std::is_enum<T>::value>
582
583template <typename T>
585 using type = typename std::underlying_type<T>::type;
586 static const bool value = std::is_arithmetic<type>::value;
587};
588
589template <typename T>
591 using type = T;
592 static const bool value = std::is_arithmetic<type>::value;
593};
594
595// The following are helper templates used in the CheckedNumeric class.
596template <typename T>
597class CheckedNumeric;
598
599template <typename T>
600class ClampedNumeric;
601
602template <typename T>
603class StrictNumeric;
604
605// Used to treat CheckedNumeric and arithmetic underlying types the same.
606template <typename T>
609 static const bool is_numeric = std::is_arithmetic<type>::value;
610 static const bool is_checked = false;
611 static const bool is_clamped = false;
612 static const bool is_strict = false;
613};
614
615template <typename T>
617 using type = T;
618 static const bool is_numeric = true;
619 static const bool is_checked = true;
620 static const bool is_clamped = false;
621 static const bool is_strict = false;
622};
623
624template <typename T>
626 using type = T;
627 static const bool is_numeric = true;
628 static const bool is_checked = false;
629 static const bool is_clamped = true;
630 static const bool is_strict = false;
631};
632
633template <typename T>
635 using type = T;
636 static const bool is_numeric = true;
637 static const bool is_checked = false;
638 static const bool is_clamped = false;
639 static const bool is_strict = true;
640};
641
642template <typename L, typename R>
648
649template <typename L, typename R>
656
657template <typename L, typename R>
665
666// as_signed<> returns the supplied integral value (or integral castable
667// Numeric template) cast as a signed integral of equivalent precision.
668// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
669template <typename Src>
670constexpr typename std::make_signed<
672as_signed(const Src value) {
673 static_assert(std::is_integral<decltype(as_signed(value))>::value,
674 "Argument must be a signed or unsigned integer type.");
675 return static_cast<decltype(as_signed(value))>(value);
676}
677
678// as_unsigned<> returns the supplied integral value (or integral castable
679// Numeric template) cast as an unsigned integral of equivalent precision.
680// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
681template <typename Src>
682constexpr typename std::make_unsigned<
684as_unsigned(const Src value) {
685 static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
686 "Argument must be a signed or unsigned integer type.");
687 return static_cast<decltype(as_unsigned(value))>(value);
688}
689
690template <typename L, typename R>
691constexpr bool IsLessImpl(const L lhs,
692 const R rhs,
693 const RangeCheck l_range,
694 const RangeCheck r_range) {
695 return l_range.IsUnderflow() || r_range.IsOverflow() ||
696 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
697 static_cast<decltype(lhs + rhs)>(rhs));
698}
699
700template <typename L, typename R>
701struct IsLess {
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 IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
706 DstRangeRelationToSrcRange<L>(rhs));
707 }
708};
709
710template <typename L, typename R>
711constexpr bool IsLessOrEqualImpl(const L lhs,
712 const R rhs,
713 const RangeCheck l_range,
714 const RangeCheck r_range) {
715 return l_range.IsUnderflow() || r_range.IsOverflow() ||
716 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
717 static_cast<decltype(lhs + rhs)>(rhs));
718}
719
720template <typename L, typename R>
722 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
723 "Types must be numeric.");
724 static constexpr bool Test(const L lhs, const R rhs) {
725 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
726 DstRangeRelationToSrcRange<L>(rhs));
727 }
728};
729
730template <typename L, typename R>
731constexpr bool IsGreaterImpl(const L lhs,
732 const R rhs,
733 const RangeCheck l_range,
734 const RangeCheck r_range) {
735 return l_range.IsOverflow() || r_range.IsUnderflow() ||
736 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
737 static_cast<decltype(lhs + rhs)>(rhs));
738}
739
740template <typename L, typename R>
741struct IsGreater {
742 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
743 "Types must be numeric.");
744 static constexpr bool Test(const L lhs, const R rhs) {
745 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
746 DstRangeRelationToSrcRange<L>(rhs));
747 }
748};
749
750template <typename L, typename R>
751constexpr bool IsGreaterOrEqualImpl(const L lhs,
752 const R rhs,
753 const RangeCheck l_range,
754 const RangeCheck r_range) {
755 return l_range.IsOverflow() || r_range.IsUnderflow() ||
756 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
757 static_cast<decltype(lhs + rhs)>(rhs));
758}
759
760template <typename L, typename R>
762 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
763 "Types must be numeric.");
764 static constexpr bool Test(const L lhs, const R rhs) {
765 return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
766 DstRangeRelationToSrcRange<L>(rhs));
767 }
768};
769
770template <typename L, typename R>
771struct IsEqual {
772 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
773 "Types must be numeric.");
774 static constexpr bool Test(const L lhs, const R rhs) {
775 return DstRangeRelationToSrcRange<R>(lhs) ==
776 DstRangeRelationToSrcRange<L>(rhs) &&
777 static_cast<decltype(lhs + rhs)>(lhs) ==
778 static_cast<decltype(lhs + rhs)>(rhs);
779 }
780};
781
782template <typename L, typename R>
784 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
785 "Types must be numeric.");
786 static constexpr bool Test(const L lhs, const R rhs) {
787 return DstRangeRelationToSrcRange<R>(lhs) !=
788 DstRangeRelationToSrcRange<L>(rhs) ||
789 static_cast<decltype(lhs + rhs)>(lhs) !=
790 static_cast<decltype(lhs + rhs)>(rhs);
791 }
792};
793
794// These perform the actual math operations on the CheckedNumerics.
795// Binary arithmetic operations.
796template <template <typename, typename> class C, typename L, typename R>
797constexpr bool SafeCompare(const L lhs, const R rhs) {
798 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
799 "Types must be numeric.");
800 using Promotion = BigEnoughPromotion<L, R>;
801 using BigType = typename Promotion::type;
802 return Promotion::is_contained
803 // Force to a larger type for speed if both are contained.
805 static_cast<BigType>(static_cast<L>(lhs)),
806 static_cast<BigType>(static_cast<R>(rhs)))
807 // Let the template functions figure it out for mixed types.
808 : C<L, R>::Test(lhs, rhs);
809}
810
811template <typename Dst, typename Src>
813 return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
814 std::numeric_limits<Src>::max());
815}
816
817template <typename Dst, typename Src>
819 return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
820 std::numeric_limits<Src>::lowest());
821}
822
823template <typename Dst, typename Src>
824constexpr Dst CommonMax() {
825 return !IsMaxInRangeForNumericType<Dst, Src>()
826 ? Dst(std::numeric_limits<Dst>::max())
827 : Dst(std::numeric_limits<Src>::max());
828}
829
830template <typename Dst, typename Src>
831constexpr Dst CommonMin() {
832 return !IsMinInRangeForNumericType<Dst, Src>()
833 ? Dst(std::numeric_limits<Dst>::lowest())
834 : Dst(std::numeric_limits<Src>::lowest());
835}
836
837// This is a wrapper to generate return the max or min for a supplied type.
838// If the argument is false, the returned value is the maximum. If true the
839// returned value is the minimum.
840template <typename Dst, typename Src = Dst>
841constexpr Dst CommonMaxOrMin(bool is_min) {
842 return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
843}
844
845} // namespace internal
846} // namespace base
847
848#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
constexpr bool IsOverflow() const
constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
constexpr bool operator==(const RangeCheck rhs) const
constexpr bool IsInvalid() const
constexpr bool IsOverflowFlagSet() const
constexpr bool IsUnderflowFlagSet() const
constexpr bool IsUnderflow() const
constexpr bool operator!=(const RangeCheck rhs) const
#define C(TEST_CATEGORY)
Definition colrv1.cpp:247
uint8_t value
#define R(r)
double x
constexpr Dst CommonMin()
constexpr bool IsMinInRangeForNumericType()
constexpr bool IsLessOrEqualImpl(const L lhs, const R rhs, const RangeCheck l_range, const RangeCheck r_range)
constexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs, const RangeCheck l_range, const RangeCheck r_range)
constexpr bool IsValueNegative(T value)
constexpr std::make_unsigned< T >::type SafeUnsignedAbs(T value)
constexpr bool SafeCompare(const L lhs, const R rhs)
constexpr std::make_signed< typenamebase::internal::UnderlyingType< Src >::type >::type as_signed(const Src value)
constexpr std::make_unsigned< typenamebase::internal::UnderlyingType< Src >::type >::type as_unsigned(const Src value)
constexpr std::make_signed< T >::type ConditionalNegate(T x, bool is_negative)
constexpr bool IsCompileTimeConstant(const T)
constexpr bool IsMaxInRangeForNumericType()
constexpr Dst CommonMaxOrMin(bool is_min)
constexpr RangeCheck DstRangeRelationToSrcRange(Src value)
constexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range, const RangeCheck r_range)
constexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range, const RangeCheck r_range)
constexpr bool MustTreatAsConstexpr(const T v)
constexpr Dst CommonMax()
constexpr bool CanDetectCompileTimeConstant()
Definition ref_ptr.h:256
#define T
#define INTEGER_FOR_DIGITS_AND_SIGN(I)
typename TwiceWiderInteger< typename MaxExponentPromotion< Lhs, Rhs >::type, std::is_signed< Lhs >::value||std::is_signed< Rhs >::value >::type type
typename MaxExponentPromotion< Lhs, Rhs >::type type
typename TwiceWiderInteger< typename MaxExponentPromotion< Lhs, Rhs >::type, std::is_signed< Lhs >::value||std::is_signed< Rhs >::value >::type type
static constexpr bool Test(const L lhs, const R rhs)
static constexpr bool Test(const L lhs, const R rhs)
static constexpr bool Test(const L lhs, const R rhs)
static constexpr bool Test(const L lhs, const R rhs)
static constexpr bool Test(const L lhs, const R rhs)
static constexpr bool Test(const L lhs, const R rhs)
static constexpr T Adjust(T value)
std::numeric_limits< Src > SrcLimits
typename std::numeric_limits< Dst > DstLimits
typename IntegerForDigitsAndSign< IntegerBitsPlusSign< Integer >::value *2, IsSigned >::type type
typename ArithmeticOrUnderlyingEnum< T >::type type