Update to current webrtc library
This is from the upstream library commit id 3326535126e435f1ba647885ce43a8f0f3d317eb, corresponding to Chromium 88.0.4290.1.
This commit is contained in:
		
							
								
								
									
										176
									
								
								webrtc/rtc_base/numerics/safe_compare.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								webrtc/rtc_base/numerics/safe_compare.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| /* | ||||
|  *  Copyright 2016 The WebRTC Project Authors. All rights reserved. | ||||
|  * | ||||
|  *  Use of this source code is governed by a BSD-style license | ||||
|  *  that can be found in the LICENSE file in the root of the source | ||||
|  *  tree. An additional intellectual property rights grant can be found | ||||
|  *  in the file PATENTS.  All contributing project authors may | ||||
|  *  be found in the AUTHORS file in the root of the source tree. | ||||
|  */ | ||||
|  | ||||
| // This file defines six constexpr functions: | ||||
| // | ||||
| //   rtc::SafeEq  // == | ||||
| //   rtc::SafeNe  // != | ||||
| //   rtc::SafeLt  // < | ||||
| //   rtc::SafeLe  // <= | ||||
| //   rtc::SafeGt  // > | ||||
| //   rtc::SafeGe  // >= | ||||
| // | ||||
| // They each accept two arguments of arbitrary types, and in almost all cases, | ||||
| // they simply call the appropriate comparison operator. However, if both | ||||
| // arguments are integers, they don't compare them using C++'s quirky rules, | ||||
| // but instead adhere to the true mathematical definitions. It is as if the | ||||
| // arguments were first converted to infinite-range signed integers, and then | ||||
| // compared, although of course nothing expensive like that actually takes | ||||
| // place. In practice, for signed/signed and unsigned/unsigned comparisons and | ||||
| // some mixed-signed comparisons with a compile-time constant, the overhead is | ||||
| // zero; in the remaining cases, it is just a few machine instructions (no | ||||
| // branches). | ||||
|  | ||||
| #ifndef RTC_BASE_NUMERICS_SAFE_COMPARE_H_ | ||||
| #define RTC_BASE_NUMERICS_SAFE_COMPARE_H_ | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
|  | ||||
| #include "rtc_base/type_traits.h" | ||||
|  | ||||
| namespace rtc { | ||||
|  | ||||
| namespace safe_cmp_impl { | ||||
|  | ||||
| template <size_t N> | ||||
| struct LargerIntImpl : std::false_type {}; | ||||
| template <> | ||||
| struct LargerIntImpl<sizeof(int8_t)> : std::true_type { | ||||
|   using type = int16_t; | ||||
| }; | ||||
| template <> | ||||
| struct LargerIntImpl<sizeof(int16_t)> : std::true_type { | ||||
|   using type = int32_t; | ||||
| }; | ||||
| template <> | ||||
| struct LargerIntImpl<sizeof(int32_t)> : std::true_type { | ||||
|   using type = int64_t; | ||||
| }; | ||||
|  | ||||
| // LargerInt<T1, T2>::value is true iff there's a signed type that's larger | ||||
| // than T1 (and no larger than the larger of T2 and int*, for performance | ||||
| // reasons); and if there is such a type, LargerInt<T1, T2>::type is an alias | ||||
| // for it. | ||||
| template <typename T1, typename T2> | ||||
| struct LargerInt | ||||
|     : LargerIntImpl<sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int*) | ||||
|                         ? sizeof(T1) | ||||
|                         : 0> {}; | ||||
|  | ||||
| template <typename T> | ||||
| constexpr typename std::make_unsigned<T>::type MakeUnsigned(T a) { | ||||
|   return static_cast<typename std::make_unsigned<T>::type>(a); | ||||
| } | ||||
|  | ||||
| // Overload for when both T1 and T2 have the same signedness. | ||||
| template <typename Op, | ||||
|           typename T1, | ||||
|           typename T2, | ||||
|           typename std::enable_if<std::is_signed<T1>::value == | ||||
|                                   std::is_signed<T2>::value>::type* = nullptr> | ||||
| constexpr bool Cmp(T1 a, T2 b) { | ||||
|   return Op::Op(a, b); | ||||
| } | ||||
|  | ||||
| // Overload for signed - unsigned comparison that can be promoted to a bigger | ||||
| // signed type. | ||||
| template <typename Op, | ||||
|           typename T1, | ||||
|           typename T2, | ||||
|           typename std::enable_if<std::is_signed<T1>::value && | ||||
|                                   std::is_unsigned<T2>::value && | ||||
|                                   LargerInt<T2, T1>::value>::type* = nullptr> | ||||
| constexpr bool Cmp(T1 a, T2 b) { | ||||
|   return Op::Op(a, static_cast<typename LargerInt<T2, T1>::type>(b)); | ||||
| } | ||||
|  | ||||
| // Overload for unsigned - signed comparison that can be promoted to a bigger | ||||
| // signed type. | ||||
| template <typename Op, | ||||
|           typename T1, | ||||
|           typename T2, | ||||
|           typename std::enable_if<std::is_unsigned<T1>::value && | ||||
|                                   std::is_signed<T2>::value && | ||||
|                                   LargerInt<T1, T2>::value>::type* = nullptr> | ||||
| constexpr bool Cmp(T1 a, T2 b) { | ||||
|   return Op::Op(static_cast<typename LargerInt<T1, T2>::type>(a), b); | ||||
| } | ||||
|  | ||||
| // Overload for signed - unsigned comparison that can't be promoted to a bigger | ||||
| // signed type. | ||||
| template <typename Op, | ||||
|           typename T1, | ||||
|           typename T2, | ||||
|           typename std::enable_if<std::is_signed<T1>::value && | ||||
|                                   std::is_unsigned<T2>::value && | ||||
|                                   !LargerInt<T2, T1>::value>::type* = nullptr> | ||||
| constexpr bool Cmp(T1 a, T2 b) { | ||||
|   return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b); | ||||
| } | ||||
|  | ||||
| // Overload for unsigned - signed comparison that can't be promoted to a bigger | ||||
| // signed type. | ||||
| template <typename Op, | ||||
|           typename T1, | ||||
|           typename T2, | ||||
|           typename std::enable_if<std::is_unsigned<T1>::value && | ||||
|                                   std::is_signed<T2>::value && | ||||
|                                   !LargerInt<T1, T2>::value>::type* = nullptr> | ||||
| constexpr bool Cmp(T1 a, T2 b) { | ||||
|   return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b)); | ||||
| } | ||||
|  | ||||
| #define RTC_SAFECMP_MAKE_OP(name, op)      \ | ||||
|   struct name {                            \ | ||||
|     template <typename T1, typename T2>    \ | ||||
|     static constexpr bool Op(T1 a, T2 b) { \ | ||||
|       return a op b;                       \ | ||||
|     }                                      \ | ||||
|   }; | ||||
| RTC_SAFECMP_MAKE_OP(EqOp, ==) | ||||
| RTC_SAFECMP_MAKE_OP(NeOp, !=) | ||||
| RTC_SAFECMP_MAKE_OP(LtOp, <) | ||||
| RTC_SAFECMP_MAKE_OP(LeOp, <=) | ||||
| RTC_SAFECMP_MAKE_OP(GtOp, >) | ||||
| RTC_SAFECMP_MAKE_OP(GeOp, >=) | ||||
| #undef RTC_SAFECMP_MAKE_OP | ||||
|  | ||||
| }  // namespace safe_cmp_impl | ||||
|  | ||||
| #define RTC_SAFECMP_MAKE_FUN(name)                                            \ | ||||
|   template <typename T1, typename T2>                                         \ | ||||
|   constexpr                                                                   \ | ||||
|       typename std::enable_if<IsIntlike<T1>::value && IsIntlike<T2>::value,   \ | ||||
|                               bool>::type Safe##name(T1 a, T2 b) {            \ | ||||
|     /* Unary plus here turns enums into real integral types. */               \ | ||||
|     return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(+a, +b);               \ | ||||
|   }                                                                           \ | ||||
|   template <typename T1, typename T2>                                         \ | ||||
|   constexpr                                                                   \ | ||||
|       typename std::enable_if<!IsIntlike<T1>::value || !IsIntlike<T2>::value, \ | ||||
|                               bool>::type Safe##name(const T1& a,             \ | ||||
|                                                      const T2& b) {           \ | ||||
|     return safe_cmp_impl::name##Op::Op(a, b);                                 \ | ||||
|   } | ||||
| RTC_SAFECMP_MAKE_FUN(Eq) | ||||
| RTC_SAFECMP_MAKE_FUN(Ne) | ||||
| RTC_SAFECMP_MAKE_FUN(Lt) | ||||
| RTC_SAFECMP_MAKE_FUN(Le) | ||||
| RTC_SAFECMP_MAKE_FUN(Gt) | ||||
| RTC_SAFECMP_MAKE_FUN(Ge) | ||||
| #undef RTC_SAFECMP_MAKE_FUN | ||||
|  | ||||
| }  // namespace rtc | ||||
|  | ||||
| #endif  // RTC_BASE_NUMERICS_SAFE_COMPARE_H_ | ||||
							
								
								
									
										76
									
								
								webrtc/rtc_base/numerics/safe_conversions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								webrtc/rtc_base/numerics/safe_conversions.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| /* | ||||
|  *  Copyright 2014 The WebRTC Project Authors. All rights reserved. | ||||
|  * | ||||
|  *  Use of this source code is governed by a BSD-style license | ||||
|  *  that can be found in the LICENSE file in the root of the source | ||||
|  *  tree. An additional intellectual property rights grant can be found | ||||
|  *  in the file PATENTS.  All contributing project authors may | ||||
|  *  be found in the AUTHORS file in the root of the source tree. | ||||
|  */ | ||||
|  | ||||
| // Borrowed from Chromium's src/base/numerics/safe_conversions.h. | ||||
|  | ||||
| #ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ | ||||
| #define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ | ||||
|  | ||||
| #include <limits> | ||||
|  | ||||
| #include "rtc_base/checks.h" | ||||
| #include "rtc_base/numerics/safe_conversions_impl.h" | ||||
|  | ||||
| namespace rtc { | ||||
|  | ||||
| // Convenience function that returns true if the supplied value is in range | ||||
| // for the destination type. | ||||
| template <typename Dst, typename Src> | ||||
| inline constexpr bool IsValueInRangeForNumericType(Src value) { | ||||
|   return internal::RangeCheck<Dst>(value) == internal::TYPE_VALID; | ||||
| } | ||||
|  | ||||
| // checked_cast<> and dchecked_cast<> are analogous to static_cast<> for | ||||
| // numeric types, except that they [D]CHECK that the specified numeric | ||||
| // conversion will not overflow or underflow. NaN source will always trigger | ||||
| // the [D]CHECK. | ||||
| template <typename Dst, typename Src> | ||||
| inline constexpr Dst checked_cast(Src value) { | ||||
|   RTC_CHECK(IsValueInRangeForNumericType<Dst>(value)); | ||||
|   return static_cast<Dst>(value); | ||||
| } | ||||
| template <typename Dst, typename Src> | ||||
| inline constexpr Dst dchecked_cast(Src value) { | ||||
|   RTC_DCHECK(IsValueInRangeForNumericType<Dst>(value)); | ||||
|   return static_cast<Dst>(value); | ||||
| } | ||||
|  | ||||
| // saturated_cast<> is analogous to static_cast<> for numeric types, except | ||||
| // that the specified numeric conversion will saturate rather than overflow or | ||||
| // underflow. NaN assignment to an integral will trigger a RTC_CHECK condition. | ||||
| template <typename Dst, typename Src> | ||||
| inline constexpr Dst saturated_cast(Src value) { | ||||
|   // Optimization for floating point values, which already saturate. | ||||
|   if (std::numeric_limits<Dst>::is_iec559) | ||||
|     return static_cast<Dst>(value); | ||||
|  | ||||
|   switch (internal::RangeCheck<Dst>(value)) { | ||||
|     case internal::TYPE_VALID: | ||||
|       return static_cast<Dst>(value); | ||||
|  | ||||
|     case internal::TYPE_UNDERFLOW: | ||||
|       return std::numeric_limits<Dst>::min(); | ||||
|  | ||||
|     case internal::TYPE_OVERFLOW: | ||||
|       return std::numeric_limits<Dst>::max(); | ||||
|  | ||||
|     // Should fail only on attempting to assign NaN to a saturated integer. | ||||
|     case internal::TYPE_INVALID: | ||||
|       FATAL(); | ||||
|       return std::numeric_limits<Dst>::max(); | ||||
|   } | ||||
|  | ||||
|   FATAL(); | ||||
|   return static_cast<Dst>(value); | ||||
| } | ||||
|  | ||||
| }  // namespace rtc | ||||
|  | ||||
| #endif  // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ | ||||
							
								
								
									
										177
									
								
								webrtc/rtc_base/numerics/safe_conversions_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								webrtc/rtc_base/numerics/safe_conversions_impl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| /* | ||||
|  *  Copyright 2014 The WebRTC Project Authors. All rights reserved. | ||||
|  * | ||||
|  *  Use of this source code is governed by a BSD-style license | ||||
|  *  that can be found in the LICENSE file in the root of the source | ||||
|  *  tree. An additional intellectual property rights grant can be found | ||||
|  *  in the file PATENTS.  All contributing project authors may | ||||
|  *  be found in the AUTHORS file in the root of the source tree. | ||||
|  */ | ||||
|  | ||||
| // Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. | ||||
|  | ||||
| #ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | ||||
| #define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | ||||
|  | ||||
| #include <limits> | ||||
|  | ||||
| namespace rtc { | ||||
| namespace internal { | ||||
|  | ||||
| enum DstSign { DST_UNSIGNED, DST_SIGNED }; | ||||
|  | ||||
| enum SrcSign { SRC_UNSIGNED, SRC_SIGNED }; | ||||
|  | ||||
| enum DstRange { OVERLAPS_RANGE, CONTAINS_RANGE }; | ||||
|  | ||||
| // Helper templates to statically determine if our destination type can contain | ||||
| // all values represented by the source type. | ||||
|  | ||||
| template <typename Dst, | ||||
|           typename Src, | ||||
|           DstSign IsDstSigned = | ||||
|               std::numeric_limits<Dst>::is_signed ? DST_SIGNED : DST_UNSIGNED, | ||||
|           SrcSign IsSrcSigned = | ||||
|               std::numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED> | ||||
| struct StaticRangeCheck {}; | ||||
|  | ||||
| template <typename Dst, typename Src> | ||||
| struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> { | ||||
|   typedef std::numeric_limits<Dst> DstLimits; | ||||
|   typedef std::numeric_limits<Src> SrcLimits; | ||||
|   // Compare based on max_exponent, which we must compute for integrals. | ||||
|   static const size_t kDstMaxExponent = | ||||
|       DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); | ||||
|   static const size_t kSrcMaxExponent = | ||||
|       SrcLimits::is_iec559 ? SrcLimits::max_exponent : (sizeof(Src) * 8 - 1); | ||||
|   static const DstRange value = | ||||
|       kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; | ||||
| }; | ||||
|  | ||||
| template <typename Dst, typename Src> | ||||
| struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> { | ||||
|   static const DstRange value = | ||||
|       sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE; | ||||
| }; | ||||
|  | ||||
| template <typename Dst, typename Src> | ||||
| struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> { | ||||
|   typedef std::numeric_limits<Dst> DstLimits; | ||||
|   typedef std::numeric_limits<Src> SrcLimits; | ||||
|   // Compare based on max_exponent, which we must compute for integrals. | ||||
|   static const size_t kDstMaxExponent = | ||||
|       DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); | ||||
|   static const size_t kSrcMaxExponent = sizeof(Src) * 8; | ||||
|   static const DstRange value = | ||||
|       kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; | ||||
| }; | ||||
|  | ||||
| template <typename Dst, typename Src> | ||||
| struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> { | ||||
|   static const DstRange value = OVERLAPS_RANGE; | ||||
| }; | ||||
|  | ||||
| enum RangeCheckResult { | ||||
|   TYPE_VALID = 0,      // Value can be represented by the destination type. | ||||
|   TYPE_UNDERFLOW = 1,  // Value would overflow. | ||||
|   TYPE_OVERFLOW = 2,   // Value would underflow. | ||||
|   TYPE_INVALID = 3     // Source value is invalid (i.e. NaN). | ||||
| }; | ||||
|  | ||||
| // This macro creates a RangeCheckResult from an upper and lower bound | ||||
| // check by taking advantage of the fact that only NaN can be out of range in | ||||
| // both directions at once. | ||||
| #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ | ||||
|   RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) |                \ | ||||
|                    ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) | ||||
|  | ||||
| template <typename Dst, | ||||
|           typename Src, | ||||
|           DstSign IsDstSigned = | ||||
|               std::numeric_limits<Dst>::is_signed ? DST_SIGNED : DST_UNSIGNED, | ||||
|           SrcSign IsSrcSigned = | ||||
|               std::numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED, | ||||
|           DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value> | ||||
| struct RangeCheckImpl {}; | ||||
|  | ||||
| // The following templates are for ranges that must be verified at runtime. We | ||||
| // split it into checks based on signedness to avoid confusing casts and | ||||
| // compiler warnings on signed an unsigned comparisons. | ||||
|  | ||||
| // Dst range always contains the result: nothing to check. | ||||
| template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned> | ||||
| struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> { | ||||
|   static constexpr RangeCheckResult Check(Src value) { return TYPE_VALID; } | ||||
| }; | ||||
|  | ||||
| // Signed to signed narrowing. | ||||
| template <typename Dst, typename Src> | ||||
| struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> { | ||||
|   static constexpr RangeCheckResult Check(Src value) { | ||||
|     typedef std::numeric_limits<Dst> DstLimits; | ||||
|     return DstLimits::is_iec559 | ||||
|                ? BASE_NUMERIC_RANGE_CHECK_RESULT( | ||||
|                      value <= static_cast<Src>(DstLimits::max()), | ||||
|                      value >= static_cast<Src>(DstLimits::max() * -1)) | ||||
|                : BASE_NUMERIC_RANGE_CHECK_RESULT( | ||||
|                      value <= static_cast<Src>(DstLimits::max()), | ||||
|                      value >= static_cast<Src>(DstLimits::min())); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // Unsigned to unsigned narrowing. | ||||
| template <typename Dst, typename Src> | ||||
| struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { | ||||
|   static constexpr RangeCheckResult Check(Src value) { | ||||
|     typedef std::numeric_limits<Dst> DstLimits; | ||||
|     return BASE_NUMERIC_RANGE_CHECK_RESULT( | ||||
|         value <= static_cast<Src>(DstLimits::max()), true); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // Unsigned to signed. | ||||
| template <typename Dst, typename Src> | ||||
| struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { | ||||
|   static constexpr RangeCheckResult Check(Src value) { | ||||
|     typedef std::numeric_limits<Dst> DstLimits; | ||||
|     return sizeof(Dst) > sizeof(Src) | ||||
|                ? TYPE_VALID | ||||
|                : BASE_NUMERIC_RANGE_CHECK_RESULT( | ||||
|                      value <= static_cast<Src>(DstLimits::max()), true); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // Signed to unsigned. | ||||
| template <typename Dst, typename Src> | ||||
| struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> { | ||||
|   typedef std::numeric_limits<Dst> DstLimits; | ||||
|   typedef std::numeric_limits<Src> SrcLimits; | ||||
|   // Compare based on max_exponent, which we must compute for integrals. | ||||
|   static constexpr size_t DstMaxExponent() { return sizeof(Dst) * 8; } | ||||
|   static constexpr size_t SrcMaxExponent() { | ||||
|     return SrcLimits::is_iec559 ? SrcLimits::max_exponent | ||||
|                                 : (sizeof(Src) * 8 - 1); | ||||
|   } | ||||
|   static constexpr RangeCheckResult Check(Src value) { | ||||
|     return (DstMaxExponent() >= SrcMaxExponent()) | ||||
|                ? BASE_NUMERIC_RANGE_CHECK_RESULT(true, | ||||
|                                                  value >= static_cast<Src>(0)) | ||||
|                : BASE_NUMERIC_RANGE_CHECK_RESULT( | ||||
|                      value <= static_cast<Src>(DstLimits::max()), | ||||
|                      value >= static_cast<Src>(0)); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename Dst, typename Src> | ||||
| inline constexpr RangeCheckResult RangeCheck(Src value) { | ||||
|   static_assert(std::numeric_limits<Src>::is_specialized, | ||||
|                 "argument must be numeric"); | ||||
|   static_assert(std::numeric_limits<Dst>::is_specialized, | ||||
|                 "result must be numeric"); | ||||
|   return RangeCheckImpl<Dst, Src>::Check(value); | ||||
| } | ||||
|  | ||||
| }  // namespace internal | ||||
| }  // namespace rtc | ||||
|  | ||||
| #endif  // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | ||||
							
								
								
									
										335
									
								
								webrtc/rtc_base/numerics/safe_minmax.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								webrtc/rtc_base/numerics/safe_minmax.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,335 @@ | ||||
| /* | ||||
|  *  Copyright 2017 The WebRTC Project Authors. All rights reserved. | ||||
|  * | ||||
|  *  Use of this source code is governed by a BSD-style license | ||||
|  *  that can be found in the LICENSE file in the root of the source | ||||
|  *  tree. An additional intellectual property rights grant can be found | ||||
|  *  in the file PATENTS.  All contributing project authors may | ||||
|  *  be found in the AUTHORS file in the root of the source tree. | ||||
|  */ | ||||
|  | ||||
| // Minimum and maximum | ||||
| // =================== | ||||
| // | ||||
| //   rtc::SafeMin(x, y) | ||||
| //   rtc::SafeMax(x, y) | ||||
| // | ||||
| // (These are both constexpr.) | ||||
| // | ||||
| // Accept two arguments of either any two integral or any two floating-point | ||||
| // types, and return the smaller and larger value, respectively, with no | ||||
| // truncation or wrap-around. If only one of the input types is statically | ||||
| // guaranteed to be able to represent the result, the return type is that type; | ||||
| // if either one would do, the result type is the smaller type. (One of these | ||||
| // two cases always applies.) | ||||
| // | ||||
| //   * The case with one floating-point and one integral type is not allowed, | ||||
| //     because the floating-point type will have greater range, but may not | ||||
| //     have sufficient precision to represent the integer value exactly.) | ||||
| // | ||||
| // Clamp (a.k.a. constrain to a given interval) | ||||
| // ============================================ | ||||
| // | ||||
| //   rtc::SafeClamp(x, a, b) | ||||
| // | ||||
| // Accepts three arguments of any mix of integral types or any mix of | ||||
| // floating-point types, and returns the value in the closed interval [a, b] | ||||
| // that is closest to x (that is, if x < a it returns a; if x > b it returns b; | ||||
| // and if a <= x <= b it returns x). As for SafeMin() and SafeMax(), there is | ||||
| // no truncation or wrap-around. The result type | ||||
| // | ||||
| //   1. is statically guaranteed to be able to represent the result; | ||||
| // | ||||
| //   2. is no larger than the largest of the three argument types; and | ||||
| // | ||||
| //   3. has the same signedness as the type of the first argument, if this is | ||||
| //      possible without violating the First or Second Law. | ||||
| // | ||||
| // There is always at least one type that meets criteria 1 and 2. If more than | ||||
| // one type meets these criteria equally well, the result type is one of the | ||||
| // types that is smallest. Note that unlike SafeMin() and SafeMax(), | ||||
| // SafeClamp() will sometimes pick a return type that isn't the type of any of | ||||
| // its arguments. | ||||
| // | ||||
| //   * In this context, a type A is smaller than a type B if it has a smaller | ||||
| //     range; that is, if A::max() - A::min() < B::max() - B::min(). For | ||||
| //     example, int8_t < int16_t == uint16_t < int32_t, and all integral types | ||||
| //     are smaller than all floating-point types.) | ||||
| // | ||||
| //   * As for SafeMin and SafeMax, mixing integer and floating-point arguments | ||||
| //     is not allowed, because floating-point types have greater range than | ||||
| //     integer types, but do not have sufficient precision to represent the | ||||
| //     values of most integer types exactly. | ||||
| // | ||||
| // Requesting a specific return type | ||||
| // ================================= | ||||
| // | ||||
| // All three functions allow callers to explicitly specify the return type as a | ||||
| // template parameter, overriding the default return type. E.g. | ||||
| // | ||||
| //   rtc::SafeMin<int>(x, y)  // returns an int | ||||
| // | ||||
| // If the requested type is statically guaranteed to be able to represent the | ||||
| // result, then everything's fine, and the return type is as requested. But if | ||||
| // the requested type is too small, a static_assert is triggered. | ||||
|  | ||||
| #ifndef RTC_BASE_NUMERICS_SAFE_MINMAX_H_ | ||||
| #define RTC_BASE_NUMERICS_SAFE_MINMAX_H_ | ||||
|  | ||||
| #include <limits> | ||||
| #include <type_traits> | ||||
|  | ||||
| #include "rtc_base/checks.h" | ||||
| #include "rtc_base/numerics/safe_compare.h" | ||||
| #include "rtc_base/type_traits.h" | ||||
|  | ||||
| namespace rtc { | ||||
|  | ||||
| namespace safe_minmax_impl { | ||||
|  | ||||
| // Make the range of a type available via something other than a constexpr | ||||
| // function, to work around MSVC limitations. See | ||||
| // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ | ||||
| template <typename T> | ||||
| struct Limits { | ||||
|   static constexpr T lowest = std::numeric_limits<T>::lowest(); | ||||
|   static constexpr T max = std::numeric_limits<T>::max(); | ||||
| }; | ||||
|  | ||||
| template <typename T, bool is_enum = std::is_enum<T>::value> | ||||
| struct UnderlyingType; | ||||
|  | ||||
| template <typename T> | ||||
| struct UnderlyingType<T, false> { | ||||
|   using type = T; | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| struct UnderlyingType<T, true> { | ||||
|   using type = typename std::underlying_type<T>::type; | ||||
| }; | ||||
|  | ||||
| // Given two types T1 and T2, find types that can hold the smallest (in | ||||
| // ::min_t) and the largest (in ::max_t) of the two values. | ||||
| template <typename T1, | ||||
|           typename T2, | ||||
|           bool int1 = IsIntlike<T1>::value, | ||||
|           bool int2 = IsIntlike<T2>::value> | ||||
| struct MType { | ||||
|   static_assert(int1 == int2, | ||||
|                 "You may not mix integral and floating-point arguments"); | ||||
| }; | ||||
|  | ||||
| // Specialization for when neither type is integral (and therefore presumably | ||||
| // floating-point). | ||||
| template <typename T1, typename T2> | ||||
| struct MType<T1, T2, false, false> { | ||||
|   using min_t = typename std::common_type<T1, T2>::type; | ||||
|   static_assert(std::is_same<min_t, T1>::value || | ||||
|                     std::is_same<min_t, T2>::value, | ||||
|                 ""); | ||||
|  | ||||
|   using max_t = typename std::common_type<T1, T2>::type; | ||||
|   static_assert(std::is_same<max_t, T1>::value || | ||||
|                     std::is_same<max_t, T2>::value, | ||||
|                 ""); | ||||
| }; | ||||
|  | ||||
| // Specialization for when both types are integral. | ||||
| template <typename T1, typename T2> | ||||
| struct MType<T1, T2, true, true> { | ||||
|   // The type with the lowest minimum value. In case of a tie, the type with | ||||
|   // the lowest maximum value. In case that too is a tie, the types have the | ||||
|   // same range, and we arbitrarily pick T1. | ||||
|   using min_t = typename std::conditional< | ||||
|       SafeLt(Limits<T1>::lowest, Limits<T2>::lowest), | ||||
|       T1, | ||||
|       typename std::conditional< | ||||
|           SafeGt(Limits<T1>::lowest, Limits<T2>::lowest), | ||||
|           T2, | ||||
|           typename std::conditional<SafeLe(Limits<T1>::max, Limits<T2>::max), | ||||
|                                     T1, | ||||
|                                     T2>::type>::type>::type; | ||||
|   static_assert(std::is_same<min_t, T1>::value || | ||||
|                     std::is_same<min_t, T2>::value, | ||||
|                 ""); | ||||
|  | ||||
|   // The type with the highest maximum value. In case of a tie, the types have | ||||
|   // the same range (because in C++, integer types with the same maximum also | ||||
|   // have the same minimum). | ||||
|   static_assert(SafeNe(Limits<T1>::max, Limits<T2>::max) || | ||||
|                     SafeEq(Limits<T1>::lowest, Limits<T2>::lowest), | ||||
|                 "integer types with the same max should have the same min"); | ||||
|   using max_t = typename std:: | ||||
|       conditional<SafeGe(Limits<T1>::max, Limits<T2>::max), T1, T2>::type; | ||||
|   static_assert(std::is_same<max_t, T1>::value || | ||||
|                     std::is_same<max_t, T2>::value, | ||||
|                 ""); | ||||
| }; | ||||
|  | ||||
| // A dummy type that we pass around at compile time but never actually use. | ||||
| // Declared but not defined. | ||||
| struct DefaultType; | ||||
|  | ||||
| // ::type is A, except we fall back to B if A is DefaultType. We static_assert | ||||
| // that the chosen type can hold all values that B can hold. | ||||
| template <typename A, typename B> | ||||
| struct TypeOr { | ||||
|   using type = typename std:: | ||||
|       conditional<std::is_same<A, DefaultType>::value, B, A>::type; | ||||
|   static_assert(SafeLe(Limits<type>::lowest, Limits<B>::lowest) && | ||||
|                     SafeGe(Limits<type>::max, Limits<B>::max), | ||||
|                 "The specified type isn't large enough"); | ||||
|   static_assert(IsIntlike<type>::value == IsIntlike<B>::value && | ||||
|                     std::is_floating_point<type>::value == | ||||
|                         std::is_floating_point<type>::value, | ||||
|                 "float<->int conversions not allowed"); | ||||
| }; | ||||
|  | ||||
| }  // namespace safe_minmax_impl | ||||
|  | ||||
| template < | ||||
|     typename R = safe_minmax_impl::DefaultType, | ||||
|     typename T1 = safe_minmax_impl::DefaultType, | ||||
|     typename T2 = safe_minmax_impl::DefaultType, | ||||
|     typename R2 = typename safe_minmax_impl::TypeOr< | ||||
|         R, | ||||
|         typename safe_minmax_impl::MType< | ||||
|             typename safe_minmax_impl::UnderlyingType<T1>::type, | ||||
|             typename safe_minmax_impl::UnderlyingType<T2>::type>::min_t>::type> | ||||
| constexpr R2 SafeMin(T1 a, T2 b) { | ||||
|   static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value, | ||||
|                 "The first argument must be integral or floating-point"); | ||||
|   static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value, | ||||
|                 "The second argument must be integral or floating-point"); | ||||
|   return SafeLt(a, b) ? static_cast<R2>(a) : static_cast<R2>(b); | ||||
| } | ||||
|  | ||||
| template < | ||||
|     typename R = safe_minmax_impl::DefaultType, | ||||
|     typename T1 = safe_minmax_impl::DefaultType, | ||||
|     typename T2 = safe_minmax_impl::DefaultType, | ||||
|     typename R2 = typename safe_minmax_impl::TypeOr< | ||||
|         R, | ||||
|         typename safe_minmax_impl::MType< | ||||
|             typename safe_minmax_impl::UnderlyingType<T1>::type, | ||||
|             typename safe_minmax_impl::UnderlyingType<T2>::type>::max_t>::type> | ||||
| constexpr R2 SafeMax(T1 a, T2 b) { | ||||
|   static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value, | ||||
|                 "The first argument must be integral or floating-point"); | ||||
|   static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value, | ||||
|                 "The second argument must be integral or floating-point"); | ||||
|   return SafeGt(a, b) ? static_cast<R2>(a) : static_cast<R2>(b); | ||||
| } | ||||
|  | ||||
| namespace safe_minmax_impl { | ||||
|  | ||||
| // Given three types T, L, and H, let ::type be a suitable return value for | ||||
| // SafeClamp(T, L, H). See the docs at the top of this file for details. | ||||
| template <typename T, | ||||
|           typename L, | ||||
|           typename H, | ||||
|           bool int1 = IsIntlike<T>::value, | ||||
|           bool int2 = IsIntlike<L>::value, | ||||
|           bool int3 = IsIntlike<H>::value> | ||||
| struct ClampType { | ||||
|   static_assert(int1 == int2 && int1 == int3, | ||||
|                 "You may not mix integral and floating-point arguments"); | ||||
| }; | ||||
|  | ||||
| // Specialization for when all three types are floating-point. | ||||
| template <typename T, typename L, typename H> | ||||
| struct ClampType<T, L, H, false, false, false> { | ||||
|   using type = typename std::common_type<T, L, H>::type; | ||||
| }; | ||||
|  | ||||
| // Specialization for when all three types are integral. | ||||
| template <typename T, typename L, typename H> | ||||
| struct ClampType<T, L, H, true, true, true> { | ||||
|  private: | ||||
|   // Range of the return value. The return type must be able to represent this | ||||
|   // full range. | ||||
|   static constexpr auto r_min = | ||||
|       SafeMax(Limits<L>::lowest, SafeMin(Limits<H>::lowest, Limits<T>::lowest)); | ||||
|   static constexpr auto r_max = | ||||
|       SafeMin(Limits<H>::max, SafeMax(Limits<L>::max, Limits<T>::max)); | ||||
|  | ||||
|   // Is the given type an acceptable return type? (That is, can it represent | ||||
|   // all possible return values, and is it no larger than the largest of the | ||||
|   // input types?) | ||||
|   template <typename A> | ||||
|   struct AcceptableType { | ||||
|    private: | ||||
|     static constexpr bool not_too_large = sizeof(A) <= sizeof(L) || | ||||
|                                           sizeof(A) <= sizeof(H) || | ||||
|                                           sizeof(A) <= sizeof(T); | ||||
|     static constexpr bool range_contained = | ||||
|         SafeLe(Limits<A>::lowest, r_min) && SafeLe(r_max, Limits<A>::max); | ||||
|  | ||||
|    public: | ||||
|     static constexpr bool value = not_too_large && range_contained; | ||||
|   }; | ||||
|  | ||||
|   using best_signed_type = typename std::conditional< | ||||
|       AcceptableType<int8_t>::value, | ||||
|       int8_t, | ||||
|       typename std::conditional< | ||||
|           AcceptableType<int16_t>::value, | ||||
|           int16_t, | ||||
|           typename std::conditional<AcceptableType<int32_t>::value, | ||||
|                                     int32_t, | ||||
|                                     int64_t>::type>::type>::type; | ||||
|  | ||||
|   using best_unsigned_type = typename std::conditional< | ||||
|       AcceptableType<uint8_t>::value, | ||||
|       uint8_t, | ||||
|       typename std::conditional< | ||||
|           AcceptableType<uint16_t>::value, | ||||
|           uint16_t, | ||||
|           typename std::conditional<AcceptableType<uint32_t>::value, | ||||
|                                     uint32_t, | ||||
|                                     uint64_t>::type>::type>::type; | ||||
|  | ||||
|  public: | ||||
|   // Pick the best type, preferring the same signedness as T but falling back | ||||
|   // to the other one if necessary. | ||||
|   using type = typename std::conditional< | ||||
|       std::is_signed<T>::value, | ||||
|       typename std::conditional<AcceptableType<best_signed_type>::value, | ||||
|                                 best_signed_type, | ||||
|                                 best_unsigned_type>::type, | ||||
|       typename std::conditional<AcceptableType<best_unsigned_type>::value, | ||||
|                                 best_unsigned_type, | ||||
|                                 best_signed_type>::type>::type; | ||||
|   static_assert(AcceptableType<type>::value, ""); | ||||
| }; | ||||
|  | ||||
| }  // namespace safe_minmax_impl | ||||
|  | ||||
| template < | ||||
|     typename R = safe_minmax_impl::DefaultType, | ||||
|     typename T = safe_minmax_impl::DefaultType, | ||||
|     typename L = safe_minmax_impl::DefaultType, | ||||
|     typename H = safe_minmax_impl::DefaultType, | ||||
|     typename R2 = typename safe_minmax_impl::TypeOr< | ||||
|         R, | ||||
|         typename safe_minmax_impl::ClampType< | ||||
|             typename safe_minmax_impl::UnderlyingType<T>::type, | ||||
|             typename safe_minmax_impl::UnderlyingType<L>::type, | ||||
|             typename safe_minmax_impl::UnderlyingType<H>::type>::type>::type> | ||||
| R2 SafeClamp(T x, L min, H max) { | ||||
|   static_assert(IsIntlike<H>::value || std::is_floating_point<H>::value, | ||||
|                 "The first argument must be integral or floating-point"); | ||||
|   static_assert(IsIntlike<T>::value || std::is_floating_point<T>::value, | ||||
|                 "The second argument must be integral or floating-point"); | ||||
|   static_assert(IsIntlike<L>::value || std::is_floating_point<L>::value, | ||||
|                 "The third argument must be integral or floating-point"); | ||||
|   RTC_DCHECK_LE(min, max); | ||||
|   return SafeLe(x, min) | ||||
|              ? static_cast<R2>(min) | ||||
|              : SafeGe(x, max) ? static_cast<R2>(max) : static_cast<R2>(x); | ||||
| } | ||||
|  | ||||
| }  // namespace rtc | ||||
|  | ||||
| #endif  // RTC_BASE_NUMERICS_SAFE_MINMAX_H_ | ||||
		Reference in New Issue
	
	Block a user