handle IsTerminating.
[mono.git] / mcs / class / corlib / System.Security.Cryptography / CryptoStream.cs
index 4c0ab0561a8ed3d6c5612a067cb9a20891433969..48f3794f8cb04684e4d656580b8ac9fd33e2f415 100644 (file)
@@ -6,7 +6,7 @@
 //     Sebastien Pouliot (sebastien@ximian.com)
 //
 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005, 2007 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
@@ -34,9 +34,7 @@ using System.Runtime.InteropServices;
 
 namespace System.Security.Cryptography {
 
-#if NET_2_0
        [ComVisible (true)]
-#endif
        public class CryptoStream : Stream {
                private Stream _stream;
                private ICryptoTransform _transform;
@@ -59,13 +57,14 @@ namespace System.Security.Cryptography {
                
                public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
                {
-                       if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) {
-                               throw new ArgumentException (
-                                       Locale.GetText ("Can't read on stream"));
-                       }
-                       if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) {
-                               throw new ArgumentException (
-                                       Locale.GetText ("Can't write on stream"));
+                       if (mode == CryptoStreamMode.Read) {
+                               if (!stream.CanRead)
+                                       throw new ArgumentException (Locale.GetText ("Can't read on stream"));
+                       } else if (mode == CryptoStreamMode.Write) {
+                               if (!stream.CanWrite)
+                                       throw new ArgumentException (Locale.GetText ("Can't write on stream"));
+                       } else {
+                               throw new ArgumentException ("mode");
                        }
                        _stream = stream;
                        _transform = transform;
@@ -73,10 +72,6 @@ namespace System.Security.Cryptography {
                        _disposed = false;
                        if (transform != null) {
                                _workingBlock = new byte [transform.InputBlockSize];
-                               if (mode == CryptoStreamMode.Read)
-                                       _currentBlock = new byte [transform.InputBlockSize];
-                               else if (mode == CryptoStreamMode.Write)
-                                       _currentBlock = new byte [transform.OutputBlockSize];
                        }
                }
 
@@ -108,19 +103,13 @@ namespace System.Security.Cryptography {
 
                public void Clear () 
                {
-                       Dispose (true);
-                       GC.SuppressFinalize (this); // not called in Stream.Dispose
+                       Close ();
                }
 
                // LAMESPEC: A CryptoStream can be close in read mode
                public override void Close () 
                {
-                       // only flush in write mode (bugzilla 46143)
-                       if ((!_flushedFinalBlock) && (_mode == CryptoStreamMode.Write))
-                               FlushFinalBlock ();
-
-                       if (_stream != null)
-                               _stream.Close ();
+                       base.Close ();
                }
 
                public override int Read ([In,Out] byte[] buffer, int offset, int count)
@@ -145,10 +134,8 @@ namespace System.Security.Cryptography {
                                        Locale.GetText ("buffer overflow"));
                        }
                        // for some strange reason ObjectDisposedException isn't throw
-                       // instead we get a ArgumentNullException (probably from an internal method)
                        if (_workingBlock == null) {
-                               throw new ArgumentNullException (
-                                       Locale.GetText ("object _disposed"));
+                               return 0;
                        }
 
                        int result = 0;
@@ -172,7 +159,7 @@ namespace System.Security.Cryptography {
                                        int transformed = 0;
 
                                        // load a new block
-                                       _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length);
+                                       _workingCount = _stream.Read (_workingBlock, 0, _transform.InputBlockSize);
                                        _endOfStream = (_workingCount < _transform.InputBlockSize);
 
                                        if (!_endOfStream) {
@@ -195,18 +182,21 @@ namespace System.Security.Cryptography {
                                                        length += transformed;
                                                        _transformedCount += transformed;
                                                }
-                                               byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
-                                               transformed = input.Length;
-                                               Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length);
-                                               // zeroize this last block
-                                               Array.Clear (input, 0, input.Length);
+                                               if (!_flushedFinalBlock) {
+                                                       byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
+                                                       transformed = input.Length;
+                                                       Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length);
+                                                       // zeroize this last block
+                                                       Array.Clear (input, 0, input.Length);
+                                                       _flushedFinalBlock = true;
+                                               }
                                        }
 
                                        length += transformed;
                                        _transformedCount += transformed;
                                }
                                // compaction
