// UB_CLN.CPP
//
// Copyright (c) 1995-1999 Symbian Ltd.  All rights reserved.
//

#include "ub_std.h"

const TInt KCleanupGranularity=4;
const TInt KCleanupInitialSlots=8;

LOCAL_C void doDelete(CBase *aPtr)
//
// Delete the CBase pointer
//
	{

	delete aPtr;
	}

LOCAL_C CCleanup &cleanup()
//
// Return the CTrapHandler's cleanup list.
//
	{

	TCleanupTrapHandler *pH=(TCleanupTrapHandler *)User::TrapHandler();
	__ASSERT_ALWAYS(pH!=NULL,Panic(EClnNoTrapHandlerInstalled));
	return(pH->Cleanup());
	}

TCleanupTrapHandler::TCleanupTrapHandler()
//
// Constructor
//
	: iCleanup(NULL)
	{}

void TCleanupTrapHandler::Trap()
//
// Handle a trap call.
//
	{

	iCleanup->NextLevel();
	}

void TCleanupTrapHandler::UnTrap()
//
// Handle an untrap exit.
//
	{

	iCleanup->PreviousLevel();
	}

void TCleanupTrapHandler::Leave(TInt /*aValue*/)
//
// Handle a leave exit.
//
	{

	iCleanup->PopAndDestroyAll();
	}

class TCleanupStackItem
	{
public:
	void Set(const TCleanupItem &aItem);
	inline void Cleanup();
	inline TBool IsLevelMarker() const;
	inline void MarkLevel();
	inline void PushLevel();
	inline TInt PopLevel();
private:
	TCleanupOperation iOperation;
	union
		{
		TAny *iPtr;
		TInt iLevelCount;			// may stack >1 level on this entry
		};
	};
inline void TCleanupStackItem::MarkLevel()
	{iOperation=NULL;iLevelCount=1;}
inline TBool TCleanupStackItem::IsLevelMarker() const
	{return(iOperation==NULL);}
inline void TCleanupStackItem::Cleanup()
	{(*iOperation)(iPtr);}
inline void TCleanupStackItem::PushLevel()
	{++iLevelCount;}
inline TInt TCleanupStackItem::PopLevel()
	{return(--iLevelCount);}

void TCleanupStackItem::Set(const TCleanupItem &anItem)
//
// Initialise an entry as a cleanup item.
//
	{

	__ASSERT_ALWAYS(anItem.iOperation!=NULL,Panic(EClnNoCleanupOperation));
	iOperation=anItem.iOperation;
	iPtr=anItem.iPtr;
	}

EXPORT_C CCleanup *CCleanup::New()
//
// Create a new cleanup object.
//
	{

	CCleanup *pC=new CCleanup;
	if (pC!=NULL)
		{
		TCleanupStackItem *base=(TCleanupStackItem *)User::Alloc(KCleanupInitialSlots*sizeof(TCleanupStackItem));
		if (base!=NULL)
			{
			pC->iBase=base;
			pC->iNext=base;
			pC->iTop=base+KCleanupInitialSlots;
			}
		else
			{
			delete pC;
			pC=NULL;
			}
		}
	return(pC);
	}

EXPORT_C CCleanup *CCleanup::NewL()
//
// Create a new cleanup object. Leave on any Error.
//
	{

	CCleanup *pC=New();
	User::LeaveIfNull(pC);
	return(pC);
	}

EXPORT_C CCleanup::CCleanup()
//
// Constructor.
//
	{

//	iBase=NULL;
//	iTop=NULL;
//	iNext=NULL;
	__DECLARE_NAME(_S("CCleanup"));
	}

EXPORT_C CCleanup::~CCleanup()
//
// Destructor.
//
	{

	while (iNext>iBase)
		PopAndDestroyAll();
	User::Free(iBase);
	}

EXPORT_C void CCleanup::NextLevel()
//
// Go to the next cleanup level.
//
	{

	if (iNext>iBase && (iNext-1)->IsLevelMarker())
		(iNext-1)->PushLevel();
	else
		{
		iNext->MarkLevel();
		++iNext;
		}
	}

EXPORT_C void CCleanup::PreviousLevel()
//
// Go to the previous cleanup level
//
	{

	TCleanupStackItem *item=iNext;
	--item;
	// current level must be empty
	__ASSERT_ALWAYS(item->IsLevelMarker(), Panic(EClnLevelNotEmpty));
	if (item->PopLevel())
		++item;
	iNext=item;
	}

EXPORT_C void CCleanup::PushL(TAny *aPtr)
//
// Push an heap cell onto the cleanup list.
//
	{

	PushL(TCleanupItem(User::Free,aPtr));
	}

EXPORT_C void CCleanup::PushL(CBase *anObject)
//
// Push a CBase derived object onto the cleanup list.
//
	{

	PushL(TCleanupItem(TCleanupOperation(doDelete),anObject));
	}

