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