// 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);
}
}
}
}