#include "precomp.h"
/*
 *	memmgr.cpp
 *
 *	Copyright (c) 1993 - 1995 by DataBeam Corporation, Lexington, KY
 *
 *	Abstract:
 *		This is the implementation file for the MemoryManager class.  This
 *		file contains the code necessary to allocate and distribute memory
 *		in the form of Memory objects.
 *
 *	Protected Instance Variables:
 *		Memory_Buffer
 *			This is the base address for the large memory buffer that the
 *			Memory Manager object allocates during instantiation.  This is
 *			remembered so that the buffer can be freed when the Memory Manager
 *			object is destroyed.
 *		Memory_Information
 *			This is a pointer to the structure in memory that contains general
 *			information about the memory being managed by this object.
 *
 *	Protected Member Functions:
 *		ReleaseMemory
 *			This is a private function releases memory used by a Memory object
 *			by putting it back into the proper free stack list.
 *		CalculateMemoryBufferSize
 *		AllocateMemoryBuffer
 *		InitializeMemoryBuffer
 *
 *	Caveats:
 *		None.
 *
 *	Author:
 *		James P. Galvin, Jr.
 */

DWORD MemoryManager::dwSystemPageSize = 0;

/*
 *	MemoryManager ()
 *
 *	Public
 *
 *	Functional Description:
 *		This is the default constructor for this class.  It does nothing and
 *		only exists to allow classes to derive from this one without having to
 *		invoke the defined constructor.
 */
MemoryManager::MemoryManager () :
		pExternal_Block_Information (NULL), fIsSharedMemory (TRUE),
		bAllocs_Restricted (TRUE), Max_External_Blocks (0)
{
}

/*
 *	MemoryManager ()
 *
 *	Public
 *
 *	Functional Description:
 *		This is the constructor for the MemoryManager class.  It calculates
 *		how much total memory will be required to hold all the blocks
 *		asked for in the memory template array that is passed in.  It then
 *		allocates all of that memory in one operating system call.  It then
 *		builds a set of free stacks, each of which contains all the blocks
 *		of a particular size.
 */
MemoryManager::MemoryManager (
		PMemoryTemplate		memory_template,
		ULong				memory_count,
		PMemoryManagerError	memory_manager_error,
		ULong				ulMaxExternalBlocks,
		BOOL			bAllocsRestricted) :
		bAllocs_Restricted (bAllocsRestricted),
		fIsSharedMemory (FALSE), Max_External_Blocks (0)
{
	ULong		memory_buffer_size;

    *memory_manager_error = MEMORY_MANAGER_NO_ERROR;

	/*
	 *	Calculate the amount of memory required for this memory manager
	 *	(including all management structures).
	 */
	memory_buffer_size = CalculateMemoryBufferSize (memory_template,
			memory_count, NULL);

	/*
	 *	Allocate the memory buffer.
	 */
	AllocateMemoryBuffer (memory_buffer_size);

	/*
	 *	If the allocation succeeded, then initialize the memory buffer so that
	 *	it can be used.
	 */
	if (Memory_Buffer != NULL)
	{
		/*
		 *	Initialize the External block information dictionary.
		 *	This is only for allocations that do not come from preallocated
		 *	buffers.
		 */
		if (ulMaxExternalBlocks > 0) {
			pExternal_Block_Information = new BlockInformationList (ulMaxExternalBlocks / 3);

			if (NULL != pExternal_Block_Information) {
				Max_External_Blocks = ulMaxExternalBlocks;
			}
			else
			{
				/*
				 *	We were unable to allocate memory for the pre-allocated
		 		 *	memory pool.
			 	 */
				ERROR_OUT(("MemoryManager::MemoryManager: "
						"failed to allocate the external block information dictionary"));
				*memory_manager_error = MEMORY_MANAGER_ALLOCATION_FAILURE;
			}
		}

		if (*memory_manager_error != MEMORY_MANAGER_ALLOCATION_FAILURE) {
			/*
			 *	Initialize the memory buffer.  Note that no error can occur doing
			 *	this, since the allocation has already succeeded.
			 */
			InitializeMemoryBuffer (memory_template, memory_count);

			/*
			 *	Indicate that no error occured.
			 */
			TRACE_OUT(("MemoryManager::MemoryManager: allocation successful"));
			TRACE_OUT(("MemoryManager::MemoryManager: Allocated %d memory blocks", GetBufferCount()));
			*memory_manager_error = MEMORY_MANAGER_NO_ERROR;
		}
	}
	else
	{
		/*
		 *	We were unable to allocate memory for the pre-allocated
		 *	memory pool.
		 */
		ERROR_OUT(("MemoryManager::MemoryManager: allocation failed"));
		*memory_manager_error = MEMORY_MANAGER_ALLOCATION_FAILURE;
	}
}

