kernel/eka/common/heap.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 18 Jan 2010 21:31:10 +0200
changeset 8 538db54a451d
parent 2 4122176ea935
permissions -rw-r--r--
Revision: 201003 Kit: 201003

// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32\common\heap.cpp
// 
//

#include "common.h"
#ifdef __KERNEL_MODE__
#include <kernel/kern_priv.h>
#endif

#ifdef _DEBUG
#define __SIMULATE_ALLOC_FAIL(s)	if (CheckForSimulatedAllocFail()) {s}
#define	__CHECK_CELL(p)				CheckCell(p)
#define	__ZAP_CELL(p)				memset( ((TUint8*)p) + RHeap::EAllocCellSize, 0xde, p->len - RHeap::EAllocCellSize)
#define __DEBUG_SAVE(p)				TInt dbgNestLevel = ((SDebugCell*)p)->nestingLevel
#define __DEBUG_RESTORE(p)			((SDebugCell*)(((TUint8*)p)-EAllocCellSize))->nestingLevel = dbgNestLevel
#else
#define __SIMULATE_ALLOC_FAIL(s)
#define	__CHECK_CELL(p)
#define	__ZAP_CELL(p)
#define __DEBUG_SAVE(p)
#define __DEBUG_RESTORE(p)
#endif

#define __NEXT_CELL(p)				((SCell*)(((TUint8*)p)+p->len))

#define __POWER_OF_2(x)				((TUint32)((x)^((x)-1))>=(TUint32)(x))

#define __MEMORY_MONITOR_CHECK_CELL(p) \
					{ \
					TLinAddr m = TLinAddr(iAlign-1); \
					SCell* c = (SCell*)(((TUint8*)p)-EAllocCellSize); \
					if((c->len & m) || (c->len<iMinCell) || ((TUint8*)c<iBase) || ((TUint8*)__NEXT_CELL(c)>iTop)) \
						BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)p, (TUint32)c->len-EAllocCellSize); \
					}
					
/**
@SYMPatchable
@publishedPartner
@released

Defines the minimum cell size of  a heap.

The constant can be changed at ROM build time using patchdata OBY keyword.
*/
#ifdef __X86GCC__	// For X86GCC we dont use the proper data import attribute
#undef IMPORT_D		// since the constant is not really imported. GCC doesn't 
#define IMPORT_D	// allow imports from self.
#endif
IMPORT_D extern const TInt KHeapMinCellSize;

/**
@SYMPatchable
@publishedPartner
@released

This constant defines the ratio that determines the amount of hysteresis between heap growing and heap
shrinking.
It is a 32-bit fixed point number where the radix point is defined to be
between bits 7 and 8 (where the LSB is bit 0) i.e. using standard notation, a Q8 or a fx24.8
fixed point number.  For example, for a ratio of 2.0, set KHeapShrinkHysRatio=0x200.

The heap shrinking hysteresis value is calculated to be:
@code
KHeapShrinkHysRatio*(iGrowBy>>8)
@endcode
where iGrowBy is a page aligned value set by the argument, aGrowBy, to the RHeap constructor.
The default hysteresis value is iGrowBy bytes i.e. KHeapShrinkHysRatio=2.0.

Memory usage may be improved by reducing the heap shrinking hysteresis
by setting 1.0 < KHeapShrinkHysRatio < 2.0.  Heap shrinking hysteresis is disabled/removed
when KHeapShrinkHysRatio <= 1.0.

The constant can be changed at ROM build time using patchdata OBY keyword.
*/
IMPORT_D extern const TInt KHeapShrinkHysRatio;

#pragma warning( disable : 4705 )	// statement has no effect
UEXPORT_C RHeap::RHeap(TInt aMaxLength, TInt aAlign, TBool aSingleThread)
/**
@internalComponent
*/
//
// Constructor for fixed size heap
//
	:	iMinLength(aMaxLength), iMaxLength(aMaxLength), iOffset(0), iGrowBy(0), iChunkHandle(0),
		iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL)
	{
	iAlign = aAlign ? aAlign : ECellAlignment;
	iPageSize = 0;
	iFlags = aSingleThread ? (ESingleThreaded|EFixedSize) : EFixedSize;
	Initialise();
	}
#pragma warning( default : 4705 )




UEXPORT_C RHeap::RHeap(TInt aChunkHandle, TInt aOffset, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread)
/**
@internalComponent
*/
//
// Constructor for chunk heaps.
//
	:	iOffset(aOffset), iChunkHandle(aChunkHandle),
		iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL)
	{
	TInt sz = iBase - ((TUint8*)this - iOffset);
	GET_PAGE_SIZE(iPageSize);
	__ASSERT_ALWAYS(iOffset>=0, HEAP_PANIC(ETHeapNewBadOffset));
	iMinLength = Max(aMinLength, sz + EAllocCellSize);
	iMinLength = _ALIGN_UP(iMinLength, iPageSize);
	iMaxLength = Max(aMaxLength, iMinLength);
	iMaxLength = _ALIGN_UP(iMaxLength, iPageSize);
	iGrowBy = _ALIGN_UP(aGrowBy, iPageSize);
	iFlags = aSingleThread ? ESingleThreaded : 0;
	iAlign = aAlign ? aAlign : ECellAlignment;
	Initialise();
	}




UEXPORT_C TAny* RHeap::operator new(TUint aSize, TAny* aBase) __NO_THROW
/**
@internalComponent
*/
	{
	__ASSERT_ALWAYS(aSize>=sizeof(RHeap), HEAP_PANIC(ETHeapNewBadSize));
	RHeap* h = (RHeap*)aBase;
	h->iAlign = 0x80000000;	// garbage value
	h->iBase = ((TUint8*)aBase) + aSize;
	return aBase;
	}

void RHeap::Initialise()
//
// Initialise the heap.
//
	{

	__ASSERT_ALWAYS((TUint32)iAlign>=sizeof(TAny*) && __POWER_OF_2(iAlign), HEAP_PANIC(ETHeapNewBadAlignment));
	iCellCount = 0;
	iTotalAllocSize = 0;
	iBase = (TUint8*)Align(iBase + EAllocCellSize);
	iBase -= EAllocCellSize;
	TInt b = iBase - ((TUint8*)this - iOffset);
	TInt len = _ALIGN_DOWN(iMinLength - b, iAlign);
	iTop = iBase + len;
	iMinLength = iTop - ((TUint8*)this - iOffset);
	iMinCell = Align(KHeapMinCellSize + Max((TInt)EAllocCellSize, (TInt)EFreeCellSize));
#ifdef _DEBUG
	memset(iBase, 0xa5, len);
#endif
	SCell* pM=(SCell*)iBase; // First free cell
	iFree.next=pM; // Free list points to first free cell
	iFree.len=0; // Stop free from joining this with a free block
	pM->next=NULL; // Terminate the free list
	pM->len=len; // Set the size of the free cell
	}

#ifdef _DEBUG
void RHeap::CheckCell(const SCell* aCell) const
	{
	TLinAddr m = TLinAddr(iAlign - 1);

	__ASSERT_DEBUG(!(aCell->len & m), HEAP_PANIC(ETHeapBadCellAddress));
	__ASSERT_DEBUG(aCell->len >= iMinCell, HEAP_PANIC(ETHeapBadCellAddress));
	__ASSERT_DEBUG((TUint8*)aCell>=iBase, HEAP_PANIC(ETHeapBadCellAddress));
	__ASSERT_DEBUG((TUint8*)__NEXT_CELL(aCell)<=iTop, HEAP_PANIC(ETHeapBadCellAddress));
	}
