OLD | NEW |
| (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_ | |
OLD | NEW |