/*<<<***************************************************************************
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: xbHeapLow
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<31/08/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#define xbHEAPINTERNALS
#include "xbHeapLow.h"

////////////////////////////////////////////////////////////////////////////////////////////
// brix default heap
////////////////////////////////////////////////////////////////////////////////////////////
#if defined(xbHEAPRELEASECHECK) || defined(_DEBUG)
#	define _xbCHECKBLOCK(pBlock)	_checkBlock(pBlock)
#else
#	define _xbCHECKBLOCK(pBlock)
#endif

#define xbREGISTER register

#if defined(xbHEAPRELEASECHECK) || defined(_DEBUG)
void	xbStaticHeapBase::_checkBlock(void *pBloc) const
{
	if (!_isValid(pBloc))
	{
		DebugDump();
		xbThrowEsehFmt("xbStaticHeapBase(%s)::_checkBlock(%p) : trying to manipulate an invalid bloc",GetName(),pBloc);
	}
}
#endif

xbStaticHeapBase::xbStaticHeapBase(const char *pszDbgName, size_t cByteSeg, size_t cByteCell, byte *pSeg, xbCtorMode CtorMode)
:xbBaseHeap(pszDbgName)
{
//	m_Share.DeadLockOrderFinal();
	m_pSeg = pSeg;
	m_bCreated = false;
	m_cByteSeg = cByteSeg;
	m_cByteCell = cByteCell;
	m_cByteBusy = 0;

	if (cByteCell < xbSTATICHEAP_MINLEN) 
	{
		cByteCell = xbSTATICHEAP_MINLEN;
		m_cByteCell = cByteCell;
	//	xbThrowEsysFmt("xbStaticHeapBase(%s)::xbStaticHeapBase() : cByteCell asked = %d, must be > %d",
	//		GetName(),cByteCell,xbSTATICHEAP_MINLEN);
	}
	if (cByteSeg/cByteCell < xbSTATICHEAP_MINCELL) 
		xbThrowEsysFmt("xbStaticHeapBase(%s)::xbStaticHeapBase() : cCell asked = %d, cByteSeg asked = %d, must be > %d",
			GetName(),cByteSeg/cByteCell,cByteSeg,xbSTATICHEAP_MINCELL);

	if (CtorMode == xbCtor_CallCreate) 
		Create();
}

xbStaticHeapBase::xbStaticHeapBase(const char * pszDbgName)
:xbBaseHeap(pszDbgName)
{
//	m_Share.DeadLockOrderFinal();
	m_pSeg = NULL;
	m_bCreated = false;
	m_cByteSeg = 0;
	m_cByteCell = 0;
	m_cByteBusy = 0;
}

xbStaticHeapBase::~xbStaticHeapBase()
{
	eProtect(Destroy());
}

void	xbStaticHeapBase::SetParams(size_t cByteSeg, size_t cByteCell, byte *pSeg)
{
	Destroy();
	if (cByteCell < xbSTATICHEAP_MINLEN) 
		xbThrowEsysFmt("xbStaticHeapBase(%s)::SetParams() : cByteCell asked = %d, must be > %d",
			GetName(),cByteCell,xbSTATICHEAP_MINLEN);
	if (cByteSeg/cByteCell < xbSTATICHEAP_MINCELL) 
		xbThrowEsysFmt("xbStaticHeapBase(%s)::SetParams() : cCell asked = %d, cByteSeg asked = %d, must be > %d",
			GetName(),cByteSeg/cByteCell,cByteSeg,xbSTATICHEAP_MINCELL);
	m_pSeg = pSeg;
	m_cByteSeg = cByteSeg;
	m_cByteCell = cByteCell;
}

void	xbStaticHeapBase::GetParams(size_t *pcByteSeg, size_t *pcByteCell, byte **ppSeg, bool bReal) const
{
//	xbSectLockSimpleRec Lck(&m_Share);
	if (pcByteCell)
		*pcByteCell = m_cByteCell;
	if (pcByteSeg)
		*pcByteSeg = m_cByteSeg;
	if (ppSeg)
		*ppSeg = m_pSeg;
}

int64	xbStaticHeapBase::GetBusySize() const
{
//	xbSectLockSimpleRec Lck(&m_Share);
	return int64(m_cByteBusy);
}

void xbStaticHeapBase::Create()
{
	if (m_bCreated) 
		return;

	m_bCreated = true;
	m_cByteBusy = 0;
#ifdef _xbALLOCDEBUG_SPACEINIT
	if (xbDebugHeap_SpaceFreed != 0)
		memset((char*)m_pSeg,xbDebugHeap_SpaceFreed,m_cByteSeg);
#endif

	m_bCreated = true;
}

void xbStaticHeapBase::Destroy()
{
	if (!m_bCreated) 
		return;

	xbSectLockSimpleRec Lck(&m_Share);
	if (m_cAlloc > 0)
	{
		DebugDump();
		xbDbgHeapOutput("xbStaticHeapBase(%s)::Destroy() : %d allocations not freed (total size: %I64d) !",
			GetName(),m_cAlloc,m_cByteAlloc);
	}
	xbAssert(m_cAlloc == 0);
	Clear();	
	resetAllocInfo();
	m_bCreated = false;
}


void	xbStaticHeapBase::Clear()
{
	xbSectLockSimpleRec Lck(&m_Share);
	m_cByteBusy = 0;
#ifdef _xbALLOCDEBUG_SPACEINIT
	if (xbDebugHeap_SpaceFreed != 0)
		memset((char*)m_pSeg,xbDebugHeap_SpaceFreed,m_cByteSeg);
#endif
	resetAllocInfo();
}

void	*xbStaticHeapBase::Alloc(size_t cByte,xbAllocMode AllocMode
#ifdef _xbALLOCDEBUG
		,const char *pszFile,int nLine
#endif
		)
{
//#ifdef _DEBUG
	if (!m_bCreated) 
	{
		throwEnotCreated();
		return NULL;
	}

//#endif
	xbSectLockSimpleRec Lck(&m_Share);

	if (cByte > xbSIZE_LIMIT) 
		xbThrowEsizeLimitFmt("Memory allocation on heap %s greater than size limit (asked=%d, limit=%d)",GetName(),cByte,xbSIZE_LIMIT);

	ulong	cCellBlock = (ulong)((cByte+xbSTATICHEAP_HDRLEN+xbSTATICHEAP_ENDLEN+m_cByteCell-1) / m_cByteCell);
	ulong	cByteBlock = (ulong)(cCellBlock * m_cByteCell);

#ifdef xbHEAPCOMPLETECHECK
	//Global check of heap before allocation
	IsValid();
#endif

	int cAllocTry = 2;
	_CellHeader	*pAllocatedBlock;
retry_alloc:
	{
		if (m_cByteBusy + cByteBlock <= m_cByteSeg)
		{
			pAllocatedBlock = (_CellHeader*)(m_pSeg + m_cByteBusy);
			pAllocatedBlock->m_cByte = cByteBlock;

			m_cByteBusy += cByteBlock;

			goto block_found;
		}

		pAllocatedBlock = _freeBlockFind(cByteBlock);
		if (pAllocatedBlock)
		{
			pAllocatedBlock->m_cByte &= ~xbSTATICHEAP_FREECELL;

			goto block_found;
		}
	}
	if (RequestCleanerToFreeBytes((xbint)cByteBlock)
		&& (--cAllocTry)>=0) goto retry_alloc;
//			DebugDump();

	throwEmem(cByteBlock);
	return NULL;
	
block_found:
	if (AllocMode == xbAlloc_Init) 
		memset((char*)pAllocatedBlock+sizeof(_CellHeader),0,cByteBlock-sizeof(_CellHeader));
#ifdef _xbALLOCDEBUG_SPACEINIT
	else if (xbDebugHeap_SpaceUninit != 0)
		memset((char*)pAllocatedBlock+sizeof(_CellHeader),xbDebugHeap_SpaceUninit,cByteBlock-sizeof(_CellHeader));
#endif

#ifdef _xbDEBUGHEAP_HDRSTR
	//Mark the start of block with pad string
	memcpy((char*)pAllocatedBlock+xbSTATICHEAP_HDRLEN-_xbDEBUGHEAP_HDRLEN,_xbDEBUGHEAP_HDRSTR,xbSTATICHEAP_HDRLEN);
#endif

#ifdef _xbDEBUGHEAP_ENDSTR
	//Mark the end of block with pad string
	memcpy((char*)pAllocatedBlock+cByteBlock-xbSTATICHEAP_ENDLEN,_xbDEBUGHEAP_ENDSTR,xbSTATICHEAP_ENDLEN);
#endif

#ifdef _xbALLOCDEBUG
	//Feed debug header with allocation location,
	// and count allocation.
	_xbDebugAllocSt *dbg = (_xbDebugAllocSt*)((char*)pAllocatedBlock+sizeof(_CellHeader));
	dbg->m_pszFile = _xbDebugFileEntry(pszFile);
	dbg->m_nLine = nLine;
	dbg->m_cAllocOp = -1;
	dbg->m_cAllocOp = addAlloc(cByteBlock);
	dbg->m_pAllocHeap = this;
/*	if (nLine == 169)
	{
		if (cByteBlock==112)
			xbRelOutput("alloc trace[GOOD]: "+xbString(pszFile)+": size="+xbString(cByteBlock));
		else
			xbRelOutput("alloc trace[    ]: "+xbString(pszFile)+": size="+xbString(cByteBlock));
	}*/
