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