/*
 *	~MemoryManager ()
 *
 *	Public
 *
 *	Functional Description:
 *		This is the destructor for the Memory Manager class.  It frees up the
 *		memory allocated for the memory pool (if any).
 */
MemoryManager::~MemoryManager ()
{
	PBlockInformation	lpBlockInfo;
	/*
	 *	Iterate through the external block information list, deleting all
	 *	block information structures contained therein.
	 */
	if (NULL != pExternal_Block_Information)
	{
		pExternal_Block_Information->reset();
		while (pExternal_Block_Information->iterate ((PDWORD_PTR) &lpBlockInfo))
		{
			delete lpBlockInfo;
	    }

		delete pExternal_Block_Information;
	}

	/*
	 *	Free up the memory buffer (if there is one).
	 */
	if (Memory_Buffer != NULL)
	{
		LocalFree ((HLOCAL) Memory_Buffer);
		Memory_Buffer = NULL;
	}
}

/*
 *	PMemory		AllocateMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to allocate a Memory object from the Memory
 *		Manager object.
 */
PMemory		MemoryManager::AllocateMemory (
					PUChar				reference_ptr,
					ULong				length,
					MemoryLockMode		memory_lock_mode)
{
	PFreeStack			free_stack;
	ULong				count;
	PBlockNumber		block_stack;
	BlockNumber			block_number;
	PBlockInformation	block_information;
	PUChar				copy_ptr = NULL;
	PMemory				memory = NULL;

	// TRACE_OUT(("MemoryManager::AllocateMemory: Remaining %d memory blocks", GetBufferCount()));
						
	/*
	 *	If the application requests a block of size zero (0), then simply
	 *	return a NULL without allocating a block.
	 */
	if (length != 0)
	{
		/*
		 *	Walk through the free stack list look for a free stack that meets
		 *	the following two allocation criteria:
		 *
		 *	1.	It must contain blocks that are big enough to hold the
		 *		reference data.  This is why it is important for the block
		 *		sizes to be specified in ascending order in the constructor.
		 *		This code checks for a block that is big enough starting at the
		 *		beginning.  By putting them in ascending order, you are insured
		 *		that the smallest available block will be used.
		 *	2.	It must have enough free blocks left to allow the allocation.
		 *		This is where priority is used.  Right now it is very simple:
		 *		the allocation will succeed if the number of available blocks
		 *		is greater than the passed in priority (which is why a lower
		 *		number actually reflects a higher priority).
		 */
		free_stack = Free_Stack;
		for (count = 0; count < Free_Stack_Count; count++)
		{
			/*
			 *	Check and see if the blocks in this free stack are big enough
			 *	to hold the reference data.  If so, are there enough to satisfy
			 *	this allocation (taking memory priority into consideration).
			 */
			if ((length <= free_stack->block_size) &&
				(free_stack->current_block_count > 0))
			{
				/*
				 *	Calculate the address of the next available block number
				 *	within the block stack.  Then read the block number and
				 *	advance the block stack offset to point to the next block.
				 */
				block_stack = (PBlockNumber) (Memory_Buffer +
						free_stack->block_stack_offset);
				block_number = *block_stack;
				free_stack->block_stack_offset += sizeof (BlockNumber);

				/*
				 *	Calculate the address of the appropriate block information
				 *	structure.  Make sure that the lock count for the newly
				 *	allocated block is zero, and the block is not marked as
				 *	freed.
				 */
				block_information = (PBlockInformation) (Block_Information +
						(sizeof (BlockInformation) * block_number));
				ASSERT (block_information->flags & FREE_FLAG);
				block_information->length = length;
				block_information->lock_count = 0;
				block_information->flags &= (~FREE_FLAG);

				/*
				 *	Decrement the number of blocks remaining, within this free stack.
				 */
				free_stack->current_block_count--;

				/*
				 *	Calculate the address of the newly allocated block.  Then
				 *	break out of the allocation loop to go use the block.
				 */
				copy_ptr = (PUChar) (Memory_Buffer +
						block_information->block_offset);

				ASSERT(copy_ptr != Memory_Buffer);


				/*
				 *	If this is a shared memory manager, and the block is not
				 *	committed, we need to commit the block.
				 */
				if ((TRUE == fIsSharedMemory) && (0 == (block_information->flags & COMMIT_FLAG))) {

					ASSERT ((free_stack->block_size % dwSystemPageSize) == 0);
					ASSERT ((((DWORD_PTR) copy_ptr) % dwSystemPageSize) == 0);

					PUChar temp = (PUChar) VirtualAlloc ((LPVOID) copy_ptr, free_stack->block_size,
														 MEM_COMMIT, PAGE_READWRITE);
					block_information->flags |= COMMIT_FLAG;

					ASSERT (temp == copy_ptr);
					ASSERT (temp != NULL);

					if (copy_ptr != temp) {
						TRACE_OUT((">>>>>#### Copy_ptr: %p, Temp: %p, Committed?: %d",
	        					copy_ptr, temp, block_information->flags & COMMIT_FLAG));
						TRACE_OUT((">>>>>#### Size: %d, Req. length: %d",
    		    				free_stack->block_size, length));
						copy_ptr = NULL;
					}
				}
				break;
			}

			/*
			 *	Point to the next entry in the free stack list.
			 */
			free_stack++;
		}

		/*
		 *	If the memory allocation failed and it's for local memory,
		 *	attempt to allocate external memory to hold the block.
		 */
		if ((copy_ptr == NULL) &&
			((FALSE == bAllocs_Restricted) ||
			((NULL != pExternal_Block_Information) &&
			(Max_External_Blocks > pExternal_Block_Information->entries()))))
		{

			ASSERT (FALSE == fIsSharedMemory);
			/*
			 *	Try allocating from system memory.  Set the free stack to NULL
			 *	to indicate that this block did NOT come from one of our free
			 *	stacks.
			 */
			copy_ptr = (PUChar) LocalAlloc (LMEM_FIXED, length);

			if (copy_ptr != NULL)
			{
				/*
				 *	Allocate a block information structure to hold relevant
				 *	information about this externally allocated block.
				 */
				block_information = new BlockInformation;

				if (block_information != NULL)
				{
					/*
					 *	Fill in the block information structure.  Block offset
					 *	is irrelevant for an externally allocated block.  A
					 *	newly allocated block has a lock count of zero, and
					 *	is not freed.
					 */
					block_information->length = length;
					block_information->lock_count = 0;
					block_information->flags = COMMIT_FLAG;

					/*
					 *	Put the block information structure into a dictionary
					 *	for future use.  This is only necessary for externally
					 *	allocated blocks, since the block information structures
					 *	for internal blocks are in the memory buffer.
					 */
					pExternal_Block_Information->insert ((DWORD_PTR) copy_ptr, (DWORD_PTR) block_information);

					/*
					 *	Set block number to be an
					 *	invalid value to indicate that this block is NOT in
					 *	the internally managed memory buffer.
					 */
					block_number = INVALID_BLOCK_NUMBER;
				}
				else
				{
					/*
					 *	We were unable to allocate the space for the block
					 *	information structure, so we must free the externally
					 *	memory we just allocated.
					 */
					LocalFree ((HLOCAL) copy_ptr);
					copy_ptr = NULL;
				}
			}
		}

		/*
		 *	If there was a block available for the allocation, it is still
		 *	necessary to create the Memory object that will hold the block.
		 */
		if (copy_ptr != NULL)
		{
			ASSERT (block_information->flags == COMMIT_FLAG);
			/*
			 *	Create the Memory object.  If it fails, then cleanly release
			 *	the memory that was to be used for this block.
			 */
			memory = new Memory (reference_ptr, length, copy_ptr,
			    				 block_number, memory_lock_mode);

			if (memory == NULL)
			{
				/*
				 *	If the free stack for the memory is not NULL, then it is
				 *	an internally managed block.  Otherwise, this was an
				 *	externally allocated block that resulted from a critical
				 *	allocation above.
				 */
				if (INVALID_BLOCK_NUMBER != block_number)
				{
					/*
					 *	Adjust the block stack offset to point to the previous
					 *	entry in the list.  Note that it is not necessary to
					 *	put the block number into the list since it still there
					 *	from when we pulled it out above.
					 */
					free_stack->block_stack_offset -= sizeof (BlockNumber);

					/*
					 *	Indicate that the block is currently freed.  Note that
					 *	it is not necessary to calculate the address of the
					 *	block information structure since we did this above.
					 */
					block_information->flags |= FREE_FLAG;

					/*
					 *	Decrement the block counter to indicate that there
					 *	is another block in this free stack.
					 */
					free_stack->current_block_count++;
				}
				else
				{
					/*
					 *	This block was externally allocated, so it must be
					 *	externally freed.  Also eliminate the block information
					 *	structure associated with this memory block.
					 */
					pExternal_Block_Information->remove ((DWORD_PTR) copy_ptr);
					delete block_information;
					LocalFree ((HLOCAL) copy_ptr);
				}
			}
		}
	}
	else
	{
		/*
		 *	The application has attempted to allocate a block of size zero.
		 *	It is necessary to fail the request.
		 */
		ERROR_OUT(("MemoryManager::AllocateMemory: attempt to allocate zero-length block"));
	}

	/*
	 *	Decrement the number of blocks remaining
	 *	in this memory manager as a whole.
	 */
	if ((TRUE == bAllocs_Restricted) && (memory != NULL))
		Memory_Information->current_block_count--;

	return (memory);
}

