#pragma once

#include "MemoryException.h"
#include "MemoryStructs.h"

namespace Combustion
{
	struct Heap_Impl;

	class Heap
	{
	public:
		/*	Creates a Heap that manages the provided memory
			pMemory: pointer to the memory to manage
			size:	 size of pMemory
			
			Optional Params (used for more verbose debugging data):
			line:	 line number of the call to create the heap
			filename:filename of the line with the call to create the heap
			tag:	 an integer representing a 4 character string to mark the memory

			throws MemoryException for failures
		*/
		Heap(void* pMemory, size_t size, int line = 0, char* filename = 0, int tag = 0);

		/*  Destroys the Heap
			Note: Does not call delete on the pMemory provided during the Heaps construction
		*/
		~Heap();

		/*	Allocates at least the amount of memory specified of the given type
		and returns a typed pointer
			size: Amount of memory requested
			
			Optional Params (used for more verbose debugging data):
			line:	 line number of the call to allocate memory
			filename:filename of the line with the call to allocate memory
			tag:	 an integer representing a 4 character string to allocate memory

			throws MemoryException for failures
		*/
		template<typename T>
		T* Alloc(size_t size, int line = 0, char* filename = 0, int tag = 0)
		{ return reinterpret_cast<T*>( Alloc(size, line, filename, tag) ); }
		
		/*
			Frees the memory allocated at the specified pointer
			pMemory: A pointer to the beginning of a block of memory allocated by the heap
			
			Optional Params (used for more verbose debugging data):
			line:	 line number of the call to free memory
			filename:filename of the line with the call to free memory
			
			throws MemoryException for failures
		*/
		void Free(void* pMemory, int line = 0, char* filename = 0);

		/*
			Creates a new Heap inside the memory allocated for the Heap
			size: Minimum requested size of the new Heap

			Optional Params (used for more verbose debugging data):
			line:	 line number of the call to create the heap
			filename:filename of the line with the call to create the heap
			
			throws MemoryException for failures
		*/
		Heap* SubHeap(size_t size, int line = 0, char* filename = 0);

		/*
			Frees the SubHeap pointed to by the provided pointer
			heap: Pointer to the Heap to free

			Optional Params (used for more verbose debugging data):
			line:	 line number of the call to free the heap
			filename:filename of the line with the call to free the heap
			
			throws MemoryException for failures
		*/
		void FreeSubHeap(Heap* heap, int line = 0, char* filename = 0);

		/* Merges block with sourrounding free blocks of memory. If pMemory==0, 
		entire heap is coalesced */
		void Coalesce(void *pMemory = 0);	

		/* Checks all guard blocks in heap for changes. Returns true if Heap is
		valid, false otherwise */
		bool Validate();

		/* Returns size of all free memory if checkFree = true. Otherwise returns
		size of allocated memory */
		size_t TotalMemory(bool checkFree);

		/* Returns size of largest free block in the Heap */
		size_t LargestFreeBlock();

		/* Checks if the block is allocated by the Heap and checks if the block is corrupted*/
		bool IsInvalid(void *pMemory);

		/* Returns 0-1 value for fragmentation ratio. 0=no fragmentation 1=max fragmentation */
		float ExternalFragmentation();

		/* Returns 0-1 value for fragmentation ratio. 0=no fragmentation 1=max fragmentation */
		float InternalFragmentation();

		/* Returns the largest amount of memory ever stored in allocated blocks at once */
		int GetMaxMemoryUsage();

		/* prints recorded statistics for the heap */
		void PrintHeapStatistics();

		//=====================================================================
		//	Helper Debug Functions				  
		//=====================================================================
		
		/* Returns the number of unfreed blocks */
		int NumLeaks();

		/* Prints the allocation number, file, line, and tag (if provided) of any 
		unallocated memory */
		void PrintLeaks();

		/* Returns oldest allocated block in debug. returns 0 in release */
		void* OldestBlock();

		/* Prints the number of bytes stored in blocks marked by the given tag */
		size_t GetUsageFromTag(int tag);

	private:
		void* Alloc(size_t size, int line = 0, char* filename = 0, int tag = 0);
		
		Heap_Impl* heap;
	};
}
#include "Heap.h"
#include "Heap_Impl.cpp"
#include <cassert>
#include <memory>
#include <iostream>

using namespace std;

namespace Combustion
{
	int g_NextAllocNum = 1;
	int GetAllocationNumber() { return g_NextAllocNum++; }

