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