/*
 *	Void	FreeMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to release a previously allocated Memory object.
 */
Void	MemoryManager::FreeMemory (
				PMemory		memory)
{
	BlockNumber			block_number;
	PBlockInformation	block_information;
	PUChar				copy_ptr;

	/*
	 *	Ask the specified memory object what block number it represents.
	 */
	block_number = memory->GetBlockNumber ();

	/*
	 *	Use the block number to determine if this is an internally
	 *	allocated memory block, or an externally allocated one.
	 */
	if (block_number != INVALID_BLOCK_NUMBER)
	{
		/*
		 *	From that, calculate the address of the block information structure.
		 */
		block_information = (PBlockInformation) (Block_Information +
				(sizeof (BlockInformation) * block_number));
	}
	else
	{
		/*
		 *	This is externally allocated memory, so it must be handled
		 *	differently.  Ask the memory block what the copy pointer is, and
		 *	use that to look up the address of the block information structure.
		 */
		copy_ptr = memory->GetPointer ();
		pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
	}

	/*
	 *	Make sure that the indicated memory block has not already been
	 *	freed.
	 */
	if ((block_information->flags & FREE_FLAG) == 0)
	{
		/*
		 *	Mark the memory block as being freed.
		 */
		block_information->flags |= FREE_FLAG;

		/*
		 *	If the lock count for this block has reached zero, we can free
		 *	the block for re-use.  We can also delete the memory object, as it
		 *	is no longer needed.
		 */
		if (block_information->lock_count == 0)
		{
			ReleaseMemory (memory);
			delete memory;
		}
		else
		{
			/*
			 *	If the lock count has not yet reached zero, check to see if the
			 *	memory object is to be deleted anyway.  If the memory lock mode
			 *	is set to "IGNORED", then delete the memory object immediately.
			 */
			if (memory->GetMemoryLockMode () == MEMORY_LOCK_IGNORED)
				delete memory;
		}
	}
	else
	{
		/*
		 *	The memory block has already been freed, so this call will be
		 *	ignored.
		 */
		ERROR_OUT(("MemoryManager::FreeMemory: memory block already freed"));
	}
}

