178 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  *  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_
 |