Click here to Skip to main content
15,892,537 members
Articles / Programming Languages / C#

Using a Database Over a Webservice

Rate me:
Please Sign up or sign in to vote.
4.43/5 (5 votes)
29 May 20073 min read 48.6K   539   44  
This article shows an example implementation of a database used over a Web-Service
/* Copyright (C) 2004 - 2007  db4objects Inc.  http://www.db4o.com */

using System;
using Db4objects.Db4o.IO;
using Db4objects.Db4o.Internal.Fileheader;
using Sharpen;

namespace Db4objects.Db4o.IO
{
	/// <summary>
	/// CachedIoAdapter is an IOAdapter for random access files, which caches data
	/// for IO access.
	/// </summary>
	/// <remarks>
	/// CachedIoAdapter is an IOAdapter for random access files, which caches data
	/// for IO access. Its functionality is similar to OS cache.<br />
	/// Example:<br />
	/// <code>delegateAdapter = new RandomAccessFileAdapter();</code><br />
	/// <code>Db4o.configure().io(new CachedIoAdapter(delegateAdapter));</code><br />
	/// </remarks>
	public class CachedIoAdapter : IoAdapter
	{
		private CachedIoAdapter.Page _head;

		private CachedIoAdapter.Page _tail;

		private long _position;

		private int _pageSize;

		private int _pageCount;

		private long _fileLength;

		private long _filePointer;

		private IoAdapter _io;

		private static int DEFAULT_PAGE_SIZE = 1024;

		private static int DEFAULT_PAGE_COUNT = 64;

		public CachedIoAdapter(IoAdapter ioAdapter) : this(ioAdapter, DEFAULT_PAGE_SIZE, 
			DEFAULT_PAGE_COUNT)
		{
		}

		public CachedIoAdapter(IoAdapter ioAdapter, int pageSize, int pageCount)
		{
			_io = ioAdapter;
			_pageSize = pageSize;
			_pageCount = pageCount;
		}

		public CachedIoAdapter(string path, bool lockFile, long initialLength, IoAdapter 
			io, int pageSize, int pageCount)
		{
			_pageSize = pageSize;
			_pageCount = pageCount;
			InitCache();
			InitIOAdaptor(path, lockFile, initialLength, io);
			_position = initialLength;
			_filePointer = initialLength;
			_fileLength = _io.GetLength();
		}

		/// <summary>Creates and returns a new CachedIoAdapter <br /></summary>
		/// <param name="path">database file path</param>
		/// <param name="lockFile">determines if the file should be locked</param>
		/// <param name="initialLength">initial file length, new writes will start from this point
		/// 	</param>
		public override IoAdapter Open(string path, bool lockFile, long initialLength)
		{
			return new Db4objects.Db4o.IO.CachedIoAdapter(path, lockFile, initialLength, _io, 
				_pageSize, _pageCount);
		}

		/// <summary>Deletes the database file</summary>
		/// <param name="path">file path</param>
		public override void Delete(string path)
		{
			_io.Delete(path);
		}

		/// <summary>Checks if the file exists</summary>
		/// <param name="path">file path</param>
		public override bool Exists(string path)
		{
			return _io.Exists(path);
		}

		private void InitIOAdaptor(string path, bool lockFile, long initialLength, IoAdapter
			 io)
		{
			_io = io.Open(path, lockFile, initialLength);
		}

		private void InitCache()
		{
			_head = new CachedIoAdapter.Page(_pageSize);
			_head._prev = null;
			CachedIoAdapter.Page page = _head;
			CachedIoAdapter.Page next = _head;
			for (int i = 0; i < _pageCount - 1; ++i)
			{
				next = new CachedIoAdapter.Page(_pageSize);
				page._next = next;
				next._prev = page;
				page = next;
			}
			_tail = next;
		}