	Heap::Heap(void* pMemory, size_t size, int line /* = 0*/, char* filename /* = 0*/,
			   int tag /* = 0*/)
	{
		if(!pMemory) throw MemoryException("Heap Initialized With Null Data", line, filename);
		if(size < sizeof(Heap)) throw MemoryException("Heap Size Too Small", line, filename);

		heap = new(pMemory) Heap_Impl(size);
		#ifdef _DEBUG
			heap->allocNum = GetAllocationNumber();
		#endif
	}

	Heap::~Heap()
	{
		heap->~Heap_Impl();
	}

#define FIND_SUITABLE_FREE while(current != 0) { \
			if(!current->isAllocated && current->size >= correctSize) break;\
			current = current->nextBlock; }

	void* Heap::Alloc(size_t size, int line /* = 0*/, char* filename /* = 0*/,
					  int tag /* = 0*/)
	{
		size_t correctSize = size + GUARD_SIZE;//BLOCK_HEADER_AND_GUARD;

		//First-fit allocation (for speed)
		MemoryHeader* current = (MemoryHeader*) (reinterpret_cast<char*>(heap) +
		sizeof(Heap_Impl));

		FIND_SUITABLE_FREE

		if(!current)
		{
			Coalesce();	//if no suitable block found, coalesce to try to find one
			FIND_SUITABLE_FREE //try again
			if(!current) throw MemoryException("Out of Memory", line, filename);
		}

		heap->Split(current, size); //if no split is needed, none is made
		
		#ifdef _DEBUG
			heap->stats.numAllocs++;
			current->allocNum = GetAllocationNumber();
			current->lineNum = line;
			current->file = filename;
			current->tag = tag;
		#endif
		
		return reinterpret_cast<void*>( (reinterpret_cast<char*>(current) +
		sizeof(MemoryHeader)) );
	}

	Heap* Heap::SubHeap(size_t size, int line /* = 0*/, char* filename /* = 0*/)
	{
		//allocate enough room for everything	HeapObj Heap_Impl MemoryHeader size
		size_t correctSize = sizeof(Heap) + sizeof(Heap_Impl) + sizeof(MemoryHeader) + size;

		Heap* subheap = reinterpret_cast<Heap*>( Alloc(correctSize, line, filename) );
		void* subheapData = reinterpret_cast<void*>( reinterpret_cast<char*>(subheap) +
		sizeof(Heap) );
		subheap = new(subheap) Heap(subheapData, size, line, filename);
		
		return subheap;
	}

	void Heap::FreeSubHeap(Heap* subheap, int line /*= 0*/, char* filename /*= 0*/)
	{
		subheap->~Heap();					//delete subeap
		Free(subheap, line, filename);		//free data in outer heap
	}

	void Heap::Free(void* pMemory, int line /*= 0*/, char* filename /*= 0*/)
	{
		if(!pMemory) throw MemoryException("Null Pointer", line, filename);
		if(IsInvalid(pMemory)) throw MemoryException("Invalid Pointer in Free",
		line, filename);

		MemoryHeader* memHeader = (MemoryHeader*) (reinterpret_cast<char*>(pMemory) -
		sizeof(MemoryHeader));

		if(!memHeader->isAllocated)	throw MemoryException("Double Free", line, filename);

		memHeader->isAllocated = false;
		memHeader->guardData = _MAGIC;

		int* endGuard = (int*) (reinterpret_cast<char*>(pMemory) + memHeader->size);
		*endGuard = _MAGIC;

		//the guard block now counts towards the block size
		memHeader->size += GUARD_SIZE; 

		#ifdef _DEBUG
			heap->stats.numDeallocs++;
		#endif

		heap->numAllocatedBlocks--;
		heap->currUsage -= memHeader->size;
	}

	void Heap::Coalesce(void *pMemory)
	{
		if(pMemory)
		{//only coalesce immediately surrounding blocks
			MemoryHeader* memHeader = (MemoryHeader*) (reinterpret_cast<char*>(pMemory) -
			sizeof(MemoryHeader));

			//walk left until a non-free block is hit
			MemoryHeader* start = memHeader;
			while(start->prevBlock != 0 && !start->prevBlock->isAllocated)
				start = start->prevBlock;

			//march right, coalescing until a non-free block is hit
			while(start->nextBlock != 0 && !start->nextBlock->isAllocated)
				heap->MergeWithNextBlock(start);
		}
		else
		{//coalesce all free blocks in the heap...
			MemoryHeader* memHeader = (MemoryHeader*) (reinterpret_cast<char*>(heap) +
			sizeof(Heap_Impl));

			while(memHeader != 0)
			{
				if(!memHeader->isAllocated && memHeader->nextBlock != 0 &&
				!memHeader->nextBlock->isAllocated)
					heap->MergeWithNextBlock(memHeader);	//merge, but don't move memHeader
				else
					memHeader = memHeader->nextBlock; //don't merge, but move memHeader
			}
		}
	}

	size_t Heap::TotalMemory(bool checkFree)
	{
		//Walk through the memory and count the size of the free blocks
		MemoryHeader* current = (MemoryHeader*)(reinterpret_cast<char*>(heap) +
		sizeof(Heap_Impl));

		size_t totalFreeMem = 0;

		while(current != 0)
		{
			if( (checkFree && !current->isAllocated) ||
				(!checkFree && current->isAllocated) )
				totalFreeMem += current->size;

			current = current->nextBlock;
		}

		return totalFreeMem;
	}

	size_t Heap::LargestFreeBlock()
	{
		//Walk through the memory and find the biggest free
		MemoryHeader* current = (MemoryHeader*)(reinterpret_cast<char*>(heap) +
		sizeof(Heap_Impl));

		size_t largestFree = 0;

		while(current != 0)
		{
			if(!current->isAllocated && current->size > largestFree)
				largestFree = current->size;

			current = current->nextBlock;
		}

		return largestFree;
	}

	void* Heap::OldestBlock()
	{
		#ifndef _DEBUG
				return 0;
		#else
			//Walk through the memory and find the earliest allocation
			MemoryHeader* current = (MemoryHeader*)(reinterpret_cast<char*>(heap) +
			sizeof(Heap_Impl));

			size_t oldest = INT_MAX;
			void* pOldest = 0;

			while(current != 0)
			{
				if(current->isAllocated && current->allocNum < oldest)
				{
					oldest = current->allocNum;
					pOldest = reinterpret_cast<void*>( reinterpret_cast<char*>(current) +
					sizeof(MemoryHeader));
				}

				current = current->nextBlock;
			}
			return pOldest;
		#endif
	}

	bool Heap::Validate()
	{
		//walk entire heap, checking each block
		MemoryHeader* memHeader = reinterpret_cast<MemoryHeader*>(
									(reinterpret_cast<char*>(heap) + sizeof(Heap_Impl)) );
		while(memHeader != 0)
		{
			if(memHeader->guardData != _MAGIC)
				return false;
			if(memHeader->isAllocated)
			{
				int* endGuard = (int*)(reinterpret_cast<char*>(memHeader) +
				sizeof(MemoryHeader) + memHeader->size);
					if(*endGuard != memHeader->guardData)
						return false;
			}

			memHeader = memHeader->nextBlock;
		}

		return true;
	}

	bool Heap::IsInvalid(void* pMemory)
	{
		if(pMemory == 0) return true;

		//check if the address is in the range of the heap
		void* start = reinterpret_cast<void*>(heap);
		void* end = (void*) (reinterpret_cast<char*>(heap) +
		sizeof(Heap_Impl) + heap->size);
		if(pMemory <= start || pMemory >= end)
			return true;

		MemoryHeader* memHeader = (MemoryHeader*) (reinterpret_cast<char*>(pMemory) -
		sizeof(MemoryHeader));

		//check to make sure the pointer was allocated
		if(!memHeader->isAllocated)
			return true;

		//check the guard blocks
		if(memHeader->guardData != _MAGIC)
			return true;
		if(memHeader->isAllocated)
		{
			int* endGuard = (int*)(reinterpret_cast<char*>(pMemory) +
			memHeader->size);
			if(*endGuard != memHeader->guardData)
				return true;
		}

		return false;
	}

	void Heap::PrintHeapStatistics()
	{
		cout << "===================================\n";
#ifdef _DEBUG
		cout << "Number of Allocations: " << heap->stats.numAllocs << endl;
		cout << "Number of Deallocations: " << heap->stats.numDeallocs << endl;
#endif
		cout << "Number of Blocks: " << heap->numTotalBlocks << endl;
		cout << "Number of Alloced: " << heap->numAllocatedBlocks << endl;
		cout << "Number of Free: " << heap->numTotalBlocks - heap->numAllocatedBlocks << endl;
		cout << "===================================\n" << endl;
	}

	float Heap::ExternalFragmentation()
	{
		int totalFreeBlocks = heap->numTotalBlocks - heap->numAllocatedBlocks;
		return 1.0f - (LargestFreeBlock() / totalFreeBlocks);
	}

	float Heap::InternalFragmentation()
	{
		return float(heap->numTotalBlocks * sizeof(MemoryHeader)) /
		float(heap->size);
	}

	size_t Heap::GetUsageFromTag(int tag)
	{
		#ifdef _DEBUG
				//Walk through the memory and count the size of the free blocks
				MemoryHeader* current = (MemoryHeader*)(reinterpret_cast<char*>(heap) +
				sizeof(Heap_Impl));
		
				size_t totalUsed = 0;
		
				while(current != 0)
				{
					if(current->isAllocated && current->tag == tag)
						totalUsed += current->size;
		
					current = current->nextBlock;
				}
		
				return totalUsed;
		#else
				return 0;
		#endif
	}

	int Heap::NumLeaks()
	{
		#ifdef _DEBUG
					return heap->stats.numAllocs - heap->stats.numDeallocs;
		#else
				return false;
		#endif
	}

	void Heap::PrintLeaks()
	{
		#ifdef _DEBUG
			
		//walk entire heap, checking each block
		MemoryHeader* memHeader = reinterpret_cast<MemoryHeader*>(
									(reinterpret_cast<char*>(heap) + sizeof(Heap_Impl)) );
		while(memHeader != 0)
		{
			if(memHeader->isAllocated)
			{
				cout << "Leak in file " << memHeader->file << " on line " <<
				memHeader->lineNum << ".\n";
				cout << "Allocation number: " << memHeader->allocNum << ".\n";
				if(memHeader->tag != 0) 
				{
					char tag[4];
					memcpy(tag, &(memHeader->tag), 4);
					cout << "Tagged As: ";
					for(int i = 3; i >= 0; --i)	//tag is backwards, so print in reverse
						cout << tag[i];
					cout <<"\n\n";
				}
			}

			memHeader = memHeader->nextBlock;
		}

		#endif
	}

	int Heap::GetMaxMemoryUsage()
	{
		return heap->maxUsage;
	}
}
#include "MemoryException.h"
#include "MemoryStructs.h"
#include <cassert>

