#ifndef _CRY_COMMON_TARRAYS_HDR_ #define _CRY_COMMON_TARRAYS_HDR_ #include "platform.h" #if !defined(LINUX) #include #endif #include "TAlloc.h" template 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 > 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& that): m_pData (NULL) { assert(0); } // assignment is impossible because the size is unknown TElementaryArray& operator = (const TElementaryArray& 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& 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 > 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 TFixedArray (const TFixedArray& 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& 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 TFixedArray (const char* szParentObject, int nParentIndex, const TFixedArray& 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& 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 TFixedArray& operator = (const TFixedArray& src) { reinit (src.size()); for (size_t i = 0; i < m_nSize; ++i) m_pData[i] = src[i]; return *this; } TFixedArray& operator = (const TFixedArray& 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& 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 > class TGrowArray : protected TFixedArray { public: typedef typename TFixedArray::iterator iterator; typedef typename TFixedArray::const_iterator const_iterator; typedef typename TFixedArray::reference reference; typedef typename TFixedArray::const_reference const_reference; TGrowArray (): TFixedArray ("TGrowArray()", 1), m_nSize(0) { } TGrowArray (const char* szParentObject, int nParentIndex = 0): TFixedArray (szParentObject, nParentIndex), m_nSize (0) { } TGrowArray (int numElements): TFixedArray(numElements, "TGrowArray(n)",2), m_nSize (numElements) { } // copy constructor: full copy implementation // reserves enough data to contain the whole array template TGrowArray (const TGrowArray& that): TFixedArray(that), m_nSize (that.size()) { } // copy constructor: full copy implementation TGrowArray (const TGrowArray& that): TFixedArray(that), m_nSize (that.size()) { } // copy constructor: full copy implementation // reserves enough data to contain the whole array template TGrowArray (const char* szParentObject, int nParentIndex, const TGrowArray& that): TFixedArray(szParentObject, nParentIndex, that), m_nSize (that.size()) { } // copy constructor: full copy implementation TGrowArray (const char* szParentObject, int nParentIndex, const TGrowArray& that): TFixedArray(szParentObject, nParentIndex, that), m_nSize (that.size()) { } template TGrowArray& operator = (const TGrowArray& src) { reinit (src.size()); for (size_t i = 0; i < m_nSize; ++i) this->m_pData[i] = src[i]; return *this; } TGrowArray& operator = (const TGrowArray& 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::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::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& rRight) { TFixedArray::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::resize (numElements); else TFixedArray::reinit (numElements); } } // makes sure capacity() returns the same value as size() void shrink () { assert (capacity() >= size()); if (size() == 0) { // clear the reserve TFixedArray::clear(); } else { // make the reserve exactly the size of this array TFixedArray::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::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 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