Serious-Engine/Sources/Engine/Templates/DynamicArray.cpp
Ryan C. Gordon 24cb244d43 First attempt to hand-merge Ryan's Linux and Mac OS X port.
This was a _ton_ of changes, made 15 years ago, so there are probably some
problems to work out still.

Among others: Engine/Base/Stream.* was mostly abandoned and will need to be
re-ported.

Still, this is a pretty good start, and probably holds a world record for
lines of changes or something.  :)
2016-03-28 23:46:13 -04:00

444 lines
10 KiB
C++

/* 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. */