// Mem.cpp: implementation of the CFreqMem class.
//
//////////////////////////////////////////////////////////////////////

#include <StdAfx.h>
#include "mem.h"

static HANDLE s_hHeap = NULL;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

static BOOL s_blAllowUseBuf = TRUE;
	// 如果本变量被设置为假，则缓冲区功能被禁止，但在 CVarManger 中被自动启用。

BOOL CFreqMem::IsAllowUseBuf ()
{
	return s_blAllowUseBuf;
}

void CFreqMem::AllowUseBuf (BOOL blAllow)
{
	s_blAllowUseBuf = blAllow;
}

//////////////////////////////////////////////////////////////////////

MEM_BUF _m_membuf;	// !!! 不允许直接引用此变量。

CFreqMem::CFreqMem ()
{
	m_pMemBuf = &_m_membuf;
	m_pData = NULL;
	m_nAllocedSize = m_nSize = 0;
}

// IMPLEMENT_SERIAL (CFreqMem, CObject, 1)

LPBYTE CFreqMem::Alloc (INT nSize)
{
	ASSERT (nSize > 0);

	if (m_pData != NULL)
	{
		ASSERT (m_nAllocedSize > 0);
		return Realloc (nSize);
	}

	if (nSize == 0)  return NULL;

	if (s_blAllowUseBuf == TRUE && nSize <= PAGE_SIZE &&
			m_pMemBuf->m_nPageUsedCount < PAGE_COUNT)
	{
		INT n = FindByte (m_pMemBuf->m_btaryUsed, PAGE_COUNT, 0);
		ASSERT (n != -1);

		m_pMemBuf->m_btaryUsed [n] = 1;
		m_pMemBuf->m_nPageUsedCount++;

		m_nAllocedSize = PAGE_SIZE;
		m_pData = m_pMemBuf->m_buf + n * PAGE_SIZE;
		m_nSize = nSize;
		return m_pData;
	}

	if (s_hHeap == NULL)
		s_hHeap = ::GetProcessHeap ();

	m_pData = (LPBYTE)::HeapAlloc (s_hHeap, 0, nSize + FM_GROW_SIZE);
	if (m_pData == NULL)
	{
		m_nAllocedSize = 0;
		m_nSize = 0;
	}
	else
	{
		m_nAllocedSize = nSize + FM_GROW_SIZE;
		m_nSize = nSize;
	}
	return m_pData;
}

LPBYTE CFreqMem::Realloc (INT nSize)
{
	ASSERT (nSize >= 0);
	if (nSize == 0)
	{
		Free ();
		return NULL;
	}
	if (m_pData == NULL)
	{
		ASSERT (m_nAllocedSize == 0);
		return Alloc (nSize);
	}

	BOOL blIsAllocBuf = m_pData >= m_pMemBuf->m_buf &&
			m_pData < m_pMemBuf->m_buf + PAGE_BUF_SIZE;

	if (m_nAllocedSize >= nSize && (blIsAllocBuf == TRUE ||
			m_nAllocedSize - nSize <= FM_MAX_FREE_BUF_SIZE + FM_GROW_SIZE))
	{
		m_nSize = nSize;
		return m_pData;
	}

	if (s_hHeap == NULL)
		s_hHeap = ::GetProcessHeap ();

	LPBYTE pNew;
	if (blIsAllocBuf == TRUE)
	{
		pNew = (LPBYTE)::HeapAlloc (s_hHeap, 0, nSize + FM_GROW_SIZE);
		if (pNew != NULL)
		{
			CopyMemory (pNew, m_pData, min (nSize, m_nSize));
			_Free (m_pData);
			m_pData = pNew;
			m_nAllocedSize = nSize + FM_GROW_SIZE;
			m_nSize = nSize;
			return m_pData;
		}
		else
			return NULL;
	}

	pNew = (LPBYTE)::HeapReAlloc (s_hHeap, 0, m_pData, nSize + FM_GROW_SIZE);
	if (pNew != NULL)
	{
		m_pData = pNew;
		m_nAllocedSize = nSize + FM_GROW_SIZE;
		m_nSize = nSize;
		return m_pData;
	}
	else
		return NULL;
}

void CFreqMem::Free ()
{
	_Free (m_pData);

	m_pData = NULL;
	m_nAllocedSize = m_nSize = 0;
}

