/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbSharedMem
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbSharedMem.h"
#include "xbFileTools.h"
#include "xbException.h"

#define	SHAREDMEMHDR_PAGEMINBYHDR	80

#pragma warning(push)
#pragma warning(disable: 4355) //'this' : utilisé dans la liste des initialiseurs membre de base

const GUID &xbSharedHeap::GetFileID() const
{
	return __uuidof(xbSharedMemHdr);
}

#ifdef _dbgMEM
#	define NOMANSLAND_LG		8
#	define NOMANSLAND_LGEND		8
xbString dbgCheck(const char *pZname,BYTE ch,uint64 offs,BYTE *pSeg,ulong cByte)
{
	xbString str;
	while(cByte > 0)
	{
		if (*pSeg != ch) 
			str += xbFormat("\n -> %016I64X: %s zone, ch=0x%02lX and should be 0x%02lX",offs,pZname,uint(*pSeg),uint(ch));
		pSeg++;
		cByte--;
		offs++;
	}
	return str;
}

void dbgCheckNoMansLand(const xbString &filepath,uint64 offs,BYTE *pSeg,ulong cByte)
{
	xbString str(dbgCheck("NoMansLand",0xFD,offs,pSeg,cByte));
	if (str.IsValid())
	{
		xbDbgOutput("xbSharedHeap('"+filepath+"')::dbgCheckNoMansLand() errors :"+str);
	}
}

void dbgMarkAsNew(SharedBlockHdr *pBlock,ulong cByte)
{
	memset(pBlock+1,0xFD,NOMANSLAND_LG);
	memset((BYTE*)(pBlock+1)+NOMANSLAND_LG,0xCD,cByte-NOMANSLAND_LG-NOMANSLAND_LGEND-sizeof(SharedBlockHdr));
	memset((BYTE*)pBlock+cByte-NOMANSLAND_LGEND,0xFD,NOMANSLAND_LGEND);
}

void dbgMarkAsFreed(SharedBlockHdr *pBlock,ulong cByte)
{
	memset(pBlock+1,0xFD,NOMANSLAND_LG);
	memset((BYTE*)(pBlock+1)+NOMANSLAND_LG,0xDD,cByte-NOMANSLAND_LG-NOMANSLAND_LGEND-sizeof(SharedBlockHdr));
	memset((BYTE*)pBlock+cByte-NOMANSLAND_LGEND,0xFD,NOMANSLAND_LGEND);
}

void dbgMarkAsNoMansLand(BYTE *pSeg,ulong cByte)
{
	memset(pSeg,0xFD,cByte);
}

void dbgCheckFreed(const xbString &filepath,uint64 offs,SharedBlockHdr *pBlock)
{
	xbAssert(pBlock->m_cLock == 0);
	xbString str(dbgCheck("NoMansLand Block",0xFD,offs,(BYTE*)(pBlock+1),NOMANSLAND_LG));
	str+=dbgCheck("Freed Block",0xDD,offs+sizeof(SharedBlockHdr),(BYTE*)(pBlock+1)+NOMANSLAND_LG,pBlock->m_cByte-NOMANSLAND_LG-NOMANSLAND_LGEND-sizeof(SharedBlockHdr));
	str+=dbgCheck("NoMansLand BlockEnd",0xFD,offs+pBlock->m_cByte-NOMANSLAND_LGEND,(BYTE*)pBlock+pBlock->m_cByte-NOMANSLAND_LGEND,NOMANSLAND_LGEND);
	
	if (str.IsValid())
	{
		xbDbgOutput("xbSharedHeap('"+filepath+"')::dbgCheckFreed() errors :"+str);
	}
}

void dbgCheckNew(const xbString &filepath,uint64 offs,SharedBlockHdr *pBlock)
{
	xbAssert(pBlock->m_cLock > 0 && pBlock->m_cLock < 0x00010000);
	xbString str(dbgCheck("NoMansLand Block",0xFD,offs,(BYTE*)(pBlock+1),NOMANSLAND_LG));
	str+=dbgCheck("NoMansLand BlockEnd",0xFD,offs+pBlock->m_cByte-NOMANSLAND_LGEND,(BYTE*)pBlock+pBlock->m_cByte-NOMANSLAND_LGEND,NOMANSLAND_LGEND);
	
	if (str.IsValid())
	{
		xbDbgOutput("xbSharedHeap('"+filepath+"')::dbgCheckNew() errors :"+str);
	}
}

#	define dbgPageCheck(offs,pBlock) \
		xbAssert(!pBlock || pBlock->m_cByte % m_pHeap->m_cByteSect == 0); \
		xbAssert(!pBlock || pBlock->m_cBytePrev % m_pHeap->m_cByteSect == 0); \
		if (pBlock) \
		{ \
			if (pBlock->m_cLock > 0)  \
				dbgCheckNew(m_pHeap->m_filepath,m_offset+offs,pBlock); \
			else \
				dbgCheckFreed(m_pHeap->m_filepath,m_offset+offs,pBlock); \
		}

#	define dbgPageCheckFreed(offs,pBlock) \
		xbAssert(!pBlock || pBlock->m_cByte % m_pHeap->m_cByteSect == 0); \
		xbAssert(!pBlock || pBlock->m_cBytePrev % m_pHeap->m_cByteSect == 0); \
		if (pBlock) dbgCheckFreed(m_pHeap->m_filepath,m_offset+offs,pBlock);

#	define dbgPageCheckNew(offs,pBlock) \
		xbAssert(!pBlock || pBlock->m_cByte % m_pHeap->m_cByteSect == 0); \
		xbAssert(!pBlock || pBlock->m_cBytePrev % m_pHeap->m_cByteSect == 0); \
		if (pBlock) dbgCheckNew(m_pHeap->m_filepath,m_offset+offs,pBlock);

#	define dbgHeapCheck(page,offs,pBlock) \
		xbAssert(!pBlock || pBlock->m_cByte % m_cByteSect == 0); \
		xbAssert(!pBlock || pBlock->m_cBytePrev % m_cByteSect == 0); \
		if (pBlock) \
		{ \
			if (pBlock->m_cLock > 0)  \
				dbgCheckNew(m_filepath,page->m_offset+offs,pBlock); \
			else \
				dbgCheckFreed(m_filepath,page->m_offset+offs,pBlock); \
		}

#	define dbgHeapCheckNew(page,offs,pBlock) \
		xbAssert(!pBlock || pBlock->m_cByte % m_cByteSect == 0); \
		xbAssert(!pBlock || pBlock->m_cBytePrev % m_cByteSect == 0); \
		if (pBlock) dbgCheckNew(m_filepath,page->m_offset+offs,pBlock);

#	define dbgHeapCheckFreed(page,offs,pBlock) \
		xbAssert(!pBlock || pBlock->m_cByte % m_cByteSect == 0); \
		xbAssert(!pBlock || pBlock->m_cBytePrev % m_cByteSect == 0); \
		if (pBlock) dbgCheckFreed(m_filepath,page->m_offset+offs,pBlock);
#else
#	define NOMANSLAND_LG		0
#	define NOMANSLAND_LGEND		0
#	define dbgCheck(pZname,ch,offs,pSeg,cByte)
#	define dbgCheckNoMansLand(filepath,offs,pSeg,cByte)
#	define dbgMarkAsNew(pBlock,cByte)
#	define dbgMarkAsFreed(pBlock,cByte)
#	define dbgMarkAsNoMansLand(pSeg,cByte)
#	define dbgCheckFreed(filepath,offs,pBlock)
#	define dbgCheckNew(filepath,offs,pBlock)
#	define dbgPageCheck(offs,pBlock)
#	define dbgPageCheckFreed(offs,pBlock)
#	define dbgPageCheckNew(offs,pBlock)
#	define dbgHeapCheckNew(page,offs,pBlock)
#	define dbgHeapCheckFreed(page,offs,pBlock)
#	define dbgHeapCheck(page,offs,pBlock)
#endif

