// UB_OBJ.CPP
//
// Copyright (c) 1994-1999 Symbian Ltd.  All rights reserved.
//

#include "ub_std.h"

const TInt KObjectIxGranularity=8;
const TInt KObjectConGranularity=8;
const TInt KObjectConIxGranularity=4;
const TInt KObjectIndexMask=0x7fff;
const TInt KObjectMaxIndex=0x7fff;
const TInt KObjectInstanceShift=16;
const TInt KObjectInstanceMask=0x3fff;
const TInt KObjectUniqueIDShift=16;
const TInt KObjectIxMaxHandles=0x8000;

inline TInt index(TInt aHandle)
	{return(aHandle&KObjectIndexMask);}
inline TInt instance(TInt aHandle)
	{return((aHandle>>KObjectInstanceShift)&KObjectInstanceMask);}
inline TInt instanceLimit(TInt& aCount)
	{return ((aCount&KObjectInstanceMask)==0) ? ((++aCount)&KObjectInstanceMask) : aCount&KObjectInstanceMask;}
inline TInt makeHandle(TInt anIndex,TInt anInstance)
	{return((TInt)((anInstance<<KObjectInstanceShift)|anIndex));}

inline TInt uniqueID(TInt aHandle)
	{return((aHandle>>KObjectUniqueIDShift)&KObjectInstanceMask);}
inline TInt makeFindHandle(TInt anIndex,TInt anUniqueID)
	{return((TInt)((anUniqueID<<KObjectUniqueIDShift)|anIndex));}

LOCAL_C void makeFullName(TFullName &aFullName,const CObject *anOwner,const TDesC &aName)
//
// Make a name from its owner name and aName.
//
	{

	aFullName.Zero();
	if (anOwner)
		{
        makeFullName(aFullName,anOwner->Owner(),anOwner->Name());
		aFullName+=_L("::");
		}
	aFullName+=aName;
	}

EXPORT_C CObject::CObject()
//
// Constructor
//
	{

//	iContainer=NULL;
//	iOwner=NULL;
//	iName=NULL;
	iAccessCount=1;
	__DECLARE_NAME(_S("CObject"));
	}

EXPORT_C CObject::~CObject()
//
// Destructor
//
	{

	__ASSERT_ALWAYS(AccessCount()==0,Panic(EObjObjectStillReferenced));
	User::Free(iName);
	if (iContainer)
		{
		CObjectCon *p=iContainer;
		iContainer=NULL;
		p->Remove(this);
		}
	}

EXPORT_C TInt CObject::Open()
//
// Open an object.
//
	{

	Inc();
	return(KErrNone);
	}

EXPORT_C void CObject::Close()
//
// Close an object.
//
	{

	Dec();
	__ASSERT_ALWAYS(AccessCount()>=0,Panic(EObjNegativeAccessCount));
	if (AccessCount()==0)
		delete this;
	}

LOCAL_C TName GetLocalObjectName(const CObject *anObj)
	{
	TName n=_L("Local-");
	n.AppendNumFixedWidth((TInt)anObj,EHex,8);
	return n;
	}

EXPORT_C TName CObject::Name() const
//
// Return the object's name.
//
	{
	if (iName)
		return(*iName);
	return GetLocalObjectName(this);
//	return _L("");
	}

EXPORT_C TFullName CObject::FullName() const
//
// Return the object's full name.
//
	{

	TFullName n;
	makeFullName(n,Owner(),Name());
	return(n);
	}

EXPORT_C TInt CObject::SetName(const TDesC *aName)
//
// Set the object's name
//
	{

	User::Free(iName);
	iName=NULL;
	if (aName!=NULL)
		{
		iName=aName->Alloc();
		if (iName==NULL)
			return(KErrNoMemory);
		}
	return(KErrNone);
	}

EXPORT_C void CObject::SetNameL(const TDesC *aName)
//
// Set the object's name and leave on any error.
//
	{

	User::Free(iName);
	iName=NULL;
	if (aName!=NULL)
		iName=aName->AllocL();
	}

EXPORT_C CObjectIx *CObjectIx::NewL()
//
// Create an instance of this class.
//
	{

	CObjectIx *pI=new(ELeave) CObjectIx();
	SObjectIxArray *pA=new SObjectIxArray;
	if (!pA)
		{
		delete pI;
		User::LeaveNoMemory();
		}
	pI->iArray=pA;
	pA->iAllocated=0;
	pA->iCount=0;
	pA->iObjects=NULL;
	return(pI);
	}

