--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/OsLayer/FileBuf64.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,1131 @@
+// Copyright (c) 2008-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 "FileBuf64.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////// PROFILER ////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _SQLPROFILER
+
+extern TBool TheOsCallTimeDetailedProfileEnabled;//If true, the OS porting layer call details are enabled and for each call an entry will be added to the log file (epocwind.out).
+
+#define PROFILE_READ(pos,amount) \
+ do \
+ { \
+ if(TheOsCallTimeDetailedProfileEnabled) \
+ { \
+ ++iFileReadCount; iFileReadAmount += (amount); \
+ RDebug::Print(_L(" -- FRead this=%X, Cnt=%d, Pos=%ld, Amt=%d, Ttl=%ld\r\n"), (TUint32)this, iFileReadCount, pos, amount, iFileReadAmount); \
+ } \
+ } while(0)
+
+#define PROFILE_WRITE(pos,amount) \
+ do \
+ { \
+ if(TheOsCallTimeDetailedProfileEnabled) \
+ { \
+ ++iFileWriteCount, iFileWriteAmount += (amount); \
+ RDebug::Print(_L(" -- FWrite this=%X, Cnt=%d, Pos=%ld, Amt=%d, Ttl=%ld\r\n"), (TUint32)this, iFileWriteCount, pos, amount, iFileWriteAmount); \
+ } \
+ } while(0)
+
+#define PROFILE_SIZE() \
+ do \
+ { \
+ if(TheOsCallTimeDetailedProfileEnabled) \
+ { \
+ ++iFileSizeCount; \
+ RDebug::Print(_L(" -- FSize this=%X, Cnt=%d\r\n"), (TUint32)this, iFileSizeCount); \
+ } \
+ } while(0)
+
+#define PROFILE_SETSIZE() \
+ do \
+ { \
+ if(TheOsCallTimeDetailedProfileEnabled) \
+ { \
+ ++iFileSetSizeCount; \
+ RDebug::Print(_L(" -- FSetSize this=%X, Cnt=%d\r\n"), (TUint32)this, iFileSetSizeCount); \
+ } \
+ } while(0)
+
+#define PROFILE_FLUSH() \
+ do \
+ { \
+ if(TheOsCallTimeDetailedProfileEnabled) \
+ { \
+ ++iFileFlushCount; \
+ RDebug::Print(_L(" -- FFlush this=%X, Cnt=%d\r\n"), (TUint32)this, iFileFlushCount); \
+ } \
+ } while(0)
+
+//Resets the profiler counters
+void RFileBuf64::ProfilerReset()
+ {
+ iFileReadCount = 0; iFileReadAmount = 0; iFileWriteCount = 0; iFileWriteAmount = 0; iFileSizeCount = 0; iFileSetSizeCount = 0; iFileFlushCount = 0;
+ }
+
+#else
+
+#define PROFILE_READ(pos,amount) void(0)
+#define PROFILE_WRITE(pos,amount) void(0)
+
+#define PROFILE_SIZE() void(0)
+#define PROFILE_SETSIZE() void(0)
+#define PROFILE_FLUSH() void(0)
+
+#endif//_SQLPROFILER
+
+/**
+This constant is used for initializing the RFileBuf64::iFileSize data member and means that
+the iFileSize is not yet initialized with the real file size value.
+(RFileBuf64::iFileSize caches the file size value)
+@internalComponent
+*/
+static const TInt KFileSizeNotSet = -1;
+
+/**
+This constant is used as a default initializer for the RFileBuf64::iNextReadFilePos data member,
+indicating that the "guessed" file read offset is invalid and should not be used.
+@internalComponent
+*/
+static const TInt KNextReadFilePosNotSet = -1;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////// ASSERTS & INVARIANT //////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _DEBUG
+
+#define __FILEBUF64_INVARIANT() Invariant()
+
+/**
+String literal used in _DEBUG mode for indicating that the reported panic happened inside the RFileBuf64 implementation.
+
+@see TFileBufPanic64
+@internalComponent
+*/
+_LIT(KPanicCategory, "FBuf64");
+
+/**
+Set of numeric constants used together with the KPanicCategory string literal in _DEBUG mode for providing more detailed
+information about the reason of the panic.
+
+@see KPanicCategory
+@internalComponent
+*/
+enum TFileBufPanic64
+ {
+ EFBufPanicCapacity = 1, //1
+ EFBufPanicNullBuf,
+ EFBufPanicBufLen,
+ EFBufPanicFilePos,
+ EFBufPanicFileSize, //5
+ EFBufPanicFileHandle,
+ EFBufPanicFsHandle,
+ EFBufPanicMsgHandle,
+ EFBufPanicMsgIndex,
+ EFBufPanicFileNameLen, //10
+ EFBufPanicNullThis,
+ EFBufPanicDirty,
+ EFBufPanicNextReadFilePos,
+ EFBufPanicNextReadFilePosHits,
+ EFBufPanicFileBlockSize, //15
+ };
+
+/**
+Helper function used in the implementation of the __FBUF64_ASSERT() macro.
+In case if the expression in __FBUF64_ASSERT() macro evaluates to false,
+PanicFileBuf64() will use the supplied aLine and aPanicCode arguments together with the KPanicCategory string literal
+to prepare and print out a line (including the time of the panic) to the default log. The calling thread will be panic'ed
+after that.
+
+@see TFileBufPanic64
+@see KPanicCategory
+@internalComponent
+*/
+static void PanicFileBuf64(TInt aLine, TFileBufPanic64 aPanicCode)
+ {
+ TTime time;
+ time.HomeTime();
+ TDateTime dt = time.DateTime();
+ TBuf<16> tbuf;
+ tbuf.Format(_L("%02d:%02d:%02d.%06d"), dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond());
+
+ TBuf<64> buf;
+ _LIT(KFormat,"**%S:RFileBuf64 panic %d, at line(%d)");
+ buf.Format(KFormat, &tbuf, aPanicCode, aLine);
+ RDebug::Print(buf);
+ User::Panic(KPanicCategory, aPanicCode);
+ }
+
+/**
+This macro should be used when there is a need to panic the client/server if "expr" condition is not satisfied.
+Works in only in debug mode. In release mode evaluates to nothing.
+
+@see TFileBufPanic64
+@see KPanicCategory
+@see PanicFileBuf64()
+@internalComponent
+*/
+#define __FBUF64_ASSERT(expr, panicCode) (void)(!(expr) ? ::PanicFileBuf64(__LINE__, panicCode) : void(0))
+
+#else //_DEBUG
+
+#define __FILEBUF64_INVARIANT() void(0)
+
+#define __FBUF64_ASSERT(expr, panicCode) void(0)
+
+#endif//_DEBUG
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////// MFileInitializer64 /////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+MFileInitializer64 interface provides only one abstract method - Init() that is used during the initialization of
+the RFileBuf64 objects.
+Here is what is the problem MFileInitializer64 tries to solve.
+RFileBuf64 has 4 different "resource acquisition" methods - Create(), Open(), Temp() and AdoptFromClient().
+They perform different actions and have different input arguments.
+This is the variable part of the RFileBuf64 initialization.
+Apart from that, RFileBuf64 has a "fixed" initialization part that does not change whatever the variable part is.
+If MFileInitializer64 interface is not used then the following chunk of code has to be duplicated 4 times:
+@code
+ TInt err = do_fixed_init();
+ if(err == KErrNone)
+ {
+ err = do_variable_init();
+ if(err != KErrNone)
+ {
+ revert_fixed_init();
+ }
+ }
+ return err;
+@endcode
+In order to avoid the code duplication, the fixed part of the initialization is moved to RFileBuf64::DoInit(), which
+is given a reference to a MFileInitializer64 derived class that performas the variable part of the initialization.
+4 different MFileInitializer64 derived classes are provided for the 4 different "resource acquisition" methods.
+All they store the variable part of the RFileBuf64 initialization parameters and implement MFileInitializer64::Init().
+
+@see RFileBuf64::DoInit()
+@internalComponent
+*/
+struct MFileInitializer64
+ {
+ virtual TInt Init(RFile64& aFile) = 0;
+ };
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////// RFileBuf64 /////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Initializes RFileBuf64 data members with their default values.
+
+@param aSize Max file buffer size (capacity) in bytes.
+
+@panic FBuf64 1 In _DEBUG mode - aSize is 0 or negative.
+*/
+RFileBuf64::RFileBuf64(TInt aSize) :
+ iCapacity(aSize),
+ iReadAheadSize(RFileBuf64::KDefaultReadAheadSize)
+ {
+ __FBUF64_ASSERT(aSize > 0, EFBufPanicCapacity);
+ }
+
+/**
+Initializes the RFileBuf64 object and creates and opens a new file that will be accessed through RFileBuf64 public interface.
+If the file already exists, an error is returned.
+If the resulting path does not exist, then the operation cannot proceed and the function returns an error code.
+
+@param aFs The file server session.
+@param aFileName The name of the file. Any path components (i.e. drive letter
+ or directory), which are not specified, are taken from
+ the session path.
+@param aFileMode The mode in which the file is opened. See TFileMode for details.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@see TFileMode
+@see RFile64::Create()
+@see MFileInitializer64
+
+@panic FBuf64 7 In _DEBUG mode - Invalid aFs object (null file session handle).
+@panic FBuf64 10 In _DEBUG mode - Invalid file name length (zero file name length).
+*/
+TInt RFileBuf64::Create(RFs& aFs, const TDesC& aFileName, TUint aFileMode)
+ {
+ __FBUF64_ASSERT(aFs.Handle() != 0, EFBufPanicFsHandle);
+ __FBUF64_ASSERT(aFileName.Length() > 0, EFBufPanicFileNameLen);
+
+ struct TFileCreateInitializer64 : public MFileInitializer64
+ {
+ inline TFileCreateInitializer64(RFs& aFs, const TDesC& aFileName, TUint aFileMode) :
+ iFs(aFs),
+ iFileName(aFileName),
+ iFileMode(aFileMode)
+ {
+ }
+ virtual TInt Init(RFile64& aFile)
+ {
+ return aFile.Create(iFs, iFileName, iFileMode);
+ }
+ RFs& iFs;
+ const TDesC& iFileName;
+ TUint iFileMode;
+ } initializer(aFs, aFileName, aFileMode);
+
+ return DoInit(initializer);
+ }
+
+/**
+Initializes the RFileBuf64 object and opens an existing file that will be accessed through RFileBuf64 public interface.
+If the file does not already exist, an error is returned.
+
+@param aFs The file server session.
+@param aFileName The name of the file. Any path components (i.e. drive letter
+ or directory), which are not specified, are taken from
+ the session path.
+@param aFileMode The mode in which the file is opened. See TFileMode for details.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@see TFileMode
+@see RFile64::Open()
+@see MFileInitializer64
+
+@panic FBuf64 7 In _DEBUG mode - Invalid aFs object (null file session handle).
+@panic FBuf64 10 In _DEBUG mode - Invalid file name length (zero file name length).
+*/
+TInt RFileBuf64::Open(RFs& aFs, const TDesC& aFileName, TUint aFileMode)
+ {
+ __FBUF64_ASSERT(aFs.Handle() != 0, EFBufPanicFsHandle);
+ __FBUF64_ASSERT(aFileName.Length() > 0, EFBufPanicFileNameLen);
+
+ struct TFileOpenInitializer64 : public MFileInitializer64
+ {
+ inline TFileOpenInitializer64(RFs& aFs, const TDesC& aFileName, TUint aFileMode) :
+ iFs(aFs),
+ iFileName(aFileName),
+ iFileMode(aFileMode)
+ {
+ }
+ virtual TInt Init(RFile64& aFile)
+ {
+ return aFile.Open(iFs, iFileName, iFileMode);
+ }
+ RFs& iFs;
+ const TDesC& iFileName;
+ TUint iFileMode;
+ } initializer(aFs, aFileName, aFileMode);
+
+ return DoInit(initializer);
+ }
+
+/**
+Initializes the RFileBuf64 object and creates and opens a temporary file with unique name that will be accessed through
+RFileBuf64 public interface.
+
+@param aFs The file server session.
+@param aPath The directory in which the file is created.
+@param aFileName On return, contains the full path and file name of the file.
+ The filename is guaranteed to be unique within the directory
+ specified by aPath.
+@param aFileMode The mode in which the file is opened. The access mode is
+ automatically set to EFileWrite. See TFileMode for details.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@see TFileMode
+@see RFile64::Temp()
+@see MFileInitializer64
+
+@panic FBuf64 7 In _DEBUG mode - Invalid aFs object (null file session handle).
+*/
+TInt RFileBuf64::Temp(RFs& aFs, const TDesC& aPath, TFileName& aFileName, TUint aFileMode)
+ {
+ __FBUF64_ASSERT(aFs.Handle() != 0, EFBufPanicFsHandle);
+
+ struct TFileTempInitializer64 : public MFileInitializer64
+ {
+ inline TFileTempInitializer64(RFs& aFs, const TDesC& aPath, TFileName& aFileName, TUint aFileMode) :
+ iFs(aFs),
+ iPath(aPath),
+ iFileName(aFileName),
+ iFileMode(aFileMode)
+ {
+ }
+ virtual TInt Init(RFile64& aFile)
+ {
+ return aFile.Temp(iFs, iPath, iFileName, iFileMode);
+ }
+ RFs& iFs;
+ const TDesC& iPath;
+ TFileName& iFileName;
+ TUint iFileMode;
+ } initializer(aFs, aPath, aFileName, aFileMode);
+
+ return DoInit(initializer);
+ }
+
+/**
+Initializes the RFileBuf64 object and creates and adopts an already open file from a client that will be accessed through
+RFileBuf64 public interface.
+The client's RFs and RFile or RFile64 handles are contained in message slots within aMsg.
+Assumes that the client's RFs and RFile or RFile64 handles have been sent to the server using TransferToServer().
+
+@param aMsg The message received from the client
+@param aFsIndex The index that identifies the message slot
+ of a file server session (RFs) handle
+@param aFileIndex The index that identifies the message slot
+ of the sub-session (RFile or RFile64) handle of the already opened file
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@see TFileMode
+@see RFile64::AdoptFromClient()
+@see MFileInitializer64
+@see KMaxMessageArguments
+
+@panic FBuf64 8 In _DEBUG mode - Invalid aMsg object (null message handle).
+@panic FBuf64 9 In _DEBUG mode - Invalid file session handle message slot index or invalid file handle message slot index.
+ (Probably negative index or index bigger or equal to KMaxMessageArguments)
+*/
+TInt RFileBuf64::AdoptFromClient(const RMessage2& aMsg, TInt aFsIndex, TInt aFileIndex)
+ {
+ __FBUF64_ASSERT(aMsg.Handle() != 0, EFBufPanicMsgHandle);
+ __FBUF64_ASSERT(aFsIndex >= 0 && aFsIndex < KMaxMessageArguments, EFBufPanicMsgIndex);
+ __FBUF64_ASSERT(aFileIndex >= 0 && aFileIndex < KMaxMessageArguments, EFBufPanicMsgIndex);
+
+ struct TFileAdoptInitializer64 : public MFileInitializer64
+ {
+ inline TFileAdoptInitializer64(const RMessage2& aMsg, TInt aFsIndex, TInt aFileIndex) :
+ iMsg(aMsg),
+ iFsIndex(aFsIndex),
+ iFileIndex(aFileIndex)
+ {
+ }
+ virtual TInt Init(RFile64& aFile)
+ {
+ return aFile.AdoptFromClient(iMsg, iFsIndex, iFileIndex);
+ }
+ const RMessage2& iMsg;
+ TInt iFsIndex;
+ TInt iFileIndex;
+ } initializer(aMsg, aFsIndex, aFileIndex);
+
+ return DoInit(initializer);
+ }
+
+/**
+Writes to the file the pending data (if the buffer contains pending data), closes the file and releases
+the RFileBuf64 resources.
+RFileBuf64::Flush() should be called before RFileBuf64::Close() to ensure that if there are pending data, they will
+be written to the file and if the operation fails, the caller will be notified with an appropriate return error.
+
+@see RFileBuf64::Flush()
+*/
+void RFileBuf64::Close()
+ {
+ if(iBase != 0 && iFile.SubSessionHandle() != 0)
+ {
+ (void)DoFileWrite2();
+ }
+ iFile.Close();
+ User::Free(iBase);
+ iBase = 0;
+ }
+
+/**
+Calculates and sets optimal read-ahead buffer size.
+
+@param aBlockSize The size of a file block in bytes
+@param aReadRecBufSize The recommended buffer size for optimised reading performance
+
+@return The new read-ahead value
+
+@see TVolumeIOParamInfo
+*/
+TInt RFileBuf64::SetReadAheadSize(TInt aBlockSize, TInt aReadRecBufSize)
+ {
+ __FILEBUF64_INVARIANT();
+ if((aReadRecBufSize & (aReadRecBufSize - 1)) == 0 && aReadRecBufSize > RFileBuf64::KDefaultReadAheadSize)
+ {
+ iReadAheadSize = aReadRecBufSize > iCapacity ? iCapacity : aReadRecBufSize;
+ }
+ else if((aBlockSize & (aBlockSize - 1)) == 0 && aBlockSize > RFileBuf64::KDefaultReadAheadSize)
+ {
+ iReadAheadSize = aBlockSize > iCapacity ? iCapacity : aBlockSize;
+ }
+ __FILEBUF64_INVARIANT();
+ return iReadAheadSize;
+ }
+
+/**
+Reads from the file at the specified position (aFilePos).
+If the data to be read is in the buffer, then the data will be taken from the buffer.
+
+@param aFilePos Position of first byte to be read. This is an offset from
+ the start of the file.
+ If aPos is beyond the end of the file, the function returns
+ a zero length descriptor.
+@param aDes Descriptor into which binary data is read. Any existing contents
+ are overwritten. On return, its length is set to the number of
+ bytes read.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@panic FBuf64 4 In _DEBUG mode - negative aFilePos value.
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::Read(TInt64 aFilePos, TDes8& aDes)
+ {
+ __FBUF64_ASSERT(aFilePos >= 0, EFBufPanicFilePos);
+ __FILEBUF64_INVARIANT();
+ aDes.SetLength(0);
+ //1. The output buffer max len is 0
+ if(aDes.MaxLength() == 0)
+ {
+ __FILEBUF64_INVARIANT();
+ return KErrNone;
+ }
+ //2. Initialize the "iFileSize" if it is not initialized yet
+ TInt err = DoFileSize();
+ if(err != KErrNone)
+ {
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+ //3. Too big "read" request - read directly from the file
+ TInt len = aDes.MaxLength();
+ if(len > iCapacity)
+ {
+ if((aFilePos + len) > iFilePos && !(aFilePos >= (iFilePos + iLength)))
+ {//Write the pending data if the iDirty flag is set, otherwise preserve the buffer content.
+ err = DoFileWrite1(aFilePos);
+ }
+ if(err == KErrNone)
+ {
+ err = iFile.Read(aFilePos, aDes);
+ PROFILE_READ(aFilePos, aDes.Size());
+ }
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+ //4. The requested data size is smaller than the buffer capacity
+ TUint8* outptr = const_cast <TUint8*> (aDes.Ptr());
+ while(len > 0 && err == KErrNone && aFilePos < iFileSize)
+ {
+ //1. If part of all of the data is in the buffer - copy the data to the target location
+ if(aFilePos >= iFilePos && aFilePos < (iFilePos + iLength))
+ {
+ TInt l = Min(len, (iFilePos + iLength - aFilePos));
+ outptr = Mem::Copy(outptr, iBase + (aFilePos - iFilePos), l);
+ len -= l;
+ aFilePos += l;
+ }
+ //2. Perform a read-ahead operation
+ else
+ {
+ //Write the pending data if the iDirty flag is set, otherwise preserve the buffer content.
+ err = DoFileWrite1(aFilePos);
+ if(err != KErrNone)
+ {
+ break;
+ }
+ if(iNextReadFilePos != aFilePos)
+ {//Direct "file read" operation
+ iNextReadFilePosHits = 0;
+ TPtr8 ptr2(outptr, len);
+ err = iFile.Read(aFilePos, ptr2);
+ PROFILE_READ(aFilePos, ptr2.Size());
+ if(err == KErrNone)
+ {
+ iNextReadFilePos = aFilePos + len;
+ len -= ptr2.Length();
+ }
+ break;
+ }
+ //The guessed from the previous "file read" operation file pos is correct. Start reading-ahead.
+ const TInt KMaxReadFilePosHits = 8;//The max read-ahead buffer size can be up to 2^8 times the iReadAheadSize
+ if(iNextReadFilePosHits < KMaxReadFilePosHits)
+ {
+ ++iNextReadFilePosHits;
+ }
+ TInt maxReadAhead = iReadAheadSize * (1 << iNextReadFilePosHits);
+ TInt align = (aFilePos + len + maxReadAhead) & (iReadAheadSize - 1);
+ TInt readahead = maxReadAhead - align;
+ if(readahead < 0)
+ {
+ // if read-ahead doesn't cross block boundary do it all
+ readahead = maxReadAhead;
+ }
+ TPtr8 ptr(iBase, Min(iCapacity, (len + readahead)));
+ err = iFile.Read(aFilePos, ptr);
+ PROFILE_READ(aFilePos, ptr.Size());
+ if(err == KErrNone)
+ {
+ iFilePos = aFilePos;
+ iLength = ptr.Length();
+ iNextReadFilePos = iFilePos + iLength;
+ if(iLength == 0)
+ {
+ break;
+ }
+ }
+ else
+ {
+ DoDiscard();
+ }
+ }
+ }
+ aDes.SetLength(aDes.MaxLength() - len);
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Writes to the file at the specified offset (aFilePos) within the file.
+If certain conditions are met, the data will be stored in the buffer - no call to the file server.
+
+@param aFilePos The offset from the start of the file at which the first byte is written.
+ If a position beyond the end of the file is specified, then
+ the write operation begins at the end of the file.
+ If the position has been locked, then the write fails.
+
+@param aData The descriptor from which binary data is written. The function writes
+ the entire contents of aData to the file.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@panic FBuf64 4 In _DEBUG mode - negative aFilePos value.
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::Write(TInt64 aFilePos, const TDesC8& aData)
+ {
+ __FBUF64_ASSERT(aFilePos >= 0, EFBufPanicFilePos);
+ __FILEBUF64_INVARIANT();
+ if(aData.Length() == 0)
+ {
+ __FILEBUF64_INVARIANT();
+ return KErrNone;
+ }
+ TInt err = DoFileSize();
+ if(err != KErrNone)
+ {
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+ DoDiscardBufferedReadData();
+ const TUint8* data = aData.Ptr();
+ for(TInt len = aData.Length(); len > 0 && err == KErrNone;)
+ {
+ //1. The new write pos is before the buffered file pos
+ if(aFilePos < iFilePos)
+ {
+ //If the new data sticks to/overlapps the old data and there is room in the buffer to move the old data
+ //toward the end, then the new data can be copied at the beginning of the buffer.
+ if((aFilePos + len) >= iFilePos && (iFilePos - aFilePos) <= (iCapacity - iLength))
+ {
+ (void)Mem::Copy(iBase + (iFilePos - aFilePos), iBase, iLength); //Make room - move the existing data toward the end
+ (void)Mem::Copy(iBase, data, len); //of the buffer. Stick the new data to the old data
+ iLength += (iFilePos - aFilePos);
+ iFilePos = aFilePos; //The new file pos is associated with the buffer
+ iFileSize = Max(iFileSize, (iFilePos + iLength));
+ len = 0; //No more new data
+ iDirty = ETrue;
+ }
+ else
+ //The "aFilePos" is too far before the "iFilePos". Write the buffer and associate the new pos with the buffer
+ {
+ err = DoFileWrite2(aFilePos);
+ }
+ }
+ //2. The new write pos is after the associated file pos + the data length.
+ else if(aFilePos > (iFilePos + iLength))
+ {
+ if(aFilePos > iFileSize) //Beyond the end of the file
+ {
+ if((iFilePos + iLength) == iFileSize && (aFilePos - iFilePos) < iCapacity)
+ { //but within the buffer => extend the file with zeros.
+ Mem::FillZ(iBase + iLength, aFilePos - iFilePos - iLength);
+ iLength = aFilePos - iFilePos;
+ iFileSize = Max(iFileSize, (iFilePos + iLength));
+ iDirty = ETrue;
+ }
+ else
+ //Beyond the end of the file and not in the buffer - set file size.
+ {
+ err = DoSetFileSize(aFilePos);
+ }
+ }
+ else
+ //Within the file, not in the buffer - write the buffer and associate the new file pos with the buffer
+ {
+ err = DoFileWrite2(aFilePos);
+ }
+ }
+ //3. The new write pos is in the buffer, but the data length is too big
+ // (For SQLite is OK, otherwise the whole block must be written to the file)
+ //4. The new write pos is in the buffer, the data entirely fits in the buffer
+ else
+ {
+ if(iCapacity == iLength) //The buffer is full. Write the buffer and associate the new file pos
+ {
+ err = DoFileWrite2(aFilePos);
+ }
+ if(err == KErrNone)
+ {
+ TInt amount = Min(len, (iCapacity - (aFilePos - iFilePos)));
+ const TUint8* end = Mem::Copy(iBase + (aFilePos - iFilePos), data, amount);
+ iLength = Max(iLength, (end - iBase));
+ iFileSize = Max(iFileSize, (iFilePos + iLength));
+ len -= amount;
+ data += amount;
+ aFilePos += amount;
+ iDirty = ETrue;
+ }
+ }
+ }
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Gets the current file size.
+
+@param aFileSize On return, the size of the file in bytes.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::Size(TInt64& aFileSize)
+ {
+ __FILEBUF64_INVARIANT();
+ TInt err = DoFileSize();
+ if(err == KErrNone)
+ {
+ aFileSize = iFileSize;
+ }
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Sets the file size.
+
+If the size of the file is reduced, data may be lost from the end of the file.
+
+Note:
+
+1. The current file position remains unchanged unless SetSize() reduces the size
+ of the file in such a way that the current file position is now beyond
+ the end of the file. In this case, the current file position is set to
+ the end of file.
+
+2. If the file was not opened for writing, an error is returned.
+
+@param aFileSize The new size of the file, in bytes. This value must not be negative, otherwise the function raises a panic.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@panic FBuf64 5 In _DEBUG mode - negative aFileSize value.
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::SetSize(TInt64 aFileSize)
+ {
+ __FBUF64_ASSERT(aFileSize >= 0, EFBufPanicFileSize);
+ __FILEBUF64_INVARIANT();
+ return DoSetFileSize(aFileSize);
+ }
+
+/**
+Writes the pending data and then flushes the file.
+
+Although RFileBuf64::Close() also flushes internal buffers, it is better
+to call RFileBuf64::Flush() before the file is closed. This is because Close() returns no
+error information, so there is no way of telling whether the final data was
+written to the file successfully or not.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::Flush()
+ {
+ __FILEBUF64_INVARIANT();
+ return DoFileFlush();
+ }
+
+/**
+Gets information about the drive on which this file resides.
+
+@param aDriveNumber On return, the drive number.
+
+@param aDriveInfo On return, contains information describing the drive
+ and the medium mounted on it. The value of TDriveInfo::iType
+ shows whether the drive contains media.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::Drive(TInt& aDriveNumber, TDriveInfo& aDriveInfo) const
+ {
+ __FILEBUF64_INVARIANT();
+ return iFile.Drive(aDriveNumber, aDriveInfo);
+ }
+
+/**
+Performs the fixed part of the RFileBuf64 initialization and then calls MFileInitializer64::Init() to perform
+the variable part of the initialization.
+
+@param aFileInitializer A reference to an initializer object that implements MFileInitializer64::Init()
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+*/
+TInt RFileBuf64::DoInit(MFileInitializer64& aFileInitializer)
+ {
+ DoDiscard();
+ iReadAheadSize = RFileBuf64::KDefaultReadAheadSize;
+ TInt err = KErrNoMemory;
+ iBase = static_cast <TUint8*> (User::Alloc(iCapacity));
+ if(!iBase)
+ {
+ return KErrNoMemory;
+ }
+ err = aFileInitializer.Init(iFile);
+ if(err != KErrNone)
+ {
+ User::Free(iBase);
+ iBase = 0;
+ }
+ return err;
+ }
+
+/**
+Discards the content of the RFileBuf64 object returning it to the state as if it has just been created.
+*/
+void RFileBuf64::DoDiscard()
+ {
+ iLength = 0;
+ iFilePos = 0;
+ iFileSize = KFileSizeNotSet;
+ iDirty = EFalse;
+ iNextReadFilePos = KNextReadFilePosNotSet;
+ iNextReadFilePosHits = 0;
+ }
+
+/**
+Gets the current file size.
+If iFileSize value is valid, then no call to the file server will be made.
+Otherwise the file server will be called and the file size - stored (cached) in iFileSize data member for later use.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::DoFileSize()
+ {
+ __FILEBUF64_INVARIANT();
+ if(iFileSize != KFileSizeNotSet)
+ {
+ __FILEBUF64_INVARIANT();
+ return KErrNone;
+ }
+ PROFILE_SIZE();
+ TInt err = iFile.Size(iFileSize);
+ if(err != KErrNone)
+ {
+ DoDiscard();
+ }
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Sets the file size.
+If the buffer contains pending data, the data will be written to the file
+before the "set file size" operation, if certain conditions are met.
+
+@param aFileSize The new size of the file, in bytes. This value must not be negative, otherwise the function raises a panic.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+@panic FBuf64 5 In _DEBUG mode - negative aFileSize value.
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::DoSetFileSize(TInt64 aFileSize)
+ {
+ __FBUF64_ASSERT(aFileSize >= 0, EFBufPanicFileSize);
+ __FILEBUF64_INVARIANT();
+ if(aFileSize < iFilePos)
+ {
+ iDirty = EFalse;
+ iLength = 0;
+ }
+ //If the new file size is "in" the buffer then change the "iLength"
+ else if(aFileSize < (iFilePos + iLength))
+ {
+ iLength = aFileSize - iFilePos;
+ }
+ PROFILE_SETSIZE();
+ TInt err = iFile.SetSize(aFileSize);
+ if(err != KErrNone)
+ {
+ DoDiscard();
+ }
+ else
+ {
+ iFileSize = aFileSize;
+ }
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Writes the pending data and flushes the file.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for possible panics that may occur when this method is called.
+
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::DoFileFlush()
+ {
+ __FILEBUF64_INVARIANT();
+ TInt err = DoFileWrite2();//Write the buffer if the iDirty flag is set. Do not preserve the buffer content and file pos.
+ if(err != KErrNone)
+ {
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+ PROFILE_FLUSH();
+ err = iFile.Flush();
+ if(err != KErrNone)
+ {
+ DoDiscard();
+ }
+ iLength = 0;
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Writes the buffered data to the file if the iLength value is > 0.
+If the file write operation extends the file, the iFileSize data member will be initialized with the new file size.
+No changes occur in the other data member values.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@see RFileBuf64::DoFileWrite1()
+@see RFileBuf64::DoFileWrite2()
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::DoFileWrite()
+ {
+ __FILEBUF64_INVARIANT();
+ if(iLength == 0)
+ {
+ __FILEBUF64_INVARIANT();
+ return KErrNone;
+ }
+ PROFILE_WRITE(iFilePos, iLength);
+ TPtrC8 data(iBase, iLength);
+ TInt err = iFile.Write(iFilePos, data);
+ if(err == KErrNone)
+ {
+ iFileSize = Max(iFileSize, (iFilePos + iLength));
+ }
+ else
+ {
+ DoDiscard();
+ }
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+Writes the buffered data to the file if the iDirty flag is set.
+If the iDirty flag is set and the file write operation was successful, the iFilePos will be initialized with
+the aNewFilePos value, the iLength will be set to 0.
+This method is called from RFileBuf64::Read(), where:
+ - if the buffer contains cached writes (iDirty flag is set), the buffer has to be flushed and iFilePos initialized
+ with aNewFilePos - the offset in the file where the next file read operation should start from;
+ - if the buffer contains cached reads, then nothing happens, the buffer content will be kept;
+The function resets the iDirty flag.
+
+@param aNewFilePos If the buffer is successfully written to the file the iFilePos data member will be initialized with
+ the aNewFilePos value.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@panic FBuf64 4 In _DEBUG mode - negative aNewFilePos value.
+
+@see RFileBuf64::Read()
+@see RFileBuf64::DoFileWrite()
+@see RFileBuf64::DoFileWrite2()
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::DoFileWrite1(TInt64 aNewFilePos)
+ {
+ __FBUF64_ASSERT(aNewFilePos >= 0, EFBufPanicFilePos);
+ __FILEBUF64_INVARIANT();
+ TInt err = KErrNone;
+ if(iDirty)
+ {
+ err = DoFileWrite();
+ if(err == KErrNone)
+ {
+ iFilePos = aNewFilePos;
+ iLength = 0;
+ }
+ }
+ iDirty = EFalse;
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/*
+Writes the buffered data to the file if the iDirty flag is set.
+If the file write operation was successful or if the iDirty flag was not set, the iFilePos will be initialized with
+the aNewFilePos value, the iLength will be set to 0.
+This method is called from RFileBuf64::Write() an other RFileBuf64 methods (but not from RFileBuf64::Read()), where:
+ - if the buffer contains cached writes (iDirty flag is set), the buffer has to be flushed and iFilePos initialized
+ with aNewFilePos - the offset in the file for which the write data will be cached in the buffer;
+ - if the buffer contains cached reads, then the buffer content will be destroyed, iFilePos initialized with aNewFilePos
+ and iLength set to 0;
+The function resets the iDirty flag.
+The difference between RFileBuf64::DoFileWrite1() and RFileBuf64::DoFileWrite2() is:
+ - RFileBuf64::DoFileWrite1() perserves the buffer content if iDirty is not set;
+ - RFileBuf64::DoFileWrite2() always destroys the buffer content and initializes iFilePos;
+
+@param aNewFilePos If the buffer is successfully written to the file the iFilePos data member will be initialized with
+ the aNewFilePos value.
+
+@return KErrNone if successful, otherwise one of the other system-wide error codes.
+
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+
+@panic FBuf64 4 In _DEBUG mode - negative aNewFilePos value.
+
+@see RFileBuf64::Write()
+@see RFileBuf64::DoFileWrite()
+@see RFileBuf64::DoFileWrite1()
+@see RFileBuf64::Invariant()
+*/
+TInt RFileBuf64::DoFileWrite2(TInt64 aNewFilePos)
+ {
+ __FBUF64_ASSERT(aNewFilePos >= 0, EFBufPanicFilePos);
+ __FILEBUF64_INVARIANT();
+ TInt err = KErrNone;
+ if(iDirty)
+ {
+ err = DoFileWrite();
+ }
+ if(err == KErrNone)
+ {
+ iFilePos = aNewFilePos;
+ iLength = 0;
+ }
+ iDirty = EFalse;
+ __FILEBUF64_INVARIANT();
+ return err;
+ }
+
+/**
+This function discards the buffer content if the buffer contains cached read data.
+The function is called from RFileBuf64::Write(), because if the buffer contains cached read data,
+they cannot be mixed with the cached write data.
+Reason: for example the buffer contains 8Kb cached read data from file offset 0.
+ The data write request is 10 bytes at offset 4000. The write data will be cached,
+ because the buffer contains data from from this file area: [0..8192].
+ The iDirty flag will be set. Later when RFileBuf64::Flush() is called, the whole
+ 8Kb buffer will be written. There is nothing wrong with that, the file content will be consistent.
+ But from performance point of view: 8Kb written vs. 10 bytes written - that may badly impact the performance.
+
+@see RFileBuf64::Write()
+
+See RFileBuf64::Invariant() for other possible panics that may occur when this method is called.
+*/
+void RFileBuf64::DoDiscardBufferedReadData()
+ {
+ __FILEBUF64_INVARIANT();
+ if(!iDirty && iLength > 0)
+ {
+ iLength = 0;
+ iFilePos = 0;
+ iNextReadFilePos = KNextReadFilePosNotSet;
+ iNextReadFilePosHits = 0;
+ }
+ __FILEBUF64_INVARIANT();
+ }
+
+#ifdef _DEBUG
+
+/**
+RFileBuf64 invariant. Called in _DEBUG mode at the beginning and before the end of every RFileBuf64 method
+(except the init/destroy methods).
+
+@panic FBuf64 11 In _DEBUG mode - null "this" pointer.
+@panic FBuf64 1 In _DEBUG mode - negative iCapacity value.
+@panic FBuf64 2 In _DEBUG mode - the buffer pointer is null (possible the buffer is not allocated or already destroyed).
+@panic FBuf64 3 In _DEBUG mode - invalid iLength value (negative or bigger than iCapacity).
+@panic FBuf64 4 In _DEBUG mode - negative iFilePos value.
+@panic FBuf64 5 In _DEBUG mode - set but negative iFileSize value.
+@panic FBuf64 6 In _DEBUG mode - null file handle (the RFile64 object is not created or already destroyed).
+@panic FBuf64 13 In _DEBUG mode - set but negative iNextReadFilePos value.
+@panic FBuf64 14 In _DEBUG mode - negative iNextReadFilePosHits value.
+@panic FBuf64 15 In _DEBUG mode - iReadAheadSize is negative or is not power of two.
+*/
+void RFileBuf64::Invariant() const
+ {
+ __FBUF64_ASSERT(this != 0, EFBufPanicNullThis);
+ __FBUF64_ASSERT(iCapacity > 0, EFBufPanicCapacity);
+ __FBUF64_ASSERT(iBase != 0, EFBufPanicNullBuf);
+ __FBUF64_ASSERT(iLength >= 0 && iLength <= iCapacity, EFBufPanicBufLen);
+ __FBUF64_ASSERT(iFilePos >= 0, EFBufPanicFilePos);
+ __FBUF64_ASSERT(iFileSize == KFileSizeNotSet || iFileSize >= 0, EFBufPanicFileSize);
+ __FBUF64_ASSERT(iFile.SubSessionHandle() != 0, EFBufPanicFileHandle);
+ __FBUF64_ASSERT(iNextReadFilePos == KNextReadFilePosNotSet || iNextReadFilePos >= 0, EFBufPanicNextReadFilePos);
+ __FBUF64_ASSERT(iNextReadFilePosHits >= 0, EFBufPanicNextReadFilePosHits);
+ __FBUF64_ASSERT(iReadAheadSize > 0 && (iReadAheadSize & (iReadAheadSize - 1)) == 0, EFBufPanicFileBlockSize);
+ }
+
+#endif