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