#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_MEMORY);
/*
 *	memmgr.cpp
 *
 *	Copyright (c) 1998 by Microsoft Corporation, Redmond, WA
 *
 *	Abstract:
 *		This is the implementation file for the T.120 memory allocation mechanism.  This
 *		file contains the code necessary to allocate and distribute memory
 *		in the form of Memory objects.
 *
 *	This implementation defines priorities of memory allocations.  A lower
 *	priority number implies higher priority.  Priority-0 allocations will be
 *	satisfied, unless the system is out of memory.  Priorities 1 and 2
 *	limit the amount of total memory that can be allocated, but priority 1 (recv priority)
 *	has higher water mark limits than priority 2 (send priority).
 *
 *	Protected Member Functions:
 *		None.
 *
 *	Caveats:
 *		None.
 *
 *	Author:
 *		Christos Tsollis
 */
static int						s_anCurrentSize[MEMORY_PRIORITIES] = { 0, 0, 0 };
static const int				sc_iLimit[MEMORY_PRIORITIES] = {
										0x7FFFFFFF,
										0x100000,
										0xE0000
								};

#ifdef DEBUG
static int						s_TotalSize = 0;
#endif // DEBUG

/*
 *	PMemory		AllocateMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to allocate a buffer together with a
 *		Memory (buffer header) object
 */
PMemory	AllocateMemory (
					PUChar				reference_ptr,
					UINT				length,
					MemoryPriority		priority)
{

	PUChar				copy_ptr;
	PMemory				memory;
						
	ASSERT (length > 0);

	if (s_anCurrentSize[priority] < sc_iLimit[priority]) {
		/*
		 *	We attempt to allocate enough space for the buffer and the
		 *	Memory object.
		 */
#ifdef DEBUG
		memory = (PMemory) new BYTE[length + sizeof (Memory)];
#else // DEBUG
		memory = (PMemory) LocalAlloc (LMEM_FIXED, length + sizeof (Memory));
#endif // DEBUG
	}
	else {
		/*
		 *	The application has attempted to allocate past its limit
		 *	It is necessary to fail the request.
		 */
		memory = NULL;
		WARNING_OUT (("AllocateMemory: attempt to allocate past the allowable limit. "
					  "Request: %d. Currently allocated: %d. Priority: %d",
					  length, s_anCurrentSize[priority], priority));
	}

	/*
	 *	Check to see whether the allocation was successful.
	 */
	if (memory != NULL) {
#ifdef DEBUG
		s_TotalSize += (int) length;
#endif // DEBUG
		/*
		 * Update the currently allocated size. Notice that we only
		 * do this for buffers used in the send/recv code path in
		 * MCS.  Since this is only one thread, we do not have to
		 * use a critical section to protect the size variable.
		 */
		ASSERT (s_anCurrentSize[priority] >= 0);
		s_anCurrentSize[priority] += (int) length;

		copy_ptr = (PUChar) memory + sizeof(Memory);
		memory->Init (reference_ptr, length, priority, copy_ptr);

		TRACE_OUT (("Allocate: successful request. "
						"Request: %d. Currently allocated: %d. Total: %d. Priority: %d",
					  	length, s_anCurrentSize[priority], s_TotalSize, priority));
		TRACE_OUT (("AllocateMemory: buffer at address %p; memory segment at address %p",
					copy_ptr, memory));
	}
	else {
		/*
		 *	We failed to allocate the requested size
		 *	It is necessary to fail the request.
		 */
		WARNING_OUT (("AllocateMemory: failed to allocated buffer.  We are out of system memory. "
					 "Request: %d. Last error: %d",
					 length, GetLastError()));
	}
	return (memory);
}

/*
 *	PUChar		ReAllocate ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to re-allocate a buffer with a Memory
 *		(buffer header) object. The buffer must have been allocated by
 *		a call to AllocateMemory.  This call assumes RECV_PRIORITY.  However,
 *		it's not restricted in allocations, because, if it did, this might
 *		cause deadlocks (some memory has already been allocated for the
 *		new arriving data).
 */
