// $Id$ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2007 Maik Semder // // 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, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef HEADER_REPLAYBUFFERTPL_H #define HEADER_REPLAYBUFFERTPL_H #ifdef HAVE_GHOST_REPLAY #include // needed for MSVC-memory-leak-checks #if defined( REPLAY_UNIT_TEST ) && defined( _MSC_VER ) # ifdef _DEBUG # ifndef _DBG_NEW # include inline void* __operator_new(size_t __n) { return ::operator new(__n,_NORMAL_BLOCK,__FILE__,__LINE__); } inline void* _cdecl operator new(size_t __n,const char* __fname,int __line) { return ::operator new(__n,_NORMAL_BLOCK,__fname,__line); } inline void _cdecl operator delete(void* __p,const char*,int) { ::operator delete(__p);} # define _DBG_NEW new(__FILE__,__LINE__) # define new _DBG_NEW # endif // _DBG_NEW # else # define __operator_new(__n) operator new(__n) # endif #endif template class ReplayBufferArray; template class ReplayBuffer { friend class ReplayBufferArray; public: ReplayBuffer() : m_pp_blocks(NULL),m_number_blocks(0),m_block_size(0),m_number_objects_used(0),m_healthy(true) {} ~ReplayBuffer() { destroy(); } private: ReplayBuffer( ReplayBuffer const &c ); ReplayBuffer const &operator=( ReplayBuffer const &c ); public: bool init( size_t number_preallocated_objects ); void destroy(); // this is false, if a reallocation failed bool isHealthy() const { return m_healthy; } // returns a new *free* object, allocated memory if necessary T* getNewObject(); // returs object at given position, like usual array access, // does not allocate memory, index must be < getNumberObjectsUsed() T const* getObjectAt( size_t index ) const; T* getObjectAt( size_t index ); size_t getNumberObjectsUsed() const { return m_number_objects_used; } size_t getNumberBlocks() const { return m_number_blocks; } private: // adds a new block of objects to m_pp_blocks with a size of m_block_size bool addNewBlock(); // helper to make sure healthy bit is set, if new fails template TT* buffer_new_array( size_t n ) { #if defined( REPLAY_UNIT_TEST ) && defined( _MSC_VER ) // the msvc-debug-new macros didnt like nothrow TT *p = new TT[ n ]; #else TT *p = new(std::nothrow) TT[ n ]; #endif m_healthy = (NULL != p ); return p; } private: T **m_pp_blocks; // number of allocated blocks. we start with 1 for recording // for showing a replay-file it is 1 size_t m_number_blocks; // size of all blocks size_t m_block_size; // number of used objects size_t m_number_objects_used; // this flag indicates, that, if any reallocation happedened, it failed // then recordings is blocked bool m_healthy; }; // does the same as ReplayBuffer, but it returns an array of objects, rather than just one // object .. template class ReplayBufferArray { public: ReplayBufferArray() : m_Buffer(), m_array_size(0) {} ~ReplayBufferArray() { destroy(); } void destroy(); bool init( size_t number_preallocated_arrays, size_t array_size ); // returns a new *free* array of objects with size of 2nd param in init T* getNewArray(); // returs objects at given position, like usual array access, // does not allocate memory T const* getArrayAt( size_t index ) const { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); } T* getArrayAt( size_t index ) { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); } size_t getNumberArraysUsed() const { return m_Buffer.getNumberObjectsUsed() / m_array_size; } size_t getNumberBlocks() const { return m_Buffer.getNumberBlocks(); } bool isHealthy() const { return m_Buffer.isHealthy(); } private: ReplayBuffer m_Buffer; size_t m_array_size; }; template bool ReplayBuffer::init( size_t number_preallocated_objects ) { // make sure *clean* usage assert( !m_pp_blocks ); assert( number_preallocated_objects ); m_block_size = number_preallocated_objects; if( !addNewBlock() ) return false; return true; } template void ReplayBuffer::destroy() { size_t tmp; if( m_pp_blocks ) { for( tmp = 0; tmp < m_number_blocks; ++tmp ) delete[] m_pp_blocks[tmp]; delete[] m_pp_blocks; m_pp_blocks = NULL; m_number_blocks = 0; m_block_size = 0; m_number_objects_used = 0; m_healthy = true; } } // returns a new *free* frame to be used to store the current frame-data into it // used to *record* the replay template T* ReplayBuffer::getNewObject() { // make sure initialization was called properly assert( m_pp_blocks ); assert( m_number_blocks ); if( !m_healthy ) return NULL; // check, if we need a new block if( m_number_objects_used == (m_block_size*m_number_blocks) ) { // we need a new block if( !addNewBlock() ) return NULL; } // get current frame T* block_current = m_pp_blocks[ m_number_blocks-1 ]; size_t new_in_block_idx = m_number_objects_used % m_block_size; T* current = block_current + new_in_block_idx; ++m_number_objects_used; return current; } // returs frame at given position from replay data // used to *show* the replay template T const* ReplayBuffer::getObjectAt( size_t index ) const { // make sure initialization was called properly assert( m_pp_blocks ); assert( m_number_blocks ); assert( index < m_number_objects_used ); assert( (index / m_block_size) < m_number_blocks ); T const* block = m_pp_blocks[ index / m_block_size ]; return block + (index % m_block_size); } template T* ReplayBuffer::getObjectAt( size_t index ) { // make sure initialization was called properly assert( m_pp_blocks ); assert( m_number_blocks ); assert( index < m_number_objects_used ); assert( (index / m_block_size) < m_number_blocks ); T* block = m_pp_blocks[ index / m_block_size ]; return block + (index % m_block_size); } // adds a new block of objects to m_pp_blocks with a size of m_block_size template bool ReplayBuffer::addNewBlock() { assert( m_block_size ); if( !m_healthy ) return false; size_t number_blocks_new = m_number_blocks + 1; T **pp_blocks_old = m_pp_blocks; m_pp_blocks = buffer_new_array( number_blocks_new ); if( !m_pp_blocks ) { // put back old blocks m_pp_blocks = pp_blocks_old; return false; } // copy old block pointers .. note: we dont copy the objects, // only the pointers, to blocks of objects, which is supposed // to be a very small number .. 2 is probably never reached size_t tmp; for( tmp = 0; tmp < m_number_blocks; ++tmp ) m_pp_blocks[tmp] = pp_blocks_old[tmp]; // create new objects at new block position m_pp_blocks[m_number_blocks] = buffer_new_array( m_block_size ); if( !m_pp_blocks[m_number_blocks] ) { // delete and put back old blocks delete[] m_pp_blocks; m_pp_blocks = pp_blocks_old; return false; } // everything went fine, we got new arrays of objects delete[] pp_blocks_old; pp_blocks_old = NULL; ++m_number_blocks; return true; } template void ReplayBufferArray::destroy() { m_Buffer.destroy(); m_array_size = 0; } template bool ReplayBufferArray::init( size_t number_preallocated_arrays, size_t array_size ) { assert( number_preallocated_arrays ); assert( array_size ); m_array_size = array_size; return m_Buffer.init( number_preallocated_arrays * array_size ); } // returns a new *free* array of objects template T* ReplayBufferArray::getNewArray() { if( !isHealthy() ) return NULL; // check, if we need a new block if( m_Buffer.m_number_objects_used == (m_Buffer.m_block_size*m_Buffer.m_number_blocks) ) { // we need a new block if( !m_Buffer.addNewBlock() ) return NULL; } // get current frame T* block_current = m_Buffer.m_pp_blocks[ m_Buffer.m_number_blocks-1 ]; size_t new_in_block_idx = m_Buffer.m_number_objects_used % m_Buffer.m_block_size; T* current = block_current + new_in_block_idx; assert( (current + m_array_size) <= (m_Buffer.m_pp_blocks[ m_Buffer.m_number_blocks-1 ] + m_Buffer.m_block_size) ); m_Buffer.m_number_objects_used += m_array_size; return current; } #endif // HAVE_GHOST_REPLAY #endif // HEADER_REPLAYBUFFERTPL_H