]>
Commit | Line | Data |
---|---|---|
1df5f87f A |
1 | /* |
2 | * Copyright (C) 2011 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #ifndef CheckedArithmetic_h | |
27 | #define CheckedArithmetic_h | |
28 | ||
29 | #include "Assertions.h" | |
30 | #include "TypeTraits.h" | |
31 | ||
32 | #include <limits> | |
33 | #include <stdint.h> | |
34 | ||
35 | /* Checked<T> | |
36 | * | |
37 | * This class provides a mechanism to perform overflow-safe integer arithmetic | |
38 | * without having to manually ensure that you have all the required bounds checks | |
39 | * directly in your code. | |
40 | * | |
41 | * There are two modes of operation: | |
42 | * - The default is Checked<T, CrashOnOverflow>, and crashes at the point | |
43 | * and overflow has occurred. | |
44 | * - The alternative is Checked<T, RecordOverflow>, which uses an additional | |
45 | * byte of storage to track whether an overflow has occurred, subsequent | |
46 | * unchecked operations will crash if an overflow has occured | |
47 | * | |
48 | * It is possible to provide a custom overflow handler, in which case you need | |
49 | * to support these functions: | |
50 | * - void overflowed(); | |
51 | * This function is called when an operation has produced an overflow. | |
52 | * - bool hasOverflowed(); | |
53 | * This function must return true if overflowed() has been called on an | |
54 | * instance and false if it has not. | |
55 | * - void clearOverflow(); | |
56 | * Used to reset overflow tracking when a value is being overwritten with | |
57 | * a new value. | |
58 | * | |
59 | * Checked<T> works for all integer types, with the following caveats: | |
60 | * - Mixing signedness of operands is only supported for types narrower than | |
61 | * 64bits. | |
62 | * - It does have a performance impact, so tight loops may want to be careful | |
63 | * when using it. | |
64 | * | |
65 | */ | |
66 | ||
67 | namespace WTF { | |
68 | ||
69 | class CrashOnOverflow { | |
70 | protected: | |
71 | void overflowed() | |
72 | { | |
73 | CRASH(); | |
74 | } | |
75 | ||
76 | void clearOverflow() { } | |
77 | ||
78 | public: | |
79 | bool hasOverflowed() const { return false; } | |
80 | }; | |
81 | ||
82 | class RecordOverflow { | |
83 | protected: | |
84 | RecordOverflow() | |
85 | : m_overflowed(false) | |
86 | { | |
87 | } | |
88 | ||
89 | void overflowed() | |
90 | { | |
91 | m_overflowed = true; | |
92 | } | |
93 | ||
94 | void clearOverflow() | |
95 | { | |
96 | m_overflowed = false; | |
97 | } | |
98 | ||
99 | public: | |
100 | bool hasOverflowed() const { return m_overflowed; } | |
101 | ||
102 | private: | |
103 | unsigned char m_overflowed; | |
104 | }; | |
105 | ||
106 | template <typename T, class OverflowHandler = CrashOnOverflow> class Checked; | |
107 | template <typename T> struct RemoveChecked; | |
108 | template <typename T> struct RemoveChecked<Checked<T> >; | |
109 | ||
110 | template <typename Target, typename Source, bool targetSigned = std::numeric_limits<Target>::is_signed, bool sourceSigned = std::numeric_limits<Source>::is_signed> struct BoundsChecker; | |
111 | template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, false> { | |
112 | static bool inBounds(Source value) | |
113 | { | |
114 | // Same signedness so implicit type conversion will always increase precision | |
115 | // to widest type | |
116 | return value <= std::numeric_limits<Target>::max(); | |
117 | } | |
118 | }; | |
119 | ||
120 | template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, true> { | |
121 | static bool inBounds(Source value) | |
122 | { | |
123 | // Same signedness so implicit type conversion will always increase precision | |
124 | // to widest type | |
125 | return std::numeric_limits<Target>::min() <= value && value <= std::numeric_limits<Target>::max(); | |
126 | } | |
127 | }; | |
128 | ||
129 | template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, true> { | |
130 | static bool inBounds(Source value) | |
131 | { | |
132 | // Target is unsigned so any value less than zero is clearly unsafe | |
133 | if (value < 0) | |
134 | return false; | |
135 | // If our (unsigned) Target is the same or greater width we can | |
136 | // convert value to type Target without losing precision | |
137 | if (sizeof(Target) >= sizeof(Source)) | |
138 | return static_cast<Target>(value) <= std::numeric_limits<Target>::max(); | |
139 | // The signed Source type has greater precision than the target so | |
140 | // max(Target) -> Source will widen. | |
141 | return value <= static_cast<Source>(std::numeric_limits<Target>::max()); | |
142 | } | |
143 | }; | |
144 | ||
145 | template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, false> { | |
146 | static bool inBounds(Source value) | |
147 | { | |
148 | // Signed target with an unsigned source | |
149 | if (sizeof(Target) <= sizeof(Source)) | |
150 | return value <= static_cast<Source>(std::numeric_limits<Target>::max()); | |
151 | // Target is Wider than Source so we're guaranteed to fit any value in | |
152 | // unsigned Source | |
153 | return true; | |
154 | } | |
155 | }; | |
156 | ||
157 | template <typename Target, typename Source, bool SameType = IsSameType<Target, Source>::value> struct BoundsCheckElider; | |
158 | template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, true> { | |
159 | static bool inBounds(Source) { return true; } | |
160 | }; | |
161 | template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, false> : public BoundsChecker<Target, Source> { | |
162 | }; | |
163 | ||
164 | template <typename Target, typename Source> static inline bool isInBounds(Source value) | |
165 | { | |
166 | return BoundsCheckElider<Target, Source>::inBounds(value); | |
167 | } | |
168 | ||
169 | template <typename T> struct RemoveChecked { | |
170 | typedef T CleanType; | |
171 | static const CleanType DefaultValue = 0; | |
172 | }; | |
173 | ||
174 | template <typename T> struct RemoveChecked<Checked<T, CrashOnOverflow> > { | |
175 | typedef typename RemoveChecked<T>::CleanType CleanType; | |
176 | static const CleanType DefaultValue = 0; | |
177 | }; | |
178 | ||
179 | template <typename T> struct RemoveChecked<Checked<T, RecordOverflow> > { | |
180 | typedef typename RemoveChecked<T>::CleanType CleanType; | |
181 | static const CleanType DefaultValue = 0; | |
182 | }; | |
183 | ||
184 | // The ResultBase and SignednessSelector are used to workaround typeof not being | |
185 | // available in MSVC | |
186 | template <typename U, typename V, bool uIsBigger = (sizeof(U) > sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; | |
187 | template <typename U, typename V> struct ResultBase<U, V, true, false> { | |
188 | typedef U ResultType; | |
189 | }; | |
190 | ||
191 | template <typename U, typename V> struct ResultBase<U, V, false, false> { | |
192 | typedef V ResultType; | |
193 | }; | |
194 | ||
195 | template <typename U> struct ResultBase<U, U, false, true> { | |
196 | typedef U ResultType; | |
197 | }; | |
198 | ||
199 | template <typename U, typename V, bool uIsSigned = std::numeric_limits<U>::is_signed, bool vIsSigned = std::numeric_limits<V>::is_signed> struct SignednessSelector; | |
200 | template <typename U, typename V> struct SignednessSelector<U, V, true, true> { | |
201 | typedef U ResultType; | |
202 | }; | |
203 | ||
204 | template <typename U, typename V> struct SignednessSelector<U, V, false, false> { | |
205 | typedef U ResultType; | |
206 | }; | |
207 | ||
208 | template <typename U, typename V> struct SignednessSelector<U, V, true, false> { | |
209 | typedef V ResultType; | |
210 | }; | |
211 | ||
212 | template <typename U, typename V> struct SignednessSelector<U, V, false, true> { | |
213 | typedef U ResultType; | |
214 | }; | |
215 | ||
216 | template <typename U, typename V> struct ResultBase<U, V, false, true> { | |
217 | typedef typename SignednessSelector<U, V>::ResultType ResultType; | |
218 | }; | |
219 | ||
220 | template <typename U, typename V> struct Result : ResultBase<typename RemoveChecked<U>::CleanType, typename RemoveChecked<V>::CleanType> { | |
221 | }; | |
222 | ||
223 | template <typename LHS, typename RHS, typename ResultType = typename Result<LHS, RHS>::ResultType, | |
224 | bool lhsSigned = std::numeric_limits<LHS>::is_signed, bool rhsSigned = std::numeric_limits<RHS>::is_signed> struct ArithmeticOperations; | |
225 | ||
226 | template <typename LHS, typename RHS, typename ResultType> struct ArithmeticOperations<LHS, RHS, ResultType, true, true> { | |
227 | // LHS and RHS are signed types | |
228 | ||
229 | // Helper function | |
230 | static inline bool signsMatch(LHS lhs, RHS rhs) | |
231 | { | |
232 | return (lhs ^ rhs) >= 0; | |
233 | } | |
234 | ||
235 | static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN | |
236 | { | |
237 | if (signsMatch(lhs, rhs)) { | |
238 | if (lhs >= 0) { | |
239 | if ((std::numeric_limits<ResultType>::max() - rhs) < lhs) | |
240 | return false; | |
241 | } else { | |
242 | ResultType temp = lhs - std::numeric_limits<ResultType>::min(); | |
243 | if (rhs < -temp) | |
244 | return false; | |
245 | } | |
246 | } // if the signs do not match this operation can't overflow | |
247 | result = lhs + rhs; | |
248 | return true; | |
249 | } | |
250 | ||
251 | static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN | |
252 | { | |
253 | if (!signsMatch(lhs, rhs)) { | |
254 | if (lhs >= 0) { | |
255 | if (lhs > std::numeric_limits<ResultType>::max() + rhs) | |
256 | return false; | |
257 | } else { | |
258 | if (rhs > std::numeric_limits<ResultType>::max() + lhs) | |
259 | return false; | |
260 | } | |
261 | } // if the signs match this operation can't overflow | |
262 | result = lhs - rhs; | |
263 | return true; | |
264 | } | |
265 | ||
266 | static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN | |
267 | { | |
268 | if (signsMatch(lhs, rhs)) { | |
269 | if (lhs >= 0) { | |
270 | if (lhs && (std::numeric_limits<ResultType>::max() / lhs) < rhs) | |
271 | return false; | |
272 | } else { | |
273 | if (lhs == std::numeric_limits<ResultType>::min() || rhs == std::numeric_limits<ResultType>::min()) | |
274 | return false; | |
275 | if ((std::numeric_limits<ResultType>::max() / -lhs) < -rhs) | |
276 | return false; | |
277 | } | |
278 | } else { | |
279 | if (lhs < 0) { | |
280 | if (rhs && lhs < (std::numeric_limits<ResultType>::min() / rhs)) | |
281 | return false; | |
282 | } else { | |
283 | if (lhs && rhs < (std::numeric_limits<ResultType>::min() / lhs)) | |
284 | return false; | |
285 | } | |
286 | } | |
287 | result = lhs * rhs; | |
288 | return true; | |
289 | } | |
290 | ||
291 | static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } | |
292 | ||
293 | }; | |
294 | ||
295 | template <typename LHS, typename RHS, typename ResultType> struct ArithmeticOperations<LHS, RHS, ResultType, false, false> { | |
296 | // LHS and RHS are unsigned types so bounds checks are nice and easy | |
297 | static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN | |
298 | { | |
299 | ResultType temp = lhs + rhs; | |
300 | if (temp < lhs) | |
301 | return false; | |
302 | result = temp; | |
303 | return true; | |
304 | } | |
305 | ||
306 | static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN | |
307 | { | |
308 | ResultType temp = lhs - rhs; | |
309 | if (temp > lhs) | |
310 | return false; | |
311 | result = temp; | |
312 | return true; | |
313 | } | |
314 | ||
315 | static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN | |
316 | { | |
317 | ResultType temp = lhs * rhs; | |
318 | if (temp < lhs) | |
319 | return false; | |
320 | result = temp; | |
321 | return true; | |
322 | } | |
323 | ||
324 | static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } | |
325 | ||
326 | }; | |
327 | ||
328 | template <typename ResultType> struct ArithmeticOperations<int, unsigned, ResultType, true, false> { | |
329 | static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) | |
330 | { | |
331 | int64_t temp = lhs + rhs; | |
332 | if (temp < std::numeric_limits<ResultType>::min()) | |
333 | return false; | |
334 | if (temp > std::numeric_limits<ResultType>::max()) | |
335 | return false; | |
336 | result = static_cast<ResultType>(temp); | |
337 | return true; | |
338 | } | |
339 | ||
340 | static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) | |
341 | { | |
342 | int64_t temp = lhs - rhs; | |
343 | if (temp < std::numeric_limits<ResultType>::min()) | |
344 | return false; | |
345 | if (temp > std::numeric_limits<ResultType>::max()) | |
346 | return false; | |
347 | result = static_cast<ResultType>(temp); | |
348 | return true; | |
349 | } | |
350 | ||
351 | static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) | |
352 | { | |
353 | int64_t temp = lhs * rhs; | |
354 | if (temp < std::numeric_limits<ResultType>::min()) | |
355 | return false; | |
356 | if (temp > std::numeric_limits<ResultType>::max()) | |
357 | return false; | |
358 | result = static_cast<ResultType>(temp); | |
359 | return true; | |
360 | } | |
361 | ||
362 | static inline bool equals(int lhs, unsigned rhs) | |
363 | { | |
364 | return static_cast<int64_t>(lhs) == static_cast<int64_t>(rhs); | |
365 | } | |
366 | }; | |
367 | ||
368 | template <typename ResultType> struct ArithmeticOperations<unsigned, int, ResultType, false, true> { | |
369 | static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) | |
370 | { | |
371 | return ArithmeticOperations<int, unsigned, ResultType>::add(rhs, lhs, result); | |
372 | } | |
373 | ||
374 | static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) | |
375 | { | |
376 | return ArithmeticOperations<int, unsigned, ResultType>::sub(lhs, rhs, result); | |
377 | } | |
378 | ||
379 | static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) | |
380 | { | |
381 | return ArithmeticOperations<int, unsigned, ResultType>::multiply(rhs, lhs, result); | |
382 | } | |
383 | ||
384 | static inline bool equals(unsigned lhs, int rhs) | |
385 | { | |
386 | return ArithmeticOperations<int, unsigned, ResultType>::equals(rhs, lhs); | |
387 | } | |
388 | }; | |
389 | ||
390 | template <typename U, typename V, typename R> static inline bool safeAdd(U lhs, V rhs, R& result) | |
391 | { | |
392 | return ArithmeticOperations<U, V, R>::add(lhs, rhs, result); | |
393 | } | |
394 | ||
395 | template <typename U, typename V, typename R> static inline bool safeSub(U lhs, V rhs, R& result) | |
396 | { | |
397 | return ArithmeticOperations<U, V, R>::sub(lhs, rhs, result); | |
398 | } | |
399 | ||
400 | template <typename U, typename V, typename R> static inline bool safeMultiply(U lhs, V rhs, R& result) | |
401 | { | |
402 | return ArithmeticOperations<U, V, R>::multiply(lhs, rhs, result); | |
403 | } | |
404 | ||
405 | template <typename U, typename V> static inline bool safeEquals(U lhs, V rhs) | |
406 | { | |
407 | return ArithmeticOperations<U, V>::equals(lhs, rhs); | |
408 | } | |
409 | ||
410 | enum ResultOverflowedTag { ResultOverflowed }; | |
411 | ||
412 | // FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 | |
413 | static inline bool workAroundClangBug() { return true; } | |
414 | ||
415 | template <typename T, class OverflowHandler> class Checked : public OverflowHandler { | |
416 | public: | |
417 | template <typename _T, class _OverflowHandler> friend class Checked; | |
418 | Checked() | |
419 | : m_value(0) | |
420 | { | |
421 | } | |
422 | ||
423 | Checked(ResultOverflowedTag) | |
424 | : m_value(0) | |
425 | { | |
426 | // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 | |
427 | if (workAroundClangBug()) | |
428 | this->overflowed(); | |
429 | } | |
430 | ||
431 | template <typename U> Checked(U value) | |
432 | { | |
433 | if (!isInBounds<T>(value)) | |
434 | this->overflowed(); | |
435 | m_value = static_cast<T>(value); | |
436 | } | |
437 | ||
438 | template <typename V> Checked(const Checked<T, V>& rhs) | |
439 | : m_value(rhs.m_value) | |
440 | { | |
441 | if (rhs.hasOverflowed()) | |
442 | this->overflowed(); | |
443 | } | |
444 | ||
445 | template <typename U> Checked(const Checked<U, OverflowHandler>& rhs) | |
446 | : OverflowHandler(rhs) | |
447 | { | |
448 | if (!isInBounds<T>(rhs.m_value)) | |
449 | this->overflowed(); | |
450 | m_value = static_cast<T>(rhs.m_value); | |
451 | } | |
452 | ||
453 | template <typename U, typename V> Checked(const Checked<U, V>& rhs) | |
454 | { | |
455 | if (rhs.hasOverflowed()) | |
456 | this->overflowed(); | |
457 | if (!isInBounds<T>(rhs.m_value)) | |
458 | this->overflowed(); | |
459 | m_value = static_cast<T>(rhs.m_value); | |
460 | } | |
461 | ||
462 | const Checked& operator=(Checked rhs) | |
463 | { | |
464 | this->clearOverflow(); | |
465 | if (rhs.hasOverflowed()) | |
466 | this->overflowed(); | |
467 | m_value = static_cast<T>(rhs.m_value); | |
468 | return *this; | |
469 | } | |
470 | ||
471 | template <typename U> const Checked& operator=(U value) | |
472 | { | |
473 | return *this = Checked(value); | |
474 | } | |
475 | ||
476 | template <typename U, typename V> const Checked& operator=(const Checked<U, V>& rhs) | |
477 | { | |
478 | return *this = Checked(rhs); | |
479 | } | |
480 | ||
481 | // prefix | |
482 | const Checked& operator++() | |
483 | { | |
484 | if (m_value == std::numeric_limits<T>::max()) | |
485 | this->overflowed(); | |
486 | m_value++; | |
487 | return *this; | |
488 | } | |
489 | ||
490 | const Checked& operator--() | |
491 | { | |
492 | if (m_value == std::numeric_limits<T>::min()) | |
493 | this->overflowed(); | |
494 | m_value--; | |
495 | return *this; | |
496 | } | |
497 | ||
498 | // postfix operators | |
499 | const Checked operator++(int) | |
500 | { | |
501 | if (m_value == std::numeric_limits<T>::max()) | |
502 | this->overflowed(); | |
503 | return Checked(m_value++); | |
504 | } | |
505 | ||
506 | const Checked operator--(int) | |
507 | { | |
508 | if (m_value == std::numeric_limits<T>::min()) | |
509 | this->overflowed(); | |
510 | return Checked(m_value--); | |
511 | } | |
512 | ||
513 | // Boolean operators | |
514 | bool operator!() const | |
515 | { | |
516 | if (this->hasOverflowed()) | |
517 | CRASH(); | |
518 | return !m_value; | |
519 | } | |
520 | ||
521 | typedef void* (Checked::*UnspecifiedBoolType); | |
522 | operator UnspecifiedBoolType*() const | |
523 | { | |
524 | if (this->hasOverflowed()) | |
525 | CRASH(); | |
526 | return (m_value) ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0; | |
527 | } | |
528 | ||
529 | // Value accessors. unsafeGet() will crash if there's been an overflow. | |
530 | T unsafeGet() const | |
531 | { | |
532 | if (this->hasOverflowed()) | |
533 | CRASH(); | |
534 | return m_value; | |
535 | } | |
536 | ||
537 | bool safeGet(T& value) const WARN_UNUSED_RETURN | |
538 | { | |
539 | value = m_value; | |
540 | return this->hasOverflowed(); | |
541 | } | |
542 | ||
543 | // Mutating assignment | |
544 | template <typename U> const Checked operator+=(U rhs) | |
545 | { | |
546 | if (!safeAdd(m_value, rhs, m_value)) | |
547 | this->overflowed(); | |
548 | return *this; | |
549 | } | |
550 | ||
551 | template <typename U> const Checked operator-=(U rhs) | |
552 | { | |
553 | if (!safeSub(m_value, rhs, m_value)) | |
554 | this->overflowed(); | |
555 | return *this; | |
556 | } | |
557 | ||
558 | template <typename U> const Checked operator*=(U rhs) | |
559 | { | |
560 | if (!safeMultiply(m_value, rhs, m_value)) | |
561 | this->overflowed(); | |
562 | return *this; | |
563 | } | |
564 | ||
565 | template <typename U, typename V> const Checked operator+=(Checked<U, V> rhs) | |
566 | { | |
567 | if (rhs.hasOverflowed()) | |
568 | this->overflowed(); | |
569 | return *this += rhs.m_value; | |
570 | } | |
571 | ||
572 | template <typename U, typename V> const Checked operator-=(Checked<U, V> rhs) | |
573 | { | |
574 | if (rhs.hasOverflowed()) | |
575 | this->overflowed(); | |
576 | return *this -= rhs.m_value; | |
577 | } | |
578 | ||
579 | template <typename U, typename V> const Checked operator*=(Checked<U, V> rhs) | |
580 | { | |
581 | if (rhs.hasOverflowed()) | |
582 | this->overflowed(); | |
583 | return *this *= rhs.m_value; | |
584 | } | |
585 | ||
586 | // Equality comparisons | |
587 | template <typename V> bool operator==(Checked<T, V> rhs) | |
588 | { | |
589 | return unsafeGet() == rhs.unsafeGet(); | |
590 | } | |
591 | ||
592 | template <typename U> bool operator==(U rhs) | |
593 | { | |
594 | if (this->hasOverflowed()) | |
595 | this->overflowed(); | |
596 | return safeEquals(m_value, rhs); | |
597 | } | |
598 | ||
599 | template <typename U, typename V> const Checked operator==(Checked<U, V> rhs) | |
600 | { | |
601 | return unsafeGet() == Checked(rhs.unsafeGet()); | |
602 | } | |
603 | ||
604 | template <typename U> bool operator!=(U rhs) | |
605 | { | |
606 | return !(*this == rhs); | |
607 | } | |
608 | ||
609 | private: | |
610 | // Disallow implicit conversion of floating point to integer types | |
611 | Checked(float); | |
612 | Checked(double); | |
613 | void operator=(float); | |
614 | void operator=(double); | |
615 | void operator+=(float); | |
616 | void operator+=(double); | |
617 | void operator-=(float); | |
618 | void operator-=(double); | |
619 | T m_value; | |
620 | }; | |
621 | ||
622 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs) | |
623 | { | |
624 | U x = 0; | |
625 | V y = 0; | |
626 | bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); | |
627 | typename Result<U, V>::ResultType result = 0; | |
628 | overflowed |= !safeAdd(x, y, result); | |
629 | if (overflowed) | |
630 | return ResultOverflowed; | |
631 | return result; | |
632 | } | |
633 | ||
634 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs) | |
635 | { | |
636 | U x = 0; | |
637 | V y = 0; | |
638 | bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); | |
639 | typename Result<U, V>::ResultType result = 0; | |
640 | overflowed |= !safeSub(x, y, result); | |
641 | if (overflowed) | |
642 | return ResultOverflowed; | |
643 | return result; | |
644 | } | |
645 | ||
646 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs) | |
647 | { | |
648 | U x = 0; | |
649 | V y = 0; | |
650 | bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); | |
651 | typename Result<U, V>::ResultType result = 0; | |
652 | overflowed |= !safeMultiply(x, y, result); | |
653 | if (overflowed) | |
654 | return ResultOverflowed; | |
655 | return result; | |
656 | } | |
657 | ||
658 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(Checked<U, OverflowHandler> lhs, V rhs) | |
659 | { | |
660 | return lhs + Checked<V, OverflowHandler>(rhs); | |
661 | } | |
662 | ||
663 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(Checked<U, OverflowHandler> lhs, V rhs) | |
664 | { | |
665 | return lhs - Checked<V, OverflowHandler>(rhs); | |
666 | } | |
667 | ||
668 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(Checked<U, OverflowHandler> lhs, V rhs) | |
669 | { | |
670 | return lhs * Checked<V, OverflowHandler>(rhs); | |
671 | } | |
672 | ||
673 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(U lhs, Checked<V, OverflowHandler> rhs) | |
674 | { | |
675 | return Checked<U, OverflowHandler>(lhs) + rhs; | |
676 | } | |
677 | ||
678 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(U lhs, Checked<V, OverflowHandler> rhs) | |
679 | { | |
680 | return Checked<U, OverflowHandler>(lhs) - rhs; | |
681 | } | |
682 | ||
683 | template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(U lhs, Checked<V, OverflowHandler> rhs) | |
684 | { | |
685 | return Checked<U, OverflowHandler>(lhs) * rhs; | |
686 | } | |
687 | ||
688 | } | |
689 | ||
690 | using WTF::Checked; | |
691 | using WTF::RecordOverflow; | |
692 | ||
693 | #endif |