// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
//
#include "UT_STD.H"
// PFS frame-related utilities
TInt SkipLink(TInt anExtent)
//
// anExtent is the end-of-stream, where is the next link pos?
// : add the size of the next frame link, or skip past anchor link
//
{return anExtent+Min(KSizeFrameDes16,(-anExtent)&KMaskFrameLength16);}
inline TBool SpaceFor(TInt aSpace,TInt anEnd,TInt aLength)
// Check if there is space for at least aLength between links at aSpace and anEnd
{return SkipLink(aSpace+aLength)<=anEnd;}
TBool Fits(TInt aSpace,TInt anEnd,TInt aLength)
//
// Check that aLength can be relocated into the space
// either an exact fit, or leaves space for a free frame of at least one byte
//
{
TInt end=SkipLink(aSpace+aLength);
return end==anEnd ? ETrue : SpaceFor(end,anEnd,1);
}
TInt ExtentL(TStreamExchange& aHost,TStreamMark& aMark,TStreamPos aBase,TInt aStream)
//
// scan a stream to discover it's extent
//
{
__ASSERT_DEBUG(aMark.RelatesTo(aHost),User::Invariant());
__ASSERT_DEBUG(aBase>=KStreamBeginning,User::Invariant());
__ASSERT_DEBUG(aStream>=0,User::Invariant());
//
aMark.SeekL(aHost,RFrame16Buf::Position(aBase,aStream)-KSizeFrameDes16);
TFrameDes16 des;
aMark.ReadL(aHost,&des,KSizeFrameDes16);
TInt frame=des;
if ((frame&KMaskFrameType16)!=EFrameData16)
if ((frame&KMaskFrameType16)!=EFrameDescriptive16)
{
aMark.Withdraw(aHost);
__LEAVE(KErrCorrupt);
}
//
TInt anchor=((aStream>>KShiftFrameLength16)+1)<<KShiftFrameLength16;
do
{
frame&=KMaskFrameLength16;
TInt lim=anchor-aStream;
if (frame!=KFrameOpen16)
{
if (frame>=lim)
{
aMark.Withdraw(aHost);
__LEAVE(KErrCorrupt);
}
return aStream+frame;
}
aMark.SeekL(aHost,EStreamMark,lim);
aStream=anchor;
anchor+=KFrameFullLength16;
aMark.ReadL(aHost,&des,KSizeFrameDes16);
frame=des;
} while ((frame&KMaskFrameType16)==EFrameContinuation16);
return aStream;
}
// Class TRelocatorInput
// Used to transfer a frame-set within the file
const TInt KRelocatorBufferSize=0xc00;
NONSHARABLE_CLASS(TRelocatorInput) : public MStreamInput
{
public:
inline TRelocatorInput(TStreamExchange& aHost,TStreamMark& aMark);
void OpenL(TFrameType16 aType,TStreamPos aBase,TInt aPos,TInt aLength,TInt aTerminator);
void CommitL();
private:
TInt PushL(const TAny* aPtr,TInt aMaxLength);
TStreamTransfer ReadFromL(MStreamBuf& aSource,TStreamTransfer aTransfer);
void DesL(TFrameType16 aType);
//
inline TStreamExchange& Host() const;
inline TStreamMark& Mark();
private:
TStreamExchange* iHost;
TStreamMark* iMark;
TFrameType16 iType;
TInt iRemain;
TInt iAnchor;
TInt iTerminator;
};
inline TRelocatorInput::TRelocatorInput(TStreamExchange& aHost,TStreamMark& aMark)
: iHost(&aHost),iMark(&aMark)
{}
inline TStreamExchange& TRelocatorInput::Host() const
{
__ASSERT_DEBUG(iHost!=NULL,User::Invariant());
return *iHost;
}
inline TStreamMark& TRelocatorInput::Mark()
{
__ASSERT_DEBUG(iMark!=NULL,User::Invariant());
return *iMark;
}
void TRelocatorInput::OpenL(TFrameType16 aType,TStreamPos aBase,TInt aPos,TInt aLength,TInt aTerminator)
//
// initiate the relocated stream
//
{
__ASSERT_DEBUG(aType!=EFrameFree16,User::Invariant());
__ASSERT_DEBUG(aBase>=KStreamBeginning,User::Invariant());
__ASSERT_DEBUG(aPos>=0,User::Invariant());
__ASSERT_DEBUG(aLength>0,User::Invariant());
//
iType=aType;
iRemain=aLength;
iTerminator=aTerminator;
iAnchor=KFrameFullLength16-(aPos&KMaskFrameLength16);
Mark().SeekL(Host(),RFrame16Buf::Position(aBase,aPos)-KSizeFrameDes16);
}
void TRelocatorInput::CommitL()
//
// complete the relocated stream
//
{
__ASSERT_DEBUG(iRemain==0,User::Invariant());
__ASSERT_DEBUG(iAnchor>=0,User::Invariant());
//
if (iTerminator<0)
return;
//
TFrameDes16 des[2];
des[1]=TFrameDes16(iTerminator);
TInt l=KSizeFrameDes16;
if (iAnchor<=KSizeFrameDes16)
l+=iAnchor;
Mark().WriteL(Host(),(const TUint8*)&des[2]-l,l);
}
TInt TRelocatorInput::PushL(const TAny*,TInt)
//
// use a passive write through to the host
//
{
return 0;
}
void TRelocatorInput::DesL(TFrameType16 aType)
//
// Write the next frame descriptor
//
{
__ASSERT_DEBUG(aType!=EFrameFree16,User::Invariant());
TFrameDes16 des=TFrameDes16(iRemain<iAnchor ? aType+iRemain : aType+KFrameOpen16);
Mark().WriteL(Host(),&des,KSizeFrameDes16);
}
TStreamTransfer TRelocatorInput::ReadFromL(MStreamBuf& aSource,TStreamTransfer aTransfer)
//
// bulk of the transfer happens here
//
{
__ASSERT_DEBUG(!(aTransfer>iRemain),User::Invariant());
do
{
TUint8 buf[KRelocatorBufferSize];
TInt len=aSource.ReadL(buf,aTransfer[sizeof(buf)]);
if (len==0)
break;
aTransfer-=len;
const TUint8* p=buf;
if (iType!=EFrameFree16)
{
DesL(iType);
iType=EFrameFree16;
}
for (;;)
{
if (iAnchor==0)
{ // write next anchor
iAnchor=KFrameFullLength16;
DesL(EFrameContinuation16);
}
TInt frame=Min(len,iAnchor);
Mark().WriteL(Host(),p,frame);
iAnchor-=frame;
iRemain-=frame;
len-=frame;
if (len==0)
break;
p+=frame;
}
} while (aTransfer>0);
return aTransfer;
}
// Class TPermanentStoreRelocator
// used to locate streams for relocation in limited memory
class TPermanentStoreRelocator
{
#if defined(__SMALL_BUNDLE)
enum {EBundleSize=8-1};
#else
enum {EBundleSize=64-1};
#endif
public:
typedef CPermanentStoreCollector::TEntry TEntry;
public:
void Reset();
TBool Reset(TInt aPos);
TInt FillL(CPermanentStoreToc& aToc);
void EvaluateLengthsL(TStreamExchange& aHost,TStreamMark& aMark,TStreamPos aBase);
//
TBool FindFit(TInt aSpace,TInt anEnd);
inline const TEntry* Current() const;
void Relocated(const TEntry* anEntry);
//
TInt Extent(TInt aStream) const;
inline TInt MinLength() const;
private:
static void Push(TEntry* aHeap,TEntry* aHole,const TEntry& aValue);
static TEntry PopPush(TEntry* aHeap,TEntry* anEnd,const TEntry& aValue);
private:
TEntry* iNext;
const TEntry* iFinish;
TInt iMore;
TInt iPos;
TInt iCurrentMin,iMinLength;
TEntry iTable[EBundleSize];
};
inline const TPermanentStoreRelocator::TEntry* TPermanentStoreRelocator::Current() const
{__ASSERT_DEBUG(iNext>=iTable&&iNext<iFinish,User::Invariant());return iNext;}
inline TInt TPermanentStoreRelocator::MinLength() const
{return iMinLength;}
void TPermanentStoreRelocator::Reset()
//
// reset the cached length values
//
{
iPos=0;
iMinLength=1;
}
TBool TPermanentStoreRelocator::Reset(TInt aPos)
//
// reset the iterator for a new scan from aPos
//
{
__ASSERT_DEBUG(aPos>=0,User::Invariant());
//
iCurrentMin=KMaxTInt;
TEntry* e=iTable;
if (aPos>=iPos || e==iFinish || aPos<e->entry.ref)
{ // rescan required
iFinish=iNext=NULL;
iMore=-1;
iPos=aPos;
return EFalse;
}
// can use current data
for (;e->entry.ref!=aPos;++e)
{
__ASSERT_DEBUG(e->entry.ref<aPos,User::Invariant());
__ASSERT_DEBUG(e<iFinish,User::Invariant());
}
__ASSERT_DEBUG(e->entry.handle>=0,User::Invariant());
iNext=e;
return ETrue;
}
TBool TPermanentStoreRelocator::FillL(CPermanentStoreToc& aToc)
//
// Fill the table with the next bundle of stream offsets
// return if there are more available
//
{
__ASSERT_DEBUG(iNext==iFinish,User::Invariant());
if (!iMore)
return EFalse;
//
__ASSERT_DEBUG(iPos>=0,User::Invariant());
//
RPermanentStoreTocIter iter(aToc);
CleanupReleasePushL(iter);
TInt streams=0;
TInt from=iPos;
TEntry* table=iTable;
TEntry* last=table;
TEntry* end=table+EBundleSize;
TEntry entry;
__DEBUG(entry.len=-1);
for (iter.ResetL();iter.NextL(entry.entry);)
{
if (entry.entry.handle<0)
continue;
TInt off=entry.entry.ref;
if (off<from)
continue;
++streams;
if (last<end)
Push(table,last++,entry); // push onto the bottom
else if (off<table->entry.ref)
PopPush(table,last,entry); // replace item in heap
}
CleanupStack::PopAndDestroy();
iMore=streams+table-last;
if (streams)
iPos=table->entry.ref+1; // largest offset in table
iNext=table;
iFinish=last;
while (--last>table)
*last=PopPush(table,last,*last);
return streams>0;
}
void TPermanentStoreRelocator::Push(TEntry* aHeap,TEntry* aHole,const TEntry& aValue)
//
// push aValue onto the heap (which will grow)
//
{
TInt ix=aHole+1-aHeap;
while (aHole>aHeap)
{
TInt link=ix-(ix>>1);
ix-=link;
TEntry* parent=aHole-link;
if (parent->entry.ref>=aValue.entry.ref)
break;
*aHole=*parent;
aHole=parent;
}
*aHole=aValue;
}
TPermanentStoreRelocator::TEntry TPermanentStoreRelocator::PopPush(TEntry* aHeap,TEntry* anEnd,const TEntry& aValue)
//
// pop the top and push aValue
//
{
TEntry top=*aHeap;
TEntry* hole=aHeap;
--anEnd;
TInt ix=1;
for (;;)
{
TEntry* child=hole+ix;
ix<<=1;
if (child>anEnd)
break;
if (child<anEnd && (child+1)->entry.ref>child->entry.ref)
{
++ix;
++child;
}
if (child->entry.ref<=aValue.entry.ref)
break;
*hole=*child;
hole=child;
}
*hole=aValue;
return top;
}
void TPermanentStoreRelocator::EvaluateLengthsL(TStreamExchange& aHost,TStreamMark& aMark,TStreamPos aBase)
//
// Evaluate the lengths for all the entries in the table
//
{
const TEntry* end=iFinish;
for (TEntry* e=iTable;e<end;++e)
{
__ASSERT_DEBUG(e->entry.handle>=0,User::Invariant());
__ASSERT_DEBUG(e->entry.ref>=0,User::Invariant());
__ASSERT_DEBUG(e->len==-1,User::Invariant());
TInt pos=e->entry.ref;
e->len=ExtentL(aHost,aMark,aBase,pos)-pos;
}
}
TBool TPermanentStoreRelocator::FindFit(TInt aSpace,TInt anEnd)
//
// Find a stream which will fit into the space
//
{
const TEntry* end=iFinish;
TEntry* e;
for (e=iNext;e<end;++e)
{
if (e->entry.handle<0)
continue;
TInt len=e->len;
__ASSERT_DEBUG(len>0,User::Invariant());
if (Fits(aSpace,anEnd,len))
{
iNext=e;
return ETrue;
}
// len has been left behind, check for minimum
if (len<iCurrentMin)
iCurrentMin=len;
}
// if no more data, use current min as cached value
if (iMore==0)
iMinLength=iCurrentMin;
iNext=e;
return EFalse;
}
void TPermanentStoreRelocator::Relocated(const TEntry* anEntry)
//
// Relocation has been successful
//
{
__ASSERT_DEBUG(anEntry==iNext,User::Invariant());
TEntry* e=CONST_CAST(TEntry*,anEntry);
e->entry.handle=-1;
iNext=e+1;
}
TInt TPermanentStoreRelocator::Extent(TInt aStream) const
//
// Return this stream extent if we know it
//
{
const TEntry* e=iTable;
if (aStream>=iPos || e==iFinish || aStream<e->entry.ref)
return -1; // we don't know it
for (;e->entry.ref!=aStream;++e)
{
__ASSERT_DEBUG(e->entry.ref<aStream,User::Invariant());
__ASSERT_DEBUG(e<iFinish,User::Invariant());
}
__ASSERT_DEBUG(e->entry.handle>=0,User::Invariant());
__ASSERT_DEBUG(e->len>=0,User::Invariant());
return aStream+e->len;
}
// class TPermanentStoreStreamIter
void TPermanentStoreStreamIter::Reset()
//
// reset the iterator for a new scan
//
{
iNext=NULL;
iFinish=NULL;
iMore=-1;
iPos=0;
}
TInt TPermanentStoreStreamIter::FillL(CPermanentStoreToc& aToc)
//
// Fill the table with the next bundle of stream offsets
// return the total streams left to iterate
//
{
__ASSERT_DEBUG(iNext==iFinish,User::Invariant());
if (!iMore)
return 0;
//
__ASSERT_DEBUG(iPos>=0,User::Invariant());
//
RPermanentStoreTocIter iter(aToc);
CleanupReleasePushL(iter);
TInt streams=0;
TInt from=iPos;
TInt* heap=iTable;
TInt* last=heap;
TInt* end=heap+EBundleSize;
RPermanentStoreTocIter::TEntry entry;
for (iter.ResetL();iter.NextL(entry);)
{
if (entry.handle<0)
continue;
TInt off=entry.ref;
if (off<from)
continue;
++streams;
if (last<end)
Push(heap,last++,off); // push onto the bottom
else if (off<*heap)
PopPush(heap,last,off); // replace item in heap
}
CleanupStack::PopAndDestroy();
iMore=streams+(heap-last);
iPos=*heap+1; // largest offset in table
iNext=heap;
iFinish=last;
while (--last>heap)
*last=PopPush(heap,last,*last);
return streams;
}
TInt TPermanentStoreStreamIter::Next()
//
// return the next offset, or <0 if the table is empty
//
{
__ASSERT_DEBUG(iMore>=0,User::Invariant());
//
while (iNext<iFinish)
{
TInt off=*iNext++;
if (off>=0)
return off;
}
return -1;
}
void TPermanentStoreStreamIter::Relocated(TInt aStream)
//
// aStream has been relocated, mark the table
//
{
__ASSERT_DEBUG(iMore>=0,User::Invariant());
//
if (aStream>=iPos)
return;
TInt* p=iNext;
for (;;)
{
if (p==iFinish)
return;
if (*p>=0)
break;
++p;
}
if (aStream<*p)
return;
//
for (;*p!=aStream;++p)
{
__ASSERT_DEBUG(p<iFinish,User::Invariant());
__ASSERT_DEBUG(*p<aStream,User::Invariant());
}
if (p==iNext)
iNext=p+1;
else
*p=-1;
}
void TPermanentStoreStreamIter::Push(TInt* aHeap,TInt* aHole,TInt aValue)
//
// push aValue onto the heap (which will grow)
//
{
TInt ix=aHole+1-aHeap;
while (aHole>aHeap)
{
TInt link=ix-(ix>>1);
ix-=link;
TInt* parent=aHole-link;
if (*parent>=aValue)
break;
*aHole=*parent;
aHole=parent;
}
*aHole=aValue;
}
TInt TPermanentStoreStreamIter::PopPush(TInt* aHeap,TInt* anEnd,TInt aValue)
//
// pop the top and push aValue
//
{
TInt top=*aHeap;
TInt* hole=aHeap;
--anEnd;
TInt ix=1;
for (;;)
{
TInt* child=hole+ix;
ix<<=1;
if (child>anEnd)
break;
if (child<anEnd && *(child+1)>*child)
{
++ix;
++child;
}
if (*child<=aValue)
break;
*hole=*child;
hole=child;
}
*hole=aValue;
return top;
}
// Class CPermanentStoreCollector
CPermanentStoreCollector* CPermanentStoreCollector::CompactorL(CPermanentStoreCoord& aCoord)
{
CPermanentStoreCollector* self=ReclaimerL(aCoord);
CleanupStack::PushL(self);
self->iReloc=new(ELeave) TPermanentStoreRelocator;
CleanupStack::Pop();
return self;
}
CPermanentStoreCollector* CPermanentStoreCollector::ReclaimerL(CPermanentStoreCoord& aCoord)
{
return new(ELeave) CPermanentStoreCollector(aCoord);
}
CPermanentStoreCollector::CPermanentStoreCollector(CPermanentStoreCoord& aCoord)
: iCoord(&aCoord),iHost(&aCoord.Host()),iStreams(EGranularity,_FOFF(TEntry,entry.ref))
{
Coord().Inc();
}
CPermanentStoreCollector::~CPermanentStoreCollector()
{
delete iReloc;
iStreams.Close();
iMark.Withdraw(Host());
Coord().Dec();
}
void CPermanentStoreCollector::DoRelease()
{
delete this;
}
void CPermanentStoreCollector::DoResetL(TInt& aCount)
{
iMark.Withdraw(Host());
iMark=KStreamBeginning;
iCoordGen=Coord().Generation();
iFree=0;
TRAPD(r, aCount = FastResetL());
if (r == KErrNone)
return;
if (r != KErrNoMemory)
User::Leave(r);
// fall back to fixed memory solution
iState=EGetFree;
if (Compacting())
iReloc->Reset();
iIter.Reset();
TInt streams=iIter.FillL(Coord().ConsolidateL());
aCount=streams+1;
}
const TInt KMaxStepEffort=9;
void CPermanentStoreCollector::DoNextL(TInt& aStep,TInt& aTotal)
//
// Dispatch the next set of operations
//
{
if (aStep<=0)
{
__DEBUG(Panic(TStorePanic()));
return;
}
//
if (iCoordGen!=Coord().Generation() || Coord().TableL().IsVirtual())
__LEAVE(KErrNotReady);
//
TInt effort=0;
do
{
switch (iState)
{
default:
__DEBUG(User::Invariant());
case EGetFree:
effort+=GetFreeL();
break;
case ESkip:
effort+=SkipL(aTotal);
--aStep;
break;
case EInitRelocator:
effort+=InitRelocator();
break;
case EFillRelocator:
effort+=FillRelocatorL();
break;
case EEvalRelocator:
effort+=EvalRelocatorL();
break;
case EScanRelocator:
effort+=ScanRelocator();
break;
case ERelocateStream:
effort+=RelocateStreamL();
--aStep;
break;
case ERelocateToc:
RelocateTocL(aTotal);
iStreams.Close();
__ASSERT_DEBUG(aStep==1,User::Invariant());
aStep=0;
return;
case EFastSort:
FastSort();
--aStep;
return;
case EFastExtent:
FastExtentL(aTotal);
--aStep;
return;
case EFastRelocate:
FastRelocateL(aTotal);
--aStep;
return;
}
} while(effort<KMaxStepEffort);
__ASSERT_DEBUG(aStep>=1,User::Invariant());
}
TInt CPermanentStoreCollector::GetFreeL()
//
// find the end of the free space
//
{
__ASSERT_DEBUG(iState==EGetFree,User::Invariant());
__ASSERT_DEBUG(iFree>=0,User::Invariant());
//
TInt effort=0;
iEnd=iIter.Next();
if (iEnd<0)
{
if (iIter.FillL(Coord().ConsolidateL())==0)
{
iState=ERelocateToc; // no more streams
return effort;
}
effort=KMaxStepEffort;
iEnd=iIter.Next();
__ASSERT_DEBUG(iEnd>=0,User::Invariant());
}
iState=Compacting() && HaveEnoughSpace() ? EInitRelocator : ESkip;
return effort;
}
TInt CPermanentStoreCollector::SkipL(TInt& aTotal)
//
// Find some free space, iEnd was the last stream extracted from concat
//
{
__ASSERT_DEBUG(iState==ESkip,User::Invariant());
__ASSERT_DEBUG(iFree>=0&&iFree<=iEnd,User::Invariant());
//
aTotal+=iEnd-iFree;
iFree=SkipLink(ExtentL(iEnd));
iState=EGetFree;
return 1; // effort
}
TInt CPermanentStoreCollector::InitRelocator()
//
// initialise the relocator for the free space
//
{
__ASSERT_DEBUG(iState==EInitRelocator,User::Invariant());
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
iState=iReloc->Reset(iEnd) ? EScanRelocator : EFillRelocator;
return 0; // no real work at all
}
TInt CPermanentStoreCollector::FillRelocatorL()
//
// Fill the relocator table
//
{
__ASSERT_DEBUG(iState==EFillRelocator,User::Invariant());
__ASSERT_DEBUG(iFree>=0&&iFree<iEnd,User::Invariant());
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
TBool more=iReloc->FillL(Coord().ConsolidateL());
iState=more ? EEvalRelocator : ESkip;
return more ? KMaxStepEffort : 0;
}
TInt CPermanentStoreCollector::EvalRelocatorL()
//
// evaluate the extents for the relocator
//
{
__ASSERT_DEBUG(iState==EEvalRelocator,User::Invariant());
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
iReloc->EvaluateLengthsL(Host(),iMark,Coord().Base());
iState=EScanRelocator;
return KMaxStepEffort;
}
TInt CPermanentStoreCollector::ScanRelocator()
//
// find a stream that will fit
//
{
__ASSERT_DEBUG(iState==EScanRelocator,User::Invariant());
__ASSERT_DEBUG(iFree>=0&&iFree<iEnd,User::Invariant());
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
iState=iReloc->FindFit(iFree,iEnd) ? ERelocateStream : EFillRelocator;
return 0;
}
TInt CPermanentStoreCollector::RelocateStreamL()
//
// find and relocate a stream
//
{
__ASSERT_DEBUG(iState==ERelocateStream,User::Invariant());
__ASSERT_DEBUG(iFree>=0&&iFree<iEnd,User::Invariant());
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
const TEntry& e = *iReloc->Current();
RelocateStreamL(e, iEnd);
iState=e.entry.ref==iEnd ? EGetFree : HaveEnoughSpace() ? EScanRelocator : ESkip;
iIter.Relocated(e.entry.ref);
iReloc->Relocated(&e);
return KMaxStepEffort;
}
void CPermanentStoreCollector::RelocateTocL(TInt& aTotal)
//
// relocate the toc - return any wasted bytes
//
{
__ASSERT_DEBUG(iState==ERelocateToc,User::Invariant());
__ASSERT_DEBUG(iFree>=0,User::Invariant());
//
TInt toc=Coord().iToc+KOffsetTocHeader;
if (toc<0)
return;
//
if (Compacting())
{
TInt len=Coord().TableL().Extent()-toc;
if (Fits(iFree,toc,len))
{
RelocateL(toc, len, EFrameDescriptive16, toc);
Coord().MoveL(iFree-KOffsetTocHeader,iFree + len);
return;
}
}
// don't move it
aTotal += toc-iFree;
}
TBool CPermanentStoreCollector::HaveEnoughSpace() const
{
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
return SpaceFor(iFree,iEnd,iReloc->MinLength());
}
TInt CPermanentStoreCollector::ExtentL(TInt aStream)
//
// Check if the relocator knows before scanning the file
//
{
if (Compacting())
{
TInt ext=iReloc->Extent(aStream);
if (ext>=0)
return ext;
}
return ::ExtentL(Host(),iMark,Coord().Base(),aStream);
}
void CPermanentStoreCollector::RelocateStreamL(const CPermanentStoreCollector::TEntry& aReloc, TInt aExtent)
//
// relocate a stream into [iFree, aExtent)
//
{
if (Coord().Accessed()) // must have exclusive access to relocate the stream
__LEAVE(KErrInUse);
//
TInt end=RelocateL(aReloc.entry.ref,aReloc.len,aReloc.entry.handle == KHandleTocBase ? EFrameDescriptive16 : EFrameData16, aExtent);
Coord().RelocateL(aReloc.entry.handle, iFree);
iCoordGen=Coord().Generation(); // changed by relocation
iFree = end;
}
TInt CPermanentStoreCollector::RelocateL(TInt aStream, TInt aLength, TFrameType16 aType, TInt aExtent)
//
// Relocate a data stream into [iFree, aExtent)
//
{
__ASSERT_DEBUG(Fits(iFree,aExtent,aLength),User::Invariant());
__ASSERT_DEBUG(Compacting(),User::Invariant());
//
TInt end=SkipLink(iFree+aLength);
TInt terminator;
if (end==aExtent)
terminator=-1;
else
{
TInt anchor=((end>>KShiftFrameLength16)+1)<<KShiftFrameLength16;
if (aExtent<anchor)
{
__ASSERT_DEBUG(aExtent-KSizeFrameDes16-end>0,User::Invariant());
terminator=EFrameFree16+(aExtent-KSizeFrameDes16-end);
}
else
terminator=EFrameFree16+KFrameOpen16;
}
//
RFrame16Buf from(Coord().Base());
from.Set(Host(),aStream,aStream+aLength,MStreamBuf::ERead);
from.PushL();
TRelocatorInput input(Host(),iMark);
input.OpenL(aType,Coord().Base(),iFree,aLength,terminator);
from.ReadL(input,aLength);
input.CommitL();
CleanupStack::PopAndDestroy();
//
return end;
}
TInt CPermanentStoreCollector::FastResetL()
//
// Fill the table with the streams with data in the store
//
{
iStreams.Reset();
//
CleanupClosePushL(iStreams);
RPermanentStoreTocIter iter(Coord().ConsolidateL());
CleanupReleasePushL(iter);
TEntry entry;
for (iter.ResetL();iter.NextL(entry.entry);)
{
if (entry.entry.handle<0)
continue;
if (entry.entry.ref<0)
continue;
User::LeaveIfError(iStreams.Append(entry));
}
CleanupStack::PopAndDestroy(&iter);
CleanupStack::Pop(&iStreams);
//
// always have final (toc) step
iState = ERelocateToc;
TInt streams = iStreams.Count();
if (streams == 0)
return 1;
iState = EFastSort;
// ordering is 1 step and evaluating the extents is several more
TInt count = 2 + (streams + EExtentStep - 1)/EExtentStep;
if (Compacting())
count += streams;
return count;
}
void CPermanentStoreCollector::FastSort()
{
__ASSERT_DEBUG(iState == EFastSort, User::Invariant());
//
iStreams.SortSigned();
iNext = &iStreams[0];
iLast = iNext + iStreams.Count();
iState = EFastExtent;
}
void CPermanentStoreCollector::FastExtentL(TInt& aTotal)
//
// Evaluate the lengths for all the streams
// if reclaiming, update aTotal with free space skipped
// return false until we've done the last one
//
{
__ASSERT_DEBUG(iState == EFastExtent, User::Invariant());
__ASSERT_DEBUG(iNext != iLast, User::Invariant());
TEntry* e = iNext;
const TEntry* end = Min(iLast, e + EExtentStep);
do
{
TInt ref = e->entry.ref;
__ASSERT_DEBUG(TUint(iFree)<=TUint(ref),User::Invariant());
TInt ext = ::ExtentL(Host(), iMark, Coord().Base(), ref);
e->len = ext - ref;
if (!Compacting())
aTotal += ref - iFree;
iFree = SkipLink(ext);
} while (++e < end);
iNext = e;
//
if (e == iLast)
{
if (!Compacting())
iState = ERelocateToc;
else
{
iNext = &iStreams[0];
iFree = 0;
iState = EFastRelocate;
}
}
}
CPermanentStoreCollector::TEntry* CPermanentStoreCollector::BestFit(TInt aPos, TInt aExt, TEntry* aFirst, TEntry* aLast)
//
// [aPos, aExt) is a hole - find the best fit to fill it in [aFirst, aLast)
//
{
__ASSERT_DEBUG(aPos <= aExt, User::Invariant());
TEntry* r = 0;
if (aPos == aExt)
return r;
//
if ((aExt & KMaskFrameLength16) != 0)
aExt -= KSizeFrameDes16;
const TInt mingap = Min(KSizeFrameDes16 + 1, (aExt & KMaskFrameLength16));
TInt rlen = 0;
TInt avail = aExt - aPos;
do
{
TInt len;
for (;;)
{
len = (--aLast)->len;
if (len > rlen)
break;
if (aFirst == aLast)
return r;
}
TInt d = avail - len;
if (d < 0)
continue;
if (d == 0) // exact fit
return aLast;
if (d < mingap)
continue;
r = aLast;
rlen = len;
} while (aFirst != aLast);
return r;
}
void CPermanentStoreCollector::FastRelocateL(TInt& aTotal)
//
// Look for a stream to move. Either move a stream or fail to find a fit before returning
// fill holes from the front of the file with the biggest block that will fit (inverted current algorithm)
// return true when the last hole has been looked at
//
{
__ASSERT_DEBUG(iState == EFastRelocate, User::Invariant());
__ASSERT_DEBUG(iNext != iLast, User::Invariant());
TEntry* e = iNext;
TInt ext = e->entry.ref;
__ASSERT_DEBUG(ext >= 0, User::Invariant());
TEntry* r = BestFit(iFree, ext, e, iLast);
if (!r)
{
// Nothing fits, accumulate free space
aTotal += ext - iFree;
iFree = SkipLink(ext + e->len);
}
else
{
// lets move it
RelocateStreamL(*r, ext);
if (r != e)
{
// relocated a stream other than the one terminating the current hole
// mark it at moved
r->entry.ref = -1;
r->len = -1;
return;
}
}
// skip through any relocated streams
while (++e < iLast && e->entry.ref < 0)
;
iNext = e;
if (e == iLast)
iState = ERelocateToc;
}