void CFreqMem::_Free (LPBYTE pData)
{
	if (pData == NULL)  return;

	if (pData < m_pMemBuf->m_buf || pData >= m_pMemBuf->m_buf + PAGE_BUF_SIZE)
	{
		if (s_hHeap == NULL)
			s_hHeap = ::GetProcessHeap ();
		VERIFY (::HeapFree (s_hHeap, NULL, pData));
	}
	else
	{
		ASSERT ((pData - m_pMemBuf->m_buf) % PAGE_SIZE == 0 &&
				m_pMemBuf->m_btaryUsed [(pData - m_pMemBuf->m_buf) / PAGE_SIZE] == 1 &&
				m_pMemBuf->m_nPageUsedCount > 0);
		m_pMemBuf->m_btaryUsed [(pData - m_pMemBuf->m_buf) / PAGE_SIZE] = 0;
		m_pMemBuf->m_nPageUsedCount--;
	}
}

///////////////////////////////////////////

void CFreqMem::AddFloat	(FLOAT flt)
{
	if (m_nAllocedSize - m_nSize >= sizeof (FLOAT))
	{
		*(FLOAT*)(m_pData + m_nSize) = flt;
		m_nSize += sizeof (FLOAT);
	}
	else
		Append ((LPBYTE)&flt, sizeof (flt));
}

void CFreqMem::AddDouble (DOUBLE db)
{
	if (m_nAllocedSize - m_nSize >= sizeof (DOUBLE))
	{
		*(DOUBLE*)(m_pData + m_nSize) = db;
		m_nSize += sizeof (DOUBLE);
	}
	else
		Append ((LPBYTE)&db, sizeof (DOUBLE));
}

void CFreqMem::AddDWord (DWORD dw)
{
	if (m_nAllocedSize - m_nSize >= sizeof (DWORD))
	{
		*(DWORD*)(m_pData + m_nSize) = dw;
		m_nSize += sizeof (DWORD);
	}
	else
		Append ((LPBYTE)&dw, sizeof (DWORD));
}

void CFreqMem::AddInt64 (INT64 n64)
{
	if (m_nAllocedSize - m_nSize >= sizeof (INT64))
	{
		*(INT64*)(m_pData + m_nSize) = n64;
		m_nSize += sizeof (INT64);
	}
	else
		Append ((LPBYTE)&n64, sizeof (INT64));
}

void CFreqMem::AddInt (INT n)
{
	if (m_nAllocedSize - m_nSize >= sizeof (INT))
	{
		*(INT*)(m_pData + m_nSize) = n;
		m_nSize += sizeof (INT);
	}
	else
		Append ((LPBYTE)&n, sizeof (INT));
}

void CFreqMem::AddWord (WORD w)
{
	if (m_nAllocedSize - m_nSize >= sizeof (WORD))
	{
		*(WORD*)(m_pData + m_nSize) = w;
		m_nSize += sizeof (WORD);
	}
	else
		Append ((LPBYTE)&w, sizeof (WORD));
}

void CFreqMem::AddShort (SHORT sht)
{
	if (m_nAllocedSize - m_nSize >= sizeof (SHORT))
	{
		*(SHORT*)(m_pData + m_nSize) = sht;
		m_nSize += sizeof (SHORT);
	}
	else
		Append ((LPBYTE)&sht, sizeof (SHORT));
}

void CFreqMem::AddByte (BYTE bt)
{
	if (m_nAllocedSize > m_nSize)
		m_pData [m_nSize++] = bt;
	else
		Append ((LPBYTE)&bt, sizeof (BYTE));
}

void CFreqMem::AddBool (BOOL bl)
{
	if (m_nAllocedSize - m_nSize >= sizeof (BOOL))
	{
		*(BOOL*)(m_pData + m_nSize) = bl;
		m_nSize += sizeof (BOOL);
	}
	else
		Append ((LPBYTE)&bl, sizeof (BOOL));
}

void CFreqMem::AddStr (LPTSTR pstr)
{
	if (IsEmptyStr (pstr) == TRUE)
		AddByte (0);
	else
		Append ((LPBYTE)pstr, tcsallbytes (pstr));
}

void CFreqMem::AddText (LPTSTR pstr)
{
	if (IsEmptyStr (pstr) == FALSE)
		Append ((LPBYTE)pstr, tcsbytes (pstr));
}

///////////////////////////////////////////

