stk-code_catmod/src/replay_buffer_tpl.hpp
ikework 32b88d2091 - put a proper copyright to all replay-files
- 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
2007-09-28 13:06:16 +00:00

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