#endif

UEXPORT_C RHeap::SCell* RHeap::GetAddress(const TAny* aCell) const
//
// As much as possible, check a cell address and backspace it
// to point at the cell header.
//
	{

	TLinAddr m = TLinAddr(iAlign - 1);
	__ASSERT_ALWAYS(!(TLinAddr(aCell)&m), HEAP_PANIC(ETHeapBadCellAddress));

	SCell* pC = (SCell*)(((TUint8*)aCell)-EAllocCellSize);
	__CHECK_CELL(pC);

	return pC;
	}




UEXPORT_C TInt RHeap::AllocLen(const TAny* aCell) const
/**
Gets the length of the available space in the specified allocated cell.

@param aCell A pointer to the allocated cell.

@return The length of the available space in the allocated cell.

@panic USER 42 if aCell does not point to  a valid cell.
*/
	{

	SCell* pC = GetAddress(aCell);
	return pC->len - EAllocCellSize;
	}





#if !defined(__HEAP_MACHINE_CODED__) || defined(_DEBUG)
RHeap::SCell* RHeap::DoAlloc(TInt aSize, SCell*& aLastFree)
//
// Allocate without growing. aSize includes cell header and alignment.
// Lock already held.
//
	{
	SCell* pP = &iFree;
	SCell* pC = pP->next;
	for (; pC; pP=pC, pC=pC->next) // Scan the free list
		{
		__CHECK_CELL(pC);
		SCell* pE;
		if (pC->len >= aSize)				// Block size bigger than request
			{
			if (pC->len - aSize < iMinCell)	// Leftover must be large enough to hold an SCell
			   	{
			   	aSize = pC->len;			// It isn't, so take it all
			   	pE = pC->next;				// Set the next field
			   	}
			else
			   	{
			   	pE = (SCell*)(((TUint8*)pC)+aSize); // Take amount required
			   	pE->len = pC->len - aSize;	// Initialize new free cell
			   	pE->next = pC->next;
			   	}
			pP->next = pE;					// Update previous pointer
			pC->len = aSize;				// Set control size word
#if defined(_DEBUG)														
			((SDebugCell*)pC)->nestingLevel = iNestingLevel;
			((SDebugCell*)pC)->allocCount = ++iAllocCount;
#endif
			return pC;
			}
		}
	aLastFree = pP;
	return NULL;
	}
#endif




UEXPORT_C TAny* RHeap::Alloc(TInt aSize)
/**
Allocates a cell of the specified size from the heap.

If there is insufficient memory available on the heap from which to allocate
a cell of the required size, the function returns NULL.

The cell is aligned according to the alignment value specified at construction,
or the default alignment value, if an explict value was not specified.

The resulting size of the allocated cell may be rounded up to a
value greater than aSize, but is guaranteed to be not less than aSize.

@param aSize The 
size of the cell to be allocated from the heap

@return A pointer to the allocated cell. NULL if there is insufficient memory 
        available.
        
@panic USER 47 if the maximum unsigned value of aSize is greater than or equal
       to the value of KMaxTInt/2; for example, calling Alloc(-1) raises
       this panic.
       
@see KMaxTInt        
*/
	{

	__CHECK_THREAD_STATE;
	__ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize));
	__SIMULATE_ALLOC_FAIL(return NULL;)
	
	TInt origSize = aSize;
	aSize = Max(Align(aSize + EAllocCellSize), iMinCell);
	SCell* pL = NULL;
	Lock();
	SCell* pC = (SCell*)DoAlloc(aSize, pL);
	if (!pC && !(iFlags & EFixedSize))
		{
		// try to grow chunk heap
		TInt r = TryToGrowHeap(aSize, pL);
		if (r==KErrNone)
			pC = DoAlloc(aSize, pL);
		}
	if (pC)
		++iCellCount, iTotalAllocSize += (pC->len - EAllocCellSize);
	Unlock();
	if (pC)
		{
		TAny* result=((TUint8*)pC) + EAllocCellSize;
		if (iFlags & ETraceAllocs)
			{
			TUint32 traceData[2];
			traceData[0] = AllocLen(result);
			traceData[1] = origSize;
			BTraceContextN(BTrace::EHeap, BTrace::EHeapAlloc, (TUint32)this, (TUint32)result, traceData, sizeof(traceData));
			}
#ifdef __KERNEL_MODE__
		memclr(result, pC->len - EAllocCellSize);
#endif		
		return result;
		}
	if (iFlags & ETraceAllocs)						
			BTraceContext8(BTrace::EHeap, BTrace::EHeapAllocFail, (TUint32)this, (TUint32)origSize);
	return NULL;
	}




TInt RHeap::TryToGrowHeap(TInt aSize, SCell* aLastFree)
	{
	TBool at_end = IsLastCell(aLastFree);
	TInt extra = at_end ? aSize - aLastFree->len : aSize;
	extra = (extra + iGrowBy - 1) / iGrowBy;
	extra *= iGrowBy;
	TInt cur_len = _ALIGN_UP(iTop - ((TUint8*)this - iOffset), iPageSize);
	TInt new_len = cur_len + extra;
	TInt r = KErrNoMemory;
	if (new_len <= iMaxLength)
		{
		r = SetBrk(new_len);
		if (r == KErrNone)
			{
			if (at_end)
				aLastFree->len += extra;
			else
				{
				SCell* pC = (SCell*)iTop;
				pC->len = extra;
				pC->next = NULL;
				aLastFree->next = pC;
				}
			iTop += extra;
			}
		}
	return r;
	}




#ifndef __KERNEL_MODE__
EXPORT_C TInt RHeap::Compress()
/**
Compresses the heap.

The function frees excess committed space from the top 
of the heap. The size of the heap is never reduced below the minimum size 
specified during creation of the heap.

@return The space reclaimed. If no space can be reclaimed, then this value 
        is zero.
*/
	{

	if (iFlags & EFixedSize)
		return 0;
	TInt r = 0;
	Lock();
	SCell* pC = &iFree;
	for (; pC->next; pC=pC->next) {}
	if (pC!=&iFree)
		{
		__CHECK_CELL(pC);
		if (IsLastCell(pC))
			r = Reduce(pC);
		}
	Unlock();
	return r;
	}
#endif




#if !defined(__HEAP_MACHINE_CODED__) || defined(_DEBUG)
void RHeap::DoFree(SCell* pC)
	{
	__ZAP_CELL(pC);

	SCell* pP = &iFree;
	SCell* pE = pP->next;
	for (; pE && pE<pC; pP=pE, pE=pE->next) {}
	if (pE)			// Is there a following free cell?
		{
		SCell* pN = __NEXT_CELL(pC);
		__ASSERT_ALWAYS(pN<=pE, HEAP_PANIC(ETHeapFreeBadNextCell)); // Following cell overlaps
		if (pN==pE) // Is it adjacent
			{
			pC->len += pE->len; // Yes - coalesce adjacent free cells
			pC->next = pE->next;
			}
		else					// pN<pE, non-adjacent free cells
			pC->next = pE;		// Otherwise just point to it
		}
	else
		pC->next = NULL;		// No following free cell
	SCell* pN = __NEXT_CELL(pP);	// pN=pP=&iFree if no preceding free cell
	__ASSERT_ALWAYS(pN<=pC, HEAP_PANIC(ETHeapFreeBadPrevCell)); // Previous cell overlaps
	if (pN==pC) // Is it adjacent
		{
		pP->len += pC->len;		// Yes - coalesce adjacent free cells
		pP->next = pC->next;
		pC = pP;				// for size reduction check
		}
	else						// pN<pC, non-adjacent free cells
		pP->next = pC;			// point previous cell to the one being freed
	pN = __NEXT_CELL(pC);		// End of amalgamated free cell
	if ((TUint8*)pN==iTop && !(iFlags & EFixedSize) && 
		pC->len >= KHeapShrinkHysRatio*(iGrowBy>>8))
		Reduce(pC);
	}