xbSharedHeap::xbSharedHeap(uint64 cByteMax,LPCSTR pFilePath,bool bCreate,ulong cByteLoadedMax,ulong cByteSect,ulong cBytePage)
	:m_mutex(xbCtor_NoOp),m_attr(xbSecurityAll)
{
#ifdef _DEBUG
	SharedBlockHdr	_sectHdr;
#endif
	m_bPermanentShared = false;
	if (pFilePath && *pFilePath != 0)
	{
		m_filepath = pFilePath;
#ifdef _dbgMEM
		m_filepath += "-d";
#endif
#ifdef XDCBASE_X64
		m_filepath += "-x64";
#endif
		if (m_filepath.First() == '!')
		{
			m_name = m_filepath.Mid(1);
		}
		else
		{
			m_name = ::xbGetFileNameExt(pFilePath);
			m_name.Lower();
			m_bPermanentShared = true;
		}
	}
	m_bHeapCreated = false;
	m_cMemLock = 0;
	m_cAskedByteSect = cByteSect;
	m_cAskedBytePage = cBytePage;
	m_cAskedByteLoadedMax = cByteLoadedMax;
	m_cAskedByteMax = cByteMax;
	m_cByteSect = m_cSectPage = m_cBytePage = m_cPageLoadedMax = 0;
	m_cByteMax = 0;
	m_cByteHdr = m_cPageMax = 0;
	m_hFile = INVALID_HANDLE_VALUE;
	m_hMap = NULL;
	m_pHdr = NULL;
	m_pOldestLoadedPage = m_pNewestLoadedPage = NULL;
	m_cPageLoaded = 0;
	m_bNoMemLocks = false;
	m_mutex.Create(&m_attr,m_name.IsEmpty()?xbString():m_name+".EprocessHeapMutex");

	if (bCreate) Create();
}

xbSharedHeap::xbSharedHeap(const xbSecurityAttr &attr,uint64 cByteMax,LPCSTR pFilePath,bool bCreate,ulong cByteLoadedMax,ulong cByteSect,ulong cBytePage)
	:m_mutex(xbCtor_NoOp),m_attr(attr)
{
#ifdef _DEBUG
	SharedBlockHdr	_sectHdr;
#endif
	m_bPermanentShared = false;
	if (pFilePath && *pFilePath != 0)
	{
		m_filepath = pFilePath;
#ifdef _dbgMEM
		m_filepath += "-d";
#endif
#ifdef XDCBASE_X64
		m_filepath += "-x64";
#endif
		if (m_filepath.First() == '!')
		{
			m_name = m_filepath.Mid(1);
		}
		else
		{
			m_name = ::xbGetFileNameExt(m_filepath);
			m_name.Lower();
			m_bPermanentShared = true;
		}
	}
	m_bHeapCreated = false;
	m_cMemLock = 0;
	m_cAskedByteSect = cByteSect;
	m_cAskedBytePage = cBytePage;
	m_cAskedByteLoadedMax = cByteLoadedMax;
	m_cAskedByteMax = cByteMax;
	m_cByteSect = m_cSectPage = m_cBytePage = m_cPageLoadedMax = 0;
	m_cByteMax = 0;
	m_cByteHdr = m_cPageMax = 0;
	m_hFile = INVALID_HANDLE_VALUE;
	m_hMap = NULL;
	m_pHdr = NULL;
	m_pOldestLoadedPage = m_pNewestLoadedPage = NULL;
	m_cPageLoaded = 0;
	m_bNoMemLocks = false;

	m_mutex.Create(&m_attr,m_name.IsEmpty()?xbString():(m_name+".EprocessHeapMutex"));

	if (bCreate) Create();
}