/*
 *	PMemory		CreateMemory ()
 *
 *	Public
 *
 *	Functional Description:
 */
PMemory		MemoryManager::CreateMemory (
					BlockNumber		block_number,
					MemoryLockMode	memory_lock_mode)
{
	ULong				total_block_count = 0;
	PFreeStack			free_stack;
	ULong				count;
	PBlockInformation	block_information;
	PUChar				copy_ptr;
	PMemory				memory = NULL;

	/*
	 *	Make sure that this block number lies within the range handled by
	 *	this memory manager.
	 */
	if (block_number < Memory_Information->total_block_count)
	{
		/*
		 *	We must first walk through the free stack list to determine which
		 *	free stack the specified block is in.  Start by pointing to the
		 *	first free stack.
		 */
		free_stack = Free_Stack;
		for (count = 0; count < Free_Stack_Count; count++)
		{
			/*
			 *	Update the counter which keeps track of how many blocks are
			 *	represented by this free stack and the ones already processed.
			 *	This is used to determine if the specified block number is in
			 *	this free stack.
			 */
			total_block_count += free_stack->total_block_count;

			/*
			 *	Is the block in this free stack?
			 */
			if (block_number < total_block_count)
			{
				/*
				 *	Yes it is.  Claculate the address of the block information
				 *	structure for this block.  Then calculate the address of
				 *	the actual block based on the address of the local memory
				 *	buffer.
				 */
				block_information = (PBlockInformation) (Block_Information +
						(sizeof (BlockInformation) * block_number));
				copy_ptr = (PUChar) (Memory_Buffer +
						block_information->block_offset);
				ASSERT (block_information->flags & COMMIT_FLAG);

				/*
				 *	Create a memory object to represent this block.
				 */
				memory = new Memory (NULL, block_information->length, copy_ptr,
									 block_number, memory_lock_mode);

				if (memory == NULL)
				{
					/*
					 *	Allocation of the memory object failed, so we cannot
					 *	create a memory block at this time.
					 */
					ERROR_OUT(("MemoryManager::CreateMemory: memory object allocation failed"));
				}
				break;
			}

			/*
			 *	The block was not in the last free stack, so point to the
			 *	next one.
			 */
			free_stack++;
		}
	}
	else
	{
		/*
		 *	The specified block number is out of range for this memory manager.
		 *	The request must therefore fail.
		 */
		ERROR_OUT(("MemoryManager::CreateMemory: block number out of range"));
	}

	return (memory);
}


