//
//  Copyright © 2003, RESON Inc. All Rights Reserved.
//
//  No part of this file may be reproduced or transmitted in any form or by
//  any means, electronic or mechanical, including photocopy, recording, or
//  information storage or retrieval system, without permission in writing
//  from RESON Inc.
//
//  Filename:   CircularBuffer.h
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Template class helper to access a pool of memory as a circualar buffer.
//              Note: the memory can be any pool of bytes, provided the size criteria is met
//              ( > sizeof( MEMORYPOOLHEADER ) + cells ).
//
//  Notes:  1)  The Read() method RETURNS A POINTER DIRECLTY INTO THE MEMORY POOL and pointing to the next available 
//              record to be read. It should be accessed from within the m_pSwmrGuard->WaitToRead() and 
//              m_pSwmrGuard->DoneReading() block. FAILURE TO DO SO WILL RESULT IN DATA CORRUPTION IN A MULTITHREADED
//              ENVIRONMENT. Clients may use the operator SWMRG *() to access the appropriate sync object.
//              NB: It would be an error to handle synchronization in this routine since the client will still
//              be directly accessing the MMF and a deadlock would likely result.
//

#if !defined(AFX_CIRCULARBUFFER_H__A93F104E_7F89_4934_8FE5_FB082CAB79D1__INCLUDED_)
#define AFX_CIRCULARBUFFER_H__A93F104E_7F89_4934_8FE5_FB082CAB79D1__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "..\..\Components\NetUtils\MemMap.h"

#pragma warning( disable : 4018 )
#pragma warning( disable : 4146 )
#pragma warning( push, 3 )
#include <memory>
#pragma warning( pop )
#pragma warning( default : 4146 )
#pragma warning( default : 4018 )

///////////////////////////////////////////////////////////////////////////////
// Module constants and definitions.

namespace NCIRCULARBUFFER                                                                                               // Begin namespace NCIRCULARBUFFER
{
    const unsigned long                     ulVersion_c         =   1UL;                                                // Version of the MMF structure definition.
    const unsigned long                     ulNumberOfCells_c   =   255UL;                                              // Number of data cells in MMF.
    const unsigned long                     ulCellSizeInBytes_c =   128UL * 1024UL;                                     // Size of each data cell in bytes.
    const unsigned long                     ulReservedSpace_c   =   122888UL;                                           // Padding to make entire header 128k.
}

#pragma pack ( push, CIRCULARBUFFER_H_PACK, 1 )                                                                         // Single byte alignment.

typedef struct tagCELLINDEX
{
    unsigned long                           m_ulCellsInSequence;                                                        // Number of cells that this sequence of data occupies.
    unsigned long                           m_ulSequenceNumberOfCell;                                                   // Cell number in sequence of cells for which data occupies.
    unsigned long                           m_TotalBytesInSequence;                                                     // Total bytes across all cell for data in sequence.
    unsigned long                           m_ulBytesInCell;                                                            // Total bytes used in this cell.
    unsigned long                           m_ulUsed;                                                                   // Cell is used.
                                            
    BYTE                                    m_byReserved    [ 12 ];                                                     // Padding to 32 bytes.
}
CELLINDEX, *PCELLINDEX;

#pragma pack ( pop, CIRCULARBUFFER_H_PACK )                                                                             // Single byte alignment.


template <typename tRecordHeader>
class CCircularBuffer
{
public:

    ///////////////
    // Services.

    // Construction, destruction etc.

                                            CCircularBuffer             (   BYTE * const            pbyMemoryPool,
                                                                            const unsigned long    &rulMemoryPoolSize,
                                                                            LPCTSTR                 lpszGuardName               = NULL,
                                                                            LPCTSTR                 lpszDataAvailableEventName  = NULL );

    virtual                                ~CCircularBuffer             (   void );

    bool                                    IsInitialized               (   void ) const;

    bool                                    InitializeHeader            (   void );

    bool                                    Write                       (   const tRecordHeader    *psRecord,
                                                                            const unsigned long    &rulTotalBytesThisRecord );

    bool                                    Read                        (   unsigned long          &rulNextToRead,
                                                                            unsigned long          &rulTotalBytesThisRecord,
                                                                            tRecordHeader        * &rpsRecord );

