Back to index

supertuxkart  0.5+dfsg1
replay_buffer_tpl.hpp
Go to the documentation of this file.
00001 //  $Id$
00002 //
00003 //  SuperTuxKart - a fun racing game with go-kart
00004 //  Copyright (C) 2007 Maik Semder <ikework@gmx.de>
00005 //
00006 //  This program is free software; you can redistribute it and/or
00007 //  modify it under the terms of the GNU General Public License
00008 //  as published by the Free Software Foundation; either version 2
00009 //  of the License, or (at your option) any later version.
00010 //
00011 //  This program is distributed in the hope that it will be useful,
00012 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //  GNU General Public License for more details.
00015 //
00016 //  You should have received a copy of the GNU General Public License
00017 //  along with this program; if not, write to the Free Software
00018 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019 
00020 #ifndef HEADER_REPLAYBUFFERTPL_H
00021 #define HEADER_REPLAYBUFFERTPL_H
00022 
00023 #ifdef HAVE_GHOST_REPLAY
00024 
00025 
00026 #include <new>
00027 
00028 // needed for MSVC-memory-leak-checks
00029 #if defined( REPLAY_UNIT_TEST ) && defined( _MSC_VER )
00030 #  ifdef _DEBUG
00031 #    ifndef _DBG_NEW
00032 #      include <crtdbg.h>
00033         inline void* __operator_new(size_t __n) { return ::operator new(__n,_NORMAL_BLOCK,__FILE__,__LINE__); }
00034         inline void* _cdecl operator new(size_t __n,const char* __fname,int __line) { return ::operator new(__n,_NORMAL_BLOCK,__fname,__line); }
00035         inline void _cdecl operator delete(void* __p,const char*,int) { ::operator delete(__p);}
00036 #       define _DBG_NEW new(__FILE__,__LINE__)
00037 #       define new _DBG_NEW
00038 #    endif // _DBG_NEW
00039 #  else
00040 #    define __operator_new(__n) operator new(__n)
00041 #  endif
00042 #endif
00043 
00044 
00045 template<typename T> class ReplayBufferArray;
00046 
00047 template<typename T>
00048 class ReplayBuffer
00049 {
00050     friend class ReplayBufferArray<T>;
00051 
00052 public:
00053     ReplayBuffer() : m_pp_blocks(NULL),m_number_blocks(0),m_block_size(0),m_number_objects_used(0),m_healthy(true) {}
00054     ~ReplayBuffer() { destroy(); }
00055 
00056 private:
00057     ReplayBuffer( ReplayBuffer<T> const &c );
00058     ReplayBuffer<T> const &operator=( ReplayBuffer<T> const &c );
00059 
00060 public:
00061     bool            init( size_t number_preallocated_objects );
00062     void            destroy();
00063     // this is false, if a reallocation failed
00064     bool            isHealthy() const                   { return m_healthy; }
00065     // returns a new *free* object, allocated memory if necessary
00066     T*              getNewObject();
00067     // returs object at given position, like usual array access,
00068     // does not allocate memory, index must be < getNumberObjectsUsed()
00069     T const*        getObjectAt( size_t index ) const;
00070     T*              getObjectAt( size_t index );
00071     size_t          getNumberObjectsUsed() const        { return m_number_objects_used; }
00072     size_t          getNumberBlocks() const             { return m_number_blocks; }
00073 
00074 private:
00075     // adds a new block of objects to m_pp_blocks with a size of m_block_size
00076     bool            addNewBlock();
00077     // helper to make sure healthy bit is set, if new fails
00078     template<typename TT>
00079     TT*              buffer_new_array( size_t n )
00080     {
00081 #if defined( REPLAY_UNIT_TEST ) && defined( _MSC_VER )
00082         // the msvc-debug-new macros didnt like nothrow
00083         TT *p = new TT[ n ];
00084 #else
00085         TT *p = new(std::nothrow) TT[ n ];
00086 #endif
00087         m_healthy = (NULL != p );
00088         return p;
00089     }
00090 
00091 private:
00092     T       **m_pp_blocks;
00093     // number of allocated blocks. we start with 1 for recording
00094     // for showing a replay-file it is 1
00095     size_t  m_number_blocks;
00096     // size of all blocks
00097     size_t  m_block_size;
00098     // number of used objects
00099     size_t  m_number_objects_used;
00100     // this flag indicates, that, if any reallocation happedened, it failed
00101     // then recordings is blocked
00102     bool    m_healthy;
00103 };
00104 
00105 
00106 // does the same as ReplayBuffer<T>, but it returns an array of objects, rather than just one 
00107 // object .. 
00108 template<typename T>
00109 class ReplayBufferArray
00110 {
00111 public:
00112     ReplayBufferArray() : m_Buffer(), m_array_size(0) {}
00113     ~ReplayBufferArray() { destroy(); }
00114 
00115     void            destroy();
00116     bool            init( size_t number_preallocated_arrays, size_t array_size );
00117     // returns a new *free* array of objects with size of 2nd param in init
00118     T*              getNewArray();
00119     // returs objects at given position, like usual array access,
00120     // does not allocate memory
00121     T const*        getArrayAt( size_t index ) const    { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); }
00122     T*              getArrayAt( size_t index )          { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); }
00123     size_t          getNumberArraysUsed() const         { return m_Buffer.getNumberObjectsUsed() / m_array_size; }
00124     size_t          getNumberBlocks() const             { return m_Buffer.getNumberBlocks(); }
00125     bool            isHealthy() const                   { return m_Buffer.isHealthy(); }
00126 
00127 private:
00128     ReplayBuffer<T> m_Buffer;
00129     size_t          m_array_size;
00130 };
00131 
00132 
00133 
00134 
00135 template<typename T>
00136 bool ReplayBuffer<T>::init( size_t number_preallocated_objects )
00137 {
00138     // make sure *clean* usage
00139     assert( !m_pp_blocks );
00140     assert( number_preallocated_objects );
00141     m_block_size = number_preallocated_objects;
00142 
00143     if( !addNewBlock() ) return false;
00144 
00145     return true;
00146 }
00147 
00148 template<typename T>
00149 void ReplayBuffer<T>::destroy()
00150 {
00151     size_t tmp;
00152     if( m_pp_blocks )
00153     {
00154         for( tmp = 0; tmp < m_number_blocks; ++tmp ) delete[] m_pp_blocks[tmp];
00155         delete[] m_pp_blocks; m_pp_blocks = NULL;
00156         m_number_blocks = 0;
00157         m_block_size = 0;
00158         m_number_objects_used = 0;
00159         m_healthy = true;
00160     }
00161 }
00162 
00163 // returns a new *free* frame to be used to store the current frame-data into it
00164 // used to *record* the replay
00165 template<typename T>
00166 T* ReplayBuffer<T>::getNewObject()
00167 {
00168     // make sure initialization was called properly
00169     assert( m_pp_blocks );
00170     assert( m_number_blocks );
00171 
00172     if( !m_healthy ) return NULL;
00173 
00174     // check, if we need a new block
00175     if( m_number_objects_used == (m_block_size*m_number_blocks) )
00176     {
00177         // we need a new block
00178         if( !addNewBlock() ) return NULL;
00179     }
00180 
00181     // get current frame
00182     T* block_current = m_pp_blocks[ m_number_blocks-1 ];
00183     size_t new_in_block_idx = m_number_objects_used % m_block_size;
00184     T* current = block_current + new_in_block_idx;
00185 
00186     ++m_number_objects_used;
00187 
00188     return current;
00189 }
00190 
00191 // returs frame at given position from replay data
00192 // used to *show* the replay
00193 template<typename T>
00194 T const* ReplayBuffer<T>::getObjectAt( size_t index ) const
00195 {
00196     // make sure initialization was called properly
00197     assert( m_pp_blocks );
00198     assert( m_number_blocks );
00199     assert( index < m_number_objects_used );
00200     assert( (index / m_block_size) < m_number_blocks );
00201 
00202     T const* block = m_pp_blocks[ index / m_block_size ];
00203     return block + (index % m_block_size);
00204 }
00205 
00206 template<typename T>
00207 T* ReplayBuffer<T>::getObjectAt( size_t index )
00208 {
00209     // make sure initialization was called properly
00210     assert( m_pp_blocks );
00211     assert( m_number_blocks );
00212     assert( index < m_number_objects_used );
00213     assert( (index / m_block_size) < m_number_blocks );
00214 
00215     T* block = m_pp_blocks[ index / m_block_size ];
00216     return block + (index % m_block_size);
00217 }
00218 
00219 // adds a new block of objects to m_pp_blocks with a size of m_block_size
00220 template<typename T>
00221 bool ReplayBuffer<T>::addNewBlock()
00222 {
00223     assert( m_block_size );
00224 
00225     if( !m_healthy ) return false;
00226 
00227     size_t number_blocks_new = m_number_blocks + 1;
00228 
00229     T **pp_blocks_old = m_pp_blocks;
00230     m_pp_blocks = buffer_new_array<T*>( number_blocks_new );
00231     if( !m_pp_blocks ) 
00232     {
00233         // put back old blocks
00234         m_pp_blocks = pp_blocks_old;
00235         return false;
00236     }
00237 
00238     // copy old block pointers .. note: we dont copy the objects,
00239     // only the pointers, to blocks of objects, which is supposed 
00240     // to be a very small number .. 2 is probably never reached
00241     size_t tmp;
00242     for( tmp = 0; tmp < m_number_blocks; ++tmp ) m_pp_blocks[tmp] = pp_blocks_old[tmp];
00243 
00244     // create new objects at new block position
00245     m_pp_blocks[m_number_blocks] = buffer_new_array<T>( m_block_size );
00246     if( !m_pp_blocks[m_number_blocks] )
00247     {
00248         // delete and put back old blocks
00249         delete[] m_pp_blocks;
00250         m_pp_blocks = pp_blocks_old;
00251         return false;
00252     }
00253 
00254     // everything went fine, we got new arrays of objects
00255     delete[] pp_blocks_old; pp_blocks_old = NULL;
00256 
00257     ++m_number_blocks;
00258 
00259     return true;
00260 }
00261 
00262 
00263 template<typename T>
00264 void ReplayBufferArray<T>::destroy()
00265 {
00266     m_Buffer.destroy();
00267     m_array_size = 0;
00268 }
00269 
00270 template<typename T>
00271 bool ReplayBufferArray<T>::init( size_t number_preallocated_arrays, size_t array_size )
00272 {
00273     assert( number_preallocated_arrays );
00274     assert( array_size );
00275     m_array_size = array_size;
00276     return m_Buffer.init( number_preallocated_arrays * array_size );
00277 }
00278 
00279 // returns a new *free* array of objects
00280 template<typename T>
00281 T* ReplayBufferArray<T>::getNewArray()
00282 {
00283     if( !isHealthy() ) return NULL;
00284 
00285     // check, if we need a new block
00286     if( m_Buffer.m_number_objects_used == (m_Buffer.m_block_size*m_Buffer.m_number_blocks) )
00287     {
00288         // we need a new block
00289         if( !m_Buffer.addNewBlock() ) return NULL;
00290     }
00291 
00292     // get current frame
00293     T* block_current = m_Buffer.m_pp_blocks[ m_Buffer.m_number_blocks-1 ];
00294     size_t new_in_block_idx = m_Buffer.m_number_objects_used % m_Buffer.m_block_size;
00295     T* current = block_current + new_in_block_idx;
00296 
00297     assert( (current + m_array_size) <= (m_Buffer.m_pp_blocks[ m_Buffer.m_number_blocks-1 ] + m_Buffer.m_block_size) );
00298 
00299     m_Buffer.m_number_objects_used += m_array_size;
00300 
00301     return current;
00302 }
00303 
00304 
00305 #endif // HAVE_GHOST_REPLAY
00306 
00307 #endif // HEADER_REPLAYBUFFERTPL_H
00308