/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef SE_INCL_DYNAMICARRAY_CPP #define SE_INCL_DYNAMICARRAY_CPP #ifdef PRAGMA_ONCE #pragma once #endif #include #include #include #include // 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 iter(array); !iter.IsPastEnd(); iter.MoveToNext() ) class CDABlockInfo { public: CListNode bi_ListNode; void *bi_Memory; }; /* * Default constructor. */ template CDynamicArray::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 CDynamicArray::CDynamicArray(CDynamicArray &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 CDynamicArray::~CDynamicArray(void) { Clear(); } /* * Destroy all objects, and reset the array to initial (empty) state. */ template void CDynamicArray::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; iPointerbi_Memory; // free memory used by block info delete &itBlock.Current(); } } /* * Grow pointer array by a given number of members. */ template void CDynamicArray::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 void CDynamicArray::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 Type *CDynamicArray::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 Type *CDynamicArray::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 void CDynamicArray::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 Type *CDynamicArray::Pointer(INDEX iMember) { ASSERT(this!=NULL); // check that index is currently valid ASSERT(iMember>=0 && iMember0); #endif return da_Pointers[iMember]; } template const Type *CDynamicArray::Pointer(INDEX iMember) const { ASSERT(this!=NULL); // check that index is currently valid ASSERT(iMember>=0 && iMember0); #endif return da_Pointers[iMember]; } /* * Lock for getting indices. */ template void CDynamicArray::Lock(void) { ASSERT(this!=NULL); #if CHECKARRAYLOCKING ASSERT(da_LockCt>=0); // increment lock counter da_LockCt++; #endif } /* * Unlock after getting indices. */ template void CDynamicArray::Unlock(void) { ASSERT(this!=NULL); #if CHECKARRAYLOCKING da_LockCt--; ASSERT(da_LockCt>=0); #endif } /* * Get index of a member from it's pointer. */ template INDEX CDynamicArray::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 INDEX CDynamicArray::GetIndex(Type *ptMember) { ASSERT(this!=NULL); // slow !!!! // check all members for (INDEX iMember=0; iMember::Index(): Not a member of this array!"); return 0; } /* * Get number of elements in array. */ template INDEX CDynamicArray::Count(void) const { ASSERT(this!=NULL); return da_Count; } /* * Assignment operator. */ template CDynamicArray &CDynamicArray::operator=(CDynamicArray &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 void CDynamicArray::MoveArray(CDynamicArray &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 class CDynamicArrayIterator { private: INDEX dai_Index; // index of current element CDynamicArray &dai_Array; // reference to array public: /* Constructor for given array. */ inline CDynamicArrayIterator(CDynamicArray &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 inline CDynamicArrayIterator::CDynamicArrayIterator(CDynamicArray &da) : dai_Array(da) { // lock indices dai_Array.Lock(); dai_Index = 0; } /* * Destructor. */ template inline CDynamicArrayIterator::~CDynamicArrayIterator(void) { // unlock indices dai_Array.Unlock(); dai_Index = -1; } /* * Move to next object. */ template inline void CDynamicArrayIterator::MoveToNext(void) { ASSERT(this!=NULL); dai_Index++; } /* * Check if finished. */ template inline BOOL CDynamicArrayIterator::IsPastEnd(void) { ASSERT(this!=NULL); return dai_Index>=dai_Array.Count(); } #endif /* include-once check. */