    HANDLE                                  GetWriteEvent               (   void );

    CSWMRG *                                GetDataGuard                (   void );

private:

    ///////////////
    // Constants and definitions.

#pragma pack ( push, CIRCULARBUFFER_H_PACK, 1 )                                                                                    // Single byte alignment.

    typedef struct tagMEMORYPOOLHEADER
    {
        // General structure information.

        unsigned long                       m_ulVersionNumber;                                                              // Version number of this structure.

        // Data relative parameters.

        unsigned long                       m_ulNumberOfCells;                                                              // Number of cells in this MMF.
        unsigned long                       m_ulCellSizeInBytes;                                                            // Cell size in bytes.
        unsigned long                       m_ulNextWritePoint;                                                             // Pointer for next write.
        unsigned long                       m_ulNextSequenceNumber;                                                         // Sequence number assigned to each block of cells written.
        unsigned long                       m_ulWrapPoint;                                                                  // Cell index at which wrap occured and therefore readers should not go beyond.

        // Cell occupancy index table.

        CELLINDEX                           m_sCellIndex[ NCIRCULARBUFFER::ulNumberOfCells_c ];                             // Cell occupancy data.

        // Add new elements here to maintain backward compatibility; reduce reserved space accordingly.

        BYTE                                m_byReserved[ NCIRCULARBUFFER::ulReservedSpace_c ];                             // Reserved space to pad header.

        // Data portion of the quantized circular buffer area immediately follows header.
    }
    MEMORYPOOLHEADER, *PMEMORYPOOLHEADER;

#pragma pack ( pop, CIRCULARBUFFER_H_PACK )                                                                                 // Single byte alignment.

    ///////////////
    // Attributes.

    const unsigned long                     m_ulVersion;
    const unsigned long                     m_ulNumberOfCells;
    const unsigned long                     m_ulCellSizeInBytes;
    const unsigned long                     m_ulPoolSize;                                                                   // Total pool size in bytes.
    const unsigned long                     m_ulReservedSpace;

    bool                                    m_bInitialized;                                                                 // Construction success/failure indicator flag.
    HANDLE                                  m_hDataAvailable;                                                               // Data written event object.
    std::auto_ptr<CSWMRG>                   m_pSwmrGuard;                                                                   // Pointer to our read/write sync pointer.

    MEMORYPOOLHEADER  * const               m_psMemoryPool;                                                                 // Pointer to our memory pool as a formated header.
    BYTE              * const               m_pbyCells;

    ///////////////
    // Services.

    bool                                    WriteRecord                 (   const tRecordHeader    *pbyRecord,
                                                                            const unsigned long    &rulTotalBytesThisRecord );

    void                                    InvalidateCells             (   const unsigned long    &rulStartCell,
                                                                            const unsigned long    &rulStopCell,
                                                                            const bool             &rbHandleFinalSequence );

    void                                    SetCellSequence             (   const unsigned long    &rulStartCell,
                                                                            const unsigned long    &rulNumberOfCells,
                                                                            const unsigned long    &rulTotalBytesThisRecord );

    void                                    DataIsAvailable             (   void )  const;

    // Standard methods that are not implemented thus private.

                                            CCircularBuffer             (   void );                                             // Not implemented thus private.
                                            CCircularBuffer             (   const CCircularBuffer  &rRhs );                     // Not implemented thus private.
    CCircularBuffer &                       operator =                  (   const CCircularBuffer  &rRhs );                     // Not implemented thus private.

};

//////////////////////////////////////////////////////////////////////
// CCircularBuffer class implemenation.

template <typename tRecordHeader>
inline
CCircularBuffer<tRecordHeader>::CCircularBuffer(    BYTE * const            pbyMemoryPool,
                                                    const unsigned long    &rulMemoryPoolSize,
                                                    LPCTSTR                 lpszGuardName,
                                                    LPCTSTR                 lpszDataAvailableEventName )

                               :m_psMemoryPool      ( reinterpret_cast<MEMORYPOOLHEADER  * const>( pbyMemoryPool ) ),
                                m_pbyCells          (  pbyMemoryPool + sizeof (MEMORYPOOLHEADER ) ),
                                m_ulPoolSize        ( rulMemoryPoolSize   ),
                                m_ulVersion         ( NCIRCULARBUFFER::ulVersion_c  ),
                                m_ulNumberOfCells   ( NCIRCULARBUFFER::ulNumberOfCells_c ),
                                m_ulCellSizeInBytes ( NCIRCULARBUFFER::ulCellSizeInBytes_c ),
                                m_ulReservedSpace   ( NCIRCULARBUFFER::ulReservedSpace_c )