void xbSharedHeap::Create()
{
	xbLock lck(&m_mutex);
	m_bNoMemLocks = false;
	if (m_bHeapCreated) return;
	m_bHeapCreated = true;
	m_bCreated = false;
	xbObject::Create();

	bool bToCreate = false;
	try
	{
		if (m_bPermanentShared)
		{
			m_hFile = ::CreateFile(m_filepath,GENERIC_READ | GENERIC_WRITE,
				FILE_SHARE_READ | FILE_SHARE_WRITE,m_attr,
				OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
			if (m_hFile == INVALID_HANDLE_VALUE)
			{
				lck.Unlock();
				xbThrowEsys(xbHR_LAST,"xbSharedHeap::Create() : can't open file '"+m_filepath+"'");
			}
			ULARGE_INTEGER CurSz;
			CurSz.LowPart=GetFileSize(m_hFile,&CurSz.HighPart);
			if (CurSz.LowPart == 0xffffffff && GetLastError() != NO_ERROR)
			{
				lck.Unlock();
				xbThrowEsys(xbHR_LAST,"xbSharedHeap::Create('"+m_filepath+"') failed");
			}
			if (CurSz.QuadPart == 0) bToCreate = true;
		}

		ulong cGran = GetSysGranularity();
		m_cBytePage = (m_cAskedBytePage + cGran - 1) / cGran;
		m_cBytePage *= cGran;

		m_cPageMax = ulong((m_cAskedByteMax + m_cBytePage - 1) / m_cBytePage);
		if (m_cPageMax < 2) m_cPageMax = 2;
		m_cByteMax = m_cBytePage * m_cPageMax;

		{
			m_cByteHdr = (m_cPageMax-1) * sizeof(xbSharedPageHdr) + sizeof(xbSharedMemHdr);
			m_cByteHdr = (m_cByteHdr + cGran - 1) / cGran;
			m_cByteHdr *= cGran;

			m_cPageMax = (m_cByteHdr - sizeof(xbSharedMemHdr) + sizeof(xbSharedPageHdr)) / sizeof(xbSharedPageHdr);
		}

		m_cByteSect = (m_cAskedByteSect + sizeof(SharedBlockHdr) + 7) / 8;
		m_cByteSect *= 8;
		if (m_cByteSect < 32) m_cByteSect = 32;

		{
//			bool bFirst = true;
			ulong _cByteSect = m_cByteSect;
			while(_cByteSect < (m_cByteSect * 3))
			{
				if ((m_cBytePage % _cByteSect) == 0)
				{
					m_cByteSect = _cByteSect;
					break;
				}
//				if (bFirst && _cByteSect < 48) _cByteSect = 64;
//				bFirst = false;
				_cByteSect += 4;
			}
		}
		m_cSectPage = m_cBytePage / m_cByteSect;
	
		m_cPageLoadedMax = (m_cAskedByteLoadedMax + m_cBytePage - 1) / m_cBytePage;
		if (m_cPageLoadedMax < 2) m_cPageLoadedMax = 2;

		ULARGE_INTEGER sz;
		sz.QuadPart = m_cByteMax + m_cByteHdr;

		xbString	name;
		LPCSTR		pName;
		if (m_name.IsEmpty()) pName = NULL;
		else {name = m_name+".EprocessHeapFile";pName = name;}

		m_hMap = ::CreateFileMapping(m_bPermanentShared?m_hFile:INVALID_HANDLE_VALUE,m_attr,
			PAGE_READWRITE,sz.HighPart,sz.LowPart,pName);
		if (!m_hMap) 
		{
			lck.Unlock();
			xbThrowEsys(xbHR_LAST,"xbSharedHeap::Create('"+m_filepath+"') failed");
		}

		if (!m_bPermanentShared)
			bToCreate = (GetLastError() != ERROR_ALREADY_EXISTS);

		if (bToCreate)
		{
			m_pHdr = (xbSharedMemHdr*)::MapViewOfFile(m_hMap,FILE_MAP_ALL_ACCESS,0,0,m_cByteHdr);
			if (!m_pHdr) 
			{
				lck.Unlock();
				xbThrowEsys(xbHR_LAST,"xbSharedHeap::Create('"+m_filepath+"') failed");
			}
			xbSharedMemHdr hdr(GetFileID());
			hdr.m_cByteSect = m_cByteSect;
			hdr.m_cPageMax = m_cPageMax;
			hdr.m_cSectPage = m_cSectPage;
			hdr.m_cByteMax = m_cByteMax;
			hdr.Check(m_filepath,GetFileID());
			*m_pHdr = hdr;
		}
		else
		{
			m_pHdr = (xbSharedMemHdr*)::MapViewOfFile(m_hMap,FILE_MAP_ALL_ACCESS,0,0,sizeof(xbSharedMemHdr));
			if (!m_pHdr) 
			{
				lck.Unlock();
				xbThrowEsys(xbHR_LAST,"xbSharedHeap::Create('"+m_filepath+"') failed");
			}
			m_pHdr->Check(m_filepath,GetFileID());

			m_cByteSect = m_pHdr->m_cByteSect;
			m_cPageMax = m_pHdr->m_cPageMax;
			m_cSectPage = m_pHdr->m_cSectPage;

			m_cBytePage = m_cByteSect * m_cSectPage;
			m_cByteMax = m_pHdr->m_cByteMax;
			m_cByteHdr = (m_cPageMax-1) * sizeof(xbSharedPageHdr) + sizeof(xbSharedMemHdr);

			m_cPageLoadedMax = (m_cAskedByteLoadedMax + m_cBytePage - 1) / m_cBytePage;
			if (m_cPageLoadedMax < 2) m_cPageLoadedMax = 2;

			xbMustBe(::UnmapViewOfFile(m_pHdr));
			m_pHdr = NULL;
			m_pHdr = (xbSharedMemHdr*)::MapViewOfFile(m_hMap,FILE_MAP_ALL_ACCESS,0,0,m_cByteHdr);
			if (!m_pHdr) 
			{
				lck.Unlock();
				xbThrowEsys(xbHR_LAST,"xbSharedHeap::Create('"+m_filepath+"') failed");
			}

			m_pHdr->Check(m_filepath,GetFileID());
		}

		xbDbgOutput("xbSharedHeap::Create('"+m_filepath+"') : cByteSect="+xbString(m_cByteSect)
			+", cSectPage="+xbString(m_cSectPage)
			+", cPageMax="+xbString(m_cPageMax)
			+", cByteMax="+xbString(m_cByteMax)
			+", cByteHdr="+xbString(m_cByteHdr)
			+", cBytePage="+xbString(m_cBytePage)
			+", cPage="+xbString(m_pHdr->m_cPage)
			+", cPageLoadedMax="+xbString(m_cPageLoadedMax)
			+", cByteSysGranularity="+xbString(cGran)
			+", Main block="+xbString(m_pHdr->m_mainBlockId.m_handle));


		m_arrPage.Length = m_cPageMax;

		memset(m_arrPage.Begin(),0,sizeof(void*) * m_cPageMax);
	}
	catch(...)
	{
		Destroy();
		throw;
	}
	return;
}

void xbSharedHeap::Destroy()
{
	xbObject::Destroy();
	xbLock lck(&m_mutex);
	if (!m_bHeapCreated) return;
	m_bHeapCreated = false;
	m_arrPage.Clear();
	if (m_pHdr != NULL)
	{
		xbMustBe(::UnmapViewOfFile(m_pHdr));
		m_pHdr = NULL;
	}
	if (m_hMap != NULL)
	{
		::CloseHandle(m_hMap);
		m_hMap = NULL;
	}
	if (m_hFile != INVALID_HANDLE_VALUE)
	{
		::CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
	}
}

xbSharedHeap::Page *xbSharedHeap::newPage(ulong sz,xbOpMode opm)
{
	Page *pLastPage;
	uint64	newoffs;
	ulong	pageNo = m_pHdr->m_cPage;
	if (m_pHdr->m_cPage <= 0) 
	{
		pLastPage = NULL;

		newoffs = m_cByteHdr;
	}
	else
	{
		if (m_pHdr->m_cPage >= m_cPageMax)
		{
			if (opm == xbOp_Required)
				xbThrowEsys(E_OUTOFMEMORY,"xbSharedHeap('"+xbString(m_filepath)+"')::newPage() : too many pages");
			return NULL;
		}

		pLastPage = getPage(m_pHdr->m_cPage);

		newoffs = pLastPage->m_offset + pLastPage->m_cByte;
	}

	ulong	newsz;
	ulong	cSect;
	if (sz == 0 || sz <= m_cBytePage)
	{
		newsz = m_cBytePage; 
		cSect = m_cSectPage;
	}
	else
	{
		newsz = (sz + m_cByteSect - 1) / m_cByteSect;
		newsz *= m_cByteSect;

		ulong cGran = GetSysGranularity();
		newsz =  (newsz + cGran - 1) / cGran;
		newsz *= cGran;

		cSect = newsz / m_cByteSect;
	}

	if (newoffs + newsz > m_cByteMax + m_cByteHdr)
	{
		if (opm == xbOp_Required)
			xbThrowEsys(E_OUTOFMEMORY,"xbSharedHeap('"+xbString(m_filepath)+"')::newPage() : heap full");
		return NULL;
	}
	
	if (m_arrPage.GetAt(pageNo) != NULL)
		xbThrowEsys("xbSharedHeap('"+xbString(m_filepath)+"')::newPage() : internal error");


	xbSharedPageHdr *pPageHdr = &m_pHdr->m_pages[pageNo];
	*pPageHdr = xbSharedPageHdr();
	pPageHdr->m_offset = newoffs;
	pPageHdr->m_cSect = cSect;
	pPageHdr->m_cFreeSect = cSect;

	pLastPage = xbDnew Page(this,newoffs,cSect * m_cByteSect,pageNo+1,pPageHdr);

	m_arrPage.SetAt(pageNo,pLastPage);

	m_pHdr->m_cPage++;

	xbRdbgAssert(m_pHdr->m_cPage == pageNo+1);

	return pLastPage;
}

xbSharedHeap::Page *xbSharedHeap::getPage(ulong ix,xbOpMode opm)
{
	if (ix < 1 || ix > m_pHdr->m_cPage)
	{
		if (opm == xbOp_Required)
			xbThrowEarg("xbSharedHeap('"+xbString(m_filepath)+"')::getPage(ulong ix = "+xbString(ix)+") : ix out of range");
		return NULL;
	}
	ix--;
	Page *pPage = m_arrPage.GetAt(ix);

	if (pPage) 
		return pPage;

	xbSharedPageHdr *pPageHdr = &m_pHdr->m_pages[ix];
	
	pPage = xbDnew Page(this,pPageHdr->m_offset,pPageHdr->m_cSect * m_cByteSect,ix+1,pPageHdr);
	
	m_arrPage.SetAt(ix,pPage);

	return pPage;
}

BYTE *xbSharedHeap::Page::load()
{
	loadedLink();
	if (m_pMem) return m_pMem;

	try
	{
		ULARGE_INTEGER offs;
		offs.QuadPart = m_offset;
		m_pMem = (BYTE*)::MapViewOfFile(m_pHeap->m_hMap,FILE_MAP_ALL_ACCESS,offs.HighPart,offs.LowPart,m_cByte);
		if (!m_pMem) 
			xbThrowEsys(xbHR_LAST,"xbSharedHeap('"+m_pHeap->m_filepath+"')::Page::Load() failed (offs="+xbString(m_offset)+",sz="+xbString(m_cByte)+")");
	}
	catch(...)
	{
		loadedUnlink();
		throw;
	}
	return m_pMem;
}

void xbSharedHeap::Page::unload()
{
	loadedUnlink();
	if (!m_pMem) return;
	xbMustBe(::UnmapViewOfFile(m_pMem));
	m_pMem = NULL;
}

void xbSharedHeap::Page::loadedLink()
{
	loadedUnlink();

	m_pHeap->cleanLoadedPages(m_pMem == NULL);

	m_pLoadedNext = NULL;

	m_pLoadedPrev = m_pHeap->m_pNewestLoadedPage;
	m_pHeap->m_pNewestLoadedPage = this;

	if (m_pLoadedPrev) m_pLoadedPrev->m_pLoadedNext = this;
	if (m_pLoadedPrev) 
	{
		xbAssert(m_pHeap->m_pOldestLoadedPage != NULL);
		m_pLoadedPrev->m_pLoadedNext = this;
	}
	else 
	{
		xbAssert(m_pHeap->m_pOldestLoadedPage == NULL);
		m_pHeap->m_pOldestLoadedPage = this;
	}

	m_bLoadedLinked = true;

	m_pHeap->m_cPageLoaded++;
}

void xbSharedHeap::Page::loadedUnlink()
{
	if (!m_bLoadedLinked) return;
	m_bLoadedLinked = false;

	if (m_pLoadedNext) m_pLoadedNext->m_pLoadedPrev = m_pLoadedPrev;
	else m_pHeap->m_pNewestLoadedPage = m_pLoadedPrev;
	
	if (m_pLoadedPrev) m_pLoadedPrev->m_pLoadedNext = m_pLoadedNext;
	else m_pHeap->m_pOldestLoadedPage = m_pLoadedNext;

	m_pHeap->m_cPageLoaded--;
}

void xbSharedHeap::cleanLoadedPages(bool bToLoad)
{
	ulong lim = m_cPageLoadedMax;
	if (bToLoad) lim--;
	if (m_cPageLoaded <= lim) return;
	
	Page *ptr = m_pOldestLoadedPage;
	while(ptr && m_cPageLoaded > lim)
	{
		Page *pCur = ptr;
		ptr = ptr->m_pLoadedNext;
		if (ptr->m_cMemLock <= 0) 
			ptr->unload();
	}
}

void *xbSharedHeap::Page::alloc(ulong cSect,ulong cByte,ulong *pSectNo)
{
	BYTE *pSeg = load();
	ulong offs = m_pHdr->m_offsFirstFreeBlock;
	ulong cFreeSect = m_pHdr->m_cFreeSect;
	if (offs == -1)
	{
		xbAssert(cFreeSect == m_pHdr->m_cSect);
		if (cSect > cFreeSect 
			|| m_pHdr->m_offsFirstBusyBlock != -1) 
			xbThrowEsys("xbSharedHeap::Page::alloc() : internal error");

		dbgMarkAsNoMansLand(pSeg,m_cByte);
//		dbgCheckNoMansLand(m_pHeap->m_filepath,m_offset,pSeg,m_cByte);

		SharedBlockHdr	*pBlock = (SharedBlockHdr*)pSeg;

		pBlock->m_offsNext = -1;	
		pBlock->m_offsPrev = -1;
		pBlock->m_cByte = cByte;
		pBlock->m_cBytePrev = 0;
		pBlock->m_cLock = 1;

		m_pHdr->m_offsFirstBusyBlock = 0;

		dbgMarkAsNew(pBlock,cByte);

		if (pSectNo) *pSectNo = 0;

		xbAssert(m_pHdr->m_cFreeSect >= cSect);
		m_pHdr->m_cFreeSect -= cSect;

		if (cSect == cFreeSect) return (BYTE*)(pBlock + 1) + NOMANSLAND_LG;

		SharedBlockHdr *pFreeBlock = (SharedBlockHdr*)(pSeg + cByte);
		pFreeBlock->m_offsNext = -1;	
		pFreeBlock->m_offsPrev = -1;
		pFreeBlock->m_cByte = (cFreeSect - cSect) * m_pHeap->m_cByteSect;
		pFreeBlock->m_cBytePrev = cByte;
		pFreeBlock->m_cLock = 0;

		m_pHdr->m_offsFirstFreeBlock = cByte;

		dbgMarkAsFreed(pFreeBlock,pFreeBlock->m_cByte);

		return (BYTE*)(pBlock + 1) + NOMANSLAND_LG;
	}

	SharedBlockHdr	*pBlock = NULL;
	while(offs != -1)
	{
		pBlock = (SharedBlockHdr*)(pSeg + offs);
		xbAssert(pBlock->m_cByte % m_pHeap->m_cByteSect == 0);
		xbAssert(pBlock->m_cBytePrev % m_pHeap->m_cByteSect == 0);
		xbAssert(pBlock->m_cLock == 0);
		dbgCheckFreed(m_pHeap->m_filepath,m_offset+offs,pBlock);
		if (pBlock->m_cByte >= cByte) break;
		offs = pBlock->m_offsNext;
		cFreeSect -= (pBlock->m_cByte / m_pHeap->m_cByteSect);
		if (cFreeSect < cSect) return NULL;
	}
	if (offs == -1) return NULL;

	ulong	offsPrev = pBlock->m_cBytePrev?offs - pBlock->m_cBytePrev:-1;
	xbAssert(offsPrev == -1 || offsPrev < m_cByte);
	SharedBlockHdr	*pPrevPhysBlock = offsPrev!=-1?(SharedBlockHdr*)(pSeg + offsPrev):NULL;
	dbgPageCheck(offsPrev,pPrevPhysBlock);

	ulong	offsNext = offs + pBlock->m_cByte;
	xbAssert(offsNext <= m_cByte);
	if (offsNext >= m_cByte) offsNext = -1;
	SharedBlockHdr	*pNextPhysBlock = offsNext!=-1?(SharedBlockHdr*)(pSeg + offsNext):NULL;
	dbgPageCheck(offsNext,pNextPhysBlock);
	xbAssert(!pNextPhysBlock || pNextPhysBlock->m_cBytePrev == pBlock->m_cByte);

	SharedBlockHdr	*pPrevFreeBlock = pBlock->m_offsPrev!=-1?(SharedBlockHdr*)(pSeg + pBlock->m_offsPrev):NULL;
	dbgPageCheckFreed(pBlock->m_offsPrev,pPrevFreeBlock);

	SharedBlockHdr	*pNextFreeBlock = pBlock->m_offsNext!=-1?(SharedBlockHdr*)(pSeg + pBlock->m_offsNext):NULL;
	dbgPageCheckFreed(pBlock->m_offsNext,pNextFreeBlock);

	if (pPrevFreeBlock) pPrevFreeBlock->m_offsNext = pBlock->m_offsNext;
	else 
	{
		xbAssert(offs == m_pHdr->m_offsFirstFreeBlock);
		m_pHdr->m_offsFirstFreeBlock = pBlock->m_offsNext;
	}

	if (pNextFreeBlock) pNextFreeBlock->m_offsPrev = pBlock->m_offsPrev;

	ulong cByteFreeBlock = pBlock->m_cByte;
	ulong cSectFreeBlock = (cByteFreeBlock / m_pHeap->m_cByteSect);
	xbAssert(m_pHdr->m_cFreeSect >= cSectFreeBlock);
	m_pHdr->m_cFreeSect -= cSectFreeBlock;

	pBlock->m_offsNext = m_pHdr->m_offsFirstBusyBlock;	
	pBlock->m_offsPrev = -1;
	pBlock->m_cByte = cByte;
	pBlock->m_cBytePrev = pPrevPhysBlock?pPrevPhysBlock->m_cByte:0;
	pBlock->m_cLock = 1;

	dbgMarkAsNew(pBlock,pBlock->m_cByte);

	SharedBlockHdr	*pFirstBusyBlock = pBlock->m_offsNext!=-1 ?(SharedBlockHdr*)(pSeg + pBlock->m_offsNext):NULL;
	dbgPageCheckNew(pBlock->m_offsNext,pFirstBusyBlock);
	if (pFirstBusyBlock) pFirstBusyBlock->m_offsPrev = offs;

	m_pHdr->m_offsFirstBusyBlock = offs;

	if (pSectNo) *pSectNo = offs / m_pHeap->m_cByteSect;

	if (cSect == cSectFreeBlock) 
	{
		xbAssert(!pNextPhysBlock 
			|| (pNextPhysBlock->m_cBytePrev == cByte && pNextPhysBlock->m_cLock > 0));
		return (BYTE*)(pBlock + 1) + NOMANSLAND_LG;
	}

	SharedBlockHdr *pFreeBlock = (SharedBlockHdr*)(pSeg + offs + cByte);
	pFreeBlock->m_offsNext = m_pHdr->m_offsFirstFreeBlock;	
	pFreeBlock->m_offsPrev = -1;
	pFreeBlock->m_cByte = cByteFreeBlock - cByte;
	pFreeBlock->m_cBytePrev = cByte;
	pFreeBlock->m_cLock = 0;

	dbgMarkAsFreed(pFreeBlock,pFreeBlock->m_cByte);

	if (pNextPhysBlock)
	{
		xbAssert(pNextPhysBlock->m_cBytePrev == cByteFreeBlock && pNextPhysBlock->m_cLock > 0);
		pNextPhysBlock->m_cBytePrev = pFreeBlock->m_cByte;
	}

	SharedBlockHdr	*pFirstFreeBlock = pFreeBlock->m_offsNext!=-1 ?(SharedBlockHdr*)(pSeg + pFreeBlock->m_offsNext):NULL;
	dbgPageCheckFreed(pFreeBlock->m_offsNext,pFirstFreeBlock);
	if (pFirstFreeBlock) pFirstFreeBlock->m_offsPrev = offs+cByte;

	m_pHdr->m_offsFirstFreeBlock = offs+cByte;

	m_pHdr->m_cFreeSect += (pFreeBlock->m_cByte) / m_pHeap->m_cByteSect;

	return (BYTE*)(pBlock + 1) + NOMANSLAND_LG;
}

void xbSharedHeap::Page::free(ulong sectNo,ulong cByte)
{
	ulong offs = sectNo * m_pHeap->m_cByteSect;
	if (offs >= m_cByte)
		xbThrowEsys("xbSharedHeap::Page(ulong pageNo="+xbString(m_pageNo)+")::free(ulong sectNo="+xbString(sectNo)+",ulong cByte="+xbString(cByte)+") : sector ouf of bounds");
	BYTE *pSeg = load();

	SharedBlockHdr *pBlock = (SharedBlockHdr*)(pSeg + offs);
	dbgPageCheckNew(offs,pBlock);
	if (pBlock->m_cByte != cByte)
		xbThrowEsys("xbSharedHeap::Page(ulong pageNo="+xbString(m_pageNo)+")::free(ulong sectNo="+xbString(sectNo)+",ulong cByte="+xbString(cByte)+") : sector ouf of bounds");
	if (pBlock->m_cLock <= 0)
		xbThrowEsys("xbSharedHeap::Page(ulong pageNo="+xbString(m_pageNo)+")::free(ulong sectNo="+xbString(sectNo)+",ulong cByte="+xbString(cByte)+") : sector already freed");
	pBlock->m_cLock--;
	if (pBlock->m_cLock > 0) return;

	{
		SharedBlockHdr	*pPrevBusyBlock = pBlock->m_offsPrev!=-1?(SharedBlockHdr*)(pSeg + pBlock->m_offsPrev):NULL;
		dbgPageCheckNew(pBlock->m_offsPrev,pPrevBusyBlock);
		SharedBlockHdr	*pNextBusyBlock = pBlock->m_offsNext!=-1?(SharedBlockHdr*)(pSeg + pBlock->m_offsNext):NULL;
		dbgPageCheckNew(pBlock->m_offsNext,pNextBusyBlock);

		if (pPrevBusyBlock) pPrevBusyBlock->m_offsNext = pBlock->m_offsNext;
		else 
		{
			xbAssert(offs == m_pHdr->m_offsFirstBusyBlock);
			m_pHdr->m_offsFirstBusyBlock = pBlock->m_offsNext;
		}

		if (pNextBusyBlock) pNextBusyBlock->m_offsPrev = pBlock->m_offsPrev;
	}

	ulong	offsPrev = pBlock->m_cBytePrev?offs - pBlock->m_cBytePrev:-1;
	xbAssert(offsPrev == -1 || offsPrev < m_cByte);
	SharedBlockHdr	*pPrevPhysBlock = offsPrev!=-1?(SharedBlockHdr*)(pSeg + offsPrev):NULL;
	dbgPageCheck(offsPrev,pPrevPhysBlock);

	ulong	offsNext = offs + pBlock->m_cByte;
	xbAssert(offsNext <= m_cByte);
	if (offsNext >= m_cByte) offsNext = -1;
	SharedBlockHdr	*pNextPhysBlock = offsNext!=-1?(SharedBlockHdr*)(pSeg + offsNext):NULL;
	dbgPageCheck(offsNext,pNextPhysBlock);
	xbAssert(!pNextPhysBlock || pNextPhysBlock->m_cBytePrev == pBlock->m_cByte);

	if (pPrevPhysBlock && pPrevPhysBlock->m_cLock == 0)
	{
		{
			SharedBlockHdr	*pPrevFreeBlock = pPrevPhysBlock->m_offsPrev!=-1?(SharedBlockHdr*)(pSeg + pPrevPhysBlock->m_offsPrev):NULL;
			dbgPageCheckFreed(pPrevPhysBlock->m_offsPrev,pPrevFreeBlock);

			SharedBlockHdr	*pNextFreeBlock = pPrevPhysBlock->m_offsNext!=-1?(SharedBlockHdr*)(pSeg + pPrevPhysBlock->m_offsNext):NULL;
			dbgPageCheckFreed(pPrevPhysBlock->m_offsNext,pNextFreeBlock);

			if (pPrevFreeBlock) pPrevFreeBlock->m_offsNext = pPrevPhysBlock->m_offsNext;
			else 
			{
				xbAssert(offsPrev == m_pHdr->m_offsFirstFreeBlock);
				m_pHdr->m_offsFirstFreeBlock = pPrevPhysBlock->m_offsNext;
			}

			if (pNextFreeBlock) pNextFreeBlock->m_offsPrev = pPrevPhysBlock->m_offsPrev;
		}

		xbAssert(m_pHdr->m_cFreeSect >= (pPrevPhysBlock->m_cByte / m_pHeap->m_cByteSect));
		m_pHdr->m_cFreeSect -= (pPrevPhysBlock->m_cByte / m_pHeap->m_cByteSect);

		cByte += pPrevPhysBlock->m_cByte;
		
		pBlock = pPrevPhysBlock;

		offs = offsPrev;
	}

	if (pNextPhysBlock && pNextPhysBlock->m_cLock == 0)
	{
		{
			SharedBlockHdr	*pPrevFreeBlock = pNextPhysBlock->m_offsPrev!=-1?(SharedBlockHdr*)(pSeg + pNextPhysBlock->m_offsPrev):NULL;
			dbgPageCheckFreed(pNextPhysBlock->m_offsPrev,pPrevFreeBlock);

			SharedBlockHdr	*pNextFreeBlock = pNextPhysBlock->m_offsNext!=-1?(SharedBlockHdr*)(pSeg + pNextPhysBlock->m_offsNext):NULL;
			dbgPageCheckFreed(pNextPhysBlock->m_offsNext,pNextFreeBlock);

			if (pPrevFreeBlock) 
				pPrevFreeBlock->m_offsNext = pNextPhysBlock->m_offsNext;
			else 
			{
				xbAssert(offsNext == m_pHdr->m_offsFirstFreeBlock);
				m_pHdr->m_offsFirstFreeBlock = pNextPhysBlock->m_offsNext;
			}

			if (pNextFreeBlock) pNextFreeBlock->m_offsPrev = pNextPhysBlock->m_offsPrev;
		}

		xbAssert(m_pHdr->m_cFreeSect >= (pNextPhysBlock->m_cByte / m_pHeap->m_cByteSect));
		m_pHdr->m_cFreeSect -= (pNextPhysBlock->m_cByte / m_pHeap->m_cByteSect);

		cByte += pNextPhysBlock->m_cByte;
	}

	ulong cSect = cByte / m_pHeap->m_cByteSect;

	offsNext = offs + cByte;
	xbAssert(offsNext <= m_cByte);
	if (offsNext >= m_cByte) offsNext = -1;
	pNextPhysBlock = offsNext!=-1?(SharedBlockHdr*)(pSeg + offsNext):NULL;
	dbgPageCheck(offsNext,pNextPhysBlock);
//	xbAssert(!pNextPhysBlock || pNextPhysBlock->m_cBytePrev == pBlock->m_cByte);

	if (pNextPhysBlock) 
		pNextPhysBlock->m_cBytePrev = cByte;

	xbAssert(pBlock->m_cLock == 0);
	pBlock->m_cByte = cByte;
	pBlock->m_offsPrev = -1;
	pBlock->m_offsNext = m_pHdr->m_offsFirstFreeBlock;

	dbgMarkAsFreed(pBlock,pBlock->m_cByte);

	SharedBlockHdr	*pFirstFreeBlock = pBlock->m_offsNext!=-1 ?(SharedBlockHdr*)(pSeg + pBlock->m_offsNext):NULL;
	dbgPageCheckFreed(pBlock->m_offsNext,pFirstFreeBlock);
	if (pFirstFreeBlock) pFirstFreeBlock->m_offsPrev = offs;

	m_pHdr->m_offsFirstFreeBlock = offs;

	m_pHdr->m_cFreeSect += cSect;
}

bool xbSharedHeap::IsValid(const xbSharedHandle &handle,ulong cByte) const
{
	if (handle.IsEmpty()) return false;
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::IsValid() : heap not created");

	ulong pageNo = (handle.m_handle & 0xFFFF0000) >> 16;
	ulong sectNo = (handle.m_handle & 0xFFFF);

	if (sectNo < 0 || sectNo >= m_cSectPage) return false;

	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo,xbOp_Optional);
	if (!pPage) return false;
	
	if (sectNo >= pPage->m_pHdr->m_cSect) return false;
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect) return false;

	ulong offsBlock =  sectNo * m_cByteSect;
	BYTE *pSeg = pPage->load();
	ulong offs = pPage->m_pHdr->m_offsFirstBusyBlock;
	SharedBlockHdr	*pBlock;
	while(offs != -1)
	{
		pBlock = (SharedBlockHdr*)(pSeg + offs);
		dbgHeapCheckNew(pPage,offs,pBlock);
		xbAssert(pBlock->m_cByte > 0 && pBlock->m_cLock > 0);
		if (offs == offsBlock)
		{
			if (cByte <= 0) return true;
			return cByte+sizeof(SharedBlockHdr) == pBlock->m_cByte;
		}
		offs = pBlock->m_offsNext;
	}
	
	return false;
}