		/// <summary>Reads the file into the buffer using pages from cache.</summary>
		/// <remarks>
		/// Reads the file into the buffer using pages from cache. If the next page
		/// is not cached it will be read from the file.
		/// </remarks>
		/// <param name="buffer">destination buffer</param>
		/// <param name="length">how many bytes to read</param>
		public override int Read(byte[] buffer, int length)
		{
			long startAddress = _position;
			CachedIoAdapter.Page page;
			int readBytes;
			int bytesToRead = length;
			int totalRead = 0;
			while (bytesToRead > 0)
			{
				page = GetPage(startAddress, true);
				readBytes = page.Read(buffer, totalRead, startAddress, bytesToRead);
				MovePageToHead(page);
				if (readBytes <= 0)
				{
					break;
				}
				bytesToRead -= readBytes;
				startAddress += readBytes;
				totalRead += readBytes;
			}
			_position = startAddress;
			return totalRead == 0 ? -1 : totalRead;
		}

		/// <summary>Writes the buffer to cache using pages</summary>
		/// <param name="buffer">source buffer</param>
		/// <param name="length">how many bytes to write</param>
		public override void Write(byte[] buffer, int length)
		{
			long startAddress = _position;
			CachedIoAdapter.Page page = null;
			int writtenBytes;
			int bytesToWrite = length;
			int bufferOffset = 0;
			while (bytesToWrite > 0)
			{
				bool loadFromDisk = (bytesToWrite < _pageSize) || (startAddress % _pageSize != 0);
				page = GetPage(startAddress, loadFromDisk);
				page.EnsureEndAddress(GetLength());
				writtenBytes = page.Write(buffer, bufferOffset, startAddress, bytesToWrite);
				if (ContainsHeaderBlock(page))
				{
					FlushPage(page);
				}
				MovePageToHead(page);
				bytesToWrite -= writtenBytes;
				startAddress += writtenBytes;
				bufferOffset += writtenBytes;
			}
			long endAddress = startAddress;
			_position = endAddress;
			_fileLength = Math.Max(endAddress, _fileLength);
		}

		/// <summary>Flushes cache to a physical storage</summary>
		public override void Sync()
		{
			FlushAllPages();
			_io.Sync();
		}

		/// <summary>Returns the file length</summary>
		public override long GetLength()
		{
			return _fileLength;
		}

		/// <summary>Flushes and closes the file</summary>
		public override void Close()
		{
			FlushAllPages();
			_io.Close();
		}

		public override IoAdapter DelegatedIoAdapter()
		{
			return _io.DelegatedIoAdapter();
		}

		private CachedIoAdapter.Page GetPage(long startAddress, bool loadFromDisk)
		{
			CachedIoAdapter.Page page = GetPageFromCache(startAddress);
			if (page != null)
			{
				if (ContainsHeaderBlock(page))
				{
					GetPageFromDisk(page, startAddress);
				}
				page.EnsureEndAddress(_fileLength);
				return page;
			}
			page = GetFreePageFromCache();
			if (loadFromDisk)
			{
				GetPageFromDisk(page, startAddress);
			}
			else
			{
				ResetPageAddress(page, startAddress);
			}
			return page;
		}

		private bool ContainsHeaderBlock(CachedIoAdapter.Page page)
		{
			return page.StartAddress() <= FileHeader1.LENGTH;
		}

		private void ResetPageAddress(CachedIoAdapter.Page page, long startAddress)
		{
			page.StartAddress(startAddress);
			page.EndAddress(startAddress + _pageSize);
		}

		private CachedIoAdapter.Page GetFreePageFromCache()
		{
			if (!_tail.IsFree())
			{
				FlushPage(_tail);
			}
			return _tail;
		}

		private CachedIoAdapter.Page GetPageFromCache(long pos)
		{
			CachedIoAdapter.Page page = _head;
			while (page != null)
			{
				if (page.Contains(pos))
				{
					return page;
				}
				page = page._next;
			}
			return null;
		}

		private void FlushAllPages()
		{
			CachedIoAdapter.Page node = _head;
			while (node != null)
			{
				FlushPage(node);
				node = node._next;
			}
		}

		private void FlushPage(CachedIoAdapter.Page page)
		{
			if (!page._dirty)
			{
				return;
			}
			IoSeek(page.StartAddress());
			WritePageToDisk(page);
			return;
		}

		private void GetPageFromDisk(CachedIoAdapter.Page page, long pos)
		{
			long startAddress = pos - pos % _pageSize;
			page.StartAddress(startAddress);
			IoSeek(page._startAddress);
			int count = IoRead(page);
			if (count > 0)
			{
				page.EndAddress(startAddress + count);
			}
			else
			{
				page.EndAddress(startAddress);
			}
		}

