/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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, see . * */ /* * This file is based on WME Lite. * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ #ifndef WINTERMUTE_COLL_TEMPL_H #define WINTERMUTE_COLL_TEMPL_H #include "engines/wintermute/base/base_persistence_manager.h" namespace Wintermute { #include ///////////////////////////////////////////////////////////////////////////// template inline void dcConstructElements(TYPE *pElements, int32 nCount) { // first do bit-wise zero initialization memset((void *)pElements, 0, nCount * sizeof(TYPE)); // then call the constructor(s) for (; nCount--; pElements++) ::new((void *)pElements) TYPE; } ///////////////////////////////////////////////////////////////////////////// template inline void dcDestructElements(TYPE *pElements, int32 nCount) { // call the destructor(s) for (; nCount--; pElements++) pElements->~TYPE(); } ///////////////////////////////////////////////////////////////////////////// template inline void dcCopyElements(TYPE *pDest, const TYPE *pSrc, int32 nCount) { // default is element-copy using assignment while (nCount--) *pDest++ = *pSrc++; } ///////////////////////////////////////////////////////////////////////////// // BaseArray ///////////////////////////////////////////////////////////////////////////// template class BaseArrayBase { public: // Construction BaseArrayBase(); // Attributes int32 getSize() const; int32 getUpperBound() const; void setSize(int32 nNewSize, int32 nGrowBy = -1); // Operations // Clean up void freeExtra(); void removeAll(); // Accessing elements TYPE getAt(int32 nIndex) const; void setAt(int32 nIndex, TYPE newElement); TYPE &elementAt(int32 nIndex); // Direct Access to the element data (may return NULL) const TYPE *getData() const; TYPE *getData(); // Potentially growing the array void setAtGrow(int32 nIndex, TYPE newElement); int32 add(TYPE newElement); int32 append(const BaseArrayBase &src); void copy(const BaseArrayBase &src); // overloaded operator helpers TYPE operator[](int32 nIndex) const; TYPE &operator[](int32 nIndex); // Operations that move elements around void insertAt(int32 nIndex, TYPE newElement, int32 nCount = 1); void removeAt(int32 nIndex, int32 nCount = 1); void insertAt(int32 nStartIndex, BaseArrayBase *pNewArray); // Implementation protected: TYPE *_pData; // the actual array of data int32 _nSize; // # of elements (upperBound - 1) int32 _nMaxSize; // max allocated int32 _nGrowBy; // grow amount public: ~BaseArrayBase(); }; ///////////////////////////////////////////////////////////////////////////// // CBArray inline functions ///////////////////////////////////////////////////////////////////////////// template inline int32 BaseArrayBase::getSize() const { return _nSize; } template inline int32 BaseArrayBase::getUpperBound() const { return _nSize - 1; } template inline void BaseArrayBase::removeAll() { setSize(0, -1); } template inline TYPE BaseArrayBase::getAt(int32 nIndex) const { return _pData[nIndex]; } template inline void BaseArrayBase::setAt(int32 nIndex, TYPE newElement) { _pData[nIndex] = newElement; } template inline TYPE &BaseArrayBase::elementAt(int32 nIndex) { return _pData[nIndex]; } template inline const TYPE *BaseArrayBase::getData() const { return (const TYPE *)_pData; } template inline TYPE *BaseArrayBase::getData() { return (TYPE *)_pData; } template inline int32 BaseArrayBase::add(TYPE newElement) { int32 nIndex = _nSize; setAtGrow(nIndex, newElement); return nIndex; } template inline TYPE BaseArrayBase::operator[](int32 nIndex) const { return getAt(nIndex); } template inline TYPE &BaseArrayBase::operator[](int32 nIndex) { return elementAt(nIndex); } ///////////////////////////////////////////////////////////////////////////// // BaseArray out-of-line functions ///////////////////////////////////////////////////////////////////////////// template BaseArrayBase::BaseArrayBase() { _pData = nullptr; _nSize = _nMaxSize = _nGrowBy = 0; } ///////////////////////////////////////////////////////////////////////////// template BaseArrayBase::~BaseArrayBase() { if (_pData != nullptr) { dcDestructElements(_pData, _nSize); delete[] (byte *)_pData; } } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::setSize(int32 nNewSize, int32 nGrowBy) { if (nGrowBy != -1) _nGrowBy = nGrowBy; // set new size if (nNewSize == 0) { // shrink to nothing if (_pData != nullptr) { dcDestructElements(_pData, _nSize); delete[] (byte *)_pData; _pData = nullptr; } _nSize = _nMaxSize = 0; } else if (_pData == nullptr) { // create one with exact size _pData = (TYPE *) new byte[nNewSize * sizeof(TYPE)]; dcConstructElements(_pData, nNewSize); _nSize = _nMaxSize = nNewSize; } else if (nNewSize <= _nMaxSize) { // it fits if (nNewSize > _nSize) { // initialize the new elements dcConstructElements(&_pData[_nSize], nNewSize - _nSize); } else if (_nSize > nNewSize) { // destroy the old elements dcDestructElements(&_pData[nNewSize], _nSize - nNewSize); } _nSize = nNewSize; } else { // otherwise, grow array int32 numGrowBy = _nGrowBy; if (numGrowBy == 0) { // heuristically determine growth when nGrowBy == 0 // (this avoids heap fragmentation in many situations) numGrowBy = _nSize / 8; numGrowBy = (numGrowBy < 4) ? 4 : ((numGrowBy > 1024) ? 1024 : numGrowBy); } int nNewMax; if (nNewSize < _nMaxSize + numGrowBy) nNewMax = _nMaxSize + numGrowBy; // granularity else nNewMax = nNewSize; // no slush TYPE *pNewData = (TYPE *) new byte[nNewMax * sizeof(TYPE)]; // copy new data from old memcpy(pNewData, _pData, _nSize * sizeof(TYPE)); // construct remaining elements dcConstructElements(&pNewData[_nSize], nNewSize - _nSize); // get rid of old stuff (note: no destructors called) delete[] (byte *)_pData; _pData = pNewData; _nSize = nNewSize; _nMaxSize = nNewMax; } } ///////////////////////////////////////////////////////////////////////////// template int32 BaseArrayBase::append(const BaseArrayBase &src) { int32 nOldSize = _nSize; setSize(_nSize + src._nSize); dcCopyElements(_pData + nOldSize, src._pData, src._nSize); return nOldSize; } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::copy(const BaseArrayBase &src) { setSize(src._nSize); dcCopyElements(_pData, src._pData, src._nSize); } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::freeExtra() { if (_nSize != _nMaxSize) { // shrink to desired size TYPE *pNewData = nullptr; if (_nSize != 0) { pNewData = (TYPE *) new byte[_nSize * sizeof(TYPE)]; // copy new data from old memcpy(pNewData, _pData, _nSize * sizeof(TYPE)); } // get rid of old stuff (note: no destructors called) delete[] (byte *)_pData; _pData = pNewData; _nMaxSize = _nSize; } } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::setAtGrow(int32 nIndex, TYPE newElement) { if (nIndex >= _nSize) setSize(nIndex + 1, -1); _pData[nIndex] = newElement; } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::insertAt(int32 nIndex, TYPE newElement, int32 nCount /*=1*/) { if (nIndex >= _nSize) { // adding after the end of the array setSize(nIndex + nCount, -1); // grow so nIndex is valid } else { // inserting in the middle of the array int32 nOldSize = _nSize; setSize(_nSize + nCount, -1); // grow it to new size // destroy intial data before copying over it dcDestructElements(&_pData[nOldSize], nCount); // shift old data up to fill gap memmove(&_pData[nIndex + nCount], &_pData[nIndex], (nOldSize - nIndex) * sizeof(TYPE)); // re-init slots we copied from dcConstructElements(&_pData[nIndex], nCount); } // insert new value in the gap while (nCount--) _pData[nIndex++] = newElement; } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::removeAt(int32 nIndex, int32 nCount) { // just remove a range int32 nMoveCount = _nSize - (nIndex + nCount); dcDestructElements(&_pData[nIndex], nCount); if (nMoveCount) memmove(&_pData[nIndex], &_pData[nIndex + nCount], nMoveCount * sizeof(TYPE)); _nSize -= nCount; } ///////////////////////////////////////////////////////////////////////////// template void BaseArrayBase::insertAt(int32 nStartIndex, BaseArrayBase *pNewArray) { if (pNewArray->getSize() > 0) { insertAt(nStartIndex, pNewArray->getAt(0), pNewArray->getSize()); for (int32 i = 0; i < pNewArray->getSize(); i++) setAt(nStartIndex + i, pNewArray->getAt(i)); } } ///////////////////////////////////////////////////////////////////////////// template class BaseArray : public BaseArrayBase { public: bool persist(BasePersistenceManager *persistMgr) { int32 i, j; if (persistMgr->getIsSaving()) { j = BaseArray::getSize(); persistMgr->transferSint32("ArraySize", &j); for (i = 0; i < j; i++) { TYPE obj = BaseArray::getAt(i); persistMgr->transferPtr("", &obj); } } else { BaseArray::setSize(0, -1); persistMgr->transferSint32("ArraySize", &j); for (i = 0; i < j; i++) { TYPE obj = nullptr; persistMgr->transferPtr("", &obj); BaseArray::add(obj); } } return true; } }; ///////////////////////////////////////////////////////////////////////////// template <> class BaseArray : public BaseArrayBase { public: bool persist(BasePersistenceManager *persistMgr) { int32 i, j; if (persistMgr->getIsSaving()) { j = BaseArray::getSize(); persistMgr->transferSint32("ArraySize", &j); for (i = 0; i < j; i++) { char *obj = BaseArray::getAt(i); persistMgr->transferCharPtr("", &obj); } } else { setSize(0, -1); persistMgr->transferSint32("ArraySize", &j); for (i = 0; i < j; i++) { char *obj = nullptr; persistMgr->transferCharPtr("", &obj); add(obj); } } return true; } }; ///////////////////////////////////////////////////////////////////////////// template <> class BaseArray : public BaseArrayBase { public: bool persist(BasePersistenceManager *persistMgr) { int32 i, j; if (persistMgr->getIsSaving()) { j = BaseArray::getSize(); persistMgr->transferSint32("ArraySize", &j); for (i = 0; i < j; i++) { const char * obj = BaseArray::getAt(i); persistMgr->transferConstChar("", &obj); } } else { BaseArray::setSize(0, -1); persistMgr->transferSint32("ArraySize", &j); for (i = 0; i < j; i++) { const char * obj; persistMgr->transferConstChar("", &obj); BaseArray::add(obj); } } return true; } }; } // End of namespace Wintermute #endif