tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TimeUnits.h (13508B)


      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 <limits>
     11 #include <type_traits>
     12 
     13 #include "Intervals.h"
     14 #include "mozilla/CheckedInt.h"
     15 #include "mozilla/FloatingPoint.h"
     16 #include "mozilla/IntegerPrintfMacros.h"
     17 #include "mozilla/Maybe.h"
     18 #include "mozilla/TimeStamp.h"
     19 #include "nsPrintfCString.h"
     20 
     21 namespace mozilla::media {
     22 class TimeIntervals;
     23 }  // namespace mozilla::media
     24 // CopyChooser specialization for nsTArray
     25 template <>
     26 struct nsTArray_RelocationStrategy<mozilla::media::TimeIntervals> {
     27  using Type =
     28      nsTArray_RelocateUsingMoveConstructor<mozilla::media::TimeIntervals>;
     29 };
     30 
     31 namespace mozilla {
     32 
     33 // Number of milliseconds per second. 1e3.
     34 static const int64_t MSECS_PER_S = 1000;
     35 
     36 // Number of microseconds per second. 1e6.
     37 static const int64_t USECS_PER_S = 1000000;
     38 
     39 // Number of nanoseconds per second. 1e9.
     40 static const int64_t NSECS_PER_S = 1000000000;
     41 
     42 namespace media {
     43 
     44 #ifndef PROCESS_DECODE_LOG
     45 #  define PROCESS_DECODE_LOG(sample)                                   \
     46    MOZ_LOG(sPDMLog, mozilla::LogLevel::Verbose,                       \
     47            ("ProcessDecode: mDuration=%" PRIu64 "µs ; mTime=%" PRIu64 \
     48             "µs ; mTimecode=%" PRIu64 "µs",                           \
     49             (sample)->mDuration.ToMicroseconds(),                     \
     50             (sample)->mTime.ToMicroseconds(),                         \
     51             (sample)->mTimecode.ToMicroseconds()))
     52 #endif  // PROCESS_DECODE_LOG
     53 
     54 // TimeUnit is a class that represents a time value, that can be negative or
     55 // positive.
     56 //
     57 // Internally, it works by storing a numerator (the tick numbers), that uses
     58 // checked arithmetics, and a denominator (the base), that is a regular integer
     59 // on which arithmetics is never performed, and is only set at construction, or
     60 // replaced.
     61 //
     62 // Dividing the tick count by the base always yields a value in seconds,
     63 // but it's very useful to have a base that is dependant on the context: it can
     64 // be the sample-rate of an audio stream, the time base of an mp4, that's often
     65 // 90000 because it's divisible by 24, 25 and 30.
     66 //
     67 // Keeping time like this allows performing calculations on time values with
     68 // maximum precision, without having to have to care about rounding errors or
     69 // precision loss.
     70 //
     71 // If not specified, the base is 1e6, representing microseconds, for historical
     72 // reasons. Users can gradually move to more precise representations when
     73 // needed.
     74 //
     75 // INT64_MAX has the special meaning of being +∞, and INT64_MIN means -∞. Any
     76 // other value corresponds to a valid time.
     77 //
     78 // If an overflow or other problem occurs, the underlying CheckedInt<int64_t> is
     79 // invalid and a crash is triggered.
     80 class TimeUnit final {
     81 public:
     82  constexpr TimeUnit(CheckedInt64 aTicks, int64_t aBase)
     83      : mTicks(aTicks), mBase(aBase) {
     84    MOZ_RELEASE_ASSERT(mBase > 0);
     85    // aBase is often from a uint32_t and assumed less than 2^32.
     86    MOZ_DIAGNOSTIC_ASSERT(mBase <= UINT32_MAX);
     87  }
     88 
     89  explicit constexpr TimeUnit(CheckedInt64 aTicks)
     90      : mTicks(aTicks), mBase(USECS_PER_S) {}
     91 
     92  // Return the maximum number of ticks that a TimeUnit can contain.
     93  static constexpr int64_t MaxTicks() {
     94    return std::numeric_limits<int64_t>::max() - 1;
     95  }
     96 
     97  // These are only precise up to a point, which is aValue * aBase <= 2^53 - 1
     98  static TimeUnit FromSeconds(double aValue, int64_t aBase = USECS_PER_S);
     99  static TimeUnit FromSecondsWithBaseOf(double aSeconds,
    100                                        const TimeUnit& aOtherForBase) {
    101    return FromSeconds(aSeconds, aOtherForBase.mBase);
    102  }
    103 
    104  static constexpr TimeUnit FromMicroseconds(int64_t aValue) {
    105    return TimeUnit(aValue, USECS_PER_S);
    106  }
    107  static TimeUnit FromHns(int64_t aValue, int64_t aBase) {
    108    // Truncating here would mean a loss of precision.
    109    return TimeUnit::FromNanoseconds(aValue * 100).ToBase<RoundPolicy>(aBase);
    110  }
    111  static constexpr TimeUnit FromNanoseconds(int64_t aValue) {
    112    return TimeUnit(aValue, NSECS_PER_S);
    113  }
    114  static TimeUnit FromInfinity();
    115  static TimeUnit FromNegativeInfinity();
    116  static TimeUnit FromTimeDuration(const TimeDuration& aDuration);
    117  static constexpr TimeUnit Zero(int64_t aBase = USECS_PER_S) {
    118    return TimeUnit(0, aBase);
    119  }
    120  static constexpr TimeUnit Zero(const TimeUnit& aOther) {
    121    return TimeUnit(0, aOther.mBase);
    122  }
    123  static TimeUnit Invalid();
    124  int64_t ToMilliseconds() const;
    125  int64_t ToMicroseconds() const;
    126  int64_t ToNanoseconds() const;
    127  int64_t ToTicksAtRate(int64_t aRate) const;
    128  // Only to be used in release assertions or unit testing, returns true if this
    129  // TimeUnit has base aBase
    130  bool IsBase(int64_t aBase) const;
    131  double ToSeconds() const;
    132  nsCString ToString() const;
    133  TimeDuration ToTimeDuration() const;
    134  bool IsInfinite() const;
    135  bool IsPositive() const;
    136  bool IsPositiveOrZero() const;
    137  bool IsZero() const;
    138  bool IsNegative() const;
    139 
    140  // Returns true if the fractions are equal when converted to the smallest
    141  // base.
    142  bool EqualsAtLowestResolution(const TimeUnit& aOther) const;
    143  // Strict equality -- the fractions must be exactly equal
    144  bool operator==(const TimeUnit& aOther) const;
    145  bool operator!=(const TimeUnit& aOther) const;
    146  bool operator>=(const TimeUnit& aOther) const;
    147  bool operator>(const TimeUnit& aOther) const;
    148  bool operator<=(const TimeUnit& aOther) const;
    149  bool operator<(const TimeUnit& aOther) const;
    150  TimeUnit operator%(const TimeUnit& aOther) const;
    151  TimeUnit operator+(const TimeUnit& aOther) const;
    152  TimeUnit operator-(const TimeUnit& aOther) const;
    153  TimeUnit& operator+=(const TimeUnit& aOther);
    154  TimeUnit& operator-=(const TimeUnit& aOther);
    155  template <typename T>
    156  TimeUnit operator*(T aVal) const {
    157    // See bug 853398 for the reason to block double multiplier.
    158    // If required, use MultDouble below and with caution.
    159    static_assert(std::is_integral_v<T>, "Must be an integral type");
    160    return TimeUnit(mTicks * aVal, mBase);
    161  }
    162  TimeUnit MultDouble(double aVal) const;
    163  friend TimeUnit operator/(const TimeUnit& aUnit, int64_t aVal) {
    164    MOZ_DIAGNOSTIC_ASSERT(0 <= aVal && aVal <= UINT32_MAX);
    165    return TimeUnit(aUnit.mTicks / aVal, aUnit.mBase);
    166  }
    167  friend TimeUnit operator%(const TimeUnit& aUnit, int64_t aVal) {
    168    MOZ_DIAGNOSTIC_ASSERT(0 <= aVal && aVal <= UINT32_MAX);
    169    return TimeUnit(aUnit.mTicks % aVal, aUnit.mBase);
    170  }
    171 
    172  struct TruncatePolicy {
    173    template <typename T>
    174    static T policy(T& aValue) {
    175      return static_cast<T>(aValue);
    176    }
    177  };
    178 
    179  struct FloorPolicy {
    180    template <typename T>
    181    static T policy(T& aValue) {
    182      return std::floor(aValue);
    183    }
    184  };
    185 
    186  struct RoundPolicy {
    187    template <typename T>
    188    static T policy(T& aValue) {
    189      return std::round(aValue);
    190    }
    191  };
    192 
    193  struct CeilingPolicy {
    194    template <typename T>
    195    static T policy(T& aValue) {
    196      return std::ceil(aValue);
    197    }
    198  };
    199 
    200  template <class RoundingPolicy = TruncatePolicy>
    201  TimeUnit ToBase(int64_t aTargetBase) const {
    202    double dummy = 0.0;
    203    return ToBase<RoundingPolicy>(aTargetBase, dummy);
    204  }
    205 
    206  template <class RoundingPolicy = TruncatePolicy>
    207  TimeUnit ToBase(const TimeUnit& aTimeUnit) const {
    208    double dummy = 0.0;
    209    return ToBase<RoundingPolicy>(aTimeUnit, dummy);
    210  }
    211 
    212  // Allow returning the same value, in a base that matches another TimeUnit.
    213  template <class RoundingPolicy = TruncatePolicy>
    214  TimeUnit ToBase(const TimeUnit& aTimeUnit, double& aOutError) const {
    215    int64_t targetBase = aTimeUnit.mBase;
    216    return ToBase<RoundingPolicy>(targetBase, aOutError);
    217  }
    218 
    219  template <class RoundingPolicy = TruncatePolicy>
    220  TimeUnit ToBase(int64_t aTargetBase, double& aOutError) const {
    221    aOutError = 0.0;
    222    if (mTicks.value() == INT64_MAX || mTicks.value() == INT64_MIN) {
    223      // -ve or +ve infinity
    224      return TimeUnit(mTicks, aTargetBase);
    225    }
    226    CheckedInt<int64_t> ticks = mTicks * aTargetBase;
    227    if (ticks.isValid()) {
    228      imaxdiv_t rv = imaxdiv(ticks.value(), mBase);
    229      if (!rv.rem) {
    230        return TimeUnit(rv.quot, aTargetBase);
    231      }
    232    }
    233    double approx = static_cast<double>(mTicks.value()) *
    234                    static_cast<double>(aTargetBase) /
    235                    static_cast<double>(mBase);
    236    double integer;
    237    aOutError = modf(approx, &integer);
    238    return TimeUnit(AssertedCast<int64_t>(RoundingPolicy::policy(approx)),
    239                    aTargetBase);
    240  }
    241 
    242  bool IsValid() const;
    243 
    244  constexpr TimeUnit() = default;
    245 
    246  TimeUnit(const TimeUnit&) = default;
    247 
    248  TimeUnit& operator=(const TimeUnit&) = default;
    249 
    250  bool IsPosInf() const;
    251  bool IsNegInf() const;
    252 
    253  // Allow serializing a TimeUnit via IPC
    254  friend IPC::ParamTraits<mozilla::media::TimeUnit>;
    255 
    256 #ifndef VISIBLE_TIMEUNIT_INTERNALS
    257 private:
    258 #endif
    259  int64_t ToCommonUnit(int64_t aRatio) const;
    260  // Reduce a TimeUnit to the smallest possible ticks and base. This is useful
    261  // to comparison with big time values that can otherwise overflow.
    262  TimeUnit Reduced() const;
    263 
    264  CheckedInt64 mTicks{0};
    265  // Default base is microseconds.
    266  int64_t mBase{USECS_PER_S};
    267 };
    268 
    269 using NullableTimeUnit = Maybe<TimeUnit>;
    270 
    271 using TimeInterval = Interval<TimeUnit>;
    272 
    273 // A set of intervals, containing TimeUnit.
    274 class TimeIntervals : public IntervalSet<TimeUnit> {
    275 public:
    276  using BaseType = IntervalSet<TimeUnit>;
    277  using InnerType = TimeUnit;
    278 
    279  // We can't use inherited constructors yet. So we have to duplicate all the
    280  // constructors found in IntervalSet base class.
    281  // all this could be later replaced with:
    282  // using IntervalSet<TimeUnit>::IntervalSet;
    283 
    284  // MOZ_IMPLICIT as we want to enable initialization in the form:
    285  // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
    286  MOZ_IMPLICIT TimeIntervals(const BaseType& aOther) : BaseType(aOther) {}
    287  MOZ_IMPLICIT TimeIntervals(BaseType&& aOther) : BaseType(std::move(aOther)) {}
    288  explicit TimeIntervals(const BaseType::ElemType& aOther) : BaseType(aOther) {}
    289  explicit TimeIntervals(BaseType::ElemType&& aOther)
    290      : BaseType(std::move(aOther)) {}
    291 
    292  static TimeIntervals Invalid() {
    293    return TimeIntervals(TimeInterval(TimeUnit::FromNegativeInfinity(),
    294                                      TimeUnit::FromNegativeInfinity()));
    295  }
    296  bool IsInvalid() const {
    297    return Length() == 1 && Start(0).IsNegInf() && End(0).IsNegInf();
    298  }
    299 
    300  // Returns the same interval, with a given base resolution.
    301  TimeIntervals ToBase(const TimeUnit& aBase) const {
    302    TimeIntervals output;
    303    for (const auto& interval : mIntervals) {
    304      TimeInterval convertedInterval{interval.mStart.ToBase(aBase),
    305                                     interval.mEnd.ToBase(aBase),
    306                                     interval.mFuzz.ToBase(aBase)};
    307      output += convertedInterval;
    308    }
    309    return output;
    310  }
    311 
    312  // Returns the same interval, with a microsecond resolution. This is used to
    313  // compare TimeUnits internal to demuxers (that use a base from the container)
    314  // to floating point numbers in seconds from content.
    315  TimeIntervals ToMicrosecondResolution() const {
    316    TimeIntervals output;
    317 
    318    for (const auto& interval : mIntervals) {
    319      TimeInterval reducedPrecision{interval.mStart.ToBase(USECS_PER_S),
    320                                    interval.mEnd.ToBase(USECS_PER_S),
    321                                    interval.mFuzz.ToBase(USECS_PER_S)};
    322      output += reducedPrecision;
    323    }
    324    return output;
    325  }
    326 
    327  nsCString ToString() const {
    328    nsCString dump;
    329    for (const auto& interval : mIntervals) {
    330      dump += nsPrintfCString("[%s],", interval.ToString().get());
    331    }
    332    return dump;
    333  }
    334 
    335  TimeIntervals() = default;
    336 };
    337 
    338 using TimeRange = Interval<double>;
    339 
    340 // A set of intervals, containing doubles that are seconds.
    341 class TimeRanges : public IntervalSet<double> {
    342 public:
    343  using BaseType = IntervalSet<double>;
    344  using InnerType = double;
    345  using nld = std::numeric_limits<double>;
    346 
    347  // We can't use inherited constructors yet. So we have to duplicate all the
    348  // constructors found in IntervalSet base class.
    349  // all this could be later replaced with:
    350  // using IntervalSet<TimeUnit>::IntervalSet;
    351 
    352  // MOZ_IMPLICIT as we want to enable initialization in the form:
    353  // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
    354  MOZ_IMPLICIT TimeRanges(const BaseType& aOther) : BaseType(aOther) {}
    355  MOZ_IMPLICIT TimeRanges(BaseType&& aOther) : BaseType(std::move(aOther)) {}
    356  explicit TimeRanges(const BaseType::ElemType& aOther) : BaseType(aOther) {}
    357  explicit TimeRanges(BaseType::ElemType&& aOther)
    358      : BaseType(std::move(aOther)) {}
    359 
    360  static TimeRanges Invalid() {
    361    return TimeRanges(TimeRange(-nld::infinity(), nld::infinity()));
    362  }
    363  bool IsInvalid() const {
    364    return Length() == 1 && Start(0) == -nld::infinity() &&
    365           End(0) == nld::infinity();
    366  }
    367  // Convert from TimeUnit-based intervals to second-based TimeRanges.
    368  explicit TimeRanges(const TimeIntervals& aIntervals) {
    369    for (const auto& interval : aIntervals) {
    370      Add(TimeRange(interval.mStart.ToSeconds(), interval.mEnd.ToSeconds()));
    371    }
    372  }
    373 
    374  TimeRanges ToMicrosecondResolution() const;
    375 
    376  TimeRanges() = default;
    377 };
    378 
    379 }  // namespace media
    380 }  // namespace mozilla
    381 
    382 #endif  // TIME_UNITS_H