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 18namespace rtc { 19namespace internal { 20 21enum DstSign { 22 DST_UNSIGNED, 23 DST_SIGNED 24}; 25 26enum SrcSign { 27 SRC_UNSIGNED, 28 SRC_SIGNED 29}; 30 31enum 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 39template <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> 44struct StaticRangeCheck {}; 45 46template <typename Dst, typename Src> 47struct 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 61template <typename Dst, typename Src> 62struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> { 63 static const DstRange value = sizeof(Dst) >= sizeof(Src) ? 64 CONTAINS_RANGE : OVERLAPS_RANGE; 65}; 66 67template <typename Dst, typename Src> 68struct 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 80template <typename Dst, typename Src> 81struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> { 82 static const DstRange value = OVERLAPS_RANGE; 83}; 84 85 86enum 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 100template <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> 107struct 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. 114template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned> 115struct 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. 122template <typename Dst, typename Src> 123struct 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. 137template <typename Dst, typename Src> 138struct 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. 147template <typename Dst, typename Src> 148struct 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. 158template <typename Dst, typename Src> 159struct 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 176template <typename Dst, typename Src> 177inline RangeCheckResult RangeCheck(Src value) { 178 COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, 179 argument_must_be_numeric); 180 COMPILE_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