mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
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:
parent
d11b95703b
commit
e9073fbdc0
@ -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"
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user