// MemoryFactory.cpp : Defines the initialization routines for the DLL.
//
#include <crtdbg.h>
#include <pool.hpp>
#include <list>
#include <utility>

#include "MemoryFactory.h"
#include "stdlib.h"

#pragma warning(push)
#pragma warning(disable : 4702) 

#define MEMORY_FACTORY_USE_BLOCK_PREAMBLE

#pragma warning(pop)

class CPoolRef
{
	boost::pool<> *m_pPool ;
	boost::pool<>::size_type m_size ;

public:
	CPoolRef() : m_pPool(NULL), m_size(0) {}

	CPoolRef(boost::pool<> * const pPool, const boost::pool<>::size_type size) 
	{
		// Preconditions.
		_ASSERTE(pPool) ;
		_ASSERTE(size) ;

		m_pPool = pPool ;
		m_size = size ; 
	}

	boost::pool<> *getPool()
	{
		return m_pPool ;
	}

	boost::pool<>::size_type getSize()
	{
		return m_size ;
	}
} ;

// Typedefs
typedef boost::pool<>		TPool ;
typedef std::list<CPoolRef>	TPoolRefContainer ;

// Globals.
static TPoolRefContainer	pools ;

static TPoolRefContainer::iterator find_pool(TPoolRefContainer::iterator first, TPoolRefContainer::iterator last, TPool *pPool)
{
	// Preconditions.
	_ASSERTE(pPool) ;

	TPoolRefContainer::iterator it = first ;
	while(it != last && it->getPool() != pPool)
		++it ;

	return it ;
}

static TPoolRefContainer::iterator find_pool(TPoolRefContainer::iterator first, TPoolRefContainer::iterator last, const TPool::size_type size)
{
	// Preconditions.
	_ASSERTE(size) ;

	TPoolRefContainer::iterator it = first ;
	while(it != last && it->getSize() != size)
		++it ;

	return it ;
}

static TPoolRefContainer::iterator find_pool(TPoolRefContainer::iterator first, TPoolRefContainer::iterator last, void *pMemory)
{
	// Preconditions.
	_ASSERTE(pMemory) ;

#ifdef MEMORY_FACTORY_USE_BLOCK_PREAMBLE
	pMemory = (void *)(((int *)pMemory) - 1);
#endif

	TPoolRefContainer::iterator it = first ;
	while(it != last && !it->getPool()->is_from(pMemory))
		++it ;

	return it ;
}

TPoolHandle CALL_CONV MemoryFactory_AllocPool(unsigned long size)
{
	TPoolHandle result = NULL;

	// Preconditions.
	_ASSERTE(size) ;

#ifndef MEMORY_FACTORY_NO_MEMORY_POOLING
	TPoolRefContainer::iterator it = find_pool(pools.begin(), pools.end(), size) ;
	if(it == pools.end())
	{
#ifdef MEMORY_FACTORY_USE_BLOCK_PREAMBLE
		CPoolRef newPool(new TPool(size + sizeof(int)), size) ;
#else
		CPoolRef newPool(new TPool(size), size) ;
#endif
		pools.push_back(newPool) ;
		result = newPool.getPool() ;
	}
	else
		result = it->getPool() ;

	// Postconditions.
	_ASSERTE(result) ;
#endif

	return result ;
}

void CALL_CONV MemoryFactory_FreePool(TPoolHandle handle)
{
	TPoolRefContainer::iterator it = find_pool(pools.begin(), pools.end(), static_cast<TPool *>(handle)) ;
	if(it != pools.end())
	{
		delete it->getPool() ;
		pools.erase(it) ;
	}
}

void CALL_CONV MemoryFactory_FreePools()
{
	TPoolRefContainer::iterator it = pools.begin() ;
	TPoolRefContainer::iterator end = pools.end() ;
	while(it != end)
		delete (it++)->getPool() ;

	pools.clear() ;
}

