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

1769 lines
47 KiB
C++

/*
ZArray.hpp
Author: James Russell <jcrussell@762studios.com>
Chris Ertel <crertel@762studios.com>
Created: 9/12/2010
Purpose:
Templated dynamic array implementation.
Because ZArray is a dynamic array implementation (not a dynamic vector implementation), it
makes no guarantees about the constructor / destructor behavior of individual elements. This is
to say that as elements enter and leave scope, the underlying array may not be modified and
constructs and destructors may not be called. For example, if space is reserved for 20 elements,
then 20 elements are constructed in an array of size 20. The array will not be deleted until
Reserve(0) is called on an array of size 0, which even then only guarantees that the allocator
is called and the array provided for a 'Deallocate' function call. Whether or not this deletes
the array is up to the allocator.
Note that the default allocator stores space for a certain number of elements and does not
destruct these until the allocator itself leaves 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 _ZARRAY_HPP
#define _ZARRAY_HPP
#include <ZSTL/ZSTLCommon.hpp>
#include <ZSTL/ZSTLInvalidPos.hpp>
//Default capacity for ZArray when no capacity is defined (recommended that this be
//less than or equal to local storage count, user can define their own)
#ifndef ZARRAY_DEFAULT_CAPACITY
#define ZARRAY_DEFAULT_CAPACITY (10)
#endif
//Resize factor for ZArray when operations are performed that require an increase in capacity
//and capacity is not specified (user can define their own, should be greater than 1.0)
#ifndef ZARRAY_CAPACITY_RESIZE_FACTOR
#define ZARRAY_CAPACITY_RESIZE_FACTOR (2.0)
#endif
//Local storage number of elements (user can define their own)
#ifndef ZARRAY_DEFAULT_ALLOCATOR_STORAGE
#define ZARRAY_DEFAULT_ALLOCATOR_STORAGE (10)
#endif
//Forward Declaration of ZArray
template <typename T, typename A>
class ZArray;
//Forward Declaration of ZArrayIterator
template <typename T>
class ZArrayIterator;
/*
Forward Declaration of ZArray Method Implementation Structures
Existence of these structures allows for template specialization of
individual methods of the ZArray class. In order to specialize
a single method of ZArray, simply specialize the corresponding
method implementation structure.
*/
//Forward Declaration of ZArray<T, A>::At struct
template <typename T, typename A>
struct ZArray_AtImpl {
inline static T& Call(const ZArray<T, A>& _self, size_t _index);
};
//Forward Declaration of ZArray<T, A>::Back struct
template <typename T, typename A>
struct ZArray_BackImpl {
inline static T& Call(const ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::Capacity struct
template <typename T, typename A>
struct ZArray_CapacityImpl {
inline static size_t Call(const ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::Clear struct
template <typename T, typename A>
struct ZArray_ClearImpl {
inline static void Call(ZArray<T, A>& _self);
inline static void Call(ZArray<T, A>& _self, size_t _newCapacity);
};
//Forward Declaration of ZArray<T, A>::Copy struct
template <typename T, typename A>
struct ZArray_CopyImpl {
template <typename B> inline static void Call(ZArray<T, A>& _self, const ZArray<T, B>& _other);
};
//Forward Declaration of ZArray<T, A>::Data struct
template <typename T, typename A>
struct ZArray_DataImpl {
inline static T* Call(const ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::Empty struct
template <typename T, typename A>
struct ZArray_EmptyImpl {
inline static bool Call(const ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::Equals struct
template <typename T, typename A>
struct ZArray_EqualsImpl {
template <typename B> inline static bool Call(const ZArray<T, A>& _self, const ZArray<T, B>& _other);
};
//Forward Declaration of ZArray<T, A>::Erase struct
template <typename T, typename A>
struct ZArray_EraseImpl {
inline static T Call(ZArray<T, A>& _self, size_t _index);
inline static void Call(ZArray<T, A>& _self, size_t _start, size_t _end);
};
//Forward Declaration of ZArray<T, A>::Front struct
template <typename T, typename A>
struct ZArray_FrontImpl {
inline static T& Call(const ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::Find struct
template <typename T, typename A>
struct ZArray_FindImpl {
inline static size_t Call(const ZArray<T, A>& _self, const T& elem);
};
//Forward Declaration of ZArray<T, A>::Insert struct
template <typename T, typename A>
struct ZArray_InsertImpl {
inline static void Call(ZArray<T, A>& _self, size_t _index, const T& _value, size_t _count);
template <typename B> inline static void Call(ZArray<T, A>& _self, size_t _index, const ZArray<T, B>& _other, size_t _start, size_t _count);
};
//Forward Declaration of ZArray<T, A>::PopBack struct
template <typename T, typename A>
struct ZArray_PopBackImpl {
inline static T Call(ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::PopFront struct
template <typename T, typename A>
struct ZArray_PopFrontImpl {
inline static T Call(ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::PushBack struct
template <typename T, typename A>
struct ZArray_PushBackImpl {
inline static void Call(ZArray<T, A>& _self, const T& _value);
};
//Forward Declaration of ZArray<T, A>::PushFront struct
template <typename T, typename A>
struct ZArray_PushFrontImpl {
inline static void Call(ZArray<T, A>& _self, const T& _value);
};
//Forward Declaration of ZArray<T, A>::Reserve struct
template <typename T, typename A>
struct ZArray_ReserveImpl {
inline static void Call(ZArray<T, A>& _self, size_t _capacity, bool _reallocate);
};
//Forward Declaration of ZArray<T, A>::Resize struct
template <typename T, typename A>
struct ZArray_ResizeImpl {
inline static void Call(ZArray<T, A>& _self, size_t _size);
inline static void Call(ZArray<T, A>& _self, size_t _size, const T& _value);
inline static void Call(ZArray<T, A>& _self, size_t _size, size_t _capacity, const T& _value);
};
//Forward Declaration of ZArray<T, A>::Size struct
template <typename T, typename A>
struct ZArray_SizeImpl {
inline static size_t Call(const ZArray<T, A>& _self);
};
//Forward Declaration of ZArray<T, A>::Swap struct
template <typename T, typename A>
struct ZArray_SwapImpl {
template <typename B> inline static void Call(ZArray<T, A>& _self, ZArray<T, B>& _other);
};
//////////////////////
/* ZArray Allocator */
//////////////////////
/*
ZArray Allocator class. Used to allocate arrays of a templated type
when requested by a ZArray instance.
NOTE: This allocator keeps a set amount (N) of locally stored objects of type T. These
objects are allocated when the array is created and deallocated when the array is
destructed, and there is no way (other than specializing based on type) to cause these
local elements to destruct other than destructing the array. Reserve(0) will call
'Deallocate', but that will not destruct the elements. So, in summary, do not use the
default allocator when you have a strict requirement that Reserve(0) should deallocate
the array and call contained destructors.
The template parameter T is the type contained the array this allocator is for.
The template parameter N is the amount of local storage this allocator will keep
on hand. Allocations of size less than N (in number of elements) will use the locally
allocated array.
*/
template <typename T, size_t N = ZARRAY_DEFAULT_ALLOCATOR_STORAGE>
class ZArrayAllocator
{
public:
/*
public ZArrayAllocator<T>::Allocate
Allocation method.
@param _size - size of the array to allocate
@return - pointer to an array which can store at least _size values
*/
T* Allocate(size_t _size)
{
if (_size == 0)
return NULL;
if (_size > N)
{
return ZSTL_NEW_ARRAY(T, _size);
}
else
{
return Local;
}
}
/*
public ZArrayAllocator<T>::Deallocate
Deallocation method.
@param _ptr - pointer to previously allocated memory by this allocator
@param _size - size of the memory
*/
void Deallocate(T* _ptr, size_t _size)
{
if (_ptr == NULL)
return;
if (_ptr != &(Local[0]))
{
ZSTL_DEL_ARRAY(_ptr, _size);
}
}
protected:
//The local storage for the allocator
T Local[N];
};
/*
Specialization of ZArrayAllocator for a 0 size local storage. Will
always allocate using ZSTL_NEW_ARRAY.
*/
template <typename T>
class ZArrayAllocator <T, 0>
{
public:
//Allocates the array using ZSTL_NEW_ARRAY
T* Allocate(size_t _size)
{
return ZSTL_NEW_ARRAY(T, _size);
}
//Deallocates the array using ZSTL_DEL_ARRAY
void Deallocate(T* _ptr, size_t _size)
{
ZSTL_DEL_ARRAY(_ptr, _size);
}
};
/*
Heap allocator implementation of an allocator for ZArray. Will
always allocate on the heap.
*/
template <typename T>
class ZArrayHeapAllocator
{
public:
//Allocate on the heap
T* Allocate(size_t _size)
{
return new (std::nothrow) T[_size];
}
//Deallocate the heap array
void Deallocate(T* _ptr, size_t _size)
{
URFP(_size);
delete[] _ptr;
}
};
/////////////////////
/* ZArray Iterator */
/////////////////////
/*
Iterator class for ZArray. Can also be used to iterate a raw array of type T.
The template parameter T is the type contained in the list this iterator is for.
*/
template <typename T>
class ZArrayIterator
{
public:
/*
Default constructor.
*/
ZArrayIterator()
: Array(NULL), Index(0), Size(0)
{ }
/*
Copy constructor.
@param _other - the other iterator
*/
ZArrayIterator(const ZArrayIterator& _other)
: Array(_other.Array), Index(_other.Index), Size(_other.Size)
{ CheckCurrent(); }
/*
Parameterized Constructor.
@param _array - the array this iterator points to
@param _index - the index into the array at which to start the iterator.
@param _size - the size of the array we are pointed to
*/
ZArrayIterator(T *_array, size_t _index, size_t _size)
: Array(_array), Index(_index), Size(_size)
{ CheckCurrent(); }
/*
public ZArrayIterator<T>::CheckCurrent
Check function that determines if the iterator is valid at it's current location.
@param _endIsValid - indicates that the iterator can be at the 'end' location
@return (void)
*/
void CheckCurrent(bool _endIsValid = true) const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(Array != NULL,
"Uninitialized ZArray Iterator used!");
ZSTL_ASSERT(Index < Size + (_endIsValid ? 1 : 0),
"ZArray Iterator has gone past end of array!");
#else
URFP(_endIsValid);
#endif
}
/*
public ZArrayIterator<T>::CheckNext
Check function that determines if incrementing the iterator would be valid,
assuming it was already valid.
@param _inc - the amount by which the iterator will be incremented
@return (void)
*/
void CheckNext(size_t _inc) const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
//Integer overflow possible here, but not likely
ZSTL_ASSERT(Index + _inc <= Size, "ZArray Iterator has gone past end of array!");
#else
URFP(_inc);
#endif
}
/*
public ZArrayIterator<T>::CheckPrevious
Check function that determines if decrementing the iterator would be valid,
assuming it was already valid.
@param _dec - the amount by which the iterator will be decremented
@return (void)
*/
void CheckPrevious(size_t _dec) const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
//i.e. check that "Index - _dec >= 0", but carefully since
//these are unsigned numbers and underflow results in SIZE_MAX
ZSTL_ASSERT(Index >= _dec, "ZArray Iterator has gone past beginning of array!");
#else
URFP(_dec);
#endif
}
/*
public ZArrayIterator<T>::Get
Gets the element this iterator points to.
@return (T&) - the element pointed to
*/
T& Get() const
{
return *(*this);
}
/*
public ZArrayIterator<T>::HasCurrent
Determines if this iterator currently points to a valid element.
@return (bool) - true if element at current position
*/
bool HasCurrent() const
{
return Index < Size;
}
/*
public ZArrayIterator<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 Size != 0 && Index < Size - 1;
}
/*
public ZArrayIterator<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 Index > 0;
}
/*
public ZArrayIterator<T>::Next
Advances this iterator to the next element.
@return (void)
@assert - if this would advance past end
*/
void Next()
{
++(*this);
}
/*
public ZArrayIterator<T>::Prev
Returns this iterator to the previous element.
@return (void)
@assert - if this would advance past begin
*/
void Prev()
{
--(*this);
}
//Operator Overrides
ZArrayIterator& operator ++() { CheckNext(1); Index++; return *this; }
ZArrayIterator operator ++ (int) { CheckNext(1); return ZArrayIterator(Array, Index++, Size); }
ZArrayIterator operator + (int _value) { return ZArrayIterator(Array, Index + _value, Size); }
ZArrayIterator& operator += (int _value) { CheckNext(_value); Index += _value; return *this; }
ZArrayIterator& operator -- () { CheckPrevious(1); Index--; return *this; }
ZArrayIterator operator -- (int) { CheckPrevious(1); return ZArrayIterator(Array, Index--, Size); }
ZArrayIterator operator - (int _value) { return ZArrayIterator(Array, Index - _value, Size); }
ZArrayIterator& operator -= (int _value) { CheckPrevious(_value); Index -= _value; return *this; }
ZArrayIterator& operator = (const ZArrayIterator &_other) { Array = _other.Array; Index = _other.Index; Size = _other.Size; CheckCurrent(); return *this; }
bool operator == (const ZArrayIterator &_other) const { return Index == _other.Index; }
bool operator != (const ZArrayIterator &_other) const { return !(Index == _other.Index); }
bool operator < (const ZArrayIterator &_other) const { return (Index < _other.Index); }
bool operator <= (const ZArrayIterator &_other) const { return (Index <= _other.Index); }
bool operator > (const ZArrayIterator &_other) const { return (Index > _other.Index); }
bool operator >= (const ZArrayIterator &_other) const { return (Index >= _other.Index); }
T& operator *() const { CheckCurrent(false); return Array[Index]; }
operator size_t () const { return Index; }
protected:
//The array
T* Array;
//The current index into the array
size_t Index;
//The size of the array (at construction)
size_t Size;
};
///////////////////////////
/* ZArray Implementation */
///////////////////////////
/*
Templated dynamic array implementation.
The template parameter T is the type contained in the array.
The template parameter A is the type of allocator to use, which must allocate
arrays of type T.
*/
template <typename T, typename A = ZArrayAllocator<T, ZARRAY_DEFAULT_ALLOCATOR_STORAGE> >
class ZArray
{
friend struct ZArray_AtImpl<T, A>;
friend struct ZArray_BackImpl<T, A>;
friend struct ZArray_CapacityImpl<T, A>;
friend struct ZArray_ClearImpl<T, A>;
friend struct ZArray_CopyImpl<T, A>;
friend struct ZArray_DataImpl<T, A>;
friend struct ZArray_EmptyImpl<T, A>;
friend struct ZArray_EqualsImpl<T, A>;
friend struct ZArray_EraseImpl<T, A>;
friend struct ZArray_FrontImpl<T, A>;
friend struct ZArray_FindImpl<T, A>;
friend struct ZArray_InsertImpl<T, A>;
friend struct ZArray_PopBackImpl<T, A>;
friend struct ZArray_PopFrontImpl<T, A>;
friend struct ZArray_PushBackImpl<T, A>;
friend struct ZArray_ReserveImpl<T, A>;
friend struct ZArray_ResizeImpl<T, A>;
friend struct ZArray_SizeImpl<T, A>;
friend struct ZArray_SwapImpl<T, A>;
public:
/*
Typedef for ZArrayIterator (Allows ZArray<T, A>::Iterator notation).
*/
typedef ZArrayIterator<T> Iterator;
/*
Default Constructor.
*/
ZArray()
: Array(NULL),
ArrayCapacity(ZARRAY_DEFAULT_CAPACITY),
ArraySize(0)
{
Allocate(ArrayCapacity);
CheckIntegrity();
}
/*
Parameterized Constructor.
@param _capacity - the starting capacity of the array
*/
explicit ZArray(size_t _capacity)
: Array(NULL),
ArrayCapacity(_capacity),
ArraySize(0)
{
Allocate(ArrayCapacity);
CheckIntegrity();
}
/*
Parameterized Constructor. Given a reference to an array with a
size that can be deduced by the compiler, will create the array
using that data.
@param C - the length of the array
@param _data - the data to initialize with
@param _count - the length of the data
*/
template <size_t C>
explicit ZArray( const T (&_data)[C])
: Array(NULL),
ArrayCapacity((size_t)(C * ZARRAY_CAPACITY_RESIZE_FACTOR)),
ArraySize(C)
{
Allocate(ArrayCapacity);
for (size_t i = 0; i < C; i++)
Array[i] = _data[i];
CheckIntegrity();
}
/*
Parameterized Constructor.
@param _capacity - the starting capacity of the array
@param _size - the starting size of the array
*/
ZArray(size_t _capacity, size_t _size)
: Array(NULL),
ArrayCapacity(_capacity),
ArraySize(_size)
{
Allocate(ArrayCapacity);
Resize(ArraySize);
CheckIntegrity();
}
/*
Parameterized Constructor. Given a raw array and a size, will
create this array using that data.
@param _data - the data to initialize with
@param _count - the length of the data
*/
ZArray(const T* _data, size_t _count)
: Array(NULL),
ArrayCapacity((size_t)(_count * ZARRAY_CAPACITY_RESIZE_FACTOR)),
ArraySize(_count)
{
Allocate(ArrayCapacity);
for (size_t i = 0; i < _count; i++)
Array[i] = _data[i];
CheckIntegrity();
}
/*
Parameterized Constructor. Given a raw array and a size, will
create this array using that data.
@param _data - the data to initialize with
@param _count - the length of the data
@param _capacity - the capacity to initialize with (must be greater than size)
*/
ZArray(const T* _data, size_t _count, size_t _capacity)
: Array(NULL),
ArrayCapacity(_capacity),
ArraySize(_count)
{
Allocate(ArrayCapacity);
for (size_t i = 0; i < _count; i++)
Array[i] = _data[i];
CheckIntegrity();
}
/*
Copy Constructor. Makes a deep copy of the array. Even though the code
is the same as the more generic copy constructor below, the code is needed to
ensure a default copy constructor is not generated.
@param _other - the array to copy from
*/
ZArray(const ZArray<T, A>& _other)
: Array(NULL),
ArrayCapacity(_other.Capacity()),
ArraySize(_other.Size())
{
T* data;
size_t i;
Allocate(ArrayCapacity);
CheckIntegrity();
for (i = 0, data = _other.Data(); i < ArraySize; i++)
Array[i] = data[i];
}
/*
Copy Constructor. Makes a deep copy of the array from an array with
a different allocator type.
@param B - the allocator type of the other array
@param _other - the array to copy from
*/
template <typename B>
ZArray(const ZArray<T, B>& _other)
: Array(NULL),
ArrayCapacity(_other.Capacity()),
ArraySize(_other.Size())
{
T* data;
size_t i;
Allocate(ArrayCapacity);
CheckIntegrity();
for (i = 0, data = _other.Data(); i < ArraySize; i++)
Array[i] = data[i];
}
/*
Slice Copy Constructor. Constructs this array as a slice of another array.
@param B - the allocator type of the other array
@param _other - the array to copy from
*/
template <typename B>
ZArray(const ZArray<T, B>& _other, size_t _start, size_t _end)
: Array(NULL),
ArrayCapacity(_other.Capacity()),
ArraySize(_end - _start)
{
Allocate(ArrayCapacity);
CheckIntegrity();
_other.BoundsCheck(_start, _other.Size());
_other.BoundsCheck(_end, _other.Size() + 1);
for (size_t i = _start; i < _end; i++)
Array[i - _start] = _other.Data()[i];
}
/*
Destructor.
*/
~ZArray()
{
CheckIntegrity();
Deallocate(Array, ArrayCapacity);
}
/*
[] Operator overload. Gets a value from the array. Functionally equivalent to
a call to At.
@param _index - the integer index into the array
@return (T&) - reference to a value contained in the array
@assert - if the index is out of bounds
*/
T& operator [] (size_t _index) const
{ return At(_index); }
/*
= Operator overload. Sets this array equal to the other by making
a copy. Even though the code is the same as the more generic version below,
this is needed to prevent a default assignment operator from being constructed.
@param _other - the array to be set equal to
@return (ZArray<T, A>&) - this array
*/
ZArray<T, A>& operator = (const ZArray<T, A>& _other)
{ Copy(_other); return *this; }
/*
= Operator overload. Sets this array equal to the other by making
a copy.
@param _other - the array to be set equal to
@return (ZArray<T, A>&) - this array
*/
template <typename B>
ZArray<T, A>& operator = (const ZArray<T, B>& _other)
{ Copy(_other); return *this; }
/*
== Operator overload. Performs an element comparison on the two arrays.
@param B - the type of allocator for the other array
@param _other - the array to compare to
@return (bool) - true if this array is equivalent to the other
*/
template <typename B>
bool operator == (const ZArray<T, B>& _other) const
{ return Equals(_other); }
/*
!= Operator overload. Performs an element comparison on the two arrays.
@param B - the type of the allocator for the other array
@param _other - the array to compare to
@return (bool) - false if this array is equivalent to the other
*/
template <typename B>
bool operator != (const ZArray<T, B>& _other) const
{ return !Equals(_other); }
/*
public ZArray<T, A>::Allocator
Returns a reference to the installed allocator.
@return (A&) - the array allocator
*/
A& Allocator()
{ return ArrayAllocator; }
/*
public ZArray<T, A>::At
Gets a value from the array at a given position.
@param _index - index into the array
@return (T&) - the value at the given index
@assert - if the index is out of bounds
*/
T& At(size_t _index) const
{ return ZArray_AtImpl<T, A>::Call(*this, _index); }
/*
public ZArray<T, A>::Back
Gets a reference to the value at the back of the array.
@return (T&) - the value at the end of the array
@assert - if the array is empty
*/
T& Back() const
{ return ZArray_BackImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::Begin
Returns an iterator to the beginning of the array.
@return (ZArray<T, A>::Iterator) - Iterator with index zero
*/
Iterator Begin()
{ return ZArrayIterator<T>(Array, 0, ArraySize); }
/*
public ZArray<T, A>::BoundsCheck
Function that checks the provided index against the provided boundary, asserting if an
out of bounds access occurs. Calls to this will be compiled out if ZSTL_DISABLE_RUNTIME_CHECKS
is not zero.
@param _index - index
@param _boundary - the (exclusive) boundary to use for bounds checking
@return (size_t) - unsigned index into the array
@assert - if the index is not less than boundary
*/
size_t BoundsCheck(size_t _index, size_t _boundary) const
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_index < _boundary, "ZArray: Out of bounds access!");
#else
(void)_boundary;
#endif
return _index;
}
/*
public ZArray<T, A>::Capacity
Returns the capacity of the array, which is the number of values it can contain
before allocation is required to increase storage size.
@return (size_t) - array capacity
*/
size_t Capacity() const
{ return ZArray_CapacityImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::Clear
Clears out the array of all contained elements, keeping the currently allocated
storage.
@return (void)
*/
void Clear()
{ ZArray_ClearImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::Clear
Clears out the array of all contained elements and will ensure that the capacity is
at least as much as the provided value. Reallocates the internal storage if necessary.
@param _newCapacity - the (minimum) capacity of the array after clearing
@return (void)
*/
void Clear(size_t _newCapacity)
{ ZArray_ClearImpl<T, A>::Call(*this, _newCapacity); }
/*
public ZArray<B>::Copy
Makes a deep copy of the provided array.
@param _other - the array to copy from
@return (void)
*/
template <typename B>
void Copy(const ZArray<T, B>& _other)
{ ZArray_CopyImpl<T, A>::template Call<B>(*this, _other); }
/*
public ZArray<T, A>::Data
Gets a pointer to the underlying array data.
@return (T*) - pointer to the first element in the array
*/
T* Data() const
{ return ZArray_DataImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::Empty
Returns true if the array is empty (size 0), false otherwise.
@return (bool) - true if empty, false otherwise
*/
bool Empty() const
{ return ZArray_EmptyImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::End
Returns an iterator to the end of the array.
@return (ZArray<T, A>::Iterator) - Iterator with index set to capacity
*/
Iterator End()
{ return ZArrayIterator<T>(Array, ArraySize, ArraySize); }
/*
public ZArray<T, A>::Equals
Determines, with an element by element comparison, if this array is equivalent
to another.
@param _other - the array to compare with
@return (bool) - true if equal, false otherwise
*/
template <typename B>
bool Equals(const ZArray<T, B>& _other) const
{ return ZArray_EqualsImpl<T, A>::template Call<B>(*this, _other); }
/*
public ZArray<T, A>::Erase
Erase function. Removes an element from the array at the given index.
@param _index - the index at which to remove the element from the array.
@return (T) - the element removed
@assert - if the index is invalid
*/
T Erase(size_t _index)
{ return ZArray_EraseImpl<T, A>::Call(*this, _index); }
/*
public ZArray<T, A>::Erase
Erase function. Removes elements from the array between the given indices.
@param _start - the starting index
@param _end - the ending index (exclusive)
@return (void)
@assert - if the indices are invalid
*/
void Erase(size_t _start, size_t _end)
{ ZArray_EraseImpl<T, A>::Call(*this, _start, _end); }
/*
public ZArray<T, A>::Front
Gets a reference to the value at the front of the array.
@return (T&) - the value at the front of the array
@assert - if the array is empty
*/
T& Front() const
{ return ZArray_FrontImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::Find
Find function. Finds the first element in the array that is equal to elem,
determined via == operator.
@param _elem - the elem to find
@return (size_t) - the index of the first occurence of elem, ZSTL::InvalidPos if not found
*/
size_t Find(const T& _elem)
{ return ZArray_FindImpl<T, A>::Call(*this, _elem); }
/*
public ZArray<T, A>::Insert
Inserts the given value at the specified location.
@param _index - the index at which to perform the insertion
@param _value - the value to insert
@return (void)
@assert - if the index is invalid
*/
void Insert(size_t _index, const T& _value)
{ ZArray_InsertImpl<T, A>::Call(*this, _index, _value, 1); }
/*
public ZArray<T, A>::Insert
Inserts the given value at the specified location the given number of times.
@param _index - the index at which to perform the insertion
@param _value - the value to insert
@param _count - the number of times to insert _value
@return (void)
@assert - if the index is invalid
*/
void Insert(size_t _index, const T& _value, size_t _count)
{ ZArray_InsertImpl<T, A>::Call(*this, _index, _value, _count); }
/*
public ZArray<T, A>::Insert
Inserts the entirety of the given array at the specified location.
@param _index - the index at which to perform the insertion
@param _other - the array to insert values from
@return (void)
@assert - if the index is invalid
*/
template <typename B>
void Insert(size_t _index, const ZArray<T, B>& _other)
{ ZArray_InsertImpl<T, A>::template Call<B>(*this, _index, _other, 0, _other.Size()); }
/*
public ZArray<T, A>::Insert
Inserts data from the given array at the specified location.
@param _index - the index at which to perform the insertion
@param _other - the array to insert values from
@param _start - the index to start getting values from
@param _count - the number of elements to insert starting at index
@return (void)
@assert - if the index is invalid or the start index is invalid
*/
template <typename B>
void Insert(size_t _index, const ZArray<T, B>& _other, size_t _start, size_t _count)
{ ZArray_InsertImpl<T, A>::template Call<B>(*this, _index, _other, _start, _count); }
/*
public ZArray<T, A>::PopBack
Pop function. Removes and returns the last element in the array.
@return (T) - the last element in the array
@assert - if the array is empty
*/
T PopBack()
{ return ZArray_PopBackImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::PopFront
Pop function. Removes and returns the first element in the array. This operation
requires shifting all elements in the array forward.
@return (T) - the first element in the array
@assert - if the array is empty
*/
T PopFront()
{ return ZArray_PopFrontImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::PushBack
Push function. Attaches an element to the end of the array. Will allocate
storage if necessary.
@param _value - the element to place at the end of the array
@return (void)
*/
void PushBack(const T& _value)
{ ZArray_PushBackImpl<T, A>::Call(*this, _value); }
/*
public ZArray<T, A>::PushFront
Push function. Attaches an element to the beginning of the array. This operation
requires shifting all elements in the array back. Will allocate storage if necessary.
@param _value - the element to place at the beginning of the array
@return (void)
*/
void PushFront(const T& _value)
{ ZArray_PushFrontImpl<T, A>::Call(*this, _value); }
/*
public ZArray<T, A>::Reserve
Reserves an amount of space in the vector. Allocates space if
necessary (capacity > current capacity). The array capacity cannot
be reduced in size below the number of contained elements.
Reserve(0) is guaranteed to always deallocate the array using
the provided deallocator.
@param _capacity - the new capacity requested
@return (void)
@assert - if capacity is less than size
*/
void Reserve(size_t _capacity)
{ ZArray_ReserveImpl<T, A>::Call(*this, _capacity, false); }
/*
public ZArray<T, A>::Reserve
Reserves an amount of space in the vector. Allocates space if
necessary (capacity > current capacity). The array capacity cannot
be reduced in size below the number of contained elements.
Reserve(0) is guaranteed to always deallocate the array using
the provided deallocator, regardless of the value of _reallocate.
@param _capacity - the new capacity requested
@param _reallocate - if true, will always reallocate storage
@return (void)
@assert - if capacity is less than size
*/
void Reserve(size_t _capacity, bool _reallocate)
{ ZArray_ReserveImpl<T, A>::Call(*this, _capacity, _reallocate); }
/*
public ZArray<T, A>::Resize
Resize function. Increases or Decreases the size of the array.
@param _size - the new size to grow the array to
@return (void)
*/
void Resize(size_t _size)
{ ZArray_ResizeImpl<T, A>::Call(*this, _size); }
/*
public ZArray<T, A>::Resize
Resize function. Increases or Decreases the size of the array and
sets new values equal to the given value.
@param _size - the new size to grow (or shrink) the array to
@param _value - the new value to set all added values to
@return (void)
*/
void Resize(size_t _size, const T& _value)
{ ZArray_ResizeImpl<T, A>::Call(*this, _size, _value); }
/*
public ZArray<T, A>::Resize
Resize function. Increases or decreases the size of the array and
reserves a new capacity (must be greater than size). Sets new values
equal to the given value.
@param _size - the new size to grow (or shrink) the array to
@param _capacity - the new capacity to reserve (must be greater than or equal to size)
@param _value - the new value to set all added values to
@return (void)
*/
void Resize(size_t _size, size_t _capacity, const T& _value)
{ ZArray_ResizeImpl<T, A>::Call(*this, _size, _capacity, _value); }
/*
public ZArray<T, A>::Size
Size function. Give the size of the array.
@return (size_t) - the size of this array
*/
size_t Size() const
{ return ZArray_SizeImpl<T, A>::Call(*this); }
/*
public ZArray<T, A>::Swap
Swaps the array contents with another array with any
local storage size.
@param _other - the array to swap contents with
@return (void)
*/
template <typename B>
void Swap(ZArray<T, B>& _other)
{ ZArray_SwapImpl<T, A>::Call(*this, _other); }
protected:
//The array pointer
T *Array;
//Allocator for the Array
A ArrayAllocator;
//The 'size' of the raw array pointer
size_t ArrayCapacity;
//The current number of contained elements in the array
size_t ArraySize;
//Makes a call to the allocator and checks the result
inline void Allocate(size_t _size)
{
Array = ArrayAllocator.Allocate(_size);
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(!(Array == NULL && _size > 0), "ZArrayAllocator failed to properly allocate array! (returned NULL with size > 0)");
#endif
}
//Makes a call to the allocator
inline void Deallocate(T* _array, size_t _size)
{
ArrayAllocator.Deallocate(_array, _size);
}
//Shifts values up in the array from _start by _count
inline void ShiftUp(size_t _index, size_t _count)
{
/*
Shift elements up, but do so in reverse so that we don't just copy Array[i] to all of the higher addresses.
If you do:
Array[i+1] = Array[i];
...in a loop, then the first iteration overwrites the second value with the first, but it doesn't preserve
the second so it can be copied into the third. If you run this code on [1, 2, 3, 0], you get [1, 1, 3, 0] after the first
iteration, and then [1, 1, 1, 0] after the second iteration, and so on.
The solution is to copy backwards. Consider an array of 100 elements:
[1, 2, 3, ..., 100]
...that is to by shifted by 3 elements, starting at index 5. Then, we know that
the final array should look like:
[1, 2, 3, 4, X, X, X, ..., 5, 6, 7, ... 100].
where X is undefined (in this function, it is "_value"). The number of elements that
will be shifted is 100 - 5 = 95 elements. Further more, each element will be shifted 3
units. Let S be the size of the array (100), I be the index to start at (5), and N
be the number to shift by (3). Then,
Since we are copying backwards, consider the final element: Array[S-1]. This element
is to be moved N units, so:
Array[S-1 + N] = Array[S-1];
Add in a counter, K, that decreases the index value each loop iteration, and loop S-I times.
Array[S-1 + N - K] = ArraySize[S-1 - K]
*/
size_t i;
const size_t nShift = ArraySize - _index;
//Shift elements up
for(i = 0; i < nShift; i++)
Array[ArraySize - 1 + _count - i] = Array[ArraySize - 1 - i];
}
//Integrity Check (used for internal debugging)
inline void CheckIntegrity() const
{
#if ZSTL_CHECK_INTEGRITY
ZSTL_ASSERT(!(Array == NULL && ArrayCapacity > 0), "ZArray Error: Array is invalid!");
ZSTL_ASSERT(ArrayCapacity >= ArraySize, "ZArray Error: Array capacity less than size!");
#endif
}
};
////////////////////////////////////////////
/* Non-Specialized Method Implementations */
////////////////////////////////////////////
template <typename T, typename A>
T& ZArray_AtImpl<T, A>::Call( const ZArray<T, A>& _self, size_t _index )
{
size_t index = _self.BoundsCheck(_index, _self.ArraySize);
return _self.Array[index];
}
/*************************************************************************/
template <typename T, typename A>
T& ZArray_BackImpl<T, A>::Call( const ZArray<T, A>& _self )
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot get back element from empty array!");
#endif
return _self.Array[_self.ArraySize - 1];
}
/*************************************************************************/
template <typename T, typename A>
size_t ZArray_CapacityImpl<T, A>::Call(const ZArray<T, A>& _self)
{
//Give back our current capacity
return _self.ArrayCapacity;
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_ClearImpl<T, A>::Call(ZArray<T, A>& _self)
{
//Reset size and check integrity
_self.ArraySize = 0;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_ClearImpl<T, A>::Call(ZArray<T, A>& _self, size_t _newCapacity)
{
//Reset size, reserve new capacity, then check integrity
_self.ArraySize = 0;
_self.Reserve(_newCapacity, false);
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A> template <typename B>
void ZArray_CopyImpl<T, A>::Call(ZArray<T, A>& _self, const ZArray<T, B>& _other)
{
size_t i;
//If both arrays point to the same data, we're done here
if (_self.Data() == _other.Data())
return;
//Make sure we have room, write data, and check integrity
_self.Resize(_other.Size());
for (i = 0; i < _other.Size(); i++)
_self.Array[i] = _other.Data()[i];
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
T* ZArray_DataImpl<T, A>::Call(const ZArray<T, A>& _self)
{
return _self.Array;
}
/*************************************************************************/
template <typename T, typename A>
bool ZArray_EmptyImpl<T, A>::Call(const ZArray<T, A>& _self)
{
return _self.ArraySize == 0;
}
/*************************************************************************/
template <typename T, typename A> template <typename B>
bool ZArray_EqualsImpl<T, A>::Call(const ZArray<T, A>& _self, const ZArray<T, B>& _other)
{
size_t i;
//First see if we have the same size
if (_self.ArraySize == _other.Size())
{
//Element wise comparison
for (i = 0; i < _self.ArraySize; i++)
{
if (!(_self.Array[i] == _other.Array[i]))
return false;
}
}
else
{
//Nope, so false
return false;
}
return true;
}
/*************************************************************************/
template <typename T, typename A>
T ZArray_EraseImpl<T, A>::Call(ZArray<T, A>& _self, size_t _index)
{
size_t index = _self.BoundsCheck(_index, _self.ArraySize);
//Grab the currently held element, shift the array down, reduce size, and check integrity
T element = _self.Array[index];
for (size_t i = index; i + 1 < _self.ArraySize; i++)
_self.Array[i] = _self.Array[i + 1];
_self.ArraySize--;
_self.CheckIntegrity();
return element;
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_EraseImpl<T, A>::Call(ZArray<T, A>& _self, size_t _start, size_t _end)
{
if (_start == _end)
return;
size_t start = _self.BoundsCheck(_start, _self.ArraySize);
size_t end = _self.BoundsCheck(_end, _self.ArraySize + 1);
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(start <= end, "ZArray: cannot erase with start < end!");
#endif
//Copy the elements down, compute new size, and check integrity
for (size_t idx = start; idx + (end - start) < _self.ArraySize; idx++)
_self.Array[idx] = _self.Array[idx + (end - start)];
_self.ArraySize = _self.ArraySize - (end - start);
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
T& ZArray_FrontImpl<T, A>::Call( const ZArray<T, A>& _self )
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot get front element from empty array!");
#endif
return _self.Array[0];
}
/*************************************************************************/
template <typename T, typename A>
size_t ZArray_FindImpl<T, A>::Call( const ZArray<T, A>& _self, const T& _elem )
{
for (size_t i = 0; i < _self.ArraySize; i++) {
if (_self.Array[i] == _elem) {
return i;
}
}
return ZSTL::InvalidPos;
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_InsertImpl<T, A>::Call(ZArray<T, A>& _self, size_t _index, const T& _value, size_t _count)
{
//Get our insertion index (we allow past end to indicate insert at 'end')
size_t index = _self.BoundsCheck(_index, _self.ArraySize + 1);
const size_t newSize = _self.ArraySize + _count;
//Reserve enough space for the new elements
if (newSize > _self.ArrayCapacity)
_self.Reserve(newSize);
//Shift up existing elements
_self.ShiftUp(index, _count);
//Insert new values
for (size_t i = 0; i < _count; i++)
_self.Array[index + i] = _value;
//Increase our array size
_self.ArraySize += _count;
//We are non const, so check
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A> template <typename B>
void ZArray_InsertImpl<T, A>::Call(ZArray<T, A>& _self, size_t _index, const ZArray<T, B>& _other, size_t _start, size_t _count)
{
//Get our indices (once again, allowed to go past end to indicate 'append')
size_t index = _self.BoundsCheck(_index, _self.ArraySize + 1);
size_t start = _other.BoundsCheck(_start, _other.Size() + 1);
_other.BoundsCheck(_start + _count, _other.Size() + 1); //This will do a bounds check on other
const size_t newSize = _self.ArraySize + _count;
//Make sure we have enough space
if (newSize > _self.ArrayCapacity)
_self.Reserve( newSize );
//Shift elements up
_self.ShiftUp(index, _count);
//Copy in the data from the other
for (size_t i = 0; i < _count; i++)
_self.Array[index + i] = _other.Data()[start + i];
//Set our new size
_self.ArraySize = newSize;
//We are non-const, so check
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
T ZArray_PopBackImpl<T, A>::Call(ZArray<T, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot pop from array with no elements!");
#endif
//Grab the last element in the array and decrease our array size
return _self.Array[--(_self.ArraySize)];
}
/*************************************************************************/
template <typename T, typename A>
T ZArray_PopFrontImpl<T, A>::Call(ZArray<T, A>& _self)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot pop from array with no elements!");
#endif
//Just use our erase function
return _self.Erase(0);
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_PushBackImpl<T, A>::Call(ZArray<T, A>& _self, const T& _value)
{
//See if we need more space
if (_self.ArraySize >= _self.ArrayCapacity)
_self.Reserve((size_t)((float)_self.ArrayCapacity * ZARRAY_CAPACITY_RESIZE_FACTOR) + 1 ); //Calls CheckIntegrity
//Add in the element
_self.Array[(_self.ArraySize)++] = _value;
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_PushFrontImpl<T, A>::Call(ZArray<T, A>& _self, const T& _value)
{
//Just use the insert function
return _self.Insert(0, _value, 1);
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_ReserveImpl<T, A>::Call(ZArray<T, A>& _self, size_t _capacity, bool _reallocate)
{
#if !ZSTL_DISABLE_RUNTIME_CHECKS
ZSTL_ASSERT(_capacity >= _self.ArraySize, "ZArray: Cannot reserve capacity less than array size!");
#endif
//Special case for reserve 0
if (_capacity == 0)
{
_self.Deallocate(_self.Array, _self.ArrayCapacity);
_self.Array = NULL;
_self.ArrayCapacity = 0;
return;
}
//See if we actually need to increase our capacity
if (!_reallocate && _capacity <= _self.ArrayCapacity)
return;
//Set our temp array
T* temp = _self.Array;
//Allocate an array
_self.Allocate(_capacity);
//Make sure we aren't doing something pointless here
if (temp == _self.Array)
{
_self.ArrayCapacity = _capacity;
return;
}
//Copy the data
for (size_t i = 0; i < _self.ArraySize; i++)
_self.Array[i] = temp[i];
//Deallocate the temporary (old) array
_self.Deallocate(temp, _self.ArrayCapacity);
//Set our new capacity
_self.ArrayCapacity = _capacity;
//non-const, so check integrity
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_ResizeImpl<T, A>::Call(ZArray<T, A>& _self, size_t _size)
{
//Check to see if we need more space, change our size, and check integrity
if (_size > _self.ArrayCapacity)
_self.Reserve(_size);
_self.ArraySize = _size;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_ResizeImpl<T, A>::Call(ZArray<T, A>& _self, size_t _size, const T& _value)
{
//See if we need more space, copy in the new value, change our size, and check integrity
if (_size > _self.ArrayCapacity)
_self.Reserve(_size);
for (size_t i = _self.ArraySize; i < _size; i++)
_self.Array[i] = _value;
_self.ArraySize = _size;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
void ZArray_ResizeImpl<T, A>::Call(ZArray<T, A>& _self, size_t _size, size_t _capacity, const T& _value)
{
_self.Reserve(_capacity);
//See if we need more space, copy in the new value, change our size, and check integrity
if (_size > _self.ArrayCapacity)
_self.Reserve(_size);
for (size_t i = _self.ArraySize; i < _size; i++)
_self.Array[i] = _value;
_self.ArraySize = _size;
_self.CheckIntegrity();
}
/*************************************************************************/
template <typename T, typename A>
size_t ZArray_SizeImpl<T, A>::Call(const ZArray<T, A>& _self)
{
return _self.ArraySize;
}
/*************************************************************************/
template <typename T, typename A> template <typename B>
void ZArray_SwapImpl<T, A>::Call(ZArray<T, A>& _self, ZArray<T, B>& _other)
{
T tempElement;
size_t i;
size_t smallerSize;
// Arrays are same size.
if (_self.ArraySize == _other.ArraySize)
{
for (i = 0; i < _self.ArraySize; i++)
{
tempElement = _other[i];
_other[i] = _self[i];
_self[i] = tempElement;
}
return;
}
// Self is larger than other.
else if (_self.ArraySize > _other.ArraySize)
{
smallerSize = _other.ArraySize;
// Swap all of the smaller array's elements
for (i = 0; i < smallerSize; i++)
{
tempElement = _other[i];
_other[i] = _self[i];
_self[i] = tempElement;
}
// Resize smaller array to be larger.
_other.Reserve(_self.ArraySize);
for (i = smallerSize; i < _self.ArraySize; i++)
_other.PushBack(_self[i]);
// Truncate our array.
_self.Resize(smallerSize);
}
else
{
smallerSize = _self.ArraySize;
// Swap all of the smaller array's elements
for (i = 0; i < smallerSize; i++)
{
tempElement = _other[i];
_other[i] = _self[i];
_self[i] = tempElement;
}
// Resize smaller array to be larger.
_self.Reserve(_other.ArraySize);
for (i = smallerSize; i < _other.ArraySize; i++)
_self.PushBack(_other[i]);
// Truncate our array.
_other.Resize(smallerSize);
}
//Check integrity on both arrays
_self.CheckIntegrity();
_other.CheckIntegrity();
}
#endif