#else
	addAlloc(cByteBlock);
#endif

#ifdef xbHEAPCOMPLETECHECK
	IsValid();
#endif
	return ((char*)pAllocatedBlock)+xbSTATICHEAP_HDRLEN;
}

void	xbStaticHeapBase::Free(void *pBlock)
{
//#ifdef _DEBUG
	xbRdbgAssert(m_bCreated);
	if (!m_bCreated)
		return;
//		xbThrow xbEsys(true,"xbStaticHeapBase(%s)::Free() : heap not created",GetName());
//#endif
	if (!xbMustBe(pBlock)) 
		return;
	pBlock = ((char*)pBlock)-xbSTATICHEAP_HDRLEN;
	
#ifdef xbHEAPCOMPLETECHECK
	IsValid();
#endif
	
	xbSectLockSimpleRec	Lck(&m_Share);
	
	_xbCHECKBLOCK(pBlock);

	_CellHeader		*pFreedBlock = (_CellHeader*)pBlock;
	ulong cByteBlock = pFreedBlock->m_cByte;
	if (cByteBlock >= xbSIZE_LIMIT) 
		xbThrowEsehFmt("xbStaticHeap(%s)::Free(%08lX) : block already deleted or invalid",GetName(),pBlock);

	_CellHeader		*pBlockNext = (_CellHeader*)((char*)pFreedBlock + cByteBlock);
	for(;;)
	{
		if (pBlockNext >= (_CellHeader*)(m_pSeg + m_cByteBusy))
		{
			if (pBlockNext > (_CellHeader*)(m_pSeg + m_cByteBusy))
				xbThrowEsehFmt("xbStaticHeap(%s)::Free(%08lX) : block already deleted or invalid (2)",GetName(),pBlock);

			m_cByteBusy -= pFreedBlock->m_cByte;
			
			pFreedBlock->m_cByte = xbSIZE_LIMIT;
			pFreedBlock = NULL;

			goto free_end;
		}
		if (!(pBlockNext->m_cByte & xbSTATICHEAP_FREECELL))
		{
			pFreedBlock->m_cByte |= xbSTATICHEAP_FREECELL;

			goto free_end;
		}

		pFreedBlock->m_cByte += (pBlockNext->m_cByte & ~xbSTATICHEAP_FREECELL);
	}

free_end:
	if (pFreedBlock)
	{
#ifdef _xbALLOCDEBUG_SPACEINIT
		if (xbDebugHeap_SpaceFreed != 0)
			memset((char*)pFreedBlock+sizeof(_CellHeader),xbDebugHeap_SpaceFreed,cByteBlock-sizeof(_CellHeader));
#endif
#ifdef _xbDEBUGHEAP_HDRSTR
		//Mark the block as freed with pad string
		memcpy((char*)pFreedBlock+xbSTATICHEAP_HDRLEN-_xbDEBUGHEAP_HDRLEN,_xbDEBUGHEAP_DELSTR,_xbDEBUGHEAP_HDRLEN);
#endif
	}
	subAlloc(cByteBlock);
#ifdef xbHEAPCOMPLETECHECK
	IsValid();
#endif
}