#endif




UEXPORT_C void RHeap::Free(TAny* aCell)
/**
Frees the specified cell and returns it to the heap.

@param aCell A pointer to a valid cell; this pointer can also be NULL,
             in which case the function does nothing and just returns.

@panic USER 42 if aCell points to an invalid cell.
*/
	{
	__CHECK_THREAD_STATE;
	if (!aCell)
		return;
	Lock();
	if (iFlags & EMonitorMemory)
		__MEMORY_MONITOR_CHECK_CELL(aCell);
	SCell* pC = GetAddress(aCell);
	--iCellCount;
	iTotalAllocSize -= (pC->len - EAllocCellSize);
	DoFree(pC);
	if (iFlags & ETraceAllocs)
		BTraceContext8(BTrace::EHeap, BTrace::EHeapFree, (TUint32)this, (TUint32)aCell);
	Unlock();
	}




TInt RHeap::Reduce(SCell* aCell)
	{
	TInt reduce=0;
	TInt offset=((TUint8*)aCell)-((TUint8*)this - iOffset);
	if (offset>=iMinLength)
		reduce = aCell->len;						// length of entire free cell
	else
		reduce = offset + aCell->len - iMinLength;	// length of free cell past minimum heap size
	reduce = _ALIGN_DOWN(reduce, iPageSize);		// round down to page multiple
	if (reduce<=0)
		return 0;									// can't reduce this heap
	TInt new_cell_len = aCell->len - reduce;		// length of last free cell after reduction
	if (new_cell_len == 0)
		{
		// the free cell can be entirely eliminated
		SCell* pP = &iFree;
		for (; pP->next!=aCell; pP=pP->next) {}
		pP->next = NULL;
		}
	else
		{
		if (new_cell_len < iMinCell)
			{
			// max reduction would leave a cell too small
			reduce -= iPageSize;
			new_cell_len += iPageSize;
			}
		aCell->len = new_cell_len;	// reduce the cell length
		}
	iTop -= reduce;
	TInt new_len = _ALIGN_UP(iTop - ((TUint8*)this - iOffset), iPageSize);
	TInt r = SetBrk(new_len);
	__ASSERT_ALWAYS(r==KErrNone, HEAP_PANIC(ETHeapReduceFailed));
	return reduce;
	}




#ifndef __KERNEL_MODE__
EXPORT_C void RHeap::Reset()
/**
Frees all allocated cells on this heap.
*/
	{

	Lock();
	if (!(iFlags & EFixedSize))
		{
		TInt r = SetBrk(iMinLength);
		__ASSERT_ALWAYS(r==KErrNone, HEAP_PANIC(ETHeapResetFailed));
		}
	Initialise();
	Unlock();
	}
#endif




inline void RHeap::FindFollowingFreeCell(SCell* aCell, SCell*& aPrev, SCell*& aNext)
//
// Find the free cell that immediately follows aCell, if one exists
// If found, aNext is set to point to it, else it is set to NULL.
// aPrev is set to the free cell before aCell or the dummy free cell where there are no free cells before aCell.
// Called with lock enabled.
//
	{
	aPrev = &iFree;
	aNext = aPrev->next;
	for (; aNext && aNext<aCell; aPrev=aNext, aNext=aNext->next) {}	
	
	if (aNext) // If there is a following free cell, check its directly after aCell.
		{
			SCell* pNextCell = __NEXT_CELL(aCell);			// end of this cell
			__ASSERT_ALWAYS(pNextCell<=aNext, (Unlock(), HEAP_PANIC(ETHeapReAllocBadNextCell)));	// Following free cell overlaps
			if (pNextCell!=aNext) 
				aNext=NULL;		
		}
	}




TInt RHeap::TryToGrowCell(SCell* aCell,SCell* aPrev, SCell* aNext, TInt aSize)
//
// Try to grow the heap cell 'aCell' in place, to size 'aSize'.
// Requires the free cell immediately after aCell (aNext), and the free cell prior to
// that (aPrev), to be provided.  (As found by FindFollowingFreeCell)
//

	{
	TInt extra = aSize - aCell->len;
	if (aNext && (aNext->len>=extra)) // Is there a following free cell big enough?
		{
		if (aNext->len - extra >= iMinCell)	// take part of free cell ?
			{
			SCell* pX = (SCell*)((TUint8*)aNext + extra);	// remainder of free cell
			pX->next = aNext->next;			// remainder->next = original free cell->next
			pX->len = aNext->len - extra;		// remainder length = original free cell length - extra
			aPrev->next = pX;					// put remainder into free chain
			}
		else
			{
			extra = aNext->len;					// Take whole free cell
			aPrev->next = aNext->next;			// remove from free chain
			}
#ifdef __KERNEL_MODE__
		memclr(((TUint8*)aCell) + aCell->len, extra);
#endif		
		aCell->len += extra;					// update reallocated cell length
		iTotalAllocSize += extra;
		return KErrNone;
		}
	return KErrGeneral;  // No space to grow cell
	}




// UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode)
/**
Increases or decreases the size of an existing cell in the heap.

If the cell is being decreased in size, then it is guaranteed not to move,
and the function returns the pointer originally passed in aCell. Note that the
length of the cell will be the same if the difference between the old size
and the new size is smaller than the minimum cell size.

If the cell is being increased in size, i.e. aSize is bigger than its
current size, then the function tries to grow the cell in place.
If successful, then the function returns the pointer originally
passed in aCell. If unsuccessful, then:

1. if the cell cannot be moved, i.e. aMode has the ENeverMove bit set, then
   the function returns NULL.
2. if the cell can be moved, i.e. aMode does not have the ENeverMove bit set,
   then the function tries to allocate a new replacement cell, and, if
   successful, returns a pointer to the new cell; if unsuccessful, it
   returns NULL.

Note that in debug mode, the function returns NULL if the cell cannot be grown
in place, regardless of whether the ENeverMove bit is set.

If the reallocated cell is at a different location from the original cell, then
the content of the original cell is copied to the reallocated cell.

If the supplied pointer, aCell is NULL, then the function attempts to allocate
a new cell, but only if the cell can be moved, i.e. aMode does not have
the ENeverMove bit set.

Note the following general points:

1. If reallocation fails, the content of the original cell is preserved.

2. The resulting size of the re-allocated cell may be rounded up to a value
   greater than aSize, but is guaranteed to be not less than aSize.
 
@param aCell A pointer to the cell to be reallocated. This may be NULL.

@param aSize The new size of the cell. This may be bigger or smaller than the
             size of the original cell.
             
@param aMode Flags controlling the reallocation. The only bit which has any
             effect on this function is that defined by the enumeration
             ENeverMove of the enum RAllocator::TReAllocMode.
             If this is set, then any successful reallocation guarantees not
             to have changed the start address of the cell.
             By default, this parameter is zero.

@return A pointer to the reallocated cell. This may be the same as the original
        pointer supplied through aCell. NULL if there is insufficient memory to
        reallocate the cell, or to grow it in place.

@panic USER 42, if aCell is not NULL, and does not point to a valid cell.
@panic USER 47, if the maximum unsigned value of aSize is greater
                than or equal to KMaxTInt/2. For example,
                calling ReAlloc(someptr,-1) raises this panic.

@see RAllocator::TReAllocMode
*/
UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode)
	{
	if (aCell && iFlags&EMonitorMemory)
		__MEMORY_MONITOR_CHECK_CELL(aCell);
	TAny* retval = ReAllocImpl(aCell, aSize, aMode);
	if (iFlags & ETraceAllocs)
		{
		if (retval)
			{
			TUint32 traceData[3];
			traceData[0] = AllocLen(retval);
			traceData[1] = aSize;
			traceData[2] = (TUint32)aCell;
			BTraceContextN(BTrace::EHeap, BTrace::EHeapReAlloc,(TUint32)this, (TUint32)retval,traceData, sizeof(traceData));
			}
		else
			BTraceContext12(BTrace::EHeap, BTrace::EHeapReAllocFail, (TUint32)this, (TUint32)aCell, (TUint32)aSize);
		}
	return retval;
	}