TPoolHandle CALL_CONV MemoryFactory_FindPool(void *pMemory)
{
	TPoolHandle result = 0 ;

	TPoolRefContainer::iterator it = find_pool(pools.begin(), pools.end(), pMemory) ;
	if(it != pools.end())
		return it->getPool() ;

	return result ;
}

// *******************************************************************************

void * CALL_CONV MemoryFactory_AllocFromPool(TPoolHandle handle)
{
	void *result = 0 ;
	TPool *pPool ; 

	// Preconditions.
	_ASSERTE(handle) ;

	pPool = static_cast<TPool *>(handle) ;

	// Because we are allocating so much memory for a large scan, we need
	// to cap next_size or it grows out of control.
	if(pPool->get_next_size() > 1024)
		pPool->set_next_size(1024) ;

	result = pPool->malloc() ;
	_ASSERTE(result) ;

#ifdef MEMORY_FACTORY_USE_BLOCK_PREAMBLE
	{
		int *preamble = 0;

		preamble = (int *)result;

		// Write the size of the block at the start of the memory block.
		*preamble = find_pool(pools.begin(), pools.end(), pPool)->getSize();

		result = (void *)(preamble + 1);
	}
#endif

	// Postconditions.
	_ASSERTE(result) ;

	return result ;
}

void CALL_CONV MemoryFactory_FreeToPool(TPoolHandle handle, void *pMemory)
{
	// Preconditions.
	_ASSERTE(handle) ;
	_ASSERTE(pMemory) ;

#ifdef MEMORY_FACTORY_USE_BLOCK_PREAMBLE
	pMemory = (void *)(((int *)pMemory) - 1);
#endif

	_ASSERT(static_cast<TPool *>(handle)->is_from(pMemory)) ; 
	static_cast<TPool *>(handle)->free(pMemory) ;
}

// These functions are less efficient as they do not know which pool to allocate memory
// from or return memory to.
void * CALL_CONV MemoryFactory_Alloc(unsigned long size)
{
	void *result = NULL ;

//	char buf[500];
//	sprintf(buf, " MemoryFactory_Alloc( size %d)\n", size);
//	OutputDebugString(buf);
	// Preconditions.
	_ASSERTE(size) ;

	// Try to find a pool with the correct size.
	TPoolRefContainer::iterator it = find_pool(pools.begin(), pools.end(), size) ;
	if(it == pools.end())
	{
#ifdef MEMORY_FACTORY_USE_BLOCK_PREAMBLE
		int *preamble = 0;

		result = malloc(size + sizeof(int)) ;

		preamble = (int *)result;

		// Write the size of the block at the start of the memory block.
		*preamble = size + sizeof(int);

		result = (void *)(preamble + 1);
#else
		// There isn't one. Just malloc the memory.
		result = malloc(size) ;
#endif
	}
	else
		result = MemoryFactory_AllocFromPool(it->getPool()) ;

	// Postconditions
	_ASSERTE(result) ;

	return result ;
}

void CALL_CONV MemoryFactory_Free(void *pMemory)
{
	// Preconditions.
	_ASSERTE(pMemory) ;

	// Try to find the pool to which the memory belongs.
	TPoolRefContainer::iterator it = find_pool(pools.begin(), pools.end(), pMemory) ;

#ifdef MEMORY_FACTORY_USE_BLOCK_PREAMBLE
	pMemory = (void *)(((int *)pMemory) - 1);
#endif

	if(it == pools.end())
		// It doesn't belong to any, ie it was malloc()'ed. Just free() it.
		free(pMemory) ;
	else
		it->getPool()->free(pMemory) ;
}

void CALL_CONV MemoryFactory_PurgeMemoryPool(TPoolHandle handle)
{
	TPool *pPool ; 

	// Preconditions.
	_ASSERTE(handle) ;

	pPool = static_cast<TPool *>(handle) ;

	pPool->purge_memory() ;
}

