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 TIME_UNITS_H
8 #define TIME_UNITS_H
9 
10 #include <type_traits>
11 
12 #include "Intervals.h"
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/TimeStamp.h"
17 
18 namespace mozilla {
19 namespace media {
20 class TimeIntervals;
21 }  // namespace media
22 }  // namespace mozilla
23 // CopyChooser specialization for nsTArray
24 template <>
25 struct nsTArray_RelocationStrategy<mozilla::media::TimeIntervals> {
26   typedef nsTArray_RelocateUsingMoveConstructor<mozilla::media::TimeIntervals>
27       Type;
28 };
29 
30 namespace mozilla {
31 
32 // Number of microseconds per second. 1e6.
33 static const int64_t USECS_PER_S = 1000000;
34 
35 // Number of microseconds per millisecond.
36 static const int64_t USECS_PER_MS = 1000;
37 
38 namespace media {
39 
40 // Number of nanoseconds per second. 1e9.
41 static const int64_t NSECS_PER_S = 1000000000;
42 
43 // TimeUnit at present uses a CheckedInt64 as storage.
44 // INT64_MAX has the special meaning of being +oo.
45 class TimeUnit final {
46  public:
47   static TimeUnit FromSeconds(double aValue) {
48     MOZ_ASSERT(!IsNaN(aValue));
49 
50     if (mozilla::IsInfinite<double>(aValue)) {
51       return aValue > 0 ? FromInfinity() : FromNegativeInfinity();
52     }
53     // Due to internal double representation, this
54     // operation is not commutative, do not attempt to simplify.
55     double halfUsec = .0000005;
56     double val =
57         (aValue <= 0 ? aValue - halfUsec : aValue + halfUsec) * USECS_PER_S;
58     if (val >= double(INT64_MAX)) {
59       return FromMicroseconds(INT64_MAX);
60     } else if (val <= double(INT64_MIN)) {
61       return FromMicroseconds(INT64_MIN);
62     } else {
63       return FromMicroseconds(int64_t(val));
64     }
65   }
66 
67   static constexpr TimeUnit FromMicroseconds(int64_t aValue) {
68     return TimeUnit(aValue);
69   }
70 
71   static constexpr TimeUnit FromNanoseconds(int64_t aValue) {
72     return TimeUnit(aValue / 1000);
73   }
74 
75   static constexpr TimeUnit FromInfinity() { return TimeUnit(INT64_MAX); }
76 
77   static constexpr TimeUnit FromNegativeInfinity() {
78     return TimeUnit(INT64_MIN);
79   }
80 
81   static TimeUnit FromTimeDuration(const TimeDuration& aDuration) {
82     return FromSeconds(aDuration.ToSeconds());
83   }
84 
85   static constexpr TimeUnit Zero() { return TimeUnit(0); }
86 
87   static TimeUnit Invalid() {
88     TimeUnit ret;
89     ret.mValue = CheckedInt64(INT64_MAX);
90     // Force an overflow to render the CheckedInt invalid.
91     ret.mValue += 1;
92     return ret;
93   }
94 
95   int64_t ToMicroseconds() const { return mValue.value(); }
96 
97   int64_t ToNanoseconds() const { return mValue.value() * 1000; }
98 
99   double ToSeconds() const {
100     if (IsPosInf()) {
101       return PositiveInfinity<double>();
102     }
103     if (IsNegInf()) {
104       return NegativeInfinity<double>();
105     }
106     return double(mValue.value()) / USECS_PER_S;
107   }
108 
109   TimeDuration ToTimeDuration() const {
110     return TimeDuration::FromMicroseconds(mValue.value());
111   }
112 
113   bool IsInfinite() const { return IsPosInf() || IsNegInf(); }
114 
115   bool IsPositive() const { return mValue.value() > 0; }
116 
117   bool IsNegative() const { return mValue.value() < 0; }
118 
119   bool operator==(const TimeUnit& aOther) const {
120     MOZ_ASSERT(IsValid() && aOther.IsValid());
121     return mValue.value() == aOther.mValue.value();
122   }
123   bool operator!=(const TimeUnit& aOther) const {
124     MOZ_ASSERT(IsValid() && aOther.IsValid());
125     return mValue.value() != aOther.mValue.value();
126   }
127   bool operator>=(const TimeUnit& aOther) const {
128     MOZ_ASSERT(IsValid() && aOther.IsValid());
129     return mValue.value() >= aOther.mValue.value();
130   }
131   bool operator>(const TimeUnit& aOther) const { return !(*this <= aOther); }
132   bool operator<=(const TimeUnit& aOther) const {
133     MOZ_ASSERT(IsValid() && aOther.IsValid());
134     return mValue.value() <= aOther.mValue.value();
135   }
136   bool operator<(const TimeUnit& aOther) const { return !(*this >= aOther); }
137   TimeUnit operator%(const TimeUnit& aOther) const {
138     MOZ_ASSERT(IsValid() && aOther.IsValid());
139     return TimeUnit(mValue % aOther.mValue);
140   }
141 
142   TimeUnit operator+(const TimeUnit& aOther) const {
143     if (IsInfinite() || aOther.IsInfinite()) {
144       // When adding at least one infinite value, the result is either
145       // +/-Inf, or NaN. So do the calculation in floating point for
146       // simplicity.
147       double result = ToSeconds() + aOther.ToSeconds();
148       return IsNaN(result) ? TimeUnit::Invalid() : FromSeconds(result);
149     }
150     return TimeUnit(mValue + aOther.mValue);
151   }
152 
153   TimeUnit operator-(const TimeUnit& aOther) const {
154     if (IsInfinite() || aOther.IsInfinite()) {
155       // When subtracting at least one infinite value, the result is either
156       // +/-Inf, or NaN. So do the calculation in floating point for
157       // simplicity.
158       double result = ToSeconds() - aOther.ToSeconds();
159       return IsNaN(result) ? TimeUnit::Invalid() : FromSeconds(result);
160     }
161     MOZ_ASSERT(!IsInfinite() && !aOther.IsInfinite());
162     return TimeUnit(mValue - aOther.mValue);
163   }
164   TimeUnit& operator+=(const TimeUnit& aOther) {
165     *this = *this + aOther;
166     return *this;
167   }
168   TimeUnit& operator-=(const TimeUnit& aOther) {
169     *this = *this - aOther;
170     return *this;
171   }
172 
173   template <typename T>
174   TimeUnit operator*(T aVal) const {
175     // See bug 853398 for the reason to block double multiplier.
176     // If required, use MultDouble below and with caution.
177     static_assert(std::is_integral_v<T>, "Must be an integral type");
178     return TimeUnit(mValue * aVal);
179   }
180   TimeUnit MultDouble(double aVal) const {
181     return TimeUnit::FromSeconds(ToSeconds() * aVal);
182   }
183   friend TimeUnit operator/(const TimeUnit& aUnit, int64_t aVal) {
184     MOZ_DIAGNOSTIC_ASSERT(0 <= aVal && aVal <= UINT32_MAX);
185     return TimeUnit(aUnit.mValue / aVal);
186   }
187   friend TimeUnit operator%(const TimeUnit& aUnit, int64_t aVal) {
188     MOZ_DIAGNOSTIC_ASSERT(0 <= aVal && aVal <= UINT32_MAX);
189     return TimeUnit(aUnit.mValue % aVal);
190   }
191 
192   bool IsValid() const { return mValue.isValid(); }
193 
194   constexpr TimeUnit() = default;
195 
196   TimeUnit(const TimeUnit&) = default;
197 
198   TimeUnit& operator=(const TimeUnit&) = default;
199 
200   bool IsPosInf() const {
201     return mValue.isValid() && mValue.value() == INT64_MAX;
202   }
203   bool IsNegInf() const {
204     return mValue.isValid() && mValue.value() == INT64_MIN;
205   }
206 
207  private:
208   explicit constexpr TimeUnit(CheckedInt64 aMicroseconds)
209       : mValue(aMicroseconds) {}
210 
211   // Our internal representation is in microseconds.
212   CheckedInt64 mValue{0};
213 };
214 
215 typedef Maybe<TimeUnit> NullableTimeUnit;
216 
217 typedef Interval<TimeUnit> TimeInterval;
218 
219 class TimeIntervals : public IntervalSet<TimeUnit> {
220  public:
221   typedef IntervalSet<TimeUnit> BaseType;
222 
223   // We can't use inherited constructors yet. So we have to duplicate all the
224   // constructors found in IntervalSet base class.
225   // all this could be later replaced with:
226   // using IntervalSet<TimeUnit>::IntervalSet;
227 
228   // MOZ_IMPLICIT as we want to enable initialization in the form:
229   // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
230   MOZ_IMPLICIT TimeIntervals(const BaseType& aOther) : BaseType(aOther) {}
231   MOZ_IMPLICIT TimeIntervals(BaseType&& aOther) : BaseType(std::move(aOther)) {}
232   explicit TimeIntervals(const BaseType::ElemType& aOther) : BaseType(aOther) {}
233   explicit TimeIntervals(BaseType::ElemType&& aOther)
234       : BaseType(std::move(aOther)) {}
235 
236   static TimeIntervals Invalid() {
237     return TimeIntervals(TimeInterval(TimeUnit::FromNegativeInfinity(),
238                                       TimeUnit::FromNegativeInfinity()));
239   }
240   bool IsInvalid() const {
241     return Length() == 1 && Start(0).IsNegInf() && End(0).IsNegInf();
242   }
243 
244   TimeIntervals() = default;
245 };
246 
247 }  // namespace media
248 }  // namespace mozilla
249 
250 #endif  // TIME_UNITS_H
251