mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1163445: Part1. Keep IntervalSet normalized. r=mattwoodrow
This is the easiest to ensure consistency when making calculations on them.
This commit is contained in:
parent
40f359ebfb
commit
4265018a9f
@ -118,7 +118,7 @@ public:
|
||||
bool Contains(const SelfType& aOther) const
|
||||
{
|
||||
return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
|
||||
(aOther.mEnd + aOther.mFuzz <= mEnd - mFuzz);
|
||||
(aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
|
||||
}
|
||||
|
||||
bool ContainsStrict(const SelfType& aOther) const
|
||||
@ -127,6 +127,13 @@ public:
|
||||
}
|
||||
|
||||
bool Intersects(const SelfType& aOther) const
|
||||
{
|
||||
return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
|
||||
(aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
|
||||
}
|
||||
|
||||
// Same as Intersects, but including the boundaries.
|
||||
bool Touches(const SelfType& aOther) const
|
||||
{
|
||||
return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
|
||||
(aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
|
||||
@ -139,6 +146,16 @@ public:
|
||||
return mEnd <= aOther.mStart && aOther.mStart - mEnd <= mFuzz + aOther.mFuzz;
|
||||
}
|
||||
|
||||
bool RightOf(const SelfType& aOther) const
|
||||
{
|
||||
return aOther.mEnd - aOther.mFuzz <= mStart + mFuzz;
|
||||
}
|
||||
|
||||
bool LeftOf(const SelfType& aOther) const
|
||||
{
|
||||
return mEnd - mFuzz <= aOther.mStart + aOther.mFuzz;
|
||||
}
|
||||
|
||||
SelfType Span(const SelfType& aOther) const
|
||||
{
|
||||
SelfType result(*this);
|
||||
@ -183,6 +200,8 @@ public:
|
||||
private:
|
||||
};
|
||||
|
||||
// An IntervalSet in a collection of Intervals. The IntervalSet is always
|
||||
// normalized.
|
||||
template<typename T>
|
||||
class IntervalSet
|
||||
{
|
||||
@ -205,18 +224,22 @@ public:
|
||||
}
|
||||
|
||||
IntervalSet(SelfType&& aOther)
|
||||
: mIntervals(Move(aOther.mIntervals))
|
||||
{
|
||||
mIntervals.MoveElementsFrom(Move(aOther.mIntervals));
|
||||
}
|
||||
|
||||
explicit IntervalSet(const ElemType& aOther)
|
||||
{
|
||||
mIntervals.AppendElement(aOther);
|
||||
if (!aOther.IsEmpty()) {
|
||||
mIntervals.AppendElement(aOther);
|
||||
}
|
||||
}
|
||||
|
||||
explicit IntervalSet(ElemType&& aOther)
|
||||
{
|
||||
mIntervals.AppendElement(Move(aOther));
|
||||
if (!aOther.IsEmpty()) {
|
||||
mIntervals.AppendElement(Move(aOther));
|
||||
}
|
||||
}
|
||||
|
||||
SelfType& operator= (const SelfType& aOther)
|
||||
@ -247,19 +270,55 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
// + and += operator will append the provided interval or intervalset.
|
||||
// Note that the result is not normalized. Call Normalize() as required.
|
||||
// Alternatively, use Union()
|
||||
|
||||
SelfType& Add(const SelfType& aIntervals)
|
||||
{
|
||||
mIntervals.AppendElements(aIntervals.mIntervals);
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SelfType& Add(const ElemType& aInterval)
|
||||
{
|
||||
mIntervals.AppendElement(aInterval);
|
||||
if (aInterval.IsEmpty()) {
|
||||
return *this;
|
||||
}
|
||||
if (mIntervals.IsEmpty()) {
|
||||
mIntervals.AppendElement(aInterval);
|
||||
return *this;
|
||||
}
|
||||
// Most of our actual usage is adding an interval that will be outside the
|
||||
// range. We can speed up normalization here.
|
||||
if (aInterval.RightOf(mIntervals.LastElement()) &&
|
||||
!aInterval.Touches(mIntervals.LastElement())) {
|
||||
mIntervals.AppendElement(aInterval);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContainerType normalized;
|
||||
ElemType current(aInterval);
|
||||
bool inserted = false;
|
||||
IndexType i = 0;
|
||||
for (; i < mIntervals.Length(); i++) {
|
||||
ElemType& interval = mIntervals[i];
|
||||
if (current.Touches(interval)) {
|
||||
current = current.Span(interval);
|
||||
} else if (current.LeftOf(interval)) {
|
||||
normalized.AppendElement(Move(current));
|
||||
inserted = true;
|
||||
break;
|
||||
} else {
|
||||
normalized.AppendElement(Move(interval));
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
normalized.AppendElement(Move(current));
|
||||
}
|
||||
for (; i < mIntervals.Length(); i++) {
|
||||
normalized.AppendElement(Move(mIntervals[i]));
|
||||
}
|
||||
mIntervals.Clear();
|
||||
mIntervals.MoveElementsFrom(Move(normalized));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -299,18 +358,15 @@ public:
|
||||
}
|
||||
|
||||
// Mutate this IntervalSet to be the union of this and aOther.
|
||||
// Resulting IntervalSet is normalized.
|
||||
SelfType& Union(const SelfType& aOther)
|
||||
{
|
||||
Add(aOther);
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SelfType& Union(const ElemType& aInterval)
|
||||
{
|
||||
Add(aInterval);
|
||||
Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -331,8 +387,8 @@ public:
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
mIntervals = intersection;
|
||||
mIntervals.Clear();
|
||||
mIntervals.MoveElementsFrom(Move(intersection));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -435,33 +491,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
void Normalize()
|
||||
{
|
||||
if (mIntervals.Length() >= 2) {
|
||||
ContainerType normalized;
|
||||
|
||||
mIntervals.Sort(CompareIntervals());
|
||||
|
||||
// This merges the intervals.
|
||||
ElemType current(mIntervals[0]);
|
||||
for (IndexType i = 1; i < mIntervals.Length(); i++) {
|
||||
if (current.Contains(mIntervals[i])) {
|
||||
continue;
|
||||
}
|
||||
if (current.Intersects(mIntervals[i])) {
|
||||
current = current.Span(mIntervals[i]);
|
||||
} else {
|
||||
normalized.AppendElement(current);
|
||||
current = mIntervals[i];
|
||||
}
|
||||
}
|
||||
|
||||
normalized.AppendElement(current);
|
||||
|
||||
mIntervals = normalized;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift all values by aOffset.
|
||||
void Shift(T aOffset)
|
||||
{
|
||||
@ -487,6 +516,31 @@ protected:
|
||||
ContainerType mIntervals;
|
||||
|
||||
private:
|
||||
void Normalize()
|
||||
{
|
||||
if (mIntervals.Length() >= 2) {
|
||||
ContainerType normalized;
|
||||
|
||||
mIntervals.Sort(CompareIntervals());
|
||||
|
||||
// This merges the intervals.
|
||||
ElemType current(mIntervals[0]);
|
||||
for (IndexType i = 1; i < mIntervals.Length(); i++) {
|
||||
ElemType& interval = mIntervals[i];
|
||||
if (current.Touches(interval)) {
|
||||
current = current.Span(interval);
|
||||
} else {
|
||||
normalized.AppendElement(Move(current));
|
||||
current = Move(interval);
|
||||
}
|
||||
}
|
||||
normalized.AppendElement(Move(current));
|
||||
|
||||
mIntervals.Clear();
|
||||
mIntervals.MoveElementsFrom(Move(normalized));
|
||||
}
|
||||
}
|
||||
|
||||
struct CompareIntervals
|
||||
{
|
||||
bool Equals(const ElemType& aT1, const ElemType& aT2) const
|
||||
|
@ -89,6 +89,10 @@ TEST(IntervalSet, TimeIntervalsConstructors)
|
||||
media::TimeIntervals blah5 = CreateTimeIntervals(start.mValue, end.mValue);
|
||||
(void)test1; (void)test2; (void)test3; (void)test4; (void)test5;
|
||||
(void)blah1; (void)blah2; (void)blah3; (void)blah4; (void)blah5;
|
||||
|
||||
media::TimeIntervals i0{media::TimeInterval(media::TimeUnit::FromSeconds(0),
|
||||
media::TimeUnit::FromSeconds(0))};
|
||||
EXPECT_EQ(0u, i0.Length()); // Constructing with an empty time interval.
|
||||
}
|
||||
|
||||
TEST(IntervalSet, Length)
|
||||
@ -97,6 +101,23 @@ TEST(IntervalSet, Length)
|
||||
EXPECT_EQ(10, i.Length());
|
||||
}
|
||||
|
||||
TEST(IntervalSet, Intersects)
|
||||
{
|
||||
EXPECT_TRUE(IntInterval(1,5).Intersects(IntInterval(3,4)));
|
||||
EXPECT_TRUE(IntInterval(1,5).Intersects(IntInterval(3,7)));
|
||||
EXPECT_TRUE(IntInterval(1,5).Intersects(IntInterval(-1,3)));
|
||||
EXPECT_TRUE(IntInterval(1,5).Intersects(IntInterval(-1,7)));
|
||||
EXPECT_FALSE(IntInterval(1,5).Intersects(IntInterval(6,7)));
|
||||
EXPECT_FALSE(IntInterval(1,5).Intersects(IntInterval(-1,0)));
|
||||
// End boundary is exclusive of the interval.
|
||||
EXPECT_FALSE(IntInterval(1,5).Intersects(IntInterval(5,7)));
|
||||
EXPECT_FALSE(IntInterval(1,5).Intersects(IntInterval(0,1)));
|
||||
// Empty identical interval do not intersect.
|
||||
EXPECT_FALSE(IntInterval(1,1).Intersects(IntInterval(1,1)));
|
||||
// Empty interval do not intersect.
|
||||
EXPECT_FALSE(IntInterval(1,1).Intersects(IntInterval(2,2)));
|
||||
}
|
||||
|
||||
TEST(IntervalSet, Intersection)
|
||||
{
|
||||
IntInterval i0(10, 20);
|
||||
@ -104,6 +125,14 @@ TEST(IntervalSet, Intersection)
|
||||
IntInterval i = i0.Intersection(i1);
|
||||
EXPECT_EQ(15, i.mStart);
|
||||
EXPECT_EQ(20, i.mEnd);
|
||||
IntInterval j0(10, 20);
|
||||
IntInterval j1(20, 25);
|
||||
IntInterval j = j0.Intersection(j1);
|
||||
EXPECT_TRUE(j.IsEmpty());
|
||||
IntInterval k0(2, 2);
|
||||
IntInterval k1(2, 2);
|
||||
IntInterval k = k0.Intersection(k1);
|
||||
EXPECT_TRUE(k.IsEmpty());
|
||||
}
|
||||
|
||||
TEST(IntervalSet, Equals)
|
||||
@ -150,46 +179,19 @@ TEST(IntervalSet, IntersectionIntervalSet)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void Compare(media::IntervalSet<T> aI1, media::IntervalSet<T> aI2)
|
||||
static void Compare(const media::IntervalSet<T>& aI1,
|
||||
const media::IntervalSet<T>& aI2)
|
||||
{
|
||||
media::IntervalSet<T> i1(aI1);
|
||||
media::IntervalSet<T> i2(aI1);
|
||||
EXPECT_EQ(i1.Length(), i2.Length());
|
||||
if (i1.Length() != i2.Length()) {
|
||||
EXPECT_EQ(aI1.Length(), aI2.Length());
|
||||
if (aI1.Length() != aI2.Length()) {
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < i1.Length(); i++) {
|
||||
EXPECT_EQ(i1[i].mStart, i2[i].mStart);
|
||||
EXPECT_EQ(i1[i].mEnd, i2[i].mEnd);
|
||||
for (uint32_t i = 0; i < aI1.Length(); i++) {
|
||||
EXPECT_EQ(aI1[i].mStart, aI2[i].mStart);
|
||||
EXPECT_EQ(aI1[i].mEnd, aI2[i].mEnd);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IntervalSet, IntersectionNormalizedIntervalSet)
|
||||
{
|
||||
media::IntervalSet<int> i0;
|
||||
i0 += IntInterval(5, 10);
|
||||
i0 += IntInterval(8, 25);
|
||||
i0 += IntInterval(24, 60);
|
||||
|
||||
media::IntervalSet<int> i1;
|
||||
i1.Add(IntInterval(7, 15));
|
||||
i1.Add(IntInterval(10, 27));
|
||||
i1.Add(IntInterval(45, 50));
|
||||
i1.Add(IntInterval(53, 57));
|
||||
|
||||
// Compare intersections to ensure an intersection of normalized intervalsets
|
||||
// is equal to the intersection of non-normalized intervalsets.
|
||||
media::IntervalSet<int> intersection = media::Intersection(i0, i1);
|
||||
|
||||
media::IntervalSet<int> i0_normalize(i0);
|
||||
i0_normalize.Normalize();
|
||||
media::IntervalSet<int> i1_normalize(i1);
|
||||
i1_normalize.Normalize();
|
||||
media::IntervalSet<int> intersection_normalize =
|
||||
media::Intersection(i0_normalize, i1_normalize);
|
||||
Compare(intersection, intersection_normalize);
|
||||
}
|
||||
|
||||
static void GeneratePermutations(media::IntervalSet<int> aI1,
|
||||
media::IntervalSet<int> aI2)
|
||||
{
|
||||
@ -211,17 +213,20 @@ static void GeneratePermutations(media::IntervalSet<int> aI1,
|
||||
for (uint32_t i = 0; i < comb1.size(); i++) {
|
||||
i_0 += aI1[comb1[i]];
|
||||
}
|
||||
// Test that intervals are always normalized.
|
||||
Compare(aI1, i_0);
|
||||
media::IntervalSet<int> i_1;
|
||||
for (uint32_t i = 0; i < comb2.size(); i++) {
|
||||
i_1 += aI2[comb2[i]];
|
||||
}
|
||||
Compare(aI2, i_1);
|
||||
// Check intersections yield the same result.
|
||||
Compare(i_0.Intersection(i_1), i_ref);
|
||||
} while (std::next_permutation(comb2.begin(), comb2.end()));
|
||||
} while (std::next_permutation(comb1.begin(), comb1.end()));
|
||||
}
|
||||
|
||||
TEST(IntervalSet, IntersectionUnorderedIntervalSet)
|
||||
TEST(IntervalSet, IntersectionNormalizedIntervalSet)
|
||||
{
|
||||
media::IntervalSet<int> i0;
|
||||
i0 += IntInterval(5, 10);
|
||||
@ -270,21 +275,27 @@ TEST(IntervalSet, Normalize)
|
||||
i = IntInterval(1, 8) + i;
|
||||
media::IntervalSet<int> interval;
|
||||
interval += IntInterval(5, 10);
|
||||
// Test += with move.
|
||||
// Test += with rval move.
|
||||
i += Duplicate(interval);
|
||||
// Test = with move and add with move.
|
||||
i = Duplicate(interval) + i;
|
||||
|
||||
media::IntervalSet<int> o(i);
|
||||
o.Normalize();
|
||||
EXPECT_EQ(2u, i.Length());
|
||||
|
||||
EXPECT_EQ(2u, o.Length());
|
||||
EXPECT_EQ(1, i[0].mStart);
|
||||
EXPECT_EQ(10, i[0].mEnd);
|
||||
|
||||
EXPECT_EQ(1, o[0].mStart);
|
||||
EXPECT_EQ(10, o[0].mEnd);
|
||||
EXPECT_EQ(20, i[1].mStart);
|
||||
EXPECT_EQ(30, i[1].mEnd);
|
||||
|
||||
EXPECT_EQ(20, o[1].mStart);
|
||||
EXPECT_EQ(30, o[1].mEnd);
|
||||
media::TimeIntervals ti;
|
||||
ti += media::TimeInterval(media::TimeUnit::FromSeconds(0.0),
|
||||
media::TimeUnit::FromSeconds(3.203333));
|
||||
ti += media::TimeInterval(media::TimeUnit::FromSeconds(3.203366),
|
||||
media::TimeUnit::FromSeconds(10.010065));
|
||||
EXPECT_EQ(2u, ti.Length());
|
||||
ti += media::TimeInterval(ti.Start(0), ti.End(0), media::TimeUnit::FromMicroseconds(35000));
|
||||
EXPECT_EQ(1u, ti.Length());
|
||||
}
|
||||
|
||||
TEST(IntervalSet, Union)
|
||||
@ -347,7 +358,6 @@ TEST(IntervalSet, NormalizeFuzz)
|
||||
i0 += IntInterval(11, 25, 0);
|
||||
i0 += IntInterval(5, 10, 1);
|
||||
i0 += IntInterval(40, 60, 1);
|
||||
i0.Normalize();
|
||||
|
||||
EXPECT_EQ(2u, i0.Length());
|
||||
|
||||
@ -381,7 +391,6 @@ TEST(IntervalSet, UnionFuzz)
|
||||
EXPECT_EQ(40, i[1].mStart);
|
||||
EXPECT_EQ(60, i[1].mEnd);
|
||||
|
||||
i0.Normalize();
|
||||
EXPECT_EQ(2u, i0.Length());
|
||||
EXPECT_EQ(5, i0[0].mStart);
|
||||
EXPECT_EQ(25, i0[0].mEnd);
|
||||
@ -421,28 +430,19 @@ TEST(IntervalSet, TimeRangesSeconds)
|
||||
EXPECT_EQ(tr->End(index, rv), i[index].mEnd.ToSeconds());
|
||||
EXPECT_EQ(tr->End(index, rv), i.End(index).ToSeconds());
|
||||
}
|
||||
|
||||
i.Normalize();
|
||||
tr->Normalize();
|
||||
EXPECT_EQ(tr->Length(), i.Length());
|
||||
for (dom::TimeRanges::index_type index = 0; index < tr->Length(); index++) {
|
||||
ErrorResult rv;
|
||||
EXPECT_EQ(tr->Start(index, rv), i[index].mStart.ToSeconds());
|
||||
EXPECT_EQ(tr->Start(index, rv), i.Start(index).ToSeconds());
|
||||
EXPECT_EQ(tr->End(index, rv), i[index].mEnd.ToSeconds());
|
||||
EXPECT_EQ(tr->End(index, rv), i.End(index).ToSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckTimeRanges(dom::TimeRanges* aTr, const media::TimeIntervals& aTi)
|
||||
{
|
||||
EXPECT_EQ(aTr->Length(), aTi.Length());
|
||||
for (dom::TimeRanges::index_type i = 0; i < aTr->Length(); i++) {
|
||||
nsRefPtr<dom::TimeRanges> tr = new dom::TimeRanges;
|
||||
tr->Union(aTr, 0); // This will normalize the time range.
|
||||
EXPECT_EQ(tr->Length(), aTi.Length());
|
||||
for (dom::TimeRanges::index_type i = 0; i < tr->Length(); i++) {
|
||||
ErrorResult rv;
|
||||
EXPECT_EQ(aTr->Start(i, rv), aTi[i].mStart.ToSeconds());
|
||||
EXPECT_EQ(aTr->Start(i, rv), aTi.Start(i).ToSeconds());
|
||||
EXPECT_EQ(aTr->End(i, rv), aTi[i].mEnd.ToSeconds());
|
||||
EXPECT_EQ(aTr->End(i, rv), aTi.End(i).ToSeconds());
|
||||
EXPECT_EQ(tr->Start(i, rv), aTi[i].mStart.ToSeconds());
|
||||
EXPECT_EQ(tr->Start(i, rv), aTi.Start(i).ToSeconds());
|
||||
EXPECT_EQ(tr->End(i, rv), aTi[i].mEnd.ToSeconds());
|
||||
EXPECT_EQ(tr->End(i, rv), aTi.End(i).ToSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,11 +469,6 @@ TEST(IntervalSet, TimeRangesConversion)
|
||||
i3 = tr;
|
||||
CheckTimeRanges(tr, i3);
|
||||
|
||||
i1.Normalize();
|
||||
tr->Normalize();
|
||||
|
||||
CheckTimeRanges(tr, i1);
|
||||
|
||||
// operator= test
|
||||
i1 = tr.get();
|
||||
CheckTimeRanges(tr, i1);
|
||||
@ -511,7 +506,6 @@ TEST(IntervalSet, TimeRangesMicroseconds)
|
||||
EXPECT_EQ(tr->End(index, rv), i.End(index).ToSeconds());
|
||||
}
|
||||
|
||||
i.Normalize();
|
||||
tr->Normalize();
|
||||
EXPECT_EQ(tr->Length(), i.Length());
|
||||
for (dom::TimeRanges::index_type index = 0; index < tr->Length(); index++) {
|
||||
@ -583,8 +577,6 @@ TEST(IntervalSet, FooIntervalSet)
|
||||
is.Add(i);
|
||||
is = is + i;
|
||||
is = i + is;
|
||||
EXPECT_EQ(5u, is.Length());
|
||||
is.Normalize();
|
||||
EXPECT_EQ(1u, is.Length());
|
||||
EXPECT_EQ(Foo<int>(), is[0].mStart);
|
||||
EXPECT_EQ(Foo<int>(4,5,6), is[0].mEnd);
|
||||
|
Loading…
Reference in New Issue
Block a user