Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: webrtc/base/numerics/safe_conversions_impl.h

Issue 1753293002: Safe numeric library: base/numerics (copied from Chromium) (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Rebase with master + correct new safe_conversions.h include Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webrtc/base/numerics/safe_conversions.h ('k') | webrtc/base/numerics/safe_math.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2016 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
12 // Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h.
13 // - Modified to work in WebRTC (paths, namespace, use of webrtc/base).
14 // Based on 'chromium_revision': 'ee311243eae6aef9c907543663754ff38f1f4f40'.
15
16 #ifndef WEBRTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
17 #define WEBRTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
18
19 #include <limits.h>
20 #include <stdint.h>
21
22 #include "webrtc/base/checks.h"
23
24 namespace rtc {
25 namespace internal {
26
27 // The std library doesn't provide a binary max_exponent for integers, however
28 // we can compute one by adding one to the number of non-sign bits. This allows
29 // for accurate range comparisons between floating point and integer types.
30 template <typename NumericType>
31 struct MaxExponent {
32 static const int value = std::numeric_limits<NumericType>::is_iec559
33 ? std::numeric_limits<NumericType>::max_exponent
34 : (sizeof(NumericType) * 8 + 1 -
35 std::numeric_limits<NumericType>::is_signed);
36 };
37
38 enum IntegerRepresentation {
39 INTEGER_REPRESENTATION_UNSIGNED,
40 INTEGER_REPRESENTATION_SIGNED
41 };
42
43 // A range for a given nunmeric Src type is contained for a given numeric Dst
44 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
45 // numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
46 // We implement this as template specializations rather than simple static
47 // comparisons to ensure type correctness in our comparisons.
48 enum NumericRangeRepresentation {
49 NUMERIC_RANGE_NOT_CONTAINED,
50 NUMERIC_RANGE_CONTAINED
51 };
52
53 // Helper templates to statically determine if our destination type can contain
54 // maximum and minimum values represented by the source type.
55
56 template <
57 typename Dst,
58 typename Src,
59 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
60 ? INTEGER_REPRESENTATION_SIGNED
61 : INTEGER_REPRESENTATION_UNSIGNED,
62 IntegerRepresentation SrcSign =
63 std::numeric_limits<Src>::is_signed
64 ? INTEGER_REPRESENTATION_SIGNED
65 : INTEGER_REPRESENTATION_UNSIGNED >
66 struct StaticDstRangeRelationToSrcRange;
67
68 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
69 // larger.
70 template <typename Dst, typename Src, IntegerRepresentation Sign>
71 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
72 static const NumericRangeRepresentation value =
73 MaxExponent<Dst>::value >= MaxExponent<Src>::value
74 ? NUMERIC_RANGE_CONTAINED
75 : NUMERIC_RANGE_NOT_CONTAINED;
76 };
77
78 // Unsigned to signed: Dst is guaranteed to contain source only if its range is
79 // larger.
80 template <typename Dst, typename Src>
81 struct StaticDstRangeRelationToSrcRange<Dst,
82 Src,
83 INTEGER_REPRESENTATION_SIGNED,
84 INTEGER_REPRESENTATION_UNSIGNED> {
85 static const NumericRangeRepresentation value =
86 MaxExponent<Dst>::value > MaxExponent<Src>::value
87 ? NUMERIC_RANGE_CONTAINED
88 : NUMERIC_RANGE_NOT_CONTAINED;
89 };
90
91 // Signed to unsigned: Dst cannot be statically determined to contain Src.
92 template <typename Dst, typename Src>
93 struct StaticDstRangeRelationToSrcRange<Dst,
94 Src,
95 INTEGER_REPRESENTATION_UNSIGNED,
96 INTEGER_REPRESENTATION_SIGNED> {
97 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
98 };
99
100 enum RangeConstraint {
101 RANGE_VALID = 0x0, // Value can be represented by the destination type.
102 RANGE_UNDERFLOW = 0x1, // Value would overflow.
103 RANGE_OVERFLOW = 0x2, // Value would underflow.
104 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
105 };
106
107 // Helper function for coercing an int back to a RangeContraint.
108 inline RangeConstraint GetRangeConstraint(int integer_range_constraint) {
109 RTC_DCHECK(integer_range_constraint >= RANGE_VALID &&
110 integer_range_constraint <= RANGE_INVALID);
111 return static_cast<RangeConstraint>(integer_range_constraint);
112 }
113
114 // This function creates a RangeConstraint from an upper and lower bound
115 // check by taking advantage of the fact that only NaN can be out of range in
116 // both directions at once.
117 inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound,
118 bool is_in_lower_bound) {
119 return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
120 (is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
121 }
122
123 // The following helper template addresses a corner case in range checks for
124 // conversion from a floating-point type to an integral type of smaller range
125 // but larger precision (e.g. float -> unsigned). The problem is as follows:
126 // 1. Integral maximum is always one less than a power of two, so it must be
127 // truncated to fit the mantissa of the floating point. The direction of
128 // rounding is implementation defined, but by default it's always IEEE
129 // floats, which round to nearest and thus result in a value of larger
130 // magnitude than the integral value.
131 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
132 // // is 4294967295u.
133 // 2. If the floating point value is equal to the promoted integral maximum
134 // value, a range check will erroneously pass.
135 // Example: (4294967296f <= 4294967295u) // This is true due to a precision
136 // // loss in rounding up to float.
137 // 3. When the floating point value is then converted to an integral, the
138 // resulting value is out of range for the target integral type and
139 // thus is implementation defined.
140 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
141 // To fix this bug we manually truncate the maximum value when the destination
142 // type is an integral of larger precision than the source floating-point type,
143 // such that the resulting maximum is represented exactly as a floating point.
144 template <typename Dst, typename Src>
145 struct NarrowingRange {
146 typedef typename std::numeric_limits<Src> SrcLimits;
147 typedef typename std::numeric_limits<Dst> DstLimits;
148
149 static Dst max() {
150 // The following logic avoids warnings where the max function is
151 // instantiated with invalid values for a bit shift (even though
152 // such a function can never be called).
153 static const int shift =
154 (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
155 SrcLimits::digits < DstLimits::digits && SrcLimits::is_iec559 &&
156 DstLimits::is_integer)
157 ? (DstLimits::digits - SrcLimits::digits)
158 : 0;
159
160 // We use UINTMAX_C below to avoid compiler warnings about shifting floating
161 // points. Since it's a compile time calculation, it shouldn't have any
162 // performance impact.
163 return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1);
164 }
165
166 static Dst min() {
167 return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max()
168 : DstLimits::min();
169 }
170 };
171
172 template <
173 typename Dst,
174 typename Src,
175 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
176 ? INTEGER_REPRESENTATION_SIGNED
177 : INTEGER_REPRESENTATION_UNSIGNED,
178 IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
179 ? INTEGER_REPRESENTATION_SIGNED
180 : INTEGER_REPRESENTATION_UNSIGNED,
181 NumericRangeRepresentation DstRange =
182 StaticDstRangeRelationToSrcRange<Dst, Src>::value >
183 struct DstRangeRelationToSrcRangeImpl;
184
185 // The following templates are for ranges that must be verified at runtime. We
186 // split it into checks based on signedness to avoid confusing casts and
187 // compiler warnings on signed an unsigned comparisons.
188
189 // Dst range is statically determined to contain Src: Nothing to check.
190 template <typename Dst,
191 typename Src,
192 IntegerRepresentation DstSign,
193 IntegerRepresentation SrcSign>
194 struct DstRangeRelationToSrcRangeImpl<Dst,
195 Src,
196 DstSign,
197 SrcSign,
198 NUMERIC_RANGE_CONTAINED> {
199 static RangeConstraint Check(Src value) { return RANGE_VALID; }
200 };
201
202 // Signed to signed narrowing: Both the upper and lower boundaries may be
203 // exceeded.
204 template <typename Dst, typename Src>
205 struct DstRangeRelationToSrcRangeImpl<Dst,
206 Src,
207 INTEGER_REPRESENTATION_SIGNED,
208 INTEGER_REPRESENTATION_SIGNED,
209 NUMERIC_RANGE_NOT_CONTAINED> {
210 static RangeConstraint Check(Src value) {
211 return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()),
212 (value >= NarrowingRange<Dst, Src>::min()));
213 }
214 };
215
216 // Unsigned to unsigned narrowing: Only the upper boundary can be exceeded.
217 template <typename Dst, typename Src>
218 struct DstRangeRelationToSrcRangeImpl<Dst,
219 Src,
220 INTEGER_REPRESENTATION_UNSIGNED,
221 INTEGER_REPRESENTATION_UNSIGNED,
222 NUMERIC_RANGE_NOT_CONTAINED> {
223 static RangeConstraint Check(Src value) {
224 return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true);
225 }
226 };
227
228 // Unsigned to signed: The upper boundary may be exceeded.
229 template <typename Dst, typename Src>
230 struct DstRangeRelationToSrcRangeImpl<Dst,
231 Src,
232 INTEGER_REPRESENTATION_SIGNED,
233 INTEGER_REPRESENTATION_UNSIGNED,
234 NUMERIC_RANGE_NOT_CONTAINED> {
235 static RangeConstraint Check(Src value) {
236 return sizeof(Dst) > sizeof(Src)
237 ? RANGE_VALID
238 : GetRangeConstraint(
239 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
240 true);
241 }
242 };
243
244 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
245 // and any negative value exceeds the lower boundary.
246 template <typename Dst, typename Src>
247 struct DstRangeRelationToSrcRangeImpl<Dst,
248 Src,
249 INTEGER_REPRESENTATION_UNSIGNED,
250 INTEGER_REPRESENTATION_SIGNED,
251 NUMERIC_RANGE_NOT_CONTAINED> {
252 static RangeConstraint Check(Src value) {
253 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
254 ? GetRangeConstraint(true, value >= static_cast<Src>(0))
255 : GetRangeConstraint(
256 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
257 value >= static_cast<Src>(0));
258 }
259 };
260
261 template <typename Dst, typename Src>
262 inline RangeConstraint DstRangeRelationToSrcRange(Src value) {
263 static_assert(std::numeric_limits<Src>::is_specialized,
264 "Argument must be numeric.");
265 static_assert(std::numeric_limits<Dst>::is_specialized,
266 "Result must be numeric.");
267 return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
268 }
269
270 } // namespace internal
271 } // namespace rtc
272
273 #endif // WEBRTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
OLDNEW
« no previous file with comments | « webrtc/base/numerics/safe_conversions.h ('k') | webrtc/base/numerics/safe_math.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698