xbStaticHeapBase::_CellHeader *xbStaticHeapBase::_freeBlockFind(ulong cByte)
{
	xbREGISTER byte *pCell = m_pSeg;
	xbREGISTER byte *pCellEnd = m_pSeg + m_cByteBusy;
	xbREGISTER _CellHeader *pCellFreedHdr = NULL;
	while(pCell < pCellEnd)
	{
		xbREGISTER _CellHeader *pCellHdr = (_CellHeader*)pCell;
		if (!(pCellHdr->m_cByte & xbSTATICHEAP_FREECELL))
		{
			pCell += pCellHdr->m_cByte;
			pCellFreedHdr = NULL;
			continue;
		}

		ulong cByteCell = (pCellHdr->m_cByte & ~xbSTATICHEAP_FREECELL);
		if (pCellFreedHdr)
		{
			cByteCell += (pCellHdr->m_cByte & ~xbSTATICHEAP_FREECELL);
			pCellFreedHdr->m_cByte = cByteCell | xbSTATICHEAP_FREECELL;
			pCellHdr->m_cByte = xbSIZE_LIMIT;
			pCellHdr = pCellFreedHdr;
			pCell = (byte*)pCellFreedHdr;
		}
		if (cByteCell >= cByte)
			return pCellHdr;

		if (!pCellFreedHdr)
			pCellFreedHdr = pCellHdr;

		pCell += cByteCell;
	}
	return NULL;
}

