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

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

Powered by Google App Engine
This is Rietveld 408576698