Remove PODIntervalTree and machinery.

This was for floats and shapes which we removed.

R=ojan@chromium.org

Review URL: https://codereview.chromium.org/705373003
This commit is contained in:
Elliott Sprehn 2014-11-07 17:01:02 -08:00
parent d11b95703b
commit e9073fbdc0
15 changed files with 0 additions and 3225 deletions

View File

@ -26,7 +26,6 @@
#include "core/rendering/LayoutState.h"
#include "core/rendering/PaintInvalidationState.h"
#include "core/rendering/RenderBlockFlow.h"
#include "platform/PODFreeListArena.h"
#include "platform/scroll/ScrollableArea.h"
#include "wtf/OwnPtr.h"

View File

@ -128,11 +128,6 @@ component("platform") {
"MIMETypeRegistry.h",
"NotImplemented.cpp",
"NotImplemented.h",
"PODArena.h",
"PODFreeListArena.h",
"PODInterval.h",
"PODIntervalTree.h",
"PODRedBlackTree.h",
"ParsingUtilities.h",
"Partitions.cpp",
"Partitions.h",
@ -289,8 +284,6 @@ component("platform") {
"fonts/skia/FontPlatformDataSkia.cpp",
"fonts/skia/SimpleFontDataSkia.cpp",
"geometry/FloatBoxExtent.h",
"geometry/FloatPolygon.cpp",
"geometry/FloatPolygon.h",
"geometry/FloatPoint.cpp",
"geometry/FloatPoint.h",
"geometry/FloatPoint3D.cpp",
@ -717,10 +710,6 @@ test("platform_unittests") {
"DecimalTest.cpp",
"LayoutUnitTest.cpp",
"LifecycleContextTest.cpp",
"PODArenaTest.cpp",
"PODFreeListArenaTest.cpp",
"PODIntervalTreeTest.cpp",
"PODRedBlackTreeTest.cpp",
"PurgeableVectorTest.cpp",
"SharedBufferTest.cpp",
"TestingPlatformSupport.cpp",
@ -737,7 +726,6 @@ test("platform_unittests") {
"fonts/android/FontCacheAndroidTest.cpp",
"geometry/FloatBoxTest.cpp",
"geometry/FloatBoxTestHelpers.cpp",
"geometry/FloatPolygonTest.cpp",
"geometry/FloatRoundedRectTest.cpp",
"geometry/RegionTest.cpp",
"geometry/RoundedRectTest.cpp",
@ -749,7 +737,6 @@ test("platform_unittests") {
"image-decoders/ImageDecoderTest.cpp",
"mac/ScrollElasticityControllerTest.mm",
"network/HTTPParsersTest.cpp",
"testing/ArenaTestHelpers.h",
"testing/TreeTestHelpers.cpp",
"testing/TreeTestHelpers.h",
"testing/RunAllTests.cpp",

View File

@ -1,200 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PODArena_h
#define PODArena_h
#include <stdint.h>
#include "wtf/Assertions.h"
#include "wtf/FastMalloc.h"
#include "wtf/Noncopyable.h"
#include "wtf/OwnPtr.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/RefCounted.h"
#include "wtf/Vector.h"
namespace blink {
// An arena which allocates only Plain Old Data (POD), or classes and
// structs bottoming out in Plain Old Data. NOTE: the constructors of
// the objects allocated in this arena are called, but _not_ their
// destructors.
class PODArena final : public RefCounted<PODArena> {
public:
// The arena is configured with an allocator, which is responsible
// for allocating and freeing chunks of memory at a time.
class Allocator : public RefCounted<Allocator> {
public:
virtual void* allocate(size_t size) = 0;
virtual void free(void* ptr) = 0;
protected:
virtual ~Allocator() { }
friend class WTF::RefCounted<Allocator>;
};
// The Arena's default allocator, which uses fastMalloc and
// fastFree to allocate chunks of storage.
class FastMallocAllocator : public Allocator {
public:
static PassRefPtr<FastMallocAllocator> create()
{
return adoptRef(new FastMallocAllocator);
}
virtual void* allocate(size_t size) override { return fastMalloc(size); }
virtual void free(void* ptr) override { fastFree(ptr); }
protected:
FastMallocAllocator() { }
};
// Creates a new PODArena configured with a FastMallocAllocator.
static PassRefPtr<PODArena> create()
{
return adoptRef(new PODArena);
}
// Creates a new PODArena configured with the given Allocator.
static PassRefPtr<PODArena> create(PassRefPtr<Allocator> allocator)
{
return adoptRef(new PODArena(allocator));
}
// Allocates an object from the arena.
template<class T> T* allocateObject()
{
return new (allocateBase<T>()) T();
}
// Allocates an object from the arena, calling a single-argument constructor.
template<class T, class Argument1Type> T* allocateObject(const Argument1Type& argument1)
{
return new (allocateBase<T>()) T(argument1);
}
// The initial size of allocated chunks; increases as necessary to
// satisfy large allocations. Mainly public for unit tests.
enum {
DefaultChunkSize = 16384
};
protected:
friend class WTF::RefCounted<PODArena>;
PODArena()
: m_allocator(FastMallocAllocator::create())
, m_current(0)
, m_currentChunkSize(DefaultChunkSize) { }
explicit PODArena(PassRefPtr<Allocator> allocator)
: m_allocator(allocator)
, m_current(0)
, m_currentChunkSize(DefaultChunkSize) { }
// Returns the alignment requirement for classes and structs on the
// current platform.
template <class T> static size_t minAlignment()
{
return WTF_ALIGN_OF(T);
}
template<class T> void* allocateBase()
{
void* ptr = 0;
size_t roundedSize = roundUp(sizeof(T), minAlignment<T>());
if (m_current)
ptr = m_current->allocate(roundedSize);
if (!ptr) {
if (roundedSize > m_currentChunkSize)
m_currentChunkSize = roundedSize;
m_chunks.append(adoptPtr(new Chunk(m_allocator.get(), m_currentChunkSize)));
m_current = m_chunks.last().get();
ptr = m_current->allocate(roundedSize);
}
return ptr;
}
// Rounds up the given allocation size to the specified alignment.
size_t roundUp(size_t size, size_t alignment)
{
ASSERT(!(alignment % 2));
return (size + alignment - 1) & ~(alignment - 1);
}
// Manages a chunk of memory and individual allocations out of it.
class Chunk final {
WTF_MAKE_NONCOPYABLE(Chunk);
public:
// Allocates a block of memory of the given size from the passed
// Allocator.
Chunk(Allocator* allocator, size_t size)
: m_allocator(allocator)
, m_size(size)
, m_currentOffset(0)
{
m_base = static_cast<uint8_t*>(m_allocator->allocate(size));
}
// Frees the memory allocated from the Allocator in the
// constructor.
~Chunk()
{
m_allocator->free(m_base);
}
// Returns a pointer to "size" bytes of storage, or 0 if this
// Chunk could not satisfy the allocation.
void* allocate(size_t size)
{
// Check for overflow
if (m_currentOffset + size < m_currentOffset)
return 0;
if (m_currentOffset + size > m_size)
return 0;
void* result = m_base + m_currentOffset;
m_currentOffset += size;
return result;
}
protected:
Allocator* m_allocator;
uint8_t* m_base;
size_t m_size;
size_t m_currentOffset;
};
RefPtr<Allocator> m_allocator;
Chunk* m_current;
size_t m_currentChunkSize;
Vector<OwnPtr<Chunk> > m_chunks;
};
} // namespace blink
#endif // PODArena_h

View File

@ -1,105 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "platform/PODArena.h"
#include "platform/testing/ArenaTestHelpers.h"
#include "wtf/FastMalloc.h"
#include "wtf/RefPtr.h"
#include <algorithm>
#include <gtest/gtest.h>
namespace blink {
using ArenaTestHelpers::TrackedAllocator;
namespace {
// A couple of simple structs to allocate.
struct TestClass1 {
TestClass1()
: x(0), y(0), z(0), w(1) { }
float x, y, z, w;
};
struct TestClass2 {
TestClass2()
: a(1), b(2), c(3), d(4) { }
float a, b, c, d;
};
} // anonymous namespace
class PODArenaTest : public testing::Test {
};
// Make sure the arena can successfully allocate from more than one
// region.
TEST_F(PODArenaTest, CanAllocateFromMoreThanOneRegion)
{
RefPtr<TrackedAllocator> allocator = TrackedAllocator::create();
RefPtr<PODArena> arena = PODArena::create(allocator);
int numIterations = 10 * PODArena::DefaultChunkSize / sizeof(TestClass1);
for (int i = 0; i < numIterations; ++i)
arena->allocateObject<TestClass1>();
EXPECT_GT(allocator->numRegions(), 1);
}
// Make sure the arena frees all allocated regions during destruction.
TEST_F(PODArenaTest, FreesAllAllocatedRegions)
{
RefPtr<TrackedAllocator> allocator = TrackedAllocator::create();
{
RefPtr<PODArena> arena = PODArena::create(allocator);
for (int i = 0; i < 3; i++)
arena->allocateObject<TestClass1>();
EXPECT_GT(allocator->numRegions(), 0);
}
EXPECT_TRUE(allocator->isEmpty());
}
// Make sure the arena runs constructors of the objects allocated within.
TEST_F(PODArenaTest, RunsConstructors)
{
RefPtr<PODArena> arena = PODArena::create();
for (int i = 0; i < 10000; i++) {
TestClass1* tc1 = arena->allocateObject<TestClass1>();
EXPECT_EQ(0, tc1->x);
EXPECT_EQ(0, tc1->y);
EXPECT_EQ(0, tc1->z);
EXPECT_EQ(1, tc1->w);
TestClass2* tc2 = arena->allocateObject<TestClass2>();
EXPECT_EQ(1, tc2->a);
EXPECT_EQ(2, tc2->b);
EXPECT_EQ(3, tc2->c);
EXPECT_EQ(4, tc2->d);
}
}
} // namespace blink

View File

@ -1,130 +0,0 @@
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PODFreeListArena_h
#define PODFreeListArena_h
#include "platform/PODArena.h"
namespace blink {
template <class T>
class PODFreeListArena : public RefCounted<PODFreeListArena<T> > {
public:
static PassRefPtr<PODFreeListArena> create()
{
return adoptRef(new PODFreeListArena);
}
// Creates a new PODFreeListArena configured with the given Allocator.
static PassRefPtr<PODFreeListArena> create(PassRefPtr<PODArena::Allocator> allocator)
{
return adoptRef(new PODFreeListArena(allocator));
}
// Allocates an object from the arena.
T* allocateObject()
{
void* ptr = allocateFromFreeList();
if (ptr) {
// Use placement operator new to allocate a T at this location.
new(ptr) T();
return static_cast<T*>(ptr);
}
// PODArena::allocateObject calls T's constructor.
return static_cast<T*>(m_arena->allocateObject<T>());
}
template<class Argument1Type> T* allocateObject(const Argument1Type& argument1)
{
void* ptr = allocateFromFreeList();
if (ptr) {
// Use placement operator new to allocate a T at this location.
new(ptr) T(argument1);
return static_cast<T*>(ptr);
}
// PODArena::allocateObject calls T's constructor.
return static_cast<T*>(m_arena->allocateObject<T>(argument1));
}
void freeObject(T* ptr)
{
FixedSizeMemoryChunk* oldFreeList = m_freeList;
m_freeList = reinterpret_cast<FixedSizeMemoryChunk*>(ptr);
m_freeList->next = oldFreeList;
}
private:
PODFreeListArena()
: m_arena(PODArena::create()), m_freeList(0) { }
explicit PODFreeListArena(PassRefPtr<PODArena::Allocator> allocator)
: m_arena(PODArena::create(allocator)), m_freeList(0) { }
~PODFreeListArena() { }
void* allocateFromFreeList()
{
if (m_freeList) {
void* memory = m_freeList;
m_freeList = m_freeList->next;
return memory;
}
return 0;
}
int getFreeListSizeForTesting() const
{
int total = 0;
for (FixedSizeMemoryChunk* cur = m_freeList; cur; cur = cur->next) {
total++;
}
return total;
}
RefPtr<PODArena> m_arena;
// This free list contains pointers within every chunk that's been allocated so
// far. None of the individual chunks can be freed until the arena is
// destroyed.
struct FixedSizeMemoryChunk {
FixedSizeMemoryChunk* next;
};
FixedSizeMemoryChunk* m_freeList;
COMPILE_ASSERT(sizeof(T) >= sizeof(FixedSizeMemoryChunk), PODFreeListArena_type_should_be_larger);
friend class WTF::RefCounted<PODFreeListArena>;
friend class PODFreeListArenaTest;
};
} // namespace blink
#endif

View File

@ -1,170 +0,0 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "platform/PODFreeListArena.h"
#include "platform/testing/ArenaTestHelpers.h"
#include "wtf/FastMalloc.h"
#include "wtf/RefPtr.h"
#include <gtest/gtest.h>
namespace blink {
using ArenaTestHelpers::TrackedAllocator;
namespace {
// A couple of simple structs to allocate.
struct TestClass1 {
TestClass1()
: x(0), y(0), z(0), w(1) { }
float x, y, z, w;
};
struct TestClass2 {
TestClass2()
: padding(0)
{
static int TestIds = 0;
id = TestIds++;
}
int id;
int padding;
};
} // anonymous namespace
class PODFreeListArenaTest : public testing::Test {
protected:
int getFreeListSize(const PassRefPtr<PODFreeListArena<TestClass1> > arena) const
{
return arena->getFreeListSizeForTesting();
}
};
// Make sure the arena can successfully allocate from more than one
// region.
TEST_F(PODFreeListArenaTest, CanAllocateFromMoreThanOneRegion)
{
RefPtr<TrackedAllocator> allocator = TrackedAllocator::create();
RefPtr<PODFreeListArena<TestClass1> > arena = PODFreeListArena<TestClass1>::create(allocator);
int numIterations = 10 * PODArena::DefaultChunkSize / sizeof(TestClass1);
for (int i = 0; i < numIterations; ++i)
arena->allocateObject();
EXPECT_GT(allocator->numRegions(), 1);
}
// Make sure the arena frees all allocated regions during destruction.
TEST_F(PODFreeListArenaTest, FreesAllAllocatedRegions)
{
RefPtr<TrackedAllocator> allocator = TrackedAllocator::create();
{
RefPtr<PODFreeListArena<TestClass1> > arena = PODFreeListArena<TestClass1>::create(allocator);
for (int i = 0; i < 3; i++)
arena->allocateObject();
EXPECT_GT(allocator->numRegions(), 0);
}
EXPECT_TRUE(allocator->isEmpty());
}
// Make sure the arena runs constructors of the objects allocated within.
TEST_F(PODFreeListArenaTest, RunsConstructorsOnNewObjects)
{
RefPtr<PODFreeListArena<TestClass1> > arena = PODFreeListArena<TestClass1>::create();
for (int i = 0; i < 10000; i++) {
TestClass1* tc1 = arena->allocateObject();
EXPECT_EQ(0, tc1->x);
EXPECT_EQ(0, tc1->y);
EXPECT_EQ(0, tc1->z);
EXPECT_EQ(1, tc1->w);
}
}
// Make sure the arena runs constructors of the objects allocated within.
TEST_F(PODFreeListArenaTest, RunsConstructorsOnReusedObjects)
{
std::set<TestClass1*> objects;
RefPtr<PODFreeListArena<TestClass1> > arena = PODFreeListArena<TestClass1>::create();
for (int i = 0; i < 100; i++) {
TestClass1* tc1 = arena->allocateObject();
tc1->x = 100;
tc1->y = 101;
tc1->z = 102;
tc1->w = 103;
objects.insert(tc1);
}
for (std::set<TestClass1*>::iterator it = objects.begin(); it != objects.end(); ++it) {
arena->freeObject(*it);
}
for (int i = 0; i < 100; i++) {
TestClass1* cur = arena->allocateObject();
EXPECT_TRUE(objects.find(cur) != objects.end());
EXPECT_EQ(0, cur->x);
EXPECT_EQ(0, cur->y);
EXPECT_EQ(0, cur->z);
EXPECT_EQ(1, cur->w);
objects.erase(cur);
}
}
// Make sure freeObject puts the object in the free list.
TEST_F(PODFreeListArenaTest, AddsFreedObjectsToFreedList)
{
std::vector<TestClass1*> objects;
RefPtr<PODFreeListArena<TestClass1> > arena = PODFreeListArena<TestClass1>::create();
for (int i = 0; i < 100; i++) {
objects.push_back(arena->allocateObject());
}
for (std::vector<TestClass1*>::iterator it = objects.begin(); it != objects.end(); ++it) {
arena->freeObject(*it);
}
EXPECT_EQ(100, getFreeListSize(arena));
}
// Make sure allocations use previously freed memory.
TEST_F(PODFreeListArenaTest, ReusesPreviouslyFreedObjects)
{
std::set<TestClass2*> objects;
RefPtr<PODFreeListArena<TestClass2> > arena = PODFreeListArena<TestClass2>::create();
for (int i = 0; i < 100; i++) {
objects.insert(arena->allocateObject());
}
for (std::set<TestClass2*>::iterator it = objects.begin(); it != objects.end(); ++it) {
arena->freeObject(*it);
}
for (int i = 0; i < 100; i++) {
TestClass2* cur = arena->allocateObject();
EXPECT_TRUE(objects.find(cur) != objects.end());
EXPECT_TRUE(cur->id >= 100 && cur->id < 200);
objects.erase(cur);
}
}
} // namespace blink

View File

@ -1,163 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PODInterval_h
#define PODInterval_h
#ifndef NDEBUG
#include "wtf/text/StringBuilder.h"
#endif
namespace blink {
// Class representing a closed interval which can hold an arbitrary
// Plain Old Datatype (POD) as its endpoints and a piece of user
// data. An important characteristic for the algorithms we use is that
// if two intervals have identical endpoints but different user data,
// they are not considered to be equal. This situation can arise when
// representing the vertical extents of bounding boxes of overlapping
// triangles, where the pointer to the triangle is the user data of
// the interval.
//
// *Note* that the destructors of type T and UserData will *not* be
// called by this class. They must not allocate any memory that is
// required to be cleaned up in their destructors.
//
// The following constructors and operators must be implemented on
// type T:
//
// - Copy constructor (if user data is desired)
// - operator<
// - operator==
// - operator=
//
// If the UserData type is specified, it must support a copy
// constructor and assignment operator.
//
// In debug mode, printing of intervals and the data they contain is
// enabled. This requires the following template specializations to be
// available:
//
// template<> struct ValueToString<T> {
// static String string(const T& t);
// };
// template<> struct ValueToString<UserData> {
// static String string(const UserData& t);
// };
//
// Note that this class requires a copy constructor and assignment
// operator in order to be stored in the red-black tree.
#ifndef NDEBUG
template<class T>
struct ValueToString;
#endif
template<class T, class UserData = void*>
class PODInterval {
public:
// Constructor from endpoints. This constructor only works when the
// UserData type is a pointer or other type which can be initialized
// with 0.
PODInterval(const T& low, const T& high)
: m_low(low)
, m_high(high)
, m_data(0)
, m_maxHigh(high)
{
}
// Constructor from two endpoints plus explicit user data.
PODInterval(const T& low, const T& high, const UserData data)
: m_low(low)
, m_high(high)
, m_data(data)
, m_maxHigh(high)
{
}
const T& low() const { return m_low; }
const T& high() const { return m_high; }
const UserData& data() const { return m_data; }
bool overlaps(const T& low, const T& high) const
{
if (this->high() < low)
return false;
if (high < this->low())
return false;
return true;
}
bool overlaps(const PODInterval& other) const
{
return overlaps(other.low(), other.high());
}
// Returns true if this interval is "less" than the other. The
// comparison is performed on the low endpoints of the intervals.
bool operator<(const PODInterval& other) const
{
return low() < other.low();
}
// Returns true if this interval is strictly equal to the other,
// including comparison of the user data.
bool operator==(const PODInterval& other) const
{
return (low() == other.low() && high() == other.high() && data() == other.data());
}
const T& maxHigh() const { return m_maxHigh; }
void setMaxHigh(const T& maxHigh) { m_maxHigh = maxHigh; }
#ifndef NDEBUG
// Support for printing PODIntervals.
String toString() const
{
StringBuilder builder;
builder.appendLiteral("[PODInterval (");
builder.append(ValueToString<T>::string(low()));
builder.appendLiteral(", ");
builder.append(ValueToString<T>::string(high()));
builder.appendLiteral("), data=");
builder.append(ValueToString<UserData>::string(data()));
builder.appendLiteral(", maxHigh=");
builder.append(ValueToString<T>::string(maxHigh()));
builder.append(']');
return builder.toString();
}
#endif
private:
T m_low;
T m_high;
UserData m_data;
T m_maxHigh;
};
} // namespace blink
#endif // PODInterval_h

View File

@ -1,267 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PODIntervalTree_h
#define PODIntervalTree_h
#include "platform/PODArena.h"
#include "platform/PODInterval.h"
#include "platform/PODRedBlackTree.h"
#include "wtf/Assertions.h"
#include "wtf/Noncopyable.h"
#include "wtf/Vector.h"
namespace blink {
#ifndef NDEBUG
template<class T>
struct ValueToString;
#endif
template <class T, class UserData = void*>
class PODIntervalSearchAdapter {
public:
typedef PODInterval<T, UserData> IntervalType;
PODIntervalSearchAdapter(Vector<IntervalType>& result, const T& lowValue, const T& highValue)
: m_result(result)
, m_lowValue(lowValue)
, m_highValue(highValue)
{
}
const T& lowValue() const { return m_lowValue; }
const T& highValue() const { return m_highValue; }
void collectIfNeeded(const IntervalType& data) const
{
if (data.overlaps(m_lowValue, m_highValue))
m_result.append(data);
}
private:
Vector<IntervalType>& m_result;
T m_lowValue;
T m_highValue;
};
// An interval tree, which is a form of augmented red-black tree. It
// supports efficient (O(lg n)) insertion, removal and querying of
// intervals in the tree.
template<class T, class UserData = void*>
class PODIntervalTree final : public PODRedBlackTree<PODInterval<T, UserData> > {
WTF_MAKE_NONCOPYABLE(PODIntervalTree);
public:
// Typedef to reduce typing when declaring intervals to be stored in
// this tree.
typedef PODInterval<T, UserData> IntervalType;
typedef PODIntervalSearchAdapter<T, UserData> IntervalSearchAdapterType;
PODIntervalTree(UninitializedTreeEnum unitializedTree)
: PODRedBlackTree<IntervalType>(unitializedTree)
{
init();
}
PODIntervalTree()
: PODRedBlackTree<IntervalType>()
{
init();
}
explicit PODIntervalTree(PassRefPtr<PODArena> arena)
: PODRedBlackTree<IntervalType>(arena)
{
init();
}
// Returns all intervals in the tree which overlap the given query
// interval. The returned intervals are sorted by increasing low
// endpoint.
Vector<IntervalType> allOverlaps(const IntervalType& interval) const
{
Vector<IntervalType> result;
allOverlaps(interval, result);
return result;
}
// Returns all intervals in the tree which overlap the given query
// interval. The returned intervals are sorted by increasing low
// endpoint.
void allOverlaps(const IntervalType& interval, Vector<IntervalType>& result) const
{
// Explicit dereference of "this" required because of
// inheritance rules in template classes.
IntervalSearchAdapterType adapter(result, interval.low(), interval.high());
searchForOverlapsFrom<IntervalSearchAdapterType>(this->root(), adapter);
}
template <class AdapterType>
void allOverlapsWithAdapter(AdapterType& adapter) const
{
// Explicit dereference of "this" required because of
// inheritance rules in template classes.
searchForOverlapsFrom<AdapterType>(this->root(), adapter);
}
// Helper to create interval objects.
static IntervalType createInterval(const T& low, const T& high, const UserData data = 0)
{
return IntervalType(low, high, data);
}
virtual bool checkInvariants() const override
{
if (!PODRedBlackTree<IntervalType>::checkInvariants())
return false;
if (!this->root())
return true;
return checkInvariantsFromNode(this->root(), 0);
}
private:
typedef typename PODRedBlackTree<IntervalType>::Node IntervalNode;
// Initializes the tree.
void init()
{
// Explicit dereference of "this" required because of
// inheritance rules in template classes.
this->setNeedsFullOrderingComparisons(true);
}
// Starting from the given node, adds all overlaps with the given
// interval to the result vector. The intervals are sorted by
// increasing low endpoint.
template <class AdapterType>
void searchForOverlapsFrom(IntervalNode* node, AdapterType& adapter) const
{
if (!node)
return;
// Because the intervals are sorted by left endpoint, inorder
// traversal produces results sorted as desired.
// See whether we need to traverse the left subtree.
IntervalNode* left = node->left();
if (left
// This is phrased this way to avoid the need for operator
// <= on type T.
&& !(left->data().maxHigh() < adapter.lowValue()))
searchForOverlapsFrom<AdapterType>(left, adapter);
// Check for overlap with current node.
adapter.collectIfNeeded(node->data());
// See whether we need to traverse the right subtree.
// This is phrased this way to avoid the need for operator <=
// on type T.
if (!(adapter.highValue() < node->data().low()))
searchForOverlapsFrom<AdapterType>(node->right(), adapter);
}
virtual bool updateNode(IntervalNode* node) override
{
// Would use const T&, but need to reassign this reference in this
// function.
const T* curMax = &node->data().high();
IntervalNode* left = node->left();
if (left) {
if (*curMax < left->data().maxHigh())
curMax = &left->data().maxHigh();
}
IntervalNode* right = node->right();
if (right) {
if (*curMax < right->data().maxHigh())
curMax = &right->data().maxHigh();
}
// This is phrased like this to avoid needing operator!= on type T.
if (!(*curMax == node->data().maxHigh())) {
node->data().setMaxHigh(*curMax);
return true;
}
return false;
}
bool checkInvariantsFromNode(IntervalNode* node, T* currentMaxValue) const
{
// These assignments are only done in order to avoid requiring
// a default constructor on type T.
T leftMaxValue(node->data().maxHigh());
T rightMaxValue(node->data().maxHigh());
IntervalNode* left = node->left();
IntervalNode* right = node->right();
if (left) {
if (!checkInvariantsFromNode(left, &leftMaxValue))
return false;
}
if (right) {
if (!checkInvariantsFromNode(right, &rightMaxValue))
return false;
}
if (!left && !right) {
// Base case.
if (currentMaxValue)
*currentMaxValue = node->data().high();
return (node->data().high() == node->data().maxHigh());
}
T localMaxValue(node->data().maxHigh());
if (!left || !right) {
if (left)
localMaxValue = leftMaxValue;
else
localMaxValue = rightMaxValue;
} else {
localMaxValue = (leftMaxValue < rightMaxValue) ? rightMaxValue : leftMaxValue;
}
if (localMaxValue < node->data().high())
localMaxValue = node->data().high();
if (!(localMaxValue == node->data().maxHigh())) {
#ifndef NDEBUG
String localMaxValueString = ValueToString<T>::string(localMaxValue);
WTF_LOG_ERROR("PODIntervalTree verification failed at node 0x%p: localMaxValue=%s and data=%s",
node, localMaxValueString.utf8().data(), node->data().toString().utf8().data());
#endif
return false;
}
if (currentMaxValue)
*currentMaxValue = localMaxValue;
return true;
}
};
#ifndef NDEBUG
// Support for printing PODIntervals at the PODRedBlackTree level.
template<class T, class UserData>
struct ValueToString<PODInterval<T, UserData> > {
static String string(const PODInterval<T, UserData>& interval)
{
return interval.toString();
}
};
#endif
} // namespace blink
#endif // PODIntervalTree_h

View File

@ -1,350 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Tests for the interval tree class.
#include "config.h"
#include "platform/PODIntervalTree.h"
#include "platform/Logging.h"
#include "platform/testing/TreeTestHelpers.h"
#include "wtf/Vector.h"
#include "wtf/text/WTFString.h"
#include <gtest/gtest.h>
namespace blink {
using TreeTestHelpers::initRandom;
using TreeTestHelpers::nextRandom;
#ifndef NDEBUG
template<>
struct ValueToString<float> {
static String string(const float& value) { return String::number(value); }
};
template<>
struct ValueToString<void*> {
static String string(void* const& value)
{
return String::format("0x%p", value);
}
};
#endif
TEST(PODIntervalTreeTest, TestInsertion)
{
PODIntervalTree<float> tree;
tree.add(PODInterval<float>(2, 4));
ASSERT_TRUE(tree.checkInvariants());
}
TEST(PODIntervalTreeTest, TestInsertionAndQuery)
{
PODIntervalTree<float> tree;
tree.add(PODInterval<float>(2, 4));
ASSERT_TRUE(tree.checkInvariants());
Vector<PODInterval<float> > result = tree.allOverlaps(PODInterval<float>(1, 3));
EXPECT_EQ(1U, result.size());
EXPECT_EQ(2, result[0].low());
EXPECT_EQ(4, result[0].high());
}
TEST(PODIntervalTreeTest, TestQueryAgainstZeroSizeInterval)
{
PODIntervalTree<float> tree;
tree.add(PODInterval<float>(1, 2.5));
tree.add(PODInterval<float>(3.5, 5));
tree.add(PODInterval<float>(2, 4));
ASSERT_TRUE(tree.checkInvariants());
Vector<PODInterval<float> > result = tree.allOverlaps(PODInterval<float>(3, 3));
EXPECT_EQ(1U, result.size());
EXPECT_EQ(2, result[0].low());
EXPECT_EQ(4, result[0].high());
}
#ifndef NDEBUG
template<>
struct ValueToString<int*> {
static String string(int* const& value)
{
return String::format("0x%p", value);
}
};
#endif
TEST(PODIntervalTreeTest, TestDuplicateElementInsertion)
{
PODIntervalTree<float, int*> tree;
int tmp1 = 1;
int tmp2 = 2;
typedef PODIntervalTree<float, int*>::IntervalType IntervalType;
IntervalType interval1(1, 3, &tmp1);
IntervalType interval2(1, 3, &tmp2);
tree.add(interval1);
tree.add(interval2);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(interval1));
EXPECT_TRUE(tree.contains(interval2));
EXPECT_TRUE(tree.remove(interval1));
EXPECT_TRUE(tree.contains(interval2));
EXPECT_FALSE(tree.contains(interval1));
EXPECT_TRUE(tree.remove(interval2));
EXPECT_EQ(0, tree.size());
}
namespace {
struct UserData1 {
public:
UserData1()
: a(0), b(1) { }
float a;
int b;
};
} // anonymous namespace
#ifndef NDEBUG
template<>
struct ValueToString<UserData1> {
static String string(const UserData1& value)
{
return String("[UserData1 a=") + String::number(value.a) + " b=" + String::number(value.b) + "]";
}
};
#endif
TEST(PODIntervalTreeTest, TestInsertionOfComplexUserData)
{
PODIntervalTree<float, UserData1> tree;
UserData1 data1;
data1.a = 5;
data1.b = 6;
tree.add(tree.createInterval(2, 4, data1));
ASSERT_TRUE(tree.checkInvariants());
}
TEST(PODIntervalTreeTest, TestQueryingOfComplexUserData)
{
PODIntervalTree<float, UserData1> tree;
UserData1 data1;
data1.a = 5;
data1.b = 6;
tree.add(tree.createInterval(2, 4, data1));
ASSERT_TRUE(tree.checkInvariants());
Vector<PODInterval<float, UserData1> > overlaps = tree.allOverlaps(tree.createInterval(3, 5, data1));
EXPECT_EQ(1U, overlaps.size());
EXPECT_EQ(5, overlaps[0].data().a);
EXPECT_EQ(6, overlaps[0].data().b);
}
namespace {
class EndpointType1 {
public:
explicit EndpointType1(int value)
: m_value(value) { }
int value() const { return m_value; }
bool operator<(const EndpointType1& other) const { return m_value < other.m_value; }
bool operator==(const EndpointType1& other) const { return m_value == other.m_value; }
private:
int m_value;
// These operators should not be called by the interval tree.
bool operator>(const EndpointType1& other);
bool operator<=(const EndpointType1& other);
bool operator>=(const EndpointType1& other);
bool operator!=(const EndpointType1& other);
};
} // anonymous namespace
#ifndef NDEBUG
template<>
struct ValueToString<EndpointType1> {
static String string(const EndpointType1& value)
{
return String("[EndpointType1 value=") + String::number(value.value()) + "]";
}
};
#endif
TEST(PODIntervalTreeTest, TestTreeDoesNotRequireMostOperators)
{
PODIntervalTree<EndpointType1> tree;
tree.add(tree.createInterval(EndpointType1(1), EndpointType1(2)));
ASSERT_TRUE(tree.checkInvariants());
}
// Uncomment to debug a failure of the insertion and deletion test. Won't work
// in release builds.
// #define DEBUG_INSERTION_AND_DELETION_TEST
#ifndef NDEBUG
template<>
struct ValueToString<int> {
static String string(const int& value) { return String::number(value); }
};
#endif
namespace {
void InsertionAndDeletionTest(int32_t seed, int treeSize)
{
initRandom(seed);
int maximumValue = treeSize;
// Build the tree
PODIntervalTree<int> tree;
Vector<PODInterval<int> > addedElements;
Vector<PODInterval<int> > removedElements;
for (int i = 0; i < treeSize; i++) {
int left = nextRandom(maximumValue);
int length = nextRandom(maximumValue);
PODInterval<int> interval(left, left + length);
tree.add(interval);
#ifdef DEBUG_INSERTION_AND_DELETION_TEST
WTF_LOG_ERROR("*** Adding element %s", ValueToString<PODInterval<int> >::string(interval).ascii().data());
#endif
addedElements.append(interval);
}
// Churn the tree's contents.
// First remove half of the elements in random order.
for (int i = 0; i < treeSize / 2; i++) {
int index = nextRandom(addedElements.size());
#ifdef DEBUG_INSERTION_AND_DELETION_TEST
WTF_LOG_ERROR("*** Removing element %s", ValueToString<PODInterval<int> >::string(addedElements[index]).ascii().data());
#endif
ASSERT_TRUE(tree.contains(addedElements[index])) << "Test failed for seed " << seed;
tree.remove(addedElements[index]);
removedElements.append(addedElements[index]);
addedElements.remove(index);
ASSERT_TRUE(tree.checkInvariants()) << "Test failed for seed " << seed;
}
// Now randomly add or remove elements.
for (int i = 0; i < 2 * treeSize; i++) {
bool add = false;
if (!addedElements.size())
add = true;
else if (!removedElements.size())
add = false;
else
add = (nextRandom(2) == 1);
if (add) {
int index = nextRandom(removedElements.size());
#ifdef DEBUG_INSERTION_AND_DELETION_TEST
WTF_LOG_ERROR("*** Adding element %s", ValueToString<PODInterval<int> >::string(removedElements[index]).ascii().data());
#endif
tree.add(removedElements[index]);
addedElements.append(removedElements[index]);
removedElements.remove(index);
} else {
int index = nextRandom(addedElements.size());
#ifdef DEBUG_INSERTION_AND_DELETION_TEST
WTF_LOG_ERROR("*** Removing element %s", ValueToString<PODInterval<int> >::string(addedElements[index]).ascii().data());
#endif
ASSERT_TRUE(tree.contains(addedElements[index])) << "Test failed for seed " << seed;
ASSERT_TRUE(tree.remove(addedElements[index])) << "Test failed for seed " << seed;
removedElements.append(addedElements[index]);
addedElements.remove(index);
}
ASSERT_TRUE(tree.checkInvariants()) << "Test failed for seed " << seed;
}
}
} // anonymous namespace
TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest1)
{
InsertionAndDeletionTest(13972, 100);
}
TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest2)
{
InsertionAndDeletionTest(1283382113, 10);
}
TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest3)
{
// This is the sequence of insertions and deletions that triggered
// the failure in RandomDeletionAndInsertionRegressionTest2.
PODIntervalTree<int> tree;
tree.add(tree.createInterval(0, 5));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(4, 5));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(8, 9));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(1, 4));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(3, 5));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(4, 12));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(0, 2));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(0, 2));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(9, 13));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(0, 1));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(0, 2));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(9, 13));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(0, 2));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(0, 1));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(4, 5));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(4, 12));
ASSERT_TRUE(tree.checkInvariants());
}
TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest4)
{
// Even further reduced test case for RandomDeletionAndInsertionRegressionTest3.
PODIntervalTree<int> tree;
tree.add(tree.createInterval(0, 5));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(8, 9));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(1, 4));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(3, 5));
ASSERT_TRUE(tree.checkInvariants());
tree.add(tree.createInterval(4, 12));
ASSERT_TRUE(tree.checkInvariants());
tree.remove(tree.createInterval(4, 12));
ASSERT_TRUE(tree.checkInvariants());
}
} // namespace blink

