[docs] Import of Microsoft BCL Documentation.
[mono.git] / mcs / class / corlib / System.Security.Cryptography / FromBase64Transform.cs
index b7b8df1a45947f338ec82a28b07ef5d59a9a723b..bf4150c57d1e404e332ab54d4348388f60c5b90b 100644 (file)
@@ -1,51 +1,63 @@
 //
-// System.Security.Cryptography.FromBase64Transform
+// System.Security.Cryptography.FromBase64Transform.cs
 //
-// Author:
-//   Sergey Chaban (serge@wildwestsoftware.com)
+// Authors:
+//     Sergey Chaban (serge@wildwestsoftware.com)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// (C) 2004 Novell (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.Globalization;
+using System.Runtime.InteropServices;
 
 namespace System.Security.Cryptography {
 
        [Serializable]
+       [ComVisible (true)]
        public enum FromBase64TransformMode : int {
                IgnoreWhiteSpaces,
                DoNotIgnoreWhiteSpaces
        }
 
+       [ComVisible (true)]
        public class FromBase64Transform : ICryptoTransform {
 
                private FromBase64TransformMode mode;
                private byte[] accumulator;
-               private byte[] filterBuffer;
                private int accPtr;
                private bool m_disposed;
 
+               private const byte TerminatorByte = ((byte) '=');
 
-               /// <summary>
-               ///  Creates a new instance of the decoder
-               ///  with the default transformation mode (IgnoreWhiteSpaces).
-               /// </summary>
                public FromBase64Transform ()
                        : this (FromBase64TransformMode.IgnoreWhiteSpaces)
                {
                }
 
-
-               /// <summary>
-               ///  Creates a new instance of the decoder
-               ///  with the specified transformation mode.
-               /// </summary>
-               public FromBase64Transform (FromBase64TransformMode mode)
+               public FromBase64Transform (FromBase64TransformMode whitespaces)
                {
-                       this.mode = mode;
+                       this.mode = whitespaces;
                        accumulator = new byte [4];
-                       filterBuffer = new byte [4];
                        accPtr = 0;
                        m_disposed = false;
                }
@@ -63,22 +75,10 @@ namespace System.Security.Cryptography {
                        get { return true; }
                }
 
-               /// <summary>
-               ///  Returns the input block size for the Base64 decoder.
-               /// </summary>
-               /// <remarks>
-               ///  The input block size for Base64 decoder is always 1 byte.
-               /// </remarks>
                public int InputBlockSize {
                        get { return 1; }
                }
 
-               /// <summary>
-               ///  Returns the output block size for the Base64 decoder.
-               /// </summary>
-               /// <remarks>
-               ///  The value returned by this property is always 3.
-               /// </remarks>
                public int OutputBlockSize {
                        get { return 3; }
                }
@@ -88,7 +88,11 @@ namespace System.Security.Cryptography {
                        Dispose (true);
                }
 
+#if NET_4_0
+               public void Dispose ()
+#else
                void IDisposable.Dispose () 
+#endif
                {
                        Dispose (true);
                        GC.SuppressFinalize (this);  // Finalization is now unnecessary
@@ -100,42 +104,16 @@ namespace System.Security.Cryptography {
                                // zeroize data
                                if (accumulator != null)
                                        Array.Clear (accumulator, 0, accumulator.Length);
-                               if (filterBuffer != null)
-                                       Array.Clear (filterBuffer, 0, filterBuffer.Length);
 
                                // dispose unmanaged objects
                                if (disposing) {
                                        // dispose managed objects
                                        accumulator = null;
-                                       filterBuffer = null;
                                }
                                m_disposed = true;
                        }
                }
 
-               private int Filter (byte [] buffer, int offset, int count)
-               {
-                       int end = offset + count;
-                       int len = filterBuffer.Length;
-                       int ptr = 0;
-                       byte[] filter = this.filterBuffer;
-
-                       for (int i = offset; i < end; i++) {
-                               byte b = buffer [i];
-                               if (!Char.IsWhiteSpace ((char) b)) {
-                                       if (ptr >= len) {
-                                               len <<= 1;
-                                               this.filterBuffer = new byte [len];
-                                               Buffer.BlockCopy (filter, 0, this.filterBuffer, 0, len >> 1);
-                                               filter = this.filterBuffer;
-                                       }
-                                       filter [ptr++] = b;
-                               }
-                       }
-
-                       return ptr;
-               }
-
                private byte[] lookupTable; 
 
                private byte lookup (byte input)
@@ -154,92 +132,86 @@ namespace System.Security.Cryptography {
                        return ret;
                }
 
-               private int DoTransform (byte [] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
+               private int ProcessBlock (byte[] output, int offset)
                {
-                       int full = inputCount >> 2;
-                       if (full == 0)
-                               return 0;
-
                        int rem = 0;
-
-                       if (inputBuffer[inputCount - 1] == (byte)'=') {
-                               ++rem;
-                               --full;
-                       }
-
-                       if (inputBuffer[inputCount - 2] == (byte)'=')
-                               ++rem;
+                       if (accumulator [3] == TerminatorByte)
+                               rem++;
+                       if (accumulator [2] == TerminatorByte)
+                               rem++;
 
                        lookupTable = Base64Constants.DecodeTable;
                        int b0,b1,b2,b3;
 
-                       for (int i = 0; i < full; i++) {
-                               b0 = lookup (inputBuffer [inputOffset++]);
-                               b1 = lookup (inputBuffer [inputOffset++]);
-                               b2 = lookup (inputBuffer [inputOffset++]);
-                               b3 = lookup (inputBuffer [inputOffset++]);
-
-                               outputBuffer [outputOffset++] = (byte) ((b0 << 2) | (b1 >> 4));
-                               outputBuffer [outputOffset++] = (byte) ((b1 << 4) | (b2 >> 2));
-                               outputBuffer [outputOffset++] = (byte) ((b2 << 6) | b3);
-                       }
-
-                       int res = full * 3;
-
                        switch (rem) {
                        case 0:
+                               b0 = lookup (accumulator [0]);
+                               b1 = lookup (accumulator [1]);
+                               b2 = lookup (accumulator [2]);
+                               b3 = lookup (accumulator [3]);
+                               output [offset++] = (byte) ((b0 << 2) | (b1 >> 4));
+                               output [offset++] = (byte) ((b1 << 4) | (b2 >> 2));
+                               output [offset] = (byte) ((b2 << 6) | b3);
                                break;
                        case 1:
-                               b0 = lookup (inputBuffer [inputOffset++]);
-                               b1 = lookup (inputBuffer [inputOffset++]);
-                               b2 = lookup (inputBuffer [inputOffset++]);
-                               outputBuffer [outputOffset++] = (byte) ((b0 << 2) | (b1 >> 4));
-                               outputBuffer [outputOffset++] = (byte) ((b1 << 4) | (b2 >> 2));
-                               res += 2;
+                               b0 = lookup (accumulator [0]);
+                               b1 = lookup (accumulator [1]);
+                               b2 = lookup (accumulator [2]);
+                               output [offset++] = (byte) ((b0 << 2) | (b1 >> 4));
+                               output [offset] = (byte) ((b1 << 4) | (b2 >> 2));
                                break;
                        case 2:
-                               b0 = lookup (inputBuffer [inputOffset++]);
-                               b1 = lookup (inputBuffer [inputOffset++]);
-                               outputBuffer [outputOffset++] = (byte) ((b0 << 2) | (b1 >> 4));
-                               ++res;
+                               b0 = lookup (accumulator [0]);
+                               b1 = lookup (accumulator [1]);
+                               output [offset] = (byte) ((b0 << 2) | (b1 >> 4));
                                break;
                        }
 
-                       return res;
+                       return (3 - rem);
+               }
+
+               private void CheckInputParameters (byte[] inputBuffer, int inputOffset, int inputCount)
+               {
+                       if (inputBuffer == null)
+                               throw new ArgumentNullException ("inputBuffer");
+                       if (inputOffset < 0)
+                               throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
+                       if (inputCount > inputBuffer.Length)
+                               throw new OutOfMemoryException ("inputCount " + Locale.GetText ("Overflow"));
+                       if (inputOffset > inputBuffer.Length - inputCount)
+                               throw new ArgumentException ("inputOffset", Locale.GetText ("Overflow"));
+                       if (inputCount < 0)
+                               throw new OverflowException ("inputCount < 0");
                }
 
                public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
                {
                        if (m_disposed)
                                throw new ObjectDisposedException ("FromBase64Transform");
+                       // LAMESPEC: undocumented exceptions
+                       CheckInputParameters (inputBuffer, inputOffset, inputCount);
+                       if ((outputBuffer == null) || (outputOffset < 0))
+                               throw new FormatException ("outputBuffer");
 
-                       int n;
-                       byte [] src;
-                       int srcOff;
                        int res = 0;
 
-                       if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
-                               n = Filter (inputBuffer, inputOffset, inputCount);
-                               src = filterBuffer;
-                               srcOff = 0;
-                       } else {
-                               n = inputCount;
-                               src = inputBuffer;
-                               srcOff = inputOffset;
-                       }
-
-                       int count = accPtr + n;
-
-                       if (count < 4) {
-                               Buffer.BlockCopy (src, srcOff, accumulator, accPtr, n);
-                               accPtr = count;
-                       } else {
-                               byte[] tmpBuff = new byte [count];
-                               Buffer.BlockCopy (accumulator, 0, tmpBuff, 0, accPtr);
-                               Buffer.BlockCopy (src, srcOff, tmpBuff, accPtr, n);
-                               accPtr = count & 3;
-                               Buffer.BlockCopy (src, srcOff + (n - accPtr), accumulator, 0, accPtr);
-                               res = DoTransform (tmpBuff, 0, count & (~3), outputBuffer, outputOffset) ;
+                       while (inputCount > 0) {
+                               if (accPtr < 4) {
+                                       byte b = inputBuffer [inputOffset++];
+                                       if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
+                                               if (!Char.IsWhiteSpace ((char) b))
+                                                       accumulator [accPtr++] = b;
+                                       } else {
+                                               // don't ignore, we'll fail if bad data is provided
+                                               accumulator [accPtr++] = b;
+                                       }
+                               }
+                               if (accPtr == 4) {
+                                       res += ProcessBlock (outputBuffer, outputOffset);
+                                       outputOffset += 3;
+                                       accPtr = 0;
+                               }
+                               inputCount--;
                        }
 
                        return res;
@@ -249,41 +221,56 @@ namespace System.Security.Cryptography {
                {
                        if (m_disposed)
                                throw new ObjectDisposedException ("FromBase64Transform");
+                       // LAMESPEC: undocumented exceptions
+                       CheckInputParameters (inputBuffer, inputOffset, inputCount);
 
-                       byte[] src;
-                       int srcOff;
-                       int n;
-
+                       int ws = 0;
+                       int terminator = 0;
                        if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
-                               n = Filter (inputBuffer, inputOffset, inputCount);
-                               src = filterBuffer;
-                               srcOff = 0;
+                               // count whitespace inside string
+                               for (int i=inputOffset, j=0; j < inputCount; i++, j++) {
+                                       if (Char.IsWhiteSpace ((char)inputBuffer [i]))
+                                               ws++;
+                               }
+                               // no more (useful) data
+                               if (ws == inputCount)
+                                       return new byte [0];
+                               // there may be whitespace after the terminator
+                               int k = inputOffset + inputCount - 1;
+                               int n = Math.Min (2, inputCount);
+                               while (n > 0) {
+                                       char c = (char) inputBuffer [k--];
+                                       if (c == '=') {
+                                               terminator++;
+                                               n--;
+                                       } else if (Char.IsWhiteSpace (c)) {
+                                               continue;
+                                       } else {
+                                               break;
+                                       }
+                               }                                               
                        } else {
-                               n = inputCount;
-                               src = inputBuffer;
-                               srcOff = inputOffset;
+                               if (inputBuffer [inputOffset + inputCount - 1] == TerminatorByte)
+                                       terminator++;
+                               if (inputBuffer [inputOffset + inputCount - 2] == TerminatorByte)
+                                       terminator++;
+                       }
+                       // some terminators could already be in the accumulator
+                       if ((inputCount < 4) && (terminator < 2)) {
+                               if ((accPtr > 2) && (accumulator [3] == TerminatorByte))
+                                       terminator++;
+                               if ((accPtr > 1) && (accumulator [2] == TerminatorByte))
+                                       terminator++;
                        }
 
-                       int dataLen = accPtr + n;
-                       byte[] tmpBuf = new byte [dataLen];
-
-                       int resLen = ((dataLen) >> 2) * 3;
-                       byte[] res = new byte [resLen];
-
-                       Buffer.BlockCopy (accumulator, 0, tmpBuf, 0, accPtr);
-                       Buffer.BlockCopy (src, srcOff, tmpBuf, accPtr, n);
-                       
-                       int actLen = DoTransform (tmpBuf, 0, dataLen, res, 0);
-                       accPtr = 0;
-
-                       if (actLen < resLen) {
-                               byte[] newres = new byte [actLen];
+                       int count = ((accPtr + inputCount - ws) >> 2) * 3 - terminator;
+                       if (count <= 0)
+                               return new byte [0];
 
-                               Buffer.BlockCopy (res, 0, newres, 0, actLen);
-                               return newres;
-                       } 
-                       else
-                               return res;
+                       // allocate the "right" ammount (to avoid multiple allocation/copy)
+                       byte[] result = new byte [count];
+                       TransformBlock (inputBuffer, inputOffset, inputCount, result, 0);
+                       return result;
                }
        }
 }