EXPORT_C CObjectIx::CObjectIx()
//
// Constructor
//
	: iNextInstance(1)
	{
	// iArray=NULL;
	__DECLARE_NAME(_S("CObjectIx"));
	}

EXPORT_C CObjectIx::~CObjectIx()
//
// Destructor
//
	{
	if (iArray)
		{
		// We have to be very careful here. Calling Close() on the objects in the array
		// may result in other entries being removed from the array before we delete
		// them here, and may result in the array being ReAlloc()ed, corrupting the removed
		// entries, hence we must check the iArray->iCount value each time round the loop.
		TInt i=-1;
		while(++i<iArray->iCount)
			{
			SObjectIxRec* pS=iArray->iObjects+i;
			CObject *pO=pS->obj;
			if (pO)
				{
				pO->Close();
				pS->obj=NULL;	// invalidate entry after closing it
				}
			}
		delete iArray->iObjects;
		delete iArray;
		}
	}

EXPORT_C TInt CObjectIx::AddL(CObject *anObj)
//
// Add a new object to the index.
//
	{
	SObjectIxRec *pS=iArray->iObjects;
	SObjectIxRec *pE=pS+iArray->iCount;
	TInt i=0;
	TInt inc=0;
	while(pS<pE && pS->obj)
		pS++, i++;
	if (pS==pE)
		inc=1;
	if (pS==pE && iArray->iAllocated==iArray->iCount)
		{
		// no slots free, so reallocate array
		if (iArray->iCount==KObjectIxMaxHandles)
			User::LeaveNoMemory();
		TInt newAlloc=iArray->iAllocated + KObjectIxGranularity;
		SObjectIxRec *pA=(SObjectIxRec*)User::ReAllocL(iArray->iObjects, newAlloc*sizeof(SObjectIxRec));
		iArray->iObjects=pA;
		iArray->iAllocated=newAlloc;
		i=iArray->iCount;
		pS=pA+i;
		}
	pS->obj=anObj;
	pS->uniqueID=(TUint16)anObj->UniqueID();
	pS->instance=(TUint16)instanceLimit(iNextInstance);
	iNextInstance++;
	iArray->iCount+=inc;
	return(makeHandle(i,pS->instance));
	}

EXPORT_C void CObjectIx::Remove(TInt aHandle)
//
// Remove an object from the index.
//
	{

	if (aHandle&ENoClose)
		return;
	TInt i=index(aHandle);
	__ASSERT_ALWAYS(i<iArray->iCount,Panic(EObjRemoveBadHandle));
	SObjectIxRec* pR=iArray->iObjects+i;
	CObject *pO=pR->obj;
	if (!pO || pR->instance!=instance(aHandle) || pR->uniqueID!=pO->UniqueID())
		Panic(EObjRemoveBadHandle);
	pO->Close();
	pR->obj=NULL;
	if (i==iArray->iCount-1)
		{
		do
			{
			i--;
			pR--;
			} while(i>=0 && !pR->obj);
//		TInt newAlloc=((i+KObjectIxGranularity)/KObjectIxGranularity)*KObjectIxGranularity;
		TInt newAlloc=(i+KObjectIxGranularity)&~(KObjectIxGranularity-1);
		if (newAlloc!=iArray->iAllocated)
			{
			if (newAlloc)
				iArray->iObjects=(SObjectIxRec*)User::ReAlloc(iArray->iObjects,newAlloc*sizeof(SObjectIxRec));
			else
				{
				delete iArray->iObjects;
				iArray->iObjects=NULL;
				}
			iArray->iAllocated=newAlloc;
			}
		iArray->iCount=i+1;
		}
	}

EXPORT_C TInt CObjectIx::Count(CObject* anObject) const
//
// Count the number of times this object appears in this index
//
	{

	TInt anObjectCount=0;
	if (iArray->iCount)
		{
		SObjectIxRec* pS=iArray->iObjects;
		SObjectIxRec* pE=pS+iArray->iCount;
		do
			{
			if (pS->obj==anObject)
				anObjectCount++;
			} while (++pS<pE);
		}
	return(anObjectCount);
	}

