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

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

Powered by Google App Engine
This is Rietveld 408576698