/*
 *	Void	LockMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to lock a Memory object.
 */
Void	MemoryManager::LockMemory (
				PMemory		memory)
{
	BlockNumber			block_number;
	PBlockInformation	block_information;
	PUChar				copy_ptr;

	/*
	 *	Ask the specified memory object what block number it represents.
	 */
	block_number = memory->GetBlockNumber ();

	/*
	 *	Use the block number to determine if this is an internally
	 *	allocated memory block, or an externally allocated one.
	 */
	if (block_number != INVALID_BLOCK_NUMBER)
	{
		/*
		 *	From that, calculate the address of the block information structure.
		 */
		block_information = (PBlockInformation) (Block_Information +
				(sizeof (BlockInformation) * block_number));
	}
	else
	{
		/*
		 *	This is externally allocated memory, so it must be handled
		 *	differently.  Ask the memory block what the copy pointer is, and
		 *	use that to look up the address of the block information structure.
		 */
		copy_ptr = memory->GetPointer ();
		pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
	}

	ASSERT (block_information->flags & COMMIT_FLAG);
	/*
	 *	Increment the lock count for the specified memory block.
	 */
	block_information->lock_count++;

}

/*
 *	Void	UnlockMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to unlock a previously locked Memory object.
 */
Void	MemoryManager::UnlockMemory (
				PMemory	memory)
{
	BlockNumber			block_number;
	PBlockInformation	block_information;
	PUChar				copy_ptr;

	/*
	 *	Ask the specified memory object what block number it represents.
	 */
	block_number = memory->GetBlockNumber ();

	/*
	 *	Use the block number to determine if this is an internally
	 *	allocated memory block, or an externally allocated one.
	 */
	if (block_number != INVALID_BLOCK_NUMBER)
	{
		/*
		 *	From that, calculate the address of the block information structure.
		 */
		block_information = (PBlockInformation) (Block_Information +
				(sizeof (BlockInformation) * block_number));
	}
	else
	{
		/*
		 *	This is externally allocated memory, so it must be handled
		 *	differently.  Ask the memory block what the copy pointer is, and
		 *	use that to look up the address of the block information structure.
		 */
		copy_ptr = memory->GetPointer ();
		pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
	}

	ASSERT (block_information->flags & COMMIT_FLAG);
	/*
	 *	Make sure that the lock isn't already zero before proceeding.
	 */
	if (block_information->lock_count > 0)
	{
		/*
		 *	Decrement the lock count for the specified memory block.
		 */
		block_information->lock_count--;

		/*
		 *	If the lock count has reached zero and the memory block is
		 *	marked as being freed, then we can free the block for re-use.
		 */
		if ((block_information->lock_count == 0) &&
				(block_information->flags & FREE_FLAG))
		{
			ReleaseMemory (memory);

			/*
			 *	We have now released the memory buffer, so we must check to
			 *	see if we are supposed to destroy the memory object itself.
			 */
			if (memory->GetMemoryLockMode () == MEMORY_LOCK_NORMAL)
				delete memory;
		}
	}
	else
	{
		/*
		 *	The specified block has a lock count of zero already, so ignore
		 *	this call.
		 */
		ERROR_OUT(("MemoryManager::UnlockMemory: memory block already unlocked"));
	}
}

/*
 *	ULong	GetBufferCount ()
 *
 *	Public
 *
 *	Functional Description:
 */
ULong	MemoryManager::GetBufferCount (
						ULong				length)
{
	PFreeStack		free_stack;
	ULong			count;
	ULong			buffer_count;

	if (FALSE == bAllocs_Restricted)
		return (LARGE_BUFFER_COUNT);

	buffer_count = Memory_Information->current_block_count;
	free_stack = Free_Stack;
	for (count = 0; count < Free_Stack_Count; count++)
	{
		/*
		 *	Check and see if the blocks in this free stack are smaller than
		 *	the specified length.  If yes, we need to deduct these buffers.
		 *	Otherwise, we can stop deducting.
		 */
		if (length > free_stack->block_size) {
			buffer_count -= free_stack->current_block_count;

			/*
			 *	Point to the next entry in the free stack list.
			 */
			free_stack++;
		}
		else
			break;
	}

	return (buffer_count);
}

