context: // DeflateStream.cs
  // ------------------------------------------------------------------
  //
  // Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.  
  // All rights reserved.
  //
  // This code module is part of DotNetZip, a zipfile class library.
  //
  // ------------------------------------------------------------------
  //
  // This code is licensed under the Microsoft Public License. 
  // See the file License.txt for the license details.
  // More info on: http://dotnetzip.codeplex.com
  //
  // ------------------------------------------------------------------
  //
  // last saved (in emacs): 
  // Time-stamp: <2009-November-03 18:48:49>
  //
  // ------------------------------------------------------------------
  //
  // This module defines the DeflateStream class, which can be used as a replacement for
  // the System.IO.Compression.DeflateStream class in the .NET BCL.
  //
  // ------------------------------------------------------------------
  
  
  using System;
  
  namespace PMDCP.Compression.Zlib
  {
      /// <summary>
      /// A class for compressing and decompressing streams using the Deflate algorithm.
      /// </summary>
      ///
      /// <remarks>
      ///
      /// <para>
      /// The DeflateStream is a <see
      /// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
      /// cref="System.IO.Stream"/>.  It adds DEFLATE compression or decompression to any stream.
      /// </para>
      ///
      /// <para> Using this stream, applications can compress or decompress data via
      /// stream <c>Read</c> and <c>Write</c> operations.  Either compresssion or
      /// decompression can occur through either reading or writing. The compression
      /// format used is DEFLATE, which is documented in <see
      /// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
      /// Compressed Data Format Specification version 1.3.".</para>
      ///
      /// <para>
      /// This class is similar to <see cref="ZlibStream"/>, except that <c>ZlibStream</c>
      /// adds the <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950 - ZLIB</see>
      /// framing bytes to a compressed stream when compressing, or expects the RFC1950
      /// framing bytes when decompressing. The <c>DeflateStream</c> does not.
      /// </para>
      ///
      /// </remarks>
      ///
      /// <seealso cref="DeflateStream" />
      /// <seealso cref="GZipStream" />
      public class DeflateStream : System.IO.Stream
      {
          internal ZlibBaseStream _baseStream;
          internal System.IO.Stream _innerStream;
          bool _disposed;
  
          /// <summary>
          /// Create a DeflateStream using the specified CompressionMode.
          /// </summary>
          /// 
          /// <remarks> When mode is <c>CompressionMode.Compress</c>, the DeflateStream
          /// will use the default compression level. The "captive" stream will be closed
          /// when the DeflateStream is closed.  </remarks>
          /// 
          /// <example>
          /// This example uses a DeflateStream to compress data from a file, and writes
          /// the compressed data to another file.
          /// <code>
          /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
          /// {
          ///     using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
          ///     {
          ///         using (Stream compressor = new DeflateStream(raw, CompressionMode.Compress))
          ///         {
          ///             byte[] buffer = new byte[WORKING_BUFFER_SIZE];
          ///             int n;
          ///             while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
          ///             {
          ///                 compressor.Write(buffer, 0, n);
          ///             }
          ///         }
          ///     }
          /// }
          /// </code>
          /// <code lang="VB">
          /// Using input As Stream = File.OpenRead(fileToCompress)
          ///     Using raw As FileStream = File.Create(fileToCompress &amp; ".deflated")
          ///         Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress)
          ///             Dim buffer As Byte() = New Byte(4096) {}
          ///             Dim n As Integer = -1
          ///             Do While (n &lt;&gt; 0)
          ///                 If (n &gt; 0) Then
          ///                     compressor.Write(buffer, 0, n)
          ///                 End If
          ///                 n = input.Read(buffer, 0, buffer.Length)
          ///             Loop
          ///         End Using
          ///     End Using
          /// End Using
          /// </code>
          /// </example>
          /// <param name="stream">The stream which will be read or written.</param>
          /// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
          public DeflateStream(System.IO.Stream stream, CompressionMode mode)
              : this(stream, mode, CompressionLevel.Default, false)
          {
          }
  
          /// <summary>
          /// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel.
          /// </summary>
          ///
          /// <remarks>
          ///
          /// <para> When mode is <c>CompressionMode.Decompress</c>, the level parameter
          /// is ignored.  The "captive" stream will be closed when the DeflateStream is
          /// closed.
          /// </para>
          ///
          /// </remarks>
          ///
          /// <example>
          /// This example uses a DeflateStream to compress data from a file, and writes
          /// the compressed data to another file.
          /// <code>
          /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
          /// {
          ///     using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
          ///     {
          ///         using (Stream compressor = new DeflateStream(raw,
          ///                                                      CompressionMode.Compress, 
          ///                                                      CompressionLevel.BestCompression))
          ///         {
          ///             byte[] buffer = new byte[WORKING_BUFFER_SIZE];
          ///             int n= -1;
          ///             while (n != 0)
          ///             {
          ///                 if (n &gt; 0) 
          ///                     compressor.Write(buffer, 0, n);
          ///                 n= input.Read(buffer, 0, buffer.Length);
          ///             }
          ///         }
          ///     }
          /// }
          /// </code>
          ///
          /// <code lang="VB">
          /// Using input As Stream = File.OpenRead(fileToCompress)
          ///     Using raw As FileStream = File.Create(fileToCompress &amp; ".deflated")
          ///         Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
          ///             Dim buffer As Byte() = New Byte(4096) {}
          ///             Dim n As Integer = -1
          ///             Do While (n &lt;&gt; 0)
          ///                 If (n &gt; 0) Then
          ///                     compressor.Write(buffer, 0, n)
          ///                 End If
          ///                 n = input.Read(buffer, 0, buffer.Length)
          ///             Loop
          ///         End Using
          ///     End Using
          /// End Using
          /// </code>
          /// </example>
          /// <param name="stream">The stream to be read or written while deflating or inflating.</param>
          /// <param name="mode">Indicates whether the <c>DeflateStream</c> will compress or decompress.</param>
          /// <param name="level">A tuning knob to trade speed for effectiveness.</param>
          public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
              : this(stream, mode, level, false)
          {
          }
  
          /// <summary>
          ///   Create a <c>DeflateStream</c> using the specified
          ///   <c>CompressionMode</c>, and explicitly specify whether the
          ///   stream should be left open after Deflation or Inflation.
          /// </summary>
          ///
          /// <remarks>
          ///
          /// <para>
          ///   This constructor allows the application to request that the captive stream
          ///   remain open after the deflation or inflation occurs.  By default, after
          ///   <c>Close()</c> is called on the stream, the captive stream is also
          ///   closed. In some cases this is not desired, for example if the stream is a
          ///   memory stream that will be re-read after compression.  Specify true for
          ///   the <paramref name="leaveOpen"/> parameter to leave the stream open.
          /// </para>
          ///
          /// <para>
          ///   The <c>DeflateStream</c> will use the default compression level.
          /// </para>
          ///
          /// <para>
          ///   See the other overloads of this constructor for example code.
          /// </para>
          /// </remarks>
          ///
          /// <param name="stream">
          ///   The stream which will be read or written. This is called the 
          ///   "captive" stream in other places in this documentation.
          /// </param>
          ///
          /// <param name="mode">
          ///   Indicates whether the <c>DeflateStream</c> will compress or decompress.
          /// </param>
          ///
          /// <param name="leaveOpen">true if the application would like the stream to
          /// remain open after inflation/deflation.</param>
          public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
              : this(stream, mode, CompressionLevel.Default, leaveOpen)
          {
          }
  
          /// <summary>
          ///   Create a <c>DeflateStream</c> using the specified <c>CompressionMode</c>
          ///   and the specified <c>CompressionLevel</c>, and explicitly specify whether
          ///   the stream should be left open after Deflation or Inflation.
          /// </summary>
          ///
          /// <remarks>
          ///
          /// <para>
          ///   When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored. 
          /// </para>
          ///
          /// <para>
          ///   This constructor allows the application to request that the captive stream
          ///   remain open after the deflation or inflation occurs.  By default, after
          ///   <c>Close()</c> is called on the stream, the captive stream is also
          ///   closed. In some cases this is not desired, for example if the stream is a
          ///   <see cref="System.IO.MemoryStream"/> that will be re-read after
          ///   compression.  Specify true for the <paramref name="leaveOpen"/> parameter
          ///   to leave the stream open.
          /// </para>
          ///
          /// </remarks>
          ///
          /// <example>
          /// 
          ///   This example shows how to use a <c>DeflateStream</c> to compress data from
          ///   a file, and store the compressed data into another file.
          /// 
          /// <code>
          /// using (var output = System.IO.File.Create(fileToCompress + ".deflated"))
          /// {
          ///     using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
          ///     {
          ///         using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
          ///         {
          ///             byte[] buffer = new byte[WORKING_BUFFER_SIZE];
          ///             int n= -1;
          ///             while (n != 0)
          ///             {
          ///                 if (n &gt; 0) 
          ///                     compressor.Write(buffer, 0, n);
          ///                 n= input.Read(buffer, 0, buffer.Length);
          ///             }
          ///         }
          ///     }
          ///     // can write additional data to the output stream here
          /// }
          /// </code>
          ///
          /// <code lang="VB">
          /// Using output As FileStream = File.Create(fileToCompress &amp; ".deflated")
          ///     Using input As Stream = File.OpenRead(fileToCompress)
          ///         Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
          ///             Dim buffer As Byte() = New Byte(4096) {}
          ///             Dim n As Integer = -1
          ///             Do While (n &lt;&gt; 0)
          ///                 If (n &gt; 0) Then
          ///                     compressor.Write(buffer, 0, n)
          ///                 End If
          ///                 n = input.Read(buffer, 0, buffer.Length)
          ///             Loop
          ///         End Using
          ///     End Using
          ///     ' can write additional data to the output stream here.
          /// End Using
          /// </code>
          /// </example>
          /// <param name="stream">The stream which will be read or written.</param>
          /// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
          /// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
          /// <param name="level">A tuning knob to trade speed for effectiveness.</param>
          public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
          {
              _innerStream = stream;
              _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
          }
  
          #region Zlib properties
  
          /// <summary>
          /// This property sets the flush behavior on the stream.  
          /// </summary>
          /// <remarks> See the ZLIB documentation for the meaning of the flush behavior.
          /// </remarks>
          virtual public FlushType FlushMode
          {
              get { return (this._baseStream._flushMode); }
              set
              {
                  if (_disposed) throw new ObjectDisposedException("DeflateStream");
                  this._baseStream._flushMode = value;
              }
          }
  
          /// <summary>
          ///   The size of the working buffer for the compression codec. 
          /// </summary>
          ///
          /// <remarks>
          /// <para>
          ///   The working buffer is used for all stream operations.  The default size is
          ///   1024 bytes.  The minimum size is 128 bytes. You may get better performance
          ///   with a larger buffer.  Then again, you might not.  You would have to test
          ///   it.
          /// </para>
          ///
          /// <para>
          ///   Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
          ///   stream. If you try to set it afterwards, it will throw.
          /// </para>
          /// </remarks>
          public int BufferSize
          {
              get
              {
                  return this._baseStream._bufferSize;
              }
              set
              {
                  if (_disposed) throw new ObjectDisposedException("DeflateStream");
                  if (this._baseStream._workingBuffer != null)
                      throw new ZlibException("The working buffer is already set.");
                  if (value < ZlibConstants.WorkingBufferSizeMin)
                      throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
                  this._baseStream._bufferSize = value;
              }
          }
  
          /// <summary>
          ///   The ZLIB strategy to be used during compression.
          /// </summary>
          ///
          /// <remarks>
          ///   By tweaking this parameter, you may be able to optimize the compression for 
          ///   data with particular characteristics.
          /// </remarks>
          public CompressionStrategy Strategy
          {
              get
              {
                  return this._baseStream.Strategy;
              }
              set
              {
              if (_disposed) throw new ObjectDisposedException("DeflateStream");
                  this._baseStream.Strategy = value;
              }
          }
  
          /// <summary> Returns the total number of bytes input so far.</summary>
          virtual public long TotalIn
          {
              get
              {
                  return this._baseStream._z.TotalBytesIn;
              }
          }
  
          /// <summary> Returns the total number of bytes output so far.</summary>
          virtual public long TotalOut
          {
              get
              {
                  return this._baseStream._z.TotalBytesOut;
              }
          }
  
          #endregion
  
          #region System.IO.Stream methods
          /// <summary>
          /// Dispose the stream.  
          /// </summary>
          /// <remarks>
          /// This may or may not result in a <c>Close()</c> call on the captive stream. 
          /// See the constructors that have a <c>leaveOpen</c> parameter for more information.
          /// </remarks>
          protected override void Dispose(bool disposing)
          {
              try
              {
                  if (!_disposed)
                  {
                      if (disposing && (this._baseStream != null))
                          this._baseStream.Close();
                      _disposed = true;
                  }
              }
              finally
              {
                  base.Dispose(disposing);
              }
          }
  
          
  
          /// <summary>
          /// Indicates whether the stream can be read.
          /// </summary>
          /// <remarks>
          /// The return value depends on whether the captive stream supports reading.
          /// </remarks>
          public override bool CanRead
          {
              get
              {
                  if (_disposed) throw new ObjectDisposedException("DeflateStream");
                  return _baseStream._stream.CanRead;
              }
          }
  
          /// <summary>
          /// Indicates whether the stream supports Seek operations.
          /// </summary>
          /// <remarks>
          /// Always returns false.
          /// </remarks>
          public override bool CanSeek
          {
              get { return false; }
          }
  
  
          /// <summary>
          /// Indicates whether the stream can be written.
          /// </summary>
          /// <remarks>
          /// The return value depends on whether the captive stream supports writing.
          /// </remarks>
          public override bool CanWrite
          {
              get
              {
                  if (_disposed) throw new ObjectDisposedException("DeflateStream");
                  return _baseStream._stream.CanWrite;
              }
          }
  
          /// <summary>
          /// Flush the stream.
          /// </summary>
          public override void Flush()
          {
              if (_disposed) throw new ObjectDisposedException("DeflateStream");
              _baseStream.Flush();
          }
  
          /// <summary>
          /// Reading this property always throws a <see cref="NotImplementedException"/>.
          /// </summary>
          public override long Length
          {
              get { throw new NotImplementedException(); }
          }
  
          /// <summary>
          /// The position of the stream pointer. 
          /// </summary>
          ///
          /// <remarks>
          ///   Setting this property always throws a <see
          ///   cref="NotImplementedException"/>. Reading will return the total bytes
          ///   written out, if used in writing, or the total bytes read in, if used in
          ///   reading.  The count may refer to compressed bytes or uncompressed bytes,
          ///   depending on how you've used the stream.
          /// </remarks>
          public override long Position
          {
              get
              {
                  if (this._baseStream._streamMode == PMDCP.Compression.Zlib.ZlibBaseStream.StreamMode.Writer)
                      return this._baseStream._z.TotalBytesOut;
                  if (this._baseStream._streamMode == PMDCP.Compression.Zlib.ZlibBaseStream.StreamMode.Reader)
                      return this._baseStream._z.TotalBytesIn;
                  return 0;
              }
              set { throw new NotImplementedException(); }
          }
  
          /// <summary>
          /// Read data from the stream. 
          /// </summary>
          /// <remarks>
          ///
          /// <para>
          ///   If you wish to use the <c>DeflateStream</c> to compress data while
          ///   reading, you can create a <c>DeflateStream</c> with
          ///   <c>CompressionMode.Compress</c>, providing an uncompressed data stream.
          ///   Then call Read() on that <c>DeflateStream</c>, and the data read will be
          ///   compressed as you read.  If you wish to use the <c>DeflateStream</c> to
          ///   decompress data while reading, you can create a <c>DeflateStream</c> with
          ///   <c>CompressionMode.Decompress</c>, providing a readable compressed data
          ///   stream.  Then call Read() on that <c>DeflateStream</c>, and the data read
          ///   will be decompressed as you read.
          /// </para>
          ///
          /// <para>
          ///   A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both. 
          /// </para>
          ///
          /// </remarks>
          /// <param name="buffer">The buffer into which the read data should be placed.</param>
          /// <param name="offset">the offset within that data array to put the first byte read.</param>
          /// <param name="count">the number of bytes to read.</param>
          /// <returns>the number of bytes actually read</returns>
          public override int Read(byte[] buffer, int offset, int count)
          {
              if (_disposed) throw new ObjectDisposedException("DeflateStream");
  
gt:             return _baseStream.Read(buffer, offset, count);