context: using System;
  using System.Diagnostics;
  
  using Org.BouncyCastle.Crypto.Parameters;
  
  namespace Org.BouncyCastle.Crypto
  {
  	/**
  	* A wrapper class that allows block ciphers to be used to process data in
  	* a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
  	* buffer is full and more data is being added, or on a doFinal.
  	* <p>
  	* Note: in the case where the underlying cipher is either a CFB cipher or an
  	* OFB one the last block may not be a multiple of the block size.
  	* </p>
  	*/
  	public class BufferedBlockCipher
  		: BufferedCipherBase
  	{
  		internal byte[]			buf;
  		internal int			bufOff;
  		internal bool			forEncryption;
  		internal IBlockCipher	cipher;
  
  		/**
  		* constructor for subclasses
  		*/
  		protected BufferedBlockCipher()
  		{
  		}
  
  		/**
  		* Create a buffered block cipher without padding.
  		*
  		* @param cipher the underlying block cipher this buffering object wraps.
  		* false otherwise.
  		*/
  		public BufferedBlockCipher(
  			IBlockCipher cipher)
  		{
  			if (cipher == null)
  				throw new ArgumentNullException("cipher");
  
  			this.cipher = cipher;
  			buf = new byte[cipher.GetBlockSize()];
  			bufOff = 0;
  		}
  
  		public override string AlgorithmName
  		{
  			get { return cipher.AlgorithmName; }
  		}
  
  		/**
  		* initialise the cipher.
  		*
  		* @param forEncryption if true the cipher is initialised for
  		*  encryption, if false for decryption.
  		* @param param the key and other data required by the cipher.
  		* @exception ArgumentException if the parameters argument is
  		* inappropriate.
  		*/
  		// Note: This doubles as the Init in the event that this cipher is being used as an IWrapper
  		public override void Init(
  			bool				forEncryption,
  			ICipherParameters	parameters)
  		{
  			this.forEncryption = forEncryption;
  
  			if (parameters is ParametersWithRandom)
  			{
  				ParametersWithRandom p = (ParametersWithRandom)parameters;
  				parameters = p.Parameters;
  			}
  
  			Reset();
  			cipher.Init(forEncryption, parameters);
  		}
  
  		/**
  		* return the blocksize for the underlying cipher.
  		*
  		* @return the blocksize for the underlying cipher.
  		*/
  		public override int GetBlockSize()
  		{
  			return cipher.GetBlockSize();
  		}
  
  		/**
  		* return the size of the output buffer required for an update
  		* an input of len bytes.
  		*
  		* @param len the length of the input.
  		* @return the space required to accommodate a call to update
  		* with len bytes of input.
  		*/
  		public virtual int GetUpdateOutputSize(
  			int length)
  		{
  			int total = length + bufOff;
  			int leftOver = total % buf.Length;
  			return total - leftOver;
  		}
  
  		/**
  		* return the size of the output buffer required for an update plus a
  		* doFinal with an input of len bytes.
  		*
  		* @param len the length of the input.
  		* @return the space required to accommodate a call to update and doFinal
  		* with len bytes of input.
  		*/
  		public override int GetOutputSize(
  			int length)
  		{
  			int total = length + bufOff;
  			int leftOver = total % buf.Length;
  			if (leftOver == 0)
  			{
  				return total;
  			}
  			return total - leftOver + buf.Length;
  		}
  
  		/**
  		* process a single byte, producing an output block if neccessary.
  		*
  		* @param in the input byte.
  		* @param out the space for any output that might be produced.
  		* @param outOff the offset from which the output will be copied.
  		* @return the number of output bytes copied to out.
  		* @exception DataLengthException if there isn't enough space in out.
  		* @exception InvalidOperationException if the cipher isn't initialised.
  		*/
  		public override int ProcessByte(
  			byte	input,
  			byte[]	output,
  			int		outOff)
  		{
  			buf[bufOff++] = input;
  
  			if (bufOff == buf.Length)
  			{
  				if ((outOff + buf.Length) > output.Length)
  					throw new DataLengthException("output buffer too short");
  
  				bufOff = 0;
  				return cipher.ProcessBlock(buf, 0, output, outOff);
  			}
  
  			return 0;
  		}
  
  		public override byte[] ProcessByte(
  			byte input)
  		{
  			int outLength = GetUpdateOutputSize(1);
  
  			byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
  
  			int pos = ProcessByte(input, outBytes, 0);
  
  			if (outLength > 0 && pos < outLength)
  			{
  				byte[] tmp = new byte[pos];
  				Array.Copy(outBytes, 0, tmp, 0, pos);
  				outBytes = tmp;
  			}
  
  			return outBytes;
  		}
  
  		public override byte[] ProcessBytes(
  			byte[]	input,
  			int		inOff,
  			int		length)
  		{
  			if (input == null)
  				throw new ArgumentNullException("input");
  			if (length < 1)
  				return null;
  
  			int outLength = GetUpdateOutputSize(length);
  
  			byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
  
  			int pos = ProcessBytes(input, inOff, length, outBytes, 0);
  
  			if (outLength > 0 && pos < outLength)
  			{
  				byte[] tmp = new byte[pos];
  				Array.Copy(outBytes, 0, tmp, 0, pos);
  				outBytes = tmp;
  			}
  
  			return outBytes;
  		}
  
  		/**
  		* process an array of bytes, producing output if necessary.
  		*
  		* @param in the input byte array.
  		* @param inOff the offset at which the input data starts.
  		* @param len the number of bytes to be copied out of the input array.
  		* @param out the space for any output that might be produced.
  		* @param outOff the offset from which the output will be copied.
  		* @return the number of output bytes copied to out.
  		* @exception DataLengthException if there isn't enough space in out.
  		* @exception InvalidOperationException if the cipher isn't initialised.
  		*/
  		public override int ProcessBytes(
  			byte[]	input,
  			int		inOff,
  			int		length,
  			byte[]	output,
  			int		outOff)
  		{
  			if (length < 1)
  			{
  
gt: 				if (length < 0)