/*
 *	Void	ReleaseMemory (
 *					PMemory		memory)
 *
 *	Private
 *
 *	Functional Description:
 *		This function is used to release a Memory object, and free the memory
 *		it represents back to the available pool.
 *
 *	Formal Parameters:
 *		memory
 *			This is a pointer to the Memory object being released.
 *
 *	Return Value:
 *		None.
 *
 *	Side Effects:
 *		None.
 *
 *	Caveats:
 *		None.
 */
Void	MemoryManager::ReleaseMemory (
				PMemory		memory)
{
	PFreeStack			free_stack;
	BlockNumber			block_number;
	PBlockNumber		block_stack;
	PBlockInformation	block_information;
	PUChar				copy_ptr;

	/*
	 *	Ask the specified memory object what block number it represents.
	 */
	block_number = memory->GetBlockNumber ();

	/*
	 *	Use the block number to determine if this is an internally
	 *	allocated memory block, or an externally allocated one.
	 */
	if (block_number != INVALID_BLOCK_NUMBER)
	{
		/*
		 *	From that, calculate the address of the block information structure.
		 */
		 block_information = (PBlockInformation) (Block_Information +
				(sizeof (BlockInformation) * block_number));
				
		/*
		 *	Get the address of the free stack from which this block came.
		 */
		free_stack = (PFreeStack) (Memory_Buffer + block_information->free_stack_offset);

		/*
		 *	Adjust the block stack offset to point to the previous element,
		 *	and then use it to calculate an address and put the block number
		 *	there.  This effectively "pushes" the block number onto the stack.
		 */
		free_stack->block_stack_offset -= sizeof (BlockNumber);
		block_stack = (PBlockNumber) (Memory_Buffer +
				free_stack->block_stack_offset);
		*block_stack = block_number;

		/*
		 *	Indicate that this block is freed.
		 */
		block_information->flags = FREE_FLAG | COMMIT_FLAG;

		/*
		 *	Increment the counter indicating the number of available blocks
		 *	in this free stack.
		 */
		free_stack->current_block_count++;
	}
	else
	{
		/*
		 *	Since the block was allocated from system memory, thats where it
		 *	needs to go back to.
		 */
		copy_ptr = memory->GetPointer ();
		pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
		pExternal_Block_Information->remove ((DWORD_PTR) copy_ptr);
		delete block_information;
		LocalFree ((HLOCAL) copy_ptr);
	}

	/*
	 *	Increment the number of blocks available in this memory manager as a whole.
	 */
	if (TRUE == bAllocs_Restricted)
		Memory_Information->current_block_count++;
}

/*
 *	ULong	CalculateMemoryBufferSize (
 *					PMemoryTemplate		memory_template,
 *					ULong				memory_count,
 *					ULong	*			pulCommittedBytes)
 *
 *	Protected
 *
 *	Functional Description:
 *		This member function is used to calculate how much memory will be
 *		required in order to manage the number of memory blocks specified in
 *		the passed in memory template.  Note that this total includes the size
 *		of the memory blocks as well as the amount of memory used for management
 *		functions.
 *
 *	Formal Parameters:
 *		memory_template
 *			This is an array of structures that identify the blocks to be
 *			managed by this object.
 *		memory_count
 *			This is the number of entries in the above array.
 *		pulCommittedBytes
 *			If fIsSharedMemory == FALSE, this can be NULL.  Otherwise, it is
 *			used to return the size of the total memory we need to commit
 *			when the manager is getting initialized.
 *
 *	Return Value:
 *		The required size of the memory buffer for this object.
 *
 *	Side Effects:
 *		None.
 *
 *	Caveats:
 *		None.
 */

