Files
2022-10-26 12:25:11 +08:00

852 lines
20 KiB
C++

#ifndef N_ARRAY_H
#define N_ARRAY_H
//------------------------------------------------------------------------------
/**
@brief A dynamic array template class, similar to the stl vector class
@author
- RadonLabs GmbH
@since
- 2005.6.14
@remarks
- Áö¿Ï Ãß°¡
*/
//------------------------------------------------------------------------------
#include "../ProgramCommon/Macro.h"
#include "../ProgramCommon/Define.h"
//------------------------------------------------------------------------------
template<class TYPE> class nArray
{
public:
typedef TYPE* iterator;
/// behaviour flags
enum
{
DoubleGrowSize = (1<<0), // when set, grow size doubles each turn
};
/// constructor with default parameters
nArray();
/// constuctor with initial size and grow size
nArray(int initialSize, int initialGrow);
/// constructor with initial size, grow size and initial values
nArray(int initialSize, int initialGrow, TYPE initialValue);
/// copy constructor
nArray(const nArray<TYPE>& rhs);
/// destructor
~nArray();
/// assignment operator
nArray<TYPE>& operator=(const nArray<TYPE>& rhs);
/// [] operator
TYPE& operator[](int index) const;
/// set behaviour flags
void SetFlags(int f);
/// get behaviour flags
int GetFlags() const;
/// clear contents and set a fixed size
void SetFixedSize(int size);
/// push element to back of array
TYPE& PushBack(const TYPE& elm);
/// append element to array (synonym for PushBack())
void Append(const TYPE& elm);
/// reserve 'num' elements at end of array and return pointer to first element
iterator Reserve(int num);
/// get number of elements in array
int Size() const;
/// get overall allocated size of array in number of elements
int AllocSize() const;
/// set element at index, grow array if necessary
TYPE& Set(int index, const TYPE& elm);
/// return reference to nth element in array
TYPE& At(int index);
/// return reference to first element
TYPE& Front() const;
/// return reference to last element
TYPE& Back() const;
/// return true if array empty
bool Empty() const;
/// erase element at index
void Erase(int index);
/// quick erase, does not call operator= or destructor
void EraseQuick(int index);
/// erase element pointed to by iterator
iterator Erase(iterator iter);
/// quick erase, does not call operator= or destructor
iterator EraseQuick(iterator iter);
/// insert element at index
void Insert(int index, const TYPE& elm);
/// clear array (calls destructors)
void Clear();
/// reset array (does NOT call destructors)
void Reset();
/// return iterator to beginning of array
iterator Begin() const;
/// return iterator to end of array
iterator End() const;
/// find identical element in array, return iterator
iterator Find(const TYPE& elm) const;
/// find identical element in array, return index
int FindIndex(const TYPE& elm) const;
/// find array range with element
void Fill(int first, int num, const TYPE& elm);
/// clear contents and preallocate with new attributes
void Reallocate(int initialSize, int grow);
private:
/// check if index is in valid range, and grow array if necessary
void CheckIndex(int);
/// destroy an element (call destructor without freeing memory)
void Destroy(TYPE* elm);
/// copy content
void Copy(const nArray<TYPE>& src);
/// delete content
void Delete();
/// grow array
void Grow();
/// grow array to target size
void GrowTo(int newAllocSize);
/// move elements, grows array if needed
void Move(int fromIndex, int toIndex);
/// unsafe quick move, does not call operator= or destructor
void MoveQuick(int fromIndex, int toIndex);
int growSize; // grow by this number of elements if array exhausted
int allocSize; // number of elements allocated
int numElements; // number of elements in array
int flags;
TYPE* elements; // pointer to element array
};
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
nArray<TYPE>::nArray() :
growSize(16),
allocSize(0),
numElements(0),
flags(0)
{
this->elements = 0;
}
//------------------------------------------------------------------------------
/**
Note: 'grow' can be zero to create a static preallocated array.
*/
template<class TYPE>
nArray<TYPE>::nArray(int initialSize, int grow) :
growSize(grow),
allocSize(initialSize),
numElements(0),
flags(0)
{
ASSERT(initialSize >= 0);
if (initialSize > 0)
{
this->elements = new TYPE[this->allocSize];
}
else
{
this->elements = 0;
}
}
//------------------------------------------------------------------------------
/**
Note: 'grow' can be zero to create a static preallocated array.
*/
template<class TYPE>
nArray<TYPE>::nArray(int initialSize, int grow, TYPE initialValue) :
growSize(grow),
allocSize(initialSize),
numElements(initialSize),
flags(0)
{
ASSERT(initialSize >= 0);
if (initialSize > 0)
{
this->elements = new TYPE[this->allocSize];
int i;
for (i = 0; i < initialSize; i++)
{
this->elements[i] = initialValue;
}
}
else
{
this->elements = 0;
}
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Copy(const nArray<TYPE>& src)
{
ASSERT(0 == this->elements);
this->growSize = src.growSize;
this->allocSize = src.allocSize;
this->numElements = src.numElements;
this->flags = src.flags;
if (this->allocSize > 0)
{
this->elements = new TYPE[this->allocSize];
int i;
for (i = 0; i < this->numElements; i++)
{
this->elements[i] = src.elements[i];
}
}
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Delete()
{
this->growSize = 0;
this->allocSize = 0;
this->numElements = 0;
this->flags = 0;
if (this->elements)
{
delete[] this->elements;
this->elements = 0;
}
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Destroy(TYPE* elm)
{
elm->~TYPE();
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
nArray<TYPE>::nArray(const nArray<TYPE>& rhs) :
growSize(0),
allocSize(0),
numElements(0),
elements(0),
flags(0)
{
this->Copy(rhs);
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
nArray<TYPE>::~nArray()
{
this->Delete();
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::SetFlags(int f)
{
this->flags = f;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
int
nArray<TYPE>::GetFlags() const
{
return this->flags;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Reallocate(int initialSize, int grow)
{
this->Delete();
this->growSize = grow;
this->allocSize = initialSize;
this->numElements = 0;
if (initialSize > 0)
{
this->elements = new TYPE[initialSize];
}
else
{
this->elements = 0;
}
}
//------------------------------------------------------------------------------
/**
Set a new fixed size. This will throw away the current content, and
create preallocate a new array which cannot grow. All elements in
the array will be valid.
*/
template<class TYPE>
void
nArray<TYPE>::SetFixedSize(int size)
{
this->Reallocate(size, 0);
this->numElements = size;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
nArray<TYPE>&
nArray<TYPE>::operator=(const nArray<TYPE>& rhs)
{
this->Delete();
this->Copy(rhs);
return *this;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::GrowTo(int newAllocSize)
{
TYPE* newArray = new TYPE[newAllocSize];
if (this->elements)
{
// copy over contents
int i;
for (i = 0; i < this->numElements; i++)
{
newArray[i] = this->elements[i];
}
// discard old array and update contents
delete[] this->elements;
}
this->elements = newArray;
this->allocSize = newAllocSize;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Grow()
{
ASSERT(this->growSize > 0);
int growToSize;
if ((DoubleGrowSize & this->flags) != 0)
{
// double growth behaviour
if (0 == this->allocSize)
{
growToSize = growSize;
}
else
{
growToSize = 2 * this->allocSize;
}
}
else
{
// classic linear growth behaviour
growToSize = this->allocSize + this->growSize;
}
this->GrowTo(growToSize);
}
//------------------------------------------------------------------------------
/**
30-Jan-03 floh serious bugfixes!
*/
template<class TYPE>
void
nArray<TYPE>::Move(int fromIndex, int toIndex)
{
ASSERT(this->elements);
ASSERT(fromIndex < this->numElements);
// nothing to move?
if (fromIndex == toIndex)
{
return;
}
// compute number of elements to move
int num = this->numElements - fromIndex;
// check if array needs to grow
int neededSize = toIndex + num;
while (neededSize >= this->allocSize)
{
this->Grow();
}
if (fromIndex > toIndex)
{
// this is a backward move
int i;
for (i = 0; i < num; i++)
{
this->elements[toIndex + i] = this->elements[fromIndex + i];
}
// destroy remaining elements
for (i = (fromIndex + i) - 1; i < this->numElements; i++)
{
this->Destroy(&(this->elements[i]));
}
}
else
{
// this is a forward move
int i;
for (i = num - 1; i >= 0; --i)
{
this->elements[toIndex + i] = this->elements[fromIndex + i];
}
// destroy freed elements
for (i = fromIndex; i < toIndex; i++)
{
this->Destroy(&(this->elements[i]));
}
}
// adjust array size
this->numElements = toIndex + num;
}
//------------------------------------------------------------------------------
/**
Very fast move which does not call assignment operators or destructors,
so you better know what you do!
*/
template<class TYPE>
void
nArray<TYPE>::MoveQuick(int fromIndex, int toIndex)
{
ASSERT(this->elements);
ASSERT(fromIndex < this->numElements);
// compute number of elements to move
int num = this->numElements - fromIndex;
// nothing to move?
if (fromIndex == toIndex)
{
return;
}
// do a direct memory move
memmove(&(this->elements[toIndex]), &(this->elements[fromIndex]), num * sizeof(TYPE));
// adjust array size
this->numElements = toIndex + num;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
TYPE&
nArray<TYPE>::PushBack(const TYPE& elm)
{
// grow allocated space if exhausted
if (this->numElements == this->allocSize)
{
this->Grow();
}
ASSERT(this->elements);
this->elements[this->numElements] = elm;
return this->elements[this->numElements++];
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Append(const TYPE& elm)
{
// grow allocated space if exhausted
if (this->numElements == this->allocSize)
{
this->Grow();
}
ASSERT(this->elements);
this->elements[this->numElements++] = elm;
}
//------------------------------------------------------------------------------
/**
Make room for N new elements at the end of the array, and return a pointer
to the start of the reserved area. This can be (carefully!) used as a fast
shortcut to fill the array directly with data.
*/
template<class TYPE>
typename nArray<TYPE>::iterator
nArray<TYPE>::Reserve(int num)
{
ASSERT(num > 0);
int maxElement = this->numElements + num;
while (maxElement >= this->allocSize)
{
this->Grow();
}
ASSERT(this->elements);
iterator iter = this->elements + this->numElements;
this->numElements += num;
return iter;
}
//------------------------------------------------------------------------------
/**
This will check if the provided index is in the valid range. If it is
not the array will be grown to that index.
*/
template<class TYPE>
void
nArray<TYPE>::CheckIndex(int index)
{
if (index >= this->numElements)
{
// grow array if necessary
if (index >= this->allocSize)
{
ASSERT(this->growSize > 0);
this->GrowTo(index + this->growSize);
}
// update number of contained elements
this->numElements = index + 1;
}
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
TYPE&
nArray<TYPE>::Set(int index, const TYPE& elm)
{
ASSERT(index < this->numElements);
this->CheckIndex(index);
this->elements[index] = elm;
return this->elements[index];
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
int
nArray<TYPE>::Size() const
{
return this->numElements;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
int
nArray<TYPE>::AllocSize() const
{
return this->allocSize;
}
//------------------------------------------------------------------------------
/**
Access an element. This method may grow the array if the index is
outside the array range.
*/
template<class TYPE>
TYPE&
nArray<TYPE>::At(int index)
{
this->CheckIndex(index);
return this->elements[index];
}
//------------------------------------------------------------------------------
/**
Access an element. This method will NOT grow the array, and instead do
a range check, which may throw an assertion.
*/
template<class TYPE>
TYPE&
nArray<TYPE>::operator[](int index) const
{
ASSERT(this->elements && (index >= 0) && (index < this->numElements));
return this->elements[index];
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
TYPE&
nArray<TYPE>::Front() const
{
ASSERT(this->elements && (this->numElements > 0));
return this->elements[0];
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
TYPE&
nArray<TYPE>::Back() const
{
ASSERT(this->elements && (this->numElements > 0));
return this->elements[this->numElements - 1];
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
bool
nArray<TYPE>::Empty() const
{
return (this->numElements == 0);
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Erase(int index)
{
ASSERT(this->elements && (index >= 0) && (index < this->numElements));
if (index == (this->numElements - 1))
{
// special case: last element
this->Destroy(&(this->elements[index]));
this->numElements--;
}
else
{
this->Move(index + 1, index);
}
}
//------------------------------------------------------------------------------
/**
Quick erase, uses memmove() and does not call assignment operators
or destructor, so be careful about that!
*/
template<class TYPE>
void
nArray<TYPE>::EraseQuick(int index)
{
ASSERT(this->elements && (index >= 0) && (index < this->numElements));
if (index == (this->numElements - 1))
{
// special case: last element
this->numElements--;
}
else
{
this->MoveQuick(index + 1, index);
}
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
typename nArray<TYPE>::iterator
nArray<TYPE>::Erase(typename nArray<TYPE>::iterator iter)
{
ASSERT(this->elements && (iter >= this->elements) && (iter < (this->elements + this->numElements)));
this->Erase(iter - this->elements);
return iter;
}
//------------------------------------------------------------------------------
/**
Quick erase, uses memmove() and does not call assignment operators
or destructor, so be careful about that!
*/
template<class TYPE>
typename nArray<TYPE>::iterator
nArray<TYPE>::EraseQuick(typename nArray<TYPE>::iterator iter)
{
ASSERT(this->elements && (iter >= this->elements) && (iter < (this->elements + this->numElements)));
this->EraseQuick(iter - this->elements);
return iter;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
void
nArray<TYPE>::Insert(int index, const TYPE& elm)
{
ASSERT((index >= 0) && (index <= this->numElements));
if (index == this->numElements)
{
// special case: append element to back
this->PushBack(elm);
}
else
{
this->Move(index, index + 1);
this->elements[index] = elm;
}
}
//------------------------------------------------------------------------------
/**
The current implementation of this method does not shrink the
preallocated space. It simply sets the array size to 0.
*/
template<class TYPE>
void
nArray<TYPE>::Clear()
{
int i;
for (i = 0; i < this->numElements; i++)
{
this->Destroy(&(this->elements[i]));
}
this->numElements = 0;
}
//------------------------------------------------------------------------------
/**
This is identical with Clear(), but does NOT call destructors (it just
resets the numElements member. USE WITH CARE!
*/
template<class TYPE>
void
nArray<TYPE>::Reset()
{
this->numElements = 0;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
typename nArray<TYPE>::iterator
nArray<TYPE>::Begin() const
{
return this->elements;
}
//------------------------------------------------------------------------------
/**
*/
template<class TYPE>
typename nArray<TYPE>::iterator
nArray<TYPE>::End() const
{
return this->elements + this->numElements;
}
//------------------------------------------------------------------------------
/**
Find element in array, return iterator, or 0 if element not
found.
@param elm element to find
@return element iterator, or 0 if not found
*/
template<class TYPE>
typename nArray<TYPE>::iterator
nArray<TYPE>::Find(const TYPE& elm) const
{
int index;
for (index = 0; index < this->numElements; index++)
{
if (this->elements[index] == elm)
{
return &(this->elements[index]);
}
}
return 0;
}
//------------------------------------------------------------------------------
/**
Find element in array, return element index, or -1 if element not
found.
@param elm element to find
@return index to element, or -1 if not found
*/
template<class TYPE>
int
nArray<TYPE>::FindIndex(const TYPE& elm) const
{
int index;
for (index = 0; index < this->numElements; index++)
{
if (this->elements[index] == elm)
{
return index;
}
}
return -1;
}
//------------------------------------------------------------------------------
/**
Fills an array range with the given element value. Will grow the
array if necessary
@param first index of first element to start fill
@param num num elements to fill
@param elm fill value
*/
template<class TYPE>
void
nArray<TYPE>::Fill(int first, int num, const TYPE& elm)
{
if ((first + num) > this->numElements)
{
this->GrowTo(first + num);
}
int i;
for (i = first; i < (first + num); i++)
{
this->elements[i] = elm;
}
}
//------------------------------------------------------------------------------
#endif