310 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // vim: set sts=8 ts=2 sw=2 tw=99 et:
 | |
| //
 | |
| // Copyright (C) 2013, David Anderson and AlliedModders LLC
 | |
| // 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 AlliedModders LLC 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 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 _include_amtl_linkedlist_h_
 | |
| #define _include_amtl_linkedlist_h_
 | |
| 
 | |
| #include <new>
 | |
| #include <stdlib.h>
 | |
| #include <am-allocator-policies.h>
 | |
| #include <am-utility.h>
 | |
| #include <am-moveable.h>
 | |
| 
 | |
| namespace ke {
 | |
| 
 | |
| // LinkedList, analagous to std::list or SourceHook::List. Since it performs a
 | |
| // malloc() and free() on every contained node, it should be avoided unless
 | |
| // absolutely necessary, or for when allocation performance is not a factor. It
 | |
| // is provided here to safely port old AlliedModders code to AMTL.
 | |
| //
 | |
| // In order to use a circular chain, LinkedList's allocation size includes
 | |
| // exactly one T. If T is very large, LinkedList should be allocated on the
 | |
| // heap, to avoid using the stack.
 | |
| template <class T, class AllocPolicy = SystemAllocatorPolicy>
 | |
| class LinkedList : public AllocPolicy
 | |
| {
 | |
|  public:
 | |
|   friend class iterator;
 | |
| 
 | |
|   class Node
 | |
|   {
 | |
|    public:
 | |
|     Node(const T &o)
 | |
|      : obj(o)
 | |
|     {
 | |
|     }
 | |
|     Node(Moveable<T> o)
 | |
|      : obj(o)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     T obj;
 | |
|     Node *next;
 | |
|     Node *prev;
 | |
|   };
 | |
| 
 | |
| public:
 | |
|   LinkedList(AllocPolicy = AllocPolicy())
 | |
|    : length_(0)
 | |
|   {
 | |
|     head()->prev = head();
 | |
|     head()->next = head();
 | |
|   }
 | |
|   ~LinkedList() {
 | |
|     clear();
 | |
|   }
 | |
| 
 | |
|   bool append(const T &obj) {
 | |
|     return insertBefore(end(), obj) != end();
 | |
|   }
 | |
|   bool append(Moveable<T> obj) {
 | |
|     return insertBefore(end(), obj) != end();
 | |
|   }
 | |
| 
 | |
|   bool prepend(const T &obj) {
 | |
|     return insertBefore(begin(), obj) != begin();
 | |
|   }
 | |
|   bool prepend(Moveable<T> obj) {
 | |
|     return insertBefore(begin(), obj) != begin();
 | |
|   }
 | |
| 
 | |
|   size_t length() const {
 | |
|     return length_;
 | |
|   }
 | |
| 
 | |
|   void clear() {
 | |
|     Node *node = head()->next;
 | |
|     Node *temp;
 | |
|     head()->next = head();
 | |
|     head()->prev = head();
 | |
| 
 | |
|     // Iterate through the nodes until we find the sentinel again.
 | |
|     while (node != head()) {
 | |
|       temp = node->next;
 | |
|       freeNode(node);
 | |
|       node = temp;
 | |
|     }
 | |
|     length_ = 0;
 | |
|   }
 | |
| 
 | |
|   bool empty() const {
 | |
|     return (length_ == 0);
 | |
|   }
 | |
| 
 | |
|   T &front() {
 | |
|     assert(!empty());
 | |
|     return head()->next->obj;
 | |
|   }
 | |
|   T &back() {
 | |
|     assert(!empty());
 | |
|     return head()->prev->obj;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const Node *head() const {
 | |
|     return sentinel_.address();
 | |
|   }
 | |
|   Node *head() {
 | |
|     return sentinel_.address();
 | |
|   }
 | |
| 
 | |
|   Node *allocNode(const T &obj) {
 | |
|     Node *node = (Node *)this->malloc(sizeof(Node));
 | |
|     if (!node)
 | |
|       return NULL;
 | |
|     new (node) Node(obj);
 | |
|     return node;
 | |
|   }
 | |
|   Node *allocNode(Moveable<T> obj) {
 | |
|     Node *node = (Node *)this->malloc(sizeof(Node));
 | |
|     if (!node)
 | |
|       return NULL;
 | |
|     new (node) Node(obj);
 | |
|     return node;
 | |
|   }
 | |
| 
 | |
|   void freeNode(Node *node) {
 | |
|     node->obj.~T();
 | |
|     this->free(node);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   StorageBuffer<Node> sentinel_;
 | |
|   size_t length_;
 | |
| 
 | |
|  public:
 | |
|   class iterator
 | |
|   {
 | |
|     friend class LinkedList;
 | |
| 
 | |
|    public:
 | |
|     iterator()
 | |
|      : this_(NULL)
 | |
|     {
 | |
|     }
 | |
|     iterator(const LinkedList &src)
 | |
|      : this_(src.head())
 | |
|     {
 | |
|     }
 | |
|     iterator(Node *n)
 | |
|      : this_(n)
 | |
|     {
 | |
|     }
 | |
|     iterator(const iterator &where)
 | |
|      : this_(where.this_)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     iterator &operator --() {
 | |
|       if (this_)
 | |
|         this_ = this_->prev;
 | |
|       return *this;
 | |
|     }
 | |
|     iterator operator --(int) {
 | |
|       iterator old(*this);
 | |
|       if (this_)
 | |
|         this_ = this_->prev;
 | |
|       return old;
 | |
|     }  
 | |
|     iterator &operator ++() {
 | |
|       if (this_)
 | |
|         this_ = this_->next;
 | |
|       return *this;
 | |
|     }
 | |
|     iterator operator ++(int) {
 | |
|       iterator old(*this);
 | |
|       if (this_)
 | |
|         this_ = this_->next;
 | |
|       return old;
 | |
|     }
 | |
|     
 | |
|     const T &operator * () const {
 | |
|       return this_->obj;
 | |
|     }
 | |
|     T &operator * () {
 | |
|       return this_->obj;
 | |
|     }
 | |
|     T *operator ->() {
 | |
|       return &this_->obj;
 | |
|     }
 | |
|     const T *operator ->() const {
 | |
|       return &(this_->obj);
 | |
|     }
 | |
|     
 | |
|     bool operator !=(const iterator &where) const {
 | |
|       return (this_ != where.this_);
 | |
|     }
 | |
|     bool operator ==(const iterator &where) const {
 | |
|       return (this_ == where.this_);
 | |
|     }
 | |
| 
 | |
|     operator bool() {
 | |
|       return !!this_;
 | |
|     }
 | |
| 
 | |
|    private:
 | |
|     Node *this_;
 | |
|   };
 | |
| 
 | |
|  private:
 | |
|   // Insert obj right before where.
 | |
|   iterator insert(iterator where, Node *node) {
 | |
|     if (!node)
 | |
|       return where;
 | |
| 
 | |
|     Node *pWhereNode = where.this_;
 | |
|     
 | |
|     pWhereNode->prev->next = node;
 | |
|     node->prev = pWhereNode->prev;
 | |
|     pWhereNode->prev = node;
 | |
|     node->next = pWhereNode;
 | |
| 
 | |
|     length_++;
 | |
|     return iterator(node);
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   iterator begin() {
 | |
|     return iterator(head()->next);
 | |
|   }
 | |
|   iterator end() {
 | |
|     return iterator(head());
 | |
|   }
 | |
|   iterator erase(iterator where) {
 | |
|     Node *pNode = where.this_;
 | |
|     iterator iter(where);
 | |
|     iter++;
 | |
| 
 | |
|     pNode->prev->next = pNode->next;
 | |
|     pNode->next->prev = pNode->prev;
 | |
| 
 | |
|     freeNode(pNode);
 | |
|     length_--;
 | |
| 
 | |
|     return iter;
 | |
|   }
 | |
|   iterator insertBefore(iterator where, const T &obj) {
 | |
|     return insert(where, allocNode(obj));
 | |
|   }
 | |
|   iterator insertBefore(iterator where, Moveable<T> obj) {
 | |
|     return insert(where, allocNode(obj));
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   // Removes one instance of |obj| from the list, if found.
 | |
|   void remove(const T &obj) {
 | |
|     for (iterator b = begin(); b != end(); b++) {
 | |
|       if (*b == obj) {
 | |
|         erase(b);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   template <typename U>
 | |
|   iterator find(const U &equ) {
 | |
|     for (iterator iter = begin(); iter != end(); iter++) {
 | |
|       if (*iter == equ)
 | |
|         return iter;
 | |
|     }
 | |
|     return end();
 | |
|   }
 | |
| 
 | |
| 
 | |
|  private:
 | |
|   // These are disallowed because they basically violate the failure handling
 | |
|   // model for AllocPolicies and are also likely to have abysmal performance.
 | |
|   LinkedList &operator =(const LinkedList<T> &other) KE_DELETE;
 | |
|   LinkedList(const LinkedList<T> &other) KE_DELETE;
 | |
| };
 | |
| 
 | |
| } // namespace ke
 | |
| 
 | |
| #endif //_INCLUDE_CSDM_LIST_H
 |