ULong	MemoryManager::CalculateMemoryBufferSize (
				PMemoryTemplate		memory_template,
				ULong				memory_count,
 				ULong	*			pulCommittedBytes)
{
	ULong			memory_buffer_size;
	PMemoryTemplate	pMemTemplate;
	ULong			memory_per_block;

	/*
	 *	Claculate the amount of memory that will be required to hold the
	 *	memory information structure and the free stacks.
	 */
	memory_buffer_size = (sizeof (MemoryInformation) +
			(sizeof (FreeStack) * memory_count));

	if (FALSE == fIsSharedMemory) {
		/*
		 *	Add in the amount of memory the block stacks, the block information
		 *	structures, and the memory blocks themselves will take up.
		 */
		for (pMemTemplate = memory_template; pMemTemplate - memory_template < (int) memory_count; pMemTemplate++)
		{
			/*
			 *	The amount of memory required for each managed block of memory can
			 *	be calculated as a sum of the following:
			 *
			 *	1.	sizeof (BlockNumber) - This is the amount of space taken by the
			 *			block number in the block stack.
			 *	2.	sizeof (BlockInformation) - Every managed block of memory has
			 *			a BlockInformation structure associated with it.
			 *	3.	block_size - The actual size of the block.  This is provided
			 *			in the memory template.
			 */
			memory_per_block = sizeof (BlockNumber) + sizeof (BlockInformation) +
										pMemTemplate->block_size;
			memory_buffer_size += (memory_per_block * pMemTemplate->block_count);
		}
	}

	/*
	 *	For shared memory, we need to do a few more extra things:
	 *
	 *	Blocks of size greater or equal to the system's page, need to
	 *	start on a page boundary. In addition, they can be expanded to
	 *	end at a page boundary, too.
	 */
	else {
	
			ULong	reserved_buffer_size = 0;
			ULong	temp;
			
		for (pMemTemplate = memory_template; pMemTemplate - memory_template < (int) memory_count; pMemTemplate++) {		
			if (dwSystemPageSize <= pMemTemplate->block_size) {
				pMemTemplate->block_size = EXPAND_TO_PAGE_BOUNDARY(pMemTemplate->block_size);
				reserved_buffer_size += pMemTemplate->block_count * pMemTemplate->block_size;
			}
			memory_per_block = sizeof (BlockNumber) + sizeof (BlockInformation) +
								pMemTemplate->block_size;
			memory_buffer_size += memory_per_block * pMemTemplate->block_count;
		}
		*pulCommittedBytes = memory_buffer_size - reserved_buffer_size;
		temp = EXPAND_TO_PAGE_BOUNDARY(*pulCommittedBytes);
		temp -= (*pulCommittedBytes);
		*pulCommittedBytes += temp;
		memory_buffer_size += temp;
		ASSERT (*pulCommittedBytes <= memory_buffer_size);
		ASSERT ((memory_buffer_size % dwSystemPageSize) == 0);
		ASSERT ((*pulCommittedBytes % dwSystemPageSize) == 0);
		ASSERT ((reserved_buffer_size % dwSystemPageSize) == 0);
	}

	return (memory_buffer_size);
}


/*
 *	Void	AllocateMemoryBuffer (
 *					ULong		memory_buffer_size)
 *
 *	Protected
 *
 *	Functional Description:
 *		This member function allocates the memory that is managed by an instance
 *		of MemoryManager.  It does this using the standard Malloc macro.
 *
 *	Formal Parameters:
 *		memory_buffer_size
 *			The size of the buffer to be allocated.
 *
 *	Return Value:
 *		None.
 *
 *	Side Effects:
 *		The instance variable Memory_Buffer is set to the address of the
 *		allocated block of memory.  If it is NULL after the return from this
 *		call, that indicates that the memory could not be allocated.
 *
 *	Caveats:
 *		None.
 */
Void	MemoryManager::AllocateMemoryBuffer (
				ULong		memory_buffer_size)
{
	TRACE_OUT(("MemoryManager::AllocateMemoryBuffer: allocating %ld bytes", memory_buffer_size));
	if (memory_buffer_size != 0)
		Memory_Buffer = (HPUChar) LocalAlloc (LMEM_FIXED, memory_buffer_size);
	else
		Memory_Buffer = NULL;
}


/*
 *	Void	InitializeMemoryBuffer (
 *					PMemoryTemplate		memory_template,
 *					ULong				memory_count)
 *
 *	Protected
 *
 *	Functional Description:
 *		This member function is used to initialize the memory buffer for use.
 *		This primarily includes filling in the management structures that lie
 *		at the beginning of the allocated memory block, so that allocations
 *		can take place.
 *
 *	Formal Parameters:
 *		memory_template
 *			This is an array of structures that identify the blocks to be
 *			managed by this object.
 *		memory_count
 *			This is the number of entries in the above array.
 *
 *	Return Value:
 *		None.
 *
 *	Side Effects:
 *		None.
 *
 *	Caveats:
 *		None.
 */

