knightonline/server/shared/CircularBuffer.cpp

258 lines
5.8 KiB
C++

#include "stdafx.h"
#include "CircularBuffer.h"
/** Constructor
*/
CircularBuffer::CircularBuffer()
{
m_buffer = m_bufferEnd = m_regionAPointer = m_regionBPointer = nullptr;
m_regionASize = m_regionBSize = 0;
}
/** Destructor
*/
CircularBuffer::~CircularBuffer()
{
free(m_buffer);
}
/** Read bytes from the buffer
* @param destination pointer to destination where bytes will be written
* @param bytes number of bytes to read
* @return true if there was enough data, false otherwise
*/
bool CircularBuffer::Read(void * destination, size_t bytes)
{
// copy as much out of region a
size_t cnt = bytes;
size_t aRead = 0, bRead = 0;
if( (m_regionASize + m_regionBSize) < bytes )
return false;
// If we have both region A and region B, always "finish" off region A first, as
// this will contain the "oldest" data
if( m_regionASize > 0 )
{
aRead = (cnt > m_regionASize) ? m_regionASize : cnt;
memcpy(destination, m_regionAPointer, aRead);
m_regionASize -= aRead;
m_regionAPointer += aRead;
cnt -= aRead;
}
// Data left over? read the data from buffer B
if( cnt > 0 && m_regionBSize > 0 )
{
bRead = (cnt > m_regionBSize) ? m_regionBSize : cnt;
memcpy((char*)destination + aRead, m_regionBPointer, bRead);
m_regionBSize -= bRead;
m_regionBPointer += bRead;
cnt -= bRead;
}
// is buffer A empty? move buffer B to buffer A, to increase future performance
if( m_regionASize == 0 )
{
if( m_regionBSize > 0 )
{
// push it all to the start of the buffer.
if( m_regionBPointer != m_buffer )
memmove(m_buffer, m_regionBPointer, m_regionBSize);
m_regionAPointer = m_buffer;
m_regionASize = m_regionBSize;
m_regionBPointer = nullptr;
m_regionBSize = 0;
}
else
{
// no data in region b
m_regionBPointer = nullptr;
m_regionBSize = 0;
m_regionAPointer = m_buffer;
m_regionASize = 0;
}
}
return true;
}
void CircularBuffer::AllocateB()
{
//printf("[allocating B]\n");
m_regionBPointer = m_buffer;
}
/** Write bytes to the buffer
* @param data pointer to the data to be written
* @param bytes number of bytes to be written
* @return true if was successful, otherwise false
*/
bool CircularBuffer::Write(const void * data, size_t bytes)
{
// If buffer B exists, write to it.
if( m_regionBPointer != nullptr )
{
if( GetBFreeSpace() < bytes )
return false;
memcpy(&m_regionBPointer[m_regionBSize], data, bytes);
m_regionBSize += bytes;
return true;
}
// Otherwise, write to buffer A, or initialize buffer B depending on which has more space.
if( GetAFreeSpace() < GetSpaceBeforeA() )
{
AllocateB();
if( GetBFreeSpace() < bytes )
return false;
memcpy(&m_regionBPointer[m_regionBSize], data, bytes);
m_regionBSize += bytes;
return true;
}
else
{
if( GetAFreeSpace() < bytes )
return false;
memcpy(&m_regionAPointer[m_regionASize], data, bytes);
m_regionASize += bytes;
return true;
}
}
/** Returns the number of available bytes left.
*/
size_t CircularBuffer::GetSpace()
{
if( m_regionBPointer != nullptr )
return GetBFreeSpace();
else
{
// would allocating buffer B get us more data?
if( GetAFreeSpace() < GetSpaceBeforeA() )
{
AllocateB();
return GetBFreeSpace();
}
// or not?
return GetAFreeSpace();
}
}
/** Returns the number of bytes currently stored in the buffer.
*/
size_t CircularBuffer::GetSize()
{
return m_regionASize + m_regionBSize;
}
/** Returns the number of contiguous bytes (that can be pushed out in one operation)
*/
size_t CircularBuffer::GetContiguousBytes()
{
if( m_regionASize ) // A before B
return m_regionASize;
else
return m_regionBSize;
}
/** Removes len bytes from the front of the buffer
* @param len the number of bytes to "cut"
*/
void CircularBuffer::Remove(size_t len)
{
// remove from A first before we remove from b
size_t cnt = len;
size_t aRem, bRem;
// If we have both region A and region B, always "finish" off region A first, as
// this will contain the "oldest" data
if( m_regionASize > 0 )
{
aRem = (cnt > m_regionASize) ? m_regionASize : cnt;
m_regionASize -= aRem;
m_regionAPointer += aRem;
cnt -= aRem;
}
// Data left over? cut the data from buffer B
if( cnt > 0 && m_regionBSize > 0 )
{
bRem = (cnt > m_regionBSize) ? m_regionBSize : cnt;
m_regionBSize -= bRem;
m_regionBPointer += bRem;
cnt -= bRem;
}
// is buffer A empty? move buffer B to buffer A, to increase future performance
if( m_regionASize == 0 )
{
if( m_regionBSize > 0 )
{
// push it all to the start of the buffer.
if( m_regionBPointer != m_buffer )
memmove(m_buffer, m_regionBPointer, m_regionBSize);
m_regionAPointer = m_buffer;
m_regionASize = m_regionBSize;
m_regionBPointer = nullptr;
m_regionBSize = 0;
}
else
{
// no data in region b
m_regionBPointer = nullptr;
m_regionBSize = 0;
m_regionAPointer = m_buffer;
m_regionASize = 0;
}
}
}
/** Returns a pointer at the "end" of the buffer, where new data can be written
*/
void * CircularBuffer::GetBuffer()
{
if( m_regionBPointer != nullptr )
return m_regionBPointer + m_regionBSize;
else
return m_regionAPointer + m_regionASize;
}
/** Allocate the buffer with room for size bytes
* @param size the number of bytes to allocate
*/
void CircularBuffer::Allocate(size_t size)
{
m_buffer = (uint8*)malloc(size);
m_bufferEnd = m_buffer + size;
m_regionAPointer = m_buffer; // reset A to the start
m_bufferSize = size;
}
/** Increments the "writen" pointer forward len bytes
* @param len number of bytes to step
*/
void CircularBuffer::IncrementWritten(size_t len) // known as "commit"
{
if( m_regionBPointer != nullptr )
m_regionBSize += len;
else
m_regionASize += len;
}
/** Returns a pointer at the "beginning" of the buffer, where data can be pulled from
*/
void * CircularBuffer::GetBufferStart()
{
if( m_regionASize > 0 )
return m_regionAPointer;
else
return m_regionBPointer;
}