#ifndef __COBJECT_MACHINE_CODED__
EXPORT_C CObject *CObjectIx::At(TInt aHandle,TInt aUniqueID)
//
// Return the object from its handle.
//
	{
	TInt i=index(aHandle);
	if (i>=iArray->iCount)
		return(NULL);
	SObjectIxRec *pS=iArray->iObjects+i;
	if (pS->instance!=instance(aHandle) || pS->uniqueID!=aUniqueID)
		return(NULL);
	return(pS->obj);
	}

EXPORT_C CObject *CObjectIx::At(TInt aHandle)
//
// Return the object from its handle.
//
	{
	TInt i=index(aHandle);
	if (i>=iArray->iCount)
		return(NULL);
	SObjectIxRec *pS=iArray->iObjects+i;
	if (pS->instance!=instance(aHandle))
		return(NULL);
	return(pS->obj);
	}
#endif

EXPORT_C TInt CObjectIx::At(const CObject *anObj) const
//
// Return the handle from an object.
//
	{

	if (iArray->iCount)
		{
		SObjectIxRec* pS=iArray->iObjects;
		SObjectIxRec* pE=pS+iArray->iCount;
		TInt i=0;
		while(pS<pE && pS->obj!=anObj)
			pS++, i++;
		if (pS<pE)
			return(makeHandle(i,pS->instance));
		}
	return(KErrNotFound);
	}

EXPORT_C CObject *CObjectIx::AtL(TInt aHandle,TInt aUniqueID)
//
// Return the object from its handle.
//
	{

	CObject* c=At(aHandle,aUniqueID);
	if (c==NULL)
		User::Leave(KErrBadHandle);
	return(c);
	}

EXPORT_C CObject *CObjectIx::AtL(TInt aHandle)
//
// Return the object from its handle.
//
	{

	CObject* c=At(aHandle);
	if (c==NULL)
		User::Leave(KErrBadHandle);
	return(c);
	}

#ifndef __COBJECT_MACHINE_CODED__
EXPORT_C CObject* CObjectIx::operator[](TInt anIndex)
//
// Return the object at anIndex
//
	{

	__ASSERT_ALWAYS(anIndex>=0 && anIndex<iArray->iCount,Panic(EArrayIndexOutOfRange));
	return((iArray->iObjects)[anIndex].obj);
	}
#else
GLDEF_C void PanicCObjectIxIndexOutOfRange(void)
	{
	Panic(EArrayIndexOutOfRange);
	}
#endif

EXPORT_C CObjectCon *CObjectCon::NewL()
//
// Create a new instance of this class.
//
	{

	CObjectCon *pO=new(ELeave) CObjectCon(ENotOwnerID);
	SObjectConArray *pA=new SObjectConArray;
	if (!pA)
		{
		delete pO;
		User::LeaveNoMemory();
		}
	pO->iArray=pA;
	pA->iAllocated=0;
	pA->iCount=0;
	pA->iObjects=NULL;
	return(pO);
	}

EXPORT_C CObjectCon::CObjectCon(TInt aUniqueID)
//
// Constructor
//
	: iUniqueID(aUniqueID)
	{

//	iArray=NULL;
	__DECLARE_NAME(_S("CObjectCon"));
	}

EXPORT_C CObjectCon::~CObjectCon()
//
// Destructor
//
	{

	if (iUniqueID!=ENotOwnerID && iArray && iArray->iCount)
		{
		// Careful here in case deleting one object causes other objects in the array
		// to be removed and Count to change.
		TInt i=-1;
		while(++i<iArray->iCount)
			{
			CObject* pS=iArray->iObjects[i];
			delete pS;
			}
		}
	if (iArray)
		{
		delete iArray->iObjects;
		delete iArray;
		}
	}

EXPORT_C TInt CObjectCon::UniqueID() const
//
// Return the unique ID
//
	{

	return(iUniqueID);
	}

EXPORT_C TInt CObjectCon::Count() const
//
// Return the current count
//
	{

	return(iArray->iCount);
	}

EXPORT_C void CObjectCon::AddL(CObject *anObj)
//
// Install a new object to the container. The full name must be unique.
//
	{

	User::LeaveIfError(CheckUniqueFullName(anObj));
	if (iArray->iCount==iArray->iAllocated)
		{
		TInt newAlloc=iArray->iAllocated+KObjectConGranularity;
		iArray->iObjects=(CObject**)User::ReAllocL(iArray->iObjects, newAlloc*sizeof(CObject*));
		iArray->iAllocated=newAlloc;
		}
	(iArray->iObjects)[iArray->iCount++]=anObj;
	if (iUniqueID!=ENotOwnerID)
		anObj->iContainer=this;
	}