BOOL CFreqMem::EmptyAndRqSpace (INT nSize)
{
	ASSERT (nSize >= 0);
	if (nSize <= 0)
		nSize = 0;

	if (Realloc (nSize) != NULL)
	{
		m_nSize = 0;
		return TRUE;
	}
	else
		return FALSE;
}

// !! 必须保证不改变两个 MEM 的首地址指针。
void CFreqMem::XchgMem (CFreqMem& mem)
{
	PMEM_BUF pMemBuf = m_pMemBuf;
	m_pMemBuf = mem.m_pMemBuf;
	mem.m_pMemBuf = pMemBuf;

	LPBYTE p = m_pData;
	m_pData = mem.m_pData;
	mem.m_pData = p;

	INT n = m_nAllocedSize;
	m_nAllocedSize = mem.m_nAllocedSize;
	mem.m_nAllocedSize = n;

	n = m_nSize;
	m_nSize = mem.m_nSize;
	mem.m_nSize = n;
}

void CFreqMem::SaveEmpty (CArchive& ar)
{
	ASSERT (ar.IsStoring ());
	ar << (INT)0;
}

void CFreqMem::Serialize (CArchive& ar)
{
	if (ar.IsStoring())
	{
		ar << m_nSize;
		if (m_nSize > 0)
			ar.Write (m_pData, m_nSize);
	}
	else
	{
		Free ();

		INT nSize;
		ar >> nSize;
		if (nSize > 0)
		{
			if (Alloc (nSize) != NULL)
				ar.Read (m_pData, nSize);
			else
				AfxThrowArchiveException (CArchiveException::generic);
		}
	}
}

INT CFreqMem::ReadFile (CString& strFileName, INT nReadLength)
{
	TRY
	{
		CFile file;
		if (file.Open (strFileName, CFile::modeRead | CFile::shareDenyWrite) == TRUE)
		{
			if (nReadLength == -1)
				nReadLength = file.GetLength ();

			if (nReadLength > 0)
			{
				LPBYTE p = Alloc (nReadLength);
				if (p != NULL)
				{
					INT n = file.Read (p, nReadLength);
					if (n != nReadLength)
						Realloc (n);
					return n;
				}
			}
			else
			{
				Free ();
				return 0;
			}
		}
	}
	END_TRY

	Free ();
	return -1;
}

BOOL CFreqMem::WriteFile (CString& strFileName, INT nWriteLength)
{
	ASSERT (strFileName.IsEmpty () == FALSE);

	TRY
	{
		INT nSize = GetSize ();
		if (nWriteLength == -1)
			nWriteLength = nSize;
		else
			nWriteLength = min (nWriteLength, nSize);

		CFile file;
		if (file.Open (strFileName, CFile::modeCreate | CFile::modeWrite |
				CFile::shareExclusive) == TRUE)
		{
			if (nWriteLength > 0)
				file.Write (GetPtr (), nWriteLength);

			file.Close ();
			return TRUE;
		}
	}
	END_TRY

	return FALSE;
}

////////////////////////////

// if pData == NULL, insert blank data.
BOOL CFreqMem::Insert (INT nOffset, LPBYTE pData, INT nSize)
{
	ASSERT (nOffset >= 0 && nSize >= 0);
	if (nSize == 0)  return TRUE;
	if (nOffset == m_nSize)  return Append (pData, nSize);

	INT nOldSize = m_nSize;
	LPBYTE pMem = Realloc (max (nOffset, nOldSize) + nSize);
	if (pMem == NULL)  return FALSE;

	LPBYTE pInsert = pMem + nOffset;
	if (nOffset < nOldSize)
		MoveMem (pInsert + nSize, pInsert, nOldSize - nOffset);
	
	if (pData != NULL)
	{
		ASSERT (AfxIsValidAddress (pData, nSize));
		MoveMem (pInsert, pData, nSize);
	}
	else
		ZeroMem (pInsert, nSize);

	return TRUE;
}

BOOL CFreqMem::AppendSpace (INT nSpaceCount)
{
	ASSERT (nSpaceCount >= 0);

	if (Append (NULL, nSpaceCount) == TRUE)
	{
		if (nSpaceCount > 0)
			memset (GetPtr () + GetSize () - nSpaceCount, ' ', nSpaceCount);
		return TRUE;
	}

	return FALSE;
}

