remotestoragefw/remotefileengine/src/rsfwpermanentstore.cpp
changeset 0 3ad9d5175a89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remotestoragefw/remotefileengine/src/rsfwpermanentstore.cpp	Thu Dec 17 09:07:59 2009 +0200
@@ -0,0 +1,885 @@
+/*
+* Copyright (c) 2004-2006 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:  Provides persistent storage for memory blocks
+*
+*/
+
+
+// This is a simple block based permanent storage, in which transient
+// indices are computed on the fly when all data is sequentially read
+// from the storage.
+//
+// The data is put into "slots" that consist of one or more "blocks".
+// Subsequent updates to the data slots only use the current slot
+// if the required slot size (block count) has not changed.
+//
+// The storage contains a header that may contain any data.
+// In addition, Each slot contains a header (of type TInt) that
+// describes the size of the data in the slot.
+//
+// The file can be compressed for space efficiency.
+//
+// The assumption is that the store is first opened for reading for loading
+// the data, and then it is only opened for writing until the file is closed.
+//
+// (We don't use CRsfwPermanentStore because besides data, we should
+//  also maintain the indices in a stream of its own)
+
+// INCLUDE FILES
+#include <s32strm.h>
+#include <s32buf.h>
+
+#include "rsfwpermanentstore.h"
+#include "mdebug.h"
+
+
+// CONSTANTS
+const TUint KHeaderStart      = 0xbabefade;
+const TInt  KSlotHeaderSize   = 4;          // sizeof(TInt)
+const TInt  KDefaultBlockSize = 128;        // default block size in bytes
+// max value for integer read from the stream, subsequently used to create descriptor
+const TInt  KMaxDescriptorSize = 16384;         
+
+
+// ============================ MEMBER FUNCTIONS ==============================
+
+// ----------------------------------------------------------------------------
+// TFileHeader::ExternalizeL
+// ----------------------------------------------------------------------------
+//
+void TFileHeader::ExternalizeL(RWriteStream& aStream) const
+    {
+    aStream.WriteInt32L(iHeaderStart);
+    aStream.WriteInt32L(iBlockSize);
+    aStream.WriteInt32L(iHeaderSize);
+    aStream << *iHeader;
+    }
+
+// ----------------------------------------------------------------------------
+// TFileHeader::InternalizeL
+// ----------------------------------------------------------------------------
+//
+void TFileHeader::InternalizeL(RReadStream& aStream)
+    {
+    iHeaderStart = aStream.ReadInt32L();
+    iBlockSize = aStream.ReadInt32L();
+    if (iBlockSize < 0 || iBlockSize > KMaxDescriptorSize)
+        {
+        User::Leave(KErrCorrupt);
+        }    
+    iHeaderSize = aStream.ReadInt32L();
+    if (iHeaderSize < 0 || iHeaderSize > KMaxDescriptorSize)
+        {
+        User::Leave(KErrCorrupt);
+        }     
+    iHeader = HBufC8::NewL(aStream, iHeaderSize);
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::NewL
+// ----------------------------------------------------------------------------
+//
+CRsfwPermanentStore* CRsfwPermanentStore::NewL(const TDesC& aPath,
+                                       TInt aHeaderSize,
+                                       TInt aBlockSize)
+    {
+    CRsfwPermanentStore* self = new (ELeave) CRsfwPermanentStore();
+    CleanupStack::PushL(self);
+    self->ConstructL(aPath, aHeaderSize, aBlockSize);
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::~CRsfwPermanentStore
+// ----------------------------------------------------------------------------
+//
+CRsfwPermanentStore::~CRsfwPermanentStore()
+    {
+    ClearFreeBlockLists();
+    iSlots.Close();
+    iFileReadStream.Close();
+    iFileWriteStream.Close();
+    iFile.Close();
+    iFs.Close();
+    delete iFileHeader.iHeader;
+    delete iZeroBlock;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::ConstructL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::ConstructL(const TDesC& aPath,
+                                 TInt aHeaderSize,
+                                 TInt aBlockSize)
+    {
+    DEBUGSTRING(("CRsfwPermanentStore::ConstructL()"));
+    User::LeaveIfError(iFs.Connect());
+    iPath.Copy(aPath);
+
+    iHeaderSize = aHeaderSize;
+    // Offset to the block data
+    iFileHeaderSize = iHeaderSize + sizeof(TFileHeader);
+    TRAPD(err, LoadHeaderL());
+    if (err == KErrNone)
+        {
+        // Read parameters from the existing file, if any
+        iHeaderSize = iFileHeader.iHeaderSize;
+        iBlockSize = iFileHeader.iBlockSize;
+        }
+    else
+        {
+        // There was no existing file
+        DEBUGSTRING(("LoadHeaderL returns %d", err));
+        if (aBlockSize)
+            {
+            iBlockSize = aBlockSize;
+            }
+        else
+            {
+            iBlockSize = KDefaultBlockSize;
+            }
+        iFileHeader.iHeaderStart = KHeaderStart;
+        iFileHeader.iHeaderSize = iHeaderSize;
+        iFileHeader.iBlockSize = iBlockSize;
+        }
+    // Compute the offset to data blocks
+    // (this is 4 bytes too many but that is OK ...)
+    iFileHeaderSize = iHeaderSize + sizeof(TFileHeader);
+    // Make a block of zeroes
+    iZeroBlock = HBufC8::NewL(iBlockSize);
+    iZeroBlock->Des().FillZ();
+    DEBUGSTRING(("ConstructL() done, blocksize=%d", iBlockSize));
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::ResetL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::ResetL(TBool aWriting)
+    {
+    // This function is called by the client
+    // before starting to read or write data slots
+    if (aWriting)
+        {
+        SetFileStateL(EFileStateWriting);
+        // Set write pointer to the end of the file
+        MStreamBuf* streamBuf = iFileWriteStream.Sink();
+        TInt size = streamBuf->SizeL();
+        iWriteBlockNumber = (size - iFileHeaderSize) / iBlockSize;
+        if ((iWriteBlockNumber * iBlockSize) != (size - iFileHeaderSize))
+            {
+            DEBUGSTRING(("ResetL(): file size incorrect (%d)",
+                         size));
+            }
+        }
+    else
+        {
+        // reading
+        SetFileStateL(EFileStateReading);
+        // Skip file header
+        MStreamBuf* streamBuf = iFileReadStream.Source();
+        streamBuf->SeekL(MStreamBuf::ERead,
+                         EStreamBeginning,
+                         iFileHeaderSize);
+        iReadBlockNumber = 0;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::Commit
+// ----------------------------------------------------------------------------
+//
+TInt CRsfwPermanentStore::Commit()
+    {
+    TInt err = KErrNone;
+    // Commit writing
+    if (iFileState == EFileStateWriting)
+        {
+        MStreamBuf* streamBuf = iFileWriteStream.Sink();
+        err = streamBuf->Synch();
+        }
+    return err;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::Purge
+// ----------------------------------------------------------------------------
+//
+TInt CRsfwPermanentStore::Purge()
+    {
+    iFileReadStream.Close();
+    iFileWriteStream.Close();
+    iFile.Close();
+    return iFs.Delete(iPath);
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::CompactL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::CompactL()
+    {
+    // Compact the file by dropping empty slots.
+    // The slot table is also fixed and the free block lists are cleared
+    // to reflect the new file layout.
+
+    // This function must not be called while the file is opened for reading.
+    // It is most efficient to call this function before any data is read
+    // (then there is no slot table to fix)
+
+    // This function must be called before doing GetNextDataL()
+    // (must be followed by ResetL()).
+    // However, this function can be called while doing PutDataL(). 
+
+    DEBUGSTRING(("CompactL()"));
+    SetFileStateL(EFileStateClosed);
+    SetFileStateL(EFileStateReading);
+    
+    TBuf<KMaxPath> dstPath;
+    dstPath.Copy(iPath);
+    dstPath.Append('a');
+    RFile dstFile;
+    User::LeaveIfError(dstFile.Replace(iFs,
+                                       dstPath,
+                                       EFileShareAny | EFileWrite));
+    CleanupClosePushL(dstFile);
+    RFileWriteStream dstFileWriteStream(dstFile);
+    CleanupClosePushL(dstFileWriteStream);
+    MStreamBuf* streamBuf = iFileReadStream.Source();
+
+    // Copy header
+    dstFileWriteStream.WriteL(iFileReadStream, iFileHeaderSize);
+
+    RArray<TSlot> gaps;
+    CleanupClosePushL(gaps);
+
+    TInt blockNumber = 0;
+    TInt lastReadBlockNumber = 0;
+    TInt nextWriteBlockNumber = 0;
+    TInt err = KErrNone;
+    while (err == KErrNone)
+        {
+        TSlot gap;
+        TInt dataLength = 0;
+        TRAP(err, dataLength = iFileReadStream.ReadInt32L());
+        if (err == KErrNone)
+            {
+            TInt blockCount;
+            if (dataLength >= 0)
+                {
+                // real data
+                blockCount = BlockCount(dataLength);
+#if 0
+                DEBUGSTRING(("Copying at block %d, count %d",
+                             blockNumber,
+                             blockCount));
+#endif
+                // Back off four bytes
+                streamBuf->SeekL(MStreamBuf::ERead,
+                                 EStreamMark,
+                                 -KSlotHeaderSize);
+                dstFileWriteStream.WriteL(iFileReadStream,
+                                          blockCount * iBlockSize);
+                lastReadBlockNumber = blockNumber;
+                nextWriteBlockNumber += blockCount;
+                }
+            else
+                {
+                // empty slot
+                DEBUGSTRING(("Compacting at block %d", blockNumber));
+                blockCount = -dataLength;
+                streamBuf->SeekL(MStreamBuf::ERead,
+                                 EStreamMark,
+                                 blockCount * iBlockSize - KSlotHeaderSize);
+                // Mark block position
+                gap.iIndex = 0;  // not needed here
+                gap.iBlockNumber = blockNumber;
+                gap.iBlockCount = blockCount;
+                gaps.Append(gap);
+                }
+            blockNumber += blockCount;
+            }
+        }
+    if (err == KErrEof)
+        {
+        err = KErrNone;
+        }
+    // Replace old file with the compressed file
+    SetFileStateL(EFileStateClosed);
+    dstFileWriteStream.Close();
+    dstFile.Close();
+    if ((err == KErrNone) && gaps.Count())
+        {
+        // No errors and some compaction was achieved
+        err = iFs.Delete(iPath);
+        if (err == KErrNone)
+            {
+            err = iFs.Rename(dstPath, iPath);
+            if (err == KErrNone)
+                {
+                DEBUGSTRING(("CompactL(): gaps %d slots %d",
+                             gaps.Count(),
+                             iSlots.Count()));
+                if (gaps.Count() && iSlots.Count())
+                    {
+                    // Fix slot table (0(n**2))
+                    TInt oldBlockNumber = 0;
+                    TInt newBlockNumber = 0;
+                    while (oldBlockNumber <= lastReadBlockNumber)
+                        {
+                        if (gaps.Count())
+                            {
+                            // still more gaps ...
+                            if (oldBlockNumber == gaps[0].iBlockNumber)
+                                {
+                                oldBlockNumber += gaps[0].iBlockCount;
+                                gaps.Remove(0);
+                                }
+                            }
+                        if (oldBlockNumber != newBlockNumber)
+                            {
+                            FixSlot(oldBlockNumber, newBlockNumber);
+                            }
+                        oldBlockNumber++;
+                        newBlockNumber++;
+                        }
+                    }
+                }
+            }
+        // Clear free block lists
+        ClearFreeBlockLists();
+        // Setup next block to write
+        iWriteBlockNumber = nextWriteBlockNumber;
+        }
+    else
+        {
+        // Some error occured or no compaction was achieved
+        TInt r = iFs.Delete(dstPath);
+#ifdef _DEBUG
+        DEBUGSTRING(("CompactL(): destination file deletion returns %d ", r));
+#endif
+        }
+
+    CleanupStack::PopAndDestroy(3, &dstFile); // gaps, dstFileWriteStream, dstFile
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::SetHeaderL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::SetHeaderL(TDesC8& aHeader)
+    {
+    delete iFileHeader.iHeader;
+    iFileHeader.iHeader = NULL;
+    iFileHeader.iHeader = aHeader.AllocL();
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::Header
+// ----------------------------------------------------------------------------
+//
+const HBufC8* CRsfwPermanentStore::Header()
+    {
+    if (iFileHeader.iHeader)
+        {
+        return iFileHeader.iHeader;
+        }
+    else
+        {
+        return NULL;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::GetNextDataL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::GetNextDataL(TUint8* aData,
+                                   TInt& aDataLength,
+                                   TInt& aIndex)
+    {
+    // Return the next slotful of data.
+    // This function must only be used for sequential access
+    SetFileStateL(EFileStateReading);
+
+    MStreamBuf* streamBuf = iFileReadStream.Source();
+    
+    TBool done = EFalse;
+    while (!done)
+        {
+        TInt dataLength = 0;
+        // Eventually leaves with KErrEof
+        dataLength = iFileReadStream.ReadInt32L();
+        TInt blockCount;
+        if (dataLength >= 0)
+            {
+            // Fill data
+            iFileReadStream.ReadL(aData, dataLength);
+            aDataLength = dataLength;
+            aIndex = iIndex;
+
+            // Update block map
+            blockCount = BlockCount(dataLength);
+#if 0
+            DEBUGSTRING((
+                            "GetNextDataL(): index=%d, block=%d, count=%d, len=%d",
+                            iIndex,
+                            iReadBlockNumber,
+                            blockCount,
+                            dataLength));
+#endif
+
+            ReserveSlot(iIndex, iReadBlockNumber, blockCount); 
+            iIndex++;
+            iReadBlockNumber += blockCount;
+
+            // Update read position to the start of next block
+            TInt offset = iBlockSize -
+                (KSlotHeaderSize + dataLength) % iBlockSize;
+            if (offset != iBlockSize)
+                {
+                streamBuf->SeekL(MStreamBuf::ERead, EStreamMark, offset);
+                }
+            
+            done = ETrue;
+            }
+        else
+            {
+            // Negative length indicates a number of free blocks.
+            // Put such empty blocks into the free block list
+            blockCount = -dataLength;
+            PutToFreeBlockList(iReadBlockNumber, blockCount);
+            
+            // Seek to the next slot
+            streamBuf->SeekL(MStreamBuf::ERead, 
+                             EStreamMark,
+                             blockCount * iBlockSize - KSlotHeaderSize);
+            iReadBlockNumber += blockCount;
+            }
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::PutDataL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::PutDataL(const TUint8* aData,
+                               TInt aDataLength,
+                               TInt& aIndex)
+    {
+    // Write data with the given index
+    // If the index < 0, this a new data
+    SetFileStateL(EFileStateWriting);
+
+    if (aDataLength == 0)
+        {
+        // We just want to dispose of the slot
+        ClearSlotL(aIndex);
+        return;
+        }
+
+    TBool done = EFalse;
+    TInt blockCount = BlockCount(aDataLength);
+    if (aIndex >= 0)
+        {
+        // Check if we have space in the existing slot
+        TSlot* slot = Slot(aIndex);
+        // We should always find the slot
+        if (slot)
+            {
+            if (slot->iBlockCount == blockCount)
+                {
+                // We can use the current slot
+                WriteBlocksL(aData, aDataLength, slot->iBlockNumber);
+                done = ETrue;
+                }
+            else
+                {
+                // Clear the slot
+                ClearSlotL(aIndex);
+                }
+            }
+        else
+            {
+            DEBUGSTRING(("Slot %d not found!", aIndex));
+            }
+        }
+    else
+        {
+        // Allocate new index
+        aIndex = iIndex++;
+        }
+    
+    if (!done)
+        {
+        // Try to get a free slot
+        TInt blockNumber = GetFromFreeBlockList(blockCount);
+        if (blockNumber == KErrNotFound)
+            {
+            // We have to append to the file
+            blockNumber = iWriteBlockNumber;
+            iWriteBlockNumber += blockCount;
+            }
+        
+        ReserveSlot(aIndex, blockNumber, blockCount);
+        WriteBlocksL(aData, aDataLength, blockNumber);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::BlockCount
+// ----------------------------------------------------------------------------
+//
+TInt CRsfwPermanentStore::BlockCount(TInt aDataLength)
+    {
+    // Get the block count for a given data size
+    return (KSlotHeaderSize + aDataLength - 1) / iBlockSize + 1;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::StreamPosition
+// ----------------------------------------------------------------------------
+//
+TInt CRsfwPermanentStore::StreamPosition(TInt aBlockNumber)
+    {
+    // Get the stream position from block position
+    return (iFileHeaderSize + aBlockNumber * iBlockSize);
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::Slot
+// ----------------------------------------------------------------------------
+//
+TSlot* CRsfwPermanentStore::Slot(TInt aIndex)
+    {
+    // Find the slot with the given index
+    TSlot slot;             // dummy slot for Find()
+    slot.iIndex = aIndex;
+    TInt i = iSlots.Find(slot);
+    if (i != KErrNotFound)
+        {
+        return &iSlots[i];
+        }
+    else
+        {
+        return NULL;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::FixSlot
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::FixSlot(TInt aOldBlockNumber, TInt aNewBlockNumber)
+    {
+    // Assign a new starting block to the slot that used to
+    // start at aOldBlockNumber.
+    // The block numbers are changed due to compaction -
+    // thus the block numbers can only become smaller.
+    TInt i;
+    for (i = 0; i < iSlots.Count(); i++)
+        {
+        TSlot& slot = iSlots[i];
+        // Note that this function can also be called with block numbers
+        // that do not start a slot - then there will be no match
+        if (slot.iBlockNumber == aOldBlockNumber)
+            {
+            DEBUGSTRING(("Fixing slot %d = %d",
+                         aOldBlockNumber,
+                         aNewBlockNumber));
+            slot.iBlockNumber = aNewBlockNumber;
+            return;
+            }
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::SetFileStateL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::SetFileStateL(TInt aFileState)
+    {
+    DEBUGSTRING(("CRsfwPermanentStore::SetFileStateL"));
+    DEBUGSTRING(("iFileState = %d, aFileState = %d", iFileState, aFileState));
+    if (iFileState != aFileState)
+        {
+        switch (iFileState)
+            {
+        case EFileStateClosed:
+            {
+            if (aFileState == EFileStateReading)
+                {
+                DEBUGSTRING(("opening a closed file for reading"));
+                DEBUGSTRING16(("path is %S", &iPath));
+                User::LeaveIfError(iFile.Open(iFs,
+                                              iPath,
+                                              EFileShareAny | EFileRead));
+                TInt size;
+                iFile.Size(size);
+                DEBUGSTRING(("opening successfull, file size is %d", size));
+                DEBUGSTRING(("header size %d", iFileHeaderSize));
+                // sanity
+                if (size < iFileHeaderSize)
+                    {
+                    // Close the file and leave the file state as "closed"
+                    iFile.Close();
+                    User::Leave(KErrNotFound);
+                    }
+                else
+                    {
+                    iFileReadStream.Attach(iFile);
+                    }
+                }
+            else
+                {
+                // EFileStateWriting
+                TInt err = iFile.Open(iFs, iPath, EFileShareAny | EFileWrite);
+                if (err != KErrNone)
+                    {
+                    // The file did not exist
+                    User::LeaveIfError(
+                        iFile.Create(iFs,
+                                     iPath,
+                                     EFileShareAny | EFileWrite));
+                    }
+                TInt size;
+                User::LeaveIfError(iFile.Size(size));
+                iFileWriteStream.Attach(iFile);
+                if (size < iFileHeaderSize)
+                    {
+                    // Store header if this was a new file
+                    SaveHeaderL();
+                    }
+                }
+            }
+            break;
+        
+        case EFileStateReading:
+        case EFileStateWriting:
+            {
+            // close and redo
+            if (iFileState == EFileStateReading)
+                {
+                iFileReadStream.Close();
+                }
+            else
+                {
+                iFileWriteStream.Close();
+                }
+            iFile.Close();
+            iFileState = EFileStateClosed;
+            SetFileStateL(aFileState);
+            }
+            break;
+    
+        default:
+            break;
+            }
+        
+        iFileState = aFileState;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::LoadHeaderL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::LoadHeaderL()
+    {
+    DEBUGSTRING(("CRsfwPermanentStore::LoadHeaderL"));
+    SetFileStateL(EFileStateReading);
+    iFileHeader.InternalizeL(iFileReadStream);
+    ResetL(EFalse);
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::SaveHeaderL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::SaveHeaderL()
+    {
+    // Write header with filler
+    iFileHeader.ExternalizeL(iFileWriteStream);
+    MStreamBuf* streamBuf = iFileWriteStream.Sink();
+    TInt fileHeaderSize = streamBuf->TellL(MStreamBuf::EWrite).Offset();
+    TInt residue = iFileHeaderSize - fileHeaderSize;
+    HBufC8* fill = HBufC8::NewLC(residue);
+    TPtr8 fillZ = fill->Des();
+    fillZ.SetLength(residue);
+    fillZ.FillZ();
+    DEBUGSTRING(("SaveHeader(): header=%d, filler=%d",
+                 iFileHeaderSize,
+                 fillZ.Length()));
+    iFileWriteStream.WriteL(fillZ);
+    Commit();
+    CleanupStack::PopAndDestroy(fill); // fill
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::ClearFreeBlockLists
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::ClearFreeBlockLists()
+    {
+    TInt i;
+    for (i = 0; i < iFreeBlockLists.Count(); i++)
+        {
+        iFreeBlockLists[i].iFreeBlockList.Close();
+        }
+    iFreeBlockLists.Close();
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::ClearSlotL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::ClearSlotL(TInt aIndex)
+    {
+    // Mark a slot as unused in the file and
+    // add the slot in the free block list
+
+    TSlot s;                // dummy slot for iSlots.Find()
+    s.iIndex = aIndex;
+    TInt i = iSlots.Find(s);
+    if (i != KErrNotFound)
+        {
+        TSlot& slot = iSlots[i];
+        // Mark the slot in the file as empty
+        TInt pos = iFileHeaderSize +  slot.iBlockNumber * iBlockSize;
+
+        DEBUGSTRING(("ClearSlotL(): index=%d, block=%d, count=%d, pos=%d",
+                     aIndex,
+                     slot.iBlockNumber,
+                     slot.iBlockCount,
+                     pos));
+
+        MStreamBuf* streamBuf = iFileWriteStream.Sink();
+        streamBuf->SeekL(MStreamBuf::EWrite, EStreamBeginning, pos);
+        iFileWriteStream.WriteInt32L(-slot.iBlockCount);
+        
+        // Add the slot in the free block list
+        PutToFreeBlockList(slot.iBlockNumber, slot.iBlockCount);
+
+        // Delete the slot from the slot table
+        iSlots.Remove(i);
+        }
+    else
+        {
+        DEBUGSTRING(("ClearSlotL(): index=%d not found!", aIndex));
+        User::Leave(KErrNotFound);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::WriteBlocksL
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::WriteBlocksL(const TUint8* aData,
+                                   TInt aDataLength,
+                                   TInt aBlockNumber)
+    {
+    // Put the given data in the slot specified by the index
+    TInt pos = iFileHeaderSize +  aBlockNumber * iBlockSize;
+
+#if 0
+    DEBUGSTRING(("WriteBlocksL(): block=%d, len=%d, pos=%d",
+                 aBlockNumber,
+                 aDataLength,
+                 pos));
+#endif
+#if 0
+    TInt size;
+    iFile.Size(size);
+
+    DEBUGSTRING(("size=%d", size));
+#endif
+    
+    MStreamBuf* streamBuf = iFileWriteStream.Sink();
+    streamBuf->SeekL(MStreamBuf::EWrite, EStreamBeginning, pos);
+    
+    // Write the data in the file (preceded by the length)
+    iFileWriteStream.WriteInt32L(aDataLength);
+    iFileWriteStream.WriteL(aData, aDataLength);
+
+    // We have to fill the whole slot if this is the last slot
+    // in the file. Well, we will will the last bock of all slots
+    TInt residue = iBlockSize - (KSlotHeaderSize + aDataLength) % iBlockSize;
+    if (residue != iBlockSize)
+        {
+        iFileWriteStream.WriteL(iZeroBlock->Des().Ptr(), residue);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::ReserveSlot
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::ReserveSlot(TInt aIndex,
+                                  TInt aBlockNumber,
+                                  TInt aBlockCount)
+    {
+    TSlot slot;
+
+    slot.iIndex = aIndex;
+    slot.iBlockNumber = aBlockNumber;
+    slot.iBlockCount = aBlockCount;
+    iSlots.Append(slot);
+    }
+
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::PutToFreeBlockList
+// ----------------------------------------------------------------------------
+//
+void CRsfwPermanentStore::PutToFreeBlockList(TInt aBlockPos, TInt aBlockCount)
+    {
+    while (iFreeBlockLists.Count() < aBlockCount)
+        {
+        // Construct list until blockCount size
+        TFreeBlockList freeBlockList;
+        iFreeBlockLists.Append(freeBlockList);
+        }
+    iFreeBlockLists[aBlockCount - 1].iFreeBlockList.Append(aBlockPos);
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwPermanentStore::GetFromFreeBlockList
+// ----------------------------------------------------------------------------
+//
+TInt CRsfwPermanentStore::GetFromFreeBlockList(TInt aBlockCount)
+    {
+    // Only support exact matches.
+    // That is, bigger slots are not broken into smaller slots
+    if (iFreeBlockLists.Count() < aBlockCount)
+        {
+        // no list
+        return KErrNotFound;
+        }
+
+    TFreeBlockList& freeBlockList = iFreeBlockLists[aBlockCount - 1]; 
+    if (freeBlockList.iFreeBlockList.Count() == 0)
+        {
+        // no entries in the list
+        return KErrNotFound;
+        }
+
+    // Get the first entry in the list
+    TInt blockPos = freeBlockList.iFreeBlockList[0];
+    freeBlockList.iFreeBlockList.Remove(0);
+    return blockPos;
+    }
+
+// End of File