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