		private int IoRead(CachedIoAdapter.Page page)
		{
			int count = _io.Read(page._buffer);
			if (count > 0)
			{
				_filePointer = page._startAddress + count;
			}
			return count;
		}

		private void MovePageToHead(CachedIoAdapter.Page page)
		{
			if (page == _head)
			{
				return;
			}
			if (page == _tail)
			{
				CachedIoAdapter.Page tempTail = _tail._prev;
				tempTail._next = null;
				_tail._next = _head;
				_tail._prev = null;
				_head._prev = page;
				_head = _tail;
				_tail = tempTail;
			}
			else
			{
				page._prev._next = page._next;
				page._next._prev = page._prev;
				page._next = _head;
				_head._prev = page;
				page._prev = null;
				_head = page;
			}
		}

		private void WritePageToDisk(CachedIoAdapter.Page page)
		{
			_io.Write(page._buffer, page.Size());
			_filePointer = page.EndAddress();
			page._dirty = false;
		}

		/// <summary>Moves the pointer to the specified file position</summary>
		/// <param name="pos">position within the file</param>
		public override void Seek(long pos)
		{
			_position = pos;
		}

		private void IoSeek(long pos)
		{
			if (_filePointer != pos)
			{
				_io.Seek(pos);
				_filePointer = pos;
			}
		}

		private class Page
		{
			internal byte[] _buffer;

			internal long _startAddress = -1;

			internal long _endAddress;

			internal int _bufferSize;

			internal bool _dirty;

			internal CachedIoAdapter.Page _prev;

			internal CachedIoAdapter.Page _next;

			public static byte[] zeroBytes;

			public Page(int size)
			{
				_bufferSize = size;
				_buffer = new byte[_bufferSize];
			}

			internal virtual void EnsureEndAddress(long fileLength)
			{
				long bufferEndAddress = _startAddress + _bufferSize;
				if (_endAddress < bufferEndAddress && fileLength > _endAddress)
				{
					long newEndAddress = Math.Min(fileLength, bufferEndAddress);
					if (zeroBytes == null)
					{
						zeroBytes = new byte[_bufferSize];
					}
					System.Array.Copy(zeroBytes, 0, _buffer, (int)(_endAddress - _startAddress), (int
						)(newEndAddress - _endAddress));
					_endAddress = newEndAddress;
				}
			}

			internal virtual long EndAddress()
			{
				return _endAddress;
			}

			internal virtual void StartAddress(long address)
			{
				_startAddress = address;
			}

			internal virtual long StartAddress()
			{
				return _startAddress;
			}

			internal virtual void EndAddress(long address)
			{
				_endAddress = address;
			}

			internal virtual int Size()
			{
				return (int)(_endAddress - _startAddress);
			}

			internal virtual int Read(byte[] @out, int outOffset, long startAddress, int length
				)
			{
				int bufferOffset = (int)(startAddress - _startAddress);
				int pageAvailbeDataSize = (int)(_endAddress - startAddress);
				int readBytes = Math.Min(pageAvailbeDataSize, length);
				if (readBytes <= 0)
				{
					return -1;
				}
				System.Array.Copy(_buffer, bufferOffset, @out, outOffset, readBytes);
				return readBytes;
			}

			internal virtual int Write(byte[] data, int dataOffset, long startAddress, int length
				)
			{
				int bufferOffset = (int)(startAddress - _startAddress);
				int pageAvailabeBufferSize = _bufferSize - bufferOffset;
				int writtenBytes = Math.Min(pageAvailabeBufferSize, length);
				System.Array.Copy(data, dataOffset, _buffer, bufferOffset, writtenBytes);
				long endAddress = startAddress + writtenBytes;
				if (endAddress > _endAddress)
				{
					_endAddress = endAddress;
				}
				_dirty = true;
				return writtenBytes;
			}

			internal virtual bool Contains(long address)
			{
				return (_startAddress != -1 && address >= _startAddress && address < _startAddress
					 + _bufferSize);
			}

			internal virtual bool IsFree()
			{
				return _startAddress == -1;
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United Arab Emirates United Arab Emirates
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions