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