Browse Source

Added fixed node size allocator for List, Map & Set.
Fixed Map & Set iteration bugs.

Lasse Öörni 14 years ago
parent
commit
db1bd31f41

+ 112 - 0
Engine/Container/Allocator.cpp

@@ -0,0 +1,112 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Allocator.h"
+
+Allocator* AllocatorGetBlock(unsigned nodeSize, unsigned capacity)
+{
+    if (!capacity)
+        capacity = 1;
+    
+    unsigned char* blockPtr = new unsigned char[sizeof(Allocator) + capacity * (sizeof(AllocatorNode) + nodeSize)];
+    Allocator* newBlock = reinterpret_cast<Allocator*>(blockPtr);
+    newBlock->nodeSize_ = nodeSize;
+    newBlock->capacity_ = capacity;
+    newBlock->nextBlock_ = 0;
+    
+    /// Initialize the nodes
+    unsigned char* nodePtr = blockPtr + sizeof(Allocator);
+    for (unsigned i = 0; i < capacity; ++i)
+    {
+        AllocatorNode* newNode = reinterpret_cast<AllocatorNode*>(nodePtr);
+        AllocatorNode* nextNode = 0;
+        
+        if (!i)
+            newBlock->firstFreeNode_ = newNode;
+        if (i < capacity - 1)
+            nextNode = reinterpret_cast<AllocatorNode*>(nodePtr + sizeof(AllocatorNode) + nodeSize);
+        
+        newNode->parentBlock_ = newBlock;
+        newNode->nextFreeNode_ = nextNode;
+        
+        nodePtr += sizeof(AllocatorNode) + nodeSize;
+    }
+    
+    return newBlock;
+}
+
+Allocator* AllocatorInitialize(unsigned nodeSize, unsigned initialCapacity)
+{
+    return AllocatorGetBlock(nodeSize, initialCapacity);
+}
+
+void AllocatorUninitialize(Allocator* block)
+{
+    while (block)
+    {
+        Allocator* next = block->nextBlock_;
+        delete[] reinterpret_cast<unsigned char*>(block);
+        block = next;
+    }
+}
+
+void* AllocatorGet(Allocator* block)
+{
+    Allocator* lastBlock;
+    while (block)
+    {
+        /// Check if a free node exists
+        if (block->firstFreeNode_)
+        {
+            AllocatorNode* freeNode = block->firstFreeNode_;
+            void* payload = reinterpret_cast<unsigned char*>(freeNode) + sizeof(Allocator*) + sizeof(AllocatorNode*);
+            block->firstFreeNode_ = freeNode->nextFreeNode_;
+            freeNode->nextFreeNode_ = 0;
+            return payload;
+        }
+        /// Go to the next block
+        lastBlock = block;
+        block = block->nextBlock_;
+    }
+    /// Blocks have been exhausted. Allocate a new larger block, then return its first node
+    unsigned increment = lastBlock->capacity_ >> 1;
+    if (!increment)
+        increment = 1;
+    block = AllocatorGetBlock(lastBlock->nodeSize_, lastBlock->capacity_ + increment);
+    lastBlock->nextBlock_ = block;
+    
+    AllocatorNode* freeNode = block->firstFreeNode_;
+    void* payload = reinterpret_cast<unsigned char*>(freeNode) + sizeof(Allocator*) + sizeof(AllocatorNode*);
+    block->firstFreeNode_ = freeNode->nextFreeNode_;
+    freeNode->nextFreeNode_ = 0;
+    return payload;
+}
+
+void AllocatorFree(void* payload)
+{
+    unsigned char* payloadPtr = static_cast<unsigned char*>(payload);
+    AllocatorNode* node = reinterpret_cast<AllocatorNode*>(payloadPtr - sizeof(Allocator*) - sizeof(AllocatorNode*));
+    Allocator* block = node->parentBlock_;
+    node->nextFreeNode_ = block->firstFreeNode_;
+    block->firstFreeNode_ = node;
+}