View File

@ -1,828 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// A red-black tree, which is a form of a balanced binary tree. It
// supports efficient insertion, deletion and queries of comparable
// elements. The same element may be inserted multiple times. The
// algorithmic complexity of common operations is:
//
// Insertion: O(lg(n))
// Deletion: O(lg(n))
// Querying: O(lg(n))
//
// The data type T that is stored in this red-black tree must be only
// Plain Old Data (POD), or bottom out into POD. It must _not_ rely on
// having its destructor called. This implementation internally
// allocates storage in large chunks and does not call the destructor
// on each object.
//
// Type T must supply a default constructor, a copy constructor, and
// the "<" and "==" operators.
//
// In debug mode, printing of the data contained in the tree is
// enabled. This requires the template specialization to be available:
//
// template<> struct ValueToString<T> {
// static String string(const T& t);
// };
//
// Note that when complex types are stored in this red/black tree, it
// is possible that single invocations of the "<" and "==" operators
// will be insufficient to describe the ordering of elements in the
// tree during queries. As a concrete example, consider the case where
// intervals are stored in the tree sorted by low endpoint. The "<"
// operator on the Interval class only compares the low endpoint, but
// the "==" operator takes into account the high endpoint as well.
// This makes the necessary logic for querying and deletion somewhat
// more complex. In order to properly handle such situations, the
// property "needsFullOrderingComparisons" must be set to true on
// the tree.
//
// This red-black tree is designed to be _augmented_; subclasses can
// add additional and summary information to each node to efficiently
// store and index more complex data structures. A concrete example is
// the IntervalTree, which extends each node with a summary statistic
// to efficiently store one-dimensional intervals.
//
// The design of this red-black tree comes from Cormen, Leiserson,
// and Rivest, _Introduction to Algorithms_, MIT Press, 1990.
#ifndef PODRedBlackTree_h
#define PODRedBlackTree_h
#include "platform/PODFreeListArena.h"
#include "wtf/Assertions.h"
#include "wtf/Noncopyable.h"
#include "wtf/RefPtr.h"
#ifndef NDEBUG
#include "wtf/text/CString.h"
#include "wtf/text/StringBuilder.h"
#include "wtf/text/WTFString.h"
#endif
namespace blink {
#ifndef NDEBUG
template<class T>
struct ValueToString;
#endif
enum UninitializedTreeEnum {
UninitializedTree
};
template<class T>
class PODRedBlackTree {
public:
class Node;
// Visitor interface for walking all of the tree's elements.
class Visitor {
public:
virtual void visit(const T& data) = 0;
protected:
virtual ~Visitor() { }
};
// Constructs a new red-black tree without allocating an arena.
// isInitialized will return false in this case. initIfNeeded can be used
// to init the structure. This constructor is usefull for creating
// lazy initialized tree.
explicit PODRedBlackTree(UninitializedTreeEnum)
: m_root(0)
, m_needsFullOrderingComparisons(false)
#ifndef NDEBUG
, m_verboseDebugging(false)
#endif
{
}
// Constructs a new red-black tree, allocating temporary objects
// from a newly constructed PODFreeListArena.
PODRedBlackTree()
: m_arena(PODFreeListArena<Node>::create())
, m_root(0)
, m_needsFullOrderingComparisons(false)
#ifndef NDEBUG
, m_verboseDebugging(false)
#endif
{
}
// Constructs a new red-black tree, allocating temporary objects
// from the given PODArena.
explicit PODRedBlackTree(PassRefPtr<PODFreeListArena<Node> > arena)
: m_arena(arena)
, m_root(0)
, m_needsFullOrderingComparisons(false)
#ifndef NDEBUG
, m_verboseDebugging(false)
#endif
{
}
virtual ~PODRedBlackTree() { }
// Clearing will delete the contents of the tree. After this call
// isInitialized will return false.
void clear()
{
markFree(m_root);
m_arena = nullptr;
m_root = 0;
}
bool isInitialized() const
{
return m_arena;
}
void initIfNeeded()
{
if (!m_arena)
m_arena = PODFreeListArena<Node>::create();
}
void initIfNeeded(PODFreeListArena<Node>* arena)
{
if (!m_arena)
m_arena = arena;
}
void add(const T& data)
{
ASSERT(isInitialized());
Node* node = m_arena->template allocateObject<T>(data);
insertNode(node);
}
// Returns true if the datum was found in the tree.
bool remove(const T& data)
{
ASSERT(isInitialized());
Node* node = treeSearch(data);
if (node) {
deleteNode(node);
return true;
}
return false;
}
bool contains(const T& data) const
{
ASSERT(isInitialized());
return treeSearch(data);
}
void visitInorder(Visitor* visitor) const
{
ASSERT(isInitialized());
if (!m_root)
return;
visitInorderImpl(m_root, visitor);
}
int size() const
{
ASSERT(isInitialized());
Counter counter;
visitInorder(&counter);
return counter.count();
}
// See the class documentation for an explanation of this property.
void setNeedsFullOrderingComparisons(bool needsFullOrderingComparisons)
{
m_needsFullOrderingComparisons = needsFullOrderingComparisons;
}
virtual bool checkInvariants() const
{
ASSERT(isInitialized());
int blackCount;
return checkInvariantsFromNode(m_root, &blackCount);
}
#ifndef NDEBUG
// Dumps the tree's contents to the logging info stream for
// debugging purposes.
void dump() const
{
if (m_arena)
dumpFromNode(m_root, 0);
}
// Turns on or off verbose debugging of the tree, causing many
// messages to be logged during insertion and other operations in
// debug mode.
void setVerboseDebugging(bool verboseDebugging)
{
m_verboseDebugging = verboseDebugging;
}
#endif
enum Color {
Red = 1,
Black
};
// The base Node class which is stored in the tree. Nodes are only
// an internal concept; users of the tree deal only with the data
// they store in it.
class Node {
WTF_MAKE_NONCOPYABLE(Node);
public:
// Constructor. Newly-created nodes are colored red.
explicit Node(const T& data)
: m_left(0)
, m_right(0)
, m_parent(0)
, m_color(Red)
, m_data(data)
{
}
virtual ~Node() { }
Color color() const { return m_color; }
void setColor(Color color) { m_color = color; }
// Fetches the user data.
T& data() { return m_data; }
// Copies all user-level fields from the source node, but not
// internal fields. For example, the base implementation of this
// method copies the "m_data" field, but not the child or parent
// fields. Any augmentation information also does not need to be
// copied, as it will be recomputed. Subclasses must call the
// superclass implementation.
virtual void copyFrom(Node* src) { m_data = src->data(); }
Node* left() const { return m_left; }
void setLeft(Node* node) { m_left = node; }
Node* right() const { return m_right; }
void setRight(Node* node) { m_right = node; }
Node* parent() const { return m_parent; }
void setParent(Node* node) { m_parent = node; }
private:
Node* m_left;
Node* m_right;
Node* m_parent;
Color m_color;
T m_data;
};
protected:
// Returns the root of the tree, which is needed by some subclasses.
Node* root() const { return m_root; }
private:
// This virtual method is the hook that subclasses should use when
// augmenting the red-black tree with additional per-node summary
// information. For example, in the case of an interval tree, this
// is used to compute the maximum endpoint of the subtree below the
// given node based on the values in the left and right children. It
// is guaranteed that this will be called in the correct order to
// properly update such summary information based only on the values
// in the left and right children. This method should return true if
// the node's summary information changed.
virtual bool updateNode(Node*) { return false; }
//----------------------------------------------------------------------
// Generic binary search tree operations
//
// Searches the tree for the given datum.
Node* treeSearch(const T& data) const
{
if (m_needsFullOrderingComparisons)
return treeSearchFullComparisons(m_root, data);
return treeSearchNormal(m_root, data);
}
// Searches the tree using the normal comparison operations,
// suitable for simple data types such as numbers.
Node* treeSearchNormal(Node* current, const T& data) const
{
while (current) {
if (current->data() == data)
return current;
if (data < current->data())
current = current->left();
else
current = current->right();
}
return 0;
}
// Searches the tree using multiple comparison operations, required
// for data types with more complex behavior such as intervals.
Node* treeSearchFullComparisons(Node* current, const T& data) const
{
if (!current)
return 0;
if (data < current->data())
return treeSearchFullComparisons(current->left(), data);
if (current->data() < data)
return treeSearchFullComparisons(current->right(), data);
if (data == current->data())
return current;
// We may need to traverse both the left and right subtrees.
Node* result = treeSearchFullComparisons(current->left(), data);
if (!result)
result = treeSearchFullComparisons(current->right(), data);
return result;
}
void treeInsert(Node* z)
{
Node* y = 0;
Node* x = m_root;
while (x) {
y = x;
if (z->data() < x->data())
x = x->left();
else
x = x->right();
}
z->setParent(y);
if (!y) {
m_root = z;
} else {
if (z->data() < y->data())
y->setLeft(z);
else
y->setRight(z);
}
}
// Finds the node following the given one in sequential ordering of
// their data, or null if none exists.
Node* treeSuccessor(Node* x)
{
if (x->right())
return treeMinimum(x->right());
Node* y = x->parent();
while (y && x == y->right()) {
x = y;
y = y->parent();
}
return y;
}
// Finds the minimum element in the sub-tree rooted at the given
// node.
Node* treeMinimum(Node* x)
{
while (x->left())
x = x->left();
return x;
}
// Helper for maintaining the augmented red-black tree.
void propagateUpdates(Node* start)
{
bool shouldContinue = true;
while (start && shouldContinue) {
shouldContinue = updateNode(start);
start = start->parent();
}
}
//----------------------------------------------------------------------
// Red-Black tree operations
//
// Left-rotates the subtree rooted at x.
// Returns the new root of the subtree (x's right child).
Node* leftRotate(Node* x)
{
// Set y.
Node* y = x->right();
// Turn y's left subtree into x's right subtree.
x->setRight(y->left());
if (y->left())
y->left()->setParent(x);
// Link x's parent to y.
y->setParent(x->parent());
if (!x->parent()) {
m_root = y;
} else {
if (x == x->parent()->left())
x->parent()->setLeft(y);
else
x->parent()->setRight(y);
}
// Put x on y's left.
y->setLeft(x);
x->setParent(y);
// Update nodes lowest to highest.
updateNode(x);
updateNode(y);
return y;
}
// Right-rotates the subtree rooted at y.
// Returns the new root of the subtree (y's left child).
Node* rightRotate(Node* y)
{
// Set x.
Node* x = y->left();
// Turn x's right subtree into y's left subtree.
y->setLeft(x->right());
if (x->right())
x->right()->setParent(y);
// Link y's parent to x.
x->setParent(y->parent());
if (!y->parent()) {
m_root = x;
} else {
if (y == y->parent()->left())
y->parent()->setLeft(x);
else
y->parent()->setRight(x);
}
// Put y on x's right.
x->setRight(y);
y->setParent(x);
// Update nodes lowest to highest.
updateNode(y);
updateNode(x);
return x;
}
// Inserts the given node into the tree.
void insertNode(Node* x)
{
treeInsert(x);
x->setColor(Red);
updateNode(x);
logIfVerbose(" PODRedBlackTree::InsertNode");
// The node from which to start propagating updates upwards.
Node* updateStart = x->parent();
while (x != m_root && x->parent()->color() == Red) {
if (x->parent() == x->parent()->parent()->left()) {
Node* y = x->parent()->parent()->right();
if (y && y->color() == Red) {
// Case 1
logIfVerbose(" Case 1/1");
x->parent()->setColor(Black);
y->setColor(Black);
x->parent()->parent()->setColor(Red);
updateNode(x->parent());
x = x->parent()->parent();
updateNode(x);
updateStart = x->parent();
} else {
if (x == x->parent()->right()) {
logIfVerbose(" Case 1/2");
// Case 2
x = x->parent();
leftRotate(x);
}
// Case 3
logIfVerbose(" Case 1/3");
x->parent()->setColor(Black);
x->parent()->parent()->setColor(Red);
Node* newSubTreeRoot = rightRotate(x->parent()->parent());
updateStart = newSubTreeRoot->parent();
}
} else {
// Same as "then" clause with "right" and "left" exchanged.
Node* y = x->parent()->parent()->left();
if (y && y->color() == Red) {
// Case 1
logIfVerbose(" Case 2/1");
x->parent()->setColor(Black);
y->setColor(Black);
x->parent()->parent()->setColor(Red);
updateNode(x->parent());
x = x->parent()->parent();
updateNode(x);
updateStart = x->parent();
} else {
if (x == x->parent()->left()) {
// Case 2
logIfVerbose(" Case 2/2");
x = x->parent();
rightRotate(x);
}
// Case 3
logIfVerbose(" Case 2/3");
x->parent()->setColor(Black);
x->parent()->parent()->setColor(Red);
Node* newSubTreeRoot = leftRotate(x->parent()->parent());
updateStart = newSubTreeRoot->parent();
}
}
}
propagateUpdates(updateStart);
m_root->setColor(Black);
}
// Restores the red-black property to the tree after splicing out
// a node. Note that x may be null, which is why xParent must be
// supplied.
void deleteFixup(Node* x, Node* xParent)
{
while (x != m_root && (!x || x->color() == Black)) {
if (x == xParent->left()) {
// Note: the text points out that w can not be null.
// The reason is not obvious from simply looking at
// the code; it comes about from the properties of the
// red-black tree.
Node* w = xParent->right();
ASSERT(w); // x's sibling should not be null.
if (w->color() == Red) {
// Case 1
w->setColor(Black);
xParent->setColor(Red);
leftRotate(xParent);
w = xParent->right();
}
if ((!w->left() || w->left()->color() == Black)
&& (!w->right() || w->right()->color() == Black)) {
// Case 2
w->setColor(Red);
x = xParent;
xParent = x->parent();
} else {
if (!w->right() || w->right()->color() == Black) {
// Case 3
w->left()->setColor(Black);
w->setColor(Red);
rightRotate(w);
w = xParent->right();
}
// Case 4
w->setColor(xParent->color());
xParent->setColor(Black);
if (w->right())
w->right()->setColor(Black);
leftRotate(xParent);
x = m_root;
xParent = x->parent();
}
} else {
// Same as "then" clause with "right" and "left"
// exchanged.
// Note: the text points out that w can not be null.
// The reason is not obvious from simply looking at
// the code; it comes about from the properties of the
// red-black tree.
Node* w = xParent->left();
ASSERT(w); // x's sibling should not be null.
if (w->color() == Red) {
// Case 1
w->setColor(Black);
xParent->setColor(Red);
rightRotate(xParent);
w = xParent->left();
}
if ((!w->right() || w->right()->color() == Black)
&& (!w->left() || w->left()->color() == Black)) {
// Case 2
w->setColor(Red);
x = xParent;
xParent = x->parent();
} else {
if (!w->left() || w->left()->color() == Black) {
// Case 3
w->right()->setColor(Black);
w->setColor(Red);
leftRotate(w);
w = xParent->left();
}
// Case 4
w->setColor(xParent->color());
xParent->setColor(Black);
if (w->left())
w->left()->setColor(Black);
rightRotate(xParent);
x = m_root;
xParent = x->parent();
}
}
}
if (x)
x->setColor(Black);
}
// Deletes the given node from the tree. Note that this
// particular node may not actually be removed from the tree;
// instead, another node might be removed and its contents
// copied into z.
void deleteNode(Node* z)
{
// Y is the node to be unlinked from the tree.
Node* y;
if (!z->left() || !z->right())
y = z;
else
y = treeSuccessor(z);
// Y is guaranteed to be non-null at this point.
Node* x;
if (y->left())
x = y->left();
else
x = y->right();
// X is the child of y which might potentially replace y in
// the tree. X might be null at this point.
Node* xParent;
if (x) {
x->setParent(y->parent());
xParent = x->parent();
} else {
xParent = y->parent();
}
if (!y->parent()) {
m_root = x;
} else {
if (y == y->parent()->left())
y->parent()->setLeft(x);
else
y->parent()->setRight(x);
}
if (y != z) {
z->copyFrom(y);
// This node has changed location in the tree and must be updated.
updateNode(z);
// The parent and its parents may now be out of date.
propagateUpdates(z->parent());
}
// If we haven't already updated starting from xParent, do so now.
if (xParent && xParent != y && xParent != z)
propagateUpdates(xParent);
if (y->color() == Black)
deleteFixup(x, xParent);
m_arena->freeObject(y);
}
// Visits the subtree rooted at the given node in order.
void visitInorderImpl(Node* node, Visitor* visitor) const
{
if (node->left())
visitInorderImpl(node->left(), visitor);
visitor->visit(node->data());
if (node->right())
visitInorderImpl(node->right(), visitor);
}
void markFree(Node *node)
{
if (!node)
return;
if (node->left())
markFree(node->left());
if (node->right())
markFree(node->right());
m_arena->freeObject(node);
}
//----------------------------------------------------------------------
// Helper class for size()
// A Visitor which simply counts the number of visited elements.
class Counter : public Visitor {
WTF_MAKE_NONCOPYABLE(Counter);
public:
Counter()
: m_count(0) { }
virtual void visit(const T&) { ++m_count; }
int count() const { return m_count; }
private:
int m_count;
};
//----------------------------------------------------------------------
// Verification and debugging routines
//
// Returns in the "blackCount" parameter the number of black
// children along all paths to all leaves of the given node.
bool checkInvariantsFromNode(Node* node, int* blackCount) const
{
// Base case is a leaf node.
if (!node) {
*blackCount = 1;
return true;
}
// Each node is either red or black.
if (!(node->color() == Red || node->color() == Black))
return false;
// Every leaf (or null) is black.
if (node->color() == Red) {
// Both of its children are black.
if (!((!node->left() || node->left()->color() == Black)))
return false;
if (!((!node->right() || node->right()->color() == Black)))
return false;
}
// Every simple path to a leaf node contains the same number of
// black nodes.
int leftCount = 0, rightCount = 0;
bool leftValid = checkInvariantsFromNode(node->left(), &leftCount);
bool rightValid = checkInvariantsFromNode(node->right(), &rightCount);
if (!leftValid || !rightValid)
return false;
*blackCount = leftCount + (node->color() == Black ? 1 : 0);
return leftCount == rightCount;
}
#ifdef NDEBUG
void logIfVerbose(const char*) const { }
#else
void logIfVerbose(const char* output) const
{
if (m_verboseDebugging)
WTF_LOG_ERROR("%s", output);
}
#endif
#ifndef NDEBUG
// Dumps the subtree rooted at the given node.
void dumpFromNode(Node* node, int indentation) const
{
StringBuilder builder;
for (int i = 0; i < indentation; i++)
builder.append(' ');
builder.append('-');
if (node) {
builder.append(' ');
builder.append(ValueToString<T>::string(node->data()));
builder.append((node->color() == Black) ? " (black)" : " (red)");
}
WTF_LOG_ERROR("%s", builder.toString().ascii().data());
if (node) {
dumpFromNode(node->left(), indentation + 2);
dumpFromNode(node->right(), indentation + 2);
}
}
#endif
//----------------------------------------------------------------------
// Data members
RefPtr<PODFreeListArena<Node> > m_arena;
Node* m_root;
bool m_needsFullOrderingComparisons;
#ifndef NDEBUG
bool m_verboseDebugging;
#endif
};
} // namespace blink
#endif // PODRedBlackTree_h

