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