#include "Gos.pch"
#include "Heap.h"

GosHeap *		(*GosHeap::Create)( _TCHAR *, DWORD ) = Heap::Create;
void			(*GosHeap::PushCurrent)( GosHeap * ) = Heap::PushCurrent;
void			(*GosHeap::PopCurrent)() = Heap::PopCurrent;
GosHeap *		(*GosHeap::Current)() = Heap::Current;
void			(*GosHeap::Free)( void *pData ) = Heap::Free;
void			(*GosHeap::Shutdown)() = Heap::Shutdown;
DWORD			(*GosHeap::GetID)() = Heap::GetID;

GosHeap *		Heap::s_apHeapStack[20] = { NULL };
int				Heap::s_HeapStackDepth=0;
Heap *			Heap::s_pFirstHeap = NULL;
DWORD			Heap::s_NextAllocationId=1;

const _TCHAR *AllocationSignature = _T("Gos2Allocation");
const _TCHAR *FreedSignature = _T("FreedGos2Allocation");
const _TCHAR *AllocationAnchorSignature = _T("Gos2Heap");


void Heap::Shutdown()
{
	SPEW(_T("Heaps"),(_T("Heap::Shutdown()\n")));
	while( s_pFirstHeap )
		delete s_pFirstHeap;
}


GosHeap * Heap::Create( _TCHAR *pName, DWORD MaxBytes )
{
	SPEW(_T("Heaps"),(_T("Heap::Create(%s)\n"),pName));

	Heap *pHeap = new Heap();
	if( !pHeap )
		STOP(("Failed to create empty child heap"));
	_tcscpy( pHeap->m_Name, pName );
	if( s_pFirstHeap )
	{
		s_pFirstHeap->m_pPrevHeap = pHeap;
	}
	pHeap->m_pNextHeap = s_pFirstHeap;
	pHeap->m_pPrevHeap = NULL;
	s_pFirstHeap = pHeap;
	pHeap->m_MaximumSize = MaxBytes ? MaxBytes : ~0L;
	pHeap->m_BytesAllocated = 0;
	pHeap->m_Anchor.pPrev = &pHeap->m_Anchor;
	pHeap->m_Anchor.pNext = &pHeap->m_Anchor;
	pHeap->m_Anchor.SizeBytes = 0;
	pHeap->m_Anchor.Signature = AllocationAnchorSignature;
	pHeap->m_Anchor.pHeap = pHeap;

	return (GosHeap *)pHeap;
}

Heap::~Heap()
{
	SPEW(_T("Heaps"),(_T("Heap::~Heap(%s)\n"),m_Name));

	VerifyEmpty();

	for( Allocation *pAllocation = m_Anchor.pNext;
		pAllocation->Signature != AllocationAnchorSignature;
		pAllocation = m_Anchor.pNext )
	{
		Free( (void *)(pAllocation+1) );
	}

	if( s_pFirstHeap == this )
		s_pFirstHeap = m_pNextHeap;
	else
		m_pPrevHeap->m_pNextHeap = m_pNextHeap;

	if( m_pNextHeap )
		m_pNextHeap->m_pPrevHeap = m_pPrevHeap;
}

void Heap::VerifyEmpty()
{
	SPEW(_T("Heaps"),(_T("Heap::VerifyEmpty(%s)\n"),m_Name));
	if( m_BytesAllocated == 0 )
		return;
	DWORD numAllocations = 0;
	Allocation *pAllocation = m_Anchor.pNext;
	while( pAllocation->Signature != AllocationAnchorSignature )
	{
		if( pAllocation->Signature == AllocationSignature )
		{
			numAllocations++;
			SPEW(_T("LeakDetail"),(_T("Leak of %d bytes in heap %s at 0x%08x (%s) Allocation ID 0x%08x\n"),
				pAllocation->SizeBytes,
				m_Name,
				pAllocation+1,
				((pAllocation->pDescription)?(pAllocation->pDescription):(_T("Un-named"))),
				pAllocation->id));
		}
		pAllocation = pAllocation->pNext;
	}
	SPEW(_T("Error"),(_T("Heap %s Leaks %d bytes in %d allocations avg size %d bytes\n"),m_Name,m_BytesAllocated,numAllocations,m_BytesAllocated/numAllocations));
}

