Files
FC1/CryCommon/TArrays.h
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

893 lines
21 KiB
C++

#ifndef _CRY_COMMON_TARRAYS_HDR_
#define _CRY_COMMON_TARRAYS_HDR_
#include "platform.h"
#if !defined(LINUX)
#include <assert.h>
#endif
#include "TAlloc.h"
template <typename T>
void TSimpleSwap (T& a, T& b)
{
T t = a;
a = b;
b = t;
}
//////////////////////////////////////////////////////////////////////////
// a class with STL naming conventions, incapsulating just a pointer.
// it mimics the std::vector with a few methods, but the class doesn't know
// about the array size and lacks most of the vector functionality.
// WHAT IT IS FOR:
// If you need an array that you know the size for, use this.
// If you need a local dynamic array that will self-deallocate when out of scope of the function, use this.
//////////////////////////////////////////////////////////////////////////
template <typename T, class A = TSimpleAllocator<T> >
class TElementaryArray
{
public:
typedef T value_type;
typedef T& reference;
typedef const T & const_reference;
typedef T* iterator;
typedef const T* const_iterator;
TElementaryArray (const char* szParentObject, int nParentIndex = 0):
m_Allocator(szParentObject, nParentIndex),
m_pData (NULL)
{
#if defined(_DEBUG) && defined(_INC_CRTDBG)
m_nSize = 0;
#endif
}
TElementaryArray (size_t numElements)
#if defined(_DEBUG) && defined(_INC_CRTDBG)
:m_Allocator("TElementaryArray(n)",1),
m_nSize(numElements)
#endif
{
m_pData = numElements?m_Allocator.allocate_construct(numElements):NULL;
}
TElementaryArray ():
m_pData (NULL)
#if defined(_DEBUG) && defined(_INC_CRTDBG)
,m_Allocator("TElementaryArray()",2)
,m_nSize(0)
#endif
{
}
private:
// copy constructor: impossible since the number of elements is unknown
TElementaryArray (const TElementaryArray<T,A>& that):
m_pData (NULL)
{
assert(0);
}
// assignment is impossible because the size is unknown
TElementaryArray<T,A>& operator = (const TElementaryArray<T,A>& src)
{
assert(0);
}
public:
~TElementaryArray()
{
m_Allocator.deallocate_destroy(m_pData);
#if defined(_DEBUG) && defined(_INC_CRTDBG)
m_pData = (T*)(UINT_PTR)0xFFEEDDCC; // for debug
#endif
}
bool empty() const
{
return m_pData == 0;
}
// resizes the array, doesn't preserve the contents
void reinit (size_t numElements)
{
clear();
if (numElements > 0)
{
m_pData = m_Allocator.allocate_construct(numElements);
#if defined(_DEBUG) && defined(_INC_CRTDBG)
m_nSize = numElements;
#endif
}
}
// resizes the array, doesn't preserve the contents, copies the given value to all the elements
void reinit (size_t numElements, const T& element)
{
reinit(numElements);
for (size_t i = 0; i < numElements; ++i)
m_pData[i] = element;
}
// swaps this array with the given one
void swap (TElementaryArray<T,A>& rRight)
{
TSimpleSwap (m_pData, rRight.m_pData);
#if defined(_DEBUG) && defined(_INC_CRTDBG)
TSimpleSwap (m_nSize, rRight.m_nSize);
#endif
}
// cleans up the array
void clear()
{
if (m_pData)
{
m_Allocator.deallocate_destroy(m_pData);
m_pData = NULL;
#if defined(_DEBUG) && defined(_INC_CRTDBG)
m_nSize = 0;
#endif
}
}
// returns the first element of the array
reference front()
{
assert (!empty());
return m_pData[0];
}
// returns the first element of the array
const_reference front() const
{
assert (!empty());
return m_pData[0];
}
// returns the begin iterator of the contained sequence
// there is no end iterator in this array, if you need one, use std::vector
iterator begin()
{
return m_pData;
}
// returns the begin iterator of the contained sequence
// there is no end iterator in this array, if you need one, use std::vector
const_iterator begin() const
{
return m_pData;
}
reference operator [] (size_t i)
{
// it is unlikely that the array is more than 1 gigabyte long, so check the index for saneness
assert (i < 0x40000000 / sizeof(T));
#if defined(_DEBUG) && defined(_INC_CRTDBG)
assert (i < m_nSize);
#endif
return m_pData[i];
}
const_reference operator [] (size_t i) const
{
// it is unlikely that the array is more than 1 gigabyte long, so check the index for saneness
assert (i < 0x40000000 / sizeof(T));
#if defined(_DEBUG) && defined(_INC_CRTDBG)
assert (i < m_nSize);
#endif
return m_pData[i];
}
protected:
// array of elements, or NULL
T* m_pData;
#if defined(_DEBUG) && defined(_INC_CRTDBG)
// the actual size of the array, USE FOR DEBUGGING ONLY
size_t m_nSize;
#endif
A m_Allocator;
};
////////////////////////////////////////////////////////////////////////////////////////
// a class with STL naming conventions of the methods, incapsulating an array of fixed
// size. STL naming conventions are for easier transition between this and std::vector.
// optimized for one-time construction and long-term usage (minimum memory overhead)
// You can reinitialize the array with resize() though, but be careful: it's not fast
//
// The only advantage over std::vector is that the number of elements allocates is kept
// to the minimum, which is optimal in the case of one-time allocation
////////////////////////////////////////////////////////////////////////////////////////
template <typename T, class A = TSimpleAllocator<T> >
class TFixedArray
{
public:
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* iterator;
typedef const T* const_iterator;
TFixedArray (const char* szParentObject, int nParentIndex = 0):
m_nSize (0),
m_pData (NULL),
m_Allocator(szParentObject, nParentIndex)
{
}
TFixedArray (size_t numElements):
m_nSize (numElements)
{
m_pData = numElements?m_Allocator.allocate_construct(numElements):NULL;
}
TFixedArray ():
m_nSize (0),
m_pData (NULL)
{
}
// copy constructor: full copy implementation
template <typename U, typename B>
TFixedArray (const TFixedArray<U,B>& that):
m_nSize (that.size()),
m_Allocator(that.m_Allocator)
{
m_pData = that.empty() ? NULL : m_Allocator.allocate_construct(that.size());
for (size_t i = 0; i < m_nSize; ++i)
m_pData[i] = that[i];
}
// copy constructor: full copy implementation
TFixedArray (const TFixedArray<T,A>& that):
m_nSize (that.size()),
m_Allocator(that.m_Allocator)
{
m_pData = that.empty() ? NULL : m_Allocator.allocate_construct(that.size());
for (size_t i = 0; i < m_nSize; ++i)
m_pData[i] = that[i];
}
// copy constructor: full copy implementation
template <typename U, typename B>
TFixedArray (const char* szParentObject, int nParentIndex, const TFixedArray<U,B>& that):
m_nSize (that.size()),
m_Allocator(szParentObject, nParentIndex)
{
m_pData = that.empty() ? NULL : m_Allocator.allocate_construct(that.size());
for (size_t i = 0; i < m_nSize; ++i)
m_pData[i] = that[i];
}
// copy constructor: full copy implementation
TFixedArray (const char* szParentObject, int nParentIndex, const TFixedArray<T,A>& that):
m_nSize (that.size()),
m_Allocator(szParentObject, nParentIndex)
{
m_pData = that.empty() ? NULL : m_Allocator.allocate_construct(that.size());
for (size_t i = 0; i < m_nSize; ++i)
m_pData[i] = that[i];
}
// sets the owner on behalf of whom the memory will be allocated
void dbgAssignOwner (const char* szParentObject, int nParentIndex = 0)
{
m_Allocator = A (szParentObject, nParentIndex);
}
template <typename U,typename B>
TFixedArray<T,A>& operator = (const TFixedArray<U,B>& src)
{
reinit (src.size());
for (size_t i = 0; i < m_nSize; ++i)
m_pData[i] = src[i];
return *this;
}
TFixedArray<T,A>& operator = (const TFixedArray<T,A>& src)
{
reinit (src.size());
for (size_t i = 0; i < m_nSize; ++i)
m_pData[i] = src[i];
return *this;
}
~TFixedArray ()
{
m_Allocator.deallocate_destroy(m_pData);
#if defined(_DEBUG)
m_pData = (T*)0x32323232;
m_nSize = 0x43434343;
#endif
}
// returns the size of the array (the number of elements)
size_t size() const
{
return m_nSize;
}
bool empty() const
{
return m_nSize == 0;
}
// resizes the array, doesn't preserve the contents
void reinit (size_t numElements)
{
clear();
if (numElements > 0)
m_pData = m_Allocator.allocate_construct (m_nSize = numElements);
}
// resizes the array, doesn't preserve the contents, copies the given value to all the elements
void reinit (size_t numElements, const T& element)
{
reinit(numElements);
for (size_t i = 0; i < numElements; ++i)
m_pData[i] = element;
}
// swaps this array with the given one
void swap (TFixedArray<T,A>& rRight)
{
TSimpleSwap (m_pData, rRight.m_pData);
TSimpleSwap (m_nSize, rRight.m_nSize);
}
// resizes the array, preserves the contents
void resize (size_t numElements)
{
if (numElements != m_nSize)
{
if (numElements == 0)
{
clear();
}
else
if (m_nSize == 0)
{
reinit (numElements);
}
else
if (m_nSize != numElements)
{
T* pDataNew = m_Allocator.allocate_construct (numElements);
size_t numToCopy = numElements < m_nSize?numElements:m_nSize;
for (size_t i = 0; i < numToCopy; ++i)
pDataNew[i] = m_pData[i];
m_Allocator.deallocate_destroy(m_pData);
m_pData = pDataNew;
m_nSize = numElements;
}
}
}
// resizes the array, preserves the contents, copies the given value to the appended elements
void resize (size_t numElements, const T& sample)
{
size_t i = m_nSize; // remember the old size
resize (numElements);
// initialize the tail of the array
for (;i < numElements;++i)
m_pData[i] = sample;
}
// cleans up the array
void clear()
{
m_Allocator.deallocate_destroy(m_pData);
m_pData = NULL;
m_nSize = 0;
}
// adds the element to the end of the array
void push_back (const T& sample)
{
// add one element, and copy the sample to it
resize (size() + 1, sample);
}
// removes the element from the end of the array
void pop_back()
{
if (size() > 0)
resize (size()-1);
else
assert (0);
}
// inserts the element in place of the given iterator
void insert (iterator it, const T& element)
{
assert (it >= begin() && it <= end());
insert (it - begin(), element);
}
// inserts the element into the given place in the array\
// (inserts before the given element)
void insert (size_t nIndex, const T& element)
{
assert (nIndex <= size());
T* pData = m_Allocator.allocate_construct (size() + 1);
size_t i;
// copy the pre-array
for (i = 0; i < nIndex; ++i)
pData[i] = m_pData[i];
assert (i = nIndex);
pData[i] = element;
// copy the post-array
for (; i < m_nSize;++i)
pData[i+1] = m_pData[i];
// swap the new and old arrays
m_Allocator.deallocate_destroy(m_pData);
m_pData = pData;
++m_nSize;
}
// erases an element at the given position
void erase (size_t nIndex)
{
if (nIndex < size())
{
if (size() > 1)
{
T* pData = m_Allocator.allocate_construct(size() - 1);
size_t i;
// copy the pre-array
for (i = 0; i < nIndex; ++i)
pData[i] = m_pData[i];
assert (i = nIndex);
// copy the post-array
for (; i < m_nSize-1;++i)
pData[i] = m_pData[i+1];
// swap the new and old arrays
m_Allocator.deallocate_destroy(m_pData);
m_pData = pData;
--m_nSize;
}
else
clear();
}
}
// returns the last element of the array
reference back()
{
assert (!empty());
return m_pData[m_nSize-1];
}
// returns the last element of the array
const_reference back() const
{
assert (!empty());
return m_pData[m_nSize-1];
}
// returns the first element of the array
reference front()
{
assert (!empty());
return m_pData[0];
}
// returns the first element of the array
const_reference front() const
{
assert (!empty());
return m_pData[0];
}
// returns the begin iterator of the contained sequence
iterator begin()
{
return m_pData;
}
// returns the begin iterator of the contained sequence
const_iterator begin() const
{
return m_pData;
}
// returns the end iterator of the contained sequence
iterator end()
{
return m_pData+m_nSize;
}
// returns the end iterator of the contained sequence
const_iterator end() const
{
return m_pData+m_nSize;
}
reference operator [] (size_t i)
{
// the i==0 is left here to allow extract the pointer to the 0th element
// even when the array is empty. In case index is not 0, it must be < size
assert(i == 0 || i < m_nSize);
return m_pData[i];
}
const_reference operator [] (size_t i) const
{
// the i==0 is left here to allow extract the pointer to the 0th element
// even when the array is empty. In case index is not 0, it must be < size
assert(i == 0 || i < m_nSize);
return m_pData[i];
}
protected:
// array of elements
T* m_pData;
// number of elements in the array m_pData
size_t m_nSize;
A m_Allocator;
};
//////////////////////////////////////////////////////////////////////////
// A class with STL naming conventions
// Damped implementation of a resizable array: once the capacity is increased,
// it never decreases during normal resizing operations.
// The hidden fixed array is used as the storage, and the outer represnetation
// of the array size is kept in a separate variable
template <typename T, class A = TSimpleAllocator<T> > class TGrowArray : protected TFixedArray<T,A>
{
public:
typedef typename TFixedArray<T,A>::iterator iterator;
typedef typename TFixedArray<T,A>::const_iterator const_iterator;
typedef typename TFixedArray<T,A>::reference reference;
typedef typename TFixedArray<T,A>::const_reference const_reference;
TGrowArray ():
TFixedArray<T,A> ("TGrowArray()", 1),
m_nSize(0)
{
}
TGrowArray (const char* szParentObject, int nParentIndex = 0):
TFixedArray<T,A> (szParentObject, nParentIndex),
m_nSize (0)
{
}
TGrowArray (int numElements):
TFixedArray<T,A>(numElements, "TGrowArray(n)",2),
m_nSize (numElements)
{
}
// copy constructor: full copy implementation
// reserves enough data to contain the whole array
template <typename U, typename B>
TGrowArray (const TGrowArray<U,B>& that):
TFixedArray<T,A>(that),
m_nSize (that.size())
{
}
// copy constructor: full copy implementation
TGrowArray (const TGrowArray<T,A>& that):
TFixedArray<T,A>(that),
m_nSize (that.size())
{
}
// copy constructor: full copy implementation
// reserves enough data to contain the whole array
template <typename U, typename B>
TGrowArray (const char* szParentObject, int nParentIndex, const TGrowArray<U,B>& that):
TFixedArray<T,A>(szParentObject, nParentIndex, that),
m_nSize (that.size())
{
}
// copy constructor: full copy implementation
TGrowArray (const char* szParentObject, int nParentIndex, const TGrowArray<T,A>& that):
TFixedArray<T,A>(szParentObject, nParentIndex, that),
m_nSize (that.size())
{
}
template <typename U, typename B>
TGrowArray<T,A>& operator = (const TGrowArray<U,B>& src)
{
reinit (src.size());
for (size_t i = 0; i < m_nSize; ++i)
this->m_pData[i] = src[i];
return *this;
}
TGrowArray<T,A>& operator = (const TGrowArray<T,A>& src)
{
reinit (src.size());
for (size_t i = 0; i < m_nSize; ++i)
this->m_pData[i] = src[i];
return *this;
}
~TGrowArray ()
{
#if defined(_DEBUG) && defined(_INC_CRTDBG)
m_nSize = 0x42434243;
#endif
}
// returns the size of the array (the number of elements)
size_t size() const
{
return m_nSize;
}
bool empty() const
{
return m_nSize == 0;
}
size_t capacity ()const
{
return TFixedArray<T,A>::size();
}
// this is the algorithm that dampers element reallocations.
// given the minimum required number of elements, it returns
// some number >= the given so that the next reallocation will not occur immediately
// after reaching that number
static inline size_t dampNumElements(size_t numElements)
{
return numElements + (numElements >> 1);
}
// resizes the array, doesn't preserve the contents
void reinit (size_t numElements)
{
if (capacity() < numElements)
{
TFixedArray<T,A>::reinit (dampNumElements(numElements));
}
m_nSize = numElements;
}
// resizes the array, doesn't preserve the contents, copies the given value to all the elements
void reinit (size_t numElements, const T& element)
{
reinit(numElements);
for (size_t i = 0; i < numElements; ++i)
this->m_pData[i] = element;
}
// swaps this array with the given one
void swap (TGrowArray<T,A>& rRight)
{
TFixedArray<T,A>::swap (rRight);
TSimpleSwap (m_nSize, rRight.m_nSize);
}
// resizes the array, preserves the contents
void resize (size_t numElements)
{
reserve (numElements);
m_nSize = numElements;
}
// resizes the array, preserves the contents, copies the given value to the appended elements
void resize (size_t numElements, const T& sample)
{
size_t i = m_nSize; // remember the old size
resize (numElements);
// initialize the tail of the array
for (;i < numElements;++i)
this->m_pData[i] = sample;
}
// adds the element to the end of the array
void push_back (const T& sample)
{
// add one element, and copy the sample to it
resize (size() + 1, sample);
}
// deletes the element from the end of the array;
// doesn't actually destruct the element as it can be reused
void pop_back()
{
if (m_nSize > 0)
--m_nSize;
else
assert (0);
}
// inserts the element in place of the given iterator
void insert (iterator it, const T& element)
{
assert (it >= begin() && it <= end());
insert (it - begin(), element);
}
// inserts the element into the given place in the array\
// (inserts before the given element)
void insert (size_t nIndex, const T& element)
{
assert (nIndex <= size());
resize (size() + 1);
for (size_t i = size()-1; i > nIndex; --i)
(*this)[i] = (*this)[i-1];
(*this)[nIndex] = element;
}
// erases an element at the given position
void erase (iterator it)
{
assert (it >= begin() && it < end());
erase (it - begin());
}
// erases an element at the given position
void erase (size_t nIndex)
{
assert (nIndex < size());
for (size_t i = nIndex; i < size()-1; ++i)
(*this)[i] = (*this)[i+1];
resize (size()-1);
}
// makes sure capacity() returns at least the given value
// keeps the current contents
void reserve (size_t numElements)
{
if (numElements > capacity())
{
if (m_nSize)
TFixedArray<T,A>::resize (numElements);
else
TFixedArray<T,A>::reinit (numElements);
}
}
// makes sure capacity() returns the same value as size()
void shrink ()
{
assert (capacity() >= size());
if (size() == 0)
{
// clear the reserve
TFixedArray<T,A>::clear();
}
else
{
// make the reserve exactly the size of this array
TFixedArray<T,A>::resize(size());
}
}
// cleans up the array
void clear()
{
m_nSize = 0;
}
// reserves at least the given number of elements and resets (clears) the array
void reset (size_t numElements)
{
clear();
if (numElements > capacity())
TFixedArray<T,A>::reinit (numElements);
}
// returns the last element of the array
reference back()
{
assert (!empty());
return this->m_pData[m_nSize-1];
}
// returns the last element of the array
const_reference back() const
{
assert (!empty());
return this->m_pData[m_nSize-1];
}
// returns the first element of the array
reference front()
{
assert (!empty());
return this->m_pData[0];
}
// returns the first element of the array
const_reference front() const
{
assert (!empty());
return this->m_pData[0];
}
// returns the begin iterator of the contained sequence
iterator begin()
{
return this->m_pData;
}
// returns the begin iterator of the contained sequence
const_iterator begin() const
{
return this->m_pData;
}
// returns the end iterator of the contained sequence
iterator end()
{
return this->m_pData+m_nSize;
}
// returns the end iterator of the contained sequence
const_iterator end() const
{
return this->m_pData+m_nSize;
}
reference operator [] (size_t i)
{
if(this->m_pData)
assert(i >= 0 && i < m_nSize);
else
assert(i==0);
return this->m_pData[i];
}
const T& operator [] (size_t i) const
{
if(this->m_pData)
assert(i >= 0 && i < m_nSize);
else
assert(i==0);
return this->m_pData[i];
}
protected:
// number of elements - as it's seen from the outside
size_t m_nSize;
};
// the follwoing definitions are for auto-declaring getters/setters for
// T*Array members
// USAGE:
// if you have an array std::vector<CryVertex> m_arrMyArray,
// then Type is CryVertex, Singular is probably Vertex, Plural is probably Vertices, member is m_arrMyArray
#define DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(Type, Singular, Plural, member) \
Type* get##Plural() {return member.begin();} \
const Type* get##Plural()const {return member.begin();} \
Type& get##Singular(unsigned i) {assert (i < num##Plural()); return member[i];} \
const Type& get##Singular(unsigned i)const {assert (i < num##Plural()); return member[i];}
#define DECLARE_PLAIN_ARRAY_GETTER_METHODS(Type, Singular, Plural, member) \
Type* get##Plural() {return member;} \
const Type* get##Plural()const {return member;} \
Type& get##Singular(unsigned i) {assert (i < num##Plural()); return member[i];} \
const Type& get##Singular(unsigned i)const {assert (i < num##Plural()); return member[i];} \
void set##Singular (unsigned i, const Type& newValue) {assert (i < num##Plural()); member[i] = newValue;}
#define DECLARE_ARRAY_GETTER_METHODS(Type, Singular, Plural, member) \
DECLARE_ELEMENTARY_ARRAY_GETTER_METHODS(Type, Singular, Plural, member) \
unsigned num##Plural() const{return (unsigned)member.size();}
#endif