32b88d2091
- changed name of class "Buffer" to "ReplayBuffer" .. "Buffer" at global scope was not a good idea of mine .. ;) git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@1268 178a84e3-b1eb-0310-8ba1-8eac791a3b58
309 lines
9.5 KiB
C++
309 lines
9.5 KiB
C++
// $Id$
|
|
//
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2007 Maik Semder <ikework@gmx.de>
|
|
//
|
|
// 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 2
|
|
// 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 <new>
|
|
|
|
// needed for MSVC-memory-leak-checks
|
|
#if defined( REPLAY_UNIT_TEST ) && defined( _MSC_VER )
|
|
# ifdef _DEBUG
|
|
# ifndef _DBG_NEW
|
|
# include <crtdbg.h>
|
|
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<typename T> class ReplayBufferArray;
|
|
|
|
template<typename T>
|
|
class ReplayBuffer
|
|
{
|
|
friend class ReplayBufferArray<T>;
|
|
|
|
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<T> const &c );
|
|
ReplayBuffer<T> const &operator=( ReplayBuffer<T> 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<typename TT>
|
|
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<T>, but it returns an array of objects, rather than just one
|
|
// object ..
|
|
template<typename T>
|
|
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<T> m_Buffer;
|
|
size_t m_array_size;
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
bool ReplayBuffer<T>::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<typename T>
|
|
void ReplayBuffer<T>::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<typename T>
|
|
T* ReplayBuffer<T>::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<typename T>
|
|
T const* ReplayBuffer<T>::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<typename T>
|
|
T* ReplayBuffer<T>::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<typename T>
|
|
bool ReplayBuffer<T>::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<T*>( 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<T>( 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<typename T>
|
|
void ReplayBufferArray<T>::destroy()
|
|
{
|
|
m_Buffer.destroy();
|
|
m_array_size = 0;
|
|
}
|
|
|
|
template<typename T>
|
|
bool ReplayBufferArray<T>::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<typename T>
|
|
T* ReplayBufferArray<T>::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
|
|
|