// DeflateStream.cs // ------------------------------------------------------------------ // // Copyright (c) 2009-2010 Dino Chiesa. // 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: <2011-July-31 14:48:11> // // ------------------------------------------------------------------ // // 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 BestHTTP.Decompression.Zlib { /// /// A class for compressing and decompressing streams using the Deflate algorithm. /// /// /// /// /// /// The DeflateStream is a Decorator on a . It adds DEFLATE compression or decompression to any /// stream. /// /// /// /// Using this stream, applications can compress or decompress data via stream /// Read and Write operations. Either compresssion or decompression /// can occur through either reading or writing. The compression format used is /// DEFLATE, which is documented in IETF RFC 1951, "DEFLATE /// Compressed Data Format Specification version 1.3.". /// /// /// /// /// internal class DeflateStream : System.IO.Stream { internal ZlibBaseStream _baseStream; internal System.IO.Stream _innerStream; bool _disposed; /// /// Create a DeflateStream using the specified CompressionMode. /// /// /// /// When mode is CompressionMode.Compress, the DeflateStream will use /// the default compression level. The "captive" stream will be closed when /// the DeflateStream is closed. /// /// /// /// This example uses a DeflateStream to compress data from a file, and writes /// the compressed data to another file. /// /// 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); /// } /// } /// } /// } /// /// /// /// Using input As Stream = File.OpenRead(fileToCompress) /// Using raw As FileStream = File.Create(fileToCompress & ".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 <> 0) /// If (n > 0) Then /// compressor.Write(buffer, 0, n) /// End If /// n = input.Read(buffer, 0, buffer.Length) /// Loop /// End Using /// End Using /// End Using /// /// /// The stream which will be read or written. /// Indicates whether the DeflateStream will compress or decompress. public DeflateStream(System.IO.Stream stream, CompressionMode mode) : this(stream, mode, CompressionLevel.Default, false) { } /// /// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel. /// /// /// /// /// /// When mode is CompressionMode.Decompress, the level parameter is /// ignored. The "captive" stream will be closed when the DeflateStream is /// closed. /// /// /// /// /// /// /// This example uses a DeflateStream to compress data from a file, and writes /// the compressed data to another file. /// /// /// 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 > 0) /// compressor.Write(buffer, 0, n); /// n= input.Read(buffer, 0, buffer.Length); /// } /// } /// } /// } /// /// /// /// Using input As Stream = File.OpenRead(fileToCompress) /// Using raw As FileStream = File.Create(fileToCompress & ".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 <> 0) /// If (n > 0) Then /// compressor.Write(buffer, 0, n) /// End If /// n = input.Read(buffer, 0, buffer.Length) /// Loop /// End Using /// End Using /// End Using /// /// /// The stream to be read or written while deflating or inflating. /// Indicates whether the DeflateStream will compress or decompress. /// A tuning knob to trade speed for effectiveness. public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level) : this(stream, mode, level, false) { } /// /// Create a DeflateStream using the specified /// CompressionMode, and explicitly specify whether the /// stream should be left open after Deflation or Inflation. /// /// /// /// /// /// This constructor allows the application to request that the captive stream /// remain open after the deflation or inflation occurs. By default, after /// Close() 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 parameter to leave the stream open. /// /// /// /// The DeflateStream will use the default compression level. /// /// /// /// See the other overloads of this constructor for example code. /// /// /// /// /// The stream which will be read or written. This is called the /// "captive" stream in other places in this documentation. /// /// /// /// Indicates whether the DeflateStream will compress or decompress. /// /// /// true if the application would like the stream to /// remain open after inflation/deflation. public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen) : this(stream, mode, CompressionLevel.Default, leaveOpen) { } /// /// Create a DeflateStream using the specified CompressionMode /// and the specified CompressionLevel, and explicitly specify whether /// the stream should be left open after Deflation or Inflation. /// /// /// /// /// /// When mode is CompressionMode.Decompress, the level parameter is ignored. /// /// /// /// This constructor allows the application to request that the captive stream /// remain open after the deflation or inflation occurs. By default, after /// Close() 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 /// that will be re-read after /// compression. Specify true for the parameter /// to leave the stream open. /// /// /// /// /// /// /// This example shows how to use a DeflateStream to compress data from /// a file, and store the compressed data into another file. /// /// /// 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 > 0) /// compressor.Write(buffer, 0, n); /// n= input.Read(buffer, 0, buffer.Length); /// } /// } /// } /// // can write additional data to the output stream here /// } /// /// /// /// Using output As FileStream = File.Create(fileToCompress & ".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 <> 0) /// If (n > 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 /// /// /// The stream which will be read or written. /// Indicates whether the DeflateStream will compress or decompress. /// true if the application would like the stream to remain open after inflation/deflation. /// A tuning knob to trade speed for effectiveness. public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) { _innerStream = stream; _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen); } /// /// Create a DeflateStream using the specified CompressionMode /// and the specified CompressionLevel, and explicitly specify whether /// the stream should be left open after Deflation or Inflation. /// /// /// /// /// /// When mode is CompressionMode.Decompress, the level parameter is ignored. /// /// /// /// This constructor allows the application to request that the captive stream /// remain open after the deflation or inflation occurs. By default, after /// Close() 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 /// that will be re-read after /// compression. Specify true for the parameter /// to leave the stream open. /// /// /// /// /// /// /// This example shows how to use a DeflateStream to compress data from /// a file, and store the compressed data into another file. /// /// /// 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 > 0) /// compressor.Write(buffer, 0, n); /// n= input.Read(buffer, 0, buffer.Length); /// } /// } /// } /// // can write additional data to the output stream here /// } /// /// /// /// Using output As FileStream = File.Create(fileToCompress & ".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 <> 0) /// If (n > 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 /// /// /// The stream which will be read or written. /// Indicates whether the DeflateStream will compress or decompress. /// true if the application would like the stream to remain open after inflation/deflation. /// A tuning knob to trade speed for effectiveness. /// Desired window bits. public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen, int windowBits) { _innerStream = stream; _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen, windowBits); } #region Zlib properties /// /// This property sets the flush behavior on the stream. /// /// See the ZLIB documentation for the meaning of the flush behavior. /// virtual public FlushType FlushMode { get { return (this._baseStream._flushMode); } set { if (_disposed) throw new ObjectDisposedException("DeflateStream"); this._baseStream._flushMode = value; } } /// /// The size of the working buffer for the compression codec. /// /// /// /// /// 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. /// /// /// /// Set this before the first call to Read() or Write() on the /// stream. If you try to set it afterwards, it will throw. /// /// 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; } } /// /// The ZLIB strategy to be used during compression. /// /// /// /// By tweaking this parameter, you may be able to optimize the compression for /// data with particular characteristics. /// public CompressionStrategy Strategy { get { return this._baseStream.Strategy; } set { if (_disposed) throw new ObjectDisposedException("DeflateStream"); this._baseStream.Strategy = value; } } /// Returns the total number of bytes input so far. virtual public long TotalIn { get { return this._baseStream._z.TotalBytesIn; } } /// Returns the total number of bytes output so far. virtual public long TotalOut { get { return this._baseStream._z.TotalBytesOut; } } #endregion #region System.IO.Stream methods /// /// Dispose the stream. /// /// /// /// This may or may not result in a Close() call on the captive /// stream. See the constructors that have a leaveOpen parameter /// for more information. /// /// /// Application code won't call this code directly. This method may be /// invoked in two distinct scenarios. If disposing == true, the method /// has been called directly or indirectly by a user's code, for example /// via the public Dispose() method. In this case, both managed and /// unmanaged resources can be referenced and disposed. If disposing == /// false, the method has been called by the runtime from inside the /// object finalizer and this method should not reference other objects; /// in that case only unmanaged resources must be referenced or /// disposed. /// /// /// /// true if the Dispose method was invoked by user code. /// protected override void Dispose(bool disposing) { try { if (!_disposed) { if (disposing && (this._baseStream != null)) this._baseStream.Close(); _disposed = true; } } finally { base.Dispose(disposing); } } /// /// Indicates whether the stream can be read. /// /// /// The return value depends on whether the captive stream supports reading. /// public override bool CanRead { get { if (_disposed) throw new ObjectDisposedException("DeflateStream"); return _baseStream._stream.CanRead; } } /// /// Indicates whether the stream supports Seek operations. /// /// /// Always returns false. /// public override bool CanSeek { get { return false; } } /// /// Indicates whether the stream can be written. /// /// /// The return value depends on whether the captive stream supports writing. /// public override bool CanWrite { get { if (_disposed) throw new ObjectDisposedException("DeflateStream"); return _baseStream._stream.CanWrite; } } /// /// Flush the stream. /// public override void Flush() { if (_disposed) throw new ObjectDisposedException("DeflateStream"); _baseStream.Flush(); } /// /// Reading this property always throws a . /// public override long Length { get { throw new NotImplementedException(); } } /// /// The position of the stream pointer. /// /// /// /// Setting this property always throws a . 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. /// public override long Position { get { if (this._baseStream._streamMode == BestHTTP.Decompression.Zlib.ZlibBaseStream.StreamMode.Writer) return this._baseStream._z.TotalBytesOut; if (this._baseStream._streamMode == BestHTTP.Decompression.Zlib.ZlibBaseStream.StreamMode.Reader) return this._baseStream._z.TotalBytesIn; return 0; } set { throw new NotImplementedException(); } } /// /// Read data from the stream. /// /// /// /// /// If you wish to use the DeflateStream to compress data while /// reading, you can create a DeflateStream with /// CompressionMode.Compress, providing an uncompressed data stream. /// Then call Read() on that DeflateStream, and the data read will be /// compressed as you read. If you wish to use the DeflateStream to /// decompress data while reading, you can create a DeflateStream with /// CompressionMode.Decompress, providing a readable compressed data /// stream. Then call Read() on that DeflateStream, and the data read /// will be decompressed as you read. /// /// /// /// A DeflateStream can be used for Read() or Write(), but not both. /// /// /// /// The buffer into which the read data should be placed. /// the offset within that data array to put the first byte read. /// the number of bytes to read. /// the number of bytes actually read public override int Read(byte[] buffer, int offset, int count) { if (_disposed) throw new ObjectDisposedException("DeflateStream"); return _baseStream.Read(buffer, offset, count); } /// /// Calling this method always throws a . /// /// this is irrelevant, since it will always throw! /// this is irrelevant, since it will always throw! /// irrelevant! public override long Seek(long offset, System.IO.SeekOrigin origin) { throw new NotImplementedException(); } /// /// Will call the base stream's SetLength method. /// public override void SetLength(long value) { _baseStream.SetLength(value); } /// /// Write data to the stream. /// /// /// /// /// If you wish to use the DeflateStream to compress data while /// writing, you can create a DeflateStream with /// CompressionMode.Compress, and a writable output stream. Then call /// Write() on that DeflateStream, providing uncompressed data /// as input. The data sent to the output stream will be the compressed form /// of the data written. If you wish to use the DeflateStream to /// decompress data while writing, you can create a DeflateStream with /// CompressionMode.Decompress, and a writable output stream. Then /// call Write() on that stream, providing previously compressed /// data. The data sent to the output stream will be the decompressed form of /// the data written. /// /// /// /// A DeflateStream can be used for Read() or Write(), /// but not both. /// /// /// /// /// The buffer holding data to write to the stream. /// the offset within that data array to find the first byte to write. /// the number of bytes to write. public override void Write(byte[] buffer, int offset, int count) { if (_disposed) throw new ObjectDisposedException("DeflateStream"); _baseStream.Write(buffer, offset, count); } #endregion /// /// Compress a string into a byte array using DEFLATE (RFC 1951). /// /// /// /// Uncompress it with . /// /// /// DeflateStream.UncompressString(byte[]) /// DeflateStream.CompressBuffer(byte[]) /// GZipStream.CompressString(string) /// /// /// A string to compress. The string will first be encoded /// using UTF8, then compressed. /// /// /// The string in compressed form public static byte[] CompressString(String s) { using (var ms = new System.IO.MemoryStream()) { System.IO.Stream compressor = new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression); ZlibBaseStream.CompressString(s, compressor); return ms.ToArray(); } } /// /// Compress a byte array into a new byte array using DEFLATE. /// /// /// /// Uncompress it with . /// /// /// DeflateStream.CompressString(string) /// DeflateStream.UncompressBuffer(byte[]) /// GZipStream.CompressBuffer(byte[]) /// /// /// A buffer to compress. /// /// /// The data in compressed form public static byte[] CompressBuffer(byte[] b) { using (var ms = new System.IO.MemoryStream()) { System.IO.Stream compressor = new DeflateStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression ); ZlibBaseStream.CompressBuffer(b, compressor); return ms.ToArray(); } } /// /// Uncompress a DEFLATE'd byte array into a single string. /// /// /// DeflateStream.CompressString(String) /// DeflateStream.UncompressBuffer(byte[]) /// GZipStream.UncompressString(byte[]) /// /// /// A buffer containing DEFLATE-compressed data. /// /// /// The uncompressed string public static String UncompressString(byte[] compressed) { using (var input = new System.IO.MemoryStream(compressed)) { System.IO.Stream decompressor = new DeflateStream(input, CompressionMode.Decompress); return ZlibBaseStream.UncompressString(compressed, decompressor); } } /// /// Uncompress a DEFLATE'd byte array into a byte array. /// /// /// DeflateStream.CompressBuffer(byte[]) /// DeflateStream.UncompressString(byte[]) /// GZipStream.UncompressBuffer(byte[]) /// /// /// A buffer containing data that has been compressed with DEFLATE. /// /// /// The data in uncompressed form public static byte[] UncompressBuffer(byte[] compressed) { using (var input = new System.IO.MemoryStream(compressed)) { System.IO.Stream decompressor = new DeflateStream( input, CompressionMode.Decompress ); return ZlibBaseStream.UncompressBuffer(compressed, decompressor); } } } }