EXPORT_C void CObjectCon::Remove(CObject *anObj)
//
// Remove an object from the container.
//
	{
	CObject** pS=iArray->iObjects;
	CObject** pE=pS+iArray->iCount;
	while(pS<pE)
		{
		if (*pS==anObj)
			{
			Mem::Move((TAny*)pS,(TAny*)(pS+1),TInt(pE)-TInt(pS)-sizeof(CObject*));
			TInt newAlloc=--iArray->iCount;
			newAlloc=(newAlloc+(KObjectConGranularity-1))&~(KObjectConGranularity-1);
			if (newAlloc!=iArray->iAllocated)
				{
				if (newAlloc)
					iArray->iObjects=(CObject**)User::ReAlloc(iArray->iObjects,newAlloc*sizeof(CObject*));
				else
					{
					delete iArray->iObjects;
					iArray->iObjects=NULL;
					}
				iArray->iAllocated=newAlloc;
				}
			if (iUniqueID!=ENotOwnerID && anObj->iContainer)
				{
//
// An object's destructor can scan the container so its best
// to remove the object from the container before destroying it.
//
				anObj->iContainer=NULL;
				delete anObj;
				}
			return;
			}
		pS++;
		}
	Panic(EObjRemoveObjectNotFound);
	}

#ifndef __COBJECT_MACHINE_CODED__
EXPORT_C CObject *CObjectCon::operator[](TInt anIndex)
//
// Return the object at anIndex.
//
	{
	__ASSERT_ALWAYS(anIndex>=0 && anIndex<iArray->iCount, Panic(EArrayIndexOutOfRange));
	return((iArray->iObjects)[anIndex]);
	}

EXPORT_C CObject *CObjectCon::At(TInt aFindHandle) const
//
// Return the object at aFindHandle.
//
	{

	__ASSERT_ALWAYS(uniqueID(aFindHandle)==iUniqueID,Panic(EObjFindBadHandle));
	TInt ix=index(aFindHandle);
	__ASSERT_ALWAYS(ix<iArray->iCount,Panic(EObjFindIndexOutOfRange));
	return((iArray->iObjects)[ix]);
	}

EXPORT_C CObject *CObjectCon::AtL(TInt aFindHandle) const
//
// Return the object at aFindHandle.
//
	{

	__ASSERT_ALWAYS(uniqueID(aFindHandle)==iUniqueID,User::Leave(KErrBadHandle));
	TInt ix=index(aFindHandle);
	__ASSERT_ALWAYS(ix<iArray->iCount,User::Leave(KErrArgument));
	return((iArray->iObjects)[ix]);
	}
#else
GLDEF_C void PanicCObjectConFindBadHandle(void)
	{
	Panic(EObjFindBadHandle);
	}

GLDEF_C void PanicCObjectConFindIndexOutOfRange(void)
	{
	Panic(EObjFindIndexOutOfRange);
	}

GLDEF_C void PanicCObjectConIndexOutOfRange(void)
	{
	Panic(EArrayIndexOutOfRange);
	}
#endif

#if !defined(__DES8_MACHINE_CODED__) || defined(_UNICODE)
EXPORT_C TInt User::ValidateName(const TDesC &aName)
//
// Return KErrBadName if the name is invalid.
//
	{

	if (aName.Locate('*')!=KErrNotFound || aName.Locate('?')!=KErrNotFound || aName.Locate(':')!=KErrNotFound)
		return(KErrBadName);
	return(KErrNone);
	}
#endif

EXPORT_C TInt CObjectCon::CheckUniqueFullName(const CObject *anOwner,const TDesC &aName) const
//
// Returns KErrBadName if the name is invalid or
// returns KErrAlreadyExists if the full name is not unique.
//
	{

	TInt r=User::ValidateName(aName);
	if (r==KErrNone)
		{
		TFullName n;
		makeFullName(n,anOwner,aName);
		TFullName res;
		TInt h=0;
		if (FindByFullName(h,n,res)==KErrNone)
			r=KErrAlreadyExists;
		}
	return(r);
	}

