/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */ #ifndef SE_INCL_DYNAMICARRAY_CPP #define SE_INCL_DYNAMICARRAY_CPP #ifdef PRAGMA_ONCE #pragma once #endif #include <stddef.h> #include <Engine/Base/Memory.h> #include <Engine/Base/ListIterator.inl> #include <Engine/Templates/DynamicArray.h> // iterate whole dynamic array /* NOTE: The iterator defined by this macro must be destroyed before adding/removing * elements in the array. To do so, embed the for loop in additional curly braces. */ #define FOREACHINDYNAMICARRAY(array, type, iter) \ for(CDynamicArrayIterator<type> iter(array); !iter.IsPastEnd(); iter.MoveToNext() ) class CDABlockInfo { public: CListNode bi_ListNode; void *bi_Memory; }; /* * Default constructor. */ template<class Type> CDynamicArray<Type>::CDynamicArray(void) { #if CHECKARRAYLOCKING // not locked da_LockCt = 0; #endif // set to empty array of pointers da_Pointers = NULL; da_Count = 0; } /* * Copy constructor. */ template<class Type> CDynamicArray<Type>::CDynamicArray(CDynamicArray<Type> &daOriginal) { #if CHECKARRAYLOCKING // not locked da_LockCt = 0; #endif // set to empty array of pointers da_Pointers = NULL; da_Count = 0; // call assignment operator (*this) = daOriginal; } /* * Destructor -- frees all memory. */ template<class Type> CDynamicArray<Type>::~CDynamicArray(void) { Clear(); } /* * Destroy all objects, and reset the array to initial (empty) state. */ template<class Type> void CDynamicArray<Type>::Clear(void) { ASSERT(this!=NULL); // if any pointers are allocated if (da_Count!=0) { /* NOTE: We must explicitly clear objects here, because array deleting * does not call object destructors! */ // for all pointers for (INDEX iPointer=0; iPointer<da_Count; iPointer++) { // destroy the object that it points to ::Clear(*da_Pointers[iPointer]); } // free the pointers FreeMemory(da_Pointers); // mark as freed da_Pointers = NULL; da_Count = 0; // otherwise } else { // check that the pointers are really not allocated ASSERT(da_Pointers==NULL); // nothing to free } // for all memory blocks FORDELETELIST(CDABlockInfo, bi_ListNode, da_BlocksList, itBlock) { // free memory used by block (this doesn't call destructors - see note above!) delete[] (Type *)itBlock->bi_Memory; // free memory used by block info delete &itBlock.Current(); } } /* * Grow pointer array by a given number of members. */ template<class Type> void CDynamicArray<Type>::GrowPointers(INDEX iCount) { ASSERT(this!=NULL && iCount>0); // if not yet allocated if (da_Count==0) { // check that the pointers are really not allocated ASSERT(da_Pointers==NULL); // allocate da_Count=iCount; da_Pointers = (Type **)AllocMemory(da_Count*sizeof(Type*)); // if allocated } else { // grow to new size da_Count+=iCount; GrowMemory((void **)&da_Pointers, da_Count*sizeof(Type*)); } } /* * Shrink pointer array by a given number of members. */ template<class Type> void CDynamicArray<Type>::ShrinkPointers(INDEX iCount) { ASSERT(this!=NULL && iCount>0); // check that the pointers are allocated ASSERT(da_Pointers!=NULL); // decrement count da_Count-=iCount; // checked that it has not dropped below zero ASSERT(da_Count>=0); // if all pointers are freed by this if (da_Count==0) { // free the array FreeMemory(da_Pointers); da_Pointers = NULL; // if some remain } else { // shrink to new size ShrinkMemory((void **)&da_Pointers, da_Count*sizeof(Type*)); } } /* * Allocate a new memory block. */ template<class Type> Type *CDynamicArray<Type>::AllocBlock(INDEX iCount) { ASSERT(this!=NULL && iCount>0); Type *ptBlock; CDABlockInfo *pbi; // allocate the memory and call constructors for all members (+1 for cache-prefetch opt) ptBlock = new Type[iCount+1]; // call vector constructor, for better performance // allocate the block info pbi = new CDABlockInfo; // add the block to list da_BlocksList.AddTail(pbi->bi_ListNode); // remember block memory pbi->bi_Memory = ptBlock; return ptBlock; } /* * Create a given number of new members. */ template<class Type> Type *CDynamicArray<Type>::New(INDEX iCount /*= 1*/) { ASSERT(this!=NULL && iCount>=0); // if no new members are needed in fact if (iCount==0) { // do nothing return NULL; } Type *ptBlock; INDEX iOldCount = da_Count; // grow the pointer table GrowPointers(iCount); // allocate the memory block ptBlock = AllocBlock(iCount); // set pointers for(INDEX iNewMember=0; iNewMember<iCount; iNewMember++) { da_Pointers[iOldCount+iNewMember] = ptBlock+iNewMember; } return ptBlock; } /* * Delete a given member. */ template<class Type> void CDynamicArray<Type>::Delete(Type *ptMember) { ASSERT(this!=NULL); #if CHECKARRAYLOCKING // check that not locked for indices ASSERT(da_LockCt == 0); #endif // clear the object ::Clear(*ptMember); INDEX iMember=GetIndex(ptMember); // move last pointer here da_Pointers[iMember]=da_Pointers[da_Count-1]; // shrink pointers by one ShrinkPointers(1); // do nothing to free memory //!!!! } /* * Get pointer to a member from it's index. */ template<class Type> Type *CDynamicArray<Type>::Pointer(INDEX iMember) { ASSERT(this!=NULL); // check that index is currently valid ASSERT(iMember>=0 && iMember<da_Count); #if CHECKARRAYLOCKING // check that locked for indices ASSERT(da_LockCt>0); #endif return da_Pointers[iMember]; } template<class Type> const Type *CDynamicArray<Type>::Pointer(INDEX iMember) const { ASSERT(this!=NULL); // check that index is currently valid ASSERT(iMember>=0 && iMember<da_Count); #if CHECKARRAYLOCKING // check that locked for indices ASSERT(da_LockCt>0); #endif return da_Pointers[iMember]; } /* * Lock for getting indices. */ template<class Type> void CDynamicArray<Type>::Lock(void) { ASSERT(this!=NULL); #if CHECKARRAYLOCKING ASSERT(da_LockCt>=0); // increment lock counter da_LockCt++; #endif } /* * Unlock after getting indices. */ template<class Type> void CDynamicArray<Type>::Unlock(void) { ASSERT(this!=NULL); #if CHECKARRAYLOCKING da_LockCt--; ASSERT(da_LockCt>=0); #endif } /* * Get index of a member from it's pointer. */ template<class Type> INDEX CDynamicArray<Type>::Index(Type *ptMember) { ASSERT(this!=NULL); #if CHECKARRAYLOCKING // check that locked for indices ASSERT(da_LockCt>0); #endif return GetIndex(ptMember); } /* * Get index of a member from it's pointer without locking. */ template<class Type> INDEX CDynamicArray<Type>::GetIndex(Type *ptMember) { ASSERT(this!=NULL); // slow !!!! // check all members for (INDEX iMember=0; iMember<da_Count; iMember++) { if(da_Pointers[iMember]==ptMember) { return iMember; } } ASSERTALWAYS("CDynamicArray<>::Index(): Not a member of this array!"); return 0; } /* * Get number of elements in array. */ template<class Type> INDEX CDynamicArray<Type>::Count(void) const { ASSERT(this!=NULL); return da_Count; } /* * Assignment operator. */ template<class Type> CDynamicArray<Type> &CDynamicArray<Type>::operator=(CDynamicArray<Type> &arOriginal) { ASSERT(this!=NULL); ASSERT(&arOriginal!=NULL); ASSERT(this!=&arOriginal); // clear previous contents Clear(); // get count of elements in original array INDEX ctOriginal = arOriginal.Count(); // if the other array has no elements if (ctOriginal ==0) { // no assignment return*this; } // create that much elements Type *atNew = New(ctOriginal); // copy them all arOriginal.Lock(); for (INDEX iNew=0; iNew<ctOriginal; iNew++) { atNew[iNew] = arOriginal[iNew]; } arOriginal.Unlock(); return *this; } /* * Move all elements of another array into this one. */ template<class Type> void CDynamicArray<Type>::MoveArray(CDynamicArray<Type> &arOther) { ASSERT(this!=NULL && &arOther!=NULL); #if CHECKARRAYLOCKING // check that not locked for indices ASSERT(da_LockCt==0 && arOther.da_LockCt==0); #endif // if the other array has no elements if (arOther.da_Count==0) { // no moving return; } // remember number of elements INDEX iOldCount = da_Count; // grow pointer array to add the pointers to elements of other array GrowPointers(arOther.da_Count); // for each pointer in other array for (INDEX iOtherPointer=0; iOtherPointer<arOther.da_Count; iOtherPointer++) { // copy it at the end of this array da_Pointers[iOldCount+iOtherPointer] = arOther.da_Pointers[iOtherPointer]; } // remove array of pointers in other array arOther.ShrinkPointers(arOther.da_Count); // move list of allocated blocks from the other array to the end of this one da_BlocksList.MoveList(arOther.da_BlocksList); } ///////////////////////////////////////////////////////////////////// // CDynamicArrayIterator /* * Template class for iterating dynamic array. */ template<class Type> class CDynamicArrayIterator { private: INDEX dai_Index; // index of current element CDynamicArray<Type> &dai_Array; // reference to array public: /* Constructor for given array. */ inline CDynamicArrayIterator(CDynamicArray<Type> &da); /* Destructor. */ inline ~CDynamicArrayIterator(void); /* Move to next object. */ inline void MoveToNext(void); /* Check if finished. */ inline BOOL IsPastEnd(void); /* Get current element. */ Type &Current(void) { return *dai_Array.Pointer(dai_Index); } Type &operator*(void) { return *dai_Array.Pointer(dai_Index); } operator Type *(void) { return dai_Array.Pointer(dai_Index); } Type *operator->(void) { return dai_Array.Pointer(dai_Index); } }; /* * Constructor for given array. */ template<class Type> inline CDynamicArrayIterator<Type>::CDynamicArrayIterator(CDynamicArray<Type> &da) : dai_Array(da) { // lock indices dai_Array.Lock(); dai_Index = 0; } /* * Destructor. */ template<class Type> inline CDynamicArrayIterator<Type>::~CDynamicArrayIterator(void) { // unlock indices dai_Array.Unlock(); dai_Index = -1; } /* * Move to next object. */ template<class Type> inline void CDynamicArrayIterator<Type>::MoveToNext(void) { ASSERT(this!=NULL); dai_Index++; } /* * Check if finished. */ template<class Type> inline BOOL CDynamicArrayIterator<Type>::IsPastEnd(void) { ASSERT(this!=NULL); return dai_Index>=dai_Array.Count(); } #endif /* include-once check. */