// UB_REG.CPP
//
// Copyright (c) 1996-1999 Symbian Ltd.  All rights reserved.
//

#include "ub_std.h"

const TInt KRegistryGranularity=0x1000;
//
class TCatInfo
	{
public:
	TUid iCategory;
	TInt iOffset;
	TInt iSize;
	TInt iKey;
	};
//
class TRegistry
	{
public:
	TInt iCount;
	TInt iSize;
	TInt iMaxSize;
	TInt iKey;
	TCatInfo iCat[1];
	};

LOCAL_C TRegistryItemValue* value(TRegistryItemName* aPtr)
//
// Return the value associated with this name.
//
	{

	return((TRegistryItemValue*)PtrAdd(aPtr,Align4(aPtr->Size()+sizeof(TInt))));
	}

LOCAL_C TRegistryItemName* name(TRegistryItemValue* aPtr)
//
// Return the next item name after this value.
//
	{

	return((TRegistryItemName*)PtrAdd(aPtr,Align4(aPtr->Size()+sizeof(TInt))));
	}

LOCAL_C TRegistryItemName* nextName(TRegistryItemName* aPtr)
//
// Return the next item name after this name.
//
	{

	TRegistryItemValue* pV=value(aPtr);
	return(name(pV));
	}

LOCAL_C TAny* copyDescriptor(TRegistryItemName* aPtr,const TDesC8& aName)
//
// Copy the descriptor to the registry
//
	{

	TInt* pI=(TInt*)aPtr;
	*pI++=aName.Size();
	return(Align4(Mem::Copy(pI,aName.Ptr(),aName.Size())));
	}

EXPORT_C CRegistry* CRegistry::NewL()
//
// Creator
//
	{

	CRegistry* pR=new(ELeave) CRegistry;
	TRAPD(r,pR->ReAllocL(KRegistryGranularity));
	if (r!=KErrNone)
		{
		delete pR;
		User::Leave(r);
		}
	return(pR);
	}

EXPORT_C TRegistryItemValue CRegistry::ItemL(const TDesC8& aMatch,const TUid& aCategory) const
//
// Locate an item within a category.
//
	{

	TRegistryItemName* pI=((CRegistry*)this)->FindItem(aMatch,aCategory);
	if (pI==NULL)
		User::Leave(KErrNotFound);
	return(*value(pI));
	}

EXPORT_C void CRegistry::SetItemL(const TDesC8& aName,const TDesC8& aValue,const TUid& aCategory)
//
// Set an item in a category.
//
	{

	TInt r,catOff,required;
	TRegistryItemName* pN;
	TCatInfo* pC=FindCategory(aCategory);
	if (pC==NULL) // First time an item is being added
		{
		pC=AddCategoryL(aCategory);
		pN=ItemAtOffset(pC->iOffset);
addItem:
		catOff=((TUint8*)pC)-((TUint8*)iReg);
		required=Align4(aName.Size())+Align4(aValue.Size())+sizeof(TInt)*2;
		TRAP(r,pN=(TRegistryItemName*)GrowL(pC,pN,required))
		if (r!=KErrNone)
			{
			pC=PtrAdd((TCatInfo*)iReg,catOff);
			if (pC->iSize==0)
				RemoveCategory(pC);
			User::Leave(r);
			}
		pC->iSize+=required;
		pN=(TRegistryItemName*)copyDescriptor(pN,aName);
		copyDescriptor(pN,aValue);
		return;
		}
	pN=FindItem(aName,aCategory);
	if (pN==NULL) // Item not currently present
		{
		pN=ItemAtOffset(pC->iOffset+pC->iSize);
		goto addItem;
		}
	pN=(TRegistryItemName*)value(pN); // Get the item's current value
	r=Align4(aValue.Size())-Align4(pN->Size());
	if (r>0) // Getting bigger
		{
		catOff=((TUint8*)pC)-((TUint8*)iReg);
		pN=(TRegistryItemName*)GrowL(pC,pN,r);
		pC=PtrAdd((TCatInfo*)iReg,catOff);
		pC->iSize+=r;
		pC->iKey++;
		}
	else if (r<0) // Getting smaller
		{
		Shrink(pC,pN,-r);
		pC->iSize+=r;
		pC->iKey++;
		}
	copyDescriptor(pN,aValue);
	}