bool xbSharedHeap::Load(xbRefPtr<xbSharedMem> *pprMem,const xbSharedHandle &handle,ulong cByte,xbOpMode opm)
{
	if (handle.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Load() : handle is Null");
		return false;
	}

	xbLock lck(&m_mutex);
	if (!m_pHdr) 
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Load() : heap not created");
		return false;
	}

	ulong pageNo = (handle.m_handle & 0xFFFF0000) >> 16;
	ulong sectNo = (handle.m_handle & 0xFFFF);

	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo,opm);
	if (!pPage) return false;
	
	if (sectNo > pPage->m_pHdr->m_cSect)
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Load() : invalid handle");
		return false;
	}
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect)
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Load() : invalid handle (no busy block in page)");
		return false;
	}

	ulong offsBlock =  sectNo * m_cByteSect;
	BYTE *pSeg = pPage->load();

	ulong offs = pPage->m_pHdr->m_offsFirstBusyBlock;
	SharedBlockHdr	*pBlock;
	while(offs != -1)
	{
		pBlock = (SharedBlockHdr*)(pSeg + offs);
		dbgHeapCheckNew(pPage,offs,pBlock);
		xbAssert(pBlock->m_cByte > 0 && pBlock->m_cLock > 0);
		if (offs == offsBlock) break;
		offs = pBlock->m_offsNext;
	}

	if (offs == -1)
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Load() : invalid handle (not found)");
		return false;
	}

	if (cByte > 0 && pBlock->m_cByte != cByte + sizeof(SharedBlockHdr) + NOMANSLAND_LG + NOMANSLAND_LGEND)
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Load() : invalid handle (bad size)");
		return false;
	}

	if (!pprMem) return true;

	xbSharedMem *pShMem = xbDnew xbSharedMem(this,pageNo,sectNo,
		pBlock->m_cByte - sizeof(SharedBlockHdr) - NOMANSLAND_LG - NOMANSLAND_LGEND,true);
	try
	{
		pBlock->m_cLock++;
		pShMem->Create();

		*pprMem = pShMem;

		pShMem->Release();
	}
	catch(...)
	{
		pShMem->Release();
		throw;
	}
	return true;
}

