context: //
  // System.Security.Cryptography MD5CryptoServiceProvider Class implementation
  //
  // Authors:
  //	Matthew S. Ford (Matthew.S.Ford@Rose-Hulman.Edu)
  //	Sebastien Pouliot (spouliot@motus.com)
  //
  // Copyright 2001 by Matthew S. Ford.
  //
  // Comment: Adapted to the Project from Mono CVS as Sebastien Pouliot suggested to enable
  // support of Npgsql MD5 authentication in platforms which don't have support for MD5 algorithm.
  //
  
  
  using System;
  
  namespace Npgsql
  {
  	/// <summary>
  	/// C# implementation of the MD5 cryptographic hash function.
  	/// </summary>
  #if USE_VERSION_1_0
      internal class MD5CryptoServiceProvider : MD5
      {
  #else
  	internal sealed class MD5CryptoServiceProvider : MD5
  	{
  #endif
  		private const int BLOCK_SIZE_BYTES = 64;
  		private const int HASH_SIZE_BYTES = 16;
  		private const int HASH_SIZE_BITS = 128;
  		private readonly uint[] _H;
  		private uint count;
  		private readonly byte[] _ProcessingBuffer; // Used to start data when passed less than a block worth.
  		private int _ProcessingBufferCount; // Counts how much data we have stored that still needs processed.
  
  		/// <summary>
  		/// Creates a new MD5CryptoServiceProvider.
  		/// </summary>
  		public MD5CryptoServiceProvider()
  		{
  			_H = new uint[4];
  			HashSizeValue = HASH_SIZE_BITS;
  			_ProcessingBuffer = new byte[BLOCK_SIZE_BYTES];
  
  			Initialize();
  		}
  
  		~MD5CryptoServiceProvider()
  		{
  			Dispose(false);
  		}
  
  		protected override void Dispose(bool disposing)
  		{
  			// nothing to do (managed implementation)
  		}
  
  		/// <summary>
  		/// Drives the hashing function.
  		/// </summary>
  		/// <param name="rgb">Byte array containing the data to hash.</param>
  		/// <param name="start">Where in the input buffer to start.</param>
  		/// <param name="size">Size in bytes of the data in the buffer to hash.</param>
  		protected override void HashCore(byte[] rgb, int start, int size)
  		{
  			int i;
  			State = 1;
  
  			if (_ProcessingBufferCount != 0)
  			{
  				if (size < (BLOCK_SIZE_BYTES - _ProcessingBufferCount))
  				{
  					Buffer.BlockCopy(rgb, start, _ProcessingBuffer, _ProcessingBufferCount, size);
  					_ProcessingBufferCount += size;
  					return;
  				}
  				else
  				{
  					i = (BLOCK_SIZE_BYTES - _ProcessingBufferCount);
  					Buffer.BlockCopy(rgb, start, _ProcessingBuffer, _ProcessingBufferCount, i);
  					ProcessBlock(_ProcessingBuffer, 0);
  					_ProcessingBufferCount = 0;
  					start += i;
  					size -= i;
  				}
  			}
  
  			for (i = 0; i < size - size%BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES)
  			{
  				ProcessBlock(rgb, start + i);
  			}
  
  			if (size%BLOCK_SIZE_BYTES != 0)
  			{
  				Buffer.BlockCopy(rgb, size - size%BLOCK_SIZE_BYTES + start, _ProcessingBuffer, 0, size%BLOCK_SIZE_BYTES);
  				_ProcessingBufferCount = size%BLOCK_SIZE_BYTES;
  			}
  		}
  
  		/// <summary>
  		/// This finalizes the hash.  Takes the data from the chaining variables and returns it.
  		/// </summary>
  		protected override byte[] HashFinal()
  		{
  			byte[] hash = new byte[16];
  			int i, j;
  
  			ProcessFinalBlock(_ProcessingBuffer, 0, _ProcessingBufferCount);
  
  			for (i = 0; i < 4; i++)
  			{
  				for (j = 0; j < 4; j++)
  				{
  					hash[i*4 + j] = (byte) (_H[i] >> j*8);
  				}
  			}
  
  			return hash;
  		}
  
  		/// <summary>
  		/// Resets the class after use.  Called automatically after hashing is done.
  		/// </summary>
  		public override void Initialize()
  		{
  			count = 0;
  			_ProcessingBufferCount = 0;
  
  			_H[0] = 0x67452301;
  			_H[1] = 0xefcdab89;
  			_H[2] = 0x98badcfe;
  			_H[3] = 0x10325476;
  		}
  
  		/// <summary>
  		/// This is the meat of the hash function.  It is what processes each block one at a time.
  		/// </summary>
  		/// <param name="inputBuffer">Byte array to process data from.</param>
  		/// <param name="inputOffset">Where in the byte array to start processing.</param>
  		private void ProcessBlock(byte[] inputBuffer, int inputOffset)
  		{
  			uint[] buff = new uint[16];
  			uint a, b, c, d;
  			int i;
  
  			count += BLOCK_SIZE_BYTES;
  
  			for (i = 0; i < 16; i++)
  			{
  				buff[i] = (inputBuffer[inputOffset + 4*i]) | (((uint) (inputBuffer[inputOffset + 4*i + 1])) << 8) |
  				          (((uint) (inputBuffer[inputOffset + 4*i + 2])) << 16) |
  				          (((uint) (inputBuffer[inputOffset + 4*i + 3])) << 24);
  			}
  
  			a = _H[0];
  			b = _H[1];
  			c = _H[2];
  			d = _H[3];
  
  			// This function was unrolled because it seems to be doubling our performance with current compiler/VM.
  			// Possibly roll up if this changes.
  
  
  			// ---- Round 1 --------
  
  			a += (((c ^ d) & b) ^ d) + (uint) Constants.C0 + buff[0];
  			a = (a << 7) | (a >> 25);
  			a += b;
  
  			d += (((b ^ c) & a) ^ c) + (uint) Constants.C1 + buff[1];
  			d = (d << 12) | (d >> 20);
  			d += a;
  
  			c += (((a ^ b) & d) ^ b) + (uint) Constants.C2 + buff[2];
  			c = (c << 17) | (c >> 15);
  			c += d;
  
  			b += (((d ^ a) & c) ^ a) + (uint) Constants.C3 + buff[3];
  			b = (b << 22) | (b >> 10);
  			b += c;
  
  			a += (((c ^ d) & b) ^ d) + (uint) Constants.C4 + buff[4];
  			a = (a << 7) | (a >> 25);
  			a += b;
  
  			d += (((b ^ c) & a) ^ c) + (uint) Constants.C5 + buff[5];
  			d = (d << 12) | (d >> 20);
  			d += a;
  
  			c += (((a ^ b) & d) ^ b) + (uint) Constants.C6 + buff[6];
  			c = (c << 17) | (c >> 15);
  			c += d;
  
  			b += (((d ^ a) & c) ^ a) + (uint) Constants.C7 + buff[7];
  			b = (b << 22) | (b >> 10);
  			b += c;
  
  			a += (((c ^ d) & b) ^ d) + (uint) Constants.C8 + buff[8];
  			a = (a << 7) | (a >> 25);
  			a += b;
  
  			d += (((b ^ c) & a) ^ c) + (uint) Constants.C9 + buff[9];
  			d = (d << 12) | (d >> 20);
  			d += a;
  
  			c += (((a ^ b) & d) ^ b) + (uint) Constants.C10 + buff[10];
  			c = (c << 17) | (c >> 15);
  			c += d;
  
  			b += (((d ^ a) & c) ^ a) + (uint) Constants.C11 + buff[11];
  			b = (b << 22) | (b >> 10);
  			b += c;
  
  			a += (((c ^ d) & b) ^ d) + (uint) Constants.C12 + buff[12];
  			a = (a << 7) | (a >> 25);
  			a += b;
  
  			d += (((b ^ c) & a) ^ c) + (uint) Constants.C13 + buff[13];
  			d = (d << 12) | (d >> 20);
  			d += a;
  
  			c += (((a ^ b) & d) ^ b) + (uint) Constants.C14 + buff[14];
  			c = (c << 17) | (c >> 15);
  			c += d;
  
  			b += (((d ^ a) & c) ^ a) + (uint) Constants.C15 + buff[15];
  			b = (b << 22) | (b >> 10);
  			b += c;
  
  
  			// ---- Round 2 --------
  
  			a += (((b ^ c) & d) ^ c) + (uint) Constants.C16 + buff[1];
  			a = (a << 5) | (a >> 27);
  			a += b;
  
  			d += (((a ^ b) & c) ^ b) + (uint) Constants.C17 + buff[6];
  			d = (d << 9) | (d >> 23);
  			d += a;
  
  			c += (((d ^ a) & b) ^ a) + (uint) Constants.C18 + buff[11];
  			c = (c << 14) | (c >> 18);
  			c += d;
  
  			b += (((c ^ d) & a) ^ d) + (uint) Constants.C19 + buff[0];
  			b = (b << 20) | (b >> 12);
  			b += c;
  
  			a += (((b ^ c) & d) ^ c) + (uint) Constants.C20 + buff[5];
  			a = (a << 5) | (a >> 27);
  			a += b;
  
  			d += (((a ^ b) & c) ^ b) + (uint) Constants.C21 + buff[10];
  			d = (d << 9) | (d >> 23);
  			d += a;
  
  			c += (((d ^ a) & b) ^ a) + (uint) Constants.C22 + buff[15];
  			c = (c << 14) | (c >> 18);
  			c += d;
  
  			b += (((c ^ d) & a) ^ d) + (uint) Constants.C23 + buff[4];
  			b = (b << 20) | (b >> 12);
  			b += c;
  
  			a += (((b ^ c) & d) ^ c) + (uint) Constants.C24 + buff[9];
  			a = (a << 5) | (a >> 27);
  			a += b;
  
  			d += (((a ^ b) & c) ^ b) + (uint) Constants.C25 + buff[14];
  			d = (d << 9) | (d >> 23);
  			d += a;
  
  			c += (((d ^ a) & b) ^ a) + (uint) Constants.C26 + buff[3];
  			c = (c << 14) | (c >> 18);
  			c += d;
  
  			b += (((c ^ d) & a) ^ d) + (uint) Constants.C27 + buff[8];
  			b = (b << 20) | (b >> 12);
  			b += c;
  
  			a += (((b ^ c) & d) ^ c) + (uint) Constants.C28 + buff[13];
  			a = (a << 5) | (a >> 27);
  			a += b;
  
  			d += (((a ^ b) & c) ^ b) + (uint) Constants.C29 + buff[2];
  			d = (d << 9) | (d >> 23);
  			d += a;
  
  			c += (((d ^ a) & b) ^ a) + (uint) Constants.C30 + buff[7];
  			c = (c << 14) | (c >> 18);
  			c += d;
  
  			b += (((c ^ d) & a) ^ d) + (uint) Constants.C31 + buff[12];
  			b = (b << 20) | (b >> 12);
  			b += c;
  
  
  			// ---- Round 3 --------
  
  
gt: 			a += (b ^ c ^ d) + (uint) Constants.C32 + buff[5];