bool	xbStaticHeapBase::IsValid() const
{
	xbSectLockSimpleRec Lck(&m_Share);
	xbREGISTER byte *pCell = m_pSeg;
	xbREGISTER byte *pCellEnd = m_pSeg + m_cByteBusy;
	while(pCell < pCellEnd)
	{
		xbREGISTER _CellHeader *pCellHdr = (_CellHeader*)pCell;
		if (pCellHdr->m_cByte & xbSTATICHEAP_FREECELL)
		{
			pCell += (pCellHdr->m_cByte & ~xbSTATICHEAP_FREECELL);
			continue;
		}
		if (!_isValid(pCell))
		{
			DebugDump();
			return false;
		}
		pCell += pCellHdr->m_cByte;
	}
	if (pCell > pCellEnd)
	{
		DebugDump();
		return false;
	}
	return true;
}

bool	xbStaticHeapBase::IsValid(void *pBlockSearched) const
{
	if (!pBlockSearched)
		return false;

	pBlockSearched = ((char*)pBlockSearched)-xbSTATICHEAP_HDRLEN;

	xbSectLockSimpleRec Lck(&m_Share);
	xbREGISTER byte *pCell = m_pSeg;
	xbREGISTER byte *pCellEnd = m_pSeg + m_cByteBusy;
	while(pCell < pCellEnd)
	{
		xbREGISTER _CellHeader *pCellHdr = (_CellHeader*)pCell;
		if (pCellHdr->m_cByte & xbSTATICHEAP_FREECELL)
		{
			if (pBlockSearched == pCell)
				return false;

			pCell += (pCellHdr->m_cByte & ~xbSTATICHEAP_FREECELL);
			continue;
		}
		if (!_isValid(pCell))
		{
			DebugDump();
			return false;
		}
		
		if (pBlockSearched == pCell)
			return true;

		pCell += pCellHdr->m_cByte;
	}

	if (pCell > pCellEnd)
		DebugDump();

	return false;
}

