/*++

Copyright (C) 1996-2001 Microsoft Corporation

Module Name:

    MMFARENA.CPP

Abstract:

    CMMFArena2 implementation (arenas based on memory-mapped files).

History:

--*/

#include "precomp.h"
#include <stdio.h>
#include "wbemutil.h"
#include "mmfarena2.h"

CMMFArena2 *  g_pDbArena;

#define MAX_PAGE_SIZE_WIN9X		0x200000	/*2MB*/
#define MAX_PAGE_SIZE_NT		0x3200000	/*50MB*/

struct MMFOffsetItem
{
	DWORD_PTR m_dwBaseOffset;
	LPBYTE    m_pBasePointer;
	HANDLE    m_hMappingHandle;
	DWORD     m_dwBlockSize;
};

#if (defined DEBUG || defined _DEBUG)
void MMFDebugBreak()
{
	DebugBreak();
}
#else
inline void MMFDebugBreak() {}
#endif

//***************************************************************************
//
//	CMMFArena2::CMMFArena2
//
//	Constructor.  Initialises a few things.
//
//***************************************************************************
CMMFArena2::CMMFArena2(bool bReadOnly) 
: m_bReadOnlyAccess(bReadOnly), m_dwStatus(0), m_hFile(INVALID_HANDLE_VALUE)
{
	g_pDbArena = this;

	//Get processor granularity
	SYSTEM_INFO sysInfo;
	GetSystemInfo(&sysInfo);
	m_dwMappingGranularity = sysInfo.dwAllocationGranularity;

	//Create the maximum page size... based on the OS
    OSVERSIONINFO os;
    os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if(!GetVersionEx(&os))
	{
		//Failed... assume the worst!
		m_dwMaxPageSize = MAX_PAGE_SIZE_WIN9X;
	}
	else
	{
		if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
		{
			m_dwMaxPageSize = MAX_PAGE_SIZE_NT;
		}
		else
		{
			m_dwMaxPageSize = MAX_PAGE_SIZE_WIN9X;
		}
	}
}

//***************************************************************************
//
//	CMMFArena2::CreateNewMMF
//
//	Creates a new MMF.  This creates a single page with the header initialised
//	for with the MMF header information.
//
//	pszFile			: Filename of the MMF to open
//	dwGranularity	: Minimum block allocation size
//	dwInitSize		: Initial size of the repository.
//
//	Return value	: false if we failed, true if we succeed.
//
//***************************************************************************
bool CMMFArena2::CreateNewMMF(const char *pszFile,		//File of MMF to open
							  DWORD dwGranularity,		// Granularity per allocation (min block size)
							  DWORD dwInitSize)			// Initial heap size
{
	//Size should be of a specific granularity...
	if (dwInitSize % m_dwMappingGranularity)
		dwInitSize += (m_dwMappingGranularity - (dwInitSize % m_dwMappingGranularity));

	// Create a new file
	// =================
	m_hFile = CreateFile(
		 pszFile,
		 GENERIC_READ | (m_bReadOnlyAccess ? 0 : GENERIC_WRITE),
		 FILE_SHARE_READ | (m_bReadOnlyAccess ? FILE_SHARE_WRITE : 0),
		 0,
		 CREATE_NEW,
		 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
		 0
		 );

	if (m_hFile == INVALID_HANDLE_VALUE) 
	{
		_ASSERT(0, "WinMgmt: Failed to create a new repository file");
		m_dwStatus = 7;
		return false;
	}

	//Create the end page marker here in case we have no memory...
	MMFOffsetItem *pOffsetEnd = 0;
	pOffsetEnd = new MMFOffsetItem;
	if (pOffsetEnd == 0)
	{
		_ASSERT(0, "WinMgmt: Out of memory");
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		throw CX_MemoryException();
	}

	//Create the base page...
	MMFOffsetItem *pOffsetItem = CreateBasePage(dwInitSize, dwGranularity);
	if (pOffsetItem == 0)
	{
		_ASSERT(0, "WinMgmt: Failed to create base MMF page");
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		return false;
	}
	int nStatus = -1;
	//Add this page information to the offset manager...
	nStatus = m_OffsetManager.Add(pOffsetItem);
	if (nStatus != 0)
	{
		_ASSERT(0, "WinMgmt: Failed to add offset information into offset table");
		//We are out of memory... lets tidy up...
		ClosePage(pOffsetItem);
		delete pOffsetItem;

		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		throw CX_MemoryException();
	}

	//Fill in the end page marker information and add it to the offset manager...
	pOffsetEnd->m_dwBaseOffset = m_pHeapDescriptor->m_dwCurrentSize;
	pOffsetEnd->m_pBasePointer = 0;
	pOffsetEnd->m_hMappingHandle = 0;
	pOffsetEnd->m_dwBlockSize = 0;
	
	nStatus = -1;
	nStatus = m_OffsetManager.Add(pOffsetEnd);
	if (nStatus != 0)
	{
		_ASSERT(0, "WinMgmt: Failed to add end block marker into offset table");
		//We are out of memory... lets tidy up...
		ClosePage(pOffsetItem);
		m_OffsetManager.Empty();
		delete pOffsetItem;
		delete pOffsetEnd;

		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		throw CX_MemoryException();
	}

	return true;
}

//***************************************************************************
//
//	CMMFArena2::LoadMMF
//
//	Loads an existing MMF.  Loads in the base page and all pages following
//	that
//
//	pszFile			: Filename of the MMF to open
//
//	Return value	: false if we failed, true if we succeed.
//
//***************************************************************************
bool CMMFArena2::LoadMMF(const char *pszFile)
{
	//Open the file...
	m_hFile = CreateFile(
		 pszFile,
		 GENERIC_READ | (m_bReadOnlyAccess ? 0 : GENERIC_WRITE),
		 FILE_SHARE_READ | (m_bReadOnlyAccess ? FILE_SHARE_WRITE : 0),
		 0,
		 OPEN_EXISTING,
		 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
		 0
		 );

	if (m_hFile == INVALID_HANDLE_VALUE) 
	{
		_ASSERT(0, "WinMgmt: Failed to open existing repository file");
		m_dwStatus = 7;
		return false;
	}
	DWORD dwSizeOfRepository = 0;
	MMFOffsetItem *pOffsetItem = 0;

	//Open the base page...
	pOffsetItem = OpenBasePage(dwSizeOfRepository);
	if (pOffsetItem == 0)
	{
		_ASSERT(0, "WinMgmt: Failed to open base page in MMF");
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		return false;
	}

	//Add the details to the offset manager...
	int nStatus = -1;
	nStatus = m_OffsetManager.Add(pOffsetItem);
	if (nStatus)
	{
		_ASSERT(0, "WinMgmt: Failed to add offset information into offset table");
		ClosePage(pOffsetItem);
		delete pOffsetItem;

		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		throw CX_MemoryException();
	}

	DWORD_PTR dwPageBase = 0;

	if (m_pHeapDescriptor->m_dwVersion == 9)
	{
		//Now loop through all the following pages and load them...
		DWORD dwSizeLastPage = 0;
		nStatus = -1;
		for (dwPageBase = pOffsetItem->m_dwBlockSize; dwPageBase < dwSizeOfRepository; dwPageBase += dwSizeLastPage)
		{
			//Open the next...
			pOffsetItem = OpenExistingPage(dwPageBase);
			if (pOffsetItem == 0)
			{
				_ASSERT(0, "WinMgmt: Failed to open an existing page in the MMF");
				//Failed to do that!
				CloseAllPages();
				CloseHandle(m_hFile);
				m_hFile = INVALID_HANDLE_VALUE;
				m_dwStatus = 7;
				return false;
			}
			//Add the information to the offset manager...
			nStatus = -1;
			nStatus = m_OffsetManager.Add(pOffsetItem);
			if (nStatus)
			{
				_ASSERT(0, "WinMgmt: Failed to add offset information into offset table");
				//Failed to do that!
				ClosePage(pOffsetItem);
				delete pOffsetItem;
				CloseAllPages();
				CloseHandle(m_hFile);
				m_hFile = INVALID_HANDLE_VALUE;
				m_dwStatus = 7;
				throw CX_MemoryException();
			}
			dwSizeLastPage = pOffsetItem->m_dwBlockSize;
		}
	}
	else if ((m_pHeapDescriptor->m_dwVersion == 10) || (m_pHeapDescriptor->m_dwVersion < 9))
	{
		dwPageBase = pOffsetItem->m_dwBlockSize;
	}
	else
	{
		_ASSERT(0, "WinMgmt: Database error... Code has not been added to support the opening of this database!!!!!");
		ERRORTRACE((LOG_WBEMCORE, "Database error... Code has not been added to support the opening of this database!!!!!\n"));
	}

	//Create a mapping entry to mark the end of the MMF
	pOffsetItem = new MMFOffsetItem;
	if (pOffsetItem == 0)
	{
		_ASSERT(0, "WinMgmt: Out of memory");
		//Failed to do that!
		CloseAllPages();
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		throw CX_MemoryException();
	}
	pOffsetItem->m_dwBaseOffset = dwPageBase;
	pOffsetItem->m_dwBlockSize = 0;
	pOffsetItem->m_hMappingHandle = 0;
	pOffsetItem->m_pBasePointer = 0;
	nStatus = -1;
	nStatus = m_OffsetManager.Add(pOffsetItem);
	if (nStatus)
	{
		_ASSERT(0, "WinMgmt: Failed to add offset information into offset table");
		//Failed to do that!
		ClosePage(pOffsetItem);
		delete pOffsetItem;
		CloseAllPages();
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		m_dwStatus = 7;
		throw CX_MemoryException();
	}

	return true;
};

