csharp/Adoxio/xRM-Portals-Community-Edition/Framework/Adxstudio.Xrm/Search/Store/Encryption/BufferedPageReaderWriter.cs

BufferedPageReaderWriter.cs
/*
  Copyright (c) Microsoft Corporation. All rights reserved.
  Licensed under the MIT License. See License.txt in the project root for license information.
*/

namespace Adxstudio.Xrm.Search.Store.Encryption
{
	using System;
	using System.Linq;

	/// 
	/// The buffered page reader writer.
	/// 
	public abstract clast BufferedPageReaderWriter : IBufferedPageReader, IBufferedPageWriter
	{
		/// 
		/// The buffer pointer.
		/// 
		protected int bufferPointer;

		/// 
		/// The file pointer.
		/// 
		protected long filePointer;

		/// 
		/// The file size.
		/// 
		protected long fileSize;

		/// 
		/// The page buffer.
		/// 
		protected byte[] pageBuffer;

		/// 
		/// The page is changed.
		/// 
		protected bool pageIsChanged;

		/// 
		/// The page number.
		/// 
		protected long pageNumber;

		/// 
		/// Gets the length.
		/// 
		public abstract long Length { get; }

		/// 
		/// Gets the page size.
		/// 
		public abstract int PageSize { get; }

		/// 
		/// Clones the object.
		/// 
		/// copy of instance
		public abstract object Clone();

		/// 
		/// Gets the length.
		/// 
		long IBufferedPageReader.Length
		{
			get
			{
				return this.Length;
			}
		}

		/// 
		/// Gets the position.
		/// 
		long IBufferedPageReader.Position
		{
			get
			{
				return this.filePointer;
			}
		}

		/// 
		/// Gets the length.
		/// 
		long IBufferedPageWriter.Length
		{
			get
			{
				return this.Length;
			}
		}

		/// 
		/// Gets the position.
		/// 
		long IBufferedPageWriter.Position
		{
			get
			{
				return this.filePointer;
			}
		}

		/// 
		/// The dispose.
		/// 
		public virtual void Dispose()
		{
			this.Flush();
		}

		/// 
		/// The flush.
		/// 
		public void Flush()
		{
			this.WriteCurrentPage();
		}

		/// 
		/// The read byte.
		/// 
		/// 
		/// The .
		/// 
		public byte ReadByte()
		{
			if (this.bufferPointer == this.PageSize)
			{
				this.pageNumber++;
				this.bufferPointer = 0;

				this.ReadCurrentPage();
			}

			var @byte = this.pageBuffer[this.bufferPointer++];
			this.filePointer++;

			return @byte;
		}

		/// 
		/// The read bytes.
		/// 
		/// 
		/// The destination.
		/// 
		/// 
		/// The offset.
		/// 
		/// 
		/// The length.
		/// 
		public void ReadBytes(byte[] destination, int offset, int length)
		{
			// TODO: Optimize with Buffer.BlockCopy
			for (var i = 0; i < length; i++)
			{
				var @byte = this.ReadByte();

				destination[offset + i] = @byte;
			}
		}

		/// 
		/// The read page.
		/// 
		/// 
		/// The page number.
		/// 
		/// 
		/// The destination.
		/// 
		public abstract void ReadPage(long pageNumber, byte[] destination);

		/// 
		/// The write byte.
		/// 
		/// 
		/// The byte.
		/// 
		public void WriteByte(byte @byte)
		{
			if (this.bufferPointer == this.PageSize)
			{
				this.WriteCurrentPage();
				this.bufferPointer = 0;
				this.pageNumber++;
				this.ReadCurrentPage();
			}

			this.pageBuffer[this.bufferPointer++] = @byte;
			this.pageIsChanged = true;

			this.filePointer++;

			if (this.filePointer > this.fileSize)
			{
				this.fileSize = this.filePointer;
			}
		}

		/// 
		/// The write bytes.
		/// 
		/// 
		/// The source.
		/// 
		/// 
		/// The offset.
		/// 
		/// 
		/// The length.
		/// 
		public void WriteBytes(byte[] source, int offset, int length)
		{
			var range = source.Skip(offset).Take(length).ToArray();

			foreach (byte @byte in range)
			{
				this.WriteByte(@byte);
			}
		}

		/// 
		/// The write page.
		/// 
		/// 
		/// The page number.
		/// 
		/// 
		/// The source.
		/// 
		public abstract void WritePage(long pageNumber, byte[] source);

		/// 
		/// The seek.
		/// 
		/// 
		/// The position.
		/// 
		void IBufferedPageReader.Seek(long position)
		{
			this.Seek(position);
		}

		/// 
		/// The seek.
		/// 
		/// 
		/// The position.
		/// 
		void IBufferedPageWriter.Seek(long position)
		{
			this.Seek(position);
		}

		/// 
		/// The initialize.
		/// 
		protected void Initialize()
		{
			this.pageBuffer = new byte[this.PageSize];
			this.pageNumber = -1;
			this.Seek(0);
		}

		/// 
		/// The read current page.
		/// 
		/// 
		/// Invalid Operation Exception
		/// 
		protected void ReadCurrentPage()
		{
			if (this.pageIsChanged)
			{
				throw new InvalidOperationException("Save page before read");
			}

			if (this.pageNumber * this.PageSize >= this.Length)
			{
				// There are no more data. Fill next page with zeros
				Array.Clear(this.pageBuffer, 0, this.PageSize);
			}
			else
			{
				this.ReadPage(this.pageNumber, this.pageBuffer);
			}

			this.pageIsChanged = false;
		}

		/// 
		/// The seek.
		/// 
		/// 
		/// The position.
		/// 
		protected void Seek(long position)
		{
			this.Flush();

			var pageNumber = (int)Math.Floor((double)position / this.PageSize);

			this.filePointer = position;
			this.bufferPointer = (int)(position - (pageNumber * this.PageSize));

			if (pageNumber == this.pageNumber)
			{
				return;
			}

			this.pageNumber = pageNumber;

			this.ReadCurrentPage();
		}

		/// 
		/// The write current page.
		/// 
		protected void WriteCurrentPage()
		{
			if (!this.pageIsChanged)
			{
				return;
			}

			this.WritePage(this.pageNumber, this.pageBuffer);

			this.pageIsChanged = false;
		}
	}
}