xbint		xbStaticHeapBase::GetSize(void *pBlock) const
{
	xbSectLockSimpleRec Lck(&m_Share);

	pBlock = ((char*)pBlock)-xbSTATICHEAP_HDRLEN;
	_xbCHECKBLOCK(pBlock);

	_CellHeader	*pCell = (_CellHeader*)pBlock;
	return (xbint)(pCell->m_cByte - xbSTATICHEAP_HDRLEN - xbSTATICHEAP_ENDLEN);
}

bool	xbStaticHeapBase::_isValid(void *pBlock) const
{
	char *pUserBlock = ((char*)pBlock)+xbSTATICHEAP_HDRLEN;

#ifdef _xbALLOCDEBUG
	_xbDebugAllocSt *dbg = (_xbDebugAllocSt*)((char*)pBlock+sizeof(_CellHeader));
	if (dbg->m_pAllocHeap != this)
	{
		try
		{
			if (!dbg->m_pszFile || ::IsBadStringPtrA(dbg->m_pszFile,DBGFILE_SZMAX))
				xbDbgHeapOutput(
					"²\t "
					"?? %08lX(%d) :(%u) "
					"[PB]xbStaticHeapBase(%s): Block (%p) not in this heap !!: phys=%p, size=%d [heap=%s]",
					dbg->m_pszFile,dbg->m_nLine,dbg->m_cAllocOp,
					m_szDbgName,(char*)pBlock+xbSTATICHEAP_HDRLEN,
					pBlock,((_CellHeader*)pBlock)->m_cByte,
					(dbg->m_pAllocHeap && IsBadReadPtr(dbg->m_pAllocHeap,sizeof(xbBaseHeap))?dbg->m_pAllocHeap->GetName():"???"));
			else
				xbDbgHeapOutput(
					"²\t "
					"%s(%d) :(%u) "
					"[PB]xbStaticHeapBase(%s): Block (%p) not in this heap !!: phys=%p, size=%d [heap=%s]",
					dbg->m_pszFile,dbg->m_nLine,dbg->m_cAllocOp,
					m_szDbgName,(char*)pBlock+xbSTATICHEAP_HDRLEN,
					pBlock,((_CellHeader*)pBlock)->m_cByte,
					(dbg->m_pAllocHeap && IsBadReadPtr(dbg->m_pAllocHeap,sizeof(xbBaseHeap))?dbg->m_pAllocHeap->GetName():"???"));
		}
		catch(...)
		{
			xbDbgHeapOutput(
				"²\t "
				"SCRATCHED!! [PB]xbStaticHeapBase(%s): Block (%p) not in this heap !!: phys=%p, size=%d [heap=?]",
				m_szDbgName,(char*)pBlock+xbSTATICHEAP_HDRLEN,
				pBlock,((_CellHeader*)pBlock)->m_cByte);
		}
	}
#endif
	if (!m_cByteBusy) 
	{
		xbDbgHeapOutput("xbStaticHeapBase(%s)::IsValid(void *pBlock=%p) : Heap empty, block is not in this heap",
			GetName(),pUserBlock);
		return false;
	}
#ifdef _xbDEBUGHEAP_DELSTR
	if (memcmp((char*)pBlock+xbSTATICHEAP_HDRLEN-_xbDEBUGHEAP_HDRLEN, _xbDEBUGHEAP_DELSTR, _xbDEBUGHEAP_HDRLEN)
		==0)
	{
		xbDbgHeapOutput("xbStaticHeapBase(%s)::IsValid(void *pBlock=%p) : Block already deleted",
			GetName(),pUserBlock);
		return false;
	}
#endif
#ifdef _xbDEBUGHEAP_HDRSTR
	if (memcmp((char*)pBlock+xbSTATICHEAP_HDRLEN-_xbDEBUGHEAP_HDRLEN, _xbDEBUGHEAP_HDRSTR, _xbDEBUGHEAP_HDRLEN)
		!=0)
		xbDbgHeapOutput("xbStaticHeapBase(%s)::IsValid(void *pBlock=%p) : Block header scratched",
			GetName(),pUserBlock);
#endif
	
	if (!((_CellHeader*)pBlock)->m_cByte
		|| ((_CellHeader*)pBlock)->m_cByte >= xbSIZE_LIMIT)
	{
		xbDbgHeapOutput("xbStaticHeapBase(%s)::IsValid(void *pBlock=%p) : Invalid block (size=%d,block start=%p)",
			GetName(),pUserBlock,((_CellHeader*)pBlock)->m_cByte,pBlock);
		return false;
	}
	
	if (pBlock < m_pSeg 
		|| pBlock >= m_pSeg + m_cByteBusy
		|| ((byte*)pBlock+((_CellHeader*)pBlock)->m_cByte) >= m_pSeg + m_cByteBusy)
	{
		
		xbDbgHeapOutput("xbStaticHeapBase(%s)::IsValid(void *pBlock=%p) : Offset of block (seg_start=%p,seg_end=%p,size=%c)",
			GetName(),pUserBlock,m_pSeg,m_pSeg + m_cByteBusy,
			((_CellHeader*)pBlock)->m_cByte);
		return false;
	}

#ifdef _xbDEBUGHEAP_ENDSTR
	if (memcmp((char*)pBlock + ((_CellHeader*)pBlock)->m_cByte - xbSTATICHEAP_ENDLEN, 
		_xbDEBUGHEAP_ENDSTR, xbSTATICHEAP_ENDLEN))
		xbDbgHeapOutput("xbStaticHeapBase(%s)::IsValid(void *pBlock=%p) : Block footer scratched (size=%d)",
			GetName(),pUserBlock,((_CellHeader*)pBlock)->m_cByte);
#endif
	return true;
}