namespace Combustion
{
const int _MAGIC = 0xBAADF00D;
#define GUARD_SIZE 4
#define TRUE_MIN_BLOCK_SIZE sizeof(MemoryHeader) + GUARD_SIZE + 4 
#define BLOCK_HEADER_AND_GUARD sizeof(MemoryHeader) + GUARD_SIZE

	struct MemoryHeader
	{
		#ifdef _DEBUG
				unsigned int allocNum;		//number of alloc (used to report unfreed memory)
				int lineNum;				//line that called Alloc
				int tag;					//block tag information
				char* file;					//file of line that called Alloc
		#endif

		size_t size;			//size of data in the block (not incl. guard block/header size)
		bool isAllocated;		//marks whether block is free or allocated
		MemoryHeader* prevBlock;//pointer to prev block in heap
		MemoryHeader* nextBlock;//pointer to next block in heap
		int guardData;			//contains begining guard block data

		MemoryHeader(size_t memsize, int guard, bool isalloced, int memoryTag = 0) 
			: size(memsize), isAllocated(isalloced), prevBlock(0), nextBlock(0),
			  guardData(guard)
		{
			#ifdef _DEBUG
				tag = memoryTag;
			#endif
		}
	};

	struct Heap_Impl
	{
		size_t size;				//size of the entire heap
		int currUsage;				//current amount allocated
		int maxUsage;				//largest amount allocated
		int numAllocatedBlocks;		//number of currently allocated blocks
		int numTotalBlocks;			//number of all blocks (free and allocated)

		#ifdef _DEBUG
				unsigned int allocNum;//allocation number
				HeapStats stats;	  //struct that holds debugging stats about heap usage
		#endif

		/*
			Constructs the Heap Header and creates a free block spanning all memory 
			not taken up by the Heap Header
		*/
		Heap_Impl(size_t heapSize, int line = 0, char* filename = 0) 
			: size(heapSize - sizeof(Heap_Impl)), numAllocatedBlocks(0),
			  numTotalBlocks(1), currUsage(0), maxUsage(0)
		{
			size_t minSize = sizeof(Heap_Impl) + sizeof(MemoryHeader);
			if(heapSize < minSize) throw MemoryException("Out of Memory", line, filename);

			//Create Header for a free block
			MemoryHeader* pFirstFree = (MemoryHeader*) (reinterpret_cast<char*>(this) +
			sizeof(Heap_Impl));
			pFirstFree = new(pFirstFree) MemoryHeader( heapSize - minSize, 0xBAADF00D, false );
			
			#ifdef _DEBUG
				pFirstFree->lineNum = line;
				pFirstFree->file = filename;
				stats = HeapStats();
			#endif
		}

		/*  Attempts to split a free block to make an allocated block of minSize and a free
			block for the remaining data.
			In the case where the resulting free block is too small to hold a header, the block
			is not split, and the allocated block is given the remainder.

			Allocated blocks are marked at the end with a guard block marker
		*/
		void Split(void* pHeader, size_t minSize)
		{
			MemoryHeader* memHeader = reinterpret_cast<MemoryHeader*>(pHeader);

			assert(memHeader->size >= minSize);
			assert(memHeader->isAllocated == false);

			//Check if there is enough leftover to justify split
			if(memHeader->size > TRUE_MIN_BLOCK_SIZE)
			{	//can split
				size_t originalSize = sizeof(MemoryHeader) + memHeader->size;

				//create first header
				memHeader->isAllocated = true;
				memHeader->size = minSize;
				//write magic marker at end
				int* ptr = reinterpret_cast<int*>( (reinterpret_cast<char*>(memHeader) +
				sizeof(MemoryHeader) + memHeader->size) );
				*ptr = _MAGIC;
				numAllocatedBlocks++;
				numTotalBlocks++;
				
				//create second header
				MemoryHeader* freeHeader = reinterpret_cast<MemoryHeader*>(
					(reinterpret_cast<char*>(memHeader) + sizeof(MemoryHeader) +
					memHeader->size + GUARD_SIZE) );
				
				int freeBlockSize = originalSize - (sizeof(MemoryHeader) * 2 + GUARD_SIZE +
				memHeader->size);
				freeHeader = new(freeHeader) MemoryHeader(freeBlockSize, _MAGIC, false);
				
				//repair pointers
				freeHeader->prevBlock = memHeader;
				freeHeader->nextBlock = memHeader->nextBlock;
				memHeader->nextBlock = freeHeader;
			}
			else
			{	//can't split, return entire block
				memHeader->isAllocated = true;
				int* ptr = (int*) (reinterpret_cast<char*>(memHeader) + sizeof(MemoryHeader) +
				memHeader->size);
				*ptr = _MAGIC;
				numAllocatedBlocks++;
			}
			currUsage += memHeader->size;
			if(currUsage > maxUsage) 
				maxUsage = currUsage;
		}

