Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* Provides checked integers, detecting integer overflow and divide-by-0. */
8 :
9 : #ifndef mozilla_CheckedInt_h
10 : #define mozilla_CheckedInt_h
11 :
12 : #include <stdint.h>
13 : #include "mozilla/Assertions.h"
14 : #include "mozilla/Attributes.h"
15 : #include "mozilla/IntegerTypeTraits.h"
16 :
17 : // Probe for builtin math overflow support. Disabled for 32-bit builds for now
18 : // since "gcc -m32" claims to support these but its implementation is buggy.
19 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82274
20 : #if defined(HAVE_64BIT_BUILD)
21 : #if defined(__has_builtin)
22 : #define MOZ_HAS_BUILTIN_OP_OVERFLOW (__has_builtin(__builtin_add_overflow))
23 : #elif defined(__GNUC__)
24 : // (clang also defines __GNUC__ but it supports __has_builtin since at least
25 : // v3.1 (released in 2012) so it won't get here.)
26 : #define MOZ_HAS_BUILTIN_OP_OVERFLOW (__GNUC__ >= 5)
27 : #else
28 : #define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
29 : #endif
30 : #else
31 : #define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
32 : #endif
33 :
34 : namespace mozilla {
35 :
36 : template<typename T> class CheckedInt;
37 :
38 : namespace detail {
39 :
40 : /*
41 : * Step 1: manually record supported types
42 : *
43 : * What's nontrivial here is that there are different families of integer
44 : * types: basic integer types and stdint types. It is merrily undefined which
45 : * types from one family may be just typedefs for a type from another family.
46 : *
47 : * For example, on GCC 4.6, aside from the basic integer types, the only other
48 : * type that isn't just a typedef for some of them, is int8_t.
49 : */
50 :
51 : struct UnsupportedType {};
52 :
53 : template<typename IntegerType>
54 : struct IsSupportedPass2
55 : {
56 : static const bool value = false;
57 : };
58 :
59 : template<typename IntegerType>
60 : struct IsSupported
61 : {
62 : static const bool value = IsSupportedPass2<IntegerType>::value;
63 : };
64 :
65 : template<>
66 : struct IsSupported<int8_t>
67 : { static const bool value = true; };
68 :
69 : template<>
70 : struct IsSupported<uint8_t>
71 : { static const bool value = true; };
72 :
73 : template<>
74 : struct IsSupported<int16_t>
75 : { static const bool value = true; };
76 :
77 : template<>
78 : struct IsSupported<uint16_t>
79 : { static const bool value = true; };
80 :
81 : template<>
82 : struct IsSupported<int32_t>
83 : { static const bool value = true; };
84 :
85 : template<>
86 : struct IsSupported<uint32_t>
87 : { static const bool value = true; };
88 :
89 : template<>
90 : struct IsSupported<int64_t>
91 : { static const bool value = true; };
92 :
93 : template<>
94 : struct IsSupported<uint64_t>
95 : { static const bool value = true; };
96 :
97 :
98 : template<>
99 : struct IsSupportedPass2<char>
100 : { static const bool value = true; };
101 :
102 : template<>
103 : struct IsSupportedPass2<signed char>
104 : { static const bool value = true; };
105 :
106 : template<>
107 : struct IsSupportedPass2<unsigned char>
108 : { static const bool value = true; };
109 :
110 : template<>
111 : struct IsSupportedPass2<short>
112 : { static const bool value = true; };
113 :
114 : template<>
115 : struct IsSupportedPass2<unsigned short>
116 : { static const bool value = true; };
117 :
118 : template<>
119 : struct IsSupportedPass2<int>
120 : { static const bool value = true; };
121 :
122 : template<>
123 : struct IsSupportedPass2<unsigned int>
124 : { static const bool value = true; };
125 :
126 : template<>
127 : struct IsSupportedPass2<long>
128 : { static const bool value = true; };
129 :
130 : template<>
131 : struct IsSupportedPass2<unsigned long>
132 : { static const bool value = true; };
133 :
134 : template<>
135 : struct IsSupportedPass2<long long>
136 : { static const bool value = true; };
137 :
138 : template<>
139 : struct IsSupportedPass2<unsigned long long>
140 : { static const bool value = true; };
141 :
142 : /*
143 : * Step 2: Implement the actual validity checks.
144 : *
145 : * Ideas taken from IntegerLib, code different.
146 : */
147 :
148 : template<typename IntegerType, size_t Size = sizeof(IntegerType)>
149 : struct TwiceBiggerType
150 : {
151 : typedef typename detail::StdintTypeForSizeAndSignedness<
152 : sizeof(IntegerType) * 2,
153 : IsSigned<IntegerType>::value
154 : >::Type Type;
155 : };
156 :
157 : template<typename IntegerType>
158 : struct TwiceBiggerType<IntegerType, 8>
159 : {
160 : typedef UnsupportedType Type;
161 : };
162 :
163 : template<typename T>
164 : inline bool
165 : HasSignBit(T aX)
166 : {
167 : // In C++, right bit shifts on negative values is undefined by the standard.
168 : // Notice that signed-to-unsigned conversions are always well-defined in the
169 : // standard, as the value congruent modulo 2**n as expected. By contrast,
170 : // unsigned-to-signed is only well-defined if the value is representable.
171 : return bool(typename MakeUnsigned<T>::Type(aX) >>
172 : PositionOfSignBit<T>::value);
173 : }
174 :
175 : // Bitwise ops may return a larger type, so it's good to use this inline
176 : // helper guaranteeing that the result is really of type T.
177 : template<typename T>
178 : inline T
179 : BinaryComplement(T aX)
180 : {
181 0 : return ~aX;
182 : }
183 :
184 : template<typename T,
185 : typename U,
186 : bool IsTSigned = IsSigned<T>::value,
187 : bool IsUSigned = IsSigned<U>::value>
188 : struct DoesRangeContainRange
189 : {
190 : };
191 :
192 : template<typename T, typename U, bool Signedness>
193 : struct DoesRangeContainRange<T, U, Signedness, Signedness>
194 : {
195 : static const bool value = sizeof(T) >= sizeof(U);
196 : };
197 :
198 : template<typename T, typename U>
199 : struct DoesRangeContainRange<T, U, true, false>
200 : {
201 : static const bool value = sizeof(T) > sizeof(U);
202 : };
203 :
204 : template<typename T, typename U>
205 : struct DoesRangeContainRange<T, U, false, true>
206 : {
207 : static const bool value = false;
208 : };
209 :
210 : template<typename T,
211 : typename U,
212 : bool IsTSigned = IsSigned<T>::value,
213 : bool IsUSigned = IsSigned<U>::value,
214 : bool DoesTRangeContainURange = DoesRangeContainRange<T, U>::value>
215 : struct IsInRangeImpl {};
216 :
217 : template<typename T, typename U, bool IsTSigned, bool IsUSigned>
218 : struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true>
219 : {
220 : static bool constexpr run(U)
221 : {
222 : return true;
223 : }
224 : };
225 :
226 : template<typename T, typename U>
227 : struct IsInRangeImpl<T, U, true, true, false>
228 : {
229 : static bool constexpr run(U aX)
230 : {
231 0 : return aX <= MaxValue<T>::value && aX >= MinValue<T>::value;
232 : }
233 : };
234 :
235 : template<typename T, typename U>
236 : struct IsInRangeImpl<T, U, false, false, false>
237 : {
238 : static bool constexpr run(U aX)
239 : {
240 0 : return aX <= MaxValue<T>::value;
241 : }
242 : };
243 :
244 : template<typename T, typename U>
245 : struct IsInRangeImpl<T, U, true, false, false>
246 : {
247 : static bool constexpr run(U aX)
248 : {
249 0 : return sizeof(T) > sizeof(U) || aX <= U(MaxValue<T>::value);
250 : }
251 : };
252 :
253 : template<typename T, typename U>
254 : struct IsInRangeImpl<T, U, false, true, false>
255 : {
256 : static bool constexpr run(U aX)
257 : {
258 : return sizeof(T) >= sizeof(U)
259 : ? aX >= 0
260 0 : : aX >= 0 && aX <= U(MaxValue<T>::value);
261 : }
262 : };
263 :
264 : template<typename T, typename U>
265 : inline constexpr bool
266 : IsInRange(U aX)
267 : {
268 0 : return IsInRangeImpl<T, U>::run(aX);
269 : }
270 :
271 : template<typename T>
272 : inline bool
273 : IsAddValid(T aX, T aY)
274 : {
275 : #if MOZ_HAS_BUILTIN_OP_OVERFLOW
276 : T dummy;
277 : return !__builtin_add_overflow(aX, aY, &dummy);
278 : #else
279 : // Addition is valid if the sign of aX+aY is equal to either that of aX or
280 : // that of aY. Since the value of aX+aY is undefined if we have a signed
281 : // type, we compute it using the unsigned type of the same size. Beware!
282 : // These bitwise operations can return a larger integer type, if T was a
283 : // small type like int8_t, so we explicitly cast to T.
284 :
285 0 : typename MakeUnsigned<T>::Type ux = aX;
286 0 : typename MakeUnsigned<T>::Type uy = aY;
287 0 : typename MakeUnsigned<T>::Type result = ux + uy;
288 : return IsSigned<T>::value
289 : ? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY))))
290 0 : : BinaryComplement(aX) >= aY;
291 : #endif
292 : }
293 :
294 : template<typename T>
295 : inline bool
296 : IsSubValid(T aX, T aY)
297 : {
298 : #if MOZ_HAS_BUILTIN_OP_OVERFLOW
299 : T dummy;
300 : return !__builtin_sub_overflow(aX, aY, &dummy);
301 : #else
302 : // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
303 : // have same sign. Since the value of aX-aY is undefined if we have a signed
304 : // type, we compute it using the unsigned type of the same size.
305 : typename MakeUnsigned<T>::Type ux = aX;
306 : typename MakeUnsigned<T>::Type uy = aY;
307 : typename MakeUnsigned<T>::Type result = ux - uy;
308 :
309 : return IsSigned<T>::value
310 : ? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY))))
311 : : aX >= aY;
312 : #endif
313 : }
314 :
315 : template<typename T,
316 : bool IsTSigned = IsSigned<T>::value,
317 : bool TwiceBiggerTypeIsSupported =
318 : IsSupported<typename TwiceBiggerType<T>::Type>::value>
319 : struct IsMulValidImpl {};
320 :
321 : template<typename T, bool IsTSigned>
322 : struct IsMulValidImpl<T, IsTSigned, true>
323 : {
324 : static bool run(T aX, T aY)
325 : {
326 : typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
327 : TwiceBiggerType product = TwiceBiggerType(aX) * TwiceBiggerType(aY);
328 : return IsInRange<T>(product);
329 : }
330 : };
331 :
332 : template<typename T>
333 : struct IsMulValidImpl<T, true, false>
334 : {
335 : static bool run(T aX, T aY)
336 : {
337 : const T max = MaxValue<T>::value;
338 : const T min = MinValue<T>::value;
339 :
340 : if (aX == 0 || aY == 0) {
341 : return true;
342 : }
343 : if (aX > 0) {
344 : return aY > 0
345 : ? aX <= max / aY
346 : : aY >= min / aX;
347 : }
348 :
349 : // If we reach this point, we know that aX < 0.
350 : return aY > 0
351 : ? aX >= min / aY
352 : : aY >= max / aX;
353 : }
354 : };
355 :
356 : template<typename T>
357 : struct IsMulValidImpl<T, false, false>
358 : {
359 : static bool run(T aX, T aY)
360 : {
361 : return aY == 0 || aX <= MaxValue<T>::value / aY;
362 : }
363 : };
364 :
365 : template<typename T>
366 : inline bool
367 : IsMulValid(T aX, T aY)
368 : {
369 : #if MOZ_HAS_BUILTIN_OP_OVERFLOW
370 : T dummy;
371 : return !__builtin_mul_overflow(aX, aY, &dummy);
372 : #else
373 : return IsMulValidImpl<T>::run(aX, aY);
374 : #endif
375 : }
376 :
377 : template<typename T>
378 : inline bool
379 0 : IsDivValid(T aX, T aY)
380 : {
381 : // Keep in mind that in the signed case, min/-1 is invalid because
382 : // abs(min)>max.
383 0 : return aY != 0 &&
384 0 : !(IsSigned<T>::value && aX == MinValue<T>::value && aY == T(-1));
385 : }
386 :
387 : template<typename T, bool IsTSigned = IsSigned<T>::value>
388 : struct IsModValidImpl;
389 :
390 : template<typename T>
391 : inline bool
392 : IsModValid(T aX, T aY)
393 : {
394 0 : return IsModValidImpl<T>::run(aX, aY);
395 : }
396 :
397 : /*
398 : * Mod is pretty simple.
399 : * For now, let's just use the ANSI C definition:
400 : * If aX or aY are negative, the results are implementation defined.
401 : * Consider these invalid.
402 : * Undefined for aY=0.
403 : * The result will never exceed either aX or aY.
404 : *
405 : * Checking that aX>=0 is a warning when T is unsigned.
406 : */
407 :
408 : template<typename T>
409 : struct IsModValidImpl<T, false>
410 : {
411 : static inline bool run(T aX, T aY)
412 : {
413 : return aY >= 1;
414 : }
415 : };
416 :
417 : template<typename T>
418 : struct IsModValidImpl<T, true>
419 : {
420 : static inline bool run(T aX, T aY)
421 : {
422 0 : if (aX < 0) {
423 : return false;
424 : }
425 0 : return aY >= 1;
426 : }
427 : };
428 :
429 : template<typename T, bool IsSigned = IsSigned<T>::value>
430 : struct NegateImpl;
431 :
432 : template<typename T>
433 : struct NegateImpl<T, false>
434 : {
435 : static CheckedInt<T> negate(const CheckedInt<T>& aVal)
436 : {
437 : // Handle negation separately for signed/unsigned, for simpler code and to
438 : // avoid an MSVC warning negating an unsigned value.
439 : return CheckedInt<T>(0, aVal.isValid() && aVal.mValue == 0);
440 : }
441 : };
442 :
443 : template<typename T>
444 : struct NegateImpl<T, true>
445 : {
446 0 : static CheckedInt<T> negate(const CheckedInt<T>& aVal)
447 : {
448 : // Watch out for the min-value, which (with twos-complement) can't be
449 : // negated as -min-value is then (max-value + 1).
450 0 : if (!aVal.isValid() || aVal.mValue == MinValue<T>::value) {
451 0 : return CheckedInt<T>(aVal.mValue, false);
452 : }
453 0 : return CheckedInt<T>(-aVal.mValue, true);
454 : }
455 : };
456 :
457 : } // namespace detail
458 :
459 :
460 : /*
461 : * Step 3: Now define the CheckedInt class.
462 : */
463 :
464 : /**
465 : * @class CheckedInt
466 : * @brief Integer wrapper class checking for integer overflow and other errors
467 : * @param T the integer type to wrap. Can be any type among the following:
468 : * - any basic integer type such as |int|
469 : * - any stdint type such as |int8_t|
470 : *
471 : * This class implements guarded integer arithmetic. Do a computation, check
472 : * that isValid() returns true, you then have a guarantee that no problem, such
473 : * as integer overflow, happened during this computation, and you can call
474 : * value() to get the plain integer value.
475 : *
476 : * The arithmetic operators in this class are guaranteed not to raise a signal
477 : * (e.g. in case of a division by zero).
478 : *
479 : * For example, suppose that you want to implement a function that computes
480 : * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
481 : * zero or integer overflow). You could code it as follows:
482 : @code
483 : bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
484 : {
485 : CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
486 : if (checkedResult.isValid()) {
487 : *aResult = checkedResult.value();
488 : return true;
489 : } else {
490 : return false;
491 : }
492 : }
493 : @endcode
494 : *
495 : * Implicit conversion from plain integers to checked integers is allowed. The
496 : * plain integer is checked to be in range before being casted to the
497 : * destination type. This means that the following lines all compile, and the
498 : * resulting CheckedInts are correctly detected as valid or invalid:
499 : * @code
500 : // 1 is of type int, is found to be in range for uint8_t, x is valid
501 : CheckedInt<uint8_t> x(1);
502 : // -1 is of type int, is found not to be in range for uint8_t, x is invalid
503 : CheckedInt<uint8_t> x(-1);
504 : // -1 is of type int, is found to be in range for int8_t, x is valid
505 : CheckedInt<int8_t> x(-1);
506 : // 1000 is of type int16_t, is found not to be in range for int8_t,
507 : // x is invalid
508 : CheckedInt<int8_t> x(int16_t(1000));
509 : // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
510 : // x is invalid
511 : CheckedInt<int32_t> x(uint32_t(3123456789));
512 : * @endcode
513 : * Implicit conversion from
514 : * checked integers to plain integers is not allowed. As shown in the
515 : * above example, to get the value of a checked integer as a normal integer,
516 : * call value().
517 : *
518 : * Arithmetic operations between checked and plain integers is allowed; the
519 : * result type is the type of the checked integer.
520 : *
521 : * Checked integers of different types cannot be used in the same arithmetic
522 : * expression.
523 : *
524 : * There are convenience typedefs for all stdint types, of the following form
525 : * (these are just 2 examples):
526 : @code
527 : typedef CheckedInt<int32_t> CheckedInt32;
528 : typedef CheckedInt<uint16_t> CheckedUint16;
529 : @endcode
530 : */
531 : template<typename T>
532 : class CheckedInt
533 : {
534 : protected:
535 : T mValue;
536 : bool mIsValid;
537 :
538 : template<typename U>
539 0 : CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid)
540 : {
541 : static_assert(detail::IsSupported<T>::value &&
542 : detail::IsSupported<U>::value,
543 : "This type is not supported by CheckedInt");
544 : }
545 :
546 : friend struct detail::NegateImpl<T>;
547 :
548 : public:
549 : /**
550 : * Constructs a checked integer with given @a value. The checked integer is
551 : * initialized as valid or invalid depending on whether the @a value
552 : * is in range.
553 : *
554 : * This constructor is not explicit. Instead, the type of its argument is a
555 : * separate template parameter, ensuring that no conversion is performed
556 : * before this constructor is actually called. As explained in the above
557 : * documentation for class CheckedInt, this constructor checks that its
558 : * argument is valid.
559 : */
560 : template<typename U>
561 0 : MOZ_IMPLICIT constexpr CheckedInt(U aValue) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
562 : : mValue(T(aValue)),
563 0 : mIsValid(detail::IsInRange<T>(aValue))
564 : {
565 : static_assert(detail::IsSupported<T>::value &&
566 : detail::IsSupported<U>::value,
567 : "This type is not supported by CheckedInt");
568 0 : }
569 :
570 : template<typename U>
571 : friend class CheckedInt;
572 :
573 : template<typename U>
574 : CheckedInt<U> toChecked() const
575 : {
576 : CheckedInt<U> ret(mValue);
577 : ret.mIsValid = ret.mIsValid && mIsValid;
578 : return ret;
579 : }
580 :
581 : /** Constructs a valid checked integer with initial value 0 */
582 0 : constexpr CheckedInt() : mValue(0), mIsValid(true)
583 : {
584 : static_assert(detail::IsSupported<T>::value,
585 : "This type is not supported by CheckedInt");
586 0 : }
587 :
588 : /** @returns the actual value */
589 0 : T value() const
590 : {
591 0 : MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)");
592 0 : return mValue;
593 : }
594 :
595 : /**
596 : * @returns true if the checked integer is valid, i.e. is not the result
597 : * of an invalid operation or of an operation involving an invalid checked
598 : * integer
599 : */
600 : bool isValid() const
601 : {
602 : return mIsValid;
603 : }
604 :
605 : template<typename U>
606 : friend CheckedInt<U> operator +(const CheckedInt<U>& aLhs,
607 : const CheckedInt<U>& aRhs);
608 : template<typename U>
609 : CheckedInt& operator +=(U aRhs);
610 : CheckedInt& operator +=(const CheckedInt<T>& aRhs);
611 :
612 : template<typename U>
613 : friend CheckedInt<U> operator -(const CheckedInt<U>& aLhs,
614 : const CheckedInt<U>& aRhs);
615 : template<typename U>
616 : CheckedInt& operator -=(U aRhs);
617 : CheckedInt& operator -=(const CheckedInt<T>& aRhs);
618 :
619 : template<typename U>
620 : friend CheckedInt<U> operator *(const CheckedInt<U>& aLhs,
621 : const CheckedInt<U>& aRhs);
622 : template<typename U>
623 : CheckedInt& operator *=(U aRhs);
624 : CheckedInt& operator *=(const CheckedInt<T>& aRhs);
625 :
626 : template<typename U>
627 : friend CheckedInt<U> operator /(const CheckedInt<U>& aLhs,
628 : const CheckedInt<U>& aRhs);
629 : template<typename U>
630 : CheckedInt& operator /=(U aRhs);
631 : CheckedInt& operator /=(const CheckedInt<T>& aRhs);
632 :
633 : template<typename U>
634 : friend CheckedInt<U> operator %(const CheckedInt<U>& aLhs,
635 : const CheckedInt<U>& aRhs);
636 : template<typename U>
637 : CheckedInt& operator %=(U aRhs);
638 : CheckedInt& operator %=(const CheckedInt<T>& aRhs);
639 :
640 : CheckedInt operator -() const
641 : {
642 0 : return detail::NegateImpl<T>::negate(*this);
643 : }
644 :
645 : /**
646 : * @returns true if the left and right hand sides are valid
647 : * and have the same value.
648 : *
649 : * Note that these semantics are the reason why we don't offer
650 : * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
651 : * but that would mean that whenever a or b is invalid, a!=b
652 : * is always true, which would be very confusing.
653 : *
654 : * For similar reasons, operators <, >, <=, >= would be very tricky to
655 : * specify, so we just avoid offering them.
656 : *
657 : * Notice that these == semantics are made more reasonable by these facts:
658 : * 1. a==b implies equality at the raw data level
659 : * (the converse is false, as a==b is never true among invalids)
660 : * 2. This is similar to the behavior of IEEE floats, where a==b
661 : * means that a and b have the same value *and* neither is NaN.
662 : */
663 0 : bool operator ==(const CheckedInt& aOther) const
664 : {
665 0 : return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
666 : }
667 :
668 : /** prefix ++ */
669 0 : CheckedInt& operator++()
670 : {
671 0 : *this += 1;
672 0 : return *this;
673 : }
674 :
675 : /** postfix ++ */
676 0 : CheckedInt operator++(int)
677 : {
678 0 : CheckedInt tmp = *this;
679 0 : *this += 1;
680 0 : return tmp;
681 : }
682 :
683 : /** prefix -- */
684 : CheckedInt& operator--()
685 : {
686 : *this -= 1;
687 : return *this;
688 : }
689 :
690 : /** postfix -- */
691 : CheckedInt operator--(int)
692 : {
693 : CheckedInt tmp = *this;
694 : *this -= 1;
695 : return tmp;
696 : }
697 :
698 : private:
699 : /**
700 : * The !=, <, <=, >, >= operators are disabled:
701 : * see the comment on operator==.
702 : */
703 : template<typename U> bool operator !=(U aOther) const = delete;
704 : template<typename U> bool operator < (U aOther) const = delete;
705 : template<typename U> bool operator <=(U aOther) const = delete;
706 : template<typename U> bool operator > (U aOther) const = delete;
707 : template<typename U> bool operator >=(U aOther) const = delete;
708 : };
709 :
710 : #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
711 : template<typename T> \
712 : inline CheckedInt<T> \
713 : operator OP(const CheckedInt<T>& aLhs, const CheckedInt<T>& aRhs) \
714 : { \
715 : if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
716 : return CheckedInt<T>(0, false); \
717 : } \
718 : return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
719 : aLhs.mIsValid && aRhs.mIsValid); \
720 : }
721 :
722 : #if MOZ_HAS_BUILTIN_OP_OVERFLOW
723 : #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN) \
724 : template<typename T> \
725 : inline CheckedInt<T> \
726 : operator OP(const CheckedInt<T>& aLhs, const CheckedInt<T>& aRhs) \
727 : { \
728 : T result; \
729 : if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \
730 : return CheckedInt<T>(0, false); \
731 : } \
732 : return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid); \
733 : }
734 0 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, __builtin_add_overflow)
735 0 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, __builtin_sub_overflow)
736 0 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, __builtin_mul_overflow)
737 : #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2
738 : #else
739 0 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
740 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
741 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *)
742 : #endif
743 :
744 0 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /)
745 0 : MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %)
746 : #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
747 :
748 : // Implement castToCheckedInt<T>(x), making sure that
749 : // - it allows x to be either a CheckedInt<T> or any integer type
750 : // that can be casted to T
751 : // - if x is already a CheckedInt<T>, we just return a reference to it,
752 : // instead of copying it (optimization)
753 :
754 : namespace detail {
755 :
756 : template<typename T, typename U>
757 : struct CastToCheckedIntImpl
758 : {
759 : typedef CheckedInt<T> ReturnType;
760 0 : static CheckedInt<T> run(U aU) { return aU; }
761 : };
762 :
763 : template<typename T>
764 : struct CastToCheckedIntImpl<T, CheckedInt<T> >
765 : {
766 : typedef const CheckedInt<T>& ReturnType;
767 : static const CheckedInt<T>& run(const CheckedInt<T>& aU) { return aU; }
768 : };
769 :
770 : } // namespace detail
771 :
772 : template<typename T, typename U>
773 : inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType
774 : castToCheckedInt(U aU)
775 : {
776 : static_assert(detail::IsSupported<T>::value &&
777 : detail::IsSupported<U>::value,
778 : "This type is not supported by CheckedInt");
779 0 : return detail::CastToCheckedIntImpl<T, U>::run(aU);
780 : }
781 :
782 : #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
783 : template<typename T> \
784 : template<typename U> \
785 : CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) \
786 : { \
787 : *this = *this OP castToCheckedInt<T>(aRhs); \
788 : return *this; \
789 : } \
790 : template<typename T> \
791 : CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(const CheckedInt<T>& aRhs) \
792 : { \
793 : *this = *this OP aRhs; \
794 : return *this; \
795 : } \
796 : template<typename T, typename U> \
797 : inline CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) \
798 : { \
799 : return aLhs OP castToCheckedInt<T>(aRhs); \
800 : } \
801 : template<typename T, typename U> \
802 : inline CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) \
803 : { \
804 : return castToCheckedInt<T>(aLhs) OP aRhs; \
805 : }
806 :
807 0 : MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
808 0 : MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
809 0 : MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
810 0 : MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
811 0 : MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
812 :
813 : #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
814 :
815 : template<typename T, typename U>
816 : inline bool
817 0 : operator ==(const CheckedInt<T>& aLhs, U aRhs)
818 : {
819 0 : return aLhs == castToCheckedInt<T>(aRhs);
820 : }
821 :
822 : template<typename T, typename U>
823 : inline bool
824 : operator ==(U aLhs, const CheckedInt<T>& aRhs)
825 : {
826 0 : return castToCheckedInt<T>(aLhs) == aRhs;
827 : }
828 :
829 : // Convenience typedefs.
830 : typedef CheckedInt<int8_t> CheckedInt8;
831 : typedef CheckedInt<uint8_t> CheckedUint8;
832 : typedef CheckedInt<int16_t> CheckedInt16;
833 : typedef CheckedInt<uint16_t> CheckedUint16;
834 : typedef CheckedInt<int32_t> CheckedInt32;
835 : typedef CheckedInt<uint32_t> CheckedUint32;
836 : typedef CheckedInt<int64_t> CheckedInt64;
837 : typedef CheckedInt<uint64_t> CheckedUint64;
838 :
839 : } // namespace mozilla
840 :
841 : #endif /* mozilla_CheckedInt_h */
|