bool xbSharedHeap::Alloc(xbRefPtr<xbSharedMem> *pprMem,ulong cByte,xbOpMode opm)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Alloc() : heap not created");
	ulong cSect = (cByte + sizeof(SharedBlockHdr) + NOMANSLAND_LG + NOMANSLAND_LGEND + m_cByteSect - 1) / m_cByteSect;
	cByte = cSect * m_cByteSect;

	ulong sectNo = -1;
	Page *pCur = NULL;
	void *pMem = NULL;
	for(;;)
	{
		{
			Page *ptr = m_pNewestLoadedPage;
			while(ptr)
			{
				pCur = ptr;
				ptr = ptr->m_pLoadedPrev;
				if (pCur->m_pHdr->m_cFreeSect < cSect) continue;

				pMem = pCur->alloc(cSect,cByte,&sectNo);
				if (pMem) break;
			}
		}
		if (pMem) break;

		{
/*			for(int pageNo = 1; pageNo <= m_pHdr->m_cPage;pageNo++)
			{
				if (pCur->m_pHdr->m_cFreeSect < cSect) continue;
				pMem = pCur->alloc(cSect,cByte,&sectNo);
				if (pMem) break;
			}*/
			Page **ptr = m_arrPage.Begin();
			Page **pend = ptr + m_pHdr->m_cPage;
			int pageNo = 1;
			while(ptr < pend)
			{
				pCur = *(ptr++);
				if (!pCur) pCur = getPage(pageNo++);
				else pageNo++;
				if (pCur->m_pHdr->m_cFreeSect < cSect) continue;
				pMem = pCur->alloc(cSect,cByte,&sectNo);
				if (pMem) break;
			}
		}
		if (pMem) break;

		pCur = newPage(cByte,opm);
		if (!pCur)
			return false;

		if (!(pMem = pCur->alloc(cSect,cByte,&sectNo)))
			xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Alloc() : internal error (newpage)");
		
		break;
	}


	xbAssert(sectNo != -1);
	xbSharedMem *pShMem = xbDnew xbSharedMem(this,pCur->m_pageNo,sectNo,
		cByte - sizeof(SharedBlockHdr) - NOMANSLAND_LG - NOMANSLAND_LGEND,false);
	try
	{
		pShMem->Create();

		*pprMem = pShMem;

		pShMem->Release();
	}
	catch(...)
	{
		pShMem->Release();
		throw;
	}

	return true;
}