//***************************************************************************
//
//	CMMFArena2::OpenBasePage
//
//	Opens the MMF first page which has all the information about the rest
//	of the MMF as well as the first page of data.
//
//	dwSizeOfRepository	: Returns the current size of the repository
//
//	Return value	: Pointer to an offset item filled in with the base
//					  page information.  NULL if we fail to open the
//					  base page.
//
//***************************************************************************
MMFOffsetItem *CMMFArena2::OpenBasePage(DWORD &dwSizeOfRepository)
{
	MMFOffsetItem *pOffsetItem = 0;
	pOffsetItem = new MMFOffsetItem;
	if (pOffsetItem == 0)
		throw CX_MemoryException();

	//Seek to the start of this page...
	if (SetFilePointer(m_hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
	{
		_ASSERT(0, "WinMgmt: Failed to set file pointer on MMF");
		delete pOffsetItem;
		return 0;
	}

	//Read in the hear information so we can find the size of this block...
	DWORD dwActualRead;
	MMF_ARENA_HEADER mmfHeader;
	if ((ReadFile(m_hFile, &mmfHeader, sizeof(MMF_ARENA_HEADER), &dwActualRead, 0) == 0) || (dwActualRead != sizeof(MMF_ARENA_HEADER)))
	{
		_ASSERT(0, "WinMgmt: Failed to read MMF header information");
		delete pOffsetItem;
		return 0;
	}

	//Record the current size information...
	dwSizeOfRepository = mmfHeader.m_dwCurrentSize;

	DWORD dwSizeToMap = 0;

	if ((mmfHeader.m_dwVersion < 9) || (mmfHeader.m_dwVersion == 10))
	{
		//old style database, we map in everything...
		dwSizeToMap = mmfHeader.m_dwCurrentSize;
	}
	else if (mmfHeader.m_dwVersion == 9)
	{
		//We get the first page...
		dwSizeToMap = mmfHeader.m_dwSizeOfFirstPage;
	}
	else
	{
		_ASSERT(0, "WinMgmt: Database error... Code has not been added to support the opening of this database!!!!!");
		ERRORTRACE((LOG_WBEMCORE, "Database error... Code has not been added to support the opening of this database!!!!!\n"));
	}

	//Create the file mapping for this page...
	HANDLE hMapping = CreateFileMapping(
		m_hFile,							// Disk file
		0,									// No security
		(m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT,		// Extend the file to match the heap size
		0,									// High-order max size
		dwSizeToMap,		// Low-order max size
		0									// No name for the mapping object
		);

	if (hMapping == NULL) 
	{
		_ASSERT(0, "WinMgmt: Failed to create file mapping");
		delete pOffsetItem;
		return 0;
	}

	// Map this into memory...
	LPBYTE pBindingAddress = (LPBYTE)MapViewOfFile(hMapping,
												(m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
												 0,
												 0,
												 dwSizeToMap
												 );

	if (pBindingAddress == NULL) 
	{
		_ASSERT(0, "WinMgmt: Failed to map MMF into memory");
		delete pOffsetItem;
		CloseHandle(hMapping);
		return 0;
	}

	//Record the base address of this because we need easy access to the header...
	m_pHeapDescriptor = (MMF_ARENA_HEADER*) pBindingAddress;

	//Create a mapping entry for this...
	pOffsetItem->m_dwBaseOffset = 0;
	pOffsetItem->m_dwBlockSize = dwSizeToMap;
	pOffsetItem->m_hMappingHandle = hMapping;
	pOffsetItem->m_pBasePointer = pBindingAddress;

	return pOffsetItem;
}

//***************************************************************************
//
//	CMMFArena2::OpenExistingPage
//
//	Opens the specified page from the repostory.
//
//	dwBaseOffset	: Offset within the MMF to map in.
//
//	Return value	: Pointer to an offset item filled in with the 
//					  page information.  NULL if we fail to open the
//					  page.
//
//***************************************************************************
MMFOffsetItem *CMMFArena2::OpenExistingPage(DWORD_PTR dwBaseOffset)
{
	MMFOffsetItem *pOffsetItem = 0;
	pOffsetItem = new MMFOffsetItem;
	if (pOffsetItem == 0)
		throw CX_MemoryException();

	//Set the file pointer to the start of this page...
	if (SetFilePointer(m_hFile, dwBaseOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
	{
		//We are messed up!
		_ASSERT(0, "WinMgmt: Failed to determine the size of the next block to load");
		return 0;
	}

	//Read in the page information so we can find out how big the page is...
	DWORD dwActualRead = 0;
	MMF_PAGE_HEADER pageHeader;
	if ((ReadFile(m_hFile, &pageHeader, sizeof(MMF_PAGE_HEADER), &dwActualRead, 0) == 0) || (dwActualRead != sizeof(MMF_PAGE_HEADER)))
	{
		_ASSERT(0, "WinMgmt: Failed to read the next page block size");
		return 0;
	}

	//Create the file mapping...
	HANDLE hMapping;
	hMapping = CreateFileMapping(m_hFile, 
								 0, 
								 (m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT,
								 0, 
								 dwBaseOffset + pageHeader.m_dwSize, 
								 0);
	
	if (hMapping == 0)
	{
		_ASSERT(0, "WinMgmt: Failed to map in part of the memory mapped file!");
		return 0;
	}

	//Map this into memory...
	LPBYTE pBindingAddress;
	pBindingAddress= (LPBYTE)MapViewOfFile(hMapping,
											(m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
											0,
											dwBaseOffset,
											pageHeader.m_dwSize);
	if (pBindingAddress == 0)
	{
		_ASSERT(0, "WinMgmt: Failed to bind part of the memory mapped file into memory!");
		return 0;
	}

	//Record the information...
	pOffsetItem->m_dwBaseOffset = dwBaseOffset;
	pOffsetItem->m_dwBlockSize = pageHeader.m_dwSize;
	pOffsetItem->m_hMappingHandle = hMapping;
	pOffsetItem->m_pBasePointer = pBindingAddress;

	return pOffsetItem;
}

//***************************************************************************
//
//	CMMFArena2::CreateBasePage
//
//	Creates a new base page and initialises it.
//
//	dwInitSize		: Initial size of the repotitory, which is also the size
//					  of the first page.
//	dwGranularity	: Size of mimimmum block allocation size...
//
//	Return value	: Pointer to an offset item filled in with the 
//					  page information.  NULL if we fail to open the
//					  page.
//
//***************************************************************************
MMFOffsetItem *CMMFArena2::CreateBasePage(DWORD dwInitSize, DWORD dwGranularity)
{
	MMFOffsetItem *pOffsetItem = 0;
	pOffsetItem = new MMFOffsetItem;
	if (pOffsetItem == 0)
		throw CX_MemoryException();

	// Create a file mapping for this base page...
	// ===========================================
	HANDLE hMapping = CreateFileMapping(
		m_hFile,							// Disk file
		0,									// No security
		(m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT,		// Extend the file to match the heap size
		0,									// High-order max size
		dwInitSize,							// Low-order max size
		0									// No name for the mapping object
		);

	if (hMapping == NULL) 
	{
		_ASSERT(0, "WinMgmt: Failed to create a new file mapping");
		return 0;
	}

	// Map this page into memory...
	// ============================
	LPBYTE pBindingAddress = (LPBYTE)MapViewOfFile(hMapping,
												(m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
												 0,
												 0,
												 dwInitSize
												 );

	if (pBindingAddress == NULL) 
	{
		_ASSERT(0, "WinMgmt: Failed to map in new page into memory");
		CloseHandle(hMapping);
		return 0;
	}

	//Record the base pointer which is used throughout to hold important items
	//and information about the MMF.
	m_pHeapDescriptor = (MMF_ARENA_HEADER*) pBindingAddress;

	// Record the final heap address and set up the other related pointers.
	m_pHeapDescriptor->m_dwVersion = 0;
	m_pHeapDescriptor->m_dwGranularity = dwGranularity;
	m_pHeapDescriptor->m_dwCurrentSize = dwInitSize;
	m_pHeapDescriptor->m_dwMaxSize = 0;
	m_pHeapDescriptor->m_dwGrowBy = 0;
	m_pHeapDescriptor->m_dwHeapExtent = sizeof(MMF_ARENA_HEADER);
	m_pHeapDescriptor->m_dwEndOfHeap = dwInitSize;
	m_pHeapDescriptor->m_dwFreeList = 0;
	m_pHeapDescriptor->m_dwRootBlock = 0;
	m_pHeapDescriptor->m_dwSizeOfFirstPage = dwInitSize;

	//Now we have to add this information to the offset manager...
	pOffsetItem->m_dwBaseOffset = 0;
	pOffsetItem->m_pBasePointer = pBindingAddress;
	pOffsetItem->m_hMappingHandle = hMapping;
	pOffsetItem->m_dwBlockSize = dwInitSize;

	return pOffsetItem;
}

//***************************************************************************
//
//	CMMFArena2::CreateNewPage
//
//	Creates a new page and initialises the header, returning a structure
//	detailing the page.
//
//	dwBaseOffset	: Base offset of this new page
//	dwSize			: Size of this page
//
//	Return value	: Pointer to an offset item filled in with the 
//					  page information.  NULL if we fail to open the
//					  page.
//
//***************************************************************************
MMFOffsetItem *CMMFArena2::CreateNewPage(DWORD_PTR dwBaseOffset, DWORD dwSize)
{
	MMFOffsetItem *pOffsetItem = 0;
	pOffsetItem = new MMFOffsetItem;
	if (pOffsetItem == 0)
		throw CX_MemoryException();

	//Create the file mapping, this may grow the file...
	HANDLE hNewMapping;
	LPBYTE pNewBindingAddress = NULL;
	hNewMapping = CreateFileMapping(
		m_hFile,										// Disk file
		0,												// No security
		(m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT,					// Extend the file to match the heap size
		0,												// High-order max size
		dwBaseOffset + dwSize,							// Low-order max size
		0												// No name for the mapping object
		);

	DWORD dwErr = GetLastError();

	if (hNewMapping != 0 && dwErr == 0) 
	{

		//Map this into memory...
		pNewBindingAddress = (LPBYTE)MapViewOfFile(
			hNewMapping,
			(m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
			0,
			dwBaseOffset,							// Old size used here!!!
			dwSize									// Size of new part of mapping
			);

		if (pNewBindingAddress)
		{
			//Record the information...
			pOffsetItem->m_dwBaseOffset = dwBaseOffset;
			pOffsetItem->m_pBasePointer = pNewBindingAddress;
			pOffsetItem->m_hMappingHandle = hNewMapping;
			pOffsetItem->m_dwBlockSize = dwSize;

			//Fill in the page header...
			MMF_PAGE_HEADER *pPageHeader = (MMF_PAGE_HEADER *)pNewBindingAddress;
			pPageHeader->m_dwSize = dwSize;
		}			 
		else
		{
			_ASSERT(0, "WinMgmt: Failed to map in page of MMF into memory");
			ERRORTRACE((LOG_WBEMCORE, "Failed to map the new file mapping into memory for block size of %lu.  Last Error = %lu\n", dwSize, GetLastError()));
			CloseHandle(hNewMapping);
			delete pOffsetItem;
			pOffsetItem = 0;
		}
	}
	else
	{
		_ASSERT(0, "WinMgmt: Failed to create a file mapping for the MMF");
		if (hNewMapping != 0)
			CloseHandle(hNewMapping);
		ERRORTRACE((LOG_WBEMCORE, "Failed to create a new file mapping for %lu bytes block.  Last Error = %lu\n", dwSize, dwErr));
		delete pOffsetItem;
		pOffsetItem = 0;
	}

	return pOffsetItem;
}

//***************************************************************************
//
//	CMMFArena2::ClosePage
//
//	Closes the page specified
//
//	pOffsetItem	: Information about the page to close.
//
//	Return value	: None
//
//***************************************************************************
void CMMFArena2::ClosePage(MMFOffsetItem *pOffsetItem)
{
	if (pOffsetItem->m_hMappingHandle)
	{
		UnmapViewOfFile(pOffsetItem->m_pBasePointer);
		CloseHandle(pOffsetItem->m_hMappingHandle);
	}
}

//***************************************************************************
//
//	CMMFArena2::CloseAllPages
//
//	Closes all pages in the offset manager, deleting the pointers of the
//	objects in there.
//
//	Return value	: None
//
//***************************************************************************
void CMMFArena2::CloseAllPages()
{
	//Close each of the file mappings...
	for (int i = 0; i != m_OffsetManager.Size(); i++)
	{
		MMFOffsetItem *pItem = (MMFOffsetItem*)m_OffsetManager[i];
		ClosePage(pItem);
		delete pItem;
	}
	m_OffsetManager.Empty();
}

//***************************************************************************
//
//	CMMFArena2::~CMMFArena2
//
//	Destructor flushes the heap, unmaps the view and closes handles.
//
//***************************************************************************

CMMFArena2::~CMMFArena2()
{
	if (m_hFile != INVALID_HANDLE_VALUE)
	{
		//Make sure what is in the MMF is flushed...
		Flush();

		//Remember the size of the file....
		DWORD dwFileSize = m_pHeapDescriptor->m_dwCurrentSize;

		//Close each of the file mappings...
		CloseAllPages();

		//Set the size in case we messed it up if we failed to do a grow...
		SetFilePointer(m_hFile, dwFileSize, NULL, FILE_BEGIN);
		SetEndOfFile(m_hFile);

		//Close the file handle
		CloseHandle(m_hFile);
	}
}

//***************************************************************************
//
//	CMMFArena2::Alloc
//
//	Allocates a new memory block.	Uses the free list if a suitable block
//	can be found; grows the heap if required.
//
//	Parameters:
//	<dwBytes>		Number of bytes to allocate.
//
//	Return value:
//	Offsete of memory, or 0 on failure.
//
//***************************************************************************
DWORD_PTR CMMFArena2::Alloc(DWORD dwBytes)
{
	//Allocation of 0 bytes returns 0!
	if (dwBytes == 0)
		return 0;

	if (m_dwStatus != 0)
		throw DATABASE_FULL_EXCEPTION();

	//Make sure we are large enough block for any information we store in the 
	//block when it is deleted
	if (dwBytes < sizeof(MMF_BLOCK_DELETED))
		dwBytes = sizeof(MMF_BLOCK_DELETED);

	//Check the granularity of the size...
	DWORD dwGran = m_pHeapDescriptor->m_dwGranularity;
	dwBytes = (dwBytes - 1 + dwGran) / dwGran * dwGran;

	//Now adjust the size to include the header and trailer blocks...
	dwBytes += sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER);

	//Get the first item in the free-list pointer
	DWORD_PTR dwFreeBlock = m_pHeapDescriptor->m_dwFreeList;
	MMF_BLOCK_HEADER *pFreeBlock = (MMF_BLOCK_HEADER*)OffsetToPtr(dwFreeBlock);

	//While free-list pointer is not NULL, and the size of the free-list pointer 
	//block is too small
	while (dwFreeBlock && (GetSize(pFreeBlock) < dwBytes))
	{
		ValidateBlock(dwFreeBlock);

		//Get the next block in the free-list pointer
		MMF_BLOCK_DELETED *pUserBlock = GetUserBlock(pFreeBlock);
		dwFreeBlock = pUserBlock->m_dwFLnext;
		pFreeBlock = (MMF_BLOCK_HEADER*)OffsetToPtr(dwFreeBlock);
	}

	//If the free-list pointer is NULL, we need to allocate more space in the arena
	if (dwFreeBlock == NULL)
	{
		//If there is not enough space at the end of the heap unused, we need to 
		//grow the heap
		if (dwBytes > (m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent)) 
		{
			//Mark the chunk of memory at the end of the arena as deleted as it is too 
			//small to use...
			if (m_pHeapDescriptor->m_dwEndOfHeap != m_pHeapDescriptor->m_dwHeapExtent)
			{
				DecorateUsedBlock(m_pHeapDescriptor->m_dwHeapExtent, m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent);
				Free(DWORD_PTR((LPBYTE)m_pHeapDescriptor->m_dwHeapExtent + sizeof(MMF_BLOCK_HEADER)));
				m_pHeapDescriptor->m_dwHeapExtent = m_pHeapDescriptor->m_dwEndOfHeap;
			}

			//If (Grow the arena by at least dwBytes) fails
			if (GrowArena(dwBytes) == 0)
			{
				//Fail the operation
				throw DATABASE_FULL_EXCEPTION();
				m_dwStatus = 1;
				return 0;
			}
		}
		
		//We have a chunk of memory at the end of the arena which is not in the 
		//free-list.  We need to extend the size of the working arena and 
		//use this.
		if ((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent - dwBytes) <= (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + max(sizeof(MMF_BLOCK_DELETED), m_pHeapDescriptor->m_dwGranularity)))
		{
			dwBytes += m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent - dwBytes;
		}
		DWORD_PTR dwFreeBlock = m_pHeapDescriptor->m_dwHeapExtent;
		m_pHeapDescriptor->m_dwHeapExtent += dwBytes;
		DecorateUsedBlock(dwFreeBlock, dwBytes);

//		_ASSERT((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) > (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + sizeof(MMF_BLOCK_DELETED)), "Space left at end of arena is too small");
		ZeroOutBlock(dwFreeBlock, 0xCD);
		return dwFreeBlock + sizeof(MMF_BLOCK_HEADER);
	}

	ValidateBlock(dwFreeBlock);

	//If this block is large enough to split
	if ((GetSize(pFreeBlock) - dwBytes) >= 
			(sizeof(MMF_BLOCK_HEADER) + 
			 sizeof(MMF_BLOCK_TRAILER) + 
			 max(sizeof(MMF_BLOCK_DELETED), m_pHeapDescriptor->m_dwGranularity)))
	{
		//We need to split this large block into two and put the free-list
		//information from the start of this block into the newly
		//slit off bit
		MMF_BLOCK_DELETED *pFreeBlockUser = GetUserBlock(pFreeBlock);
		MMF_BLOCK_TRAILER *pFreeBlockTrailer = GetTrailerBlock(pFreeBlock);
		DecorateAsDeleted(dwFreeBlock + dwBytes, 
						  GetSize(pFreeBlock) - dwBytes, 
						  pFreeBlockUser->m_dwFLnext,
						  pFreeBlockTrailer->m_dwFLback);

		//Now need to point the previous blocks and next blocks free-list pointers to point
		//to this new starting point.
		MMF_BLOCK_HEADER *pNewFreeBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwFreeBlock + dwBytes);
		MMF_BLOCK_DELETED *pNewFreeBlockUser = GetUserBlock(pNewFreeBlockHeader);
		MMF_BLOCK_TRAILER *pNewBlockTrailer = GetTrailerBlock(pNewFreeBlockHeader);

		//Deal with previous block...
		if (pNewBlockTrailer->m_dwFLback)
		{
			MMF_BLOCK_HEADER *pPrevFreeBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(pNewBlockTrailer->m_dwFLback);
			MMF_BLOCK_DELETED *pPrevFreeBlockUser = GetUserBlock(pPrevFreeBlockHeader);
			pPrevFreeBlockUser->m_dwFLnext = dwFreeBlock + dwBytes;
		}
		else
		{
			m_pHeapDescriptor->m_dwFreeList = dwFreeBlock + dwBytes;
		}
		
		//Deal with next block...if there is one
		if (pNewFreeBlockUser->m_dwFLnext)
		{
			MMF_BLOCK_HEADER *pNextFreeBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(pNewFreeBlockUser->m_dwFLnext);
			MMF_BLOCK_TRAILER *pNextFreeBlockTrailer = GetTrailerBlock(pNextFreeBlockHeader);
			pNextFreeBlockTrailer->m_dwFLback = dwFreeBlock + dwBytes;
		}

		//Set up the header and footer of this newly slit block
		DecorateUsedBlock(dwFreeBlock, dwBytes);
	}
	else
	{
		//Otherwise we need to remove this block from the free-list.
		RemoveBlockFromFreeList(pFreeBlock);

		//Populate this block with header and trailer information
		DecorateUsedBlock(dwFreeBlock, GetSize(pFreeBlock));
	}
	ZeroOutBlock(dwFreeBlock, 0xCD);

	_ASSERT(!(((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) != 0) &&
			((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) < (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + sizeof(MMF_BLOCK_DELETED)))), 
			"Space left at end of arena is too small");

	//Return the offset to the caller.
	return dwFreeBlock + sizeof(MMF_BLOCK_HEADER);
}

//***************************************************************************
//
//	CMMFArena2::Realloc
//
//	Allocates a new block of the requested size, copies the current
//	contents to it and frees up the original.  If the original is
//	large enough it returns that.
//
//	Parameters:
//	<dwAddress> 	The address to reallocate
//	<dwNewSize> 	New requested size
//	
//	Return value:
//	TRUE on success, FALSE if not enough memory of corruption detected
//
//***************************************************************************
DWORD_PTR CMMFArena2::Realloc(DWORD_PTR dwOriginal, DWORD dwNewSize)
{
	if (m_dwStatus != 0)
		throw DATABASE_FULL_EXCEPTION();

	//Validate original block...
	ValidateBlock(dwOriginal - sizeof(MMF_BLOCK_HEADER));

	MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwOriginal - sizeof(MMF_BLOCK_HEADER));

	//If the original block is large enough, return the original
	if ((GetSize(pHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER))  >= dwNewSize)
		return dwOriginal;

	//Allocate a new block
	DWORD_PTR dwNewBlock = Alloc(dwNewSize);

	//If the allocation failed return NULL
	if (dwNewBlock == 0)
		return 0;

	//Copy orignial contents into it
	memcpy(OffsetToPtr(dwNewBlock), OffsetToPtr(dwOriginal), GetSize(pHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER));

	//Free original block
	Free(dwOriginal);

	//Return newly allocated block
	return dwNewBlock;
}

//***************************************************************************
//
//	CMMFArena2::Free
//
//	Frees a block of memory and places it on the free list.
//
//	Parameters:
//	<dwAddress> 	The address to 'free'.
//	
//	Return value:
//	TRUE on success, FALSE on erroneous address.
//
//***************************************************************************

BOOL CMMFArena2::Free(DWORD_PTR dwAddress)
{
	//Freeing of NULL is OK!
	if (dwAddress == 0)
		return TRUE;

	if (m_dwStatus != 0)
		throw DATABASE_FULL_EXCEPTION();

	//Set the address to point to the actual start of the block
	dwAddress -= sizeof(MMF_BLOCK_HEADER);

	//Check the block is valid...
	ValidateBlock(dwAddress);

	MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwAddress);

	DWORD     dwMappedBlockSize = 0;
	DWORD_PTR dwMappedBlockBaseAddress = GetBlockBaseAddress(dwAddress, dwMappedBlockSize);

	//If there is a block following this and it is deleted we should remove it from the free-list
	//chain and merge it with this block... That is if this is not the last block
	//in the heap!
	DWORD_PTR dwNextBlockAddress = dwAddress + GetSize(pBlockHeader);
	if ((dwNextBlockAddress < (dwMappedBlockBaseAddress + dwMappedBlockSize)) && (dwNextBlockAddress < m_pHeapDescriptor->m_dwHeapExtent))
	{
		MMF_BLOCK_HEADER *pNextBlockHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwNextBlockAddress);
		if (IsDeleted(pNextBlockHeader))
		{
			//OK, we can now remove this block from the free-list
			RemoveBlockFromFreeList(pNextBlockHeader);

			DecorateUsedBlock(dwAddress, GetSize(pBlockHeader) + GetSize(pNextBlockHeader));
		}
	}

	//If there is a deleted block before we have to merge this block into the previous one
	//The previous block is deleted if there is something in the FL back-pointer of the 
	//previous blocks trailer.  Make sure this is not the first block in the heap though!!!
	MMF_BLOCK_HEADER *pPrevBlockHeader = 0;
	if ((dwAddress != (dwMappedBlockBaseAddress + sizeof(MMF_PAGE_HEADER))) && (pPrevBlockHeader = GetPreviousDeletedBlock(pBlockHeader)))
	{
		//There is a deleted block prior to this one!  We need to merge them
		MMF_BLOCK_DELETED *pPrevBlockUser = GetUserBlock(pPrevBlockHeader);
		MMF_BLOCK_TRAILER *pPrevBlockTrailer = GetTrailerBlock(pPrevBlockHeader);
		DecorateAsDeleted(PtrToOffset(LPBYTE(pPrevBlockHeader)), 
						  GetSize(pPrevBlockHeader) + GetSize(pBlockHeader),
						  pPrevBlockUser->m_dwFLnext, 
						  pPrevBlockTrailer->m_dwFLback);

		//This block is now fully set up in the free-list!!!
		dwAddress = PtrToOffset(LPBYTE(pPrevBlockHeader));
		
		ValidateBlock(dwAddress);
	}
	else
	{
		//We just add this block as it stands to the free-list chain.
		DecorateAsDeleted(dwAddress, GetSize(pBlockHeader), m_pHeapDescriptor->m_dwFreeList, NULL);

		//Point the free-list header pointer at us
		m_pHeapDescriptor->m_dwFreeList = dwAddress;

		//If there is a next block, set the next blocks previous FL pointer to us
		MMF_BLOCK_DELETED *pBlockUser = GetUserBlock(pBlockHeader);
		if (pBlockUser->m_dwFLnext)
		{
			MMF_BLOCK_HEADER *pNextBlockHeader = (MMF_BLOCK_HEADER*)(OffsetToPtr(pBlockUser->m_dwFLnext));
			MMF_BLOCK_TRAILER *pNextBlockTrailer = GetTrailerBlock(pNextBlockHeader);
			pNextBlockTrailer->m_dwFLback = dwAddress;
		}

		ValidateBlock(dwAddress);
	}

	ZeroOutBlock(dwAddress, 0xDD);
	return TRUE;
}

//***************************************************************************
//
//	CMMFArena2::GrowArena
//
//	Grows the head of the MMF by atleast the amount specified.	It does not
//	add the space to the free-list.
//
//	Parameters:
//	<dwGrowBySize>		Number of bytes to allocate.
//
//	Return value:
//	FALSE if cannot grow the heap (either because it is locked, or due to
//	a failure).  TRUE otherwise.
//
//***************************************************************************
DWORD_PTR CMMFArena2::GrowArena(DWORD dwGrowBySize)
{
	//Check that the arena is valid before continuing...
	if (m_dwStatus != 0)
		return 0;

	//Validation of heap and no grow passes 0 in!
	if (dwGrowBySize == 0)
		return 0;

	// We check the current size and see if applying
	// the m_dwGrowBy factor would increase the size beyond
	// the maximum heap size.
	// ======================================================
	DWORD dwIncrement = 0;

	while (dwIncrement < dwGrowBySize) 
	{
		if (((m_pHeapDescriptor->m_dwCurrentSize + dwIncrement) / 10) < m_dwMappingGranularity )
			dwIncrement += m_dwMappingGranularity;
		else
			dwIncrement += (m_pHeapDescriptor->m_dwCurrentSize + dwIncrement) / 10;
	}

	//Cap the size of the page...
	//If we are a larger block that the max size allowed... cap it...
	if (dwIncrement > m_dwMaxPageSize)
	{
		dwIncrement = m_dwMaxPageSize;
	}

	// If here, we can try to increase the heap size by remapping.
	// ===========================================================
	MMFOffsetItem *pOffsetItem = 0;
	bool bLastTry = false;
	while (pOffsetItem == 0)
	{
		//Try and use a smaller increment until we fail to allocate the smallest block size possible...
		//---------------------------------------------------------------------------------------------

		//Make sure this block is big enough for the request plus the page header...
		if (dwIncrement < (dwGrowBySize + sizeof(MMF_PAGE_HEADER)))
		{
			dwIncrement = dwGrowBySize + sizeof(MMF_PAGE_HEADER);
			bLastTry = true;
		}

		if (dwIncrement <= m_dwMappingGranularity)
		{
			bLastTry = true;
		}
		//Size should be of a specific granularity...
		if (dwIncrement % m_dwMappingGranularity)
			dwIncrement += (m_dwMappingGranularity - (dwIncrement % m_dwMappingGranularity));

		pOffsetItem = CreateNewPage(m_pHeapDescriptor->m_dwCurrentSize, dwIncrement);

		if (pOffsetItem == 0)
		{
			//We failed!  Can we try again...
			if (bLastTry)
			{
				//No!  This was the smallest we could make the block
				return 0;
			}
					
			dwIncrement /= 2;
		}

	}
	if (pOffsetItem == 0)
	{
		return 0;
	}

	int nStatus = -1;
	nStatus = m_OffsetManager.InsertAt(m_OffsetManager.Size()-1, pOffsetItem);
	if (nStatus)
	{
		ClosePage(pOffsetItem);
		delete pOffsetItem;
		throw CX_MemoryException();
	}

	DWORD dwNewSize = m_pHeapDescriptor->m_dwCurrentSize + dwIncrement;

	// If here, we succeeded completely
	// ================================
	m_pHeapDescriptor->m_dwHeapExtent = m_pHeapDescriptor->m_dwCurrentSize + sizeof(MMF_PAGE_HEADER);
	m_pHeapDescriptor->m_dwCurrentSize = dwNewSize;
	m_pHeapDescriptor->m_dwEndOfHeap = dwNewSize;

	//get end of file item... and update it with new end offset
	MMFOffsetItem *pEndOffsetItem = (MMFOffsetItem *)m_OffsetManager[m_OffsetManager.Size() - 1];
	pEndOffsetItem->m_dwBaseOffset = dwNewSize;

	return pOffsetItem->m_dwBaseOffset;  
}

//***************************************************************************
//
//	CMMFArena2::Flush
//
//	Flushes all uncommited changed to disk.
//
//	Parameters:
//
//	Return value:
//	TRUE if success.
//
//***************************************************************************
BOOL CMMFArena2::Flush()
{
	//Need to loop through all mappings and flush them all...
	//Close each of the file mappings...
	for (int i = 0; i != m_OffsetManager.Size(); i++)
	{
		MMFOffsetItem *pItem = (MMFOffsetItem*)m_OffsetManager[i];
		if (pItem->m_hMappingHandle)
		{
			FlushViewOfFile(pItem->m_pBasePointer, 0);
		}
	}
	return TRUE;
}
//***************************************************************************
//
//	CMMFArena2::Flush
//
//	Flushes the page associated with the specified address
//
//	Parameters:
//		<dwAddress>	offset within the MMF to flush
//
//	Return value:
//	TRUE if success.
//
//***************************************************************************
BOOL CMMFArena2::Flush(DWORD_PTR dwAddress)
{
    MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwAddress - sizeof(MMF_BLOCK_HEADER));

    if (!FlushViewOfFile(pHeader, GetSize(pHeader)))
        return FALSE;
    return TRUE;
}

//***************************************************************************
//
//	CMMFArena2::DecorateUsedBlock
//
//	Populates the header and trailer blocks in the specified block.
//
//	Parameters:
//		<dwBlock>	Offset of block to decorate
//		<dwBytes>	Size of block
//
//	Return value:
//	TRUE if success.
//
//***************************************************************************
BOOL CMMFArena2::DecorateUsedBlock(DWORD_PTR dwBlock, DWORD dwBytes)
{
	MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock);
	pHeader->m_dwSize = (dwBytes & MMF_REMOVE_DELETED_MASK);
	MMF_BLOCK_TRAILER *pTrailer = GetTrailerBlock(pHeader);
	pTrailer->m_dwFLback = NULL;

	if (sizeof(pTrailer->m_dwCheckBlock))
	{
		for (DWORD dwIndex = 0; dwIndex != (sizeof(pTrailer->m_dwCheckBlock) / sizeof(DWORD)); dwIndex++)
		{
			pTrailer->m_dwCheckBlock[dwIndex] = MMF_DEBUG_INUSE_TAG;
		}
	}

	return TRUE;
}

//***************************************************************************
//
//	CMMFArena2::DecorateAsDeleted
//
//	Populates the header and trailer blocks in the specified block with 
//	everything needed to make it look like a deleted block. Updates
//	the items associated with the free list within the block
//
//	Parameters:
//		<dwBlock>				Offset of block to decorate
//		<dwBytes>				Size of block
//		<dwNextFreeListOffset>	next item in the free-list
//		dwPrevFreeListOffset<>	previous item in the free list.
//
//	Return value:
//	TRUE if success.
//
//***************************************************************************
BOOL CMMFArena2::DecorateAsDeleted(DWORD_PTR dwBlock, 
								   DWORD dwBytes, 
								   DWORD_PTR dwNextFreeListOffset,
								   DWORD_PTR dwPrevFreeListOffset)
{
	MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock);
	pHeader->m_dwSize = (dwBytes | MMF_DELETED_MASK);
	MMF_BLOCK_DELETED *pUserBlock = GetUserBlock(pHeader);
	MMF_BLOCK_TRAILER *pTrailer = GetTrailerBlock(pHeader);
	pTrailer->m_dwFLback = dwPrevFreeListOffset;
	pUserBlock->m_dwFLnext = dwNextFreeListOffset;

	if (sizeof(pTrailer->m_dwCheckBlock))
	{
		for (DWORD dwIndex = 0; dwIndex != (sizeof(pTrailer->m_dwCheckBlock) / sizeof(DWORD)); dwIndex++)
		{
			pTrailer->m_dwCheckBlock[dwIndex] = MMF_DEBUG_DELETED_TAG;
		}
	}

	return TRUE;
}

//***************************************************************************
//
//	CMMFArena2::ValidateBlock
//
//	Validates the memory block as much as possible and calls a debug break
//	point if an error is detected.  Does this by analysing the size and
//	the trailer DWORDs
//
//	Parameters:
//		<dwBlock>				Offset of block to check
//
//	Return value:
//	TRUE if success.
//
//***************************************************************************
#if (defined DEBUG || defined _DEBUG)
BOOL CMMFArena2::ValidateBlock(DWORD_PTR dwBlock)
{
	try
	{
		MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock);
		MMF_BLOCK_TRAILER *pTrailer = GetTrailerBlock(pHeader);
		if (sizeof(pTrailer->m_dwCheckBlock))
		{
			DWORD dwCheckBit;

			//Is it deleted?
			if (pHeader->m_dwSize & MMF_DELETED_MASK)
			{
				//Yes it is, so the we check for 0xFFFF
				dwCheckBit = MMF_DEBUG_DELETED_TAG;
			}
			else
			{
				dwCheckBit = MMF_DEBUG_INUSE_TAG;
			}

			for (DWORD dwIndex = 0; dwIndex != (sizeof(pTrailer->m_dwCheckBlock) / sizeof(DWORD)); dwIndex++)
			{
				if (pTrailer->m_dwCheckBlock[dwIndex] != dwCheckBit)
				{
					char string[200];
					sprintf(string, "WinMgmt: MMF Arena heap corruption,offset = 0x%p\n", dwBlock);
					OutputDebugString(string);
					MMFDebugBreak();
					_ASSERT(0, string);
					return FALSE;
				}
			}
		}
		if (!(pHeader->m_dwSize & MMF_DELETED_MASK))
		{
			//We are not deleted, so we should have a trailer back pointer of NULL
			if (pTrailer->m_dwFLback != 0)
			{
				char string[200];
				sprintf(string, "WinMgmt: MMF Arena heap corruption, offset = 0x%p\n", dwBlock);
				OutputDebugString(string);
				MMFDebugBreak();
				_ASSERT(0, string);
				return FALSE;
			}

		}
	}
	catch (...)
	{
		char string[200];
		sprintf(string, "WinMgmt: MMF Arena heap corruption, offset = 0x%p\n", dwBlock);
		OutputDebugString(string);
		MMFDebugBreak();
		_ASSERT(0, string);
		return FALSE;
	}

	return TRUE;
}
#endif
//***************************************************************************
//
//	CMMFArena2::RemoveBlockFromFreeList
//
//	Removes the specified block from the free-list.
//
//	Parameters:
//		<pBlockHeader>				pointer of block to remove
//
//	Return value:
//	TRUE if success.
//
//***************************************************************************
BOOL CMMFArena2::RemoveBlockFromFreeList(MMF_BLOCK_HEADER *pBlockHeader)
{
	//Bit of validation...
	if (!IsDeleted(pBlockHeader))
	{
		char string[200];
		sprintf(string, "WinMgmt: MMF Arena heap corruption: deleting deleted block, offset = 0x%p\n", Fixdown(pBlockHeader));
		OutputDebugString(string);
		MMFDebugBreak();
		_ASSERT(0, string);
		return FALSE;
	}

	MMF_BLOCK_DELETED *pUserBlock = GetUserBlock(pBlockHeader);
	MMF_BLOCK_TRAILER *pTrailerBlock = GetTrailerBlock(pBlockHeader);

	//Deal with next block if it exists
	if (pUserBlock->m_dwFLnext != 0)
	{
		//This has a next block.  Its back pointer needs to point to our back pointer
		MMF_BLOCK_HEADER *pNextFreeBlock = (MMF_BLOCK_HEADER *)OffsetToPtr(pUserBlock->m_dwFLnext);
		MMF_BLOCK_TRAILER *pNextTrailerBlock = GetTrailerBlock(pNextFreeBlock);
		pNextTrailerBlock->m_dwFLback = pTrailerBlock->m_dwFLback;
	}

	//Deal with previous block if it exists
	if (pTrailerBlock->m_dwFLback == 0)
	{
		//This is the first item in the free-list so the heap descriptor points to it.
		//Now needs to point to the next item.
		m_pHeapDescriptor->m_dwFreeList = pUserBlock->m_dwFLnext;
	}
	else
	{
		//This has a previous block in the list
		MMF_BLOCK_HEADER *pPrevFreeBlock = (MMF_BLOCK_HEADER *)OffsetToPtr(pTrailerBlock->m_dwFLback);
		MMF_BLOCK_DELETED *pPrevUserBlock = GetUserBlock(pPrevFreeBlock);
		pPrevUserBlock->m_dwFLnext = pUserBlock->m_dwFLnext;
	}

	return TRUE;
}

//***************************************************************************
//
//	CMMFArena2::GetPreviousDeletedBlock
//
//	If the previous block in the MMF is deleted it returns the pointer to 
//	it.  If there the previous block is not deleted or there is no 
//	previous block it returns NULL
//
//	Parameters:
//		<pBlockHeader>				pointer to the current block
//
//	Return value:
//	NULL if no previous deleted block, otherwise pointer to one.
//
//***************************************************************************
MMF_BLOCK_HEADER *CMMFArena2::GetPreviousDeletedBlock(MMF_BLOCK_HEADER *pBlockHeader)
{
	//If this is the first block there is no previous block...
	if (PtrToOffset(LPBYTE(pBlockHeader)) == sizeof(MMF_ARENA_HEADER))
		return NULL;

	//Only do anything if there are deleted blocks (we have to do this check for the
	//special case, so might as well use it around everything!)
	if (m_pHeapDescriptor->m_dwFreeList)
	{
		//Special case of when the previous block is the head block, in which case the trailer of the
		//previous block is is NULL!
		MMF_BLOCK_HEADER *pHeadFreeBlock = (MMF_BLOCK_HEADER*)OffsetToPtr(m_pHeapDescriptor->m_dwFreeList);
		if ((LPBYTE(pHeadFreeBlock) + GetSize(pHeadFreeBlock)) == LPBYTE(pBlockHeader))
		{
			//The head block is the previous deleted block!
			return pHeadFreeBlock;
		}

		//Get the trailer pointer to the previous block...
		MMF_BLOCK_TRAILER *pPrevBlockTrailer = (MMF_BLOCK_TRAILER *)(LPBYTE(pBlockHeader) - sizeof(MMF_BLOCK_TRAILER));
		if (pPrevBlockTrailer->m_dwFLback == NULL)
		{
			//The previous block is not deleted. (if this was the head free-list item we would
			//have returned it in the special case!)
			return NULL;
		}

		//Now we need to get the previous deleted block to this previous block!
		MMF_BLOCK_HEADER *pPrevPrevBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(pPrevBlockTrailer->m_dwFLback);

		//Now need this blocks user section so we can get the start of the next free-list item (the one we
		//want!)
		MMF_BLOCK_DELETED *pPrevPrevBlockUser = (MMF_BLOCK_DELETED *)(LPBYTE(pPrevPrevBlockHeader) + sizeof(MMF_BLOCK_HEADER));
		return (MMF_BLOCK_HEADER *)OffsetToPtr(pPrevPrevBlockUser->m_dwFLnext);
	}

	//There are no deleted blocks!
	return NULL;
}

#if (defined DEBUG || defined _DEBUG)
BOOL CMMFArena2::ZeroOutBlock(DWORD_PTR dwBlock, int cFill)
{
	MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock);
	if (IsDeleted(pBlockHeader))
	{
		memset(LPBYTE(pBlockHeader) + sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_DELETED), cFill, GetSize(pBlockHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER) - sizeof(MMF_BLOCK_DELETED));
	}
	else
	{
		memset(LPBYTE(pBlockHeader) + sizeof(MMF_BLOCK_HEADER), cFill, GetSize(pBlockHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER));
	}
	return TRUE;
}
#endif

//Some debugging functions...
void CMMFArena2::Reset()
{
    // Move cursor to first block.
    // ===========================

    m_dwCursor = sizeof(MMF_ARENA_HEADER);
}

BOOL CMMFArena2::Next(LPBYTE *ppBlock, DWORD *pdwSize, BOOL *pbActive)
{
    if (m_dwCursor >= m_pHeapDescriptor->m_dwHeapExtent)
        return FALSE;

    // Return the block address and block size values to the caller.
    // We strip out the MS bit of the block size, since it is not
    // part of the size, but indicates whether or not the block is
    // part of the free list or part of the active chain.
    // =============================================================

	MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(m_dwCursor);

    *ppBlock = LPBYTE(pBlockHeader) + sizeof(MMF_BLOCK_HEADER);  // Move past the head block.
    *pdwSize = GetSize(pBlockHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER);

    *pbActive = !IsDeleted(pBlockHeader);

    // Advance our cursor.
    // ===================

    m_dwCursor += GetSize(pBlockHeader);

    return TRUE;
}

//***************************************************************************
//
//  CMMFArena::TextDump
//
//  Dumps a summary of the heap to a text file.
//
//  Parameters:
//  <pszFile>       The text file to which to perform the dump. This
//                  file is appended to.
//
//***************************************************************************

void CMMFArena2::TextDump(const char *pszFile)
{
    FILE *f = fopen(pszFile, "at");
    if (!f)
        return;
        
    LPBYTE pAddress = 0;
    DWORD dwSize = 0;
    BOOL bActive = 0;

    // Primary heap.
    // =============

    fprintf(f, "---Primary Heap Contents---\n");

    Reset();
    while (Next(&pAddress, &dwSize, &bActive))
    {
        fprintf(f, "BLOCK 0x%X  Size=%06d ", PtrToOffset(pAddress), dwSize);
        if (bActive)
            fprintf(f, "(active)  :");
        else
            fprintf(f, "(deleted) :");

        for (int i = 0; i < (int) dwSize && i < 32; i++)
            if (pAddress[i] < 32)
                fprintf(f, ".");
            else
                fprintf(f, "%c", pAddress[i]);
        fprintf(f, "\n");
    }
    
    fclose(f);
}

//***************************************************************************
//
//  CMMFArena::GetHeapInfo
//
//  Gets detailed summary info about the heap.  Completely walks the
//  heap to do this.
//
//  Parameters:
//      <pdwTotalSize>          Receives the heap size.
//      <pdwActiveBlocks>       Receives the number of allocated blocks.
//      <pdwActiveBytes>        Receives the total allocated bytes.
//      <pdwFreeBlocks>         Receives the number of 'free' blocks.
//      <pdwFreeByte>           Receives the number of 'free' bytes.
//
//***************************************************************************

void CMMFArena2::GetHeapInfo(
    DWORD *pdwTotalSize,
    DWORD *pdwActiveBlocks,
    DWORD *pdwActiveBytes,
    DWORD *pdwFreeBlocks,
    DWORD *pdwFreeBytes
    )
{
    LPBYTE pAddress = 0;
    DWORD dwSize = 0;
    BOOL bActive = 0;

    if (pdwTotalSize)   
        *pdwTotalSize = m_pHeapDescriptor->m_dwCurrentSize;
 
    if (pdwActiveBlocks)
        *pdwActiveBlocks = 0;
    if (pdwActiveBytes)
        *pdwActiveBytes = 0;
    if (pdwFreeBlocks)
        *pdwFreeBlocks = 0;
    if (pdwFreeBytes)
        *pdwFreeBytes = 0;
        
 
    Reset();
    while (Next(&pAddress, &dwSize, &bActive))
    {
        if (bActive)
        {
            if (pdwActiveBytes)
                (*pdwActiveBytes) += dwSize;
            if (pdwActiveBlocks)
                (*pdwActiveBlocks)++;
        }            
        else
        {
            if (pdwFreeBytes)
                (*pdwFreeBytes) += dwSize;
            if (pdwFreeBlocks)
                (*pdwFreeBlocks)++;
        }
    }        
}

BOOL CMMFArena2::FileValid(const char *pszFilename)
{
	BOOL bValid = FALSE;
	HANDLE hFile = CreateFile(
		 pszFilename,
		 GENERIC_READ | GENERIC_WRITE,
		 FILE_SHARE_READ,									// Share mode = exclusive
		 0, 												// Security
		 OPEN_EXISTING,										// Creation distribution
		 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,	// Attribute
		 0													// Template file
		 );

	if (hFile == INVALID_HANDLE_VALUE)
	{
		//This means the file does not exist or something screwy has happened.
		return FALSE;
	}

	MMF_ARENA_HEADER header;
	DWORD dwBytesRead;
	if (ReadFile(hFile, &header, sizeof(MMF_ARENA_HEADER), &dwBytesRead, NULL))
	{
		if (sizeof(MMF_ARENA_HEADER) == dwBytesRead)
		{
			if (header.m_dwVersion <= 3)
			{
				//The header exists, now we need to check to make sure the file length is
				//correct...
				//Magic numbers :-)  This is how the Version 3 or less size was calculated.
				//actually it was sizeof the header, which was 568 in those days.
				if (GetFileSize(hFile, NULL) == (header.m_dwCurrentSize + 568))
				{
					bValid = TRUE;
				}
				else
				{
					//The sizes are not the same...
				}
			}
			else
			{
				//The header exists, now we need to check to make sure the file length is
				//correct...
				if (GetFileSize(hFile, NULL) == header.m_dwCurrentSize)
				{
					bValid = TRUE;
				}
				else
				{
					//The sizes are not the same...
				}
			}

		}
		else
		{
			//Implies the file length may have been zeroed out or something
		}
	}
	else
	{
		//Implies the file length may have been zeroed out or something
	}

	CloseHandle(hFile);

	_ASSERT(bValid == TRUE, "Repository size is not the same as the size we think it should be.");

	return bValid;
}

DWORD_PTR CMMFArena2::GetRootBlock()
{ 
	if (m_pHeapDescriptor->m_dwVersion <= 3)
	{
		DWORD_PTR *pMem = (DWORD_PTR*) m_pHeapDescriptor;
		return pMem[9];
	}
	else
	{
		return (m_pHeapDescriptor ? m_pHeapDescriptor->m_dwRootBlock : 0); 
	}
}

DWORD CMMFArena2::Size(DWORD_PTR dwBlock)
{
	if (m_dwStatus != 0)
		throw DATABASE_FULL_EXCEPTION();

	//Set the address to point to the actual start of the block
	dwBlock -= sizeof(MMF_BLOCK_HEADER);

	//Check the block is valid...
	ValidateBlock(dwBlock);

	MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwBlock);

	return GetSize(pBlockHeader);
}

