/*++

Copyright (c) 2000-2001  Microsoft Corporation

Module Name:

    bsfixalloc.cpp

Abstract:

    Adapted from MFC 6 SR 1 release of fixalloc.cpp.  Removed all MFC stuff.

Author:

    Stefan R. Steiner   [ssteiner]        4-10-2000

Revision History:

--*/

// fixalloc.cpp - implementation of fixed block allocator

#include "stdafx.h"
#include "bsfixalloc.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;

// #define new DEBUG_NEW
#endif


/////////////////////////////////////////////////////////////////////////////
// CBsFixedAlloc

CBsFixedAlloc::CBsFixedAlloc(UINT nAllocSize, UINT nBlockSize)
{
	ASSERT(nAllocSize >= sizeof(CNode));
	ASSERT(nBlockSize > 1);
    //wprintf( L"CBsFixedAlloc called, nAllocSize: %d, nBlockSize: %d\n", nAllocSize, nBlockSize );
	m_nAllocSize = nAllocSize;
	m_nBlockSize = nBlockSize;
	m_pNodeFree = NULL;
	m_pBlocks = NULL;
	InitializeCriticalSection(&m_protect);
}

CBsFixedAlloc::~CBsFixedAlloc()
{
	FreeAll();
	DeleteCriticalSection(&m_protect);
}

void CBsFixedAlloc::FreeAll()
{
	EnterCriticalSection(&m_protect);
	m_pBlocks->FreeDataChain();
	m_pBlocks = NULL;
	m_pNodeFree = NULL;
	LeaveCriticalSection(&m_protect);
}

void* CBsFixedAlloc::Alloc()
{
	EnterCriticalSection(&m_protect);
	if (m_pNodeFree == NULL)
	{
		CBsPlex* pNewBlock = NULL;
		try
		{
			// add another block
			pNewBlock = CBsPlex::Create(m_pBlocks, m_nBlockSize, m_nAllocSize);
            //wprintf( L"Alloc getting more core, nAllocSize: %d\n", m_nAllocSize );
		}
		catch( ... )
		{
			LeaveCriticalSection(&m_protect);
			throw;
		}

		// chain them into free list
		CNode* pNode = (CNode*)pNewBlock->data();
		// free in reverse order to make it easier to debug
		(BYTE*&)pNode += (m_nAllocSize * m_nBlockSize) - m_nAllocSize;
		for (int i = m_nBlockSize-1; i >= 0; i--, (BYTE*&)pNode -= m_nAllocSize)
		{
			pNode->pNext = m_pNodeFree;
			m_pNodeFree = pNode;
		}
	}
	ASSERT(m_pNodeFree != NULL);  // we must have something

	// remove the first available node from the free list
	void* pNode = m_pNodeFree;
	m_pNodeFree = m_pNodeFree->pNext;

	LeaveCriticalSection(&m_protect);
	return pNode;
}

void CBsFixedAlloc::Free(void* p)
{
	if (p != NULL)
	{
		EnterCriticalSection(&m_protect);

		// simply return the node to the free list
		CNode* pNode = (CNode*)p;
		pNode->pNext = m_pNodeFree;
		m_pNodeFree = pNode;
		LeaveCriticalSection(&m_protect);
	}
}


/////////////////////////////////////////////////////////////////////////////
// CBsPlex
// throws E_OUTOFMEMORY

CBsPlex* PASCAL CBsPlex::Create(CBsPlex*& pHead, UINT nMax, UINT cbElement)
{
	ASSERT(nMax > 0 && cbElement > 0);
	CBsPlex* p = (CBsPlex*) new BYTE[sizeof(CBsPlex) + nMax * cbElement];
	if ( p == NULL )    //  prefix #118827
	    throw E_OUTOFMEMORY;
	
	p->pNext = pHead;
	pHead = p;  // change head (adds in reverse order for simplicity)
	return p;
}

void CBsPlex::FreeDataChain()     // free this one and links
{
	CBsPlex* p = this;
	while (p != NULL)
	{
		BYTE* bytes = (BYTE*) p;
		CBsPlex* pNext = p->pNext;
		delete[] bytes;
		p = pNext;
	}
}