inline TAny* RHeap::ReAllocImpl(TAny* aCell, TInt aSize, TInt aMode)
	{
	__CHECK_THREAD_STATE;
	if (!aCell)
		return (aMode & ENeverMove) ? NULL : Alloc(aSize);
	__ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize));
	Lock();
	SCell* pC = GetAddress(aCell);
	TInt old_len = pC->len;
	__DEBUG_SAVE(pC);
	aSize = Max(Align(aSize + EAllocCellSize), iMinCell);
	if (aSize > old_len)	// Trying to grow cell
		{
		__SIMULATE_ALLOC_FAIL({	Unlock(); return NULL;})			
		
		// Try to grow cell in place, without reallocation
		SCell* pPrev;
		SCell* pNext;
		FindFollowingFreeCell(pC,pPrev, pNext);
		TInt r = TryToGrowCell(pC, pPrev, pNext, aSize);
		
		if (r==KErrNone) 
			{
			Unlock();
			return aCell;
			}

		if (!(aMode & ENeverMove))
		// If moving allowed, try re-alloc. 
		// If we need to extend heap,and cell is at the end, try and grow in place
			{
			SCell* pLastFree;
			SCell* pNewCell = (SCell*)DoAlloc(aSize, pLastFree);
			if (!pNewCell && !(iFlags & EFixedSize))
			// if we need to extend the heap to alloc
				{
				if (IsLastCell(pC) || (pNext && IsLastCell(pNext)))
				// if last used Cell, try and extend heap and then cell 
					{
					TInt r = TryToGrowHeap(aSize - old_len, pLastFree);
					if (r==KErrNone)
						{
						r = TryToGrowCell(pC, pPrev, pPrev->next, aSize);
						Unlock();
						__ASSERT_DEBUG(r == KErrNone, HEAP_PANIC(ETHeapCellDidntGrow));						
						return aCell;
						}
					}
				else
				// try to grow chunk heap and Alloc on it
					{
					TInt r = TryToGrowHeap(aSize, pLastFree);
					if (r==KErrNone)
						pNewCell = DoAlloc(aSize, pLastFree);
					}
				}

			if (pNewCell)
			// if we created a new cell, adjust tellies, copy the contents and delete old cell.
				{
				iCellCount++;
				iTotalAllocSize += (pNewCell->len - EAllocCellSize);

				Unlock();
				TUint8* raw = ((TUint8*) pNewCell);
				
				memcpy(raw + EAllocCellSize, aCell, old_len - EAllocCellSize);
#ifdef __KERNEL_MODE__
				memclr(raw + old_len, pNewCell->len - old_len);
#endif		
				Free(aCell);
				__DEBUG_RESTORE(raw + EAllocCellSize);
				return raw + EAllocCellSize;
				}
			}
		else 
		// No moving, but still posible to extend the heap (if heap extendable)
			{
			if (!(iFlags & EFixedSize) && (IsLastCell(pC) || (pNext && IsLastCell(pNext))))
				{
				SCell* pLastFree = pNext ? pNext : pPrev;
				TInt r = TryToGrowHeap(aSize - old_len, pLastFree);
				if (r==KErrNone)
					{
					r = TryToGrowCell(pC, pPrev, pPrev->next, aSize);
					Unlock();
					__ASSERT_DEBUG(r==KErrNone, HEAP_PANIC(ETHeapCellDidntGrow));					
					return aCell;
					}
				}
			}			
		Unlock();
		return NULL;
		}
	if (old_len - aSize >= iMinCell)
		{
		// cell shrinking, remainder big enough to form a new free cell
		SCell* pX = (SCell*)((TUint8*)pC + aSize);	// pointer to new free cell
		pC->len = aSize;			// update cell size
		pX->len = old_len - aSize;	// size of remainder
		iTotalAllocSize -= pX->len;
		DoFree(pX);					// link new free cell into chain, shrink heap if necessary
		}
	Unlock();
	return aCell;
	}




#ifndef __KERNEL_MODE__

EXPORT_C TInt RHeap::Available(TInt& aBiggestBlock) const
/**
Gets the total free space currently available on the heap and the space 
available in the largest free block.

The space available represents the total space which can be allocated.

Note that compressing the heap may reduce the total free space available and 
the space available in the largest free block.

@param aBiggestBlock On return, contains the space available 
                     in the largest free block on the heap.
                     
@return The total free space currently available on the heap.
*/
	{

	TInt total = 0;
	TInt max = 0;
	Lock();
	SCell* pC = iFree.next;
	for (; pC; pC=pC->next)
		{
		TInt l = pC->len - EAllocCellSize;
		if (l > max)
			max = l;
		total += l;
		}
	Unlock();
	aBiggestBlock = max;
	return total;
	}




EXPORT_C TInt RHeap::AllocSize(TInt& aTotalAllocSize) const
/**
Gets the number of cells allocated on this heap, and the total space 
allocated to them.

@param aTotalAllocSize On return, contains the total space allocated
                       to the cells.

@return The number of cells allocated on this heap.
*/
	{
	Lock();
	TInt c = iCellCount;
	aTotalAllocSize = iTotalAllocSize;
	Unlock();
	return c;
	}




EXPORT_C RHeap* UserHeap::FixedHeap(TAny* aBase, TInt aMaxLength, TInt aAlign, TBool aSingleThread)
/**
Creates a fixed length heap at a specified location.

On successful return from this function, aMaxLength bytes are committed by the chunk.
The heap cannot be extended.

@param aBase         A pointer to the location where the heap is to be constructed.
@param aMaxLength    The length of the heap. If the supplied value is less
                     than KMinHeapSize, it is discarded and the value KMinHeapSize
                     is used instead.
@param aAlign        The alignment of heap cells.
@param aSingleThread Indicates whether single threaded or not.

@return A pointer to the new heap, or NULL if the heap could not be created.

@panic USER 56 if aMaxLength is negative.
@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
*/
//
// Force construction of the fixed memory.
//
	{

	__ASSERT_ALWAYS(aMaxLength>=0, ::Panic(ETHeapMaxLengthNegative));
	if (aMaxLength<KMinHeapSize)
		aMaxLength=KMinHeapSize;
	RHeap* h = new(aBase) RHeap(aMaxLength, aAlign, aSingleThread);
	if (!aSingleThread)
		{
		TInt r = h->iLock.CreateLocal();
		if (r!=KErrNone)
			return NULL;
		h->iHandles = (TInt*)&h->iLock;
		h->iHandleCount = 1;
		}
	return h;
	}


