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/. */
11 #include "mozilla/TypeTraits.h"
14 // Specialization for nsTArray CopyChooser.
20 } // namespace mozilla
23 struct nsTArray_CopyChooser
<mozilla::media::IntervalSet
<E
>> {
24 typedef nsTArray_CopyWithConstructors
<mozilla::media::IntervalSet
<E
>> Type
;
30 /* Interval defines an interval between two points. Unlike a traditional
31 interval [A,B] where A <= x <= B, the upper boundary B is exclusive: A <= x <
32 B (e.g [A,B[ or [A,B) depending on where you're living) It provides basic
33 interval arithmetic and fuzzy edges. The type T must provides a default
34 constructor and +, -, <, <= and == operators.
39 typedef Interval
<T
> SelfType
;
41 Interval() : mStart(T()), mEnd(T()), mFuzz(T()) {}
43 template <typename StartArg
, typename EndArg
>
44 Interval(StartArg
&& aStart
, EndArg
&& aEnd
)
45 : mStart(std::forward
<StartArg
>(aStart
)),
46 mEnd(std::forward
<EndArg
>(aEnd
)),
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(std::forward
<StartArg
>(aStart
)),
54 mEnd(std::forward
<EndArg
>(aEnd
)),
55 mFuzz(std::forward
<FuzzArg
>(aFuzz
)) {
56 MOZ_DIAGNOSTIC_ASSERT(mStart
<= mEnd
, "Invalid Interval");
59 Interval(const SelfType
& aOther
)
60 : mStart(aOther
.mStart
), mEnd(aOther
.mEnd
), mFuzz(aOther
.mFuzz
) {}
62 Interval(SelfType
&& aOther
)
63 : mStart(std::move(aOther
.mStart
)),
64 mEnd(std::move(aOther
.mEnd
)),
65 mFuzz(std::move(aOther
.mFuzz
)) {}
67 SelfType
& operator=(const SelfType
& aOther
) {
68 mStart
= aOther
.mStart
;
74 SelfType
& operator=(SelfType
&& aOther
) {
75 MOZ_ASSERT(&aOther
!= this, "self-moves are prohibited");
77 new (this) Interval(std::move(aOther
));
81 // Basic interval arithmetic operator definition.
82 SelfType
operator+(const SelfType
& aOther
) const {
83 return SelfType(mStart
+ aOther
.mStart
, mEnd
+ aOther
.mEnd
,
84 mFuzz
+ aOther
.mFuzz
);
87 SelfType
operator+(const T
& aVal
) const {
88 return SelfType(mStart
+ aVal
, mEnd
+ aVal
, mFuzz
);
91 SelfType
operator-(const SelfType
& aOther
) const {
92 return SelfType(mStart
- aOther
.mEnd
, mEnd
- aOther
.mStart
,
93 mFuzz
+ aOther
.mFuzz
);
96 SelfType
operator-(const T
& aVal
) const {
97 return SelfType(mStart
- aVal
, mEnd
- aVal
, mFuzz
);
100 SelfType
& operator+=(const SelfType
& aOther
) {
101 mStart
+= aOther
.mStart
;
103 mFuzz
+= aOther
.mFuzz
;
107 SelfType
& operator+=(const T
& aVal
) {
113 SelfType
& operator-=(const SelfType
& aOther
) {
114 mStart
-= aOther
.mStart
;
116 mFuzz
+= aOther
.mFuzz
;
120 SelfType
& operator-=(const T
& aVal
) {
126 bool operator==(const SelfType
& aOther
) const {
127 return mStart
== aOther
.mStart
&& mEnd
== aOther
.mEnd
;
130 bool operator!=(const SelfType
& aOther
) const { return !(*this == aOther
); }
132 bool Contains(const T
& aX
) const {
133 return mStart
- mFuzz
<= aX
&& aX
< mEnd
+ mFuzz
;
136 bool ContainsStrict(const T
& aX
) const { return mStart
<= aX
&& aX
< mEnd
; }
138 bool ContainsWithStrictEnd(const T
& aX
) const {
139 return mStart
- mFuzz
<= aX
&& aX
< mEnd
;
142 bool Contains(const SelfType
& aOther
) const {
143 return (mStart
- mFuzz
<= aOther
.mStart
+ aOther
.mFuzz
) &&
144 (aOther
.mEnd
- aOther
.mFuzz
<= mEnd
+ mFuzz
);
147 bool ContainsStrict(const SelfType
& aOther
) const {
148 return mStart
<= aOther
.mStart
&& aOther
.mEnd
<= mEnd
;
151 bool ContainsWithStrictEnd(const SelfType
& aOther
) const {
152 return (mStart
- mFuzz
<= aOther
.mStart
+ aOther
.mFuzz
) &&
156 bool Intersects(const SelfType
& aOther
) const {
157 return (mStart
- mFuzz
< aOther
.mEnd
+ aOther
.mFuzz
) &&
158 (aOther
.mStart
- aOther
.mFuzz
< mEnd
+ mFuzz
);
161 bool IntersectsStrict(const SelfType
& aOther
) const {
162 return mStart
< aOther
.mEnd
&& aOther
.mStart
< mEnd
;
165 // Same as Intersects, but including the boundaries.
166 bool Touches(const SelfType
& aOther
) const {
167 return (mStart
- mFuzz
<= aOther
.mEnd
+ aOther
.mFuzz
) &&
168 (aOther
.mStart
- aOther
.mFuzz
<= mEnd
+ mFuzz
);
171 // Returns true if aOther is strictly to the right of this and contiguous.
172 // This operation isn't commutative.
173 bool Contiguous(const SelfType
& aOther
) const {
174 return mEnd
<= aOther
.mStart
&&
175 aOther
.mStart
- mEnd
<= mFuzz
+ aOther
.mFuzz
;
178 bool RightOf(const SelfType
& aOther
) const {
179 return aOther
.mEnd
- aOther
.mFuzz
<= mStart
+ mFuzz
;
182 bool LeftOf(const SelfType
& aOther
) const {
183 return mEnd
- mFuzz
<= aOther
.mStart
+ aOther
.mFuzz
;
186 SelfType
Span(const SelfType
& aOther
) const {
190 SelfType
result(*this);
191 if (aOther
.mStart
< mStart
) {
192 result
.mStart
= aOther
.mStart
;
194 if (mEnd
< aOther
.mEnd
) {
195 result
.mEnd
= aOther
.mEnd
;
197 if (mFuzz
< aOther
.mFuzz
) {
198 result
.mFuzz
= aOther
.mFuzz
;
203 SelfType
Intersection(const SelfType
& aOther
) const {
204 const T
& s
= std::max(mStart
, aOther
.mStart
);
205 const T
& e
= std::min(mEnd
, aOther
.mEnd
);
206 const T
& f
= std::max(mFuzz
, aOther
.mFuzz
);
208 return SelfType(s
, e
, f
);
210 // Return an empty interval.
214 T
Length() const { return mEnd
- mStart
; }
216 bool IsEmpty() const { return mStart
== mEnd
; }
218 void SetFuzz(const T
& aFuzz
) { mFuzz
= aFuzz
; }
220 // Returns true if the two intervals intersect with this being on the right
222 bool TouchesOnRight(const SelfType
& aOther
) const {
223 return aOther
.mStart
<= mStart
&&
224 (mStart
- mFuzz
<= aOther
.mEnd
+ aOther
.mFuzz
) &&
225 (aOther
.mStart
- aOther
.mFuzz
<= mEnd
+ mFuzz
);
228 // Returns true if the two intervals intersect with this being on the right
229 // of aOther, ignoring fuzz.
230 bool TouchesOnRightStrict(const SelfType
& aOther
) const {
231 return aOther
.mStart
<= mStart
&& mStart
<= aOther
.mEnd
;
241 // An IntervalSet in a collection of Intervals. The IntervalSet is always
243 template <typename T
>
246 typedef IntervalSet
<T
> SelfType
;
247 typedef Interval
<T
> ElemType
;
248 typedef AutoTArray
<ElemType
, 4> ContainerType
;
249 typedef typename
ContainerType::index_type IndexType
;
252 virtual ~IntervalSet() {}
254 IntervalSet(const SelfType
& aOther
) : mIntervals(aOther
.mIntervals
) {}
256 IntervalSet(SelfType
&& aOther
) {
257 mIntervals
.AppendElements(std::move(aOther
.mIntervals
));
260 explicit IntervalSet(const ElemType
& aOther
) {
261 if (!aOther
.IsEmpty()) {
262 mIntervals
.AppendElement(aOther
);
266 explicit IntervalSet(ElemType
&& aOther
) {
267 if (!aOther
.IsEmpty()) {
268 mIntervals
.AppendElement(std::move(aOther
));
272 bool operator==(const SelfType
& aOther
) const {
273 return mIntervals
== aOther
.mIntervals
;
276 bool operator!=(const SelfType
& aOther
) const {
277 return mIntervals
!= aOther
.mIntervals
;
280 SelfType
& operator=(const SelfType
& aOther
) {
281 mIntervals
= aOther
.mIntervals
;
285 SelfType
& operator=(SelfType
&& aOther
) {
286 MOZ_ASSERT(&aOther
!= this, "self-moves are prohibited");
287 this->~IntervalSet();
288 new (this) IntervalSet(std::move(aOther
));
292 SelfType
& operator=(const ElemType
& aInterval
) {
294 if (!aInterval
.IsEmpty()) {
295 mIntervals
.AppendElement(aInterval
);
300 SelfType
& operator=(ElemType
&& aInterval
) {
302 if (!aInterval
.IsEmpty()) {
303 mIntervals
.AppendElement(std::move(aInterval
));
308 SelfType
& Add(const SelfType
& aIntervals
) {
309 if (aIntervals
.mIntervals
.Length() == 1) {
310 Add(aIntervals
.mIntervals
[0]);
312 mIntervals
.AppendElements(aIntervals
.mIntervals
);
318 SelfType
& Add(const ElemType
& aInterval
) {
319 if (aInterval
.IsEmpty()) {
322 if (mIntervals
.IsEmpty()) {
323 mIntervals
.AppendElement(aInterval
);
326 ElemType
& last
= mIntervals
.LastElement();
327 if (aInterval
.TouchesOnRight(last
)) {
328 last
= last
.Span(aInterval
);
331 // Most of our actual usage is adding an interval that will be outside the
332 // range. We can speed up normalization here.
333 if (aInterval
.RightOf(last
)) {
334 mIntervals
.AppendElement(aInterval
);
338 ContainerType normalized
;
339 ElemType
current(aInterval
);
341 for (; i
< mIntervals
.Length(); i
++) {
342 ElemType
& interval
= mIntervals
[i
];
343 if (current
.Touches(interval
)) {
344 current
= current
.Span(interval
);
345 } else if (current
.LeftOf(interval
)) {
348 normalized
.AppendElement(std::move(interval
));
351 normalized
.AppendElement(std::move(current
));
352 for (; i
< mIntervals
.Length(); i
++) {
353 normalized
.AppendElement(std::move(mIntervals
[i
]));
356 mIntervals
.AppendElements(std::move(normalized
));
361 SelfType
& operator+=(const SelfType
& aIntervals
) {
366 SelfType
& operator+=(const ElemType
& aInterval
) {
371 SelfType
operator+(const SelfType
& aIntervals
) const {
372 SelfType
intervals(*this);
373 intervals
.Add(aIntervals
);
377 SelfType
operator+(const ElemType
& aInterval
) const {
378 SelfType
intervals(*this);
379 intervals
.Add(aInterval
);
383 friend SelfType
operator+(const ElemType
& aInterval
,
384 const SelfType
& aIntervals
) {
386 intervals
.Add(aInterval
);
387 intervals
.Add(aIntervals
);
391 // Excludes an interval from an IntervalSet.
392 SelfType
& operator-=(const ElemType
& aInterval
) {
393 if (aInterval
.IsEmpty() || mIntervals
.IsEmpty()) {
396 if (mIntervals
.Length() == 1 &&
397 mIntervals
[0].TouchesOnRightStrict(aInterval
)) {
398 // Fast path when we're removing from the front of a set with a
399 // single interval. This is common for the buffered time ranges
401 if (aInterval
.mEnd
>= mIntervals
[0].mEnd
) {
402 mIntervals
.RemoveElementAt(0);
404 mIntervals
[0].mStart
= aInterval
.mEnd
;
405 mIntervals
[0].mFuzz
= std::max(mIntervals
[0].mFuzz
, aInterval
.mFuzz
);
410 // General case performed by inverting aInterval within the bounds of
411 // mIntervals and then doing the intersection.
412 T firstEnd
= std::max(mIntervals
[0].mStart
, aInterval
.mStart
);
413 T secondStart
= std::min(mIntervals
.LastElement().mEnd
, aInterval
.mEnd
);
414 ElemType
startInterval(mIntervals
[0].mStart
, firstEnd
);
415 ElemType
endInterval(secondStart
, mIntervals
.LastElement().mEnd
);
416 SelfType
intervals(std::move(startInterval
));
417 intervals
+= std::move(endInterval
);
418 return Intersection(intervals
);
421 SelfType
& operator-=(const SelfType
& aIntervals
) {
422 for (const auto& interval
: aIntervals
.mIntervals
) {
428 SelfType
operator-(const SelfType
& aInterval
) const {
429 SelfType
intervals(*this);
430 intervals
-= aInterval
;
434 SelfType
operator-(const ElemType
& aInterval
) const {
435 SelfType
intervals(*this);
436 intervals
-= aInterval
;
440 // Mutate this IntervalSet to be the union of this and aOther.
441 SelfType
& Union(const SelfType
& aOther
) {
446 SelfType
& Union(const ElemType
& aInterval
) {
451 // Mutate this TimeRange to be the intersection of this and aOther.
452 SelfType
& Intersection(const SelfType
& aOther
) {
453 ContainerType intersection
;
455 // Ensure the intersection has enough capacity to store the upper bound on
456 // the intersection size. This ensures that we don't spend time reallocating
457 // the storage as we append, at the expense of extra memory.
458 intersection
.SetCapacity(std::max(aOther
.Length(), mIntervals
.Length()));
460 const ContainerType
& other
= aOther
.mIntervals
;
461 IndexType i
= 0, j
= 0;
462 for (; i
< mIntervals
.Length() && j
< other
.Length();) {
463 if (mIntervals
[i
].IntersectsStrict(other
[j
])) {
464 intersection
.AppendElement(mIntervals
[i
].Intersection(other
[j
]));
466 if (mIntervals
[i
].mEnd
< other
[j
].mEnd
) {
472 mIntervals
= std::move(intersection
);
476 SelfType
& Intersection(const ElemType
& aInterval
) {
477 SelfType
intervals(aInterval
);
478 return Intersection(intervals
);
481 const ElemType
& operator[](IndexType aIndex
) const {
482 return mIntervals
[aIndex
];
485 // Returns the start boundary of the first interval. Or a default constructed
486 // T if IntervalSet is empty (and aExists if provided will be set to false).
487 T
GetStart(bool* aExists
= nullptr) const {
488 bool exists
= !mIntervals
.IsEmpty();
495 return mIntervals
[0].mStart
;
501 // Returns the end boundary of the last interval. Or a default constructed T
502 // if IntervalSet is empty (and aExists if provided will be set to false).
503 T
GetEnd(bool* aExists
= nullptr) const {
504 bool exists
= !mIntervals
.IsEmpty();
510 return mIntervals
.LastElement().mEnd
;
516 IndexType
Length() const { return mIntervals
.Length(); }
518 T
Start(IndexType aIndex
) const { return mIntervals
[aIndex
].mStart
; }
520 T
Start(IndexType aIndex
, bool& aExists
) const {
521 aExists
= aIndex
< mIntervals
.Length();
524 return mIntervals
[aIndex
].mStart
;
530 T
End(IndexType aIndex
) const { return mIntervals
[aIndex
].mEnd
; }
532 T
End(IndexType aIndex
, bool& aExists
) const {
533 aExists
= aIndex
< mIntervals
.Length();
536 return mIntervals
[aIndex
].mEnd
;
542 bool Contains(const ElemType
& aInterval
) const {
543 for (const auto& interval
: mIntervals
) {
544 if (interval
.Contains(aInterval
)) {
551 bool ContainsStrict(const ElemType
& aInterval
) const {
552 for (const auto& interval
: mIntervals
) {
553 if (interval
.ContainsStrict(aInterval
)) {
560 bool Contains(const T
& aX
) const {
561 for (const auto& interval
: mIntervals
) {
562 if (interval
.Contains(aX
)) {
569 bool ContainsStrict(const T
& aX
) const {
570 for (const auto& interval
: mIntervals
) {
571 if (interval
.ContainsStrict(aX
)) {
578 bool ContainsWithStrictEnd(const T
& aX
) const {
579 for (const auto& interval
: mIntervals
) {
580 if (interval
.ContainsWithStrictEnd(aX
)) {
587 bool ContainsWithStrictEnd(const ElemType
& aInterval
) const {
588 for (const auto& interval
: mIntervals
) {
589 if (interval
.ContainsWithStrictEnd(aInterval
)) {
596 bool Intersects(const ElemType
& aInterval
) const {
597 for (const auto& interval
: mIntervals
) {
598 if (interval
.Intersects(aInterval
)) {
605 bool IntersectsStrict(const ElemType
& aInterval
) const {
606 for (const auto& interval
: mIntervals
) {
607 if (interval
.IntersectsStrict(aInterval
)) {
614 bool IntersectsWithStrictEnd(const ElemType
& aInterval
) const {
615 for (const auto& interval
: mIntervals
) {
616 if (interval
.IntersectsWithStrictEnd(aInterval
)) {
623 // Shift all values by aOffset.
624 SelfType
& Shift(const T
& aOffset
) {
625 for (auto& interval
: mIntervals
) {
626 interval
.mStart
= interval
.mStart
+ aOffset
;
627 interval
.mEnd
= interval
.mEnd
+ aOffset
;
632 void SetFuzz(const T
& aFuzz
) {
633 for (auto& interval
: mIntervals
) {
634 interval
.SetFuzz(aFuzz
);
636 MergeOverlappingIntervals();
639 static const IndexType NoIndex
= IndexType(-1);
641 IndexType
Find(const T
& aValue
) const {
642 for (IndexType i
= 0; i
< mIntervals
.Length(); i
++) {
643 if (mIntervals
[i
].Contains(aValue
)) {
650 // Methods for range-based for loops.
651 typename
ContainerType::iterator
begin() { return mIntervals
.begin(); }
653 typename
ContainerType::const_iterator
begin() const {
654 return mIntervals
.begin();
657 typename
ContainerType::iterator
end() { return mIntervals
.end(); }
659 typename
ContainerType::const_iterator
end() const {
660 return mIntervals
.end();
663 ElemType
& LastInterval() {
664 MOZ_ASSERT(!mIntervals
.IsEmpty());
665 return mIntervals
.LastElement();
668 const ElemType
& LastInterval() const {
669 MOZ_ASSERT(!mIntervals
.IsEmpty());
670 return mIntervals
.LastElement();
673 void Clear() { mIntervals
.Clear(); }
676 ContainerType mIntervals
;
680 if (mIntervals
.Length() < 2) {
683 mIntervals
.Sort(CompareIntervals());
684 MergeOverlappingIntervals();
687 void MergeOverlappingIntervals() {
688 if (mIntervals
.Length() < 2) {
692 // This merges the intervals in place.
695 while (read
< mIntervals
.Length()) {
696 ElemType
current(mIntervals
[read
]);
698 while (read
< mIntervals
.Length() && current
.Touches(mIntervals
[read
])) {
699 current
= current
.Span(mIntervals
[read
]);
702 mIntervals
[write
] = current
;
705 mIntervals
.SetLength(write
);
708 struct CompareIntervals
{
709 bool Equals(const ElemType
& aT1
, const ElemType
& aT2
) const {
710 return aT1
.mStart
== aT2
.mStart
&& aT1
.mEnd
== aT2
.mEnd
;
713 bool LessThan(const ElemType
& aT1
, const ElemType
& aT2
) const {
714 return aT1
.mStart
- aT1
.mFuzz
< aT2
.mStart
+ aT2
.mFuzz
;
719 // clang doesn't allow for this to be defined inline of IntervalSet.
720 template <typename T
>
721 IntervalSet
<T
> Union(const IntervalSet
<T
>& aIntervals1
,
722 const IntervalSet
<T
>& aIntervals2
) {
723 IntervalSet
<T
> intervals(aIntervals1
);
724 intervals
.Union(aIntervals2
);
728 template <typename T
>
729 IntervalSet
<T
> Intersection(const IntervalSet
<T
>& aIntervals1
,
730 const IntervalSet
<T
>& aIntervals2
) {
731 IntervalSet
<T
> intersection(aIntervals1
);
732 intersection
.Intersection(aIntervals2
);
737 } // namespace mozilla
739 #endif // INTERVALS_H