void *Heap::Malloc( DWORD SizeBytes, const TCHAR *pDescription )
{
	SPEW(_T("Heaps"),(_T("Heap::Malloc( Heap %s %d Bytes )\n"),m_Name,SizeBytes));
	ASSERT( SizeBytes != 0 );

	if( SizeBytes + m_BytesAllocated > m_MaximumSize )
	{
		STOP(("Attempting to allocate beyond maximum size for heap"));
	}

	m_BytesAllocated += SizeBytes;

	Allocation *pAllocation = (Allocation *)malloc( SizeBytes + sizeof(Allocation) );

	ASSERT( pAllocation != NULL );

	pAllocation->pPrev = &m_Anchor;
	pAllocation->pNext = m_Anchor.pNext;
	m_Anchor.pNext = pAllocation;
	pAllocation->pNext->pPrev = pAllocation;
	pAllocation->Signature = AllocationSignature;
	pAllocation->SizeBytes = SizeBytes;
	pAllocation->pDescription = pDescription;
	pAllocation->pHeap = (GosHeap *)this;
	pAllocation->id = s_NextAllocationId++;
	SPEW(_T("Heaps"),(_T("Allocation at 0x%08x Data at 0x%08x\n"),pAllocation,pAllocation+1));

	return (void *)(++pAllocation);
}

void Heap::Free( void *pData )
{
	SPEW(_T("Heaps"),(_T("Heap::Free( pData 0x%08x )\n"),pData));

	Allocation *pAllocation = ((Allocation *)pData)-1;

	ASSERT( pAllocation->Signature == AllocationSignature );

	pAllocation->pHeap->FreeLocal( pData );
}

DWORD Heap::GetID( void *pData )
{
	SPEW(_T("Heaps"),(_T("Heap::GetID( pData 0x%08x )\n"),pData));

	Allocation *pAllocation = ((Allocation *)pData)-1;

	ASSERT( pAllocation->Signature == AllocationSignature );

	return pAllocation->pHeap->GetIDLocal( pData );
}

DWORD Heap::GetIDLocal( void *pData )
{
	Allocation *pAllocation = ((Allocation *)pData)-1;
	SPEW(_T("Heaps"),(_T("Heap::GetIDLocal( Heap %s pData 0x%08x pAllocation 0x%08x ID 0x%08x )\n"),m_Name,pData,pAllocation,pAllocation->id));

	ASSERT( pAllocation->pHeap == this );
	ASSERT( pAllocation->Signature == AllocationSignature );

	return pAllocation->id;
}



void Heap::FreeLocal( void *pData )
{
	Allocation *pAllocation = ((Allocation *)pData)-1;
	SPEW(_T("Heaps"),(_T("Heap::FreeLocal( Heap %s pData 0x%08x pAllocation 0x%08x )\n"),m_Name,pData,pAllocation));

	ASSERT( pAllocation->pHeap == this );
	ASSERT( pAllocation->Signature == AllocationSignature );

	pAllocation->Signature = FreedSignature;
	pAllocation->pPrev->pNext = pAllocation->pNext;
	if( pAllocation->pNext )
		pAllocation->pNext->pPrev = pAllocation->pPrev;

	m_BytesAllocated -= pAllocation->SizeBytes;

	free( (void *)pAllocation );
}


void Heap::PushCurrent( GosHeap *pHeap )
{
	SPEW(_T("Heaps"),(_T("Heap::PushCurrent( Heap %s )\n"),pHeap->Name() ));

	s_apHeapStack[++s_HeapStackDepth] = pHeap;
}

void Heap::PopCurrent()
{
	SPEW(_T("Heaps"),(_T("Heap::PopCurrent()\n") ));
	s_HeapStackDepth--;
}

GosHeap *Heap::Current()
{
	return s_apHeapStack[s_HeapStackDepth];
}

void* __cdecl operator new (size_t Size, GosHeap *pHeap)
{
	SPEW(_T("Heaps"),(_T("New using Heap %s\n"), pHeap->Name() ));
	return GosMalloc( Size , pHeap );
}