bool	xbStaticHeapBase::Compact()
{
#ifdef xbHEAPCOMPLETECHECK
	IsValid();
#endif
	return false;
}

void	xbStaticHeapBase::DebugDump() const
{
#ifdef _xbHEAPDEBUG
	xbSectLockSimpleRec Lck(&m_Share);

//	if (!m_cByteBusy) 
//		return;
	
	xbDbgHeapOutput("xbStaticHeapBase(%s,cByteCell=%d,cCellPerSeg=%d,cByteSeg=%d)::DebugDump()\n{ alloc nb = %d, total sz = %I64d",
		GetName(),m_cByteCell,m_cByteSeg/m_cByteCell,m_cByteSeg,m_cAlloc,m_cByteAlloc);

	xbint		cAlloc=0;
	int64	cByteAlloc=0;

	xbREGISTER byte *pCell = m_pSeg;
	xbREGISTER byte *pCellEnd = m_pSeg + m_cByteBusy;
	while(pCell < pCellEnd)
	{
		xbREGISTER _CellHeader *pCellHdr = (_CellHeader*)pCell;
		if (pCellHdr->m_cByte & xbSTATICHEAP_FREECELL)
		{
			pCell += (pCellHdr->m_cByte & ~xbSTATICHEAP_FREECELL);
			continue;
		}
#		ifdef _xbALLOCDEBUG
		_xbDebugAllocSt *dbg = (_xbDebugAllocSt*)((char*)pCell+sizeof(_CellHeader));
#		endif

		try
		{
#			ifdef _xbALLOCDEBUG
			if (!dbg->m_pszFile || ::IsBadStringPtrA(dbg->m_pszFile,DBGFILE_SZMAX))
				xbDbgHeapOutput(
					"²\t "
					"?? %08lX(%d) :(%u) "
					"Busy block (%p) : phys=%p, size=%d",
					dbg->m_pszFile,dbg->m_nLine,dbg->m_cAllocOp,
					(char*)pCell+xbSTATICHEAP_HDRLEN,pCell,pCellHdr->m_cByte);
			else
#			endif
				xbDbgHeapOutput(
					"²\t "
#				ifdef _xbALLOCDEBUG
					"%s(%d) :(%u) "
#				endif
					"Busy block (%p) : phys=%p, size=%d",
#				ifdef _xbALLOCDEBUG
					dbg->m_pszFile,dbg->m_nLine,dbg->m_cAllocOp,
#				endif
					(char*)pCell+xbSTATICHEAP_HDRLEN,pCell,pCellHdr->m_cByte);
				
			_isValid(pCell);
		}
		catch(...)
		{
			xbDbgHeapOutput(
				"²\t "
			"SCRATCHED!! busy block (%p) : phys=%p, size=%d",
			(char*)pCell+xbSTATICHEAP_HDRLEN,pCell,pCellHdr->m_cByte);
		}

		cByteAlloc += (xbint)pCellHdr->m_cByte;
		cAlloc++;

		pCell += pCellHdr->m_cByte;
	}

	if (pCell > pCellEnd)
	{
		xbDbgHeapOutput("²\tSegment(%p) : block outside of this segment (block=%p, segment end=%p) !",
			m_pSeg,pCell,pCellEnd);
	}
	xbDbgHeapOutput("} alloc cAlloc = %d, alloc cByteAlloc = %I64d",cAlloc,cByteAlloc);
#endif
}

