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 |