TBool CObjectCon::NamesMatch(const CObject* anObject, const CObject* aCurrentObject) const
//
// protected recursive function for use by CheckUniqueFullName
//
	{

	if (aCurrentObject->iName==NULL) // current object has no name, therefore not the same
		return(EFalse);
	if ((anObject->Name()).Compare(aCurrentObject->Name())!=0) // short names are different, therefore not the same
		return(EFalse);
	if ((aCurrentObject->Owner()==NULL)&&(anObject->Owner()==NULL)) // same short name, no owners = same
		return (ETrue);
	if ((aCurrentObject->Owner()==NULL)||(anObject->Owner()==NULL)) // one has no owner, therefore not the same
		return(EFalse);
	return(NamesMatch(anObject->Owner(),aCurrentObject->Owner())); // go recursive
	}

TBool CObjectCon::NamesMatch(const CObject* anObject, const TName& anObjectName, const CObject* aCurrentObject) const
//
// protected recursive function for use by CheckUniqueFullName
//
	{

	if (aCurrentObject->iName==NULL) // current object has no name, therefore not the same
		return(EFalse);
	if (anObjectName.Compare(aCurrentObject->Name())!=0) // short names are different, therefore not the same
		return(EFalse);
	if ((aCurrentObject->Owner()==NULL)&&(anObject->Owner()==NULL)) // same short name, no owners = same
		return (ETrue);
	if ((aCurrentObject->Owner()==NULL)||(anObject->Owner()==NULL)) // one has no owner, therefore not the same
		return(EFalse);
	return(NamesMatch(anObject->Owner(),aCurrentObject->Owner())); // go recursive
	}

EXPORT_C TInt CObjectCon::CheckUniqueFullName(const CObject* anObject) const
//
// Returns KErrBadName if the name is invalid or
// returns KErrAlreadyExists if the full name is not unique.
//
	{

	TName name(anObject->Name());
	TInt r=User::ValidateName(name);
	if (r!=KErrNone)
		return(r);

	if (!iArray || !iArray->iCount)
		return(KErrNone);

	// if it's name is null, just need to check it's not already there
	if (anObject->iName==NULL)
		{
		CObject** pS=iArray->iObjects;
		CObject** pE=pS+iArray->iCount;
		do
			{
			if (*pS==anObject)
				return(KErrAlreadyExists);
			} while(++pS<pE);
		return(KErrNone);
		}

	CObject** pS=iArray->iObjects;
	CObject** pE=pS+iArray->iCount;
	do
		{
		if (NamesMatch(anObject,name,*pS))
			return (KErrAlreadyExists);
		} while(++pS<pE);
	return(KErrNone);
	}


EXPORT_C TInt CObjectCon::FindByName(TInt &aFindHandle,const TDesC &aMatch,TName &aName) const
//
// Find an object starting at aFindHandle which matches aMatch
// just using the objects name.
//
	{

	if (!iArray || !iArray->iCount)
		return(KErrNotFound);
	TInt ix=0;
	if (aFindHandle!=0)
		{
		__ASSERT_ALWAYS(uniqueID(aFindHandle)==iUniqueID,Panic(EObjFindBadHandle));
		ix=index(aFindHandle)+1;
		};
	CObject** pS=iArray->iObjects;
	CObject** pE=pS+iArray->iCount;
	pS+=ix;
	while(pS<pE)
		{
		aName=(*pS++)->Name();
		if (aName.MatchF(aMatch)!=KErrNotFound)
			{
			aFindHandle=makeFindHandle(ix,iUniqueID);
			return(KErrNone);
			}
		ix++;
		}
	aName.Zero();
	aFindHandle=makeFindHandle(KObjectMaxIndex,iUniqueID);
	return(KErrNotFound);
	}