void	*xbStaticHeapBase::ReAlloc(void *pBlock,size_t cByte,xbAllocMode AllocMode
#ifdef _xbALLOCDEBUG
		,const char *pszFile,int nLine
#endif
		)
{
	if (!pBlock)
		return Alloc(cByte,AllocMode
#ifdef _xbALLOCDEBUG
			,pszFile,nLine
#endif
			);

//#ifdef _DEBUG
	if (!m_bCreated)
	{
		throwEnotCreated();
		return NULL;
	}
//#endif
	xbSectLockSimpleRec Lck(&m_Share);

	if (cByte > xbSIZE_LIMIT) 
		xbThrowEsizeLimitFmt("Memory allocation on heap %s greater than size limit (asked=%d, limit=%d)",GetName(),cByte,xbSIZE_LIMIT);

	ulong		cCellBlockNew = (ulong)((cByte+xbSTATICHEAP_HDRLEN+xbSTATICHEAP_ENDLEN+m_cByteCell-1) / m_cByteCell);
	ulong		cByteBlockNew = (ulong)(cCellBlockNew * m_cByteCell);

#ifdef xbHEAPCOMPLETECHECK
	IsValid();
#endif
	
	pBlock = ((char*)pBlock)-xbSTATICHEAP_HDRLEN;
	
//	xbSectLockSimpleRec		Lck(&m_Share);
	
	_xbCHECKBLOCK(pBlock);

	_CellHeader	*pReAllocatedBlock = (_CellHeader*)pBlock;
	if (pReAllocatedBlock->m_cByte == cByteBlockNew)
		return ((char*)pBlock)+xbSTATICHEAP_HDRLEN;

	if (cByteBlockNew < pReAllocatedBlock->m_cByte)
	{
		_CellHeader	*pFreedBlock = (_CellHeader*)((char*)pReAllocatedBlock + cByteBlockNew);

		pFreedBlock->m_cByte = (pReAllocatedBlock->m_cByte - cByteBlockNew) | xbSTATICHEAP_FREECELL;

		goto realloc_moveblocend;
	}

	{
		_CellHeader	*pNextBlock = (_CellHeader*)((char*)pBlock + pReAllocatedBlock->m_cByte);
		_CellHeader	*pEndBlock = (_CellHeader*)((char*)m_pSeg + m_cByteBusy);
		_CellHeader	*pFreeBlock = NULL;
		while(pNextBlock < pEndBlock
			&& (pNextBlock->m_cByte & xbSTATICHEAP_FREECELL))
		{
			if (!pFreeBlock)
				pFreeBlock = pNextBlock;
			else
			{
				pFreeBlock->m_cByte = (pFreeBlock->m_cByte & ~xbSTATICHEAP_FREECELL)
					+(pNextBlock->m_cByte & ~xbSTATICHEAP_FREECELL);

				pNextBlock->m_cByte = xbSIZE_LIMIT;
			}

			if ((pFreeBlock->m_cByte & ~xbSTATICHEAP_FREECELL) 
				+ pReAllocatedBlock->m_cByte < cByteBlockNew)
			{
				pNextBlock = (_CellHeader*)((char*)pNextBlock + (pNextBlock->m_cByte & ~xbSTATICHEAP_FREECELL));
				continue;
			}

			if ((pFreeBlock->m_cByte & ~xbSTATICHEAP_FREECELL) 
				+ pReAllocatedBlock->m_cByte > cByteBlockNew)
			{
				_CellHeader	*pFreedBlock = (_CellHeader*)((char*)pReAllocatedBlock + cByteBlockNew);

				pFreedBlock->m_cByte = ((pFreeBlock->m_cByte & ~xbSTATICHEAP_FREECELL) 
					+ pReAllocatedBlock->m_cByte - cByteBlockNew) | xbSTATICHEAP_FREECELL;
			}
			
			goto realloc_moveblocend;
		}
	}

	void *pNewBlock = Alloc(cByte,AllocMode
#ifdef _xbALLOCDEBUG
			,pszFile,nLine
#endif
			);
	if (pNewBlock)
		::memcpy(pNewBlock,((char*)pReAllocatedBlock)+xbSTATICHEAP_HDRLEN,
			pReAllocatedBlock->m_cByte - xbSTATICHEAP_HDRLEN - xbSTATICHEAP_ENDLEN);
	Free(((char*)pReAllocatedBlock)+xbSTATICHEAP_HDRLEN);
	return pNewBlock;
	
realloc_moveblocend:
	if (cByteBlockNew > pReAllocatedBlock->m_cByte)
	{
		//Initialize new space allocated
		if (AllocMode == xbAlloc_Init) 
			memset((char*)pReAllocatedBlock+pReAllocatedBlock->m_cByte - xbSTATICHEAP_ENDLEN,0,
			cByteBlockNew - pReAllocatedBlock->m_cByte 
			- xbSTATICHEAP_ENDLEN);
#ifdef _xbALLOCDEBUG_SPACEINIT
		else if (xbDebugHeap_SpaceUninit != 0) 
			memset((char*)pReAllocatedBlock+pReAllocatedBlock->m_cByte - xbSTATICHEAP_ENDLEN,
			xbDebugHeap_SpaceUninit,
			cByteBlockNew - pReAllocatedBlock->m_cByte 
			- xbSTATICHEAP_ENDLEN);
#endif
	}

	m_cByteAlloc += cByteBlockNew - pReAllocatedBlock->m_cByte;
	pReAllocatedBlock->m_cByte = cByteBlockNew;

#ifdef _xbALLOCDEBUG
	//Feed debug header with new allocation location
	_xbDebugAllocSt *dbg = (_xbDebugAllocSt*)((char*)pReAllocatedBlock+sizeof(_CellHeader));
	dbg->m_pszFile = _xbDebugFileEntry(pszFile);
	dbg->m_nLine = nLine;
	dbg->m_pAllocHeap = this;
#endif

#ifdef _xbDEBUGHEAP_ENDSTR
	memcpy((char*)pReAllocatedBlock + pReAllocatedBlock->m_cByte - xbSTATICHEAP_ENDLEN,_xbDEBUGHEAP_ENDSTR,_xbDEBUGHEAP_ENDLEN);
#endif

#ifdef xbHEAPCOMPLETECHECK
	IsValid();
#endif
	return ((char*)pReAllocatedBlock)+xbSTATICHEAP_HDRLEN;
}

void	xbStaticHeapBase::throwEnotCreated()
{
	xbThrowEsysFmt("xbStaticHeapBase(%s)::Alloc() : heap not created",GetName());
}

void	xbStaticHeapBase::throwEmem(ulong cByteBlock)
{
	xbThrowEmemFmt("xbStaticHeap(%s)::Alloc(sz=%d) : Heap is full (alloc nb = %d, total sz = %I64d)",
		GetName(),cByteBlock,m_cAlloc,m_cByteAlloc);
}

char *xbStaticHeapBase::AllocString(const char *pszStr
#ifdef _xbALLOCDEBUG
		,const char *pszFile,int nLine
#endif
		)
{
	size_t len = strlen(pszStr);
	char *newStr = (char*)Alloc(len+1,xbAlloc_Default
#ifdef _xbALLOCDEBUG
		,pszFile,nLine
#endif
		);
	
	if (!newStr)
		return NULL;

	xbStrncpy(newStr,pszStr,len);
	newStr[len] = 0;
	return newStr;
}
