--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/store/USTOR/UT_PERM.CPP Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,1441 @@
+// 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());
+ }
+