EXPORT_C TInt CObjectCon::FindByFullName(TInt &aFindHandle,const TDesC &aMatch,TFullName &aFullName) const
//
// Find an object starting at aFindHandle which matches aMatch
// using the objects full name.
//
	{

	if (!iArray || !iArray->iCount)
		return(KErrNotFound);
	TInt ix=0;
	if (aFindHandle!=0)
		{
		__ASSERT_ALWAYS(uniqueID(aFindHandle)==iUniqueID,Panic(EObjFindBadHandle));
		ix=index(aFindHandle)+1;
		};
	CObject** pS=iArray->iObjects;
	CObject** pE=pS+iArray->iCount;
	pS+=ix;
	while(pS<pE)
		{
		aFullName=(*pS++)->FullName();
		if (aFullName.MatchF(aMatch)!=KErrNotFound)
			{
			aFindHandle=makeFindHandle(ix,iUniqueID);
			return(KErrNone);
			}
		ix++;
		}
	aFullName.Zero();
	aFindHandle=makeFindHandle(KObjectMaxIndex,UniqueID());
	return(KErrNotFound);
	}

EXPORT_C CObjectConIx *CObjectConIx::NewL()
//
// Create an instance of this class.
//
	{

	CObjectConIx *pO=new(ELeave) CObjectConIx();
	SObjectConIxArray *pA=new SObjectConIxArray;
	if (!pA)
		{
		delete pO;
		User::LeaveNoMemory();
		}
	pO->iArray=pA;
	pA->iAllocated=0;
	pA->iCount=0;
	pA->iContainers=NULL;
	return(pO);
	}

EXPORT_C CObjectConIx::CObjectConIx()
//
// Constructor
//
	: iNextUniqueID(1)
	{

//	iArray=NULL;
	__DECLARE_NAME(_S("CObjectConIx"));
	}

EXPORT_C CObjectConIx::~CObjectConIx()
//
// Destructor
//
	{
	if (iArray && iArray->iCount)
		{
		TInt i=-1;
		while(++i<iArray->iCount)
			{
			CObjectCon* pS=iArray->iContainers[i];
			delete pS;
			}
		}
	if (iArray)
		{
		delete iArray->iContainers;
		delete iArray;
		}
	}

EXPORT_C void CObjectConIx::CreateContainerL(CObjectCon * &aContainer)
//
// Actually create the container
//
	{

	aContainer=CObjectCon::NewL();
	aContainer->iUniqueID=iNextUniqueID;
	if (iArray->iCount==iArray->iAllocated)
		{
		TInt newAlloc=iArray->iAllocated+KObjectConIxGranularity;
		iArray->iContainers=(CObjectCon**)User::ReAllocL(iArray->iContainers, newAlloc*sizeof(CObjectCon*));
		iArray->iAllocated=newAlloc;
		}
	(iArray->iContainers)[iArray->iCount++]=aContainer;
	}

EXPORT_C CObjectCon *CObjectConIx::CreateL()
//
// Create a new container.
//
	{

	CObjectCon *pC=NULL;
	TRAPD(r,CreateContainerL(pC))
	if (r!=KErrNone)
		{
		delete pC;
		User::Leave(r);
		}
	iNextUniqueID++;
	return(pC);
	}

EXPORT_C void CObjectConIx::Remove(CObjectCon *aCon)
//
// Remove a container from the index.
//
	{
	if (!aCon)
		return;
	CObjectCon** pS=iArray->iContainers;
	CObjectCon** pE=pS+iArray->iCount;
	while(pS<pE)
		{
		if (*pS==aCon)
			{
			Mem::Move((TAny*)pS,(TAny*)(pS+1),TInt(pE)-TInt(pS)-sizeof(CObjectCon*));
			TInt newAlloc=--iArray->iCount;
			newAlloc=(newAlloc+(KObjectConIxGranularity-1))&~(KObjectConIxGranularity-1);
			if (newAlloc!=iArray->iAllocated)
				{
				if (newAlloc)
					iArray->iContainers=(CObjectCon**)User::ReAlloc(iArray->iContainers,newAlloc*sizeof(CObjectCon*));
				else
					{
					delete iArray->iContainers;
					iArray->iContainers=NULL;
					}
				iArray->iAllocated=newAlloc;
				}
			delete aCon;
			return;
			}
		pS++;
		}
	Panic(EObjRemoveContainerNotFound);
	}

EXPORT_C CObjectCon *CObjectConIx::Lookup(TInt aUniqueID) const
//
// Locate the container having aUniqueID.
//
	{

	TInt u=uniqueID(aUniqueID);
	CObjectCon** pS=iArray->iContainers;
	CObjectCon** pE=pS+iArray->iCount;
	while(pS<pE)
		{
		CObjectCon *pO=*pS++;
		if (pO->iUniqueID==u)
			return(pO);
		}
	return(NULL);
	}