/**
Constructor where minimum and maximum length of the heap can be defined.
It defaults the chunk heap to be created to have use a new local chunk, 
to have a grow by value of KMinHeapGrowBy, to be unaligned, not to be 
single threaded and not to have any mode flags set.

@param aMinLength    The minimum length of the heap to be created.
@param aMaxLength    The maximum length to which the heap to be created can grow.
                     If the supplied value is less than KMinHeapSize, then it
                     is discarded and the value KMinHeapSize used instead.
*/
EXPORT_C TChunkHeapCreateInfo::TChunkHeapCreateInfo(TInt aMinLength, TInt aMaxLength) :
	iVersionNumber(EVersion0), iMinLength(aMinLength), iMaxLength(aMaxLength),
	iAlign(0), iGrowBy(1), iSingleThread(EFalse), 
	iOffset(0), iPaging(EUnspecified), iMode(0), iName(NULL)
	{
	}


/**
Sets the chunk heap to create a new chunk with the specified name.

This overriddes any previous call to TChunkHeapCreateInfo::SetNewChunkHeap() or
TChunkHeapCreateInfo::SetExistingChunkHeap() for this TChunkHeapCreateInfo object.

@param aName	The name to be given to the chunk heap to be created
				If NULL, the function constructs a local chunk to host the heap.
				If not NULL, a pointer to a descriptor containing the name to be 
				assigned to the global chunk hosting the heap.
*/
EXPORT_C void TChunkHeapCreateInfo::SetCreateChunk(const TDesC* aName)
	{
	iName = (TDesC*)aName;
	iChunk.SetHandle(KNullHandle);
	}


/**
Sets the chunk heap to be created to use the chunk specified.

This overriddes any previous call to TChunkHeapCreateInfo::SetNewChunkHeap() or
TChunkHeapCreateInfo::SetExistingChunkHeap() for this TChunkHeapCreateInfo object.

@param aChunk	A handle to the chunk to use for the heap.
*/
EXPORT_C void TChunkHeapCreateInfo::SetUseChunk(const RChunk aChunk)
	{
	iName = NULL;
	iChunk = aChunk;
	}


/**
Creates a chunk heap of the type specified by the parameter aCreateInfo.

@param aCreateInfo	A reference to a TChunkHeapCreateInfo object specifying the
					type of chunk heap to create.

@return A pointer to the new heap or NULL if the heap could not be created.

@panic USER 41 if the heap's specified minimum length is greater than the specified maximum length.
@panic USER 55 if the heap's specified minimum length is negative.
@panic USER 172 if the heap's specified alignment is not a power of 2 or is less than the size of a TAny*.
*/
EXPORT_C RHeap* UserHeap::ChunkHeap(const TChunkHeapCreateInfo& aCreateInfo)
	{
	// aCreateInfo must have been configured to use a new chunk or an exiting chunk.
	__ASSERT_ALWAYS(!(aCreateInfo.iMode & (TUint32)~EChunkHeapMask), ::Panic(EHeapCreateInvalidMode));
	RHeap* h = NULL;

	if (aCreateInfo.iChunk.Handle() == KNullHandle)
		{// A new chunk is to be created for this heap.
		__ASSERT_ALWAYS(aCreateInfo.iMinLength >= 0, ::Panic(ETHeapMinLengthNegative));
		__ASSERT_ALWAYS(aCreateInfo.iMaxLength >= aCreateInfo.iMinLength, ::Panic(ETHeapCreateMaxLessThanMin));

		TInt maxLength = aCreateInfo.iMaxLength;
		if (maxLength < KMinHeapSize)
			maxLength = KMinHeapSize;

		TChunkCreateInfo chunkInfo;
		chunkInfo.SetNormal(0, maxLength);
		chunkInfo.SetOwner((aCreateInfo.iSingleThread)? EOwnerThread : EOwnerProcess);
		if (aCreateInfo.iName)
			chunkInfo.SetGlobal(*aCreateInfo.iName);
		// Set the paging attributes of the chunk.
		if (aCreateInfo.iPaging == TChunkHeapCreateInfo::EPaged)
			chunkInfo.SetPaging(TChunkCreateInfo::EPaged);
		if (aCreateInfo.iPaging == TChunkHeapCreateInfo::EUnpaged)
			chunkInfo.SetPaging(TChunkCreateInfo::EUnpaged);
		// Create the chunk.
		RChunk chunk;
		if (chunk.Create(chunkInfo) != KErrNone)
			return NULL;
		// Create the heap using the new chunk.
		TUint mode = aCreateInfo.iMode | EChunkHeapDuplicate;	// Must duplicate the handle.
		h = OffsetChunkHeap(chunk, aCreateInfo.iMinLength, aCreateInfo.iOffset,
							aCreateInfo.iGrowBy, maxLength, aCreateInfo.iAlign,
							aCreateInfo.iSingleThread, mode);
		chunk.Close();
		}
	else
		{
		h = OffsetChunkHeap(aCreateInfo.iChunk, aCreateInfo.iMinLength, aCreateInfo.iOffset,
							aCreateInfo.iGrowBy, aCreateInfo.iMaxLength, aCreateInfo.iAlign,
							aCreateInfo.iSingleThread, aCreateInfo.iMode);
		}
	return h;
	}


EXPORT_C RHeap* UserHeap::ChunkHeap(const TDesC* aName, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread)
/**
Creates a heap in a local or global chunk.

The chunk hosting the heap can be local or global.

A local chunk is one which is private to the process creating it and is not
intended for access by other user processes.
A global chunk is one which is visible to all processes.

The hosting chunk is local, if the pointer aName is NULL, otherwise
the hosting chunk is global and the descriptor *aName is assumed to contain
the name to be assigned to it.

Ownership of the host chunk is vested in the current process.

A minimum and a maximum size for the heap can be specified. On successful
return from this function, the size of the heap is at least aMinLength.
If subsequent requests for allocation of memory from the heap cannot be
satisfied by compressing the heap, the size of the heap is extended in
increments of aGrowBy until the request can be satisfied. Attempts to extend
the heap causes the size of the host chunk to be adjusted.

Note that the size of the heap cannot be adjusted by more than aMaxLength.

@param aName         If NULL, the function constructs a local chunk to host
                     the heap.
                     If not NULL, a pointer to a descriptor containing the name
                     to be assigned to the global chunk hosting the heap.
@param aMinLength    The minimum length of the heap.
@param aMaxLength    The maximum length to which the heap can grow.
                     If the supplied value is less than KMinHeapSize, then it
                     is discarded and the value KMinHeapSize used instead.
@param aGrowBy       The increments to the size of the host chunk. If a value is
                     not explicitly specified, the value KMinHeapGrowBy is taken
                     by default
@param aAlign        The alignment of heap cells.
@param aSingleThread Indicates whether single threaded or not.

@return A pointer to the new heap or NULL if the heap could not be created.

@panic USER 41 if aMinLength is greater than the supplied value of aMaxLength.
@panic USER 55 if aMinLength is negative.
@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
*/
//
// Allocate a Chunk of the requested size and force construction.
//
	{
	TChunkHeapCreateInfo createInfo(aMinLength, aMaxLength);
	createInfo.SetCreateChunk(aName);
	createInfo.SetGrowBy(aGrowBy);
	createInfo.SetAlignment(aAlign);
	createInfo.SetSingleThread(aSingleThread);
	return ChunkHeap(createInfo);
	}




