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_
11 #include <type_traits>
15 #include "nsPrintfCString.h"
17 // Specialization for nsTArray CopyChooser.
18 namespace mozilla::media
{
22 } // namespace mozilla::media
25 struct nsTArray_RelocationStrategy
<mozilla::media::IntervalSet
<E
>> {
26 typedef nsTArray_RelocateUsingMoveConstructor
<mozilla::media::IntervalSet
<E
>>
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.
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
;
72 SelfType
& operator=(SelfType
&& aOther
) {
73 MOZ_ASSERT(&aOther
!= this, "self-moves are prohibited");
75 new (this) Interval(std::move(aOther
));
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
;
101 mFuzz
+= aOther
.mFuzz
;
105 SelfType
& operator+=(const T
& aVal
) {
111 SelfType
& operator-=(const SelfType
& aOther
) {
112 mStart
-= aOther
.mStart
;
114 mFuzz
+= aOther
.mFuzz
;
118 SelfType
& operator-=(const T
& aVal
) {
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
) &&
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 {
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
;
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
);
206 return SelfType(s
, e
, f
);
208 // Return an empty interval.
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
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
);
246 // An IntervalSet in a collection of Intervals. The IntervalSet is always
248 template <typename T
>
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();
290 SelfType
& operator=(SelfType
&& aOther
) {
291 MOZ_ASSERT(&aOther
!= this, "self-moves are prohibited");
292 this->~IntervalSet();
293 new (this) IntervalSet(std::move(aOther
));
297 SelfType
& operator=(const ElemType
& aInterval
) {
299 if (!aInterval
.IsEmpty()) {
300 mIntervals
.AppendElement(aInterval
);
305 SelfType
& operator=(ElemType
&& aInterval
) {
307 if (!aInterval
.IsEmpty()) {
308 mIntervals
.AppendElement(std::move(aInterval
));
313 SelfType
& Add(const SelfType
& aIntervals
) {
314 if (aIntervals
.mIntervals
.Length() == 1) {
315 Add(aIntervals
.mIntervals
[0]);
317 mIntervals
.AppendElements(aIntervals
.mIntervals
);
323 SelfType
& Add(const ElemType
& aInterval
) {
324 if (aInterval
.IsEmpty()) {
327 if (mIntervals
.IsEmpty()) {
328 mIntervals
.AppendElement(aInterval
);
331 ElemType
& last
= mIntervals
.LastElement();
332 if (aInterval
.TouchesOnRight(last
)) {
333 last
= last
.Span(aInterval
);
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
);
343 ContainerType normalized
;
344 ElemType
current(aInterval
);
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
)) {
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
]));
361 mIntervals
.AppendElements(std::move(normalized
));
366 SelfType
& operator+=(const SelfType
& aIntervals
) {
371 SelfType
& operator+=(const ElemType
& aInterval
) {
376 SelfType
operator+(const SelfType
& aIntervals
) const {
377 SelfType
intervals(*this);
378 intervals
.Add(aIntervals
);
382 SelfType
operator+(const ElemType
& aInterval
) const {
383 SelfType
intervals(*this);
384 intervals
.Add(aInterval
);
388 friend SelfType
operator+(const ElemType
& aInterval
,
389 const SelfType
& aIntervals
) {
391 intervals
.Add(aInterval
);
392 intervals
.Add(aIntervals
);
396 // Excludes an interval from an IntervalSet.
397 SelfType
& operator-=(const ElemType
& aInterval
) {
398 if (aInterval
.IsEmpty() || mIntervals
.IsEmpty()) {
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
406 if (aInterval
.mEnd
>= mIntervals
[0].mEnd
) {
407 mIntervals
.RemoveElementAt(0);
409 mIntervals
[0].mStart
= aInterval
.mEnd
;
410 mIntervals
[0].mFuzz
= std::max(mIntervals
[0].mFuzz
, aInterval
.mFuzz
);
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
) {
433 SelfType
operator-(const SelfType
& aInterval
) const {
434 SelfType
intervals(*this);
435 intervals
-= aInterval
;
439 SelfType
operator-(const ElemType
& aInterval
) const {
440 SelfType
intervals(*this);
441 intervals
-= aInterval
;
445 // Mutate this IntervalSet to be the union of this and aOther.
446 SelfType
& Union(const SelfType
& aOther
) {
451 SelfType
& Union(const ElemType
& aInterval
) {
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
) {
477 mIntervals
= std::move(intersection
);
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();
500 return mIntervals
[0].mStart
;
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();
515 return mIntervals
.LastElement().mEnd
;
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();
531 return mIntervals
[aIndex
].mStart
;
537 T
End(IndexType aIndex
) const { return mIntervals
[aIndex
].mEnd
; }
539 T
End(IndexType aIndex
, bool& aExists
) const {
540 aExists
= aIndex
< mIntervals
.Length();
543 return mIntervals
[aIndex
].mEnd
;
549 bool Contains(const ElemType
& aInterval
) const {
550 for (const auto& interval
: mIntervals
) {
551 if (interval
.Contains(aInterval
)) {
558 bool ContainsStrict(const ElemType
& aInterval
) const {
559 for (const auto& interval
: mIntervals
) {
560 if (interval
.ContainsStrict(aInterval
)) {
567 bool Contains(const T
& aX
) const {
568 for (const auto& interval
: mIntervals
) {
569 if (interval
.Contains(aX
)) {
576 bool ContainsStrict(const T
& aX
) const {
577 for (const auto& interval
: mIntervals
) {
578 if (interval
.ContainsStrict(aX
)) {
585 bool ContainsWithStrictEnd(const T
& aX
) const {
586 for (const auto& interval
: mIntervals
) {
587 if (interval
.ContainsWithStrictEnd(aX
)) {
594 bool ContainsWithStrictEnd(const ElemType
& aInterval
) const {
595 for (const auto& interval
: mIntervals
) {
596 if (interval
.ContainsWithStrictEnd(aInterval
)) {
603 bool Intersects(const ElemType
& aInterval
) const {
604 for (const auto& interval
: mIntervals
) {
605 if (interval
.Intersects(aInterval
)) {
612 bool IntersectsStrict(const ElemType
& aInterval
) const {
613 for (const auto& interval
: mIntervals
) {
614 if (interval
.IntersectsStrict(aInterval
)) {
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
])) {
629 if (mIntervals
[i
].mEnd
< other
[j
].mEnd
) {
638 bool IntersectsWithStrictEnd(const ElemType
& aInterval
) const {
639 for (const auto& interval
: mIntervals
) {
640 if (interval
.IntersectsWithStrictEnd(aInterval
)) {
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
;
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
)) {
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::const_iterator
cbegin() const {
682 return mIntervals
.cbegin();
685 typename
ContainerType::iterator
end() { return mIntervals
.end(); }
687 typename
ContainerType::const_iterator
end() const {
688 return mIntervals
.end();
691 typename
ContainerType::const_iterator
cend() const {
692 return mIntervals
.cend();
695 ElemType
& LastInterval() {
696 MOZ_ASSERT(!mIntervals
.IsEmpty());
697 return mIntervals
.LastElement();
700 const ElemType
& LastInterval() const {
701 MOZ_ASSERT(!mIntervals
.IsEmpty());
702 return mIntervals
.LastElement();
705 void Clear() { mIntervals
.Clear(); }
708 ContainerType mIntervals
;
712 if (mIntervals
.Length() < 2) {
715 mIntervals
.Sort(CompareIntervals());
716 MergeOverlappingIntervals();
719 void MergeOverlappingIntervals() {
720 if (mIntervals
.Length() < 2) {
724 // This merges the intervals in place.
727 while (read
< mIntervals
.Length()) {
728 ElemType
current(mIntervals
[read
]);
730 while (read
< mIntervals
.Length() && current
.Touches(mIntervals
[read
])) {
731 current
= current
.Span(mIntervals
[read
]);
734 mIntervals
[write
] = current
;
737 mIntervals
.SetLength(write
);
740 struct CompareIntervals
{
741 bool Equals(const ElemType
& aT1
, const ElemType
& aT2
) const {
742 return aT1
.mStart
== aT2
.mStart
&& aT1
.mEnd
== aT2
.mEnd
;
745 bool LessThan(const ElemType
& aT1
, const ElemType
& aT2
) const {
746 return aT1
.mStart
< aT2
.mStart
;
751 // clang doesn't allow for this to be defined inline of IntervalSet.
752 template <typename T
>
753 IntervalSet
<T
> Union(const IntervalSet
<T
>& aIntervals1
,
754 const IntervalSet
<T
>& aIntervals2
) {
755 IntervalSet
<T
> intervals(aIntervals1
);
756 intervals
.Union(aIntervals2
);
760 template <typename T
>
761 IntervalSet
<T
> Intersection(const IntervalSet
<T
>& aIntervals1
,
762 const IntervalSet
<T
>& aIntervals2
) {
763 IntervalSet
<T
> intersection(aIntervals1
);
764 intersection
.Intersection(aIntervals2
);
768 } // namespace mozilla::media
770 #endif // DOM_MEDIA_INTERVALS_H_