sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: #ifndef FILEBUF64_H sl@0: #define FILEBUF64_H sl@0: sl@0: #include sl@0: #include sl@0: sl@0: /** sl@0: The RFileBuf64 class provides buffered file read/write operations on a single RFile64 object. sl@0: RFileBuf64::Read() and RFileBuf64::Write() methods may boost the performance of the read/write file operations up to 30% sl@0: if compared to their RFile64 equivalents. Especially this is true in the case of a set of sequential read or write sl@0: operations when the file offset of operation N+1 is the end file offset of operation N plus one byte. sl@0: sl@0: The RFileBuf64 public methods declarations match the declarations of the most often used RFile64 public methods. sl@0: That makes the source code migration from RFile64 to RFileBuf64 easier (or from RFileBuf64 to RFile64). sl@0: sl@0: The RFileBuf64 capabilities are similar to those of the RFileBuf class, except the fact that RFileBuf sl@0: uses 32-bit file offsets, RFileBuf64 uses 64-bit file offsets and RFileBuf64 provides optimised read-ahead operations, sl@0: crafted especially for the case when RFileBuf64 is used with a page based database management system (like SQLite). sl@0: sl@0: Usage notes: sl@0: @code sl@0: - an object of RFileBuf64 type must be defined first, specifying the max size (capacity) of the buffer as a parameter sl@0: of the constructor: sl@0: sl@0: RFileBuf64 fbuf();// is the buffer capacity in bytes sl@0: sl@0: - the second step is to initialize the just defined RFileBuf64 object by calling one of the "resource acquisition" sl@0: methods: RFileBuf64::Create(), RFileBuf64::Open() or RFileBuf64::Temp(). sl@0: sl@0: In details, to create a file and access it through a RFileBuf64 object: sl@0: sl@0: RFs fs; sl@0: TInt err = fs.Connect(); sl@0: //check the error sl@0: ... sl@0: RFileBuf64 fbuf(); // is the buffer capacity in bytes sl@0: err = fbuf.Create(fs, , ); sl@0: //check the error sl@0: sl@0: To open an existing file and access it through a RFileBuf64 object: sl@0: sl@0: RFs fs; sl@0: TInt err = fs.Connect(); sl@0: //check the error sl@0: ... sl@0: RFileBuf64 fbuf(); // is the buffer capacity in bytes sl@0: err = fbuf.Open(fs, , ); sl@0: //check the error sl@0: sl@0: To create a temporary file and access it through a RFileBuf64 object: sl@0: sl@0: RFs fs; sl@0: TInt err = fs.Connect(); sl@0: //check the error sl@0: ... sl@0: RFileBuf64 fbuf(); // is the buffer capacity in bytes sl@0: err = fbuf.Temp(fs, , , ); sl@0: //check the error sl@0: sl@0: - if the RFileBuf64 object is initialised successfully, now the public RFileBuf64 methods can be called to perform sl@0: requested operations on the file: sl@0: sl@0: err = fbuf.Write(, ); sl@0: //check the error sl@0: .... sl@0: err = fbuf.Read(, ); sl@0: //check the error sl@0: .... sl@0: Note: do not forget to call RFileBuf64::Flush() at the moment when you want to ensure that the file data sl@0: (possibly buffered) is really written to the file. sl@0: sl@0: - The final step is to close the RFileBuf64 object and the corresponding file thus realising the used resouces: sl@0: sl@0: fbuf.Close(); sl@0: sl@0: @endcode sl@0: sl@0: Implementation notes: the current RFileBuf64 implementation is optimised for use by the SQLite OS porting layer. sl@0: After investigation of SQLite file read/write operations it was found that buffering of sl@0: two or more logical file writes into a single physical file write has a positive impact (as expected) on the performance sl@0: of the database write operations. But the picture is quite different for the file read operations. The database data is sl@0: organised in pages with fixed size. After a database is created and set of insert/update/delete operations is performed sl@0: on it, after a while the database pages (identified by their numbers) are not sequential in the database file and using sl@0: a read-ahead buffer with fixed size makes no sense because for each "page read" request of N bytes, the RFileBuf64 object sl@0: will read up to K bytes, K >= N, where K is the read-ahead value in bytes. Since the "read page" requests in general are sl@0: not sequential (relatively to the page numbers), obviously the read-ahead data is wasted and the "read page" performance sl@0: is negatively impacted. This observation is true in general except two cases: sl@0: - sequential scan of a database table; sl@0: - reading of a BLOB column; sl@0: In these two cases it is likely that the table/blob data occupies pages with sequential numbers located in a continuous sl@0: file area. Then if a read-ahead buffer is used that will have a positive impact on the "read page" performance, because sl@0: the number of the read IPC calls to the file server will be reduced. sl@0: In order to satisfy those two orthogonal requirements, the RFileBuf64 implementation uses a "read file offset prediction" sl@0: algorithm: sl@0: - The file buffer object is created with 0 read-ahead value; sl@0: - After each "file read" operation the buffer implementation "guesses" what might be the file offset of the sl@0: next file read operation (it is possible because the database data is organised in pages with fixed size and sl@0: generally all "file read" requests are for reading a database page) and stores the value in one of its data members; sl@0: - If the file offset of the next file read operation matches the "guessed" offset, the read-ahead value is changed from sl@0: 0 to 1024 bytes (1024 bytes is the default database page size); sl@0: - Every next match of the "guessed" file offset value doubles the read-ahead value. But the max read-ahead value is sl@0: capped by the capacity of the buffer; sl@0: - If the file offset of the next file read operation does not match the "guessed" file offset, then the read-ahead sl@0: value is set back to 0; sl@0: Shortly, depending of the nature of the file read requests, the RFileBuf64 object will dynamically change the read-ahead sl@0: value thus minimising the amount of the wasted read data and improving the "file read" performance in general. sl@0: sl@0: @see RFile64 sl@0: @see RFileBuf sl@0: sl@0: @internalComponent sl@0: */ sl@0: class RFileBuf64 sl@0: { sl@0: enum {KDefaultReadAheadSize = 1024};//Default size in bytes of the read-ahead buffer sl@0: sl@0: public: sl@0: RFileBuf64(TInt aSize); sl@0: sl@0: TInt Create(RFs& aFs, const TDesC& aFileName, TUint aFileMode); sl@0: TInt Open(RFs& aFs, const TDesC& aFileName, TUint aFileMode); sl@0: TInt Temp(RFs& aFs, const TDesC& aPath, TFileName& aFileName, TUint aFileMode); sl@0: void Close(); sl@0: TInt SetReadAheadSize(TInt aBlockSize, TInt aReadRecBufSize); sl@0: sl@0: TInt Read(TInt64 aFilePos, TDes8& aDes); sl@0: TInt Write(TInt64 aFilePos, const TDesC8& aData); sl@0: sl@0: TInt Size(TInt64& aFileSize); sl@0: TInt SetSize(TInt64 aFileSize); sl@0: TInt Lock(TInt64 aFilePos, TInt64 aLength) const; sl@0: TInt UnLock(TInt64 aFilePos, TInt64 aLength) const; sl@0: TInt Flush(TBool aResetCachedFileSize = EFalse); sl@0: sl@0: TInt Drive(TInt& aDriveNumber, TDriveInfo& aDriveInfo) const; sl@0: sl@0: private: sl@0: void Invariant() const; sl@0: TInt DoPreInit(); sl@0: void DoPostInit(TInt aInitErr); sl@0: void DoDiscard(); sl@0: TInt DoFileSize(); sl@0: TInt DoSetFileSize(TInt64 aFileSize); sl@0: TInt DoFileFlush(); sl@0: TInt DoFileWrite(); sl@0: TInt DoFileWrite1(TInt64 aNewFilePos); sl@0: TInt DoFileWrite2(TInt64 aNewFilePos = 0LL); sl@0: void DoDiscardBufferedReadData(); sl@0: sl@0: private: sl@0: //Buffer related sl@0: const TInt iCapacity; //The buffer size. Indicates how much data can be put in. sl@0: TUint8* iBase; //Pointer to the beginning of the buffer. sl@0: TInt iLength; //The length of the data currently held in the buffer. sl@0: //File related sl@0: TInt64 iFilePos; //The file position associated with the beginning of the buffer. sl@0: TInt64 iFileSize; //The file size. sl@0: RFile64 iFile; //The file object. sl@0: //Read-ahead related sl@0: TBool iDirty; //The buffer contains pending data to be written to the file sl@0: TInt64 iNextReadFilePos; //The guessed file position of the next "file read" operation sl@0: TInt iNextReadFilePosHits; //How many times the guessed file position of the "file read" operation was correct sl@0: TInt iReadAheadSize; sl@0: sl@0: //Profiler related sl@0: #ifdef _SQLPROFILER sl@0: public: sl@0: TInt iFileReadCount; //The number of the non-buffered file reads (RFile64::Read() calls). sl@0: TInt64 iFileReadAmount; //The amount of the data read from the file. sl@0: TInt iFileWriteCount; //The number of the non-buffered file writes (RFile64::Write() calls). sl@0: TInt64 iFileWriteAmount; //The amount of the data written to the file. sl@0: TInt iFileSizeCount; //The number of non-buffered RFile64::Size() calls. sl@0: TInt iFileSetSizeCount; //The number of non-buffered RFile64::SetSize() calls. sl@0: TInt iFileFlushCount; //The number of RFile64::Flush() calls. sl@0: #endif sl@0: sl@0: }; sl@0: sl@0: #endif//FILEBUF64_H