// if pData == NULL, append blank data.
BOOL CFreqMem::Append (LPBYTE pData, INT nSize)
{
	ASSERT (nSize >= 0);
	if (nSize <= 0)  return TRUE;

	INT nOldSize = m_nSize;
	LPBYTE pMem = Realloc (nOldSize + nSize);
	if (pMem == NULL)  return FALSE;

	LPBYTE pAppend = pMem + nOldSize;
	if (pData != NULL)
		MoveMem (pAppend, pData, nSize);
	else
		ZeroMem (pAppend, nSize);

	return TRUE;
}

// if pReplaceData == NULL, replace blank data.
BOOL CFreqMem::Replace (INT nOffset, INT nSize, LPBYTE pReplaceData, INT nReplaceSize)
{
	ASSERT (nOffset >= 0 && nSize >= 0 && nReplaceSize >= 0);

	INT nOldSize = m_nSize;
	if (nSize == 0 || nOffset >= nOldSize)
		return Insert (nOffset, pReplaceData, nReplaceSize);
	if (nReplaceSize == 0)  
		return Remove (nOffset, nSize);

	if (nSize > nOldSize - nOffset)  nSize = nOldSize - nOffset;

	LPBYTE pReplaceAt;
	INT nMoveSize = nOldSize - nOffset - nSize;
	if (nReplaceSize > nSize)
	{
		pReplaceAt = Realloc (nOldSize + nReplaceSize - nSize);
		if (pReplaceAt == NULL)  return FALSE;
		pReplaceAt += nOffset;

		if (nMoveSize > 0)
			MoveMem (pReplaceAt + nReplaceSize, pReplaceAt + nSize, nMoveSize);
	}
	else
	{
		pReplaceAt = m_pData;
		ASSERT (pReplaceAt != NULL);
		pReplaceAt += nOffset;

		if (nReplaceSize < nSize)
		{
			if (nMoveSize > 0)
				MoveMem (pReplaceAt + nReplaceSize, pReplaceAt + nSize, nMoveSize);

			pReplaceAt = Realloc (nOldSize - (nSize - nReplaceSize));
			ASSERT (pReplaceAt != NULL);
			if (pReplaceAt == NULL)  return FALSE;  // data already be changed!
			pReplaceAt += nOffset;
		}
	}

	if (pReplaceData != NULL)
	{
		ASSERT (AfxIsValidAddress (pReplaceData, nReplaceSize, FALSE));
		MoveMem (pReplaceAt, pReplaceData, nReplaceSize);
	}
	else
		ZeroMem (pReplaceAt, nReplaceSize);
	return TRUE;
}

// if nOffset == -1, delete data from tail.
BOOL CFreqMem::Remove (INT nOffset, INT nSize)
{
	if (nOffset == -1)
	{
		INT nNewSize = max (0, m_nSize - nSize);
		if (nNewSize == 0)
		{
			Free ();
			return TRUE;
		}
		else
			return Realloc (nNewSize) != NULL;
	}

	if (nSize == 0)  return TRUE;
	if (m_pData == NULL || nOffset >= m_nSize)  return FALSE;
	
	LPBYTE pRemoveAt = m_pData + nOffset;
	
	INT nRemoveSize;
	if (nSize < m_nSize - nOffset)
	{
		nRemoveSize = nSize;
		MoveMem (pRemoveAt, pRemoveAt + nRemoveSize, m_nSize - nOffset - nRemoveSize);
	}
	else
		nRemoveSize = m_nSize - nOffset;

	if (m_nSize == nRemoveSize)
	{
		Free ();
		return TRUE;
	}
	else
		return Realloc (m_nSize - nRemoveSize) != NULL;
}

//////////////////////////////////////////