EXPORT_C void CRegistry::DeleteItemL(const TDesC8& aName,const TUid& aCategory)
//
// Delete an item from the registry.
//
	{

	TCatInfo* pC=FindCategory(aCategory);
	if (pC==NULL)
		User::Leave(KErrNotFound);
	TRegistryItemName* pN=FindItem(aName,aCategory);
	if (pN==NULL)
		User::Leave(KErrNotFound);
	TInt size=Align4(pN->Size())+Align4(value(pN)->Size())+sizeof(TInt)*2;
	Shrink(pC,pN,size);
	pC->iKey++;
	pC->iSize-=size;
	if (pC->iSize==0) // No more items left
		RemoveCategory(pC);
	}

EXPORT_C void CRegistry::DeleteCategory(const TUid& aCategory)
//
// Remove all items from the category
//
	{

	TCatInfo* pC=FindCategory(aCategory);
	if (pC==NULL)
		return;
	Shrink(pC,ItemAtOffset(pC->iOffset),pC->iSize);
	RemoveCategory(pC);
	}

EXPORT_C TInt CRegistry::NextCategory(TUid& aUid,TInt& anIndex,TInt& aKey) const
//
// Get the next category
//
	{

	Mem::FillZ(&aUid,sizeof(TUid));
	if (anIndex==(-1))
		{
		anIndex=0;
		aKey=iReg->iKey;
		}
	if (iReg->iKey!=aKey)
		return(KErrGeneral);
	if (anIndex>=iReg->iCount)
		return(KErrNotFound);
	aUid=iReg->iCat[anIndex++].iCategory;
	return(KErrNone);
	}

EXPORT_C TInt CRegistry::NextItem(TRegistryItemName* aName,TRegistryItemValue* aValue,const TUid& aCategory,const TDesC8& aMatch,TInt& anOffset,TInt& aKey) const
//
// Get the next item in a category
//
	{

	if (aName!=NULL)
		*aName=_L8("");
	if (aValue!=NULL)
		*aValue=_L8("");
	TCatInfo* pC=((CRegistry*)this)->FindCategory(aCategory);
	if (pC==NULL)
		return(KErrNotFound);
	if (anOffset==(-1))
		{
		anOffset=0;
		aKey=pC->iKey;
		}
	if (pC->iKey!=aKey)
		return(KErrGeneral);
	if (anOffset>=pC->iSize)
		return(KErrNotFound);
	TRegistryItemName* pN=((CRegistry*)this)->ItemAtOffset(pC->iOffset+anOffset);
	TRegistryItemName* pE=((CRegistry*)this)->ItemAtOffset(pC->iOffset+pC->iSize);
	while (pN<pE)
		{
		if (pN->Match(aMatch)!=KErrNotFound)
			{
			if (aName!=NULL)
				*aName=(*pN);
			TRegistryItemValue* pV=value(pN);
			if (aValue!=NULL)
				*aValue=(*pV);
			pN=name(pV);
			anOffset=((TUint8*)pN)-((TUint8*)((CRegistry*)this)->ItemAtOffset(pC->iOffset));
			return(KErrNone);
			}
		pN=nextName(pN);
		}
	anOffset=KMaxTInt;
	return(KErrNotFound);
	}

EXPORT_C CRegistry::CRegistry()
//
// Constructor
//
	{

//	iReg=NULL;
	__DECLARE_NAME(_S("CRegistry"));
	}

EXPORT_C CRegistry::~CRegistry()
//
// Destructor
//
	{

	delete iReg;
	}

EXPORT_C const TPtrC8 CRegistry::Contents() const
//
// Return the contents of the registry
//
    {

    if (iReg)
        return(TPtrC8((TUint8*)iReg,iReg->iSize));
    return(TPtrC8(NULL,0));
    }

EXPORT_C TInt CRegistry::SetContents(const TPtrC8& aContents)
//
// Establish the new contents of the registry
//
    {

    TInt len=aContents.Length();
	TRAPD(r,ReAllocL((len/KRegistryGranularity+1)*KRegistryGranularity));
	if (r!=KErrNone)
        return(KErrNoMemory);
	Mem::Copy(iReg,aContents.Ptr(),len);
    return(KErrNone);
    }

void CRegistry::Patch(TCatInfo* anInfo,TInt aDelta)
//
// Adjust all the offsets in the catgories from anInfo+1 by aDelta.
//
	{

	anInfo++;
	TCatInfo* pE=EndCat();
	while (anInfo<pE)
		{
		anInfo->iOffset+=aDelta;
		anInfo++;
		}
	}

