//=======================================================================
//
//  Copyright (c) 1998-2000 Microsoft Corporation.  All Rights Reserved.
//
//  File:   MemUtil.CPP
//	Author:	Charles Ma, 10/13/2000
//
//	Revision History:
//
//
//
//
//  Description:
//
//      Implement IU memory utility library
//
//=======================================================================

#include <windows.h>
#include <MemUtil.h>




// *******************************************************************************
//
//	Implementation of class CSmartHeapMem
//
// *******************************************************************************


const size_t ArrayGrowChunk = 4;

//
// constructor
//
CSmartHeapMem::CSmartHeapMem()
{
	m_ArraySize		= 0;
	m_lppMems		= NULL;
	m_Heap			= GetProcessHeap();
}



//
// desctructor
//
CSmartHeapMem::~CSmartHeapMem()
{
	if (NULL != m_Heap)
	{
		for (size_t i = 0; i < m_ArraySize; i++)
		{
			if (NULL != m_lppMems[i])
				HeapFree(m_Heap, 0, m_lppMems[i]);
		}
		HeapFree(m_Heap, 0, m_lppMems);
	}
}


//
// allocate mem
//
LPVOID CSmartHeapMem::Alloc(size_t nBytes, DWORD dwFlags /*= HEAP_ZERO_MEMORY*/)
{
	int		iNdx;
	LPVOID	pMem			= NULL;
	DWORD	dwBytes			= (DWORD) nBytes;
	DWORD	dwCurrentFlag	= dwFlags & (~HEAP_GENERATE_EXCEPTIONS | 
										 ~HEAP_NO_SERIALIZE);
	
	if (NULL == m_Heap || 0x0 == dwBytes)
	{
		return NULL;
	}

	iNdx = GetUnusedArraySlot();

	if (iNdx < 0 || NULL == m_Heap)
	{
		//
		// out of mem
		//
		return NULL;
	}

	
	pMem = m_lppMems[iNdx] = HeapAlloc(m_Heap, dwCurrentFlag, dwBytes);

	return pMem;
}



//
// reallocate mem
//
LPVOID CSmartHeapMem::ReAlloc(LPVOID lpMem, size_t nBytes, DWORD dwFlags)
{
	LPVOID	pMem			= NULL;
	DWORD	dwBytes			= (DWORD) nBytes;
	DWORD	dwCurrentFlag	= dwFlags & (~HEAP_GENERATE_EXCEPTIONS | 
										 ~HEAP_NO_SERIALIZE);
	int n;

	if (0x0 == dwBytes || NULL == m_Heap)
	{
		return NULL;
	}

	n = FindIndex(lpMem);
	if (n < 0)
	{
		return NULL;
	}

	pMem = HeapReAlloc(m_Heap, dwCurrentFlag, lpMem, dwBytes);
	if (NULL != pMem)
	{
		m_lppMems[n] = pMem;
	}

	return pMem;
}


//
// return the size allocated
//
size_t CSmartHeapMem::Size(LPVOID lpMem)
{
	if (NULL == m_Heap) return 0;
	return HeapSize(m_Heap, 0, lpMem);
}



void CSmartHeapMem::FreeAllocatedMem(LPVOID lpMem)
{
	int n = FindIndex(lpMem);
	if (n < 0 || NULL == m_Heap)
	{
		return;
	}
	HeapFree(m_Heap, 0, lpMem);
	m_lppMems[n] = NULL;
}



//
// get first empty slot from mem pointer array
// expand array if needed
//
int CSmartHeapMem::GetUnusedArraySlot()
{
	int iNdx = -1;
	UINT i;
	LPVOID lpCurrent;
	LPVOID lpTemp;

	if (0 == m_ArraySize)
	{
		if (NULL == (m_lppMems = (LPVOID*)HeapAlloc(
										m_Heap, 
										HEAP_ZERO_MEMORY, 
										ArrayGrowChunk * sizeof(LPVOID))))
		{
			return -1;
		}
		m_ArraySize = ArrayGrowChunk;
	}
	
		
	while (true)
	{
		for (i = 0; i < m_ArraySize; i++)
		{
			if (NULL == m_lppMems[i])
			{
				return i;
			}
		}
		
		//
		// if come to here, we didn't find an empty slot
		//
		if (NULL == (lpTemp = HeapReAlloc(
										m_Heap, 
										HEAP_ZERO_MEMORY, 
										m_lppMems, 
										(m_ArraySize + ArrayGrowChunk) * sizeof(LPVOID))))
		{
			//
			// when fail, original mem buffer pointed by m_lppMems untouched, 
			// we we simply return -1 to signal caller that no more free slots.
			//
			return -1;
		}

		//
		// when success, the mem pointers previously stored in m_lppMems already
		// been copied to lpTemp, and lppMems was freed.
		//

		//
		// assign the newly allocated mems to m_lppMems in success case
		//
		m_lppMems = (LPVOID *) lpTemp;

		m_ArraySize += ArrayGrowChunk;

		//
		// go back to loop again
		//
	}
}



//
// based on mem pointer, find index
//
int CSmartHeapMem::FindIndex(LPVOID pMem)
{
	if (NULL == pMem) return -1;
	for (size_t i = 0; i < m_ArraySize; i++)
	{
		if (pMem == m_lppMems[i]) return (int)i;
	}
	return -1;
}






// *******************************************************************************
//
//	Other memory related functions
//
// *******************************************************************************



//
// implemenation of CRT memcpy() function
//
LPVOID MyMemCpy(LPVOID dest, const LPVOID src, size_t nBytes)
{
	LPBYTE lpDest = (LPBYTE)dest;
	LPBYTE lpSrc = (LPBYTE)src;

	if (NULL == src || NULL == dest || src == dest)
	{
		return dest;
	}

	while (nBytes-- > 0)
	{
		*lpDest++ = *lpSrc++;
	}
	
	return dest;
}


//
// allocate heap mem and copy
//
LPVOID HeapAllocCopy(LPVOID src, size_t nBytes)
{
	LPVOID pBuffer;

	if (0 == nBytes)
	{
		return NULL;
	}
	
	pBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nBytes);
	if (NULL != pBuffer)
	{
		MyMemCpy(pBuffer, src, nBytes);
	}
	return pBuffer;
}