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

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

Powered by Google App Engine
This is Rietveld 408576698