EXPORT_C RHeap* UserHeap::ChunkHeap(RChunk aChunk, TInt aMinLength, TInt aGrowBy, TInt aMaxLength, TInt aAlign, TBool aSingleThread, TUint32 aMode)
/**
Creates a heap in an existing chunk.

This function is intended to be used to create a heap in a user writable code
chunk as created by a call to RChunk::CreateLocalCode().
This type of heap can be used to hold code fragments from a JIT compiler.

The maximum length to which the heap can grow is the same as
the maximum size of the chunk.

@param aChunk        The chunk that will host the heap.
@param aMinLength    The minimum length of the heap.
@param aGrowBy       The increments to the size of the host chunk. 
@param aMaxLength    The maximum length to which the heap can grow.
@param aAlign        The alignment of heap cells.
@param aSingleThread Indicates whether single threaded or not.
@param aMode         Flags controlling the heap creation.  This should be set 
					 from one or more of the values in TChunkHeapCreateMode.
                     
@return A pointer to the new heap or NULL if the heap could not be created.

@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
*/
//
// Construct a heap in an already existing chunk
//
	{
	
	return OffsetChunkHeap(aChunk, aMinLength, 0, aGrowBy, aMaxLength, aAlign, aSingleThread, aMode);
	}




EXPORT_C RHeap* UserHeap::OffsetChunkHeap(RChunk aChunk, TInt aMinLength, TInt aOffset, TInt aGrowBy, TInt aMaxLength, TInt aAlign, TBool aSingleThread, TUint32 aMode)
/**
Creates a heap in an existing chunk, offset from the beginning of the chunk.

This function is intended to be used to create a heap where a fixed amount of
additional data must be stored at a known location. The additional data can be
placed at the base address of the chunk, allowing it to be located without
depending on the internals of the heap structure.

The maximum length to which the heap can grow is the maximum size of the chunk,
minus the offset.

@param aChunk        The chunk that will host the heap.
@param aMinLength    The minimum length of the heap.
@param aOffset       The offset from the start of the chunk, to the start of the heap.
@param aGrowBy       The increments to the size of the host chunk. 
@param aMaxLength    The maximum length to which the heap can grow.
@param aAlign        The alignment of heap cells.
@param aSingleThread Indicates whether single threaded or not.
@param aMode         Flags controlling the heap creation.  This should be set 
					 from one or more of the values in TChunkHeapCreateMode.
                     
@return A pointer to the new heap or NULL if the heap could not be created.

@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
*/
//
// Construct a heap in an already existing chunk
//
	{

	TInt page_size;
	UserHal::PageSizeInBytes(page_size);
	if (!aAlign)
		aAlign = RHeap::ECellAlignment;
	TInt maxLength = aChunk.MaxSize();
	TInt round_up = Max(aAlign, page_size);
	TInt min_cell = _ALIGN_UP(Max((TInt)RHeap::EAllocCellSize, (TInt)RHeap::EFreeCellSize), aAlign);
	aOffset = _ALIGN_UP(aOffset, 8);
	if (aMaxLength && aMaxLength+aOffset<maxLength)
		maxLength = _ALIGN_UP(aMaxLength+aOffset, round_up);
	__ASSERT_ALWAYS(aMinLength>=0, ::Panic(ETHeapMinLengthNegative));
	__ASSERT_ALWAYS(maxLength>=aMinLength, ::Panic(ETHeapCreateMaxLessThanMin));
	aMinLength = _ALIGN_UP(Max(aMinLength, (TInt)sizeof(RHeap) + min_cell) + aOffset, round_up);
	TInt r=aChunk.Adjust(aMinLength);
	if (r!=KErrNone)
		return NULL;

	RHeap* h = new (aChunk.Base() + aOffset) RHeap(aChunk.Handle(), aOffset, aMinLength, maxLength, aGrowBy, aAlign, aSingleThread);

	TBool duplicateLock = EFalse;
	if (!aSingleThread)
		{
		duplicateLock = aMode & EChunkHeapSwitchTo;
		if(h->iLock.CreateLocal(duplicateLock ? EOwnerThread : EOwnerProcess)!=KErrNone)
			{
			h->iChunkHandle = 0;
			return NULL;
			}
		}

	if (aMode & EChunkHeapSwitchTo)
		User::SwitchHeap(h);

	h->iHandles = &h->iChunkHandle;
	if (!aSingleThread)
		{
		// now change the thread-relative chunk/semaphore handles into process-relative handles
		h->iHandleCount = 2;
		if(duplicateLock)
			{
			RHandleBase s = h->iLock;
			r = h->iLock.Duplicate(RThread());
			s.Close();
			}
		if (r==KErrNone && (aMode & EChunkHeapDuplicate))
			{
			r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread());
			if (r!=KErrNone)
				h->iLock.Close(), h->iChunkHandle=0;
			}
		}
	else
		{
		h->iHandleCount = 1;
		if (aMode & EChunkHeapDuplicate)
			r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread(), EOwnerThread);
		}

	// return the heap address
	return (r==KErrNone) ? h : NULL;
	}



#define UserTestDebugMaskBit(bit) (TBool)(UserSvr::DebugMask(bit>>5) & (1<<(bit&31)))

_LIT(KLitDollarHeap,"$HEAP");
EXPORT_C TInt UserHeap::CreateThreadHeap(SStdEpocThreadCreateInfo& aInfo, RHeap*& aHeap, TInt aAlign, TBool aSingleThread)
/**
@internalComponent
*/
//
// Create a user-side heap
//
	{
	TInt page_size;
	UserHal::PageSizeInBytes(page_size);
	TInt minLength = _ALIGN_UP(aInfo.iHeapInitialSize, page_size);
	TInt maxLength = Max(aInfo.iHeapMaxSize, minLength);
	if (UserTestDebugMaskBit(96)) // 96 == KUSERHEAPTRACE in nk_trace.h
		aInfo.iFlags |= ETraceHeapAllocs;

	// Create the thread's heap chunk.
	RChunk c;
	TChunkCreateInfo createInfo;
	createInfo.SetThreadHeap(0, maxLength, KLitDollarHeap());	// Initialise with no memory committed.

	// Set the paging policy of the heap chunk based on the thread's paging policy.
	TUint pagingflags = aInfo.iFlags & EThreadCreateFlagPagingMask;
	switch (pagingflags)
		{
		case EThreadCreateFlagPaged:
			createInfo.SetPaging(TChunkCreateInfo::EPaged);
			break;
		case EThreadCreateFlagUnpaged:
			createInfo.SetPaging(TChunkCreateInfo::EUnpaged);
			break;
		case EThreadCreateFlagPagingUnspec:
			// Leave the chunk paging policy unspecified so the process's 
			// paging policy is used.
			break;
		}

	TInt r = c.Create(createInfo);
	if (r!=KErrNone)
		return r;

	aHeap = ChunkHeap(c, minLength, page_size, maxLength, aAlign, aSingleThread, EChunkHeapSwitchTo|EChunkHeapDuplicate);
	c.Close();
	if (!aHeap)
		return KErrNoMemory;
	if (aInfo.iFlags & ETraceHeapAllocs)
		{
		aHeap->iFlags |= RHeap::ETraceAllocs;
		BTraceContext8(BTrace::EHeap, BTrace::EHeapCreate,(TUint32)aHeap, RHeap::EAllocCellSize);
		TInt handle = aHeap->ChunkHandle();
		TInt chunkId = ((RHandleBase&)handle).BTraceId();
		BTraceContext8(BTrace::EHeap, BTrace::EHeapChunkCreate, (TUint32)aHeap, chunkId);
		}
	if (aInfo.iFlags & EMonitorHeapMemory)
		aHeap->iFlags |= RHeap::EMonitorMemory;
	return KErrNone;
	}