View File

@ -1,208 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Tests for the red-black tree class.
#include "config.h"
#include "platform/PODRedBlackTree.h"
#include "platform/testing/ArenaTestHelpers.h"
#include "platform/testing/TreeTestHelpers.h"
#include "wtf/Vector.h"
#include <gtest/gtest.h>
namespace blink {
using ArenaTestHelpers::TrackedAllocator;
using TreeTestHelpers::initRandom;
using TreeTestHelpers::nextRandom;
TEST(PODRedBlackTreeTest, TestTreeAllocatesFromArena)
{
RefPtr<TrackedAllocator> allocator = TrackedAllocator::create();
{
typedef PODFreeListArena<PODRedBlackTree<int>::Node> PODIntegerArena;
RefPtr<PODIntegerArena> arena = PODIntegerArena::create(allocator);
PODRedBlackTree<int> tree(arena);
int numAdditions = 2 * PODArena::DefaultChunkSize / sizeof(int);
for (int i = 0; i < numAdditions; ++i)
tree.add(i);
EXPECT_GT(allocator->numRegions(), 1);
}
EXPECT_EQ(allocator->numRegions(), 0);
}
TEST(PODRedBlackTreeTest, TestSingleElementInsertion)
{
PODRedBlackTree<int> tree;
tree.add(5);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(5));
}
TEST(PODRedBlackTreeTest, TestMultipleElementInsertion)
{
PODRedBlackTree<int> tree;
tree.add(4);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(4));
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(3));
tree.add(5);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(5));
EXPECT_TRUE(tree.contains(4));
EXPECT_TRUE(tree.contains(3));
}
TEST(PODRedBlackTreeTest, TestDuplicateElementInsertion)
{
PODRedBlackTree<int> tree;
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_EQ(3, tree.size());
EXPECT_TRUE(tree.contains(3));
}
TEST(PODRedBlackTreeTest, TestSingleElementInsertionAndDeletion)
{
PODRedBlackTree<int> tree;
tree.add(5);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(5));
tree.remove(5);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_FALSE(tree.contains(5));
}
TEST(PODRedBlackTreeTest, TestMultipleElementInsertionAndDeletion)
{
PODRedBlackTree<int> tree;
tree.add(4);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(4));
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(3));
tree.add(5);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(5));
EXPECT_TRUE(tree.contains(4));
EXPECT_TRUE(tree.contains(3));
tree.remove(4);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(3));
EXPECT_FALSE(tree.contains(4));
EXPECT_TRUE(tree.contains(5));
tree.remove(5);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_TRUE(tree.contains(3));
EXPECT_FALSE(tree.contains(4));
EXPECT_FALSE(tree.contains(5));
EXPECT_EQ(1, tree.size());
}
TEST(PODRedBlackTreeTest, TestDuplicateElementInsertionAndDeletion)
{
PODRedBlackTree<int> tree;
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
tree.add(3);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_EQ(3, tree.size());
EXPECT_TRUE(tree.contains(3));
tree.remove(3);
ASSERT_TRUE(tree.checkInvariants());
tree.remove(3);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_EQ(1, tree.size());
EXPECT_TRUE(tree.contains(3));
tree.remove(3);
ASSERT_TRUE(tree.checkInvariants());
EXPECT_EQ(0, tree.size());
EXPECT_FALSE(tree.contains(3));
}
TEST(PODRedBlackTreeTest, FailingInsertionRegressionTest1)
{
// These numbers came from a previously-failing randomized test run.
PODRedBlackTree<int> tree;
tree.add(5113);
ASSERT_TRUE(tree.checkInvariants());
tree.add(4517);
ASSERT_TRUE(tree.checkInvariants());
tree.add(3373);
ASSERT_TRUE(tree.checkInvariants());
tree.add(9307);
ASSERT_TRUE(tree.checkInvariants());
tree.add(7077);
ASSERT_TRUE(tree.checkInvariants());
}
namespace {
void InsertionAndDeletionTest(const int32_t seed, const int treeSize)
{
initRandom(seed);
const int maximumValue = treeSize;
// Build the tree.
PODRedBlackTree<int> tree;
Vector<int> values;
for (int i = 0; i < treeSize; i++) {
int value = nextRandom(maximumValue);
tree.add(value);
ASSERT_TRUE(tree.checkInvariants()) << "Test failed for seed " << seed;
values.append(value);
}
// Churn the tree's contents.
for (int i = 0; i < treeSize; i++) {
// Pick a random value to remove.
int index = nextRandom(treeSize);
int value = values[index];
// Remove this value.
tree.remove(value);
ASSERT_TRUE(tree.checkInvariants()) << "Test failed for seed " << seed;
// Replace it with a new one.
value = nextRandom(maximumValue);
values[index] = value;
tree.add(value);
ASSERT_TRUE(tree.checkInvariants()) << "Test failed for seed " << seed;
}
}
} // anonymous namespace
TEST(PODRedBlackTreeTest, RandomDeletionAndInsertionRegressionTest1)
{
InsertionAndDeletionTest(12311, 100);
}
} // namespace blink