EXPORT_C void CCleanup::PushL(TCleanupItem anItem)
//
// Push a cleanup callback item on the cleanup list.
//
	{

	TCleanupStackItem *item=iNext;
	__ASSERT_ALWAYS(item>iBase,Panic(EClnPushAtLevelZero));
	__ASSERT_ALWAYS(item<iTop,Panic(EClnNoFreeSlotItem));
	item->Set(anItem);
	iNext=++item;
//
// We always try and make sure that there are two free slots in the cleanup array.
// one for a level marker and one for an item to follow it
// If this fails its o.k. as we have already added the entry to the list, so
// it will be cleaned up o.k.
//
	if (item+1>=iTop)
		{
		TInt size=(TUint8 *)(iTop+KCleanupGranularity)-(TUint8 *)iBase;
		TCleanupStackItem *base=(TCleanupStackItem *)User::ReAllocL(iBase,size);
		iNext=PtrAdd(base,(TUint8 *)item-(TUint8 *)iBase);
		iBase=base;
		iTop=PtrAdd(base,size);
		}
	}

EXPORT_C void CCleanup::DoPop(TInt aCount,TBool aDestroy)
//
// Pop aCount objects from the list. Cannot cross levels.
//
	{

	__ASSERT_ALWAYS(aCount>=0,Panic(EClnPopCountNegative));
	TCleanupStackItem *last=iNext-aCount;
	__ASSERT_ALWAYS(last>=iBase,Panic(EClnPopUnderflow));
	while (iNext>last)
		{
		--iNext;
		__ASSERT_ALWAYS(!iNext->IsLevelMarker(),Panic(EClnPopAcrossLevels));
		if (aDestroy)
			iNext->Cleanup();
		}
	}

EXPORT_C void CCleanup::DoPopAll(TBool aDestroy)
//
// Pop all the objects at this level.
// Also decrements the level.
//
	{

	__ASSERT_ALWAYS(iNext>iBase,Panic(EClnLevelUnderflow));
	while (!(--iNext)->IsLevelMarker())
		{
		if (aDestroy)
			iNext->Cleanup();
		}
	if (iNext->PopLevel())
		++iNext;				// still marks a level
	}

EXPORT_C void CCleanup::Pop()
//
// Pop the next object from the list. Cannot cross levels.
//
	{

	DoPop(1,EFalse);
	}

EXPORT_C void CCleanup::Pop(TInt aCount)
//
// Pop aCount objects from the list. Cannot cross levels.
//
	{

	DoPop(aCount,EFalse);
	}

EXPORT_C void CCleanup::PopAll()
//
// Pop all the objects at this level.
// Also decrements the level.
//
	{

	DoPopAll(EFalse);
	}

EXPORT_C void CCleanup::PopAndDestroy()
//
// Pop and destroy the next object from the list. Cannot cross levels.
//
	{

	DoPop(1,ETrue);
	}

EXPORT_C void CCleanup::PopAndDestroy(TInt aCount)
//
// Pop aCount objects and destroy them from the list. Cannot cross levels.
//
	{

	DoPop(aCount,ETrue);
	}

EXPORT_C void CCleanup::PopAndDestroyAll()
//
// Pop all the objects at this level and destroy them.
// Also decrements the level.
//
	{

	DoPopAll(ETrue);
	}

EXPORT_C CTrapCleanup *CTrapCleanup::New()
//
// Install the TRAP cleanup handler.
//
	{

	CTrapCleanup *pT=new CTrapCleanup;
	if (pT!=NULL)
		{
		CCleanup *pC=CCleanup::New();
		if (pC!=NULL)
			{
			pT->iHandler.iCleanup=pC;
			pT->iOldHandler=User::SetTrapHandler(&pT->iHandler);
			}
		else
			{
			delete pT;
			pT=NULL;
			}
		}
	return(pT);
	}

EXPORT_C CTrapCleanup::CTrapCleanup()
//
// Constructor.
//
//	: iHandler()
	{

	__DECLARE_NAME(_S("CTrapCleanup"));
	}

EXPORT_C CTrapCleanup::~CTrapCleanup()
//
// Destructor.
//
	{

	if (iHandler.iCleanup!=NULL)
		{
		User::SetTrapHandler(iOldHandler);
		delete iHandler.iCleanup;
		}
	}

EXPORT_C void CleanupStack::PushL(TAny *aPtr)
//
// Push an heap cell onto the cleanup list.
//
	{

	cleanup().PushL(aPtr);
	}

EXPORT_C void CleanupStack::PushL(CBase *aPtr)
//
// Push a CBase derived object onto the cleanup list.
//
	{

	cleanup().PushL(aPtr);
	}

EXPORT_C void CleanupStack::PushL(TCleanupItem anItem)
//
// Push a TCleanupItem onto the cleanup list
//		
	{

	cleanup().PushL(anItem);
	}

EXPORT_C void CleanupStack::Pop()
//
// Pop the top item from the cleanup list.
//
	{

	cleanup().Pop();
	}

EXPORT_C void CleanupStack::Pop(TInt aCount)
//
// Pop the aCount items from the cleanup list.
//
	{

	cleanup().Pop(aCount);
	}

EXPORT_C void CleanupStack::PopAndDestroy()
//
// Pop the top item from the cleanup list and destroy it.
//
	{

	cleanup().PopAndDestroy();
	}

EXPORT_C void CleanupStack::PopAndDestroy(TInt aCount)
//
// Pop the aCount items from the cleanup list and destroy them.
//
	{

	cleanup().PopAndDestroy(aCount);
	}