//Given an offset, returns a fixed up pointer
LPBYTE CMMFArena2::OffsetToPtr(DWORD_PTR dwOffset)
{
	if (dwOffset == 0)
		return 0;

	if (m_dwStatus != 0)
		throw DATABASE_FULL_EXCEPTION();

	try
	{
		LPBYTE pBlock = 0;
		int l = 0, u = m_OffsetManager.Size() - 1;

		while (l <= u)
		{
			int m = (l + u) / 2;
			if (dwOffset < ((MMFOffsetItem *)m_OffsetManager[m])->m_dwBaseOffset)
			{
				u = m - 1;
			}
			else if (dwOffset >= ((MMFOffsetItem *)m_OffsetManager[m+1])->m_dwBaseOffset)
			{
				l = m + 1;
			}
			else
			{
				return ((MMFOffsetItem *)m_OffsetManager[m])->m_pBasePointer + (dwOffset - ((MMFOffsetItem *)m_OffsetManager[m])->m_dwBaseOffset);
			}
		}
	}
	catch (...)
	{
	}
	char string[220];
	sprintf(string, "WinMgmt: Could not find the block requested in the repository, offset requested = 0x%p, end of repository = 0x%p\n", dwOffset, ((MMFOffsetItem *)m_OffsetManager[m_OffsetManager.Size()-1])->m_dwBaseOffset);
	OutputDebugString(string);
	_ASSERT(0, string);
	MMFDebugBreak();
	return 0;
}

//Given a pointer, returns an offset from the start of the MMF
DWORD_PTR  CMMFArena2::PtrToOffset(LPBYTE pBlock)
{
	if (m_dwStatus != 0)
		throw DATABASE_FULL_EXCEPTION();

	for (int i = 0; i < m_OffsetManager.Size(); i++)
	{
		register MMFOffsetItem *pItem = (MMFOffsetItem *)m_OffsetManager[i];
		if ((pBlock >= pItem->m_pBasePointer) &&
			(pBlock < (pItem->m_pBasePointer + pItem->m_dwBlockSize)))
		{
			return pItem->m_dwBaseOffset + (pBlock - pItem->m_pBasePointer);
		}
	}
	char string[220];
	sprintf(string, "WinMgmt: Could not find the offset requested in the repository, pointer requested = 0x%p\n", pBlock);
	OutputDebugString(string);
	_ASSERT(0, string);
	MMFDebugBreak();
	return 0;
}

DWORD_PTR CMMFArena2::GetBlockBaseAddress(DWORD_PTR dwOffset, DWORD &dwSize)
{
	LPBYTE pBlock = 0;
	int l = 0, u = m_OffsetManager.Size() - 1;

	while (l <= u)
	{
		int m = (l + u) / 2;
		if (dwOffset < ((MMFOffsetItem *)m_OffsetManager[m])->m_dwBaseOffset)
		{
			u = m - 1;
		}
		else if (dwOffset >= ((MMFOffsetItem *)m_OffsetManager[m+1])->m_dwBaseOffset)
		{
			l = m + 1;
		}
		else
		{
			register MMFOffsetItem *pItem = (MMFOffsetItem *)m_OffsetManager[m];
			dwSize = pItem->m_dwBlockSize;
			return pItem->m_dwBaseOffset;
		}
	}
	char string[220];
	sprintf(string, "WinMgmt: Could not find the block requested in the repository, offset requested = 0x%p, end of repository = 0x%p\n", dwOffset, ((MMFOffsetItem *)m_OffsetManager[m_OffsetManager.Size()-1])->m_dwBaseOffset);
	OutputDebugString(string);
	_ASSERT(0, string);
	MMFDebugBreak();
	return 0;
}