View File

@ -1,227 +0,0 @@
/*
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "platform/geometry/FloatPolygon.h"
#include "wtf/MathExtras.h"
namespace blink {
static inline float determinant(const FloatSize& a, const FloatSize& b)
{
return a.width() * b.height() - a.height() * b.width();
}
static inline bool areCollinearPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2)
{
return !determinant(p1 - p0, p2 - p0);
}
static inline bool areCoincidentPoints(const FloatPoint& p0, const FloatPoint& p1)
{
return p0.x() == p1.x() && p0.y() == p1.y();
}
static inline bool isPointOnLineSegment(const FloatPoint& vertex1, const FloatPoint& vertex2, const FloatPoint& point)
{
return point.x() >= std::min(vertex1.x(), vertex2.x())
&& point.x() <= std::max(vertex1.x(), vertex2.x())
&& areCollinearPoints(vertex1, vertex2, point);
}
static inline unsigned nextVertexIndex(unsigned vertexIndex, unsigned nVertices, bool clockwise)
{
return ((clockwise) ? vertexIndex + 1 : vertexIndex - 1 + nVertices) % nVertices;
}
static unsigned findNextEdgeVertexIndex(const FloatPolygon& polygon, unsigned vertexIndex1, bool clockwise)
{
unsigned nVertices = polygon.numberOfVertices();
unsigned vertexIndex2 = nextVertexIndex(vertexIndex1, nVertices, clockwise);
while (vertexIndex2 && areCoincidentPoints(polygon.vertexAt(vertexIndex1), polygon.vertexAt(vertexIndex2)))
vertexIndex2 = nextVertexIndex(vertexIndex2, nVertices, clockwise);
while (vertexIndex2) {
unsigned vertexIndex3 = nextVertexIndex(vertexIndex2, nVertices, clockwise);
if (!areCollinearPoints(polygon.vertexAt(vertexIndex1), polygon.vertexAt(vertexIndex2), polygon.vertexAt(vertexIndex3)))
break;
vertexIndex2 = vertexIndex3;
}
return vertexIndex2;
}
FloatPolygon::FloatPolygon(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule)
: m_vertices(vertices)
, m_fillRule(fillRule)
{
unsigned nVertices = numberOfVertices();
m_edges.resize(nVertices);
m_empty = nVertices < 3;
if (nVertices)
m_boundingBox.setLocation(vertexAt(0));
if (m_empty)
return;
unsigned minVertexIndex = 0;
for (unsigned i = 1; i < nVertices; ++i) {
const FloatPoint& vertex = vertexAt(i);
if (vertex.y() < vertexAt(minVertexIndex).y() || (vertex.y() == vertexAt(minVertexIndex).y() && vertex.x() < vertexAt(minVertexIndex).x()))
minVertexIndex = i;
}
FloatPoint nextVertex = vertexAt((minVertexIndex + 1) % nVertices);
FloatPoint prevVertex = vertexAt((minVertexIndex + nVertices - 1) % nVertices);
bool clockwise = determinant(vertexAt(minVertexIndex) - prevVertex, nextVertex - prevVertex) > 0;
unsigned edgeIndex = 0;
unsigned vertexIndex1 = 0;
do {
m_boundingBox.extend(vertexAt(vertexIndex1));
unsigned vertexIndex2 = findNextEdgeVertexIndex(*this, vertexIndex1, clockwise);
m_edges[edgeIndex].m_polygon = this;
m_edges[edgeIndex].m_vertexIndex1 = vertexIndex1;
m_edges[edgeIndex].m_vertexIndex2 = vertexIndex2;
m_edges[edgeIndex].m_edgeIndex = edgeIndex;
++edgeIndex;
vertexIndex1 = vertexIndex2;
} while (vertexIndex1);
if (edgeIndex > 3) {
const FloatPolygonEdge& firstEdge = m_edges[0];
const FloatPolygonEdge& lastEdge = m_edges[edgeIndex - 1];
if (areCollinearPoints(lastEdge.vertex1(), lastEdge.vertex2(), firstEdge.vertex2())) {
m_edges[0].m_vertexIndex1 = lastEdge.m_vertexIndex1;
edgeIndex--;
}
}
m_edges.resize(edgeIndex);
m_empty = m_edges.size() < 3;
if (m_empty)
return;
for (unsigned i = 0; i < m_edges.size(); ++i) {
FloatPolygonEdge* edge = &m_edges[i];
m_edgeTree.add(EdgeInterval(edge->minY(), edge->maxY(), edge));
}
}
bool FloatPolygon::overlappingEdges(float minY, float maxY, Vector<const FloatPolygonEdge*>& result) const
{
Vector<FloatPolygon::EdgeInterval> overlappingEdgeIntervals;
m_edgeTree.allOverlaps(FloatPolygon::EdgeInterval(minY, maxY, 0), overlappingEdgeIntervals);
unsigned overlappingEdgeIntervalsSize = overlappingEdgeIntervals.size();
result.resize(overlappingEdgeIntervalsSize);
for (unsigned i = 0; i < overlappingEdgeIntervalsSize; ++i) {
const FloatPolygonEdge* edge = static_cast<const FloatPolygonEdge*>(overlappingEdgeIntervals[i].data());
ASSERT(edge);
result[i] = edge;
}
return overlappingEdgeIntervalsSize > 0;
}
static inline float leftSide(const FloatPoint& vertex1, const FloatPoint& vertex2, const FloatPoint& point)
{
return ((point.x() - vertex1.x()) * (vertex2.y() - vertex1.y())) - ((vertex2.x() - vertex1.x()) * (point.y() - vertex1.y()));
}
bool FloatPolygon::containsEvenOdd(const FloatPoint& point) const
{
unsigned crossingCount = 0;
for (unsigned i = 0; i < numberOfEdges(); ++i) {
const FloatPoint& vertex1 = edgeAt(i).vertex1();
const FloatPoint& vertex2 = edgeAt(i).vertex2();
if (isPointOnLineSegment(vertex1, vertex2, point))
return true;
if ((vertex1.y() <= point.y() && vertex2.y() > point.y()) || (vertex1.y() > point.y() && vertex2.y() <= point.y())) {
float vt = (point.y() - vertex1.y()) / (vertex2.y() - vertex1.y());
if (point.x() < vertex1.x() + vt * (vertex2.x() - vertex1.x()))
++crossingCount;
}
}
return crossingCount & 1;
}
bool FloatPolygon::containsNonZero(const FloatPoint& point) const
{
int windingNumber = 0;
for (unsigned i = 0; i < numberOfEdges(); ++i) {
const FloatPoint& vertex1 = edgeAt(i).vertex1();
const FloatPoint& vertex2 = edgeAt(i).vertex2();
if (isPointOnLineSegment(vertex1, vertex2, point))
return true;
if (vertex2.y() <= point.y()) {
if ((vertex1.y() > point.y()) && (leftSide(vertex1, vertex2, point) > 0))
++windingNumber;
} else if (vertex2.y() >= point.y()) {
if ((vertex1.y() <= point.y()) && (leftSide(vertex1, vertex2, point) < 0))
--windingNumber;
}
}
return windingNumber;
}
bool FloatPolygon::contains(const FloatPoint& point) const
{
if (!m_boundingBox.contains(point))
return false;
return (fillRule() == RULE_NONZERO) ? containsNonZero(point) : containsEvenOdd(point);
}
bool VertexPair::intersection(const VertexPair& other, FloatPoint& point) const
{
// See: http://paulbourke.net/geometry/pointlineplane/, "Intersection point of two lines in 2 dimensions"
const FloatSize& thisDelta = vertex2() - vertex1();
const FloatSize& otherDelta = other.vertex2() - other.vertex1();
float denominator = determinant(thisDelta, otherDelta);
if (!denominator)
return false;
// The two line segments: "this" vertex1,vertex2 and "other" vertex1,vertex2, have been defined
// in parametric form. Each point on the line segment is: vertex1 + u * (vertex2 - vertex1),
// when 0 <= u <= 1. We're computing the values of u for each line at their intersection point.
const FloatSize& vertex1Delta = vertex1() - other.vertex1();
float uThisLine = determinant(otherDelta, vertex1Delta) / denominator;
float uOtherLine = determinant(thisDelta, vertex1Delta) / denominator;
if (uThisLine < 0 || uOtherLine < 0 || uThisLine > 1 || uOtherLine > 1)
return false;
point = vertex1() + uThisLine * thisDelta;
return true;
}
} // namespace blink

View File

@ -1,152 +0,0 @@
/*
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FloatPolygon_h
#define FloatPolygon_h
#include "platform/PODIntervalTree.h"
#include "platform/geometry/FloatPoint.h"
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/GraphicsTypes.h"
#include "wtf/OwnPtr.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/Vector.h"
namespace blink {
class FloatPolygonEdge;
// This class is used by PODIntervalTree for debugging.
#ifndef NDEBUG
template <class> struct ValueToString;
#endif
class PLATFORM_EXPORT FloatPolygon {
public:
FloatPolygon(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule);
const FloatPoint& vertexAt(unsigned index) const { return (*m_vertices)[index]; }
unsigned numberOfVertices() const { return m_vertices->size(); }
WindRule fillRule() const { return m_fillRule; }
const FloatPolygonEdge& edgeAt(unsigned index) const { return m_edges[index]; }
unsigned numberOfEdges() const { return m_edges.size(); }
FloatRect boundingBox() const { return m_boundingBox; }
bool overlappingEdges(float minY, float maxY, Vector<const FloatPolygonEdge*>& result) const;
bool contains(const FloatPoint&) const;
bool isEmpty() const { return m_empty; }
private:
typedef PODInterval<float, FloatPolygonEdge*> EdgeInterval;
typedef PODIntervalTree<float, FloatPolygonEdge*> EdgeIntervalTree;
bool containsNonZero(const FloatPoint&) const;
bool containsEvenOdd(const FloatPoint&) const;
OwnPtr<Vector<FloatPoint> > m_vertices;
WindRule m_fillRule;
FloatRect m_boundingBox;
bool m_empty;
Vector<FloatPolygonEdge> m_edges;
EdgeIntervalTree m_edgeTree; // Each EdgeIntervalTree node stores minY, maxY, and a ("UserData") pointer to a FloatPolygonEdge.
};
class PLATFORM_EXPORT VertexPair {
public:
virtual ~VertexPair() { }
virtual const FloatPoint& vertex1() const = 0;
virtual const FloatPoint& vertex2() const = 0;
float minX() const { return std::min(vertex1().x(), vertex2().x()); }
float minY() const { return std::min(vertex1().y(), vertex2().y()); }
float maxX() const { return std::max(vertex1().x(), vertex2().x()); }
float maxY() const { return std::max(vertex1().y(), vertex2().y()); }
bool intersection(const VertexPair&, FloatPoint&) const;
};
class PLATFORM_EXPORT FloatPolygonEdge : public VertexPair {
friend class FloatPolygon;
public:
virtual const FloatPoint& vertex1() const override
{
ASSERT(m_polygon);
return m_polygon->vertexAt(m_vertexIndex1);
}
virtual const FloatPoint& vertex2() const override
{
ASSERT(m_polygon);
return m_polygon->vertexAt(m_vertexIndex2);
}
const FloatPolygonEdge& previousEdge() const
{
ASSERT(m_polygon && m_polygon->numberOfEdges() > 1);
return m_polygon->edgeAt((m_edgeIndex + m_polygon->numberOfEdges() - 1) % m_polygon->numberOfEdges());
}
const FloatPolygonEdge& nextEdge() const
{
ASSERT(m_polygon && m_polygon->numberOfEdges() > 1);
return m_polygon->edgeAt((m_edgeIndex + 1) % m_polygon->numberOfEdges());
}
const FloatPolygon* polygon() const { return m_polygon; }
unsigned vertexIndex1() const { return m_vertexIndex1; }
unsigned vertexIndex2() const { return m_vertexIndex2; }
unsigned edgeIndex() const { return m_edgeIndex; }
private:
// Edge vertex index1 is less than index2, except the last edge, where index2 is 0. When a polygon edge
// is defined by 3 or more colinear vertices, index2 can be the the index of the last colinear vertex.
unsigned m_vertexIndex1;
unsigned m_vertexIndex2;
unsigned m_edgeIndex;
const FloatPolygon* m_polygon;
};
// These structures are used by PODIntervalTree for debugging.
#ifndef NDEBUG
template <> struct ValueToString<float> {
static String string(const float value) { return String::number(value); }
};
template<> struct ValueToString<FloatPolygonEdge*> {
static String string(const FloatPolygonEdge* edge) { return String::format("%p (%f,%f %f,%f)", edge, edge->vertex1().x(), edge->vertex1().y(), edge->vertex2().x(), edge->vertex2().y()); }
};
#endif
} // namespace blink
#endif // FloatPolygon_h

View File

@ -1,331 +0,0 @@
/*
* Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "platform/geometry/FloatPolygon.h"
#include <gtest/gtest.h>
namespace blink {
class FloatPolygonTestValue {
public:
FloatPolygonTestValue(const float* coordinates, unsigned coordinatesLength, WindRule fillRule)
{
ASSERT(!(coordinatesLength % 2));
OwnPtr<Vector<FloatPoint> > vertices = adoptPtr(new Vector<FloatPoint>(coordinatesLength / 2));
for (unsigned i = 0; i < coordinatesLength; i += 2)
(*vertices)[i / 2] = FloatPoint(coordinates[i], coordinates[i + 1]);
m_polygon = adoptPtr(new FloatPolygon(vertices.release(), fillRule));
}
const FloatPolygon& polygon() const { return *m_polygon; }
private:
OwnPtr<FloatPolygon> m_polygon;
};
} // namespace blink
namespace {
using namespace blink;
static bool compareEdgeIndex(const FloatPolygonEdge* edge1, const FloatPolygonEdge* edge2)
{
return edge1->edgeIndex() < edge2->edgeIndex();
}
static Vector<const FloatPolygonEdge*> sortedOverlappingEdges(const FloatPolygon& polygon, float minY, float maxY)
{
Vector<const FloatPolygonEdge*> result;
polygon.overlappingEdges(minY, maxY, result);
std::sort(result.begin(), result.end(), compareEdgeIndex);
return result;
}
#define SIZEOF_ARRAY(p) (sizeof(p) / sizeof(p[0]))
/**
* Checks a right triangle. This test covers all of the trivial FloatPolygon accessors.
*
* 200,100
* /|
* / |
* / |
* -----
* 100,200 200,200
*/
TEST(FloatPolygonTest, basics)
{
const float triangleCoordinates[] = {200, 100, 200, 200, 100, 200};
FloatPolygonTestValue triangleTestValue(triangleCoordinates, SIZEOF_ARRAY(triangleCoordinates), RULE_NONZERO);
const FloatPolygon& triangle = triangleTestValue.polygon();
EXPECT_EQ(RULE_NONZERO, triangle.fillRule());
EXPECT_FALSE(triangle.isEmpty());
EXPECT_EQ(3u, triangle.numberOfVertices());
EXPECT_EQ(FloatPoint(200, 100), triangle.vertexAt(0));
EXPECT_EQ(FloatPoint(200, 200), triangle.vertexAt(1));
EXPECT_EQ(FloatPoint(100, 200), triangle.vertexAt(2));
EXPECT_EQ(3u, triangle.numberOfEdges());
EXPECT_EQ(FloatPoint(200, 100), triangle.edgeAt(0).vertex1());
EXPECT_EQ(FloatPoint(200, 200), triangle.edgeAt(0).vertex2());
EXPECT_EQ(FloatPoint(200, 200), triangle.edgeAt(1).vertex1());
EXPECT_EQ(FloatPoint(100, 200), triangle.edgeAt(1).vertex2());
EXPECT_EQ(FloatPoint(100, 200), triangle.edgeAt(2).vertex1());
EXPECT_EQ(FloatPoint(200, 100), triangle.edgeAt(2).vertex2());
EXPECT_EQ(0u, triangle.edgeAt(0).vertexIndex1());
EXPECT_EQ(1u, triangle.edgeAt(0).vertexIndex2());
EXPECT_EQ(1u, triangle.edgeAt(1).vertexIndex1());
EXPECT_EQ(2u, triangle.edgeAt(1).vertexIndex2());
EXPECT_EQ(2u, triangle.edgeAt(2).vertexIndex1());
EXPECT_EQ(0u, triangle.edgeAt(2).vertexIndex2());
EXPECT_EQ(200, triangle.edgeAt(0).minX());
EXPECT_EQ(200, triangle.edgeAt(0).maxX());
EXPECT_EQ(100, triangle.edgeAt(1).minX());
EXPECT_EQ(200, triangle.edgeAt(1).maxX());
EXPECT_EQ(100, triangle.edgeAt(2).minX());
EXPECT_EQ(200, triangle.edgeAt(2).maxX());
EXPECT_EQ(100, triangle.edgeAt(0).minY());
EXPECT_EQ(200, triangle.edgeAt(0).maxY());
EXPECT_EQ(200, triangle.edgeAt(1).minY());
EXPECT_EQ(200, triangle.edgeAt(1).maxY());
EXPECT_EQ(100, triangle.edgeAt(2).minY());
EXPECT_EQ(200, triangle.edgeAt(2).maxY());
EXPECT_EQ(0u, triangle.edgeAt(0).edgeIndex());
EXPECT_EQ(1u, triangle.edgeAt(1).edgeIndex());
EXPECT_EQ(2u, triangle.edgeAt(2).edgeIndex());
EXPECT_EQ(2u, triangle.edgeAt(0).previousEdge().edgeIndex());
EXPECT_EQ(1u, triangle.edgeAt(0).nextEdge().edgeIndex());
EXPECT_EQ(0u, triangle.edgeAt(1).previousEdge().edgeIndex());
EXPECT_EQ(2u, triangle.edgeAt(1).nextEdge().edgeIndex());
EXPECT_EQ(1u, triangle.edgeAt(2).previousEdge().edgeIndex());
EXPECT_EQ(0u, triangle.edgeAt(2).nextEdge().edgeIndex());
EXPECT_EQ(FloatRect(100, 100, 100, 100), triangle.boundingBox());
Vector<const FloatPolygonEdge*> resultA = sortedOverlappingEdges(triangle, 100, 200);
EXPECT_EQ(3u, resultA.size());
if (resultA.size() == 3) {
EXPECT_EQ(0u, resultA[0]->edgeIndex());
EXPECT_EQ(1u, resultA[1]->edgeIndex());
EXPECT_EQ(2u, resultA[2]->edgeIndex());
}
Vector<const FloatPolygonEdge*> resultB = sortedOverlappingEdges(triangle, 200, 200);
EXPECT_EQ(3u, resultB.size());
if (resultB.size() == 3) {
EXPECT_EQ(0u, resultB[0]->edgeIndex());
EXPECT_EQ(1u, resultB[1]->edgeIndex());
EXPECT_EQ(2u, resultB[2]->edgeIndex());
}
Vector<const FloatPolygonEdge*> resultC = sortedOverlappingEdges(triangle, 100, 150);
EXPECT_EQ(2u, resultC.size());
if (resultC.size() == 2) {
EXPECT_EQ(0u, resultC[0]->edgeIndex());
EXPECT_EQ(2u, resultC[1]->edgeIndex());
}
Vector<const FloatPolygonEdge*> resultD = sortedOverlappingEdges(triangle, 201, 300);
EXPECT_EQ(0u, resultD.size());
Vector<const FloatPolygonEdge*> resultE = sortedOverlappingEdges(triangle, 98, 99);
EXPECT_EQ(0u, resultE.size());
}
/**
* Tests FloatPolygon::contains() with a right triangle, and fillRule = nonzero.
*
* 200,100
* /|
* / |
* / |
* -----
* 100,200 200,200
*/
TEST(FloatPolygonTest, triangle_nonzero)
{
const float triangleCoordinates[] = {200, 100, 200, 200, 100, 200};
FloatPolygonTestValue triangleTestValue(triangleCoordinates, SIZEOF_ARRAY(triangleCoordinates), RULE_NONZERO);
const FloatPolygon& triangle = triangleTestValue.polygon();
EXPECT_EQ(RULE_NONZERO, triangle.fillRule());
EXPECT_TRUE(triangle.contains(FloatPoint(200, 100)));
EXPECT_TRUE(triangle.contains(FloatPoint(200, 200)));
EXPECT_TRUE(triangle.contains(FloatPoint(100, 200)));
EXPECT_TRUE(triangle.contains(FloatPoint(150, 150)));
EXPECT_FALSE(triangle.contains(FloatPoint(100, 100)));
EXPECT_FALSE(triangle.contains(FloatPoint(149, 149)));
EXPECT_FALSE(triangle.contains(FloatPoint(150, 200.5)));
EXPECT_FALSE(triangle.contains(FloatPoint(201, 200.5)));
}
/**
* Tests FloatPolygon::contains() with a right triangle, and fillRule = evenodd;
*
* 200,100
* /|
* / |
* / |
* -----
* 100,200 200,200
*/
TEST(FloatPolygonTest, triangle_evenodd)
{
const float triangleCoordinates[] = {200, 100, 200, 200, 100, 200};
FloatPolygonTestValue triangleTestValue(triangleCoordinates, SIZEOF_ARRAY(triangleCoordinates), RULE_EVENODD);
const FloatPolygon& triangle = triangleTestValue.polygon();
EXPECT_EQ(RULE_EVENODD, triangle.fillRule());
EXPECT_TRUE(triangle.contains(FloatPoint(200, 100)));
EXPECT_TRUE(triangle.contains(FloatPoint(200, 200)));
EXPECT_TRUE(triangle.contains(FloatPoint(100, 200)));
EXPECT_TRUE(triangle.contains(FloatPoint(150, 150)));
EXPECT_FALSE(triangle.contains(FloatPoint(100, 100)));
EXPECT_FALSE(triangle.contains(FloatPoint(149, 149)));
EXPECT_FALSE(triangle.contains(FloatPoint(150, 200.5)));
EXPECT_FALSE(triangle.contains(FloatPoint(201, 200.5)));
}
#define TEST_EMPTY(coordinates) \
{ \
FloatPolygonTestValue emptyPolygonTestValue(coordinates, SIZEOF_ARRAY(coordinates), RULE_NONZERO); \
const FloatPolygon& emptyPolygon = emptyPolygonTestValue.polygon(); \
EXPECT_TRUE(emptyPolygon.isEmpty()); \
}
TEST(FloatPolygonTest, emptyPolygons)
{
const float emptyCoordinates1[] = {0, 0};
TEST_EMPTY(emptyCoordinates1);
const float emptyCoordinates2[] = {0, 0, 1, 1};
TEST_EMPTY(emptyCoordinates2);
const float emptyCoordinates3[] = {0, 0, 1, 1, 2, 2, 3, 3};
TEST_EMPTY(emptyCoordinates3);
const float emptyCoordinates4[] = {0, 0, 1, 1, 2, 2, 3, 3, 1, 1};
TEST_EMPTY(emptyCoordinates4);
const float emptyCoordinates5[] = {0, 0, 0, 1, 0, 2, 0, 3, 0, 1};
TEST_EMPTY(emptyCoordinates5);
const float emptyCoordinates6[] = {0, 0, 1, 0, 2, 0, 3, 0, 1, 0};
TEST_EMPTY(emptyCoordinates6);
}
/*
* Test FloatPolygon::contains() with a trapezoid. The vertices are listed in counter-clockwise order.
*
* 150,100 250,100
* +----------+
* / \
* / \
* +----------------+
* 100,150 300,150
*/
TEST(FloatPolygonTest, trapezoid)
{
const float trapezoidCoordinates[] = {100, 150, 300, 150, 250, 100, 150, 100};
FloatPolygonTestValue trapezoidTestValue(trapezoidCoordinates, SIZEOF_ARRAY(trapezoidCoordinates), RULE_EVENODD);
const FloatPolygon& trapezoid = trapezoidTestValue.polygon();
EXPECT_FALSE(trapezoid.isEmpty());
EXPECT_EQ(4u, trapezoid.numberOfVertices());
EXPECT_EQ(FloatRect(100, 100, 200, 50), trapezoid.boundingBox());
EXPECT_TRUE(trapezoid.contains(FloatPoint(150, 100)));
EXPECT_TRUE(trapezoid.contains(FloatPoint(150, 101)));
EXPECT_TRUE(trapezoid.contains(FloatPoint(200, 125)));
EXPECT_FALSE(trapezoid.contains(FloatPoint(149, 100)));
EXPECT_FALSE(trapezoid.contains(FloatPoint(301, 150)));
}
/*
* Test FloatPolygon::contains() with a non-convex rectilinear polygon. The polygon has the same shape
* as the letter "H":
*
* 100,100 150,100 200,100 250,100
* +--------+ +--------+
* | | | |
* | | | |
* | +--------+ |
* | 150,150 200,150 |
* | |
* | 150,200 200,200 |
* | +--------+ |
* | | | |
* | | | |
* +--------+ +--------+
* 100,250 150,250 200,250 250,250
*/
TEST(FloatPolygonTest, rectilinear)
{
const float hCoordinates[] = {100, 100, 150, 100, 150, 150, 200, 150, 200, 100, 250, 100, 250, 250, 200, 250, 200, 200, 150, 200, 150, 250, 100, 250};
FloatPolygonTestValue hTestValue(hCoordinates, SIZEOF_ARRAY(hCoordinates), RULE_NONZERO);
const FloatPolygon& h = hTestValue.polygon();
EXPECT_FALSE(h.isEmpty());
EXPECT_EQ(12u, h.numberOfVertices());
EXPECT_EQ(FloatRect(100, 100, 150, 150), h.boundingBox());
EXPECT_TRUE(h.contains(FloatPoint(100, 100)));
EXPECT_TRUE(h.contains(FloatPoint(125, 100)));
EXPECT_TRUE(h.contains(FloatPoint(125, 125)));
EXPECT_TRUE(h.contains(FloatPoint(150, 100)));
EXPECT_TRUE(h.contains(FloatPoint(200, 200)));
EXPECT_TRUE(h.contains(FloatPoint(225, 225)));
EXPECT_TRUE(h.contains(FloatPoint(250, 250)));
EXPECT_TRUE(h.contains(FloatPoint(100, 250)));
EXPECT_TRUE(h.contains(FloatPoint(125, 250)));
EXPECT_FALSE(h.contains(FloatPoint(99, 100)));
EXPECT_FALSE(h.contains(FloatPoint(251, 100)));
EXPECT_FALSE(h.contains(FloatPoint(151, 100)));
EXPECT_FALSE(h.contains(FloatPoint(199, 100)));
EXPECT_FALSE(h.contains(FloatPoint(175, 125)));
EXPECT_FALSE(h.contains(FloatPoint(151, 250)));
EXPECT_FALSE(h.contains(FloatPoint(199, 250)));
EXPECT_FALSE(h.contains(FloatPoint(199, 250)));
EXPECT_FALSE(h.contains(FloatPoint(175, 225)));
}
} // namespace