#endif	// __KERNEL_MODE__

void RHeap::WalkCheckCell(TAny* aPtr, TCellType aType, TAny* aCell, TInt aLen)
	{
	(void)aCell;
	SHeapCellInfo& info = *(SHeapCellInfo*)aPtr;
	switch(aType)
		{
		case EGoodAllocatedCell:
			{
			++info.iTotalAlloc;
			info.iTotalAllocSize += (aLen-EAllocCellSize);
#if defined(_DEBUG)
			RHeap& h = *info.iHeap;
			if ( ((SDebugCell*)aCell)->nestingLevel == h.iNestingLevel )
				{
				if (++info.iLevelAlloc==1)
					info.iStranded = (SDebugCell*)aCell;
#ifdef __KERNEL_MODE__
				if (KDebugNum(KSERVER) || KDebugNum(KTESTFAST))
					{
//				__KTRACE_OPT(KSERVER,Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen));
					Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen);
					TLinAddr base = ((TLinAddr)aCell)&~0x0f;
					TLinAddr end = ((TLinAddr)aCell)+(TLinAddr)aLen;
					while(base<end)
						{
						const TUint32* p = (const TUint32*)base;
						Kern::Printf("%08x: %08x %08x %08x %08x", p, p[0], p[1], p[2], p[3]);
						base += 16;
						}
					}
#endif
				}
#endif	
			break;
			}
		case EGoodFreeCell:
			++info.iTotalFree;
			break;
		case EBadAllocatedCellSize:
			HEAP_PANIC(ETHeapBadAllocatedCellSize);
		case EBadAllocatedCellAddress:
			HEAP_PANIC(ETHeapBadAllocatedCellAddress);
		case EBadFreeCellAddress:
			HEAP_PANIC(ETHeapBadFreeCellAddress);
		case EBadFreeCellSize:
			HEAP_PANIC(ETHeapBadFreeCellSize);
		default:
			HEAP_PANIC(ETHeapWalkBadCellType);
		}
	}

TInt RHeap::DoCountAllocFree(TInt& aFree)
	{
	SHeapCellInfo info;
	memclr(&info, sizeof(info));
	info.iHeap = this;
	Walk(&WalkCheckCell, &info);
	aFree = info.iTotalFree;
	return info.iTotalAlloc;
	}


UEXPORT_C TInt RHeap::DebugFunction(TInt aFunc, TAny* a1, TAny* a2)
/**
@internalComponent
*/
	{
	TInt r = KErrNone;
	switch(aFunc)
		{
		case RAllocator::ECount:
			r = DoCountAllocFree(*(TInt*)a1);
			break;
		case RAllocator::EMarkStart:
			__DEBUG_ONLY(DoMarkStart());
			break;
		case RAllocator::EMarkEnd:
			__DEBUG_ONLY( r = DoMarkEnd((TInt)a1) );
			break;
		case RAllocator::ECheck:
			r = DoCheckHeap((SCheckInfo*)a1);
			break;
		case RAllocator::ESetFail:
			__DEBUG_ONLY(DoSetAllocFail((TAllocFail)(TInt)a1, (TInt)a2));
			break;
		case RAllocator::ESetBurstFail:
#if _DEBUG
			{
			SRAllocatorBurstFail* fail = (SRAllocatorBurstFail*) a2;
			DoSetAllocFail((TAllocFail)(TInt)a1, fail->iRate, fail->iBurst);
			}
#endif
			break;

		case RAllocator::ECheckFailure:
				// iRand will be incremented for each EFailNext, EBurstFailNext,
				// EDeterministic and EBurstDeterministic failure.
				r = iRand;
				break;

		case RAllocator::ECopyDebugInfo:
			{
			TInt nestingLevel = ((SDebugCell*)a1)[-1].nestingLevel;
			((SDebugCell*)a2)[-1].nestingLevel = nestingLevel;
			break;
			}
		case RHeap::EWalk:
			Walk((TWalkFunc)a1, a2);
			break;
		default:
			return KErrNotSupported;
		}
	return r;
	}




void RHeap::Walk(TWalkFunc aFunc, TAny* aPtr)
//
// Walk the heap calling the info function.
//
	{

	Lock();
	SCell* pC = (SCell*)iBase;		// allocated cells
	SCell* pF = &iFree;				// free cells
	FOREVER
		{
		pF = pF->next;				// next free cell
		if (!pF)
			pF = (SCell*)iTop;		// to make size checking work
		else if ( (TUint8*)pF>=iTop || (pF->next && pF->next<=pF) )
			{
			if (iFlags & ETraceAllocs)
				BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pF+EFreeCellSize, 0);
			// free cell pointer off the end or going backwards
			Unlock();
			(*aFunc)(aPtr, EBadFreeCellAddress, pF, 0);
			return;
			}
		else
			{
			TInt l = pF->len;
			if (l<iMinCell || (l & (iAlign-1)))
				{
				if (iFlags & ETraceAllocs)
					BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pF+EFreeCellSize, l-EFreeCellSize);
				// free cell length invalid
				Unlock();
				(*aFunc)(aPtr, EBadFreeCellSize, pF, l);
				return;
				}
			}
		while (pC!=pF)				// walk allocated cells up to next free cell
			{
			TInt l = pC->len;
			if (l<iMinCell || (l & (iAlign-1)))
				{
				if (iFlags & ETraceAllocs)
					BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pC+EAllocCellSize, l-EAllocCellSize);
				// allocated cell length invalid
				Unlock();
				(*aFunc)(aPtr, EBadAllocatedCellSize, pC, l);
				return;
				}
			(*aFunc)(aPtr, EGoodAllocatedCell, pC, l);
			SCell* pN = __NEXT_CELL(pC);
			if (pN > pF)
				{
				if (iFlags & ETraceAllocs)
					BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pC+EAllocCellSize, l-EAllocCellSize);			
				// cell overlaps next free cell
				Unlock();
				(*aFunc)(aPtr, EBadAllocatedCellAddress, pC, l);
				return;
				}
			pC = pN;
			}
		if ((TUint8*)pF == iTop)
			break;		// reached end of heap
		pC = __NEXT_CELL(pF);	// step to next allocated cell
		(*aFunc)(aPtr, EGoodFreeCell, pF, pF->len);
		}
	Unlock();
	}

TInt RHeap::DoCheckHeap(SCheckInfo* aInfo)
	{
	(void)aInfo;
	SHeapCellInfo info;
	memclr(&info, sizeof(info));
	info.iHeap = this;
	Walk(&WalkCheckCell, &info);
#if defined(_DEBUG)
	if (!aInfo)
		return KErrNone;
	TInt expected = aInfo->iCount;
	TInt actual = aInfo->iAll ? info.iTotalAlloc : info.iLevelAlloc;
	if (actual!=expected && !iTestData)
		{
#ifdef __KERNEL_MODE__
		Kern::Fault("KERN-ALLOC COUNT", (expected<<16)|actual );
#else
		User::Panic(_L("ALLOC COUNT"), (expected<<16)|actual );
#endif
		}
#endif
	return KErrNone;
	}

