Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_StickyTimeDuration_h
8 : #define mozilla_StickyTimeDuration_h
9 :
10 : #include "mozilla/TimeStamp.h"
11 : #include "mozilla/FloatingPoint.h"
12 :
13 : namespace mozilla {
14 :
15 : /**
16 : * A ValueCalculator class that performs additional checks before performing
17 : * arithmetic operations such that if either operand is Forever (or the
18 : * negative equivalent) the result remains Forever (or the negative equivalent
19 : * as appropriate).
20 : *
21 : * Currently this only checks if either argument to each operation is
22 : * Forever/-Forever. However, it is possible that, for example,
23 : * aA + aB > INT64_MAX (or < INT64_MIN).
24 : *
25 : * We currently don't check for that case since we don't expect that to
26 : * happen often except under test conditions in which case the wrapping
27 : * behavior is probably acceptable.
28 : */
29 : class StickyTimeDurationValueCalculator
30 : {
31 : public:
32 : static int64_t
33 790 : Add(int64_t aA, int64_t aB)
34 : {
35 790 : MOZ_ASSERT((aA != INT64_MAX || aB != INT64_MIN) &&
36 : (aA != INT64_MIN || aB != INT64_MAX),
37 : "'Infinity + -Infinity' and '-Infinity + Infinity'"
38 : " are undefined");
39 :
40 : // Forever + x = Forever
41 : // x + Forever = Forever
42 790 : if (aA == INT64_MAX || aB == INT64_MAX) {
43 0 : return INT64_MAX;
44 : }
45 : // -Forever + x = -Forever
46 : // x + -Forever = -Forever
47 790 : if (aA == INT64_MIN || aB == INT64_MIN) {
48 0 : return INT64_MIN;
49 : }
50 :
51 790 : return aA + aB;
52 : }
53 :
54 : // Note that we can't just define Add and have BaseTimeDuration call Add with
55 : // negative arguments since INT64_MAX != -INT64_MIN so the saturating logic
56 : // won't work.
57 : static int64_t
58 24 : Subtract(int64_t aA, int64_t aB)
59 : {
60 24 : MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) || aA != aB,
61 : "'Infinity - Infinity' and '-Infinity - -Infinity'"
62 : " are undefined");
63 :
64 : // Forever - x = Forever
65 : // x - -Forever = Forever
66 24 : if (aA == INT64_MAX || aB == INT64_MIN) {
67 0 : return INT64_MAX;
68 : }
69 : // -Forever - x = -Forever
70 : // x - Forever = -Forever
71 24 : if (aA == INT64_MIN || aB == INT64_MAX) {
72 0 : return INT64_MIN;
73 : }
74 :
75 24 : return aA - aB;
76 : }
77 :
78 : template <typename T>
79 : static int64_t
80 : Multiply(int64_t aA, T aB) {
81 : // Specializations for double, float, and int64_t are provided following.
82 : return Multiply(aA, static_cast<int64_t>(aB));
83 : }
84 :
85 : static int64_t
86 : Divide(int64_t aA, int64_t aB) {
87 : MOZ_ASSERT(aB != 0, "Division by zero");
88 : MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) ||
89 : (aB != INT64_MAX && aB != INT64_MIN),
90 : "Dividing +/-Infinity by +/-Infinity is undefined");
91 :
92 : // Forever / +x = Forever
93 : // Forever / -x = -Forever
94 : // -Forever / +x = -Forever
95 : // -Forever / -x = Forever
96 : if (aA == INT64_MAX || aA == INT64_MIN) {
97 : return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX;
98 : }
99 : // x / Forever = 0
100 : // x / -Forever = 0
101 : if (aB == INT64_MAX || aB == INT64_MIN) {
102 : return 0;
103 : }
104 :
105 : return aA / aB;
106 : }
107 :
108 : static double
109 156 : DivideDouble(int64_t aA, int64_t aB)
110 : {
111 156 : MOZ_ASSERT(aB != 0, "Division by zero");
112 156 : MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) ||
113 : (aB != INT64_MAX && aB != INT64_MIN),
114 : "Dividing +/-Infinity by +/-Infinity is undefined");
115 :
116 : // Forever / +x = Forever
117 : // Forever / -x = -Forever
118 : // -Forever / +x = -Forever
119 : // -Forever / -x = Forever
120 156 : if (aA == INT64_MAX || aA == INT64_MIN) {
121 : return (aA >= 0) ^ (aB >= 0)
122 0 : ? NegativeInfinity<double>()
123 0 : : PositiveInfinity<double>();
124 : }
125 : // x / Forever = 0
126 : // x / -Forever = 0
127 156 : if (aB == INT64_MAX || aB == INT64_MIN) {
128 0 : return 0.0;
129 : }
130 :
131 156 : return static_cast<double>(aA) / aB;
132 : }
133 :
134 : static int64_t
135 : Modulo(int64_t aA, int64_t aB)
136 : {
137 : MOZ_ASSERT(aA != INT64_MAX && aA != INT64_MIN,
138 : "Infinity modulo x is undefined");
139 :
140 : return aA % aB;
141 : }
142 : };
143 :
144 : template <>
145 : inline int64_t
146 : StickyTimeDurationValueCalculator::Multiply<int64_t>(int64_t aA,
147 : int64_t aB)
148 : {
149 : MOZ_ASSERT((aA != 0 || (aB != INT64_MIN && aB != INT64_MAX)) &&
150 : ((aA != INT64_MIN && aA != INT64_MAX) || aB != 0),
151 : "Multiplication of infinity by zero");
152 :
153 : // Forever * +x = Forever
154 : // Forever * -x = -Forever
155 : // -Forever * +x = -Forever
156 : // -Forever * -x = Forever
157 : //
158 : // i.e. If one or more of the arguments is +/-Forever, then
159 : // return -Forever if the signs differ, or +Forever otherwise.
160 : if (aA == INT64_MAX || aA == INT64_MIN ||
161 : aB == INT64_MAX || aB == INT64_MIN) {
162 : return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX;
163 : }
164 :
165 : return aA * aB;
166 : }
167 :
168 : template <>
169 : inline int64_t
170 474 : StickyTimeDurationValueCalculator::Multiply<double>(int64_t aA, double aB)
171 : {
172 474 : MOZ_ASSERT((aA != 0 || (!IsInfinite(aB))) &&
173 : ((aA != INT64_MIN && aA != INT64_MAX) || aB != 0.0),
174 : "Multiplication of infinity by zero");
175 :
176 : // As with Multiply<int64_t>, if one or more of the arguments is
177 : // +/-Forever or +/-Infinity, then return -Forever if the signs differ,
178 : // or +Forever otherwise.
179 474 : if (aA == INT64_MAX || aA == INT64_MIN || IsInfinite(aB)) {
180 0 : return (aA >= 0) ^ (aB >= 0.0) ? INT64_MIN : INT64_MAX;
181 : }
182 :
183 474 : return aA * aB;
184 : }
185 :
186 : template <>
187 : inline int64_t
188 : StickyTimeDurationValueCalculator::Multiply<float>(int64_t aA, float aB)
189 : {
190 : MOZ_ASSERT(IsInfinite(aB) == IsInfinite(static_cast<double>(aB)),
191 : "Casting to float loses infinite-ness");
192 :
193 : return Multiply(aA, static_cast<double>(aB));
194 : }
195 :
196 : /**
197 : * Specialization of BaseTimeDuration that uses
198 : * StickyTimeDurationValueCalculator for arithmetic on the mValue member.
199 : *
200 : * Use this class when you need a time duration that is expected to hold values
201 : * of Forever (or the negative equivalent) *and* when you expect that
202 : * time duration to be used in arithmetic operations (and not just value
203 : * comparisons).
204 : */
205 : typedef BaseTimeDuration<StickyTimeDurationValueCalculator>
206 : StickyTimeDuration;
207 :
208 : // Template specializations to allow arithmetic between StickyTimeDuration
209 : // and TimeDuration objects by falling back to the safe behavior.
210 : inline StickyTimeDuration
211 454 : operator+(const TimeDuration& aA, const StickyTimeDuration& aB)
212 : {
213 454 : return StickyTimeDuration(aA) + aB;
214 : }
215 : inline StickyTimeDuration
216 336 : operator+(const StickyTimeDuration& aA, const TimeDuration& aB)
217 : {
218 336 : return aA + StickyTimeDuration(aB);
219 : }
220 :
221 : inline StickyTimeDuration
222 : operator-(const TimeDuration& aA, const StickyTimeDuration& aB)
223 : {
224 : return StickyTimeDuration(aA) - aB;
225 : }
226 : inline StickyTimeDuration
227 24 : operator-(const StickyTimeDuration& aA, const TimeDuration& aB)
228 : {
229 24 : return aA - StickyTimeDuration(aB);
230 : }
231 :
232 : inline StickyTimeDuration&
233 : operator+=(StickyTimeDuration &aA, const TimeDuration& aB)
234 : {
235 : return aA += StickyTimeDuration(aB);
236 : }
237 : inline StickyTimeDuration&
238 : operator-=(StickyTimeDuration &aA, const TimeDuration& aB)
239 : {
240 : return aA -= StickyTimeDuration(aB);
241 : }
242 :
243 : inline double
244 : operator/(const TimeDuration& aA, const StickyTimeDuration& aB)
245 : {
246 : return StickyTimeDuration(aA) / aB;
247 : }
248 : inline double
249 : operator/(const StickyTimeDuration& aA, const TimeDuration& aB)
250 : {
251 : return aA / StickyTimeDuration(aB);
252 : }
253 :
254 : inline StickyTimeDuration
255 : operator%(const TimeDuration& aA, const StickyTimeDuration& aB)
256 : {
257 : return StickyTimeDuration(aA) % aB;
258 : }
259 : inline StickyTimeDuration
260 : operator%(const StickyTimeDuration& aA, const TimeDuration& aB)
261 : {
262 : return aA % StickyTimeDuration(aB);
263 : }
264 :
265 : } // namespace mozilla
266 :
267 : #endif /* mozilla_StickyTimeDuration_h */
|