OLD | NEW |
(Empty) | |
| 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_ |
OLD | NEW |