-                               if (_transformedPos > _transform.InputBlockSize) {
+                               if (_transformedPos > _transform.OutputBlockSize) {
                                        Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
                                        _transformedCount -= _transformedPos;
                                        _transformedPos = 0;
@@ -252,6 +242,11 @@ namespace System.Security.Cryptography {
                                        Locale.GetText ("buffer overflow"));
                        }
 
+                       if (_stream == null)
+                               throw new ArgumentNullException ("inner stream was disposed");
+
+                       int buffer_length = count;
+
                        // partial block (in progress)
                        if ((_partialCount > 0) && (_partialCount != _transform.InputBlockSize)) {
                                int remainder = _transform.InputBlockSize - _partialCount;
@@ -265,6 +260,9 @@ namespace System.Security.Cryptography {
                        int bufferPos = offset;
                        while (count > 0) {
                                if (_partialCount == _transform.InputBlockSize) {
+                                       if (_currentBlock == null)
+                                               _currentBlock = new byte [_transform.OutputBlockSize];
+
                                        // use partial block to avoid (re)allocation
                                        int len = _transform.TransformBlock (_workingBlock, 0, _partialCount, _currentBlock, 0);
                                        _stream.Write (_currentBlock, 0, len);
@@ -273,25 +271,26 @@ namespace System.Security.Cryptography {
                                }
 
                                if (_transform.CanTransformMultipleBlocks) {
-                                       // transform all except the last block (which may be the last block
-                                       // of the stream and require TransformFinalBlock)
-                                       int numBlock = ((_partialCount + count) / _transform.InputBlockSize);
-                                       int multiSize = (numBlock * _transform.InputBlockSize);
-                                       if (numBlock > 0) {
-                                               byte[] multiBlocks = new byte [multiSize];
-                                               int len = _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0);
-                                               _stream.Write (multiBlocks, 0, len); 
-                                               // copy last block into _currentBlock
-                                               _partialCount = count - multiSize;
-                                               Buffer.BlockCopy (buffer, offset + multiSize, _workingBlock, 0, _partialCount);
+                                       // get the biggest multiple of InputBlockSize in count (without mul or div)
+                                       int size = (count & ~(_transform.InputBlockSize - 1));
+                                       int rem = (count & (_transform.InputBlockSize - 1));
+                                       // avoid reallocating memory at each call (reuse same buffer whenever possible)
+                                       int sizeWorkingBlock = (1 + size / _transform.InputBlockSize) * _transform.OutputBlockSize;
+                                       if (_workingBlock.Length < sizeWorkingBlock) {
+                                               Array.Clear (_workingBlock, 0, _workingBlock.Length);
+                                               _workingBlock = new byte [sizeWorkingBlock];
                                        }
-                                       else {
-                                               Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, count);
-                                               _partialCount += count;
+
+                                       if (size > 0) {
+                                               int len = _transform.TransformBlock (buffer, offset, size, _workingBlock, 0);
+                                               _stream.Write (_workingBlock, 0, len);
                                        }
+
+                                       if (rem > 0)
+                                               Buffer.BlockCopy (buffer, buffer_length - rem, _workingBlock, 0, rem);
+                                       _partialCount = rem;
                                        count = 0; // the last block, if any, is in _workingBlock
-                               }
-                               else {
+                               } else {
                                        int len = Math.Min (_transform.InputBlockSize - _partialCount, count);
                                        Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len);
                                        bufferPos += len;
@@ -305,29 +304,24 @@ namespace System.Security.Cryptography {
 
                public override void Flush ()
                {
-                       if (_stream != null)
-                               _stream.Flush ();
                }
 
                public void FlushFinalBlock ()
                {
-                       if (_flushedFinalBlock) {
-                               throw new NotSupportedException (
-                                       Locale.GetText ("This method cannot be called twice."));
-                       }
-                       if (_mode != CryptoStreamMode.Write) {
-                               throw new NotSupportedException (
-                                       Locale.GetText ("cannot flush a non-writeable CryptoStream"));
-                       }
+                       if (_flushedFinalBlock)
+                               throw new NotSupportedException (Locale.GetText ("This method cannot be called twice."));
+                       if (_disposed)
+                               throw new NotSupportedException (Locale.GetText ("CryptoStream was disposed."));
 
                        _flushedFinalBlock = true;
                        byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount);
-                       if (_stream != null) {
+                       if (_stream != null && _mode == CryptoStreamMode.Write) {
                                _stream.Write (finalBuffer, 0, finalBuffer.Length);
-                               if (_stream is CryptoStream) {
-                                       // for cascading crypto streams
-                                       (_stream as CryptoStream).FlushFinalBlock ();
-                               }
+                       }
+                       if (_stream is CryptoStream) {
+                               // for cascading crypto streams
+                               (_stream as CryptoStream).FlushFinalBlock ();
+                       } else {
                                _stream.Flush ();
                        }
                        // zeroize
@@ -345,9 +339,17 @@ namespace System.Security.Cryptography {
                        throw new NotSupportedException ("SetLength");
                }
 
-               protected virtual void Dispose (bool disposing) 
+               protected override void Dispose (bool disposing) 
                {
                        if (!_disposed) {
+                               if (disposing) {
+                                       if (!_flushedFinalBlock) {
+                                               FlushFinalBlock ();
+                                       }
+
+                                       if (_stream != null)
+                                               _stream.Close ();
+                               }
                                _disposed = true;
                                // always cleared for security reason
                                if (_workingBlock != null)