BOOL ReAllocateMemory (PMemory		*pmemory,
						UINT		length)
{

	PUChar				copy_ptr = NULL;
	UINT				new_length;
	MemoryPriority		priority;
						
	ASSERT (length > 0);
	ASSERT (pmemory != NULL);
	ASSERT (*pmemory != NULL);
	ASSERT ((*pmemory)->GetPointer());

	new_length = length + (*pmemory)->GetLength();
	priority = (*pmemory)->GetMemoryPriority();

	ASSERT (priority == RECV_PRIORITY);
	
	// We attempt to allocate enough space for the buffer.
#ifdef DEBUG
	copy_ptr = (PUChar) new BYTE[new_length + sizeof(Memory)];
	if (copy_ptr != NULL) {
		memcpy (copy_ptr, *pmemory, (*pmemory)->GetLength() + sizeof(Memory));
		delete [] (BYTE *) *pmemory;
	}
#else // DEBUG
	copy_ptr = (PUChar) LocalReAlloc ((HLOCAL) *pmemory,
										new_length + sizeof(Memory),
										LMEM_MOVEABLE);
#endif // DEBUG

	/*
	 *	Check to see whether the allocation was successful.
	 */
	if (copy_ptr != NULL) {

#ifdef DEBUG
		s_TotalSize += (int) length;
#endif // DEBUG
		/*
		 * Update the currently allocated size.
		 */
		ASSERT (s_anCurrentSize[priority] >= 0);
		s_anCurrentSize[priority] += (int) length;
		*pmemory = (PMemory) copy_ptr;
		copy_ptr += sizeof (Memory);
		(*pmemory)->Init (NULL, new_length, priority, copy_ptr);

		TRACE_OUT (("ReAllocate: successful request. "
					"Request: %d. Currently allocated: %d. Total: %d",
				  	length, s_anCurrentSize[priority], s_TotalSize));
		TRACE_OUT (("ReAllocate: buffer at address %p; memory segment at address %p",
					copy_ptr, *pmemory));
	}
	else {
		/*
		 *	We failed to allocate the requested size
		 *	It is necessary to fail the request.
		 */
		WARNING_OUT (("ReAllocate: failed to allocated buffer.  We are out of system memory. "
					 "Request: %d. Currently allocated: %d. Last error: %d",
					 length, s_anCurrentSize[priority], GetLastError()));
	}

	return (copy_ptr != NULL);
}

/*
 *	Void	FreeMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to release a previously allocated Memory object.
 */
void FreeMemory (PMemory	memory)
{
	if (memory != NULL) {

		ASSERT (SIGNATURE_MATCH(memory, MemorySignature));
		ASSERT (memory->GetPointer() == (PUChar) memory + sizeof(Memory));
		
		if (memory->Unlock() == 0) {

    	    MemoryPriority		priority = memory->GetMemoryPriority();

			TRACE_OUT (("FreeMemory: buffer at address %p (memory segment at address %p) freed. Size: %d. ",
						memory->GetPointer(), memory, memory->GetLength()));

			// We may need to adjust the variable tracking the allocated amount of mem.
			ASSERT (s_anCurrentSize[priority] >= (int) memory->GetLength());
			s_anCurrentSize[priority] -= memory->GetLength();
			ASSERT(s_anCurrentSize[priority] >= 0);
#ifdef DEBUG
			s_TotalSize -= memory->GetLength();
#endif // DEBUG
			TRACE_OUT(("FreeMemory: Currently allocated: %d. Total: %d.",
						s_anCurrentSize[priority], s_TotalSize));
			
			// free the buffer, and the memory
#ifdef DEBUG
			delete [] (BYTE *) memory;
#else // DEBUG
			LocalFree ((HLOCAL) memory);
#endif // DEBUG
		}
	}
}

#ifdef DEBUG
/*
 *	PUChar		Allocate ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to allocate a buffer without a Memory
 *		(buffer header) object.
 */
PUChar	Allocate (UINT	length)
{

	PUChar		copy_ptr;
						
	ASSERT (length > 0);

	// We attempt to allocate enough space for the buffer.
	copy_ptr = (PUChar) new BYTE[length];

	/*
	 *	Check to see whether the allocation was successful.
	 */
	if (copy_ptr == NULL) {
		/*
		 *	We failed to allocate the requested size
		 *	It is necessary to fail the request.
		 */
		ERROR_OUT (("Allocate: failed to allocated buffer.  We are out of system memory. "
					 "Request: %d. Last error: %d",
					 length, GetLastError()));
	}
		
	return (copy_ptr);
}
#endif // DEBUG

/*
 *	UINT	GetFreeMemory ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function returns the amount of space that can still be
 *		allocated at the given priority level.  The function should be
 *		called only when send/recv space is allocated.
 */

UINT GetFreeMemory (MemoryPriority		priority)
{
		int		idiff;
	
	ASSERT (priority != HIGHEST_PRIORITY);

	idiff = sc_iLimit[priority] - s_anCurrentSize[priority];
	return ((idiff > 0) ? (UINT) idiff : 0);
}