1 /* 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. 12 13 #ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ 14 #define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ 15 16 #include <limits> 17 18 namespace rtc { 19 namespace internal { 20 21 enum DstSign { DST_UNSIGNED, DST_SIGNED }; 22 23 enum SrcSign { SRC_UNSIGNED, SRC_SIGNED }; 24 25 enum DstRange { OVERLAPS_RANGE, CONTAINS_RANGE }; 26 27 // Helper templates to statically determine if our destination type can contain 28 // all values represented by the source type. 29 30 template <typename Dst, 31 typename Src, 32 DstSign IsDstSigned = 33 std::numeric_limits<Dst>::is_signed ? DST_SIGNED : DST_UNSIGNED, 34 SrcSign IsSrcSigned = 35 std::numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED> 36 struct StaticRangeCheck {}; 37 38 template <typename Dst, typename Src> 39 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> { 40 typedef std::numeric_limits<Dst> DstLimits; 41 typedef std::numeric_limits<Src> SrcLimits; 42 // Compare based on max_exponent, which we must compute for integrals. 43 static const size_t kDstMaxExponent = 44 DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); 45 static const size_t kSrcMaxExponent = 46 SrcLimits::is_iec559 ? SrcLimits::max_exponent : (sizeof(Src) * 8 - 1); 47 static const DstRange value = 48 kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; 49 }; 50 51 template <typename Dst, typename Src> 52 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> { 53 static const DstRange value = 54 sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE; 55 }; 56 57 template <typename Dst, typename Src> 58 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> { 59 typedef std::numeric_limits<Dst> DstLimits; 60 typedef std::numeric_limits<Src> SrcLimits; 61 // Compare based on max_exponent, which we must compute for integrals. 62 static const size_t kDstMaxExponent = 63 DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); 64 static const size_t kSrcMaxExponent = sizeof(Src) * 8; 65 static const DstRange value = 66 kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; 67 }; 68 69 template <typename Dst, typename Src> 70 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> { 71 static const DstRange value = OVERLAPS_RANGE; 72 }; 73 74 enum RangeCheckResult { 75 TYPE_VALID = 0, // Value can be represented by the destination type. 76 TYPE_UNDERFLOW = 1, // Value would overflow. 77 TYPE_OVERFLOW = 2, // Value would underflow. 78 TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). 79 }; 80 81 // This macro creates a RangeCheckResult from an upper and lower bound 82 // check by taking advantage of the fact that only NaN can be out of range in 83 // both directions at once. 84 #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ 85 RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ 86 ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) 87 88 template <typename Dst, 89 typename Src, 90 DstSign IsDstSigned = 91 std::numeric_limits<Dst>::is_signed ? DST_SIGNED : DST_UNSIGNED, 92 SrcSign IsSrcSigned = 93 std::numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED, 94 DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value> 95 struct RangeCheckImpl {}; 96 97 // The following templates are for ranges that must be verified at runtime. We 98 // split it into checks based on signedness to avoid confusing casts and 99 // compiler warnings on signed an unsigned comparisons. 100 101 // Dst range always contains the result: nothing to check. 102 template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned> 103 struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> { 104 static constexpr RangeCheckResult Check(Src value) { return TYPE_VALID; } 105 }; 106 107 // Signed to signed narrowing. 108 template <typename Dst, typename Src> 109 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> { 110 static constexpr RangeCheckResult Check(Src value) { 111 typedef std::numeric_limits<Dst> DstLimits; 112 return DstLimits::is_iec559 113 ? BASE_NUMERIC_RANGE_CHECK_RESULT( 114 value <= static_cast<Src>(DstLimits::max()), 115 value >= static_cast<Src>(DstLimits::max() * -1)) 116 : BASE_NUMERIC_RANGE_CHECK_RESULT( 117 value <= static_cast<Src>(DstLimits::max()), 118 value >= static_cast<Src>(DstLimits::min())); 119 } 120 }; 121 122 // Unsigned to unsigned narrowing. 123 template <typename Dst, typename Src> 124 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { 125 static constexpr RangeCheckResult Check(Src value) { 126 typedef std::numeric_limits<Dst> DstLimits; 127 return BASE_NUMERIC_RANGE_CHECK_RESULT( 128 value <= static_cast<Src>(DstLimits::max()), true); 129 } 130 }; 131 132 // Unsigned to signed. 133 template <typename Dst, typename Src> 134 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { 135 static constexpr RangeCheckResult Check(Src value) { 136 typedef std::numeric_limits<Dst> DstLimits; 137 return sizeof(Dst) > sizeof(Src) 138 ? TYPE_VALID 139 : BASE_NUMERIC_RANGE_CHECK_RESULT( 140 value <= static_cast<Src>(DstLimits::max()), true); 141 } 142 }; 143 144 // Signed to unsigned. 145 template <typename Dst, typename Src> 146 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> { 147 typedef std::numeric_limits<Dst> DstLimits; 148 typedef std::numeric_limits<Src> SrcLimits; 149 // Compare based on max_exponent, which we must compute for integrals. 150 static constexpr size_t DstMaxExponent() { return sizeof(Dst) * 8; } 151 static constexpr size_t SrcMaxExponent() { 152 return SrcLimits::is_iec559 ? SrcLimits::max_exponent 153 : (sizeof(Src) * 8 - 1); 154 } 155 static constexpr RangeCheckResult Check(Src value) { 156 return (DstMaxExponent() >= SrcMaxExponent()) 157 ? BASE_NUMERIC_RANGE_CHECK_RESULT(true, 158 value >= static_cast<Src>(0)) 159 : BASE_NUMERIC_RANGE_CHECK_RESULT( 160 value <= static_cast<Src>(DstLimits::max()), 161 value >= static_cast<Src>(0)); 162 } 163 }; 164 165 template <typename Dst, typename Src> 166 inline constexpr RangeCheckResult RangeCheck(Src value) { 167 static_assert(std::numeric_limits<Src>::is_specialized, 168 "argument must be numeric"); 169 static_assert(std::numeric_limits<Dst>::is_specialized, 170 "result must be numeric"); 171 return RangeCheckImpl<Dst, Src>::Check(value); 172 } 173 174 } // namespace internal 175 } // namespace rtc 176 177 #endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ 178