View File

@ -1,80 +0,0 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ArenaTestHelpers_h
#define ArenaTestHelpers_h
#include "platform/PODArena.h"
#include "wtf/NotFound.h"
#include "wtf/Vector.h"
#include <gtest/gtest.h>
namespace blink {
namespace ArenaTestHelpers {
// An allocator for the PODArena which tracks the regions which have
// been allocated.
class TrackedAllocator final : public PODArena::FastMallocAllocator {
public:
static PassRefPtr<TrackedAllocator> create()
{
return adoptRef(new TrackedAllocator);
}
virtual void* allocate(size_t size) override
{
void* result = PODArena::FastMallocAllocator::allocate(size);
m_allocatedRegions.append(result);
return result;
}
virtual void free(void* ptr) override
{
size_t slot = m_allocatedRegions.find(ptr);
ASSERT_NE(slot, kNotFound);
m_allocatedRegions.remove(slot);
PODArena::FastMallocAllocator::free(ptr);
}
bool isEmpty() const
{
return !numRegions();
}
int numRegions() const
{
return m_allocatedRegions.size();
}
private:
TrackedAllocator() { }
Vector<void*> m_allocatedRegions;
};
} // namespace ArenaTestHelpers
} // namespace blink
#endif // ArenaTestHelpers_h