void *xbSharedHeap::lockMem(ulong pageNo,ulong sectNo)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) 
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::lockMem() : heap not created");
	if (m_bNoMemLocks) 
		xbThrow xbEuser(E_ABORT,"Heap in destruction","xbSharedHeap('"+m_filepath+"')::lockMem()");
	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

#ifdef _DEBUG
	if (sectNo > pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::lockMem() : invalid handle");
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::lockMem() : invalid handle (no busy block in page)");
#endif
	ulong offsBlock =  sectNo * m_cByteSect;
	BYTE *pSeg = pPage->load();
	SharedBlockHdr	*pBlock = (SharedBlockHdr*)(pSeg+offsBlock);
	dbgHeapCheckNew(pPage,offsBlock,pBlock);
	pPage->m_cMemLock++;
	m_cMemLock++;

	return (BYTE*)(pBlock + 1) + NOMANSLAND_LG;
}

void xbSharedHeap::unlockMem(ulong pageNo,ulong sectNo)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockMem() : heap not created");

	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

#ifdef _DEBUG
	if (sectNo > pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockMem() : invalid handle");
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockMem() : invalid handle (no busy block in page)");
	ulong offsBlock =  sectNo * m_cByteSect;
	BYTE *pSeg = pPage->load();
	SharedBlockHdr	*pBlock = (SharedBlockHdr*)(pSeg+offsBlock);
	dbgHeapCheckNew(pPage,offsBlock,pBlock);
#endif
	if (pPage->m_cMemLock <= 0)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockMem() : page already unlocked");
	pPage->m_cMemLock--;
	m_cMemLock--;
}

