2010-11-20 10:37:14 +00:00
|
|
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
2009-10-27 14:38:53 +00:00
|
|
|
#include <base/system.h>
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
#include "ringbuffer.h"
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
if(pItem == m_pProduce)
|
|
|
|
m_pProduce = pItem->m_pPrev;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
if(pItem == m_pConsume)
|
|
|
|
m_pConsume = pItem->m_pPrev;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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);
|
2020-09-26 19:41:58 +00:00
|
|
|
m_Size = (Size) / sizeof(CItem) * sizeof(CItem);
|
2009-10-27 14:38:53 +00:00
|
|
|
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;
|
2011-04-13 18:37:12 +00:00
|
|
|
m_Flags = Flags;
|
2009-10-27 14:38:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *CRingBufferBase::Allocate(int Size)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int WantedSize = (Size + sizeof(CItem) + sizeof(CItem) - 1) / sizeof(CItem) * sizeof(CItem);
|
2009-10-27 14:38:53 +00:00
|
|
|
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;
|
|
|
|
|
2022-02-14 23:12:52 +00:00
|
|
|
while(true)
|
2009-10-27 14:38:53 +00:00
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
if(pBlock)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
// we have no block, check our policy and see what todo
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_Flags & FLAG_RECYCLE)
|
2009-10-27 14:38:53 +00:00
|
|
|
{
|
|
|
|
if(!PopFirst())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// okey, we have our block
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// split the block if needed
|
2020-09-26 19:41:58 +00:00
|
|
|
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;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
pNewItem->m_Free = 1;
|
|
|
|
pNewItem->m_Size = pBlock->m_Size - WantedSize;
|
|
|
|
pBlock->m_Size = WantedSize;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
if(!pNewItem->m_pNext)
|
|
|
|
m_pLast = pNewItem;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// set next block
|
2009-10-27 14:38:53 +00:00
|
|
|
m_pProduce = NextBlock(pBlock);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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;
|
2020-09-26 19:41:58 +00:00
|
|
|
return (void *)(pBlock + 1);
|
2009-10-27 14:38:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int CRingBufferBase::PopFirst()
|
|
|
|
{
|
|
|
|
if(m_pConsume->m_Free)
|
|
|
|
return 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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);
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-07-10 09:29:02 +00:00
|
|
|
// in the case that we have caught up with the produce pointer
|
2010-05-29 07:25:38 +00:00
|
|
|
// 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;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-02-14 23:12:52 +00:00
|
|
|
while(true)
|
2009-10-27 14:38:53 +00:00
|
|
|
{
|
|
|
|
pItem = PrevBlock(pItem);
|
|
|
|
if(pItem == m_pProduce)
|
|
|
|
return 0;
|
|
|
|
if(!pItem->m_Free)
|
2020-09-26 19:41:58 +00:00
|
|
|
return pItem + 1;
|
2009-10-27 14:38:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *CRingBufferBase::Next(void *pCurrent)
|
|
|
|
{
|
|
|
|
CItem *pItem = ((CItem *)pCurrent) - 1;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-02-14 23:12:52 +00:00
|
|
|
while(true)
|
2009-10-27 14:38:53 +00:00
|
|
|
{
|
|
|
|
pItem = NextBlock(pItem);
|
|
|
|
if(pItem == m_pProduce)
|
|
|
|
return 0;
|
|
|
|
if(!pItem->m_Free)
|
2020-09-26 19:41:58 +00:00
|
|
|
return pItem + 1;
|
2009-10-27 14:38:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *CRingBufferBase::First()
|
|
|
|
{
|
|
|
|
if(m_pConsume->m_Free)
|
|
|
|
return 0;
|
2020-09-26 19:41:58 +00:00
|
|
|
return (void *)(m_pConsume + 1);
|
2009-10-27 14:38:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *CRingBufferBase::Last()
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
return Prev(m_pProduce + 1);
|
2009-10-27 14:38:53 +00:00
|
|
|
}
|