no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / media / Intervals.h
blobecda90d350115852c91d403f75fe7ce26bd5a3fc
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/. */
7 #ifndef DOM_MEDIA_INTERVALS_H_
8 #define DOM_MEDIA_INTERVALS_H_
10 #include <algorithm>
11 #include <type_traits>
13 #include "nsTArray.h"
14 #include "nsString.h"
15 #include "nsPrintfCString.h"
17 // Specialization for nsTArray CopyChooser.
18 namespace mozilla::media {
19 template <class T>
20 class IntervalSet;
21 class TimeUnit;
22 } // namespace mozilla::media
24 template <class E>
25 struct nsTArray_RelocationStrategy<mozilla::media::IntervalSet<E>> {
26 typedef nsTArray_RelocateUsingMoveConstructor<mozilla::media::IntervalSet<E>>
27 Type;
30 namespace mozilla::media {
32 /* Interval defines an interval between two points. Unlike a traditional
33 interval [A,B] where A <= x <= B, the upper boundary B is exclusive: A <= x <
34 B (e.g [A,B[ or [A,B) depending on where you're living) It provides basic
35 interval arithmetic and fuzzy edges. The type T must provides a default
36 constructor and +, -, <, <= and == operators.
38 template <typename T>
39 class Interval {
40 public:
41 typedef Interval<T> SelfType;
43 Interval() : mStart(T()), mEnd(T()), mFuzz(T()) {}
45 template <typename StartArg, typename EndArg>
46 Interval(StartArg&& aStart, EndArg&& aEnd)
47 : mStart(aStart), mEnd(aEnd), mFuzz() {
48 MOZ_DIAGNOSTIC_ASSERT(mStart <= mEnd, "Invalid Interval");
51 template <typename StartArg, typename EndArg, typename FuzzArg>
52 Interval(StartArg&& aStart, EndArg&& aEnd, FuzzArg&& aFuzz)
53 : mStart(aStart), mEnd(aEnd), mFuzz(aFuzz) {
54 MOZ_DIAGNOSTIC_ASSERT(mStart <= mEnd, "Invalid Interval");
57 Interval(const SelfType& aOther)
58 : mStart(aOther.mStart), mEnd(aOther.mEnd), mFuzz(aOther.mFuzz) {}
60 Interval(SelfType&& aOther)
61 : mStart(std::move(aOther.mStart)),
62 mEnd(std::move(aOther.mEnd)),
63 mFuzz(std::move(aOther.mFuzz)) {}
65 SelfType& operator=(const SelfType& aOther) {
66 mStart = aOther.mStart;
67 mEnd = aOther.mEnd;
68 mFuzz = aOther.mFuzz;
69 return *this;
72 SelfType& operator=(SelfType&& aOther) {
73 MOZ_ASSERT(&aOther != this, "self-moves are prohibited");
74 this->~Interval();
75 new (this) Interval(std::move(aOther));
76 return *this;
79 // Basic interval arithmetic operator definition.
80 SelfType operator+(const SelfType& aOther) const {
81 return SelfType(mStart + aOther.mStart, mEnd + aOther.mEnd,
82 mFuzz + aOther.mFuzz);
85 SelfType operator+(const T& aVal) const {
86 return SelfType(mStart + aVal, mEnd + aVal, mFuzz);
89 SelfType operator-(const SelfType& aOther) const {
90 return SelfType(mStart - aOther.mEnd, mEnd - aOther.mStart,
91 mFuzz + aOther.mFuzz);
94 SelfType operator-(const T& aVal) const {
95 return SelfType(mStart - aVal, mEnd - aVal, mFuzz);
98 SelfType& operator+=(const SelfType& aOther) {
99 mStart += aOther.mStart;
100 mEnd += aOther.mEnd;
101 mFuzz += aOther.mFuzz;
102 return *this;
105 SelfType& operator+=(const T& aVal) {
106 mStart += aVal;
107 mEnd += aVal;
108 return *this;
111 SelfType& operator-=(const SelfType& aOther) {
112 mStart -= aOther.mStart;
113 mEnd -= aOther.mEnd;
114 mFuzz += aOther.mFuzz;
115 return *this;
118 SelfType& operator-=(const T& aVal) {
119 mStart -= aVal;
120 mEnd -= aVal;
121 return *this;
124 bool operator==(const SelfType& aOther) const {
125 return mStart == aOther.mStart && mEnd == aOther.mEnd;
128 bool operator!=(const SelfType& aOther) const { return !(*this == aOther); }
130 bool Contains(const T& aX) const {
131 return mStart - mFuzz <= aX && aX < mEnd + mFuzz;
134 bool ContainsStrict(const T& aX) const { return mStart <= aX && aX < mEnd; }
136 bool ContainsWithStrictEnd(const T& aX) const {
137 return mStart - mFuzz <= aX && aX < mEnd;
140 bool Contains(const SelfType& aOther) const {
141 return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
142 (aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
145 bool ContainsStrict(const SelfType& aOther) const {
146 return mStart <= aOther.mStart && aOther.mEnd <= mEnd;
149 bool ContainsWithStrictEnd(const SelfType& aOther) const {
150 return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
151 aOther.mEnd <= mEnd;
154 bool Intersects(const SelfType& aOther) const {
155 return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
156 (aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
159 bool IntersectsStrict(const SelfType& aOther) const {
160 return mStart < aOther.mEnd && aOther.mStart < mEnd;
163 // Same as Intersects, but including the boundaries.
164 bool Touches(const SelfType& aOther) const {
165 return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
166 (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
169 // Returns true if aOther is strictly to the right of this and contiguous.
170 // This operation isn't commutative.
171 bool Contiguous(const SelfType& aOther) const {
172 return mEnd <= aOther.mStart &&
173 aOther.mStart - mEnd <= mFuzz + aOther.mFuzz;
176 bool RightOf(const SelfType& aOther) const {
177 return aOther.mEnd - aOther.mFuzz <= mStart + mFuzz;
180 bool LeftOf(const SelfType& aOther) const {
181 return mEnd - mFuzz <= aOther.mStart + aOther.mFuzz;
184 SelfType Span(const SelfType& aOther) const {
185 if (IsEmpty()) {
186 return aOther;
188 SelfType result(*this);
189 if (aOther.mStart < mStart) {
190 result.mStart = aOther.mStart;
192 if (mEnd < aOther.mEnd) {
193 result.mEnd = aOther.mEnd;
195 if (mFuzz < aOther.mFuzz) {
196 result.mFuzz = aOther.mFuzz;
198 return result;
201 SelfType Intersection(const SelfType& aOther) const {
202 const T& s = std::max(mStart, aOther.mStart);
203 const T& e = std::min(mEnd, aOther.mEnd);
204 const T& f = std::max(mFuzz, aOther.mFuzz);
205 if (s < e) {
206 return SelfType(s, e, f);
208 // Return an empty interval.
209 return SelfType();
212 T Length() const { return mEnd - mStart; }
214 bool IsEmpty() const { return mStart == mEnd; }
216 void SetFuzz(const T& aFuzz) { mFuzz = aFuzz; }
218 // Returns true if the two intervals intersect with this being on the right
219 // of aOther
220 bool TouchesOnRight(const SelfType& aOther) const {
221 return aOther.mStart <= mStart &&
222 (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
223 (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
226 // Returns true if the two intervals intersect with this being on the right
227 // of aOther, ignoring fuzz.
228 bool TouchesOnRightStrict(const SelfType& aOther) const {
229 return aOther.mStart <= mStart && mStart <= aOther.mEnd;
232 nsCString ToString() const {
233 if constexpr (std::is_same_v<T, TimeUnit>) {
234 return nsPrintfCString("[%s, %s](%s)", mStart.ToString().get(),
235 mEnd.ToString().get(), mFuzz.ToString().get());
236 } else if constexpr (std::is_same_v<T, double>) {
237 return nsPrintfCString("[%lf, %lf](%lf)", mStart, mEnd, mFuzz);
241 T mStart;
242 T mEnd;
243 T mFuzz;
246 // An IntervalSet in a collection of Intervals. The IntervalSet is always
247 // normalized.
248 template <typename T>
249 class IntervalSet {
250 public:
251 typedef IntervalSet<T> SelfType;
252 typedef Interval<T> ElemType;
253 typedef AutoTArray<ElemType, 4> ContainerType;
254 typedef typename ContainerType::index_type IndexType;
256 IntervalSet() = default;
257 virtual ~IntervalSet() = default;
259 IntervalSet(const SelfType& aOther) : mIntervals(aOther.mIntervals.Clone()) {}
261 IntervalSet(SelfType&& aOther) {
262 mIntervals.AppendElements(std::move(aOther.mIntervals));
265 explicit IntervalSet(const ElemType& aOther) {
266 if (!aOther.IsEmpty()) {
267 mIntervals.AppendElement(aOther);
271 explicit IntervalSet(ElemType&& aOther) {
272 if (!aOther.IsEmpty()) {
273 mIntervals.AppendElement(std::move(aOther));
277 bool operator==(const SelfType& aOther) const {
278 return mIntervals == aOther.mIntervals;
281 bool operator!=(const SelfType& aOther) const {
282 return mIntervals != aOther.mIntervals;
285 SelfType& operator=(const SelfType& aOther) {
286 mIntervals = aOther.mIntervals.Clone();
287 return *this;
290 SelfType& operator=(SelfType&& aOther) {
291 MOZ_ASSERT(&aOther != this, "self-moves are prohibited");
292 this->~IntervalSet();
293 new (this) IntervalSet(std::move(aOther));
294 return *this;
297 SelfType& operator=(const ElemType& aInterval) {
298 mIntervals.Clear();
299 if (!aInterval.IsEmpty()) {
300 mIntervals.AppendElement(aInterval);
302 return *this;
305 SelfType& operator=(ElemType&& aInterval) {
306 mIntervals.Clear();
307 if (!aInterval.IsEmpty()) {
308 mIntervals.AppendElement(std::move(aInterval));
310 return *this;
313 SelfType& Add(const SelfType& aIntervals) {
314 if (aIntervals.mIntervals.Length() == 1) {
315 Add(aIntervals.mIntervals[0]);
316 } else {
317 mIntervals.AppendElements(aIntervals.mIntervals);
318 Normalize();
320 return *this;
323 SelfType& Add(const ElemType& aInterval) {
324 if (aInterval.IsEmpty()) {
325 return *this;
327 if (mIntervals.IsEmpty()) {
328 mIntervals.AppendElement(aInterval);
329 return *this;
331 ElemType& last = mIntervals.LastElement();
332 if (aInterval.TouchesOnRight(last)) {
333 last = last.Span(aInterval);
334 return *this;
336 // Most of our actual usage is adding an interval that will be outside the
337 // range. We can speed up normalization here.
338 if (aInterval.RightOf(last)) {
339 mIntervals.AppendElement(aInterval);
340 return *this;
343 ContainerType normalized;
344 ElemType current(aInterval);
345 IndexType i = 0;
346 for (; i < mIntervals.Length(); i++) {
347 ElemType& interval = mIntervals[i];
348 if (current.Touches(interval)) {
349 current = current.Span(interval);
350 } else if (current.LeftOf(interval)) {
351 break;
352 } else {
353 normalized.AppendElement(std::move(interval));
356 normalized.AppendElement(std::move(current));
357 for (; i < mIntervals.Length(); i++) {
358 normalized.AppendElement(std::move(mIntervals[i]));
360 mIntervals.Clear();
361 mIntervals.AppendElements(std::move(normalized));
363 return *this;
366 SelfType& operator+=(const SelfType& aIntervals) {
367 Add(aIntervals);
368 return *this;
371 SelfType& operator+=(const ElemType& aInterval) {
372 Add(aInterval);
373 return *this;
376 SelfType operator+(const SelfType& aIntervals) const {
377 SelfType intervals(*this);
378 intervals.Add(aIntervals);
379 return intervals;
382 SelfType operator+(const ElemType& aInterval) const {
383 SelfType intervals(*this);
384 intervals.Add(aInterval);
385 return intervals;
388 friend SelfType operator+(const ElemType& aInterval,
389 const SelfType& aIntervals) {
390 SelfType intervals;
391 intervals.Add(aInterval);
392 intervals.Add(aIntervals);
393 return intervals;
396 // Excludes an interval from an IntervalSet.
397 SelfType& operator-=(const ElemType& aInterval) {
398 if (aInterval.IsEmpty() || mIntervals.IsEmpty()) {
399 return *this;
401 if (mIntervals.Length() == 1 &&
402 mIntervals[0].TouchesOnRightStrict(aInterval)) {
403 // Fast path when we're removing from the front of a set with a
404 // single interval. This is common for the buffered time ranges
405 // we see on Twitch.
406 if (aInterval.mEnd >= mIntervals[0].mEnd) {
407 mIntervals.RemoveElementAt(0);
408 } else {
409 mIntervals[0].mStart = aInterval.mEnd;
410 mIntervals[0].mFuzz = std::max(mIntervals[0].mFuzz, aInterval.mFuzz);
412 return *this;
415 // General case performed by inverting aInterval within the bounds of
416 // mIntervals and then doing the intersection.
417 T firstEnd = std::max(mIntervals[0].mStart, aInterval.mStart);
418 T secondStart = std::min(mIntervals.LastElement().mEnd, aInterval.mEnd);
419 ElemType startInterval(mIntervals[0].mStart, firstEnd);
420 ElemType endInterval(secondStart, mIntervals.LastElement().mEnd);
421 SelfType intervals(std::move(startInterval));
422 intervals += std::move(endInterval);
423 return Intersection(intervals);
426 SelfType& operator-=(const SelfType& aIntervals) {
427 for (const auto& interval : aIntervals.mIntervals) {
428 *this -= interval;
430 return *this;
433 SelfType operator-(const SelfType& aInterval) const {
434 SelfType intervals(*this);
435 intervals -= aInterval;
436 return intervals;
439 SelfType operator-(const ElemType& aInterval) const {
440 SelfType intervals(*this);
441 intervals -= aInterval;
442 return intervals;
445 // Mutate this IntervalSet to be the union of this and aOther.
446 SelfType& Union(const SelfType& aOther) {
447 Add(aOther);
448 return *this;
451 SelfType& Union(const ElemType& aInterval) {
452 Add(aInterval);
453 return *this;
456 // Mutate this TimeRange to be the intersection of this and aOther.
457 SelfType& Intersection(const SelfType& aOther) {
458 ContainerType intersection;
460 // Ensure the intersection has enough capacity to store the upper bound on
461 // the intersection size. This ensures that we don't spend time reallocating
462 // the storage as we append, at the expense of extra memory.
463 intersection.SetCapacity(std::max(aOther.Length(), mIntervals.Length()));
465 const ContainerType& other = aOther.mIntervals;
466 IndexType i = 0, j = 0;
467 for (; i < mIntervals.Length() && j < other.Length();) {
468 if (mIntervals[i].IntersectsStrict(other[j])) {
469 intersection.AppendElement(mIntervals[i].Intersection(other[j]));
471 if (mIntervals[i].mEnd < other[j].mEnd) {
472 i++;
473 } else {
474 j++;
477 mIntervals = std::move(intersection);
478 return *this;
481 SelfType& Intersection(const ElemType& aInterval) {
482 SelfType intervals(aInterval);
483 return Intersection(intervals);
486 const ElemType& operator[](IndexType aIndex) const {
487 return mIntervals[aIndex];
490 // Returns the start boundary of the first interval. Or a default constructed
491 // T if IntervalSet is empty (and aExists if provided will be set to false).
492 T GetStart(bool* aExists = nullptr) const {
493 bool exists = !mIntervals.IsEmpty();
495 if (aExists) {
496 *aExists = exists;
499 if (exists) {
500 return mIntervals[0].mStart;
501 } else {
502 return T();
506 // Returns the end boundary of the last interval. Or a default constructed T
507 // if IntervalSet is empty (and aExists if provided will be set to false).
508 T GetEnd(bool* aExists = nullptr) const {
509 bool exists = !mIntervals.IsEmpty();
510 if (aExists) {
511 *aExists = exists;
514 if (exists) {
515 return mIntervals.LastElement().mEnd;
516 } else {
517 return T();
521 IndexType Length() const { return mIntervals.Length(); }
523 bool IsEmpty() const { return mIntervals.IsEmpty(); }
525 T Start(IndexType aIndex) const { return mIntervals[aIndex].mStart; }
527 T Start(IndexType aIndex, bool& aExists) const {
528 aExists = aIndex < mIntervals.Length();
530 if (aExists) {
531 return mIntervals[aIndex].mStart;
532 } else {
533 return T();
537 T End(IndexType aIndex) const { return mIntervals[aIndex].mEnd; }
539 T End(IndexType aIndex, bool& aExists) const {
540 aExists = aIndex < mIntervals.Length();
542 if (aExists) {
543 return mIntervals[aIndex].mEnd;
544 } else {
545 return T();
549 bool Contains(const ElemType& aInterval) const {
550 for (const auto& interval : mIntervals) {
551 if (interval.Contains(aInterval)) {
552 return true;
555 return false;
558 bool ContainsStrict(const ElemType& aInterval) const {
559 for (const auto& interval : mIntervals) {
560 if (interval.ContainsStrict(aInterval)) {
561 return true;
564 return false;
567 bool Contains(const T& aX) const {
568 for (const auto& interval : mIntervals) {
569 if (interval.Contains(aX)) {
570 return true;
573 return false;
576 bool ContainsStrict(const T& aX) const {
577 for (const auto& interval : mIntervals) {
578 if (interval.ContainsStrict(aX)) {
579 return true;
582 return false;
585 bool ContainsWithStrictEnd(const T& aX) const {
586 for (const auto& interval : mIntervals) {
587 if (interval.ContainsWithStrictEnd(aX)) {
588 return true;
591 return false;
594 bool ContainsWithStrictEnd(const ElemType& aInterval) const {
595 for (const auto& interval : mIntervals) {
596 if (interval.ContainsWithStrictEnd(aInterval)) {
597 return true;
600 return false;
603 bool Intersects(const ElemType& aInterval) const {
604 for (const auto& interval : mIntervals) {
605 if (interval.Intersects(aInterval)) {
606 return true;
609 return false;
612 bool IntersectsStrict(const ElemType& aInterval) const {
613 for (const auto& interval : mIntervals) {
614 if (interval.IntersectsStrict(aInterval)) {
615 return true;
618 return false;
621 // Returns if there's any intersection between this and aOther.
622 bool IntersectsStrict(const SelfType& aOther) const {
623 const ContainerType& other = aOther.mIntervals;
624 IndexType i = 0, j = 0;
625 for (; i < mIntervals.Length() && j < other.Length();) {
626 if (mIntervals[i].IntersectsStrict(other[j])) {
627 return true;
629 if (mIntervals[i].mEnd < other[j].mEnd) {
630 i++;
631 } else {
632 j++;
635 return false;
638 bool IntersectsWithStrictEnd(const ElemType& aInterval) const {
639 for (const auto& interval : mIntervals) {
640 if (interval.IntersectsWithStrictEnd(aInterval)) {
641 return true;
644 return false;
647 // Shift all values by aOffset.
648 SelfType& Shift(const T& aOffset) {
649 for (auto& interval : mIntervals) {
650 interval.mStart = interval.mStart + aOffset;
651 interval.mEnd = interval.mEnd + aOffset;
653 return *this;
656 void SetFuzz(const T& aFuzz) {
657 for (auto& interval : mIntervals) {
658 interval.SetFuzz(aFuzz);
660 MergeOverlappingIntervals();
663 static const IndexType NoIndex = IndexType(-1);
665 IndexType Find(const T& aValue) const {
666 for (IndexType i = 0; i < mIntervals.Length(); i++) {
667 if (mIntervals[i].Contains(aValue)) {
668 return i;
671 return NoIndex;
674 // Methods for range-based for loops.
675 typename ContainerType::iterator begin() { return mIntervals.begin(); }
677 typename ContainerType::const_iterator begin() const {
678 return mIntervals.begin();
681 typename ContainerType::iterator end() { return mIntervals.end(); }
683 typename ContainerType::const_iterator end() const {
684 return mIntervals.end();
687 ElemType& LastInterval() {
688 MOZ_ASSERT(!mIntervals.IsEmpty());
689 return mIntervals.LastElement();
692 const ElemType& LastInterval() const {
693 MOZ_ASSERT(!mIntervals.IsEmpty());
694 return mIntervals.LastElement();
697 void Clear() { mIntervals.Clear(); }
699 protected:
700 ContainerType mIntervals;
702 private:
703 void Normalize() {
704 if (mIntervals.Length() < 2) {
705 return;
707 mIntervals.Sort(CompareIntervals());
708 MergeOverlappingIntervals();
711 void MergeOverlappingIntervals() {
712 if (mIntervals.Length() < 2) {
713 return;
716 // This merges the intervals in place.
717 IndexType read = 0;
718 IndexType write = 0;
719 while (read < mIntervals.Length()) {
720 ElemType current(mIntervals[read]);
721 read++;
722 while (read < mIntervals.Length() && current.Touches(mIntervals[read])) {
723 current = current.Span(mIntervals[read]);
724 read++;
726 mIntervals[write] = current;
727 write++;
729 mIntervals.SetLength(write);
732 struct CompareIntervals {
733 bool Equals(const ElemType& aT1, const ElemType& aT2) const {
734 return aT1.mStart == aT2.mStart && aT1.mEnd == aT2.mEnd;
737 bool LessThan(const ElemType& aT1, const ElemType& aT2) const {
738 return aT1.mStart - aT1.mFuzz < aT2.mStart + aT2.mFuzz;
743 // clang doesn't allow for this to be defined inline of IntervalSet.
744 template <typename T>
745 IntervalSet<T> Union(const IntervalSet<T>& aIntervals1,
746 const IntervalSet<T>& aIntervals2) {
747 IntervalSet<T> intervals(aIntervals1);
748 intervals.Union(aIntervals2);
749 return intervals;
752 template <typename T>
753 IntervalSet<T> Intersection(const IntervalSet<T>& aIntervals1,
754 const IntervalSet<T>& aIntervals2) {
755 IntervalSet<T> intersection(aIntervals1);
756 intersection.Intersection(aIntervals2);
757 return intersection;
760 } // namespace mozilla::media
762 #endif // DOM_MEDIA_INTERVALS_H_