Files
libsst/Lib/Include/ZSTL/ZList.hpp
2026-04-03 00:22:39 -05:00

1555 lines
40 KiB
C++

/*
ZList.hpp
Author: James Russell <jcrussell@762studios.com>
Created: 9/12/2011
Purpose:
Templated doubly-linked list implementation.
A primary difference between ZList and ZArray is that ZList (rather, the default allocators for
ZList) make an absolute guarantee about scoped allocation / deallocation of objects. This
means that a ZList using any of the default allocators is guaranteed to construct and destruct
list nodes as needed, and this means element constructors and destructors will be called as
elements are allocated and deallocated.
This guarantee goes out the window when a custom allocator is used, and it becomes up to the
implementor to meet or disregard this guarantee as desired. The list will, however, always
call out to the allocator whenever a node is needed, guaranteeing that the allocator is still
called as elements enter and leave scope.
Note:
A visualizer for Visual Studio 2008 for this container is available in vs_visualizers.txt.
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#pragma once
#ifndef _ZLIST_HPP
#define _ZLIST_HPP
#include <ZSTL/ZSTLCommon.hpp>
#include <ZSTL/ZSTLInvalidPos.hpp>
//Forward Declaration of ZList
template <typename T, typename A>
class ZList;
//Forward Declaration of ZListIterator
template <typename T>
class ZListIterator;
/*
Forward Declaration of ZList Method Implementation Structures
Existence of these structures allows for template specialization of
individual methods of the ZList class. In order to specialize
a single method of ZList, simply specialize the corresponding
method implementation structure.
*/
//Forward Declaration of ZList<T, A>::At struct
template <typename T, typename A>
struct ZList_AtImpl {
inline static ZListIterator<T> Call(const ZList<T, A>& _self, size_t _index);
};
//Forward Declaration of ZList<T, A>::Back struct
template <typename T, typename A>
struct ZList_BackImpl {
inline static T& Call(const ZList<T, A>& _self);
};
//Forward Declaration of ZList<T, A>::Clear struct
template <typename T, typename A>
struct ZList_ClearImpl {
inline static void Call(ZList<T, A>& _self, ZListIterator<T>& _itr);
};
//Forward Declaration of ZList<T, A>::Copy struct
template <typename T, typename A>
struct ZList_CopyImpl {
template <typename B> inline static void Call(ZList<T, A>& _self, const ZList<T, B>& _other);
};
//Forward Declaration of ZList<T, A>::Empty struct
template <typename T, typename A>
struct ZList_EmptyImpl {
inline static bool Call(const ZList<T, A>& _self);
};
//Forward Declaration of ZList<T, A>::Equals struct
template <typename T, typename A>
struct ZList_EqualsImpl {
template <typename B> inline static bool Call(const ZList<T, A>& _self, const ZList<T, B>& _other);
};
//Forward Declaration of ZList<T, A>::Erase struct
template <typename T, typename A>
struct ZList_EraseImpl {
inline static T Call(ZList<T, A>& _self, ZListIterator<T>& _itr);
inline static void Call(ZList<T, A>& _self, ZListIterator<T>& _start, const ZListIterator<T>& _end);
};
//Forward Declaration of ZList<T, A>::Find struct
template <typename T, typename A>
struct ZList_FindImpl {
inline static ZListIterator<T> Call(const ZList<T, A>& _self, const T& _elem);
};
//Forward Declaration of ZList<T, A>::Front struct
template <typename T, typename A>
struct ZList_FrontImpl {
inline static T& Call(const ZList<T, A>& _self);
};
//Forward Declaration of ZList<T, A>::Insert struct
template <typename T, typename A>
struct ZList_InsertImpl {
inline static void Call(ZList<T, A>& _self, const ZListIterator<T>& _itr, const T& _value);
inline static void Call(ZList<T, A>& _self, const ZListIterator<T>& _itr, const ZListIterator<T>& _start, const ZListIterator<T>& _end);
};
//Forward Declaration of ZList<T, A>::PopBack struct
template <typename T, typename A>
struct ZList_PopBackImpl {
inline static T Call(ZList<T, A>& _self);
};
//Forward Declaration of ZList<T, A>::PopFront struct
template <typename T, typename A>
struct ZList_PopFrontImpl {
inline static T Call(ZList<T, A>& _self);
};
//Forward Declaration of ZList<T, A>::PushBack struct
template <typename T, typename A>
struct ZList_PushBackImpl {
inline static void Call(ZList<T, A>& _self, const T& _value);
};
//Forward Declaration of ZList<T, A>::PushFront struct
template <typename T, typename A>
struct ZList_PushFrontImpl {
inline static void Call(ZList<T, A>& _self, const T& _value);
};
//Forward Declaration of ZList<T, A>::Size struct
template <typename T, typename A>
struct ZList_SizeImpl {
inline static size_t Call(const ZList<T, A>& _self);
};
//Forward Declaration of ZList<T, A>::Splice struct
template <typename T, typename A>
struct ZList_SpliceImpl {
inline static void Call(ZList<T, A>& _self, const ZListIterator<T>& _itr, ZList<T, A>& _other, const ZListIterator<T>& _start, const ZListIterator<T>& _end);
};
//Forward Declaration of ZList<T, A>::Swap struct
template <typename T, typename A>
struct ZList_SwapImpl {
inline static void Call(ZList<T, A>& _self, ZList<T, A>& _other);
};
//Forward Declaration of ZList<T, A>::SwapNodes struct
template <typename T, typename A>
struct ZList_SwapNodesImpl {
inline static void Call(ZList<T, A>& _self, const ZListIterator<T>& _i, const ZListIterator<T>& _j);
};
//////////////////////
/* ZList Allocators */
//////////////////////
/*
Allocator for ZList. Handles allocation of nodes of the templated type.
The template parameter T is the type contained in the list this allocator is for.
*/
template <typename T>
class ZListAllocator
{
public:
/*
public ZListAllocator<T>::Allocate
Allocator function which allocates a ZListNode<T>.
@return - an allocated ZListNode<T>
*/
ZListNode<T>* Allocate()
{
return ZSTL_NEW(ZListNode<T>);
}
/*
public ZListAllocator<T>::Deallocate
Deallocation function which deallocates a previously allocated ZListNode<T>.
@param _node - node to deallocate
*/
void Deallocate(ZListNode<T>* _node)
{
ZSTL_DEL(_node);
}
};
/*
Alternate allocator for ZList Handles allocation of nodes of the templated type,
and uses local storage to avoid allocations whenever possible.
Note: This allocator is NOT suitable for a list that is later spliced using
ZList::Splice or has nodes swapped using ZList::Swap or ZList::SwapNodes.
It will cause errors when local nodes are deallocated by the other list's
allocator.
The template parameter T is the type contained in the list this allocator is for.
*/
template <typename T, size_t N>
class ZListPooledAllocator
{
private:
//Local storage space for nodes
char Local[N * sizeof(ZListNode<T>)];
//Pointer storage for nodes (if available)
ZListNode<T>* Nodes[N];
//Our current node index
size_t NodeIndex;
public:
/*
Default constructor, which initializes local storage.
*/
ZListPooledAllocator()
: NodeIndex(0)
{
uintptr_t ptr = (uintptr_t)&Local[0];
for (size_t i = 0; i < N; i++)
{
Nodes[i] = (ZListNode<T>*)ptr;
ptr += sizeof(ZListNode<T>);
}
}
/*
public ZListPooledAllocator<T>::Allocate
Allocator function which allocates a ZListNode<T>.
@return - an allocated ZListNode<T>
*/
ZListNode<T>* Allocate()
{
if (NodeIndex < N)
{
ZListNode<T>* node = Nodes[NodeIndex];
Nodes[NodeIndex++] = NULL;
return new (node) ZListNode<T>();
}
return ZSTL_NEW(ZListNode<T>);
}
/*
public ZListPooledAllocator<T>::Deallocate
Deallocation function which deallocates a previously allocated ZListNode<T>.
@param _node - node to deallocate
*/
void Deallocate(ZListNode<T>* _node)
{
const uintptr_t nodePtr = (uintptr_t)_node;
const uintptr_t poolLower = (uintptr_t)&Local[0];
const uintptr_t poolUpper = poolLower + (N-1)*sizeof(ZListNode<T>);
if (nodePtr >= poolLower && nodePtr <= poolUpper)
{
ZSTL_ASSERT((nodePtr - poolLower) % sizeof(ZListNode<T>) == 0, "Node is not aligned to this list, check for address corruption");
Nodes[--NodeIndex] = _node;
_node->~ZListNode<T>();
}
else
{
ZSTL_DEL(_node);
}
}
};
////////////////////
/* ZList Iterator */
////////////////////
/*
Iterator class for ZList.
The template parameter T is the type contained in the list this iterator is for.
*/
template <typename T>
class ZListIterator
{
public:
/*
Default Constructor.
*/
ZListIterator()
: Node(NULL), EndNode(NULL) { }
/*
Useful constructor.
@param _node - the node we are to begin iterating at
*/
ZListIterator(ZListNode<T>* _node, ZListNode<T>* _endNode)
: Node(_node), EndNode(_endNode) { }
/*
Copy Constructor.
@param _other - the other iterator
*/
ZListIterator(const ZListIterator& _other)
: Node(_other.Node), EndNode(_other.EndNode) { }
/*
public ZListIterator<T, A>::CheckNodeCurrent
Node check function that does not allow the current node to
point to NULL or to 'End'.
@return (void)
@assert - if the node is NULL
*/
void CheckNodeCurrent() const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(Node != NULL && Node != EndNode, "ZList Iterator Invalid!");
#endif
}
/*
public ZListIterator<T, A>::CheckNodeNext
Node check function that does not allow Node to be 'End'
@return (void)
*/
void CheckNodeNext() const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(Node != NULL && Node != EndNode, "ZList Iterator Next Invalid!");
#endif
}
/*
public ZListIterator<T, A>::CheckNodePrevious
Node check function that does not allow Node to be 'Begin'
@return (void)
*/
void CheckNodePrevious() const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(Node != NULL && Node->Previous != EndNode, "ZList Iterator Previous Invalid!");
#endif
}
/*
public ZListIterator<T, A>::GetNode
Gets the node that this iterator is currently pointed at.
@return (ZListNode<T>*) - the node we are currently pointed at
*/
ZListNode<T>* GetNode() const
{ return Node; }
/*
public ZListIterator<T, A>::SetNode
Sets the current node for this iterator. Useful for when the
underlying list changes state.
@param _node - the node to point this iterator at
@return (void)
*/
void SetNode(ZListNode<T>* _node)
{ Node = _node; }
/*
public ZListIterator<T>::Get
Gets the element this iterator points to.
@return (T&) - the element pointed to
*/
T& Get() const
{ return *(*this); }
/*
public ZListIterator<T>::HasCurrent
Determines if this iterator currently points to a valid element.
@return (bool) - true if element at current position
*/
bool HasCurrent() const
{ return Node != NULL && Node != EndNode; }
/*
public ZListIterator<T>::HasNext
Determines if this iterator has a valid element after the current element.
@return (bool) - true if element after current, false otherwise
*/
bool HasNext() const
{ return Node != NULL && Node != EndNode && Node->Next != EndNode; }
/*
public ZListIterator<T>::HasPrev
Determines if this iterator has a valid element before the current element.
@return (bool) - true if element before current, false otherwise
*/
bool HasPrev() const
{ return Node != NULL && Node->Previous != EndNode; }
/*
public ZListIterator<T>::Next
Advances this iterator to the next element.
@return (void)
@assert - if this would advance past end
*/
void Next()
{ ++(*this); }
/*
public ZListIterator<T>::Prev
Returns this iterator to the previous element.
@return (void)
@assert - if this would advance past begin
*/
void Prev()
{ --(*this); }
//Operator overrides
ZListIterator& operator ++() { CheckNodeNext(); Node = Node->Next; return *this; }
ZListIterator operator ++ (int) { ZListNode<T> *node = Node; CheckNodeNext(); Node = Node->Next; return ZListIterator(node, EndNode); }
ZListIterator operator + (int _value) { ZListIterator itr(*this); for (int i = 0; i < _value; i++) ++itr; return itr; }
ZListIterator& operator += (int _value) { for (int i = 0; i < _value; i++) ++(*this); return *this; }
ZListIterator& operator -- () { CheckNodePrevious(); Node = Node->Previous; return *this; }
ZListIterator operator -- (int) { ZListNode<T> *node = Node; CheckNodePrevious(); Node = Node->Previous; return ZListIterator(node, EndNode); }
ZListIterator operator - (int _value) { ZListIterator itr(*this); for (int i = _value; i > 0; i--) --itr; return itr; }
ZListIterator& operator -=(int _value) { for (int i = 0; i < _value; i--) --(*this); return *this; }
ZListIterator& operator = (const ZListIterator &_other) { Node = _other.Node; EndNode = _other.EndNode; return *this; }
bool operator == (const ZListIterator& _other) const { return (Node == _other.Node) && (EndNode == _other.EndNode); }
bool operator !=(const ZListIterator& _other) const { return !( (*this)==_other ); }
T& operator *() const { CheckNodeCurrent(); return Node->Element; }
private:
//The current node we are pointing to
ZListNode<T> *Node;
//The end node of the list
ZListNode<T> *EndNode;
};
//////////////////////////
/* ZList Implementation */
//////////////////////////
/*
List implementation for ZEngine.
ZList takes a single template parameter, which is the contained type. The allocator is
passed in at construction, which allows the allocator to be decoupled from the list container.
The template parameter T is the type to be contained in the list.
*/
template <typename T, typename A = ZListAllocator<T> >
class ZList
{
friend struct ZList_AtImpl<T, A>;
friend struct ZList_BackImpl<T, A>;
friend struct ZList_ClearImpl<T, A>;
friend struct ZList_CopyImpl<T, A>;
friend struct ZList_EmptyImpl<T, A>;
friend struct ZList_EqualsImpl<T, A>;
friend struct ZList_EraseImpl<T, A>;
friend struct ZList_FindImpl<T, A>;
friend struct ZList_FrontImpl<T, A>;
friend struct ZList_InsertImpl<T, A>;
friend struct ZList_PopBackImpl<T, A>;
friend struct ZList_PopFrontImpl<T, A>;
friend struct ZList_PushBackImpl<T, A>;
friend struct ZList_PushFrontImpl<T, A>;
friend struct ZList_SizeImpl<T, A>;
friend struct ZList_SpliceImpl<T, A>;
friend struct ZList_SwapImpl<T, A>;
friend struct ZList_SwapNodesImpl<T, A>;
protected:
/*
Our empty list node.
EmptyNode.Next is always the first node in the list (itself if empty).
EmptyNode.Previous is always the last node in the list (itself if empty).
*/
ZListNode<T> EmptyNode;
//Allocator for the list
A ListAllocator;
//Allocate function
inline ZListNode<T>* AllocateNode()
{
ZListNode<T>* node = ListAllocator.Allocate();
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(node != NULL, "ZList allocator failed to allocate node!");
#endif
return node;
}
//Deallocate function
inline void DeallocateNode(ZListNode<T> *_node)
{
ListAllocator.Deallocate(_node);
}
//Integrity Check (used for internal debugging)
inline void CheckIntegrity() const
{
#if ZSTL_CHECK_INTEGRITY
ZListNode<T>* current;
ZListNode<T>* previous;
current = EmptyNode.Next;
previous = current->Previous;
ZSTL_ASSERT(previous == &EmptyNode, "First linkage invalid!");
for(;;) //Stupid MSVC warning
{
ZSTL_ASSERT(current != NULL, "ZList Error: Contains invalid linkage pointers!");
previous = current;
current = current->Next;
if (current == &EmptyNode)
break;
}
ZSTL_ASSERT(previous == EmptyNode.Previous, "ZList Error: Last linkage invalid!");
#endif //ZSTL_CHECK_INTEGRITY
}
public:
/*
Typedef for ZListIterator (allows for ZList<T, A>::Iterator notation).
*/
typedef ZListIterator<T> Iterator;
/*
Default Constructor.
*/
ZList()
{
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
CheckIntegrity();
}
/*
Sub-List Constructor. Constructs a list containing the elements between two given iterators.
@param _begin - the iterator at which to start the list
@param _end - the iterator at which to end the list (exclusive)
*/
ZList(const ZListIterator<T>& _begin, const ZListIterator<T>& _end)
{
typename ZList<T, A>::Iterator itr;
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
for (itr = _begin; itr != _end; ++itr)
PushBack((*itr));
CheckIntegrity();
}
/*
Copy Constructor. Makes a copy of the other list. Needed to prevent
the default copy constructor from being generated.
@param _other - the other list to copy.
*/
ZList(const ZList<T, A>& _other)
{
typename ZList<T, A>::Iterator itr;
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
_other.CheckIntegrity();
for (itr = _other.Begin(); itr != _other.End(); ++itr)
PushBack((*itr));
CheckIntegrity();
}
/*
Copy constructor. Makes a copy of the other list.
@param B - the allocator type of the other list
@param _other - the other list to copy
*/
template <typename B>
ZList(const ZList<T, B>& _other)
{
typename ZList<T, A>::Iterator itr;
EmptyNode.Next = &EmptyNode;
EmptyNode.Previous = &EmptyNode;
for (itr = _other.Begin(); itr != _other.End(); ++itr)
PushBack((*itr));
CheckIntegrity();
}
/*
Destructor.
*/
~ZList()
{
Clear();
}
/*
[] Operator overload. Equivalent to a call to At.
@param _index - index into the list
@return (ZList<T, A>::Iterator) - iterator to the given index
*/
Iterator operator [] (size_t _index) const
{ return At(_index); }
/*
= Operator overload. Equivalent to a call to Copy. Needed to prevent
a default assignment operator from being created.
@param _other - the other list to copy.
@return (void)
*/
ZList<T, A>& operator = (const ZList<T, A>& _other)
{ Copy(_other); return *this; }
/*
= Operator overload. Equivalent to a call to Copy.
@param _other - the other list to copy.
@return (void)
*/
template <typename B>
ZList<T, A>& operator = (const ZList<T, B>& _other)
{ Copy(_other); return *this; }
/*
== Operator overload. Equivalent to a call to Equals.
@param B - the allocator type of the other list
@param _other - the list to compare against
@return (bool) - true if they are equal, false otherwise
*/
template <typename B>
bool operator == (const ZList<T, B>& _other) const
{ return Equals(_other); }
/*
!= Operator overload. Equivalent to !Equals.
@param B - the allocator type of the other list
@param _other - the list to compare against
@return (bool) - true if they are not equal, false if they are
*/
template <typename B>
bool operator != (const ZList<T, B>& _other) const
{ return !Equals(_other); }
/*
public ZList<T, N>::Allocator
Returns a reference to the current allocator.
@return (A&) - reference to the held allocator
*/
A& Allocator()
{ return ListAllocator; }
/*
public ZList<T, A>::At
Gets an iterator to a specific index in the list.
@param _index - the index
@return (Iterator&) - iterator to the indexed element
@assert - if the index is past the end of the list
*/
Iterator At(size_t _index) const
{ return ZList_AtImpl<T, A>::Call(*this, _index); }
/*
public ZList<T, A>::Back
Gets a reference to the value at the back of the list.
@return (T&) - last element in the list
@assert - if the list is empty
*/
T& Back() const
{ return ZList_BackImpl<T, A>::Call(*this); }
/*
public ZList<T, A>::Begin
Gets an iterator to the beginning of the list.
@return (ZList<T, A>::Iterator) - iterator pointing to the first list node
*/
Iterator Begin() const
{ return ZListIterator<T>(const_cast<ZListNode<T>*>(EmptyNode.Next), const_cast<ZListNode<T>*>(&EmptyNode)); }
/*
public ZList<T, A>::Clear
Clears the list.
@return (void)
*/
void Clear()
{ typename ZList<T, A>::Iterator temp = Begin(); return Clear(temp); }
/*
public ZList<T, A>::Clear
Clears the list after a specific iterator location. The given iterator is set
to the end of the list after this call.
@param - _itr - the iterator
@return (void)
*/
void Clear(Iterator& _itr)
{ return ZList_ClearImpl<T, A>::Call(*this, _itr); }
/*
public ZList<T, A>::Copy<B>
Copies the contents of another list into this list.
@param _other - the list to copy from
@return (void)
*/
template <typename B>
void Copy(const ZList<T, B>& _other)
{ return ZList_CopyImpl<T, A>::template Call<B>(*this, _other); }
/*
public ZList<T, A>::Empty
O(1) operation that determines if the list is empty.
@return (bool) - true if the list has no elements, false otherwise
*/
bool Empty() const
{ return ZList_EmptyImpl<T, A>::Call(*this); }
/*
public ZList<T, A>::End
Gets an iterator to the 'end' node, which is one element past
the last element in the list.
@return (ZList<T, A>::Iterator) - iterator pointing to the 'end' node
*/
Iterator End() const
{ return ZListIterator<T>(const_cast<ZListNode<T>*>(&EmptyNode), const_cast<ZListNode<T>*>(&EmptyNode)); }
/*
public ZList<T, A>::Equals
Does an element-by-element comparison to determine equivalence.
@param B - the type of the allocator used by the other list
@param _other - the list to compare against
@return (bool) - true if the lists are equivalent, false otherwise
*/
template <typename B>
bool Equals(const ZList<T, B>& _other) const
{ return ZList_EqualsImpl<T, A>::Call(*this, _other); }
/*
public ZList<T, A>::Erase
Removes a value from the list at the specified location.
@param _itr - the iterator location to remove the value from (incremented after this call)
@return (T) - the removed value
@assert - if the iterator is invalid
*/
T Erase(Iterator& _itr)
{ return ZList_EraseImpl<T, A>::Call(*this, _itr); }
/*
public ZList<T, A>::Erase
Removes a range of values from the list.
@param _from - the iterator location to start at (set to _to after this call)
@param _to - the iterator location to end at (exclusive)
@return (void)
@assert - if the iterator is invalid
*/
void Erase(Iterator& _from, const Iterator& _to)
{ return ZList_EraseImpl<T, A>::Call(*this, _from, _to); }
/*
public ZList<T, A>::Find
Finds the first occurrence of the element in the list, as determined by
the == operator.
@param _elem - the element to find
@return (Iterator) - iterator to first occurrence of the element, or ZSTL::invalidPos if not found
*/
Iterator Find(const T& _elem) const
{ return ZList_FindImpl<T, A>::Call(*this, _elem); }
/*
public ZList<T, A>::Front
Gets a reference to the value at the front of the list.
@return (T&) - first value in the list
@assert - if the list is empty
*/
T& Front() const
{ return ZList_FrontImpl<T, A>::Call(*this); }
/*
public ZList<T, A>::Insert
Inserts a value at the specified location in the list. As _itr is
not modified, it will point to the first node after the inserted value.
@param _itr - iterator to the location to insert the value
@param _value - the value to add to the beginning of the list
@return (void)
@assert - if the iterator is invalid
*/
void Insert(const ZListIterator<T>& _itr, const T& _value)
{ return ZList_InsertImpl<T, A>::Call(*this, _itr, _value); }
/*
public ZList<T, A>::Insert
Inserts the values between the provided iterators at the specified
location. As _itr is not modified, it will point to the first node
after the inserted values.
@param _itr - iterator to the location to insert the values
@param _start - the starting iterator to the values to insert
@param _end - the ending iterator (exclusive)
@return (void)
*/
void Insert(const ZListIterator<T>& _itr, const ZListIterator<T>& _start, const ZListIterator<T>& _end)
{ return ZList_InsertImpl<T, A>::Call(*this, _itr, _start, _end); }
/*
public ZList<T, A>::PopBack
Pops a value from the end of the list.
@return (T) - the value at the back of the list
@assert - if the list is empty
*/
T PopBack()
{ return ZList_PopBackImpl<T, A>::Call(*this); }
/*
public ZList<T, A>::PopFront
Removes and returns the value from the beginning of the list.
@return (T) - the value at the front of the list
@assert - if the list is empty
*/
T PopFront()
{ return ZList_PopFrontImpl<T, A>::Call(*this); }
/*
public ZList<T, A>::PushBack
Pushes a value onto the back of the list. Calls out to the allocator
to allocate new nodes.
@param _value - the value to place in the list
@return (void)
*/
void PushBack(const T& _value)
{ return ZList_PushBackImpl<T, A>::Call(*this, _value); }
/*
public ZList<T, A>::PushFront
Pushes a value onto the front of the list. Calls out to the allocator
to allocate new nodes.
@param _value - the value to place in the list
@return (void)
*/
void PushFront(const T& _value)
{ return ZList_PushFrontImpl<T, A>::Call(*this, _value); }
/*
public ZList<T, A>::Size
O(n) operation that gives the size of the list.
@return (size_t) - list length (number of contained elements)
*/
size_t Size() const
{ return ZList_SizeImpl<T, A>::Call(*this); }
/*
public ZList<T, A>::Splice
Splices the contents of another list between the given iterators
into this list. This avoids assignment operator calls on the contained
elements, but does modify the list pointed to by the given iterators
(removes the nodes) and requires that an allocator of type A be able
to deallocate nodes made by other allocators of type A.
The passed in iterators are not modified, meaning that _start will point
to the first inserted node in this list, the _end iterator will point
to the same element in the node provider list, and _itr will point to the
first element after the inserted nodes.
@param _itr - the position to insert the values at
@param _other - the list the nodes are removed from (must be passed to ensure allocator equivalence)
@param _start - the starting iterator
@param _end - the ending iterator (exclusive)
@return (void)
@assert - if allocator type A cannot deallocate nodes made by another instance of A (determined by Allocator ==)
*/
void Splice(const ZListIterator<T>& _itr, ZList<T, A>& _other, const ZListIterator<T>& _start, const ZListIterator<T>& _end)
{ return ZList_SpliceImpl<T, A>::Call(*this, _itr, _other, _start, _end); }
/*
public ZList<T, A>::Swap
Swaps the contents of this list with the contents of another.
@param _other - the list to swap contents with
@return (void)
*/
void Swap(ZList<T, A>& _other)
{ return ZList_SwapImpl<T, A>::Call(*this, _other); }
/*
public ZList<B>::SwapNodes
Swaps the actual nodes at the given positions. Avoids an assignment operator call
on the elements contained. The iterators are not modified, meaning they now point
to each other's locations after the call.
@param _i - iterator to the first element
@param _j - iterator to the second element
@return (void)
@assert - if the iterators are invalid
*/
void SwapNodes(const ZListIterator<T>& _i, const ZListIterator<T>& _j)
{ return ZList_SwapNodesImpl<T, A>::Call(*this, _i, _j); }
};
////////////////////////////////////////////
/* Non-Specialized Method Implementations */
////////////////////////////////////////////
template <typename T, typename A>
ZListIterator<T> ZList_AtImpl<T, A>::Call(const ZList<T, A>& _self, size_t _index)
{
size_t i;
ZListIterator<T> itr;
for (i = 0, itr = _self.Begin(); i < _index; i++)
++itr;
return itr;
}
/*************************************************************************/
template <typename T, typename A>
T& ZList_BackImpl<T, A>::Call(const ZList<T, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(!_self.Empty(), "ZList: No back element present!");
#endif
return _self.EmptyNode.Previous->Element;
}
/*************************************************************************/
template <typename T, typename A>
void ZList_ClearImpl<T, A>::Call(ZList<T, A>& _self, ZListIterator<T>& _itr)
{
ZListNode<T>* next;
ZListNode<T>* current = _itr.GetNode();
_self.CheckIntegrity();
//Early out
if (_self.Empty())
return;
//If we are the starting node, be sure to reset the EmptyNode.Next pointer
if (current == _self.EmptyNode.Next)
_self.EmptyNode.Next = &_self.EmptyNode;
//This is always true
_self.EmptyNode.Previous = current->Previous;
current->Previous->Next = &_self.EmptyNode;
//Iterate and deallocate the nodes
while (current != &_self.EmptyNode)
{
next = current->Next;
_self.DeallocateNode(current);
current = next;
}
//Set the node on the iterator equal to the end node
_itr.SetNode(&_self.EmptyNode);
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A> template <typename B>
void ZList_CopyImpl<T, A>::Call(ZList<T, A>& _self, const ZList<T, B>& _other)
{
typename ZList<T, A>::Iterator itr1;
typename ZList<T, B>::Iterator itr2;
if (&_self == &_other)
return;
//Assign as much as we can to avoid allocation calls with PushBack
for (itr1 = _self.Begin(), itr2 = _other.Begin(); itr2 != _other.End(); ++itr2)
{
if (itr1 != _self.End())
{
(*itr1++) = (*itr2);
}
else
{
_self.PushBack((*itr2));
}
}
_self.Clear(itr1);
}
/*************************************************************************/
template <typename T, typename A>
bool ZList_EmptyImpl<T, A>::Call(const ZList<T, A>& _self)
{
//Just need to check the EmptyNode.Next pointer
return (_self.EmptyNode.Next == &_self.EmptyNode);
}
/*************************************************************************/
template <typename T, typename A> template <typename B>
bool ZList_EqualsImpl<T, A>::Call(const ZList<T, A>& _self, const ZList<T, B>& _other)
{
typename ZList<T, A>::Iterator itr1;
typename ZList<T, B>::Iterator itr2;
if (&_self == &_other)
return true;
//Make sure we are element-wise equivalent up to the end of either list
for (itr1 = _self.Begin(), itr2 = _other.Begin(); itr1 != _self.End() && itr2 != _other.End(); ++itr1, ++itr2)
{
if ((*itr1) != (*itr2))
return false;
}
//Then make sure we have reached the end of both lists
if (itr1 == _self.End() && itr2 == _other.End())
return true;
return false;
}
/*************************************************************************/
template <typename T, typename A>
T ZList_EraseImpl<T, A>::Call(ZList<T, A>& _self, ZListIterator<T>& _itr)
{
T elem;
ZListNode<T> *node = _itr.GetNode();
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(node != NULL, "ZList: Iterator is invalid!");
ZSTL_ASSERT(node != &_self.EmptyNode, "ZList: Cannot erase end node!");
#endif
//Increment the iterator to the next list node to keep the iterator valid
++_itr;
//Rearrange the pointers
node->Previous->Next = node->Next;
node->Next->Previous = node->Previous;
elem = node->Element;
_self.DeallocateNode(node);
_self.CheckIntegrity();
return elem;
}
/*************************************************************************/
template <typename T, typename A>
void ZList_EraseImpl<T, A>::Call(ZList<T, A>& _self, ZListIterator<T>& _start, const ZListIterator<T>& _end)
{
ZListNode<T> *nodeStart = _start.GetNode();
ZListNode<T> *nodeEnd = _end.GetNode();
ZListNode<T> *curNode;
ZListNode<T> *prevNode;
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(nodeStart != NULL && nodeEnd != NULL, "ZList: Cannot erase with invalid iterator!");
ZSTL_ASSERT(nodeStart != &_self.EmptyNode, "ZList: Cannot erase end node!");
#endif
//Rearrange the pointers
nodeStart->Previous->Next = nodeEnd;
nodeEnd->Previous = nodeStart->Previous;
//Erase each element between from and to
curNode = nodeStart;
prevNode = NULL;
while (curNode != nodeEnd)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(curNode != &_self.EmptyNode, "ZList: Cannot erase end node!");
#endif
prevNode = curNode;
curNode = curNode->Next;
_self.DeallocateNode(prevNode);
}
_start = _end;
}
/*************************************************************************/
template <typename T, typename A>
ZListIterator<T> ZList_FindImpl<T, A>::Call(const ZList<T, A>& _self, const T& _elem)
{
ZListIterator<T> localItr = _self.Begin();
ZListIterator<T> end = _self.End();
//For each element, insert into the list at the given location
while (localItr != end)
{
if ((*localItr) == _elem) {
return localItr;
}
++localItr;
}
return ZSTL::InvalidPos;
}
/*************************************************************************/
template <typename T, typename A>
T& ZList_FrontImpl<T, A>::Call(const ZList<T, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(!_self.Empty(), "ZList: No front element in list!");
#endif
return _self.EmptyNode.Next->Element;
}
/*************************************************************************/
template <typename T, typename A>
void ZList_InsertImpl<T, A>::Call(ZList<T, A>& _self, const ZListIterator<T>& _itr, const T& _value)
{
ZListNode<T> *node = _itr.GetNode();
ZListNode<T> *newNode = _self.AllocateNode();
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(node != NULL, "ZList: Iterator is invalid!");
#endif
//Set our data
newNode->Element = _value;
//Swap the pointers
newNode->Next = node;
newNode->Previous = node->Previous;
node->Previous->Next = newNode;
node->Previous = newNode;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZList_InsertImpl<T, A>::Call(ZList<T, A>& _self, const ZListIterator<T>& _itr, const ZListIterator<T>& _start, const ZListIterator<T>& _end)
{
ZListIterator<T> localItr = _start;
//For each element, insert into the list at the given location
while (localItr != _end)
{
_self.Insert(_itr, *localItr);
++localItr;
}
}
/*************************************************************************/
template <typename T, typename A>
T ZList_PopBackImpl<T, A>::Call(ZList<T, A>& _self)
{
T elem;
ZListNode<T> *node;
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(!_self.Empty(), "ZList: Cannot pop from back of empty list!");
#endif
//Grab the element at the back of the list
node = _self.EmptyNode.Previous;
//Remove the node from the list
node->Previous->Next = &_self.EmptyNode;
_self.EmptyNode.Previous = node->Previous;
//Get the element value
elem = node->Element;
//Deallocate and then return
_self.DeallocateNode(node);
_self.CheckIntegrity();
return elem;
}
/*************************************************************************/
template <typename T, typename A>
T ZList_PopFrontImpl<T, A>::Call(ZList<T, A>& _self)
{
T elem;
ZListNode<T> *node;
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(!_self.Empty(), "ZList: Cannot pop from front of empty list!");
#endif
//Grab the element at the front of the list
node = _self.EmptyNode.Next;
//Remove the node from the list
node->Next->Previous = node->Previous;
node->Previous->Next = node->Next;
//Get the element value
elem = node->Element;
//Deallocate and then return
_self.DeallocateNode(node);
_self.CheckIntegrity();
return elem;
}
/*************************************************************************/
template <typename T, typename A>
void ZList_PushBackImpl<T, A>::Call(ZList<T, A>& _self, const T& _value)
{
//Get us a new node
ZListNode<T> *node = _self.AllocateNode();
//Set the value
node->Element = _value;
//Set some pointers
node->Next = &_self.EmptyNode;
node->Previous = _self.EmptyNode.Previous;
_self.EmptyNode.Previous->Next = node;
_self.EmptyNode.Previous = node;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZList_PushFrontImpl<T, A>::Call(ZList<T, A>& _self, const T& _value)
{
//Get us a new node
ZListNode<T> *node = _self.AllocateNode();
//Set the value
node->Element = _value;
//Set some pointers
node->Next = _self.EmptyNode.Next;
node->Previous = &_self.EmptyNode;
node->Next->Previous = node;
_self.EmptyNode.Next = node;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
size_t ZList_SizeImpl<T, A>::Call(const ZList<T, A>& _self)
{
size_t i;
ZListNode<T> *node;
//Iterate and count
i = 0;
for (node = _self.EmptyNode.Next; node != &_self.EmptyNode; node = node->Next)
i++;
return i;
}
/*************************************************************************/
template <typename T, typename A>
void ZList_SpliceImpl<T, A>::Call(ZList<T, A>& _self, const ZListIterator<T>& _itr, ZList<T, A>& _other, const ZListIterator<T>& _start, const ZListIterator<T>& _end)
{
ZListNode<T> *node = _itr.GetNode();
ZListNode<T> *startNode = _start.GetNode();
ZListNode<T> *endNode = _end.GetNode();
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(node != NULL, "ZList: Iterator is invalid!");
ZSTL_ASSERT(startNode != NULL, "ZList: Start Iterator is invalid!");
ZSTL_ASSERT(endNode != NULL, "ZList: End Iterator is invalid!");
#endif
//Look for easy ways out of this
if (startNode == endNode)
return;
if (_other.Empty()) //This has the convenient side effect of checking to ensure allocators are of the same type
return;
/*
Nope, gotta do this the hard way. So, we have the following.
This List:
? <-> A <-> B <-> ?
Other List:
? <-> C <-> D <-?-> E <-> F <-> ?
node is node B. startNode is node D. endNode is node F. It is entirely possible
that node E is also node D - but the algorithm should not care.
After Splice, we should have the following.
This List:
? <-> A <-> D <-?-> E <-> B <-> ?
Other List:
? <-> C <-> F <-> ?
*/
ZListNode<T> *nodeA, *nodeB, *nodeC, *nodeD, *nodeE, *nodeF;
nodeB = node;
nodeD = startNode;
nodeF = endNode;
nodeA = nodeB->Previous;
nodeE = nodeF->Previous;
nodeC = nodeD->Previous;
nodeA->Next = nodeD;
nodeD->Previous = nodeA;
nodeB->Previous = nodeE;
nodeE->Next = nodeB;
nodeF->Previous = nodeC;
nodeC->Next = nodeF;
_self.CheckIntegrity();
return;
}
/*************************************************************************/
template <typename T, typename A>
void ZList_SwapImpl<T, A>::Call(ZList<T, A>& _self, ZList<T, A>& _other)
{
ZListNode<T>* tempNode;
if (_self.Empty() && _other.Empty())
{
//Nothing to do here
}
else if (_self.Empty() && !_other.Empty())
{
//Simple swap
_self.EmptyNode.Next = _other.EmptyNode.Next;
_self.EmptyNode.Previous = _other.EmptyNode.Previous;
_self.EmptyNode.Next->Previous = &_self.EmptyNode;
_self.EmptyNode.Previous->Next = &_self.EmptyNode;
_other.EmptyNode.Next = &_other.EmptyNode;
_other.EmptyNode.Previous = &_other.EmptyNode;
}
else if (!_self.Empty() && _other.Empty())
{
//Again, simple swap
_other.EmptyNode.Next = _self.EmptyNode.Next;
_other.EmptyNode.Previous = _self.EmptyNode.Previous;
_other.EmptyNode.Next->Previous = &_other.EmptyNode;
_other.EmptyNode.Previous->Next = &_other.EmptyNode;
_self.EmptyNode.Next = &_self.EmptyNode;
_self.EmptyNode.Previous = &_self.EmptyNode;
}
else
{
//Swap the empty node pointers around
tempNode = _self.EmptyNode.Next;
_self.EmptyNode.Next = _other.EmptyNode.Next;
_other.EmptyNode.Next = tempNode;
tempNode = _self.EmptyNode.Previous;
_self.EmptyNode.Previous = _other.EmptyNode.Previous;
_other.EmptyNode.Previous = tempNode;
//Now correct the element pointers
_self.EmptyNode.Next->Previous = &_self.EmptyNode;
_self.EmptyNode.Previous->Next = &_self.EmptyNode;
_other.EmptyNode.Next->Previous = &_other.EmptyNode;
_other.EmptyNode.Previous->Next = &_other.EmptyNode;
}
_self.CheckIntegrity();
_other.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZList_SwapNodesImpl<T, A>::Call(ZList<T, A>& _self, const ZListIterator<T>& _i, const ZListIterator<T>& _j)
{
URFP(_self);
ZListNode<T> *tempNode;
ZListNode<T> *iNode = _i.GetNode();
ZListNode<T> *jNode = _j.GetNode();
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(iNode != NULL, "ZList: _i Iterator is invalid!");
ZSTL_ASSERT(jNode != NULL, "ZList: _j Iterator is invalid!");
#endif
//Early out
if (iNode == jNode)
return;
//Swap pointers around
tempNode = iNode->Next;
iNode->Next = jNode->Next;
jNode->Next = tempNode;
iNode->Next->Previous = iNode;
jNode->Next->Previous = jNode;
tempNode = iNode->Previous;
iNode->Previous = jNode->Previous;
jNode->Previous = tempNode;
iNode->Previous->Next = iNode;
jNode->Previous->Next = jNode;
}
#endif