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

Side by Side Diff: webrtc/base/numerics/safe_math_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: Comment about chromium_revision 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_math_impl.h.
13 // - Modified to work in WebRTC (paths, #ifndef, namespace, webrtc/base/checks,
14 // compiler warnings and cpplint.py, etc).
15 // Based on 'chromium_revision': 'ee311243eae6aef9c907543663754ff38f1f4f40'.
16
17 #ifndef WEBRTC_BASE_NUMERICS_SAFE_MATH_IMPL_H_
18 #define WEBRTC_BASE_NUMERICS_SAFE_MATH_IMPL_H_
19
20 #include <stddef.h>
21 #include <stdint.h>
22
23 #include <cmath>
24 #include <cstdlib>
25 #include <limits>
26 #include <type_traits>
27
28 #include "webrtc/base/checks.h"
29 #include "webrtc/base/numerics/safe_conversions.h"
30
31 namespace rtc {
32 namespace internal {
33
34 // Everything from here up to the floating point operations is portable C++,
35 // but it may not be fast. This code could be split based on
36 // platform/architecture and replaced with potentially faster implementations.
37
38 // Integer promotion templates used by the portable checked integer arithmetic.
39 template <size_t Size, bool IsSigned>
40 struct IntegerForSizeAndSign;
41 template <>
42 struct IntegerForSizeAndSign<1, true> {
43 typedef int8_t type;
44 };
45 template <>
46 struct IntegerForSizeAndSign<1, false> {
47 typedef uint8_t type;
48 };
49 template <>
50 struct IntegerForSizeAndSign<2, true> {
51 typedef int16_t type;
52 };
53 template <>
54 struct IntegerForSizeAndSign<2, false> {
55 typedef uint16_t type;
56 };
57 template <>
58 struct IntegerForSizeAndSign<4, true> {
59 typedef int32_t type;
60 };
61 template <>
62 struct IntegerForSizeAndSign<4, false> {
63 typedef uint32_t type;
64 };
65 template <>
66 struct IntegerForSizeAndSign<8, true> {
67 typedef int64_t type;
68 };
69 template <>
70 struct IntegerForSizeAndSign<8, false> {
71 typedef uint64_t type;
72 };
73
74 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
75 // support 128-bit math, then the ArithmeticPromotion template below will need
76 // to be updated (or more likely replaced with a decltype expression).
77
78 template <typename Integer>
79 struct UnsignedIntegerForSize {
80 typedef typename std::enable_if<
81 std::numeric_limits<Integer>::is_integer,
82 typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
83 };
84
85 template <typename Integer>
86 struct SignedIntegerForSize {
87 typedef typename std::enable_if<
88 std::numeric_limits<Integer>::is_integer,
89 typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
90 };
91
92 template <typename Integer>
93 struct TwiceWiderInteger {
94 typedef typename std::enable_if<
95 std::numeric_limits<Integer>::is_integer,
96 typename IntegerForSizeAndSign<
97 sizeof(Integer) * 2,
98 std::numeric_limits<Integer>::is_signed>::type>::type type;
99 };
100
101 template <typename Integer>
102 struct PositionOfSignBit {
103 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer,
104 size_t>::type value =
105 8 * sizeof(Integer) - 1;
106 };
107
108 // This is used for UnsignedAbs, where we need to support floating-point
109 // template instantiations even though we don't actually support the operations.
110 // However, there is no corresponding implementation of e.g. CheckedUnsignedAbs,
111 // so the float versions will not compile.
112 template <typename Numeric,
113 bool IsInteger = std::numeric_limits<Numeric>::is_integer,
114 bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
115 struct UnsignedOrFloatForSize;
116
117 template <typename Numeric>
118 struct UnsignedOrFloatForSize<Numeric, true, false> {
119 typedef typename UnsignedIntegerForSize<Numeric>::type type;
120 };
121
122 template <typename Numeric>
123 struct UnsignedOrFloatForSize<Numeric, false, true> {
124 typedef Numeric type;
125 };
126
127 // Helper templates for integer manipulations.
128
129 template <typename T>
130 bool HasSignBit(T x) {
131 // Cast to unsigned since right shift on signed is undefined.
132 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
133 PositionOfSignBit<T>::value);
134 }
135
136 // This wrapper undoes the standard integer promotions.
137 template <typename T>
138 T BinaryComplement(T x) {
139 return ~x;
140 }
141
142 // Here are the actual portable checked integer math implementations.
143 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
144 // way to coalesce things into the CheckedNumericState specializations below.
145
146 template <typename T>
147 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
148 CheckedAdd(T x, T y, RangeConstraint* validity) {
149 // Since the value of x+y is undefined if we have a signed type, we compute
150 // it using the unsigned type of the same size.
151 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
152 UnsignedDst ux = static_cast<UnsignedDst>(x);
153 UnsignedDst uy = static_cast<UnsignedDst>(y);
154 UnsignedDst uresult = ux + uy;
155 // Addition is valid if the sign of (x + y) is equal to either that of x or
156 // that of y.
157 if (std::numeric_limits<T>::is_signed) {
158 if (HasSignBit(BinaryComplement((uresult ^ ux) & (uresult ^ uy))))
159 *validity = RANGE_VALID;
160 else // Direction of wrap is inverse of result sign.
161 *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
162
163 } else { // Unsigned is either valid or overflow.
164 *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
165 }
166 return static_cast<T>(uresult);
167 }
168
169 template <typename T>
170 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
171 CheckedSub(T x, T y, RangeConstraint* validity) {
172 // Since the value of x+y is undefined if we have a signed type, we compute
173 // it using the unsigned type of the same size.
174 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
175 UnsignedDst ux = static_cast<UnsignedDst>(x);
176 UnsignedDst uy = static_cast<UnsignedDst>(y);
177 UnsignedDst uresult = ux - uy;
178 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
179 // the same sign.
180 if (std::numeric_limits<T>::is_signed) {
181 if (HasSignBit(BinaryComplement((uresult ^ ux) & (ux ^ uy))))
182 *validity = RANGE_VALID;
183 else // Direction of wrap is inverse of result sign.
184 *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
185
186 } else { // Unsigned is either valid or underflow.
187 *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
188 }
189 return static_cast<T>(uresult);
190 }
191
192 // Integer multiplication is a bit complicated. In the fast case we just
193 // we just promote to a twice wider type, and range check the result. In the
194 // slow case we need to manually check that the result won't be truncated by
195 // checking with division against the appropriate bound.
196 template <typename T>
197 typename std::enable_if<std::numeric_limits<T>::is_integer &&
198 sizeof(T) * 2 <= sizeof(uintmax_t),
199 T>::type
200 CheckedMul(T x, T y, RangeConstraint* validity) {
201 typedef typename TwiceWiderInteger<T>::type IntermediateType;
202 IntermediateType tmp =
203 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
204 *validity = DstRangeRelationToSrcRange<T>(tmp);
205 return static_cast<T>(tmp);
206 }
207
208 template <typename T>
209 typename std::enable_if<std::numeric_limits<T>::is_integer &&
210 std::numeric_limits<T>::is_signed &&
211 (sizeof(T) * 2 > sizeof(uintmax_t)),
212 T>::type
213 CheckedMul(T x, T y, RangeConstraint* validity) {
214 // If either side is zero then the result will be zero.
215 if (!x || !y) {
216 return RANGE_VALID;
217
218 } else if (x > 0) {
219 if (y > 0)
220 *validity =
221 x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
222 else
223 *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID
224 : RANGE_UNDERFLOW;
225
226 } else {
227 if (y > 0)
228 *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID
229 : RANGE_UNDERFLOW;
230 else
231 *validity =
232 y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
233 }
234
235 return x * y;
236 }
237
238 template <typename T>
239 typename std::enable_if<std::numeric_limits<T>::is_integer &&
240 !std::numeric_limits<T>::is_signed &&
241 (sizeof(T) * 2 > sizeof(uintmax_t)),
242 T>::type
243 CheckedMul(T x, T y, RangeConstraint* validity) {
244 *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y)
245 ? RANGE_VALID
246 : RANGE_OVERFLOW;
247 return x * y;
248 }
249
250 // Division just requires a check for an invalid negation on signed min/-1.
251 template <typename T>
252 T CheckedDiv(T x,
253 T y,
254 RangeConstraint* validity,
255 typename std::enable_if<std::numeric_limits<T>::is_integer,
256 int>::type = 0) {
257 if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
258 y == static_cast<T>(-1)) {
259 *validity = RANGE_OVERFLOW;
260 return std::numeric_limits<T>::min();
261 }
262
263 *validity = RANGE_VALID;
264 return x / y;
265 }
266
267 template <typename T>
268 typename std::enable_if<std::numeric_limits<T>::is_integer &&
269 std::numeric_limits<T>::is_signed,
270 T>::type
271 CheckedMod(T x, T y, RangeConstraint* validity) {
272 *validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
273 return x % y;
274 }
275
276 template <typename T>
277 typename std::enable_if<std::numeric_limits<T>::is_integer &&
278 !std::numeric_limits<T>::is_signed,
279 T>::type
280 CheckedMod(T x, T y, RangeConstraint* validity) {
281 *validity = RANGE_VALID;
282 return x % y;
283 }
284
285 template <typename T>
286 typename std::enable_if<std::numeric_limits<T>::is_integer &&
287 std::numeric_limits<T>::is_signed,
288 T>::type
289 CheckedNeg(T value, RangeConstraint* validity) {
290 *validity =
291 value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
292 // The negation of signed min is min, so catch that one.
293 return -value;
294 }
295
296 template <typename T>
297 typename std::enable_if<std::numeric_limits<T>::is_integer &&
298 !std::numeric_limits<T>::is_signed,
299 T>::type
300 CheckedNeg(T value, RangeConstraint* validity) {
301 // The only legal unsigned negation is zero.
302 *validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
303 return static_cast<T>(
304 -static_cast<typename SignedIntegerForSize<T>::type>(value));
305 }
306
307 template <typename T>
308 typename std::enable_if<std::numeric_limits<T>::is_integer &&
309 std::numeric_limits<T>::is_signed,
310 T>::type
311 CheckedAbs(T value, RangeConstraint* validity) {
312 *validity =
313 value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
314 return static_cast<T>(std::abs(value));
315 }
316
317 template <typename T>
318 typename std::enable_if<std::numeric_limits<T>::is_integer &&
319 !std::numeric_limits<T>::is_signed,
320 T>::type
321 CheckedAbs(T value, RangeConstraint* validity) {
322 // T is unsigned, so |value| must already be positive.
323 *validity = RANGE_VALID;
324 return value;
325 }
326
327 template <typename T>
328 typename std::enable_if<std::numeric_limits<T>::is_integer &&
329 std::numeric_limits<T>::is_signed,
330 typename UnsignedIntegerForSize<T>::type>::type
331 CheckedUnsignedAbs(T value) {
332 typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
333 return value == std::numeric_limits<T>::min()
334 ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
335 : static_cast<UnsignedT>(std::abs(value));
336 }
337
338 template <typename T>
339 typename std::enable_if<std::numeric_limits<T>::is_integer &&
340 !std::numeric_limits<T>::is_signed,
341 T>::type
342 CheckedUnsignedAbs(T value) {
343 // T is unsigned, so |value| must already be positive.
344 return value;
345 }
346
347 // These are the floating point stubs that the compiler needs to see. Only the
348 // negation operation is ever called.
349 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
350 template <typename T> \
351 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \
352 Checked##NAME(T, T, RangeConstraint*) { \
353 RTC_NOTREACHED(); \
354 return 0; \
355 }
356
357 BASE_FLOAT_ARITHMETIC_STUBS(Add)
358 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
359 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
360 BASE_FLOAT_ARITHMETIC_STUBS(Div)
361 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
362
363 #undef BASE_FLOAT_ARITHMETIC_STUBS
364
365 template <typename T>
366 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
367 T value,
368 RangeConstraint*) {
369 return -value;
370 }
371
372 template <typename T>
373 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
374 T value,
375 RangeConstraint*) {
376 return std::abs(value);
377 }
378
379 // Floats carry around their validity state with them, but integers do not. So,
380 // we wrap the underlying value in a specialization in order to hide that detail
381 // and expose an interface via accessors.
382 enum NumericRepresentation {
383 NUMERIC_INTEGER,
384 NUMERIC_FLOATING,
385 NUMERIC_UNKNOWN
386 };
387
388 template <typename NumericType>
389 struct GetNumericRepresentation {
390 static const NumericRepresentation value =
391 std::numeric_limits<NumericType>::is_integer
392 ? NUMERIC_INTEGER
393 : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING
394 : NUMERIC_UNKNOWN);
395 };
396
397 template <typename T, NumericRepresentation type =
398 GetNumericRepresentation<T>::value>
399 class CheckedNumericState {};
400
401 // Integrals require quite a bit of additional housekeeping to manage state.
402 template <typename T>
403 class CheckedNumericState<T, NUMERIC_INTEGER> {
404 private:
405 T value_;
406 RangeConstraint validity_;
407
408 public:
409 template <typename Src, NumericRepresentation type>
410 friend class CheckedNumericState;
411
412 CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
413
414 template <typename Src>
415 CheckedNumericState(Src value, RangeConstraint validity)
416 : value_(static_cast<T>(value)),
417 validity_(GetRangeConstraint(validity |
418 DstRangeRelationToSrcRange<T>(value))) {
419 static_assert(std::numeric_limits<Src>::is_specialized,
420 "Argument must be numeric.");
421 }
422
423 // Copy constructor.
424 template <typename Src>
425 CheckedNumericState(const CheckedNumericState<Src>& rhs)
426 : value_(static_cast<T>(rhs.value())),
427 validity_(GetRangeConstraint(
428 rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {}
429
430 template <typename Src>
431 explicit CheckedNumericState(
432 Src value,
433 typename std::enable_if<std::numeric_limits<Src>::is_specialized,
434 int>::type = 0)
435 : value_(static_cast<T>(value)),
436 validity_(DstRangeRelationToSrcRange<T>(value)) {}
437
438 RangeConstraint validity() const { return validity_; }
439 T value() const { return value_; }
440 };
441
442 // Floating points maintain their own validity, but need translation wrappers.
443 template <typename T>
444 class CheckedNumericState<T, NUMERIC_FLOATING> {
445 private:
446 T value_;
447
448 public:
449 template <typename Src, NumericRepresentation type>
450 friend class CheckedNumericState;
451
452 CheckedNumericState() : value_(0.0) {}
453
454 template <typename Src>
455 CheckedNumericState(
456 Src value,
457 RangeConstraint validity,
458 typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type =
459 0) {
460 switch (DstRangeRelationToSrcRange<T>(value)) {
461 case RANGE_VALID:
462 value_ = static_cast<T>(value);
463 break;
464
465 case RANGE_UNDERFLOW:
466 value_ = -std::numeric_limits<T>::infinity();
467 break;
468
469 case RANGE_OVERFLOW:
470 value_ = std::numeric_limits<T>::infinity();
471 break;
472
473 case RANGE_INVALID:
474 value_ = std::numeric_limits<T>::quiet_NaN();
475 break;
476
477 default:
478 RTC_NOTREACHED();
479 }
480 }
481
482 template <typename Src>
483 explicit CheckedNumericState(
484 Src value,
485 typename std::enable_if<std::numeric_limits<Src>::is_specialized,
486 int>::type = 0)
487 : value_(static_cast<T>(value)) {}
488
489 // Copy constructor.
490 template <typename Src>
491 CheckedNumericState(const CheckedNumericState<Src>& rhs)
492 : value_(static_cast<T>(rhs.value())) {}
493
494 RangeConstraint validity() const {
495 return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
496 value_ >= -std::numeric_limits<T>::max());
497 }
498 T value() const { return value_; }
499 };
500
501 // For integers less than 128-bit and floats 32-bit or larger, we can distil
502 // C/C++ arithmetic promotions down to two simple rules:
503 // 1. The type with the larger maximum exponent always takes precedence.
504 // 2. The resulting type must be promoted to at least an int.
505 // The following template specializations implement that promotion logic.
506 enum ArithmeticPromotionCategory {
507 LEFT_PROMOTION,
508 RIGHT_PROMOTION,
509 DEFAULT_PROMOTION
510 };
511
512 template <typename Lhs,
513 typename Rhs = Lhs,
514 ArithmeticPromotionCategory Promotion =
515 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
516 ? (MaxExponent<Lhs>::value > MaxExponent<int>::value
517 ? LEFT_PROMOTION
518 : DEFAULT_PROMOTION)
519 : (MaxExponent<Rhs>::value > MaxExponent<int>::value
520 ? RIGHT_PROMOTION
521 : DEFAULT_PROMOTION) >
522 struct ArithmeticPromotion;
523
524 template <typename Lhs, typename Rhs>
525 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
526 typedef Lhs type;
527 };
528
529 template <typename Lhs, typename Rhs>
530 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
531 typedef Rhs type;
532 };
533
534 template <typename Lhs, typename Rhs>
535 struct ArithmeticPromotion<Lhs, Rhs, DEFAULT_PROMOTION> {
536 typedef int type;
537 };
538
539 // We can statically check if operations on the provided types can wrap, so we
540 // can skip the checked operations if they're not needed. So, for an integer we
541 // care if the destination type preserves the sign and is twice the width of
542 // the source.
543 template <typename T, typename Lhs, typename Rhs>
544 struct IsIntegerArithmeticSafe {
545 static const bool value = !std::numeric_limits<T>::is_iec559 &&
546 StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
547 NUMERIC_RANGE_CONTAINED &&
548 sizeof(T) >= (2 * sizeof(Lhs)) &&
549 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
550 NUMERIC_RANGE_CONTAINED &&
551 sizeof(T) >= (2 * sizeof(Rhs));
552 };
553
554 } // namespace internal
555 } // namespace rtc
556
557 #endif // WEBRTC_BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698