ddnet/src/engine/shared/ringbuffer.cpp

193 lines
3.7 KiB
C++
Raw Normal View History

2009-10-27 14:38:53 +00:00
#include <base/system.h>
2010-05-29 07:25:38 +00:00
#include "ringbuffer.h"
2009-10-27 14:38:53 +00:00
CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem)
{
if(pItem->m_pNext)
return pItem->m_pNext;
return m_pFirst;
}
CRingBufferBase::CItem *CRingBufferBase::PrevBlock(CItem *pItem)
{
if(pItem->m_pPrev)
return pItem->m_pPrev;
return m_pLast;
}
CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem)
{
2010-05-29 07:25:38 +00:00
// make sure that this block and previous block is free
2009-10-27 14:38:53 +00:00
if(!pItem->m_Free || !pItem->m_pPrev || !pItem->m_pPrev->m_Free)
return pItem;
2010-05-29 07:25:38 +00:00
// merge the blocks
2009-10-27 14:38:53 +00:00
pItem->m_pPrev->m_Size += pItem->m_Size;
pItem->m_pPrev->m_pNext = pItem->m_pNext;
2010-05-29 07:25:38 +00:00
// fixup pointers
2009-10-27 14:38:53 +00:00
if(pItem->m_pNext)
pItem->m_pNext->m_pPrev = pItem->m_pPrev;
else
m_pLast = pItem->m_pPrev;
if(pItem == m_pProduce)
m_pProduce = pItem->m_pPrev;
if(pItem == m_pConsume)
m_pConsume = pItem->m_pPrev;
2010-05-29 07:25:38 +00:00
// return the current block
2009-10-27 14:38:53 +00:00
return pItem->m_pPrev;
}
void CRingBufferBase::Init(void *pMemory, int Size, int Flags)
{
mem_zero(pMemory, Size);
m_Size = (Size)/sizeof(CItem)*sizeof(CItem);
m_pFirst = (CItem *)pMemory;
m_pFirst->m_Free = 1;
m_pFirst->m_Size = m_Size;
m_pLast = m_pFirst;
m_pProduce = m_pFirst;
m_pConsume = m_pFirst;
m_Flags = Flags;
}
void *CRingBufferBase::Allocate(int Size)
{
int WantedSize = (Size+sizeof(CItem)+sizeof(CItem)-1)/sizeof(CItem)*sizeof(CItem);
CItem *pBlock = 0;
2010-05-29 07:25:38 +00:00
// check if we even can fit this block
2009-10-27 14:38:53 +00:00
if(WantedSize > m_Size)
return 0;
while(1)
{
2010-05-29 07:25:38 +00:00
// check for space
2009-10-27 14:38:53 +00:00
if(m_pProduce->m_Free)
{
if(m_pProduce->m_Size >= WantedSize)
pBlock = m_pProduce;
else
{
2010-05-29 07:25:38 +00:00
// wrap around to try to find a block
2009-10-27 14:38:53 +00:00
if(m_pFirst->m_Free && m_pFirst->m_Size >= WantedSize)
pBlock = m_pFirst;
}
}
if(pBlock)
break;
else
{
2010-05-29 07:25:38 +00:00
// we have no block, check our policy and see what todo
2009-10-27 14:38:53 +00:00
if(m_Flags&FLAG_RECYCLE)
{
if(!PopFirst())
return 0;
}
else
return 0;
}
}
2010-05-29 07:25:38 +00:00
// okey, we have our block
2009-10-27 14:38:53 +00:00
2010-05-29 07:25:38 +00:00
// split the block if needed
if(pBlock->m_Size > WantedSize+(int)sizeof(CItem))
2009-10-27 14:38:53 +00:00
{
CItem *pNewItem = (CItem *)((char *)pBlock + WantedSize);
pNewItem->m_pPrev = pBlock;
pNewItem->m_pNext = pBlock->m_pNext;
if(pNewItem->m_pNext)
pNewItem->m_pNext->m_pPrev = pNewItem;
pBlock->m_pNext = pNewItem;
pNewItem->m_Free = 1;
pNewItem->m_Size = pBlock->m_Size - WantedSize;
pBlock->m_Size = WantedSize;
if(!pNewItem->m_pNext)
m_pLast = pNewItem;
}
2010-05-29 07:25:38 +00:00
// set next block
2009-10-27 14:38:53 +00:00
m_pProduce = NextBlock(pBlock);
2010-05-29 07:25:38 +00:00
// set as used and return the item pointer
2009-10-27 14:38:53 +00:00
pBlock->m_Free = 0;
return (void *)(pBlock+1);
}
int CRingBufferBase::PopFirst()
{
if(m_pConsume->m_Free)
return 0;
2010-05-29 07:25:38 +00:00
// set the free flag
2009-10-27 14:38:53 +00:00
m_pConsume->m_Free = 1;
2010-05-29 07:25:38 +00:00
// previous block is also free, merge them
2009-10-27 14:38:53 +00:00
m_pConsume = MergeBack(m_pConsume);
2010-05-29 07:25:38 +00:00
// advance the consume pointer
2009-10-27 14:38:53 +00:00
m_pConsume = NextBlock(m_pConsume);
while(m_pConsume->m_Free && m_pConsume != m_pProduce)
{
m_pConsume = MergeBack(m_pConsume);
m_pConsume = NextBlock(m_pConsume);
}
2010-05-29 07:25:38 +00:00
// in the case that we have catched up with the produce pointer
// we might stand on a free block so merge em
2009-10-27 14:38:53 +00:00
MergeBack(m_pConsume);
return 1;
}
void *CRingBufferBase::Prev(void *pCurrent)
{
CItem *pItem = ((CItem *)pCurrent) - 1;
while(1)
{
pItem = PrevBlock(pItem);
if(pItem == m_pProduce)
return 0;
if(!pItem->m_Free)
return pItem+1;
}
}
void *CRingBufferBase::Next(void *pCurrent)
{
CItem *pItem = ((CItem *)pCurrent) - 1;
while(1)
{
pItem = NextBlock(pItem);
if(pItem == m_pProduce)
return 0;
if(!pItem->m_Free)
return pItem+1;
}
}
void *CRingBufferBase::First()
{
if(m_pConsume->m_Free)
return 0;
return (void *)(m_pConsume+1);
}
void *CRingBufferBase::Last()
{
return Prev(m_pProduce+1);
}