BOOL CMyDWordArray::SetSize (INT nNewSize)
{
	if (nNewSize == 0)
	{
		Free ();
	}
	else
	{
		nNewSize *= sizeof (DWORD);
		LPBYTE p = Alloc (nNewSize);
		if (p != NULL)
			ZeroMem (p, nNewSize);
		else
			return FALSE;
	}

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////

INT FindInt (LPINT pnTable, INT nCount, INT nFind)
{
	return FindDWord ((LPDWORD)pnTable, nCount, (DWORD)nFind);
}

BOOL IsEmptyStr (LPTSTR szStr)
{
	return szStr == NULL || *szStr == 0;
}

void MoveMem (LPBYTE pDest, LPBYTE pSrc, DWORD dwLength)
{
	if (dwLength == 0)
		return;
	else if (dwLength == 1)
		*pDest = *pSrc;
	else if (dwLength == sizeof (WORD))
		*(LPWORD)pDest = *(LPWORD)pSrc;
	else if (dwLength == sizeof (DWORD))
		*(LPDWORD)pDest = *(LPDWORD)pSrc;
	else
		::MoveMemory (pDest, pSrc, dwLength);
}

void CopyMem (LPBYTE pDest, LPBYTE pSrc, DWORD dwLength)
{
	if (dwLength == 0)
		return;
	else if (dwLength == 1)
		*pDest = *pSrc;
	else if (dwLength == sizeof (WORD))
		*(LPWORD)pDest = *(LPWORD)pSrc;
	else if (dwLength == sizeof (DWORD))
		*(LPDWORD)pDest = *(LPDWORD)pSrc;
	else
		::CopyMemory (pDest, pSrc, dwLength);
}

void ZeroMem (LPBYTE pDest, DWORD dwLength)
{
	if (dwLength == 0)
		return;
	else if (dwLength == 1)
		*pDest = 0;
	else if (dwLength == sizeof (WORD))
		*(LPWORD)pDest = 0;
	else if (dwLength == sizeof (DWORD))
		*(LPDWORD)pDest = 0;
	else
		::ZeroMemory (pDest, dwLength);
}

INT tcsallbytes (LPTSTR pstr)
{
	return (_tcslen (pstr) + 1) * sizeof (TCHAR);
/*
	if (*pstr == '\0')  return 1;

	DWORD dw;

	_asm
	{
		push	eax
		push	ebx
		push	esi

		mov		esi, dword ptr [pstr]
		mov		ebx, esi

		cld
	l0:
		lodsb
		or		al, al
		jne		l0

		sub		esi, ebx
		mov		dw, esi

		pop		esi
		pop		ebx
		pop		eax
	}

	return (INT)dw; */
}

INT tcsbytes (LPTSTR pstr)
{
	return _tcslen (pstr) * sizeof (TCHAR);
/*
	if (pstr == NULL || *pstr == '\0')  return 0;

	DWORD dw;

	_asm
	{
		push	eax
		push	ebx
		push	esi

		mov		esi, dword ptr [pstr]
		mov		ebx, esi

		cld
	l0:
		lodsb
		or		al, al
		jne		l0

		dec		esi
		sub		esi, ebx
		mov		dw, esi

		pop		esi
		pop		ebx
		pop		eax
	}

	return (INT)dw; */
}

// 返回的是字节偏移数。
INT FindByte (LPBYTE pTable, INT count, BYTE btFind)
{ 
	INT nRetOffset = -1;

	if (count > 0)
	{
		ASSERT (pTable != NULL);
		if (*pTable == btFind)  return 0;

		__asm
		{
			push edi
			push eax
			push ecx

			mov al, btFind
			mov ecx, count
			push ecx
			mov edi, dword ptr [pTable]

	        cld
			repne scasb
			pop eax
			jnz quit

			inc ecx
			sub eax, ecx
			mov dword ptr [nRetOffset], eax
quit:
			pop ecx
			pop eax
			pop edi
		}
	}

	return nRetOffset;
}

// 返回的不是字节数，而是DWORD偏移数。
INT FindDWord (LPDWORD pdwTable, INT count, DWORD dwFind)
{ 
	INT nRetOffset = -1;

	if (count > 0)
	{
		if (*pdwTable == dwFind)  return 0;

		ASSERT (pdwTable != NULL);
		__asm
		{
			push edi
			push eax
			push ecx

			mov eax, dwFind
			mov ecx, count
			push ecx
			mov edi, dword ptr [pdwTable]

	        cld
			repne scasd
			pop eax
			jnz quit

			inc ecx
			sub eax, ecx
			mov dword ptr [nRetOffset], eax
quit:
			pop ecx
			pop eax
			pop edi
		}
	}

	return nRetOffset;
}

// 返回的不是字节数，而是WORD偏移数。
INT FindWord (LPWORD pwTable, INT count, WORD wFind)
{ 
	INT nRetOffset = -1;

	if (count > 0)
	{
		ASSERT (pwTable != NULL);
		__asm
		{
			push edi
			push eax
			push ecx

			mov ax, wFind
			mov ecx, count
			push ecx
			mov edi, dword ptr [pwTable]

	        cld
			repne scasw
			pop eax
			jnz quit

			inc ecx
			sub eax, ecx
			mov dword ptr [nRetOffset], eax
quit:
			pop ecx
			pop eax
			pop edi
		}
	}

	return nRetOffset;
}

