diff options
author | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-12 18:37:47 +0000 |
---|---|---|
committer | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-12 18:37:47 +0000 |
commit | d4281df8eff4a69740c98619920816dc16e43962 (patch) | |
tree | a453ac0cd2ebb74c95fbef7b851d74963fbc11cb /o3d | |
parent | dc8a534e3c56f7370f33fe5a7c82fc2a94643a3b (diff) | |
download | chromium_src-d4281df8eff4a69740c98619920816dc16e43962.zip chromium_src-d4281df8eff4a69740c98619920816dc16e43962.tar.gz chromium_src-d4281df8eff4a69740c98619920816dc16e43962.tar.bz2 |
Added an arena allocator and an augmentable red-black tree which
allocates its temporary objects from it. In a subsequent checkin, the
red-black tree will be augmented into an interval tree, which is a key
data structure for efficient implementation of one of the algorithms
in the forthcoming code in this directory.
I plan to submit this for C++ readability following code review.
Review URL: http://codereview.chromium.org/574024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38908 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d')
-rw-r--r-- | o3d/core/core.gyp | 6 | ||||
-rw-r--r-- | o3d/core/cross/gpu2d/arena.h | 215 | ||||
-rw-r--r-- | o3d/core/cross/gpu2d/arena_test.cc | 145 | ||||
-rw-r--r-- | o3d/core/cross/gpu2d/red_black_tree.h | 770 | ||||
-rw-r--r-- | o3d/core/cross/gpu2d/red_black_tree_test.cc | 192 | ||||
-rw-r--r-- | o3d/core/cross/gpu2d/tree_test_helpers.cc | 61 | ||||
-rw-r--r-- | o3d/core/cross/gpu2d/tree_test_helpers.h | 60 |
7 files changed, 1449 insertions, 0 deletions
diff --git a/o3d/core/core.gyp b/o3d/core/core.gyp index fb2f04d..b864f31 100644 --- a/o3d/core/core.gyp +++ b/o3d/core/core.gyp @@ -269,6 +269,8 @@ 'cross/viewport.h', 'cross/visitor_base.h', 'cross/weak_ptr.h', + 'cross/gpu2d/arena.h', + 'cross/gpu2d/red_black_tree.h', ], 'direct_dependent_settings': { 'include_dirs': [ @@ -509,6 +511,10 @@ 'cross/vertex_source_test.cc', 'cross/visitor_base_test.cc', 'cross/weak_ptr_test.cc', + 'cross/gpu2d/arena_test.cc', + 'cross/gpu2d/red_black_tree_test.cc', + 'cross/gpu2d/tree_test_helpers.cc', + 'cross/gpu2d/tree_test_helpers.h', ], }, }, diff --git a/o3d/core/cross/gpu2d/arena.h b/o3d/core/cross/gpu2d/arena.h new file mode 100644 index 0000000..67059d5 --- /dev/null +++ b/o3d/core/cross/gpu2d/arena.h @@ -0,0 +1,215 @@ +/* + * Copyright 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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 + * OWNER 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 O3D_CORE_CROSS_GPU2D_ARENA_H_ +#define O3D_CORE_CROSS_GPU2D_ARENA_H_ + +#include <assert.h> +#include <stddef.h> +#include <algorithm> +#include <list> + +#include "core/cross/types.h" + +namespace o3d { +namespace gpu2d { + +// An arena allocates large chunks of memory internally and allocates +// individual objects out of those chunks. Objects are not freed +// individually; their storage is reclaimed all at once when the arena +// is destroyed. The arena calls the constructors of the objects +// allocated within, but NOT their destructors. +class Arena { + public: + // The arena is configured with an allocator, which is responsible + // for allocating and freeing chunks of memory at a time. + class Allocator { + public: + virtual ~Allocator() {} + virtual void* Allocate(size_t size) = 0; + virtual void Free(void* ptr) = 0; + protected: + Allocator() {} + DISALLOW_COPY_AND_ASSIGN(Allocator); + }; + + // The Arena's default allocator, which uses malloc and free to + // allocate chunks of storage. + class MallocAllocator : public Allocator { + public: + virtual void* Allocate(size_t size) { + return ::malloc(size); + } + + virtual void Free(void* ptr) { + ::free(ptr); + } + }; + + // Creates a new Arena configured with a MallocAllocator, which is + // deleted when the arena is. + Arena() + : allocator_(new MallocAllocator), + should_delete_allocator_(true), + current_(NULL), + current_chunk_size_(kDefaultChunkSize) { + } + + // Creates a new Arena configured with the given Allocator. The + // Allocator is not deleted when the arena is. + explicit Arena(Allocator* allocator) + : allocator_(allocator), + should_delete_allocator_(false), + current_(NULL), + current_chunk_size_(kDefaultChunkSize) { + } + + // Deletes this arena. + ~Arena() { + for_each(chunks_.begin(), chunks_.end(), &Arena::DeleteChunk); + if (should_delete_allocator_) { + delete allocator_; + } + } + + // Allocates an object from the arena. + template<class T> + T* Alloc() { + void* ptr = NULL; + size_t rounded_size = RoundUp(sizeof(T), MinAlignment()); + if (current_ != NULL) + ptr = current_->Allocate(rounded_size); + + if (ptr == NULL) { + if (rounded_size > current_chunk_size_) + current_chunk_size_ = rounded_size; + current_ = new Chunk(allocator_, current_chunk_size_); + chunks_.push_back(current_); + ptr = current_->Allocate(rounded_size); + } + + if (ptr != NULL) { + // Allocate a T at this spot + new(ptr) T(); + } + + return static_cast<T*>(ptr); + } + + private: + enum { + kDefaultChunkSize = 16384 + }; + + // The following two structures are intended to automatically + // determine the platform's alignment constraint for structures. + struct AlignmentInner { + int64 x; + }; + struct AlignmentStruct { + char f1; + AlignmentInner f2; + }; + + // Returns the alignment requirement for classes and structs on the + // current platform. + static size_t MinAlignment() { + return offsetof(AlignmentStruct, f2); + } + + // Rounds up the given allocation size to the specified alignment. + size_t RoundUp(size_t size, size_t alignment) { + assert(alignment % 2 == 0); + return (size + alignment - 1) & ~(alignment - 1); + } + + Allocator* allocator_; + bool should_delete_allocator_; + + // Manages a chunk of memory and individual allocations out of it. + class Chunk { + public: + // Allocates a block of memory of the given size from the passed + // Allocator. + Chunk(Allocator* allocator, size_t size) + : allocator_(allocator), + size_(size) { + base_ = static_cast<uint8*>(allocator_->Allocate(size)); + current_offset_ = 0; + } + + // Frees the memory allocated from the Allocator in the + // constructor. + ~Chunk() { + allocator_->Free(base_); + } + + // Returns a pointer to "size" bytes of storage, or NULL if this + // Chunk could not satisfy the allocation. + void* Allocate(size_t size) { + // Check for overflow + if (current_offset_ + size < current_offset_) { + return NULL; + } + + if (current_offset_ + size > size_) { + return NULL; + } + void* result = base_ + current_offset_; + current_offset_ += size; + return result; + } + + private: + Allocator* allocator_; + uint8* base_; + size_t size_; + size_t current_offset_; + DISALLOW_COPY_AND_ASSIGN(Chunk); + }; + + // Callback used to delete an individual chunk. + static void DeleteChunk(Chunk* chunk) { + delete chunk; + } + + Chunk* current_; + std::list<Chunk*> chunks_; + size_t current_chunk_size_; + + DISALLOW_COPY_AND_ASSIGN(Arena); +}; + +} // namespace gpu2d +} // namespace o3d + +#endif // O3D_CORE_CROSS_GPU2D_ARENA_H_ + diff --git a/o3d/core/cross/gpu2d/arena_test.cc b/o3d/core/cross/gpu2d/arena_test.cc new file mode 100644 index 0000000..1a217f9 --- /dev/null +++ b/o3d/core/cross/gpu2d/arena_test.cc @@ -0,0 +1,145 @@ +/* + * Copyright 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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 + * OWNER 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. + */ + +// Tests for the arena. + +#include <algorithm> +#include <vector> + +#include "gtest/gtest.h" +#include "core/cross/gpu2d/arena.h" + +namespace o3d { +namespace gpu2d { + +namespace { + +// A base allocator for the Arena which tracks the regions +// which have been allocated. +class TrackedMallocAllocator : public Arena::Allocator { + public: + TrackedMallocAllocator() { + } + + virtual void* Allocate(size_t size) { + void* res = ::malloc(size); + allocated_regions_.push_back(res); + return res; + } + + virtual void Free(void* ptr) { + std::vector<void*>::iterator slot = std::find(allocated_regions_.begin(), + allocated_regions_.end(), + ptr); + ASSERT_TRUE(slot != allocated_regions_.end()); + allocated_regions_.erase(slot); + ::free(ptr); + } + + bool IsEmpty() { + return NumRegions() == 0; + } + + int NumRegions() { + return allocated_regions_.size(); + } + + private: + std::vector<void*> allocated_regions_; + DISALLOW_COPY_AND_ASSIGN(TrackedMallocAllocator); +}; + +// 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 ArenaTest : public testing::Test { +}; + +// Make sure the arena can successfully allocate from more than one +// region. +TEST_F(ArenaTest, CanAllocateFromMoreThanOneRegion) { + TrackedMallocAllocator base_allocator; + Arena arena(&base_allocator); + for (int i = 0; i < 10000; i++) { + arena.Alloc<TestClass1>(); + } + EXPECT_GT(base_allocator.NumRegions(), 1); +} + +// Make sure the arena frees all allocated regions during destruction. +TEST_F(ArenaTest, FreesAllAllocatedRegions) { + TrackedMallocAllocator* base_allocator = new TrackedMallocAllocator(); + Arena* arena = new Arena(base_allocator); + for (int i = 0; i < 3; i++) { + arena->Alloc<TestClass1>(); + } + EXPECT_GT(base_allocator->NumRegions(), 0); + delete arena; + EXPECT_TRUE(base_allocator->IsEmpty()); + delete base_allocator; +} + +// Make sure the arena runs constructors of the objects allocated within. +TEST_F(ArenaTest, RunsConstructors) { + Arena arena; + for (int i = 0; i < 10000; i++) { + TestClass1* tc1 = arena.Alloc<TestClass1>(); + EXPECT_EQ(0, tc1->x); + EXPECT_EQ(0, tc1->y); + EXPECT_EQ(0, tc1->z); + EXPECT_EQ(1, tc1->w); + TestClass2* tc2 = arena.Alloc<TestClass2>(); + EXPECT_EQ(1, tc2->a); + EXPECT_EQ(2, tc2->b); + EXPECT_EQ(3, tc2->c); + EXPECT_EQ(4, tc2->d); + } +} + +} // namespace gpu2d +} // namespace o3d + diff --git a/o3d/core/cross/gpu2d/red_black_tree.h b/o3d/core/cross/gpu2d/red_black_tree.h new file mode 100644 index 0000000..17e1e0a --- /dev/null +++ b/o3d/core/cross/gpu2d/red_black_tree.h @@ -0,0 +1,770 @@ +/* + * Copyright 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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 + * OWNER 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 O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_H_ +#define O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_H_ + +#include <string> + +#include "base/logging.h" +#include "core/cross/gpu2d/arena.h" + +namespace o3d { +namespace gpu2d { + +// Uncomment this for verbose logging output. +// #define O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + +// 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 that is stored in this red-black tree must support +// the default constructor, the copy constructor, and the "<" and "==" +// operators. 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. +// +// 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 "needs_full_ordering_comparisons" 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. +template<class T> +class RedBlackTree { + public: + // Visitor interface for walking all of the tree's elements. + class Visitor { + public: + virtual ~Visitor() {} + virtual void Visit(const T& data) = 0; + }; + + // Constructs a new red-black tree, allocating temporary objects + // from a newly constructed Arena. + RedBlackTree() + : arena_(new Arena()), + should_delete_arena_(true), + root_(NULL), + needs_full_ordering_comparisons_(false) { + } + + // Constructs a new red-black tree, allocating temporary objects + // from the given Arena. The Arena is not destroyed when this tree + // is. + explicit RedBlackTree(Arena* arena) + : arena_(arena), + should_delete_arena_(false), + root_(NULL), + needs_full_ordering_comparisons_(false) { + } + + // Destructor deletes the internal Arena if it was not explicitly + // passed to the constructor. + virtual ~RedBlackTree() { + if (should_delete_arena_) { + delete arena_; + } + } + + // Inserts a datum into the tree. + void Insert(const T& data) { + Node* node = new Node(data); + InsertNode(node); + } + + // Deletes the given datum from the tree. Returns true if the datum + // was found in the tree. + bool Delete(const T& data) { + Node* node = TreeSearch(data); + if (node != NULL) { + DeleteNode(node); + return true; + } + return false; + } + + // Returns true if the tree contains the given datum. + bool Contains(const T& data) { + return TreeSearch(data) != NULL; + } + + // Visits the nodes of this tree in order, calling the visitor on + // each one. + void VisitInorder(Visitor* visitor) { + if (root_ != NULL) { + VisitInorderImpl(root_, visitor); + } + } + + // Returns the number of elements in the tree. + int NumElements() { + // A Visitor which simply counts the number of visited elements. + class Counter : public Visitor { + public: + Counter() : count_(0) {} + + virtual void Visit(const T& data) { + ++count_; + } + + int count() { + return count_; + } + + private: + int count_; + // Using the DISALLOW_COPY_AND_ASSIGN macro for this local class + // breaks compilation on Windows because the declarations do not + // have method bodies. + Counter(const Counter&) {} + void operator=(const Counter&) {} + }; + Counter counter; + VisitInorder(&counter); + return counter.count(); + } + + // Returns true if the tree's invariants are preserved. + bool Verify() { + int black_count = 0; + return VerifyFromNode(root_, &black_count); + } + + // Dumps the tree's contents to the logging info stream for + // debugging purposes. + void Dump() { + DumpFromNode(root_, 0); + } + + // Sets whether this tree requires full ordering comparisons; see + // the class documentation for an explanation of this property. + void set_needs_full_ordering_comparisons( + bool needs_full_ordering_comparisons) { + needs_full_ordering_comparisons_ = needs_full_ordering_comparisons; + } + + protected: + enum Color { + kRed = 1, + kBlack + }; + + // 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 { + public: + // Constructor. Newly-created nodes are colored red. + explicit Node(const T& data) + : left_(NULL), + right_(NULL), + parent_(NULL), + color_(kRed), + data_(data) { + } + + virtual ~Node() { + } + + Color color() const { + return color_; + } + + void set_color(Color color) { + color_ = color; + } + + // Fetches the user data. + const T& data() const { + return data_; + } + + // Copies all user-level fields from the source node, but not + // internal fields. For example, the base implementation of this + // method copies the "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) { + data_ = src->data(); + } + + Node* left() const { + return left_; + } + + void set_left(Node* node) { + left_ = node; + } + + Node* right() const { + return right_; + } + + void set_right(Node* node) { + right_ = node; + } + + Node* parent() const { + return parent_; + } + + void set_parent(Node* node) { + parent_ = node; + } + + private: + Node* left_; + Node* right_; + Node* parent_; + Color color_; + T data_; + DISALLOW_COPY_AND_ASSIGN(Node); + }; + + // Returns the root of the tree, which is needed by some subclasses. + Node* root() const { + return 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* node) { + return false; + } + + //---------------------------------------------------------------------- + // Generic binary search tree operations + // + + // Searches the tree for the given datum. + Node* TreeSearch(const T& data) { + if (needs_full_ordering_comparisons_) { + return TreeSearchFullComparisons(root_, data); + } else { + return TreeSearchNormal(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) { + while (current != NULL) { + if (current->data() == data) { + return current; + } + if (data < current->data()) { + current = current->left(); + } else { + current = current->right(); + } + } + return NULL; + } + + // 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) { + if (current == NULL) { + return NULL; + } + if (data < current->data()) { + return TreeSearchFullComparisons(current->left(), data); + } else if (current->data() < data) { + return TreeSearchFullComparisons(current->right(), data); + } else { + if (data == current->data()) { + return current; + } else { + // We may need to traverse both the left and right subtrees. + Node* result = TreeSearchFullComparisons(current->left(), data); + if (result == NULL) { + result = TreeSearchFullComparisons(current->right(), data); + } + return result; + } + } + } + + // Inserts the given node into the tree. + void TreeInsert(Node* z) { + Node* y = NULL; + Node* x = root_; + while (x != NULL) { + y = x; + if (z->data() < x->data()) + x = x->left(); + else + x = x->right(); + } + z->set_parent(y); + if (y == NULL) { + root_ = z; + } else { + if (z->data() < y->data()) { + y->set_left(z); + } else { + y->set_right(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() != NULL) + return TreeMinimum(x->right()); + Node* y = x->parent(); + while (y != NULL && 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() != NULL) { + x = x->left(); + } + return x; + } + + // Helper for maintaining the augmented red-black tree. + void PropagateUpdates(Node* start) { + bool should_continue = true; + while (start != NULL && should_continue) { + should_continue = UpdateNode(start); + start = start->parent(); + } + } + + //---------------------------------------------------------------------- + // Red-Black tree operations + // + + // Left-rotates the subtree rooted at x. + void LeftRotate(Node* x) { + // Set y. + Node* y = x->right(); + // Turn y's left subtree into x's right subtree. + x->set_right(y->left()); + if (y->left() != NULL) { + y->left()->set_parent(x); + } + // Link x's parent to y. + y->set_parent(x->parent()); + if (x->parent() == NULL) { + root_ = y; + } else { + if (x == x->parent()->left()) { + x->parent()->set_left(y); + } else { + x->parent()->set_right(y); + } + } + // Put x on y's left. + y->set_left(x); + x->set_parent(y); + // Update nodes lowest to highest. + UpdateNode(x); + UpdateNode(y); + } + + // Right-rotates the subtree rooted at y. + void RightRotate(Node* y) { + // Set x. + Node* x = y->left(); + // Turn x's right subtree into y's left subtree. + y->set_left(x->right()); + if (x->right() != NULL) { + x->right()->set_parent(y); + } + // Link y's parent to x. + x->set_parent(y->parent()); + if (y->parent() == NULL) { + root_ = x; + } else { + if (y == y->parent()->left()) { + y->parent()->set_left(x); + } else { + y->parent()->set_right(x); + } + } + // Put y on x's right. + x->set_right(y); + y->set_parent(x); + // Update nodes lowest to highest. + UpdateNode(y); + UpdateNode(x); + } + + // Inserts the given node into the tree. + void InsertNode(Node* x) { + TreeInsert(x); + x->set_color(kRed); + UpdateNode(x); + +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " RedBlackTree::InsertNode"; +#endif + + // The node from which to start propagating updates upwards. + Node* update_start = x->parent(); + + while (x != root_ && x->parent()->color() == kRed) { + if (x->parent() == x->parent()->parent()->left()) { + Node* y = x->parent()->parent()->right(); + if (y != NULL && y->color() == kRed) { + // Case 1 +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " Case 1/1"; +#endif + x->parent()->set_color(kBlack); + y->set_color(kBlack); + x->parent()->parent()->set_color(kRed); + UpdateNode(x->parent()); + x = x->parent()->parent(); + UpdateNode(x); + update_start = x->parent(); + } else { + if (x == x->parent()->right()) { +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " Case 1/2"; +#endif + // Case 2 + x = x->parent(); + LeftRotate(x); + } + // Case 3 +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " Case 1/3"; +#endif + x->parent()->set_color(kBlack); + x->parent()->parent()->set_color(kRed); + RightRotate(x->parent()->parent()); + update_start = x->parent()->parent(); + } + } else { + // Same as "then" clause with "right" and "left" exchanged. + Node* y = x->parent()->parent()->left(); + if (y != NULL && y->color() == kRed) { + // Case 1 +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " Case 2/1"; +#endif + x->parent()->set_color(kBlack); + y->set_color(kBlack); + x->parent()->parent()->set_color(kRed); + UpdateNode(x->parent()); + x = x->parent()->parent(); + UpdateNode(x); + update_start = x->parent(); + } else { + if (x == x->parent()->left()) { + // Case 2 +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " Case 2/2"; +#endif + x = x->parent(); + RightRotate(x); + } + // Case 3 +#ifdef O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_VERBOSE + DLOG(INFO) << " Case 2/3"; +#endif + x->parent()->set_color(kBlack); + x->parent()->parent()->set_color(kRed); + LeftRotate(x->parent()->parent()); + update_start = x->parent()->parent(); + } + } + } + + PropagateUpdates(update_start); + + root_->set_color(kBlack); + } + + // Restores the red-black property to the tree after splicing out + // a node. Note that x may be NULL, which is why x_parent must be + // supplied. + void DeleteFixup(Node* x, Node* x_parent) { + while (x != root_ && (x == NULL || x->color() == kBlack)) { + if (x == x_parent->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 = x_parent->right(); + DCHECK(w != NULL) << "x's sibling should not be null."; + if (w->color() == kRed) { + // Case 1 + w->set_color(kBlack); + x_parent->set_color(kRed); + LeftRotate(x_parent); + w = x_parent->right(); + } + if ((w->left() == NULL || w->left()->color() == kBlack) && + (w->right() == NULL || w->right()->color() == kBlack)) { + // Case 2 + w->set_color(kRed); + x = x_parent; + x_parent = x->parent(); + } else { + if (w->right() == NULL || w->right()->color() == kBlack) { + // Case 3 + w->left()->set_color(kBlack); + w->set_color(kRed); + RightRotate(w); + w = x_parent->right(); + } + // Case 4 + w->set_color(x_parent->color()); + x_parent->set_color(kBlack); + if (w->right() != NULL) { + w->right()->set_color(kBlack); + } + LeftRotate(x_parent); + x = root_; + x_parent = 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 = x_parent->left(); + DCHECK(w != NULL) << "x's sibling should not be null."; + if (w->color() == kRed) { + // Case 1 + w->set_color(kBlack); + x_parent->set_color(kRed); + RightRotate(x_parent); + w = x_parent->left(); + } + if ((w->right() == NULL || w->right()->color() == kBlack) && + (w->left() == NULL || w->left()->color() == kBlack)) { + // Case 2 + w->set_color(kRed); + x = x_parent; + x_parent = x->parent(); + } else { + if (w->left() == NULL || w->left()->color() == kBlack) { + // Case 3 + w->right()->set_color(kBlack); + w->set_color(kRed); + LeftRotate(w); + w = x_parent->left(); + } + // Case 4 + w->set_color(x_parent->color()); + x_parent->set_color(kBlack); + if (w->left() != NULL) { + w->left()->set_color(kBlack); + } + RightRotate(x_parent); + x = root_; + x_parent = x->parent(); + } + } + } + if (x != NULL) { + x->set_color(kBlack); + } + } + + // 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() == NULL || z->right() == NULL) { + y = z; + } else { + y = TreeSuccessor(z); + } + // Y is guaranteed to be non-NULL at this point. + Node* x; + if (y->left() != NULL) { + 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* x_parent; + if (x != NULL) { + x->set_parent(y->parent()); + x_parent = x->parent(); + } else { + x_parent = y->parent(); + } + if (y->parent() == NULL) { + root_ = x; + } else { + if (y == y->parent()->left()) { + y->parent()->set_left(x); + } else { + y->parent()->set_right(x); + } + } + // It has been found empirically that both of these upward + // propagations are necessary. + if (y != z) { + z->CopyFrom(y); + PropagateUpdates(z); + } + if (x_parent != NULL) { + PropagateUpdates(x_parent); + } + if (y->color() == kBlack) { + DeleteFixup(x, x_parent); + } + } + + // Visits the subtree rooted at the given node in order. + void VisitInorderImpl(Node* node, Visitor* visitor) { + if (node->left() != NULL) + VisitInorderImpl(node->left(), visitor); + visitor->Visit(node->data()); + if (node->right() != NULL) + VisitInorderImpl(node->right(), visitor); + } + + //---------------------------------------------------------------------- + // Verification and debugging routines + // + + // Returns true if verification succeeded. Returns in the + // "black_count" parameter the number of black children along all + // paths to all leaves of the given node. + bool VerifyFromNode(Node* node, int* black_count) { + // Base case is a leaf node. + if (node == NULL) { + *black_count = 1; + return true; + } + + // Each node is either red or black. + if (!(node->color() == kRed || node->color() == kBlack)) + return false; + + // Every leaf (or NULL) is black. + + if (node->color() == kRed) { + // Both of its children are black. + if (!((node->left() == NULL || node->left()->color() == kBlack))) + return false; + if (!((node->right() == NULL || node->right()->color() == kBlack))) + return false; + } + + // Every simple path to a leaf node contains the same number of + // black nodes. + int left_count = 0, right_count = 0; + bool left_valid = VerifyFromNode(node->left(), &left_count); + bool right_valid = VerifyFromNode(node->right(), &right_count); + if (!left_valid || !right_valid) + return false; + *black_count = left_count + (node->color() == kBlack ? 1 : 0); + return left_count == right_count; + } + + // Dumps the subtree rooted at the given node. + void DumpFromNode(Node* node, int indentation) { + std::string prefix; + for (int i = 0; i < indentation; i++) { + prefix += " "; + } + prefix += "-"; + if (node == NULL) { + DLOG(INFO) << prefix; + return; + } + DLOG(INFO) << prefix << " " << node->data() + << ((node->color() == kBlack) ? " (black)" : " (red)"); + DumpFromNode(node->left(), indentation + 2); + DumpFromNode(node->right(), indentation + 2); + } + + //---------------------------------------------------------------------- + // Data members + + Arena* arena_; + bool should_delete_arena_; + Node* root_; + bool needs_full_ordering_comparisons_; + + DISALLOW_COPY_AND_ASSIGN(RedBlackTree); +}; + +} // namespace gpu2d +} // namespace o3d + +#endif // O3D_CORE_CROSS_GPU2D_RED_BLACK_TREE_H_ + diff --git a/o3d/core/cross/gpu2d/red_black_tree_test.cc b/o3d/core/cross/gpu2d/red_black_tree_test.cc new file mode 100644 index 0000000..16ceb27 --- /dev/null +++ b/o3d/core/cross/gpu2d/red_black_tree_test.cc @@ -0,0 +1,192 @@ +/* + * Copyright 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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 + * OWNER 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. + */ + +// Tests for the red-black tree class. + +#include <vector> + +#include "gtest/gtest.h" +#include "core/cross/gpu2d/red_black_tree.h" +#include "core/cross/gpu2d/tree_test_helpers.h" + +namespace o3d { +namespace gpu2d { + +class RedBlackTreeTest : public testing::Test { +}; + +TEST_F(RedBlackTreeTest, TestSingleElementInsertion) { + RedBlackTree<int> tree; + tree.Insert(5); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(5)); +} + +TEST_F(RedBlackTreeTest, TestMultipleElementInsertion) { + RedBlackTree<int> tree; + tree.Insert(4); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(4)); + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(3)); + tree.Insert(5); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(5)); + EXPECT_TRUE(tree.Contains(4)); + EXPECT_TRUE(tree.Contains(3)); +} + +TEST_F(RedBlackTreeTest, TestDuplicateElementInsertion) { + RedBlackTree<int> tree; + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + EXPECT_EQ(3, tree.NumElements()); + EXPECT_TRUE(tree.Contains(3)); +} + +TEST_F(RedBlackTreeTest, TestSingleElementInsertionAndDeletion) { + RedBlackTree<int> tree; + tree.Insert(5); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(5)); + tree.Delete(5); + EXPECT_TRUE(tree.Verify()); + EXPECT_FALSE(tree.Contains(5)); +} + +TEST_F(RedBlackTreeTest, TestMultipleElementInsertionAndDeletion) { + RedBlackTree<int> tree; + tree.Insert(4); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(4)); + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(3)); + tree.Insert(5); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(5)); + EXPECT_TRUE(tree.Contains(4)); + EXPECT_TRUE(tree.Contains(3)); + tree.Delete(4); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(3)); + EXPECT_FALSE(tree.Contains(4)); + EXPECT_TRUE(tree.Contains(5)); + tree.Delete(5); + EXPECT_TRUE(tree.Verify()); + EXPECT_TRUE(tree.Contains(3)); + EXPECT_FALSE(tree.Contains(4)); + EXPECT_FALSE(tree.Contains(5)); + EXPECT_EQ(1, tree.NumElements()); +} + +TEST_F(RedBlackTreeTest, TestDuplicateElementInsertionAndDeletion) { + RedBlackTree<int> tree; + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + tree.Insert(3); + EXPECT_TRUE(tree.Verify()); + EXPECT_EQ(3, tree.NumElements()); + EXPECT_TRUE(tree.Contains(3)); + tree.Delete(3); + EXPECT_TRUE(tree.Verify()); + tree.Delete(3); + EXPECT_TRUE(tree.Verify()); + EXPECT_EQ(1, tree.NumElements()); + EXPECT_TRUE(tree.Contains(3)); + tree.Delete(3); + EXPECT_TRUE(tree.Verify()); + EXPECT_EQ(0, tree.NumElements()); + EXPECT_FALSE(tree.Contains(3)); +} + +TEST_F(RedBlackTreeTest, FailingInsertionRegressionTest1) { + RedBlackTree<int> tree; + tree.Insert(5113); + EXPECT_TRUE(tree.Verify()); + tree.Insert(4517); + EXPECT_TRUE(tree.Verify()); + tree.Insert(3373); + EXPECT_TRUE(tree.Verify()); + tree.Insert(9307); + EXPECT_TRUE(tree.Verify()); + tree.Insert(7077); + EXPECT_TRUE(tree.Verify()); +} + +namespace { +void InsertionAndDeletionTest(int32 seed, int tree_size) { + InitRandom(seed); + int max_val = tree_size; + // Build the tree. + RedBlackTree<int> tree; + std::vector<int> values; + for (int i = 0; i < tree_size; i++) { + int val = NextRandom(max_val); + tree.Insert(val); + EXPECT_TRUE(tree.Verify()) << "Test failed for seed " << seed; + values.push_back(val); + } + // Churn the tree's contents. + for (int i = 0; i < tree_size; i++) { + // Pick a random value to remove. + int idx = NextRandom(tree_size); + int val = values[idx]; + // Remove this value. + tree.Delete(val); + EXPECT_TRUE(tree.Verify()) << "Test failed for seed " << seed; + // Replace it with a new one. + val = NextRandom(max_val); + values[idx] = val; + tree.Insert(val); + EXPECT_TRUE(tree.Verify()) << "Test failed for seed " << seed; + } +} +} // anonymous namespace + +TEST_F(RedBlackTreeTest, RandomDeletionAndInsertionRegressionTest1) { + InsertionAndDeletionTest(12311, 100); +} + +TEST_F(RedBlackTreeTest, TestRandomDeletionAndInsertion) { + InsertionAndDeletionTest(GenerateSeed(), 100); +} + +} // namespace gpu2d +} // namespace o3d + diff --git a/o3d/core/cross/gpu2d/tree_test_helpers.cc b/o3d/core/cross/gpu2d/tree_test_helpers.cc new file mode 100644 index 0000000..d78fb74 --- /dev/null +++ b/o3d/core/cross/gpu2d/tree_test_helpers.cc @@ -0,0 +1,61 @@ +/* + * Copyright 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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 + * OWNER 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 <cstdlib> +#include "base/rand_util.h" +#include "core/cross/gpu2d/tree_test_helpers.h" + +namespace o3d { +namespace gpu2d { + +int32 GenerateSeed() { + // A seed of 1 has the special behavior of resetting the random + // number generator. Assume that if we call this routine that we + // don't want this behavior. + int seed = 0; + do { + seed = base::RandInt(0, 2 << 15); + } while (seed <= 1); + return seed; +} + +void InitRandom(int32 seed) { + srand(seed); +} + +int32 NextRandom(int32 max) { + // rand_r is not available on Windows + return rand() % max; // NOLINT +} + +} // namespace gpu2d +} // namespace o3d + diff --git a/o3d/core/cross/gpu2d/tree_test_helpers.h b/o3d/core/cross/gpu2d/tree_test_helpers.h new file mode 100644 index 0000000..1817f9a --- /dev/null +++ b/o3d/core/cross/gpu2d/tree_test_helpers.h @@ -0,0 +1,60 @@ +/* + * Copyright 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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 + * OWNER 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 O3D_CORE_CROSS_GPU2D_TREE_TEST_HELPERS_H_ +#define O3D_CORE_CROSS_GPU2D_TREE_TEST_HELPERS_H_ + +#include "base/basictypes.h" + +namespace o3d { +namespace gpu2d { + +// Simple pseudorandom number generator helper functions, used by the +// red-black and interval tree tests. +// +// These are **not** thread safe! + +// Generates a seed value to be passed to InitRandom(). +int32 GenerateSeed(); + +// Initializes the pseudo-random number generator with a specific seed. +void InitRandom(int32 seed); + +// Produces the next pseudo-random number in the sequence, in the +// range from [0..max). Negative numbers are not allowed and will +// produce undefined results. +int32 NextRandom(int32 max); + +} // namespace gpu2d +} // namespace o3d + +#endif // O3D_CORE_CROSS_GPU2D_TREE_TEST_HELPERS_H_ + |