void xbSharedHeap::lockUse(ulong pageNo,ulong sectNo)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::lockUse() : heap not created");

	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

#ifdef _DEBUG
	if (sectNo > pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::lockUse() : invalid handle");
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::lockUse() : invalid handle (no busy block in page)");
#endif
	ulong offsBlock =  sectNo * m_cByteSect;
	BYTE *pSeg = pPage->load();
	SharedBlockHdr	*pBlock = (SharedBlockHdr*)(pSeg+offsBlock);
	dbgHeapCheckNew(pPage,offsBlock,pBlock);
	pBlock->m_cLock++;
}

void xbSharedHeap::unlockUse(ulong pageNo,ulong sectNo)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockUse() : heap not created");

	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

#ifdef _DEBUG
	if (sectNo > pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockUse() : invalid handle");
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockUse() : invalid handle (no busy block in page)");
#endif
	ulong offsBlock =  sectNo * m_cByteSect;
	BYTE *pSeg = pPage->load();
	SharedBlockHdr	*pBlock = (SharedBlockHdr*)(pSeg+offsBlock);
	dbgHeapCheckNew(pPage,offsBlock,pBlock);
	if (pBlock->m_cLock <= 1)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::unlockUse() : block already unlocked");
	pBlock->m_cLock--;
}

void xbSharedHeap::free(ulong pageNo,ulong sectNo,ulong cByte)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::free() : heap not created");

	Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

#ifdef _DEBUG
	if (sectNo > pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::free() : invalid handle");
	if (pPage->m_pHdr->m_cFreeSect >= pPage->m_pHdr->m_cSect)
		xbThrowEsys("xbSharedHeap('"+m_filepath+"')::free() : invalid handle (no busy block in page)");
#endif
	pPage->free(sectNo,cByte + sizeof(SharedBlockHdr) + NOMANSLAND_LG + NOMANSLAND_LGEND);
}

xbSharedHandle	xbSharedMem::GetHandle() const
{
	xbSectLock lck(&m_sectData);
	if (m_pageNo == 0)
		xbThrowEsys("xbSharedMem::GetHandle() : void memory");
	if (m_pageNo > 0xFFFF || m_sectNo > 0xFFFF)
		xbThrowEsys("xbSharedMem::GetHandle() : invalid or long handle");
	xbSharedHandle h;
	h.m_handle = ((m_pageNo & 0xFFFF) << 16) + (m_sectNo & 0xFFFF);
	return h;
}

void *xbSharedMem::LockMem()
{
	xbSectLock lck(&m_sectData);
	if (!m_pHeap)
		xbThrowEsys("xbSharedMem::LockMem() : void memory");
	m_cLockMem++;
	try
	{
		return m_pHeap->lockMem(m_pageNo,m_sectNo);
	}
	catch(...)
	{
		m_cLockMem--;
		throw;
	}
}

void xbSharedMem::UnlockMem()
{
	xbSectLock lck(&m_sectData);
	if (!m_pHeap)
		xbThrowEsys("xbSharedMem::UnlockMem() : void memory");
	if (m_cLockMem <= 0) 
		xbThrowEsys("xbSharedMem::UnlockMem() : already unlocked");
	m_cLockMem--;
	m_pHeap->unlockMem(m_pageNo,m_sectNo);
}

void xbSharedMem::LockUse()
{
	xbSectLock lck(&m_sectData);
	if (!m_pHeap)
		xbThrowEsys("xbSharedMem::LockUse() : void memory");
	m_cLockUse++;
	try
	{
		m_pHeap->lockUse(m_pageNo,m_sectNo);
	}
	catch(...)
	{
		m_cLockUse--;
		throw;
	}
}

void xbSharedMem::UnlockUse()
{
	xbSectLock lck(&m_sectData);
	if (!m_pHeap)
		xbThrowEsys("xbSharedMem::UnlockUse() : void memory");
	if (m_cLockUse <= 0) 
		xbThrowEsys("xbSharedMem::UnlockUse() : already unlocked");
	m_cLockUse--;
	m_pHeap->unlockUse(m_pageNo,m_sectNo);
}

void xbSharedMem::onBeforeDestruction(bool bInDestroy)
{
	eProtectTrace(destroy());
}

void xbSharedMem::destroy()
{
	xbSectLock lck(&m_sectData);
	if (!m_pHeap) return;
	xbAssert(m_cLockMem == 0);
	eProtectTrace(while(m_cLockMem > 0) UnlockMem());
	m_cLockMem = 0;
	xbSharedHeap *pHeap = m_pHeap;
	m_pHeap = NULL;
	pHeap->free(m_pageNo,m_sectNo,m_cByte);
	m_pageNo = 0;
	m_sectNo = 0;
	m_cByte = 0;
}

xbSharedMem::xbSharedMem(xbSharedHeap *pHeap, ulong pageNo, ulong sectNo, ulong cByte, bool bLoaded)
:m_poHeap(this)
{
	m_poHeap = pHeap;
	m_pHeap = pHeap;
	m_pageNo = pageNo;
	m_sectNo = sectNo;
	m_cByte = cByte;
	m_cLockMem = 0;
	m_cLockUse = bLoaded?1:0;
}