		/*
			Coalesces the block pointed to by pHeader with the block on its right
		*/
		void MergeWithNextBlock(void* pHeader)
		{
			MemoryHeader* currHeader = reinterpret_cast<MemoryHeader*>(pHeader);
			MemoryHeader* nextHeader = currHeader->nextBlock;

			size_t newSize = currHeader->size + sizeof(MemoryHeader) + nextHeader->size;
			nextHeader->guardData = _MAGIC;

			//clear the endGuard
			if(nextHeader->isAllocated)
			{
				int* endGuard = (int*) (reinterpret_cast<char*>(nextHeader) +
				sizeof(MemoryHeader) + nextHeader->size);
				*endGuard = _MAGIC;
				newSize += GUARD_SIZE;
			}

			currHeader->isAllocated = false;
			currHeader->size = newSize;
			currHeader->nextBlock = nextHeader->nextBlock;

			//fix the pointer of the next block
			if(nextHeader->nextBlock != 0)
				nextHeader->nextBlock->prevBlock = currHeader;

			//clear nextHeader's pointers for better debugging
			nextHeader->nextBlock = 0;
			nextHeader->prevBlock = 0;

			numTotalBlocks--;
		}
	};
}
#pragma once

#include <exception>
#include <string>

namespace Combustion
{
	/*
		Thrown by MemoryManager when a problem is encountered
		Functions allow the user to request the amount of information
		they would like to display

		errorMessage: details the exact problem encountered
		linenumber:   the line of the instruction which triggered the exception
		filename:     the filename of the line which triggered the exception
	*/
	class MemoryException : public std::exception
	{
	public:
		MemoryException(const char* msg, int linenumber, char* filename)
			: errorMessage(msg), line(linenumber), file(filename)
		{}

		/* Returns the message describing the exception */
		virtual const char* what() { return errorMessage; }

		/* Returns the line number that triggered the exception */
		virtual int GetLine() {	return line; }

		/* Returns the file of the line number that triggered the exception */
		virtual char* GetFile() { return file; }

	private:
		const char* errorMessage;
		int line;
		char* file;
	};
}