+ 54 - 0
Engine/Container/Allocator.h

@@ -0,0 +1,54 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+struct Allocator;
+struct AllocatorNode;
+
+/// Allocator memory block
+struct Allocator
+{
+    unsigned nodeSize_;
+    unsigned capacity_;
+    AllocatorNode* firstFreeNode_;
+    Allocator* nextBlock_;
+    /// Nodes follow
+};
+
+/// Allocator node
+struct AllocatorNode
+{
+    Allocator* parentBlock_;
+    AllocatorNode* nextFreeNode_;
+    /// Payload follows
+};
+
+/// Initialize a fixed allocator with the allocation size and initial capacity
+Allocator* AllocatorInitialize(unsigned nodeSize, unsigned initialCapacity = 1);
+/// Uninitialize a fixed allocator. Frees all blocks in the chain
+void AllocatorUninitialize(Allocator* block);
+/// Allocate a node. Reserves a new block if necessary
+void* AllocatorGet(Allocator* block);
+/// Free a node. Does not free any blocks
+void AllocatorFree(void* payload);

+ 36 - 8
Engine/Container/List.h

@@ -25,6 +25,8 @@
 
 #include "ListBase.h"
 
+#include <new>
+
 /// Linked list template class
 template <class T> class List : public ListBase
 {
@@ -116,8 +118,9 @@ public:
         ListBase()
     {
         // Allocate and link the head and tail nodes
-        head_ = new Node();
-        tail_ = new Node();
+        AllocatorInitialize(sizeof(Node), 2);
+        head_ = AllocateNode();
+        tail_ = AllocateNode();
         Node* head = GetHead();
         Node* tail = GetTail();
         head->next_ = tail;
@@ -129,8 +132,9 @@ public:
         ListBase()
     {
         // Allocate and link the head and tail nodes
-        head_ = new Node();
-        tail_ = new Node();
+        AllocatorInitialize(sizeof(Node), list.Size() + 2);
+        head_ = AllocateNode();
+        tail_ = AllocateNode();
         Node* head = GetHead();
         Node* tail = GetTail();
         head->next_ = tail;
@@ -144,8 +148,9 @@ public:
     ~List()
     {
         Clear();
-        delete GetHead();
-        delete GetTail();
+        FreeNode(GetHead());
+        FreeNode(GetTail());
+        AllocatorUninitialize(allocator_);
     }
     
     /// Assign from another list
@@ -306,7 +311,7 @@ private:
         if ((!dest) || (dest == head_))
             return;
         
-        Node* newNode = new Node(value);
+        Node* newNode = AllocateNode(value);
         newNode->next_ = dest;
         newNode->prev_ = dest->prev_;
         dest->prev_ = newNode;
@@ -325,7 +330,30 @@ private:
         Node* next = toRemove->GetNext();
         prev->next_ = next;
         next->prev_ = prev;
-        delete toRemove;
+        FreeNode(toRemove);
         --size_;
     }
+    
+    /// Allocate a node
+    Node* AllocateNode()
+    {
+        Node* newNode = static_cast<Node*>(AllocatorGet(allocator_));
+        new(newNode) Node();
+        return newNode;
+    }
+    
+    /// Allocate a node with initial value
+    Node* AllocateNode(const T& value)
+    {
+        Node* newNode = static_cast<Node*>(AllocatorGet(allocator_));
+        new(newNode) Node(value);
+        return newNode;
+    }
+    
+    /// Free a node
+    void FreeNode(Node* node)
+    {
+        (node)->~Node();
+        AllocatorFree(node);
+    }
 };

+ 36 - 2
Engine/Container/ListBase.h

@@ -23,6 +23,7 @@
 
 #pragma once
 
+#include "Allocator.h"
 #include "Swap.h"
 
 #include <cstdlib>
@@ -79,6 +80,7 @@ class ListBase
 {
 public:
     ListBase() :
+        allocator_(0),
         size_(0)
     {
     }
@@ -88,6 +90,7 @@ public:
     {
         ::Swap(head_, rhs.head_);
         ::Swap(tail_, rhs.tail_);
+        ::Swap(allocator_, rhs.allocator_);
         ::Swap(size_, rhs.size_);
     }
     
@@ -96,6 +99,8 @@ protected:
     ListNodeBase* head_;
     /// Tail node pointer
     ListNodeBase* tail_;
+    /// Node allocator
+    Allocator* allocator_;
     /// Number of nodes
     unsigned size_;
 };
@@ -135,12 +140,16 @@ class TreeIteratorBase
 {
 public:
     TreeIteratorBase() :
-        ptr_(0)
+        ptr_(0),
+        next_(0),
+        prev_(0)
     {
     }
     
     TreeIteratorBase(TreeNodeBase* ptr) :
-        ptr_(ptr)
+        ptr_(ptr),
+        next_(0),
+        prev_(0)
     {
     }
     
@@ -152,7 +161,15 @@ public:
     void GotoNext()
     {
         if (!ptr_)
+        {
+            ptr_ = next_;
+            next_ = 0;
+            prev_ = 0;
             return;
+        }
+        
+        next_ = 0;
+        prev_ = ptr_;
         
         if (!ptr_->link_[1])
         {
@@ -171,7 +188,15 @@ public:
     void GotoPrev()
     {
         if (!ptr_)
+        {
+            ptr_ = prev_;
+            next_ = 0;
+            prev_ = 0;
             return;
+        }
+        
+        next_ = ptr_;
+        prev_ = 0;
         
         if (!ptr_->link_[0])
         {
@@ -187,7 +212,12 @@ public:
             ptr_ = ptr_->link_[1];
     }
     
+    /// Current node pointer
     TreeNodeBase* ptr_;
+    /// Next node pointer
+    TreeNodeBase* next_;
+    /// Previous node pointer
+    TreeNodeBase* prev_;
 };
 
 /// Red-black tree base class
@@ -197,6 +227,7 @@ public:
     /// Construct
     TreeBase() :
         root_(0),
+        allocator_(0),
         size_(0)
     {
     }
@@ -205,6 +236,7 @@ public:
     void Swap(TreeBase& rhs)
     {
         ::Swap(root_, rhs.root_);
+        ::Swap(allocator_, rhs.allocator_);
         ::Swap(size_, rhs.size_);
     }
     
@@ -235,6 +267,8 @@ protected:
     
     /// Root node
     TreeNodeBase* root_;
+    /// Node allocator
+    Allocator* allocator_;
     /// Number of nodes
     unsigned size_;
 };

+ 32 - 3
Engine/Container/Map.h

@@ -120,7 +120,7 @@ public:
         KeyValue& operator * () const { return (static_cast<Node*>(ptr_))->pair_; }
     };
     
-    /// Set node const iterator
+    /// Map node const iterator
     class ConstIterator : public TreeIteratorBase
     {
     public:
@@ -161,13 +161,15 @@ public:
     /// Construct with another map
     Map(const Map<T, U>& map)
     {
+        AllocatorInitialize(sizeof(Node), map.Size());
         *this = map;
     }
     
-    /// Destruct the set
+    /// Destruct the map
     ~Map()
     {
         Clear();
+        AllocatorUninitialize(allocator_);
     }
     
     /// Assign a map
@@ -319,7 +321,7 @@ public:
     const T& Back() { return FindLast()->key_; }
     /// Return number of keys
     unsigned Size() const { return size_; }
-    /// Return whether the set is empty
+    /// Return whether the map is empty
     bool Empty() const { return size_ == 0; }
     
 private:
@@ -525,4 +527,31 @@ private:
         if (right)
             EraseNodes(right);
     }
+    
+    /// Allocate a node
+    Node* AllocateNode()
+    {
+        if (!allocator_)
+            allocator_ = AllocatorInitialize(sizeof(Node));
+        Node* newNode = static_cast<Node*>(AllocatorGet(allocator_));
+        new(newNode) Node();
+        return newNode;
+    }
+    
+    /// Allocate a node with specified key and value
+    Node* AllocateNode(const T& key, const U& value)
+    {
+        if (!allocator_)
+            allocator_ = AllocatorInitialize(sizeof(Node));
+        Node* newNode = static_cast<Node*>(AllocatorGet(allocator_));
+        new(newNode) Node(key, value);
+        return newNode;
+    }
+    
+    /// Free a node
+    void FreeNode(Node* node)
+    {
+        (node)->~Node();
+        AllocatorFree(node);
+    }
 };

+ 0 - 3
Engine/Container/PODVector.h

@@ -372,9 +372,6 @@ public:
     /// Return whether vector is empty
     bool Empty() const { return size_ == 0; }
     
-    /// Minimum dynamic allocation size
-    static const unsigned MIN_CAPACITY = 1;
-    
 private:
     /// Return the buffer with right type
     T* GetBuffer() const { return reinterpret_cast<T*>(buffer_); }

+ 33 - 4
Engine/Container/Set.h

@@ -122,6 +122,7 @@ public:
     /// Construct with another set
     Set(const Set<T>& set)
     {
+        AllocatorInitialize(sizeof(Node), set.Size());
         *this = set;
     }
     
@@ -129,6 +130,7 @@ public:
     ~Set()
     {
         Clear();
+        AllocatorUninitialize(allocator_);
     }
     
     /// Assign a set
@@ -312,7 +314,7 @@ private:
         
         if (!root_)
         {
-            root_ = ret = new Node(key);
+            root_ = ret = AllocateNode(key);
             ++size_;
         }
         else
@@ -332,7 +334,7 @@ private:
             {
                 if (!q)
                 {
-                    p->SetChild(dir, q = ret = new Node(key));
+                    p->SetChild(dir, q = ret = AllocateNode(key));
                     ++size_;
                 }
                 else if ((isRed(q->link_[0])) && (isRed(q->link_[1])))
@@ -442,7 +444,7 @@ private:
         {
             f->key_ = q->key_;
             p->SetChild(p->GetChild(1) == q, q->link_[q->GetChild(0) == 0]);
-            delete q;
+            FreeNode(q);
             --size_;
             removed = true;
         }
@@ -462,7 +464,7 @@ private:
     {
         Node* left = node->GetChild(0);
         Node* right = node->GetChild(1);
-        delete node;
+        FreeNode(node);
         --size_;
         
         if (left)
@@ -470,4 +472,31 @@ private:
         if (right)
             EraseNodes(right);
     }
+    
+    /// Allocate a node
+    Node* AllocateNode()
+    {
+        if (!allocator_)
+            allocator_ = AllocatorInitialize(sizeof(Node));
+        Node* newNode = static_cast<Node*>(AllocatorGet(allocator_));
+        new(newNode) Node();
+        return newNode;
+    }
+    
+    /// Allocate a node with specified key
+    Node* AllocateNode(const T& key)
+    {
+        if (!allocator_)
+            allocator_ = AllocatorInitialize(sizeof(Node));
+        Node* newNode = static_cast<Node*>(AllocatorGet(allocator_));
+        new(newNode) Node(key);
+        return newNode;
+    }
+    
+    /// Free a node
+    void FreeNode(Node* node)
+    {
+        (node)->~Node();
+        AllocatorFree(node);
+    }
 };

+ 1 - 3
Engine/Container/Vector.h

@@ -344,9 +344,7 @@ public:
     unsigned Capacity() const { return capacity_; }
     /// Return whether vector is empty
     bool Empty() const { return size_ == 0; }
-    
-    /// Minimum dynamic allocation size
-    static const unsigned MIN_CAPACITY = 1;
+
     
 private:
     /// Return the buffer with right type