int xbSharedHeap::Check()
{
	xbLock lck(&m_mutex);

	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Check() : heap not created");
	int nberr = 0;

	for(uint pageNo = 1; pageNo <= m_pHdr->m_cPage; pageNo++)
	{
		Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);
		
		BYTE *pSeg = pPage->load();

		ulong offs = pPage->m_pHdr->m_offsFirstBusyBlock,lastoffs = -1;
		SharedBlockHdr	*pBlock;
		ulong szBusy = 0;
		while(offs != -1)
		{
			pBlock = (SharedBlockHdr*)(pSeg + offs);
			dbgHeapCheckNew(pPage,offs,pBlock);
			if (!xbMustBe(pBlock->m_offsPrev == lastoffs))
			{
				xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": block offs prev="+xbString(pBlock->m_offsPrev)+", calc="+xbString(lastoffs));
				nberr++;
			}
			lastoffs = offs;
			offs = pBlock->m_offsNext;
			szBusy += pBlock->m_cByte;
		}

		ulong szFree = 0;
		offs = pPage->m_pHdr->m_offsFirstFreeBlock;
		lastoffs = -1;
		while(offs != -1)
		{
			pBlock = (SharedBlockHdr*)(pSeg + offs);
			dbgHeapCheckFreed(pPage,offs,pBlock);
			if (!xbMustBe(pBlock->m_offsPrev == lastoffs))
			{
				xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": block offs prev="+xbString(pBlock->m_offsPrev)+", calc="+xbString(lastoffs)+" (2)");
				nberr++;
			}
			szFree += pBlock->m_cByte;
			lastoffs = offs;
			offs = pBlock->m_offsNext;
		}

		if (pPage->m_pHdr->m_offsFirstFreeBlock == -1
			&& pPage->m_pHdr->m_offsFirstBusyBlock == -1) continue;
	
		ulong szPhFree = 0,szPhBusy = 0;
		offs = 0;
		lastoffs = 0;
		while(offs != pPage->m_cByte)
		{
			pBlock = (SharedBlockHdr*)(pSeg + offs);
			dbgHeapCheck(pPage,offs,pBlock);
			if (!xbMustBe(pBlock->m_cBytePrev == lastoffs))
			{
				xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": block cByte prev="+xbString(pBlock->m_cBytePrev)+", calc="+xbString(lastoffs));
				nberr++;
			}
			lastoffs = pBlock->m_cByte;
			offs += pBlock->m_cByte;
			if (pBlock->m_cLock>0) szPhBusy += pBlock->m_cByte;
			else szPhFree += pBlock->m_cByte;
		}

		if (!xbMustBe(szBusy == szPhBusy))
		{
			xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": busy bytes="+xbString(szBusy)+", calc="+xbString(szPhBusy));
			nberr++;
		}
		if (!xbMustBe(szFree == szPhFree))
		{
			xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": free bytes="+xbString(szFree)+", calc="+xbString(szPhFree));
			nberr++;
		}
		if (!xbMustBe((szFree / m_cByteSect) == pPage->m_pHdr->m_cFreeSect))
		{
			xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": free sectors="+xbString(pPage->m_pHdr->m_cFreeSect)+", calc="+xbString(szFree / m_cByteSect)+", szFree="+xbString(szFree));
			nberr++;
		}
		if (!xbMustBe(((szFree + szBusy) / m_cByteSect) == pPage->m_pHdr->m_cSect))
		{
			xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::Check() : page "+xbString(pageNo)+": sectors="+xbString(pPage->m_pHdr->m_cSect)+", calc="+xbString((szFree + szBusy) / m_cByteSect)+", szFree="+xbString(szFree + szBusy));
			nberr++;
		}
	}
	return nberr;
}

bool xbSharedHeap::getMainBlock(xbRefPtr<xbSharedMem> *pprMem,ulong cByteForCreate,ulong cByteForCheck)
{
	xbLock lck(&m_mutex);
	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::getMainBlock() : heap not created");

	if (m_pHdr->m_mainBlockId.IsValid())
	{
		return Load(pprMem,m_pHdr->m_mainBlockId,cByteForCheck,xbOp_Required);
	}
	if (cByteForCreate == 0)
		return false;

	Alloc(pprMem,cByteForCreate,xbOp_Required);

	(*pprMem)->LockUse();
	m_pHdr->m_mainBlockId = (*pprMem)->GetHandle();

	return true;
}

bool xbSharedHeap::delMainBlock()
{
	xbRefPtr<xbSharedMem> prMem;
	xbLock lck(&m_mutex);
	if (!getMainBlock(&prMem))
		return false;
	prMem->UnlockUse();
	m_pHdr->m_mainBlockId.m_handle = 0;

	return true;
}

int64 xbSharedHeap::GetBusyByteCount() const
{
	xbLock lck(&m_mutex);

	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Check() : heap not created");

	ulong cSectBusy = 0;
	for(uint pageNo = 1; pageNo <= m_pHdr->m_cPage; pageNo++)
	{
		Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

		if (!xbMustBe(pPage->m_pHdr->m_cSect >= pPage->m_pHdr->m_cFreeSect))
		{
			xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::GetBusyByteCount() : page "+xbString(pageNo)+": free blocks="+xbString(pPage->m_pHdr->m_cFreeSect)+" > blocks = "+xbString(pPage->m_pHdr->m_cSect));
			continue;
		}
		cSectBusy += pPage->m_pHdr->m_cSect - pPage->m_pHdr->m_cFreeSect;
	}
	return int64(cSectBusy) * m_cByteSect;
}

int64 xbSharedHeap::GetFreeByteCount() const
{
	xbLock lck(&m_mutex);
	return GetUnusedFreeByteCount() + GetUsedFreeByteCount();
}

int64 xbSharedHeap::GetUsedFreeByteCount() const
{
	xbLock lck(&m_mutex);

	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Check() : heap not created");

	ulong cSectFree = 0;
	for(uint pageNo = 1; pageNo <= m_pHdr->m_cPage; pageNo++)
	{
		const Page *pPage = ((xbSharedHeap*)this)->getPage(pageNo);

		if (!xbMustBe(pPage->m_pHdr->m_cSect >= pPage->m_pHdr->m_cFreeSect))
		{
			xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::GetBusyByteCount() : page "+xbString(pageNo)+": free blocks="+xbString(pPage->m_pHdr->m_cFreeSect)+" > blocks = "+xbString(pPage->m_pHdr->m_cSect));
			continue;
		}
		cSectFree += pPage->m_pHdr->m_cFreeSect;
	}
	return int64(cSectFree) * m_cByteSect;
}

int64 xbSharedHeap::GetUnusedFreeByteCount() const
{
	xbLock lck(&m_mutex);

	if (!m_pHdr) xbThrowEsys("xbSharedHeap('"+m_filepath+"')::Check() : heap not created");

	if (m_pHdr->m_cPage >= m_cPageMax)
		return 0;
	uint64	lastoffs;
	if (m_pHdr->m_cPage <= 0) 
	{
		lastoffs = m_cByteHdr;
	}
	else
	{
		const Page *pLastPage = ((xbSharedHeap*)this)->getPage(m_pHdr->m_cPage);

		lastoffs = pLastPage->m_offset + pLastPage->m_cByte;
	}
	if (!xbMustBe(m_cByteMax + m_cByteHdr >= lastoffs))
	{
		xbRelOutput("ERROR: xbSharedHeap('"+xbString(m_filepath)+"')::GetUnusedFreeByteCount() : last offset="+xbString(lastoffs)+" > size max= "+xbString(m_cByteMax + m_cByteHdr));
	}
	return m_cByteMax + m_cByteHdr - lastoffs;
}

#pragma warning(pop)