{
    m_bInitialized    = false;

    try
    {
        m_pSwmrGuard     = std::auto_ptr<CSWMRG>( NULL );
        m_hDataAvailable = NULL;

        // First validate the expected MMF size.

        if ( m_ulPoolSize < sizeof( MEMORYPOOLHEADER ) )
        {
            ThrowMessage_m( "Memory pool is too small" );
        }

        if ( m_psMemoryPool == NULL )
        {
            ThrowMessage_m( "Invalid memory pool pointer" );
        }

        // Create our SWMR object to help with thread synchonization.

        m_pSwmrGuard = std::auto_ptr<CSWMRG>( new CSWMRG( lpszGuardName ) );

        if ( m_pSwmrGuard.get() == NULL )
        {
            ThrowMessage_m( "Can't construct SWMRG object." );
        }

        // Get the shared data write event object's handle (process relative) from its derived name.

        if ( ( m_hDataAvailable = ::CreateEvent( NULL, TRUE, FALSE, lpszDataAvailableEventName ) ) == NULL )
        {
            TRACE( _T( "Can't create data event, error code: %lu\n" ), ::GetLastError() );
            ThrowMessage_m( "Can't create our data event object" );
        }

        // Ok, so now we're correctly constructed.

        m_bInitialized = true;
    }
    catch ( LPCTSTR lpszMessage )
    {
        m_bInitialized = false;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::CCircularBuffer(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        m_bInitialized = false;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::CCircularBuffer(), unspecified exception caught\n" ) );
    }
}

template <typename tRecordHeader>
inline
CCircularBuffer<tRecordHeader>::~CCircularBuffer( void )
{
    try
    {
        // Invalidate our object.

        m_bInitialized = false;

        // Close and invalidate our data available event object.

        if ( m_hDataAvailable != NULL )
        {
            ::CloseHandle( m_hDataAvailable );
            m_hDataAvailable = NULL;
        }

        // Destroy our std::auto_ptr<> objects -- of course, we don't really need to bother.

        if ( m_pSwmrGuard.get() != NULL )
        {
            delete ( m_pSwmrGuard.release() );
            m_pSwmrGuard = std::auto_ptr<CSWMRG>( NULL );
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CCircularBuffer<tRecordHeader>::~CCircularBuffer(), unspecified exception caught.\n" ) );
    }
}

template <typename tRecordHeader>
inline
bool CCircularBuffer<tRecordHeader>::IsInitialized( void ) const
{
    return m_bInitialized;
}

template <typename tRecordHeader>
inline
bool CCircularBuffer<tRecordHeader>::InitializeHeader( void )
{
    ASSERT( m_bInitialized );

    bool bSuccess = false;          // Assume failure for now.

    // Call this member to initialize the memory pool header contents; typically done only once by the primary writer.

    try
    {
        if ( m_psMemoryPool == NULL )
        {
            ThrowMessage_m( "Invalid pointer to the memory pool" );
        }

        // Zero the header but don't bother with the circular buffer itself (for efficiency). Note: we have 

        ::memset( m_psMemoryPool, 0x00, sizeof( MEMORYPOOLHEADER ) );

        // Explicitly set the relevant header components.

        m_psMemoryPool->m_ulVersionNumber      = m_ulVersion;
        m_psMemoryPool->m_ulNumberOfCells      = m_ulNumberOfCells;
        m_psMemoryPool->m_ulCellSizeInBytes    = m_ulCellSizeInBytes;
        m_psMemoryPool->m_ulWrapPoint          = m_ulNumberOfCells - 1UL;
        m_psMemoryPool->m_ulNextSequenceNumber = 0UL;
        m_psMemoryPool->m_ulNextWritePoint     = 0UL;

        bSuccess = true;
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::InitializeMMFContents(), Unspecified exception caught!\n" ) );
    }

    return bSuccess;
}

template <typename tRecordHeader>
inline
CSWMRG * CCircularBuffer<tRecordHeader>::GetDataGuard( void )
{
    ASSERT( m_bInitialized );
    ASSERT( m_pSwmrGuard.get() != NULL );

    return ( m_pSwmrGuard.get() );
}

template <typename tRecordHeader>
inline
HANDLE CCircularBuffer<tRecordHeader>::GetWriteEvent( void )
{
    ASSERT( m_bInitialized );

    return m_hDataAvailable;
}

template <typename tRecordHeader>
inline
bool CCircularBuffer<tRecordHeader>::Write( const tRecordHeader    *psRecord, 
                                            const unsigned long    &rulTotalBytesThisRecord )
{
    ASSERT( m_bInitialized );

    bool bSuccess = false;                          // Assume failure for now.

    try
    {
        if ( rulTotalBytesThisRecord == 0UL )
        {
            bSuccess = true;                        // Nothing to do.
        }
        else
        {
            if ( psRecord == NULL )
            {
                ThrowMessage_m( "Invalid record pointer detected" );
            }

            if ( m_pSwmrGuard.get() == NULL )
            {
                ThrowMessage_m( "Pointer to SWMRG object is NULL" );
            }

            if ( m_psMemoryPool == NULL )
            {
                ThrowMessage_m( "Pointer to data pool is NULL" );
            }

            if ( ( bSuccess = WriteRecord( psRecord, rulTotalBytesThisRecord ) ) != false )
            {
                DataIsAvailable();
            }
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::Write(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::Write(), unspecified exception caught\n" ) );
    }

    return bSuccess;
}

template <typename tRecordHeader>
inline
bool CCircularBuffer<tRecordHeader>::Read(  unsigned long   &rulNextToRead,
                                            unsigned long   &rulTotalBytesThisRecord,
                                            tRecordHeader * &rpsRecord )
{
    // Notes:   1)  A return code of false can mean either: a) an error, or b) no new data is available.
    //            
    //          2)  THIS MEMBER RETURNS A POINTER DIRECLTY INTO THE MMF AND POINTING TO THE NEXT AVAILABLE RECORD TO BE READ.
    //              As such, it should be used within the m_pSwmrGuard->WaitToRead() and m_pSwmrGuard->DoneReading() block. 
    //              Failure to synchonize different threads will likely lead to data corruption in a multithreaded environment.
    //              Clients may use the operator SWMRG *() to access the appropriate sync object.
    //          
    //          3)  It would be an error to handle synchronization in this routine since the client will still be directly
    //              accessing a pointer into the MMF during which its conents could be modified by the writer.

    bool bSuccess = false;      // Assume failure for now.

    try
    {
        rulTotalBytesThisRecord = 0;
        rpsRecord               = NULL;

        if ( ! m_bInitialized )
        {
            ThrowMessage_m( "Object is not initialized" );
        }
        
        if ( m_psMemoryPool == NULL )
        {
            ThrowMessage_m( "Can't acquire pointer to MMF data" );
        }

        // Last read should always point to the beginning cell of the next sequence to be read. First, though,
        // we need to ensure we're not trying to read beyond the writer's wrap point.

        if ( rulNextToRead >= m_psMemoryPool->m_ulWrapPoint )
        {
            rulNextToRead = 0UL;
            TRACE( _T( "Specified read point is beyond the writer's wrap point; read pointer moved !\n" ) );
        }

        unsigned long       ulCellToRead = rulNextToRead;
        const unsigned long ulCellSize   = m_psMemoryPool->m_ulCellSizeInBytes;
        PCELLINDEX          psCellIndex  = &(m_psMemoryPool->m_sCellIndex[ ulCellToRead ]);

        if ( ( psCellIndex != NULL ) && ( psCellIndex->m_ulUsed != 0UL ) )
        {
            // Get the total bytes and the pointer to the beginning of the data.

            rulTotalBytesThisRecord = psCellIndex->m_TotalBytesInSequence;
            rpsRecord               = reinterpret_cast<tRecordHeader *>( &(m_pbyCells[ ulCellToRead * ulCellSize ]) );

            // Advance to the first cell in the next sequence to be read (ahead of next read).

            if ( ( rulNextToRead + psCellIndex->m_ulCellsInSequence ) >= m_psMemoryPool->m_ulNumberOfCells )
            {
                rulNextToRead = 0UL;
            }
            else
            {
                rulNextToRead += psCellIndex->m_ulCellsInSequence;
            }
                
            bSuccess = true;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess    = false;
        rulNextToRead = 0UL;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::Read(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess    = false;
        rulNextToRead = 0UL;
        TRACE( _T( "CCircularBuffer<tRecordHeader>::Read(), Unspecified exception caught!\n" ) );
    }

    if ( ! bSuccess )
    {
        rulTotalBytesThisRecord = 0UL;
        rpsRecord               = NULL;
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Internal methods (private).

template <typename tRecordHeader>
inline
bool CCircularBuffer<tRecordHeader>::WriteRecord(   const tRecordHeader     *pbyRecord,
                                                    const unsigned long    &rulTotalBytesThisRecord )
{
    bool bSuccess = false;                  // Assume failure, for now.

    ASSERT( pbyRecord               != NULL );
    ASSERT( rulTotalBytesThisRecord >  0UL  );
    ASSERT( m_psMemoryPool          != NULL );

    const unsigned long ulCellSize               = m_psMemoryPool->m_ulCellSizeInBytes;
    const unsigned long ulTotalCells             = m_psMemoryPool->m_ulNumberOfCells;
    const unsigned long ulMaxRecordSizePermitted = ulCellSize * ulTotalCells;

    ASSERT( ulMaxRecordSizePermitted > 0UL );

    if ( rulTotalBytesThisRecord == 0 )
    {
        bSuccess = true;                    // Nothing to do.
    }
    else if ( rulTotalBytesThisRecord >= ulMaxRecordSizePermitted )
    {
        bSuccess = false;

        TRACE( _T( "CCircularBuffer<tRecordHeader>::WriteRecord(), Record is "
                   "too big for MMF! Size: %lu (Max: %lu) bytes\n" ),   rulTotalBytesThisRecord,
                                                                        ulMaxRecordSizePermitted );
    }
    else
    {
        bSuccess = true;

        ASSERT( m_pSwmrGuard.get() != NULL );

        // Firstly, compute the number of cells needed to hold the record.

        const unsigned long ulCellsForThisData = ( rulTotalBytesThisRecord / ulCellSize ) + 1UL;

        try
        {
            // Acquire write exclusivity.

            m_pSwmrGuard->WaitToWrite();

            // Get the current write point and see whether data will fit between the current point and the
            // end of the MMF. If not, invalidate cells to the end of the circular buffer and set our write
            // point back to the beginning of the MMF.

            unsigned long ulWriteCell = m_psMemoryPool->m_ulNextWritePoint;

            if ( ( ulWriteCell + ulCellsForThisData ) > ulTotalCells )
            {
                // Invalidate cells to end of MMF and set write point back to beginning of MMF.
                // We also note the wrap point as being the point at which last write occured so that
                // readers don't read beyond this point.

                m_psMemoryPool->m_ulWrapPoint = ulWriteCell;

                InvalidateCells( ulWriteCell, ulTotalCells - 1UL, false );
                ulWriteCell = 0UL;
            }

            // Invalidate cells that we're about to overwrite. We must also invalidate
            // all cells in the sequence of the last cell.

            InvalidateCells( ulWriteCell, ulWriteCell + ulCellsForThisData - 1UL, true );

            // Copy the data to the MMF (can simply memcpy since we're guaranteed a contiguious cell block w/o wrap).

            ::memcpy( &(m_pbyCells[ ulWriteCell * ulCellSize ]), pbyRecord, rulTotalBytesThisRecord );

            // Set the new cell info.

            SetCellSequence( ulWriteCell, ulCellsForThisData, rulTotalBytesThisRecord );

            // Move the write pointer to the next available cell position and increment the sequence number ahead of
            // the next write operation.

            m_psMemoryPool->m_ulNextSequenceNumber = ( m_psMemoryPool->m_ulNextSequenceNumber + 1UL ) % ULONG_MAX;

            m_psMemoryPool->m_ulNextWritePoint     = ulWriteCell + ulCellsForThisData;

            if ( m_psMemoryPool->m_ulNextWritePoint >= ulTotalCells )
            {
                m_psMemoryPool->m_ulNextWritePoint = 0UL;
            }
        }
        catch ( ... )
        {
            bSuccess = false;
            TRACE( _T( "CCircularBuffer<tRecordHeader>::WriteRecord(), Unspecified exception caught!\n" ) );
            ASSERT( false );
        }

        // Relinquish (exclusive) write access.

        m_pSwmrGuard->DoneWriting();
    }

    return bSuccess;
}

template <typename tRecordHeader>
inline
void CCircularBuffer<tRecordHeader>::InvalidateCells(   const unsigned long    &rulStartCell,
                                                        const unsigned long    &rulStopCell,
                                                        const bool             &rbHandleFinalSequence )
{
    ASSERT( m_psMemoryPool != NULL );

    PCELLINDEX psCell = NULL;

    for ( unsigned long ulCell = rulStartCell; ulCell <= rulStopCell; ulCell++ )
    {
        psCell = &(m_psMemoryPool->m_sCellIndex[ ulCell ]);

        if ( ( psCell != NULL ) && ( psCell->m_ulUsed != 0UL ) )
        {
            // If it is the last specified cell, ensure we invalidate all associated cells in its sequence.

            if (   rbHandleFinalSequence         && 
                 ( ulCell       == rulStopCell ) &&
                 ( rulStartCell != rulStopCell )  )
            {
                const unsigned long ulLastSequenceNumber = psCell->m_ulSequenceNumberOfCell;
                PCELLINDEX          psNextCell           = &(m_psMemoryPool->m_sCellIndex[ ulCell + 1UL ]);

                while ( ( psNextCell->m_ulUsed != 0UL ) && 
                        ( psNextCell->m_ulSequenceNumberOfCell == ulLastSequenceNumber ) )
                {
                    psCell->m_ulUsed               = 0UL;
                    psCell->m_ulBytesInCell        = 0UL;
                    psCell->m_TotalBytesInSequence = 0UL;
                    psNextCell++;
                }
            }

            // Now invalidate the current cell; done last o that the cell's data is preserved for the above algorithm.

            psCell->m_ulUsed               = 0UL;
            psCell->m_ulBytesInCell        = 0UL;
            psCell->m_TotalBytesInSequence = 0UL;
        }
    }
}

template <typename tRecordHeader>
inline
void CCircularBuffer<tRecordHeader>::SetCellSequence(   const unsigned long    &rulStartCell,
                                                        const unsigned long    &rulNumberOfCells,
                                                        const unsigned long    &rulTotalBytesThisRecord )
{
    ASSERT( m_psMemoryPool != NULL );
    ASSERT( m_psMemoryPool->m_ulCellSizeInBytes > 0UL );

    PCELLINDEX psCell = &(m_psMemoryPool->m_sCellIndex[ rulStartCell ]);

    for ( unsigned long ulCell = rulStartCell; ulCell < ( rulStartCell + rulNumberOfCells ); ulCell++ )
    {
        psCell->m_ulUsed                 = 1UL;
        psCell->m_ulCellsInSequence      = rulNumberOfCells;
        psCell->m_ulSequenceNumberOfCell = ulCell;
        psCell->m_TotalBytesInSequence   = rulTotalBytesThisRecord;
        psCell->m_ulBytesInCell          = ( ulCell < ( rulStartCell + rulNumberOfCells - 1UL ) ) ?
                                               m_psMemoryPool->m_ulCellSizeInBytes :
                                                   rulTotalBytesThisRecord % m_psMemoryPool->m_ulCellSizeInBytes;
        psCell->m_ulSequenceNumberOfCell = m_psMemoryPool->m_ulNextSequenceNumber;

        psCell++;
    }
}

template <typename tRecordHeader>
inline
void CCircularBuffer<tRecordHeader>::DataIsAvailable( void ) const
{
    if ( m_hDataAvailable != NULL )
    {
        ::PulseEvent( m_hDataAvailable );
    }
}

#endif // !defined(AFX_CIRCULARBUFFER_H__A93F104E_7F89_4934_8FE5_FB082CAB79D1__INCLUDED_)