void CRegistry::Shrink(TCatInfo* anInfo,TAny* aPtr,TInt aSize)
//
// Shrink by aSize bytes at aPtr. Adjust categories after anInfo.
//
	{

	TInt offset=((TUint8*)aPtr)-((TUint8*)iReg);
	iReg->iSize-=aSize;
	Mem::Copy(aPtr,PtrAdd(aPtr,aSize),iReg->iSize-offset);
	Patch(anInfo,-aSize);
	TInt rem=(iReg->iMaxSize-iReg->iSize)/KRegistryGranularity;
	if (rem!=0)
		{
		iReg->iMaxSize-=(rem*KRegistryGranularity);
		ReAllocL(iReg->iMaxSize);
		}
	}

TAny* CRegistry::GrowL(TCatInfo* anInfo,TAny* aPtr,TInt aSize)
//
// Make sure that there is room for aSize bytes.
// Also open the gap.
//
	{

	TInt infoOffset=((TUint8*)anInfo)-((TUint8*)iReg);
	TInt offset=((TUint8*)aPtr)-((TUint8*)iReg);
	if ((iReg->iSize+aSize)>iReg->iMaxSize)
		{
		TInt required=((aSize/KRegistryGranularity)+1)*KRegistryGranularity;
		ReAllocL(iReg->iMaxSize+required);
		iReg->iMaxSize+=required;
		}
	TUint8* pM=PtrAdd((TUint8*)iReg,offset);
	Mem::Copy(pM+aSize,pM,iReg->iSize-offset);
	iReg->iSize+=aSize;
	Patch(PtrAdd((TCatInfo*)iReg,infoOffset),aSize);
	return(pM);
	}

void CRegistry::ReAllocL(TInt aSize)
//
// Handle allocation of the flat buffer for the registry.
// Default implementation uses a heap cell.
//
	{

	TRegistry* pR=iReg;
	iReg=(TRegistry*)User::ReAllocL(iReg,aSize);
	if (pR==NULL) // We are initialising
		{
		iReg->iCount=0;
		iReg->iKey=0;
		iReg->iSize=sizeof(TRegistry)-sizeof(TCatInfo);
		iReg->iMaxSize=aSize;
		}
	}

TCatInfo* CRegistry::AddCategoryL(const TUid& aCategory)
//
// Add a category to the registry. Leave on error.
//
	{

	TCatInfo* pI=FindCategory(aCategory);
	if (pI==NULL)
		{
		pI=EndCat();
		pI=(TCatInfo*)GrowL(pI,pI,sizeof(TCatInfo));
		pI->iCategory=aCategory;
		pI->iOffset=iReg->iSize-(((TUint8*)(pI+1))-((TUint8*)iReg));
		pI->iSize=0;
		pI->iKey=0;
		iReg->iCount++;
		}
	return(pI);
	}

void CRegistry::RemoveCategory(TCatInfo* anInfo)
//
// Remove a category from the registry.
//
	{

	TInt offset=((TUint8*)anInfo)-((TUint8*)iReg);
	iReg->iKey++;
	iReg->iCount--;
	iReg->iSize-=sizeof(TCatInfo);
	Mem::Copy(anInfo,PtrAdd(anInfo,sizeof(TCatInfo)),iReg->iSize-offset);
	}

TCatInfo* CRegistry::FindCategory(const TUid& aCategory)
//
// Find a category in the registry. Leave on error.
//
	{

	TCatInfo* pI=(&iReg->iCat[0]);
	TCatInfo* pE=pI+iReg->iCount;
	while (pI<pE)
		{
		if (pI->iCategory==aCategory)
			return(pI);
		pI++;
		}
	return(NULL);
	}

TCatInfo* CRegistry::EndCat()
//
// Return the address just after the last TCatInfo.
//
	{

	return(&iReg->iCat[iReg->iCount]);
	}

TRegistryItemName* CRegistry::FindItem(const TDesC8& aMatch,const TUid& aCategory)
//
// Find an item in the registry.
//
	{

	TCatInfo* pC=FindCategory(aCategory);
	if (pC!=NULL)
		{
		TRegistryItemName* pN=ItemAtOffset(pC->iOffset);
		TRegistryItemName* pE=PtrAdd(pN,pC->iSize);
		while (pN<pE)
			{
			if (pN->Match(aMatch)!=KErrNotFound)
				return(pN);
			pN=nextName(pN);
			}
		}
	return(NULL);
	}

TRegistryItemName* CRegistry::ItemAtOffset(TInt anOffset)
//
// Return the first item in a category from its base.
//
	{

	return(PtrAdd((TRegistryItemName*)EndCat(),anOffset));
	}