Void	MemoryManager::InitializeMemoryBuffer (
				PMemoryTemplate		memory_template,
				ULong				memory_count)
{
	ULong				block_count = 0;
	ULong				index;
	ULong				memory_information_size;
	ULong				free_stack_size;
	ULong				free_stack_offset;
	ULong				block_stack_size;
	ULong				block_information_size;
	PFreeStack			free_stack;
	PBlockNumber		block_stack;
	PBlockInformation	block_information;
	ULong				block_stack_offset;
	BlockNumber			block_number;
	ULong				block_offset;
	ULong				block_size;
	ULong				count;
	BOOL			fIsFirstTime;

	/*
	 *	Walk through the memory template calculating how many memory blocks
	 *	exist (regardless of size).
	 */
	for (index = 0; index < memory_count; index++)
		block_count += memory_template[index].block_count;

	/*
	 *	Calculate the amount of memory required to hold all the various sections
	 *	of data in the memory buffer.
	 */
	memory_information_size = sizeof (MemoryInformation);
	free_stack_size = sizeof (FreeStack) * memory_count;
	block_stack_size = sizeof (BlockNumber) * block_count;
	block_information_size = sizeof (BlockInformation) * block_count;

	/*
	 *	Initialize all elements of the memory information structure.
	 *	Note that all offsets in this structure are from the beginning of the
	 *	memory buffer.
	 */
	Memory_Information = (PMemoryInformation) Memory_Buffer;
	Memory_Information->free_stack_offset = memory_information_size;
	Memory_Information->free_stack_count = memory_count;
	Memory_Information->block_information_offset =
			memory_information_size + free_stack_size + block_stack_size;
	Memory_Information->total_block_count = block_count;
	if (TRUE == bAllocs_Restricted) {
		// The current_block_count is only needed when allocations are restricted.
		Memory_Information->current_block_count = block_count + Max_External_Blocks;
	}

	/*
	 *	Now initialize the instance variables that point to each list within
	 *	the memory buffer.  These instance variables are later used to resolve
	 *	all other offsets.
	 */
	Free_Stack = (PFreeStack) (Memory_Buffer + memory_information_size);
	Free_Stack_Count = memory_count;
	Block_Information = (Memory_Buffer +
			Memory_Information->block_information_offset);

	/*
	 *	This loop walks through the memory template array again, this time
	 *	filling in the contents of the free stacks, the blocks stacks, and
	 *	the block information structures.
	 */
	fIsFirstTime = TRUE;
	free_stack = Free_Stack;
	free_stack_offset = memory_information_size;
	block_stack_offset = memory_information_size + free_stack_size;
	block_stack = (PBlockNumber) (Memory_Buffer + block_stack_offset);
	block_information = (PBlockInformation) Block_Information;
	block_number = 0;
	block_offset = block_stack_offset + block_stack_size + block_information_size;

	for (index = 0; index < memory_count; index++)
	{
		/*
		 *	Get the block size and count from the template entry.
		 */
		block_size = memory_template[index].block_size;
		block_count = memory_template[index].block_count;

		/*
		 *	Initialize the free stack for this block size, and then point to
		 *	the next free stack in the list.
		 */
		free_stack->block_size = block_size;
		free_stack->total_block_count = block_count;
		free_stack->current_block_count = block_count;
		(free_stack++)->block_stack_offset = block_stack_offset;

		/*
		 *	Adjust the block stack offset to point to the first block number
		 *	of the next free stack (skip past all of the block numbers for
		 *	this free stack).
		 */
		block_stack_offset += (sizeof (BlockNumber) * block_count);

		/*
		 *	The following happens only once in this loop:
		 *	When the memory manager manages shared memory and
		 *	The block size becomes FOR THE 1ST TIME, bigger than
		 *	the page size, then, we need to jump to the next page
		 *	boundary.
		 */
		if ((TRUE == fIsSharedMemory) && (TRUE == fIsFirstTime)
			&& (block_size >= dwSystemPageSize)) {
			fIsFirstTime = FALSE;
			block_offset = EXPAND_TO_PAGE_BOUNDARY(block_offset);
		}
		
		/*
		 *	Initialize the block list for this block size.  Also, increment
		 *	the total number of buffers for each block that is segmented
		 *	off.
		 */
		for (count = 0; count < block_count; count++)
		{
			/*
			 *	Put the block number for this block into the current block
			 *	stack.  Increment both the block stack pointer and the block
			 *	number.
			 */
			*(block_stack++) = block_number++;

			/*
			 *	Fill in the block information structure for this block.  Then
			 *	increment the block information pointer to point to the next
			 *	entry in the list.
			 */
#ifdef _DEBUG
			if ((TRUE == fIsSharedMemory) && (block_size >= dwSystemPageSize)) {
				ASSERT ((block_size % dwSystemPageSize) == 0);
				ASSERT ((block_offset % dwSystemPageSize) == 0);
			}
#endif
			block_information->block_offset = block_offset;
			block_information->free_stack_offset = free_stack_offset;
			if ((TRUE == fIsSharedMemory) && (block_size >= dwSystemPageSize))
				block_information->flags = FREE_FLAG;
			else
				block_information->flags = FREE_FLAG | COMMIT_FLAG;
			block_information++;

			/*
			 *	Adjust the block offset to point to the next block.
			 */
			block_offset += block_size;
		}
		
		free_stack_offset += sizeof (FreeStack);
	}
}