#ifdef _DEBUG
void RHeap::DoMarkStart()
	{
	if (iNestingLevel==0)
		iAllocCount=0;
	iNestingLevel++;
	}

TUint32 RHeap::DoMarkEnd(TInt aExpected)
	{
	if (iNestingLevel==0)
		return 0;
	SHeapCellInfo info;
	SHeapCellInfo* p = iTestData ? (SHeapCellInfo*)iTestData : &info;
	memclr(p, sizeof(info));
	p->iHeap = this;
	Walk(&WalkCheckCell, p);
	if (p->iLevelAlloc != aExpected && !iTestData)
		return (TUint32)(p->iStranded + 1);
	if (--iNestingLevel == 0)
		iAllocCount = 0;
	return 0;
	}

void ResetAllocCellLevels(TAny* aPtr, RHeap::TCellType aType, TAny* aCell, TInt aLen)
	{
	(void)aPtr;
	(void)aLen;
	RHeap::SDebugCell* cell = (RHeap::SDebugCell*)aCell;
	if (aType == RHeap::EGoodAllocatedCell)
		{
		cell->nestingLevel = 0;
		}
	}

void RHeap::DoSetAllocFail(TAllocFail aType, TInt aRate)
	{// Default to a burst mode of 1, as aType may be a burst type.
	DoSetAllocFail(aType, aRate, 1);
	}

// Don't change as the ETHeapBadDebugFailParameter check below and the API 
// documentation rely on this being 16 for RHeap.
LOCAL_D const TInt KBurstFailRateShift = 16;
LOCAL_D const TInt KBurstFailRateMask = (1 << KBurstFailRateShift) - 1;

void RHeap::DoSetAllocFail(TAllocFail aType, TInt aRate, TUint aBurst)
	{
	if (aType==EReset)
		{
		// reset levels of all allocated cells to 0
		// this should prevent subsequent tests failing unnecessarily
		iFailed = EFalse;		// Reset for ECheckFailure relies on this.
		Walk(&ResetAllocCellLevels, NULL);
		// reset heap allocation mark as well
		iNestingLevel=0;
		iAllocCount=0;
		aType=ENone;
		}

	switch (aType)
		{
		case EBurstRandom:
		case EBurstTrueRandom:
		case EBurstDeterministic:
		case EBurstFailNext:
			// If the fail type is a burst type then iFailRate is split in 2:
			// the 16 lsbs are the fail rate and the 16 msbs are the burst length.
			if (TUint(aRate) > (TUint)KMaxTUint16 || aBurst > KMaxTUint16)
				HEAP_PANIC(ETHeapBadDebugFailParameter);

			iFailed = EFalse;
			iFailType = aType;
			iFailRate = (aRate == 0) ? 1 : aRate;
			iFailAllocCount = -iFailRate;
			iFailRate = iFailRate | (aBurst << KBurstFailRateShift);
			break;

		default:
			iFailed = EFalse;
			iFailType = aType;
			iFailRate = (aRate == 0) ? 1 : aRate; // A rate of <1 is meaningless
			iFailAllocCount = 0;
			break;
		}

	// Set up iRand for either:
	//		- random seed value, or
	//		- a count of the number of failures so far.
	iRand = 0;
#ifndef __KERNEL_MODE__
	switch (iFailType)
		{
		case ETrueRandom:
		case EBurstTrueRandom:
			{
			TTime time;
			time.HomeTime();
			TInt64 seed = time.Int64();
			iRand = Math::Rand(seed);
			break;
			}
		case ERandom:
		case EBurstRandom:
	        {
	        TInt64 seed = 12345;
			iRand = Math::Rand(seed);
			break;
	        }
		default:
			break;
		}
#endif
	}

TBool RHeap::CheckForSimulatedAllocFail()
//
// Check to see if the user has requested simulated alloc failure, and if so possibly 
// Return ETrue indicating a failure.
//
	{
	// For burst mode failures iFailRate is shared
	TUint16 rate  = (TUint16)(iFailRate &  KBurstFailRateMask);
	TUint16 burst = (TUint16)(iFailRate >> KBurstFailRateShift);
	TBool r = EFalse;
	switch (iFailType)
		{
#ifndef __KERNEL_MODE__
		case ERandom:
		case ETrueRandom:
			if (++iFailAllocCount>=iFailRate) 
				{	
				iFailAllocCount=0;
				if (!iFailed) // haven't failed yet after iFailRate allocations so fail now
					return(ETrue); 
				iFailed=EFalse;
				}
			else   
				{
				if (!iFailed)
					{
	                TInt64 seed=iRand;
					iRand=Math::Rand(seed);
					if (iRand%iFailRate==0)
						{
						iFailed=ETrue;
						return(ETrue);
						}
					}
				}
			break;

		case EBurstRandom:
		case EBurstTrueRandom:
			if (++iFailAllocCount < 0) 
				{
				// We haven't started failing yet so should we now?
				TInt64 seed = iRand;
				iRand = Math::Rand(seed);
				if (iRand % rate == 0)
					{// Fail now.  Reset iFailAllocCount so we fail burst times
					iFailAllocCount = 0;
					r = ETrue;
					}
				}
			else
				{
				if (iFailAllocCount < burst)
					{// Keep failing for burst times
					r = ETrue;
					}
				else
					{// We've now failed burst times so start again.
					iFailAllocCount = -(rate - 1);
					}
				}
			break;
#endif
		case EDeterministic:
			if (++iFailAllocCount%iFailRate==0)
				{
				r=ETrue;
				iRand++;	// Keep count of how many times we have failed
				}
			break;

		case EBurstDeterministic:
			// This will fail burst number of times, every rate attempts.
			if (++iFailAllocCount >= 0)
				{
				if (iFailAllocCount == burst - 1)
					{// This is the burst time we have failed so make it the last by
					// reseting counts so we next fail after rate attempts.
					iFailAllocCount = -rate;
					}
				r = ETrue;
				iRand++;	// Keep count of how many times we have failed
				}
			break;

		case EFailNext:
			if ((++iFailAllocCount%iFailRate)==0)
				{
				iFailType=ENone;
				r=ETrue;
				iRand++;	// Keep count of how many times we have failed
				}
			break;

		case EBurstFailNext:
			if (++iFailAllocCount >= 0)
				{
				if (iFailAllocCount == burst - 1)
					{// This is the burst time we have failed so make it the last.
					iFailType = ENone;
					}
				r = ETrue;
				iRand++;	// Keep count of how many times we have failed
				}
			break;
		default:
			break;
		}
	return r;
	}
#endif	// ifdef _DEBUG

UEXPORT_C TInt RHeap::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1)
	{
	return RAllocator::Extension_(aExtensionId, a0, a1);
	}

#if defined(__HEAP_MACHINE_CODED__) && !defined(_DEBUG)
GLDEF_C void RHeap_PanicBadAllocatedCellSize()
	{
	HEAP_PANIC(ETHeapBadAllocatedCellSize);
	}

GLDEF_C void RHeap_PanicBadNextCell()
	{
	HEAP_PANIC(ETHeapFreeBadNextCell);
	}

GLDEF_C void RHeap_PanicBadPrevCell()
	{
	HEAP_PANIC(ETHeapFreeBadPrevCell);
	}

GLDEF_C void RHeap_PanicBadCellAddress()
	{
	HEAP_PANIC(ETHeapBadCellAddress);
	}
#endif