// 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 <s32file.h>
#include "UT_STD.H"
//The default media block size, used in the computations, if the file system guarantees atomic "block write" file operations.
const TInt KDefaultMediaBlockSize = 512;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////// TPermanentStoreHeader /////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Returns false, if:
// - the dirty bit is set (indicates commit phase #2 or #3 is not complete);
// - the CRC check fails ("backup TOC ref", "handle" and "TOC ref" are protected by 16-bit CRC in the permanent store file header);
TBool TPermanentStoreHeader::IsValid() const
{
if (IsDirty())
return EFalse;
//
TUint16 crc=0;
Mem::Crc(crc,Ptr(),_FOFF(TPermanentStoreHeader,iCrc));
return crc==iCrc;
}
//Sets the "backup TOC ref", "handle" and "TOC ref" in current TPermanentStoreHeader object.
//16-bit CRC is calculated, based on the values of the input parameters, and stored together with them in the
//TPermanentStoreHeader object.
void TPermanentStoreHeader::Set(TInt aBackupToc,TInt aHandle,TInt aReference)
{
iBackup=TUint32(aBackupToc)<<1;
iHandle=aHandle;
iRef=aReference;
iCrc=0;
Mem::Crc(iCrc,Ptr(),_FOFF(TPermanentStoreHeader,iCrc));
__ASSERT_DEBUG(IsValid(),User::Invariant());
}
const TInt KTocGranularity=12;
struct STocEntry
{
TInt32 handle;
TInt32 ref;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////// CPermanentStoreToc ////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TInt CPermanentStoreToc::TEntry::Compare(const TEntry& aLeft, const TEntry& aRight)
//
// Ordering for Stream handles, return <0,0,>0 depending on comparison
// The index part of the handle is only 24 bits, so just subtract the handles for result
//
{
return (aLeft.handle&KMaskHandleIndex) - (aRight.handle&KMaskHandleIndex);
}
CPermanentStoreToc* CPermanentStoreToc::NewL(TStreamPos aBase,TStreamExchange& aHost,TInt aToc,TInt aBaseReloc)
{
CPermanentStoreToc* table=new(ELeave) CPermanentStoreToc(aBase,aHost);
CleanupStack::PushL(table);
table->ConstructL(aToc,aBaseReloc);
CleanupStack::Pop();
return table;
}
TBool CPermanentStoreToc::IsDelta() const
//
// Report whether the current TOC should be written as a delta, or a base TOC
//
{
TInt delta = iEntries.Count();
if (delta >= KTocDeltaCap)
return EFalse;
TInt magic = iMagic + delta;
if (magic > KMaxTocDeltaMagic)
return EFalse;
return magic*KSizeTocDeltaEntry <= iCount*KSizeTocEntry - KSizeTocDeltaExtra;
}
void CPermanentStoreToc::Move(TInt aToc,TInt anExtent)
//
// Final stage of compaction, update to address new TOC
//
{
__ASSERT_DEBUG(anExtent<=iOff&&anExtent>=aToc&&anExtent-(aToc+KOffsetTocHeader)==iExt-iOff,User::Invariant());
if (!HasDelta())
{
// Base-toc only, update the base toc position as well
TInt window=iWindow-iTocExt;
iTocOff=aToc+KOffsetTocHeader;
iTocExt=anExtent;
iWindow=anExtent+window;
}
iOff = aToc+KOffsetTocHeader;
iExt = anExtent;
}
TInt CPermanentStoreToc::RealizeL(TInt aPrimary,TInt anExtent) const
{
__ASSERT_DEBUG(IsVirtual(),User::Invariant());
RFrame16Buf buf(Base());
buf.PushL();
const TBool delta = IsDelta();
aPrimary &= KMaskStreamIdValue;
if (delta)
aPrimary |= KTocDelta;
STocHead h;
h.primary=TInt32(aPrimary);
h.avail=TInt32(iAvail);
h.count=TUint32(iCount);
//
TInt off;
if (delta)
off=DeltaL(buf,anExtent,h);
else
off=RewriteL(buf,anExtent,h);
//
buf.SynchL();
CleanupStack::PopAndDestroy(&buf);
return off-KOffsetTocHeader;
}
void CPermanentStoreToc::Adopt(TInt aToc,TInt aPrimary)
//
// Final stage of Commit - all file changes have been successful
// Record the new Toc location and reset the changed flag
//
{
__ASSERT_DEBUG(aToc+KOffsetTocHeader>=iExt,User::Invariant());
if (IsDelta())
{
// Adjust the TOC-delta location
TInt c = iEntries.Count();
iOff=aToc+KOffsetTocHeader;
iExt=aToc + KSizeTocDeltaExtra + KSizeTocDeltaEntry*c;
if (c > KTocDeltaMagic)
iMagic += c - KTocDeltaMagic;
}
else
{
// Adjust all the TOC data and reset the delta-set
TInt window = iTocOff>=0 ? iWindow-iTocOff : -KOffsetTocHeader;
iTocOff=iOff=aToc+KOffsetTocHeader;
iTocExt=iExt=aToc+KSizeTocEntry*iCount;
iWindow = iTocOff + window;
iEntries.Reset();
iMagic = 0;
}
iPrimary=aPrimary;
__ASSERT_DEBUG(!IsVirtual(),User::Invariant());
}
TInt CPermanentStoreToc::AllocL(TInt anOffset)
{
TEntry& entry=DoAllocL();
entry.ref=anOffset;
return entry.handle;
}
TInt CPermanentStoreToc::AllocL()
{
TEntry& entry=DoAllocL();
entry.ref=KFrameNonexistent16;
return entry.handle|=KHandleInvalid;
}
void CPermanentStoreToc::Cancel(TInt aHandle)
{
__ASSERT_DEBUG(aHandle<0&&IsVirtual(),User::Invariant());
TEntry* entry=Entry(aHandle);
__ASSERT_DEBUG((entry!=NULL) && (entry->handle==aHandle),User::Invariant());
entry->ref=iAvail;
iAvail=aHandle;
}
void CPermanentStoreToc::FreeL(TInt aHandle)
{
__ASSERT_DEBUG(aHandle>0,User::Invariant());
TEntry& entry=FetchL(aHandle);
aHandle|=KHandleInvalid;
entry.handle=aHandle;
entry.ref=iAvail;
iAvail=aHandle;
Changed();
}
TInt CPermanentStoreToc::AtL(TInt aHandle) const
{
__ASSERT_DEBUG(aHandle>0,User::Invariant());
// check the delta table
const TEntry* entry=Entry(aHandle);
if (entry!=NULL )
{
// if there is an entry in the delta table, but its
// not an exact match leave with KErrNotFound
// This happens when the delta indicates it's been deleted
if (entry->handle!=aHandle)
User::Leave(KErrNotFound);
return entry->ref;
}
//
// if it's not in the delta table check the base TOC
return DoAtL(aHandle);
}
void CPermanentStoreToc::PutL(TInt aHandle, TInt anOffset, TPut aCheck)
//
// Used by compaction to update a single stream reference IN-PLACE in the TOC
// We need to check in which TOC to make the update
//
{
__ASSERT_DEBUG(!IsVirtual(),User::Invariant());
__ASSERT_DEBUG(aHandle>0,User::Invariant());
__ASSERT_DEBUG(aHandle!=KHandleTocBase || HasDelta(),User::Invariant());
if (aHandle == KHandleTocBase)
{
// update the TOC-base link
PutTocL(anOffset - KOffsetTocHeader, aCheck);
if (iTocOff != anOffset)
{
TInt size = iTocExt - iTocOff;
TInt window = iWindow - iTocOff;
iTocOff = anOffset;
iTocExt = anOffset + size;
iWindow = anOffset + window;
}
return;
}
TEntry e;
e.handle=aHandle;
TInt i;
if (iEntries.FindInOrder(e,i,&TEntry::Compare)==0)
{
// update TOC-delta entry
TEntry& entry=iEntries[i];
if (entry.handle==aHandle && entry.ref != anOffset)
{
PutDeltaL(i,aHandle,anOffset);
entry.ref=anOffset;
}
return;
}
// update TOC-base entry
if (aCheck==EWrite || DoAtL(aHandle)!=anOffset)
PutBaseL(aHandle,anOffset);
}
TInt CPermanentStoreToc::GetL(TInt aHandle)
{
__ASSERT_DEBUG(aHandle>0,User::Invariant());
TEntry& entry=FetchL(aHandle);
return entry.ref;
}
TInt CPermanentStoreToc::Set(TInt aHandle,TInt anOffset)
{
__ASSERT_DEBUG(aHandle!=0,User::Invariant());
TEntry* entry=Entry(aHandle);
__ASSERT_DEBUG((entry!=NULL) && (entry->handle==aHandle),User::Invariant());
aHandle&=KMaskStreamIdValue;
entry->handle=aHandle;
entry->ref=anOffset;
Changed();
return aHandle;
}
CPermanentStoreToc::CPermanentStoreToc(TStreamPos aBase,TStreamExchange& aHost)
: /*iPrimary(0),iAvail(0),iCount(0),*/iEntries(KTocGranularity),
iBase(aBase),iHost(&aHost),iOff(KFrameNonexistent16)/*,iExt(0)*/,
iTocOff(KFrameNonexistent16)/*,iTocExt(0),iWindow(0)*/
{}
CPermanentStoreToc::~CPermanentStoreToc()
{
iEntries.Close();
}
void CPermanentStoreToc::ConstructL(TInt aToc,TInt aBaseReloc)
//
// Read and validate the TOC header at aToc.
// aBaseReloc may contain a relocated TOC-base offset, which should override the one in a TOC-delta
//
{
if (aToc==0)
return;
__ASSERT_DEBUG(aToc>0&&iEntries.Count()==0,User::Invariant());
RFrame16Buf buf(Base());
aToc+=KOffsetTocHeader;
buf.OpenL(Host(),aToc,EFrameDescriptive16|buf.ERead);
buf.PushL();
RReadStream stream(&buf);
STocHead h;
stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency
if ((h.primary&~(KMaskStreamIdValue|static_cast<TUint>(KTocDelta)))!=0||
h.avail>0||(h.avail&KMaskHandleClear)!=0||(h.count&~KMaskHandleIndex)!=0)
__LEAVE(KErrCorrupt);
//
iPrimary=TInt(h.primary) & ~KTocDelta;
iAvail=TInt(h.avail);
iCount=TInt(h.count);
iOff = aToc;
//
if (h.primary & KTocDelta)
{
// This is a TOC-delta
aToc = InternalizeL(stream, aBaseReloc) + KOffsetTocHeader;
// Now locate and validate the base TOC
buf.Release();
buf.OpenL(Host(),aToc,EFrameDescriptive16|buf.ERead);
stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency
if ((h.primary&~KMaskStreamIdValue)!=0||(h.count&~KMaskHandleIndex)!=0||
h.avail>0||(h.avail&KMaskHandleClear)!=0||TInt(h.count)>iCount)
__LEAVE(KErrCorrupt);
}
TInt size = KSizeTocEntry*TInt(h.count);
// Eagerly load the first few TOC entries into the cache
// This is good as it almost certainly lies in the file buffer already
if (size>0)
stream.ReadL(&iBuf[0],Min(size,TInt(KSizeTocBuf)));
size-=KOffsetTocHeader;
if (buf.SizeL()!=size)
__LEAVE(KErrCorrupt);
iTocOff=aToc;
iWindow=aToc-KOffsetTocHeader;
iTocExt=aToc+size;
if (iExt == 0)
iExt = iTocExt; // set extent for non-delta TOC
//
CleanupStack::PopAndDestroy(&buf);
}
TInt CPermanentStoreToc::InternalizeL(RReadStream& aIn, TInt aBaseReloc)
//
// Load and validate the delta-toc table
//
{
TInt tocoff = aIn.ReadInt32L();
if (aBaseReloc != KFrameNonexistent16)
tocoff = aBaseReloc - KOffsetTocHeader;
if (tocoff<0||tocoff>=iOff)
__LEAVE(KErrCorrupt);
iMagic = aIn.ReadUint16L();
if (!IsDelta())
__LEAVE(KErrCorrupt);
TInt n = aIn.ReadUint8L();
TInt size = -KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry * n;
if (aIn.Source()->SizeL()!=size)
__LEAVE(KErrCorrupt);
iExt = iOff + size;
TInt last = 0;
while (--n >= 0)
{
STocEntry e;
aIn.ReadL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency
if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&(e.ref<KFrameNonexistent16)||
e.handle<0&&(e.ref>0||(e.ref&KMaskHandleClear)!=0))
__LEAVE(KErrCorrupt);
TInt i = e.handle&KMaskHandleIndex;
if (i <= last || i > iCount)
__LEAVE(KErrCorrupt);
last = i;
TEntry entry;
entry.handle = TInt(e.handle);
entry.ref = TInt(e.ref);
User::LeaveIfError(iEntries.Append(entry));
}
return tocoff;
}
TInt CPermanentStoreToc::DeltaL(RFrame16Buf& aBuf,TInt aExtent,const STocHead& aHead) const
//
// Write the TOC-delta
//
{
TInt off=aBuf.ExtendL(Host(),aExtent,EFrameDescriptive16|aBuf.EWrite);
RWriteStream out(&aBuf);
out.WriteL((TUint8*)&aHead,sizeof(STocHead)); // platform dependency
out.WriteInt32L(iTocOff - KOffsetTocHeader);
TInt n = iEntries.Count();
TInt magic = iMagic;
if (n > KTocDeltaMagic)
magic += n - KTocDeltaMagic;
__ASSERT_DEBUG(magic <= KMaxTocDeltaMagic,User::Invariant());
__ASSERT_DEBUG(n <= (TInt)KMaxTUint8, User::Invariant());
out.WriteUint16L(magic);
out.WriteUint8L(n);
for (int i = 0; i < n; ++i)
{
const TEntry& entry = iEntries[i];
STocEntry e;
e.handle=TInt32(entry.handle);
e.ref=TInt32(entry.ref);
out.WriteL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency
}
return off;
}
TInt CPermanentStoreToc::RewriteL(RFrame16Buf& aBuf,TInt aExtent,const STocHead& aHead) const
//
// Write the TOC-base
//
{
const TInt KElementsRewrite=304;
const TInt KSizeRewriteBuf=KElementsRewrite*KSizeTocEntry;
TUint8 toc[KSizeRewriteBuf];
RFrame16Buf buf(Base());
buf.PushL();
TInt oldsize = 0;
TInt window = 0;
if (iTocOff>=0)
{
oldsize = iTocExt-iTocOff+KOffsetTocHeader;
window = iWindow + KOffsetTocHeader - iTocOff;
if (oldsize <= Min(KSizeTocBuf,KSizeRewriteBuf) && window == 0)
{
// the old TOC is already in the toc-base cache, no need to read it
Mem::Copy(&toc[0],&iBuf[0],oldsize);
oldsize = 0; // this prevents the read request
}
else
{
buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.ERead);
buf.SeekL(buf.ERead,TStreamPos(-KOffsetTocHeader));
}
}
RReadStream in(&buf);
// defer the initialisation of the write in roder to improve file buffering performance
RWriteStream out(&aBuf);
TInt off=-1;
TInt size=iCount*KSizeTocEntry;
for (TInt base=0,delta=0;base<size;base+=KSizeRewriteBuf)
{
// fill buffer with old TOC data
if (base < oldsize)
in.ReadL(&toc[0],Min(KSizeRewriteBuf,oldsize-base));
// apply changes to this block
for (TInt n=iEntries.Count();delta<n;++delta)
{
const TEntry& entry=iEntries[delta];
TInt pos = (entry.handle&KMaskHandleIndex)*KSizeTocEntry - KSizeTocEntry - base;
__ASSERT_DEBUG(pos>=0,User::Invariant());
if (pos>=KSizeRewriteBuf)
break;
STocEntry e;
e.handle=TInt32(entry.handle);
e.ref=TInt32(entry.ref);
Mem::Copy(&toc[pos],(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry);
pos += base - window;
if (TUint(pos) < TUint(KSizeTocBuf))
Mem::Copy(MUTABLE_CAST(TUint8*,&iBuf[pos]),(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry);
}
// write buffer to file
if (off == -1)
{
// initialise writing
off=aBuf.ExtendL(Host(),aExtent,EFrameDescriptive16|aBuf.EWrite);
out.WriteL((TUint8*)&aHead,sizeof(STocHead)); // platform dependency
}
out.WriteL(&toc[0],Min(KSizeRewriteBuf,size-base));
}
//
CleanupStack::PopAndDestroy(&buf);
return off;
}
CPermanentStoreToc::TEntry* CPermanentStoreToc::Entry(TInt aHandle)
{
TEntry e;
e.handle=aHandle;
TInt i;
if (iEntries.FindInOrder(e,i,&TEntry::Compare)!=0)
return NULL;
//
TEntry& entry=iEntries[i];
// allow entry to return a pointer even if not an exact match
return &entry;
}
CPermanentStoreToc::TEntry& CPermanentStoreToc::FetchL(TInt aHandle)
{
TEntry e;
e.handle=aHandle;
TInt i;
if (iEntries.FindInOrder(e,i,&TEntry::Compare)!=0)
{
e.ref=DoAtL(aHandle);
User::LeaveIfError(iEntries.Insert(e,i));
}
TEntry& entry=iEntries[i];
if (entry.handle!=aHandle)
__LEAVE(KErrNotFound);
//
return entry;
}
CPermanentStoreToc::TEntry& CPermanentStoreToc::DoAllocL()
{
TInt handle=iAvail;
TEntry* entry;
if (handle==0)
{
__ASSERT_DEBUG(iEntries.Count()==0||(iEntries[iEntries.Count()-1].handle&KMaskHandleIndex)<=iCount,User::Invariant());
User::LeaveIfError(iEntries.Append(TEntry()));
entry=&iEntries[iEntries.Count()-1];
handle=++iCount;
}
else
{
entry=&FetchL(handle);
handle=(handle+KIncHandleGen)&KMaskStreamIdValue;
//
TInt avail=entry->ref;
if (avail>0||(avail&KMaskHandleClear)!=0)
__LEAVE(KErrCorrupt);
//
iAvail=avail;
}
entry->handle=handle;
Changed();
return *entry;
}
TInt CPermanentStoreToc::DoAtL(TInt aHandle) const
{
TInt off=iTocOff;
if (off<0)
__LEAVE(KErrNotFound);
//
off+=-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex);
__ASSERT_DEBUG(off>=iTocOff-KOffsetTocHeader,User::Invariant());
if (off>=iTocExt)
__LEAVE(KErrNotFound);
//
TInt window=iWindow;
if (off-window<0||off-window>=KSizeTocBuf)
{
TInt len=iTocOff-KOffsetTocHeader;
window=Max(len,Min(off-KBackTocBuf,iTocExt-KSizeTocBuf));
len=Min(iTocExt-len,TInt(KSizeTocBuf));
//
RFrame16Buf buf(Base());
buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.ERead);
buf.PushL();
buf.SeekL(buf.ERead,TStreamPos(window-iTocOff));
RReadStream stream(&buf);
MUTABLE_CAST(TInt&,iWindow)=iTocExt;
stream.ReadL(MUTABLE_CAST(TUint8*,&iBuf[0]),1); // assume half decent read-ahead buffering
stream.ReadL(MUTABLE_CAST(TUint8*,&iBuf[1]),len-1);
MUTABLE_CAST(TInt&,iWindow)=window;
CleanupStack::PopAndDestroy();
}
STocEntry e;
e.handle=aHandle;
Mem::Copy((TUint8*)&e+KSizeHandleIndex,&iBuf[off-window],KSizeTocEntry); // platform dependency
if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&(e.ref<KFrameNonexistent16)||
e.handle<0&&(e.ref>0||(e.ref&KMaskHandleClear)!=0))
__LEAVE(KErrCorrupt);
//
if (TInt(e.handle)!=aHandle)
__LEAVE(KErrNotFound);
//
return TInt(e.ref);
}
void CPermanentStoreToc::PutBaseL(TInt aHandle,TInt aReference)
//
// Update a TOC-base entry
//
{
__ASSERT_DEBUG(iTocOff>=0,User::Invariant());
TInt pos=-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex);
RFrame16Buf buf(Base());
buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.EWrite);
buf.PushL();
buf.SeekL(buf.EWrite,TStreamPos(pos));
RWriteStream stream(&buf);
STocEntry e;
e.handle=aHandle;
e.ref=aReference;
stream.WriteL((TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency
CleanupStack::PopAndDestroy();
TInt off=pos+iTocOff-iWindow;
if (off>=0&&off<KSizeTocBuf)
Mem::Copy(&iBuf[off],(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency
}
void CPermanentStoreToc::PutDeltaL(TInt aPos, TInt aHandle, TInt aReference)
//
// Update a single stream reference IN-PLACE in the TOC-delta
//
{
__ASSERT_DEBUG(HasDelta(),User::Invariant());
__ASSERT_DEBUG(iOff>=0,User::Invariant());
RFrame16Buf buf(Base());
buf.Set(Host(),iOff,iExt,EFrameDescriptive16|buf.EWrite);
buf.PushL();
buf.SeekL(buf.EWrite,TStreamPos(-KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry*aPos));
RWriteStream stream(&buf);
STocEntry e;
e.handle=aHandle;
e.ref=aReference;
stream.WriteL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency
CleanupStack::PopAndDestroy();
}
void CPermanentStoreToc::PutTocL(TInt aTocBase, TPut aCheck)
//
// Update the base TOC link in the TOC-delta
//
{
__ASSERT_DEBUG(HasDelta(),User::Invariant());
__ASSERT_DEBUG(iOff>=0,User::Invariant());
RFrame16Buf buf(Base());
buf.Set(Host(),iOff,iExt,EFrameDescriptive16|buf.EWrite|buf.ERead);
buf.PushL();
buf.SeekL(buf.EWrite|buf.ERead,TStreamPos(-KOffsetTocHeader));
RReadStream in(&buf);
if (aCheck==EWrite || in.ReadInt32L() != aTocBase)
{
RWriteStream out(&buf);
out.WriteInt32L(aTocBase);
}
CleanupStack::PopAndDestroy();
}
// Used prior to PutL to determine exactly where in the file the TOC update will be written.
// This may be used to eliminate the pre-put write of the relocation information in the file
// header in situations where the write is entirely within an atomic file block.
//
// The detailed numbers used to offset into the TOCs are dependant on the TOC formats, as
// implicated by other members of this class
TInt CPermanentStoreToc::RefSpan(TInt aHandle,TInt& aLength)
{
__ASSERT_DEBUG(!IsVirtual(),User::Invariant());
__ASSERT_DEBUG(aHandle>0,User::Invariant());
if (aHandle == KHandleTocBase)
{ // locate the TOC-base link
__ASSERT_DEBUG(HasDelta(),User::Invariant());
__ASSERT_DEBUG(iOff>=0,User::Invariant());
aLength = sizeof(TInt32);
return RFrame16Buf::Position(Base(), iOff - KOffsetTocHeader).Offset();
}
TEntry e;
e.handle=aHandle;
TInt ix;
if (iEntries.FindInOrder(e, ix, &TEntry::Compare) == 0)
{ // locate TOC-delta entry
TEntry& entry=iEntries[ix];
if (entry.handle==aHandle)
{
__ASSERT_DEBUG(HasDelta(),User::Invariant());
__ASSERT_DEBUG(iOff>=0,User::Invariant());
aLength = KSizeTocDeltaEntry;
return RFrame16Buf::Position(Base(),iOff - KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry*ix).Offset();
}
}
// locate the TOC-base entry
__ASSERT_DEBUG(iTocOff>=0,User::Invariant());
aLength = KSizeTocEntry;
return RFrame16Buf::Position(Base(),iTocOff-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex)).Offset();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////// RPermanentStoreTocIter ////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RPermanentStoreTocIter::RPermanentStoreTocIter(const CPermanentStoreToc& aTable)
: iTable(aTable), iBuf(aTable.Base()), iDelta(0), iDeltaEnd(0)
{
TInt off=aTable.iTocOff;
__ASSERT_DEBUG(off>=KFrameNonexistent16,User::Invariant());
TInt ext=off<0?off:aTable.iTocExt;
iBuf.Set(aTable.Host(),off,ext,EFrameDescriptive16|iBuf.ERead);
}
void RPermanentStoreTocIter::Release()
{
iBuf.Release();
}
void RPermanentStoreTocIter::ResetL()
{
iNext = iIndex = 1;
if (iBuf.Offset()<0)
{
iCount=0;
return;
}
//
RReadStream stream(&iBuf);
STocHead h;
stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency
if (h.primary<0||(h.primary&~KMaskStreamIdValue)!=0||
h.avail>0||(h.avail&KMaskHandleClear)!=0||(h.count&~KMaskHandleIndex)!=0)
__LEAVE(KErrCorrupt);
//
const CPermanentStoreToc& table=iTable;
if (table.HasDelta())
{
TInt c = table.iEntries.Count();
if (c)
{
iDelta = &table.iEntries[0];
iDeltaEnd = iDelta + c;
}
iIndex = 0;
}
iCount = iTable.iCount;
}
TBool RPermanentStoreTocIter::NextL(TEntry& anEntry)
{
TInt i=iIndex;
__ASSERT_DEBUG(iCount>=0,User::Invariant());
if (i == 0)
{
// report TOC-base as a 'stream'
anEntry.handle = KHandleTocBase;
anEntry.ref = iTable.iTocOff;
iIndex = 1;
return ETrue;
}
__ASSERT_DEBUG(i>0,User::Invariant());
if (i>iCount)
return EFalse;
//
const TEntry* d = iDelta;
if (d != iDeltaEnd && (d->handle&KMaskHandleIndex) == i)
{
anEntry = *d;
iDelta = d+1;
iIndex = i+1;
return ETrue;
}
//
RReadStream stream(&iBuf);
TInt skip = i - iNext;
if (skip > 0)
stream.ReadL(KSizeTocEntry * skip);
STocEntry e;
e.handle=i;
stream.ReadL((TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency
if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&e.ref<KFrameNonexistent16||
e.handle<0&&(e.ref>0||(e.ref&KMaskHandleClear)!=0))
__LEAVE(KErrCorrupt);
//
iNext = iIndex = i+1;
anEntry.handle=TInt(e.handle);
anEntry.ref=TInt(e.ref);
return ETrue;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////// TPermanentStoreCache //////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const TPermanentStoreCache::TItem* TPermanentStoreCache::At(TInt aHandle) const
{
const TItem* item=&iItems[0];
TInt i=1;
TInt bit=1;
while (item->handle!=aHandle)
{
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
TInt step=(aHandle&bit)?i+1:i;
bit<<=1;
i+=step;
if (i>EItems)
return NULL;
//
item+=step;
}
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
return item;
}
void TPermanentStoreCache::Relocated(TInt aHandle,TInt anOffset)
{
const TItem* item=At(aHandle);
if (item)
{
// update the cache item with the new offset
// As 'extent' may only be a partial extent of a fragmented frame
// which is no longer fragmented, we cannot simply shift this by the same
// amount as 'offset'. Simplest to reset this to 'unknown', i.e. 0
const_cast<TItem*>(item)->offset = anOffset;
const_cast<TItem*>(item)->extent = 0;
}
}
void TPermanentStoreCache::Put(const TItem* anItem,TInt anOffset,TInt anExtent)
{
TInt handle=anItem->handle;
TItem* item=&iItems[0];
TInt i=1;
TInt bit=1;
while (item != anItem)
{
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
TInt step=(handle&bit)?i+1:i;
bit<<=1;
i+=step;
__ASSERT_DEBUG(i<=EItems,User::Invariant());
item+=step;
}
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
for(;;)
{
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
TInt step=(handle&bit)?i+1:i;
bit<<=1;
i+=step;
if (i>EItems)
break;
//
TItem* hole=item;
item+=step;
*hole=*item;
};
item->handle=handle;
item->offset=anOffset;
item->extent=anExtent;
__ASSERT_DEBUG(At(handle)==item,User::Invariant());
}
void TPermanentStoreCache::Add(TInt aHandle,TInt anOffset,TInt anExtent)
{
TItem* item=&iItems[0];
TInt i=1;
TInt bit=1;
for(;;)
{
__ASSERT_DEBUG(item==&iItems[i-1]&&item->handle!=aHandle,User::Invariant());
TInt step=(aHandle&bit)?i+1:i;
bit<<=1;
i+=step;
if (i>EItems)
break;
//
TItem* hole=item;
item+=step;
*hole=*item;
};
item->handle=aHandle;
item->offset=anOffset;
item->extent=anExtent;
__ASSERT_DEBUG(At(aHandle)==item,User::Invariant());
}
void TPermanentStoreCache::Remove(TInt aHandle)
{
TItem* item=&iItems[0];
TInt i=1;
TInt bit=1;
while (item->handle!=aHandle)
{
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
TInt step=(aHandle&bit)?i+1:i;
bit<<=1;
i+=step;
if (i>EItems)
return;
//
item+=step;
}
TItem* hole=item;
TInt mask=bit-1;
while (item!=&iItems[0])
{
__ASSERT_DEBUG(item==&iItems[i-1],User::Invariant());
TInt step=i;
bit>>=1;
i>>=1;
step-=i;
item-=step;
//
if (((aHandle^item->handle)&mask)==0)
{
*hole=*item;
hole=item;
mask=bit-1;
}
}
hole->handle=0;
__ASSERT_DEBUG(i==1&&At(aHandle)==NULL,User::Invariant());
}
void TPermanentStoreCache::Invalidate()
{
for (TItem* item=&iItems[0],*end=item+EItems;item<end;++item)
item->handle=0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////// CPermanentStoreCoord //////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Returns the file system type, according to what the "file write" operation guarantees.
//The return result could be one of:
// - ESimple - "single byte write" operations are atomic;
// - EBlockAtomic - "block/sector write" operations are atomic;
// - ETransactional - transactional file system;
CPermanentStoreCoord::TFileQoS CPermanentStoreCoord::FileQoSL()
{
//Uncomment, if you want FileQoSL() to return always EBlockAtomic/EBlockAtomic.
//iFileQos = ETransactional;
//iFileQos = EBlockAtomic;
//return iFileQos;
//
if (iFileQos == EUnknown) //get the file sytem type, if iFileQos is not set yet
{
TStreamExchange &se = Host();
RFileBuf *sb = static_cast<RFileBuf *>(se.HostL());
RFile &f = sb->File();
TInt dn;
TDriveInfo di;
User::LeaveIfError(f.Drive(dn, di));
iFileQos = (di.iDriveAtt & KDriveAttTransaction) ? ETransactional : ESimple;
if(iFileQos == ESimple && IsBlockAtomicL(dn))
{
iFileQos = EBlockAtomic;
}
}
return iFileQos;
}
//The function returns true, if the file system guarantees atomic "block write" operations on aDriveNo.
//It is not the most effective implementation at the moment (creates/closes a file session),
//probably TDriveInfo::iType can be used in a more effective implementation.
TBool CPermanentStoreCoord::IsBlockAtomicL(TInt aDriveNo) const
{
__ASSERT_DEBUG(aDriveNo >= EDriveA && aDriveNo <= EDriveZ, User::Invariant());
RFs fs;
CleanupClosePushL(fs);
User::LeaveIfError(fs.Connect());
TVolumeIOParamInfo volInfo;
TInt err = fs.VolumeIOParam(aDriveNo, volInfo);
CleanupStack::PopAndDestroy(&fs);
//If VolumeIOParam() succeeds, the media block size is >= 512 bytes and the media block size is power of two - report
//that the media supports atomic "block write" operations.
return err == KErrNone && volInfo.iBlockSize >= KDefaultMediaBlockSize && (volInfo.iBlockSize & (volInfo.iBlockSize - 1)) == 0;
}
TStreamPos CPermanentStoreCoord::LimitL()
{
TableL();
return RFrame16Buf::Position(Base(),iExt);
}
TStreamId CPermanentStoreCoord::PrimaryL()
{
return TStreamId(TableL().Primary());
}
void CPermanentStoreCoord::ChangedL()
{
ConsolidateL().Changed();
}
TBool CPermanentStoreCoord::CommitL(TStreamId aPrimary)
{
__ASSERT_DEBUG(IsTrim(),User::Invariant());
if (iExtend!=0)
__LEAVE(KErrInUse);
//
CPermanentStoreToc& table=ConsolidateL();
if (!table.IsVirtual())
return EFalse;
//
iState|=EClip;
TInt toc=table.RealizeL(aPrimary.Value(),iExt);
//
TPermanentStoreHeader header(toc);
header.SetBackupToc(iToc);
MStreamBuf* buf=BeginL(header);
buf->SynchL();
// it's done, wrap things up
Host().Share(buf);
iToc=toc;
table.Adopt(toc,aPrimary.Value());
++iGen;
iState=EBackup;
iExt=table.Extent();
return ETrue;
}
TBool CPermanentStoreCoord::RevertL(TStreamId& aPrimary)
{
__ASSERT_ALWAYS(iAccess==0,Panic(EStoreInUse));
// can't revert things under people's feet
CPermanentStoreToc* table=iTable;
iTable=NULL;
iCache.Invalidate();
if (table==NULL||!table->IsVirtual())
{
__ASSERT_DEBUG(table==NULL||aPrimary==TStreamId(table->Primary()),User::Invariant());
delete table;
return EFalse;
}
//
aPrimary=TStreamId(table->Primary());
delete table;
iState|=EClip;
//
TStreamExchange& host=Host();
MStreamBuf* buf=host.HostL();
host.Release();
buf->SynchL();
host.Share(buf);
return ETrue;
}
TStreamId CPermanentStoreCoord::ExtendL()
{
return TStreamId(ConsolidateL().AllocL(KFrameNonexistent16));
}
void CPermanentStoreCoord::DeleteL(TStreamId anId)
{
TInt handle=anId.Value();
ConsolidateL().FreeL(handle);
iCache.Remove(handle);
}
CPermanentStoreCoord::CPermanentStoreCoord(TStreamPos aBase,TStreamExchange& aHost)
: iBase(aBase),iHost(&aHost), iFileQos(EUnknown)
{}
//
// Read and analyse the store header.
// The whole header (14 bytes) is read from the file and:
// - If the dirty bit is set, the backup TOC will be used;
// - If the dirty bit is not set, and the backup TOC ref is not the same as the TOC ref,
// then it means the the backup TOC ref has not been written successfully, so the TOC ref will be used;
void CPermanentStoreCoord::InternalizeL(RReadStream& aStream)
{
if (iTable!=NULL)
__LEAVE(KErrNotReady);
//
iState=EClip;
TPermanentStoreHeader header;
aStream.ReadL(header.Ptr(),KPermanentStoreHeaderLength);
// determine where the toc lives
TInt toc=header.BackupToc();
if (header.IsDirty())
{
iReloc=0;
iTarget=0;
}
else
{
TInt handle=header.Handle();
TInt ref=header.Reference();
if (handle==0&&toc!=ref)
{ // toc pointer not backed up, validate as if it was
toc=ref;
iState|=EBackup;
header.SetBackupToc(toc);
}
if (!header.IsValid()) // not a permanent store or damaged beyond recognition
__LEAVE(KErrNotSupported);
//
if (toc<0||((handle&~KMaskStreamIdValue)!=0&&handle!=KHandleTocBase)||ref<0||(handle!=0&&ref>=toc+KOffsetTocHeader))
__LEAVE(KErrCorrupt); // integrity compromised
//
iReloc=handle;
iTarget=ref;
}
//
if (iToc!=0 && iToc!=toc) // refresh produced a different toc
__LEAVE(KErrCorrupt);
iToc=toc;
}
CPermanentStoreCoord::~CPermanentStoreCoord()
{
__ASSERT_ALWAYS(iRefs==0,Panic(EStoreInUse));
delete iTable;
}
void CPermanentStoreCoord::CanExtendL()
{
__ASSERT_DEBUG(IsTrim(),User::Invariant());
if (iExtend!=0)
__LEAVE(KErrInUse);
//
ConsolidateL();
}
TInt CPermanentStoreCoord::DoCreateL()
{
__ASSERT_DEBUG(IsTrim()&&iReloc==0&&iExtend==0,User::Invariant());
TInt handle=Table().AllocL();
iExtend=handle;
Inc();
++iAccess;
return handle;
}
void CPermanentStoreCoord::DoReplaceL(TInt aHandle)
{
__ASSERT_DEBUG(IsTrim()&&iReloc==0&&iExtend==0,User::Invariant());
TInt off=Table().GetL(aHandle);
const TItem* item=iCache.At(aHandle);
__ASSERT_DEBUG(item==NULL||iTable!=NULL&&item->handle==aHandle,User::Invariant());
if (item==NULL)
iCache.Add(aHandle,off,Min(off,0));
iExtend=aHandle;
Inc();
++iAccess;
}
TInt CPermanentStoreCoord::DoOpenL(TInt& anOffset,TInt aHandle)
{
const TItem* item=iCache.At(aHandle);
__ASSERT_DEBUG(item==NULL||iTable!=NULL&&item->handle==aHandle,User::Invariant());
TInt off;
TInt ext;
if (item==NULL)
{
off=TableL().AtL(aHandle);
if (iReloc==aHandle)
{
TInt trg=iTarget;
if (trg==off)
iReloc=0;
else
off=trg;
}
ext=Min(off,0); // ensures ext==off for empty streams
iCache.Add(aHandle,off,ext);
}
else
{
off=item->offset;
ext=item->extent;
}
Inc();
++iAccess;
anOffset=off;
return ext;
}
void CPermanentStoreCoord::DoRelease(TInt aHandle,TInt anOffset,TInt anExtent)
{
__ASSERT_DEBUG(aHandle!=0,User::Invariant());
Dec();
--iAccess;
if (anExtent==0)
{ // failed to commit the extending stream
__ASSERT_DEBUG(aHandle==iExtend&&IsTrim(),User::Invariant());
iState|=EClip;
iExtend=0;
if (aHandle<0)
Table().Cancel(aHandle);
}
else
{
const TItem* item=iCache.At(aHandle);
if (item!=NULL&&item->offset==anOffset&&anExtent>item->extent)
iCache.Put(item,anOffset,anExtent);
}
}
TInt CPermanentStoreCoord::DoCommit(TInt aHandle,TInt anOffset,TInt anExtent)
{
__ASSERT_DEBUG(aHandle!=0&&aHandle==iExtend&&(anExtent>=iExt||anOffset==anExtent),User::Invariant());
aHandle=Table().Set(aHandle,anOffset);
if (anExtent<0)
iCache.Remove(aHandle);
else
{
iExt=anExtent;
const TItem* item=iCache.At(aHandle);
if (item==NULL)
iCache.Add(aHandle,anOffset,anExtent);
else
iCache.Put(item,anOffset,anExtent);
}
iExtend=0;
return aHandle;
}
CPermanentStoreToc& CPermanentStoreCoord::TableL()
{
CPermanentStoreToc* table=iTable;
if (table==NULL)
{
table=CPermanentStoreToc::NewL(Base(),Host(),iToc,iReloc==KHandleTocBase?iTarget:KFrameNonexistent16);
iExt=table->Extent();
iTable=table;
}
return *table;
}
CPermanentStoreToc& CPermanentStoreCoord::ConsolidateL()
{
CPermanentStoreToc& table=TableL();
if (iReloc!=0)
{
table.PutL(iReloc,iTarget,CPermanentStoreToc::ETestBeforeWrite);
iCache.Relocated(iReloc,iTarget);
iReloc=0;
}
return table;
}
//After stream relocation, the stream entry in the TOC has to be updated with the new stream position.
//If the file system is not transactional or if the file system is "block atomic", but the stream entry is split
//on a block/sector boundary, then the stream handle will be stored in the permanent file store header, in case
//if the TOC entry update fails.
void CPermanentStoreCoord::RelocateL(TInt aHandle,TInt anOffset)
{
__ASSERT_DEBUG(!Accessed(),User::Invariant());
__ASSERT_DEBUG(iReloc==0,User::Invariant());
TBool updateStoreHeader = ETrue;
TFileQoS fileQos = FileQoSL();
if(fileQos == ETransactional)
{
updateStoreHeader = EFalse;
}
else if(fileQos == EBlockAtomic)
{
TInt dataLen = 0;
TInt writePos = iTable->RefSpan(aHandle, dataLen);
__ASSERT_DEBUG(writePos >= 0 && dataLen > 0, User::Invariant());
TInt startSectorAddr = writePos & ~(KDefaultMediaBlockSize - 1);
TInt endSectorAddr = (writePos + dataLen - 1) & ~(KDefaultMediaBlockSize - 1);
if(startSectorAddr == endSectorAddr)
{
updateStoreHeader = EFalse;
}
}
if (updateStoreHeader)
{
TPermanentStoreHeader header(iToc,aHandle,anOffset);
Host().Share(BeginL(header));
iReloc=aHandle;
iTarget=anOffset;
}
++iGen;
iTable->PutL(aHandle,anOffset,CPermanentStoreToc::EWrite);
iCache.Relocated(aHandle,anOffset);
iReloc=0;
}
void CPermanentStoreCoord::MoveL(TInt aToc,TInt anExtent)
{
__ASSERT_DEBUG(iReloc==0,User::Invariant());
CPermanentStoreToc& table=Table();
TPermanentStoreHeader header(aToc);
header.SetBackupToc(iToc);
Host().Share(BeginL(header));
// update data structures but defer the write
iToc=aToc;
TInt ext=table.Extent();
table.Move(aToc,anExtent);
iState|=EBackup;
if (iExt==ext)
{
iExt=anExtent;
iState|=EClip;
}
}
//
// Starts a pseudo-atomic update of the permanent file store header.
//
// For the effect to be 'atomic', writes need to meet the following requirements:
// 1. When updating n bytes using a single write, bytes 2 through n remain unchanged unless the first byte also changes.
// 2. Changes associated with successive write requests happen in strict sequence.
// 3. Updating a single byte is atomic.
//
// Also, a failure to write to a location shall be reported no later than on the next
// write to that or a different location, or on buffer synchronisation.
//
// The preconditions of the operation are:
// - all stream insert/delete/relocate operations completed, file - updated;
// - the TOC reference in the file store header points to the current (valid) TOC, which does not include the most recent
// changes, since the last commit;
// - the in-memory backup TOC reference updated and made equal to the file stored TOC reference;
//
// The procedure consists of 3 "file-write" steps:
// -1- write the backup TOC ref (4 bytes) to the permanent file store header.
// if this operation fails, when the store is reopened, the TOC ref will be used;
// -2- set the dirty bit and write the whole file store header (14 bytes).
// If this operation fails, but the dirty bit has been successfully set, the backup TOC ref will be used,
// when the store is reopened;
// -3- clear the dirty bit (1 byte "file write" op). The commit operation has completed successfully;
MStreamBuf* CPermanentStoreCoord::BeginL(TPermanentStoreHeader& aHeader)
{
__ASSERT_DEBUG(!aHeader.IsDirty()&&aHeader.BackupToc()==iToc&&iReloc==0,User::Invariant());
MStreamBuf& buf=*Host().HostL();
buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset);
TFileQoS fileQos = FileQoSL();
if (fileQos<EBlockAtomic)
{
if (iState&EBackup)
{ // we've yet to write the backup toc, do that before clobbering the header
buf.WriteL(aHeader.Ptr(),KPermanentStoreBackupLength);
buf.SeekL(buf.EWrite,EStreamBeginning);
buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset);
iState&=~EBackup;
}
// first write--dirty flag set in the first byte, backup toc unchanged, new header otherwise
aHeader.MarkDirty();
}
Host().Release(); // from this point onwards any failure results in store shutdown
//
buf.WriteL(aHeader.Ptr(),KPermanentStoreHeaderLength);
if (fileQos<EBlockAtomic)
{
buf.SeekL(buf.EWrite,EStreamBeginning);
buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset);
aHeader.SetBackupToc(iToc);
// second write--single byte write to clear the dirty flag, no change otherwise
buf.WriteL(aHeader.Ptr(),1);
}
// at this point synchronisation is atomic; ie, if successful the change
// has been recorded, and failure means it will never happen
return &buf;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////// HPermanentStoreBuf ////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HPermanentStoreBuf* HPermanentStoreBuf::CreateL(CPermanentStoreCoord& aCoord,TStreamId& anId,TInt aMode)
{
HPermanentStoreBuf* buf=ExtendLC(aCoord,aMode);
TInt handle=aCoord.DoCreateL();
buf->iHandle=handle;
CleanupStack::Pop();
anId=TStreamId(handle&KMaskStreamIdValue);
return buf;
}
HPermanentStoreBuf* HPermanentStoreBuf::ReplaceL(CPermanentStoreCoord& aCoord,TStreamId anId,TInt aMode)
{
HPermanentStoreBuf* buf=ExtendLC(aCoord,aMode);
TInt handle=anId.Value();
aCoord.DoReplaceL(handle);
buf->iHandle=handle;
CleanupStack::Pop();
return buf;
}
HPermanentStoreBuf* HPermanentStoreBuf::OpenL(CPermanentStoreCoord& aCoord,TStreamId anId,TInt aMode)
{
HPermanentStoreBuf* buf=NewLC(aCoord);
TInt handle=anId.Value();
TInt off=KFrameNonexistent16;
TInt ext=aCoord.DoOpenL(off,handle);
buf->iHandle=handle;
if (ext!=0)
buf->RFrame16Buf::Set(aCoord.Host(),off,ext,EFrameData16|aMode);
else
buf->RFrame16Buf::OpenL(aCoord.Host(),off,EFrameData16|aMode);
CleanupStack::Pop();
return buf;
}
HPermanentStoreBuf::~HPermanentStoreBuf()
{
TInt handle=iHandle;
if (handle!=0)
Coord().DoRelease(handle,Offset(),Extent());
RFrame16Buf::DoRelease();
}
HPermanentStoreBuf* HPermanentStoreBuf::NewLC(CPermanentStoreCoord& aCoord)
{
HPermanentStoreBuf* buf=new(ELeave) HPermanentStoreBuf(aCoord);
buf->PushL();
return buf;
}
HPermanentStoreBuf* HPermanentStoreBuf::ExtendLC(CPermanentStoreCoord& aCoord,TInt aMode)
{
aCoord.CanExtendL();
HPermanentStoreBuf* buf=NewLC(aCoord);
buf->RFrame16Buf::ExtendL(aCoord.Host(),aCoord.iExt,EFrameData16|aMode);
__ASSERT_DEBUG(TStreamPos(aCoord.Host().SizeL())==buf->Position(buf->Offset()),User::Invariant());
return buf;
}
void HPermanentStoreBuf::DoRelease()
{
delete this;
}
void HPermanentStoreBuf::DoSynchL()
{
__ASSERT_DEBUG(iHandle!=0,User::Invariant());
if (IsCommitted())
return;
//
CommitL();
iHandle=Coord().DoCommit(iHandle,Offset(),Extent());
}