// ZlibStream.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: <2011-July-31 14:53:33>
//
// ------------------------------------------------------------------
//
// This module defines the ZlibStream class, which is similar in idea to
// the System.IO.Compression.DeflateStream and
// System.IO.Compression.GZipStream classes in the .NET BCL.
//
// ------------------------------------------------------------------
using System;
using System.IO;
namespace OfficeOpenXml.Packaging.Ionic.Zlib
{
///
/// Represents a Zlib stream for compression or decompression.
///
///
///
///
/// The ZlibStream is a Decorator on a . It adds ZLIB 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 ZLIB, which is documented in IETF RFC 1950, "ZLIB Compressed
/// Data Format Specification version 3.3". This implementation of ZLIB always uses
/// DEFLATE as the compression method. (see IETF RFC 1951, "DEFLATE
/// Compressed Data Format Specification version 1.3.")
///
///
/// The ZLIB format allows for varying compression methods, window sizes, and dictionaries.
/// This implementation always uses the DEFLATE compression method, a preset dictionary,
/// and 15 window bits by default.
///
///
///
/// This class is similar to , except that it adds the
/// RFC1950 header and trailer bytes to a compressed stream when compressing, or expects
/// the RFC1950 header and trailer bytes when decompressing. It is also similar to the
/// .
///
///
///
///
public class ZlibStream : System.IO.Stream
{
internal ZlibBaseStream _baseStream;
bool _disposed;
///
/// Create a ZlibStream using the specified CompressionMode.
///
///
///
///
/// When mode is CompressionMode.Compress, the ZlibStream
/// will use the default compression level. The "captive" stream will be
/// closed when the ZlibStream is closed.
///
///
///
///
///
/// This example uses a ZlibStream to compress 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 + ".zlib"))
/// {
/// using (Stream compressor = new ZlibStream(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 & ".zlib")
/// Using compressor As Stream = New ZlibStream(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 ZlibStream will compress or decompress.
public ZlibStream(System.IO.Stream stream, CompressionMode mode)
: this(stream, mode, CompressionLevel.Default, false)
{
}
///
/// Create a ZlibStream 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 ZlibStream is closed.
///
///
///
///
///
/// This example uses a ZlibStream 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 + ".zlib"))
/// {
/// using (Stream compressor = new ZlibStream(raw,
/// CompressionMode.Compress,
/// CompressionLevel.BestCompression))
/// {
/// 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 & ".zlib")
/// Using compressor As Stream = New ZlibStream(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 ZlibStream will compress or decompress.
/// A tuning knob to trade speed for effectiveness.
public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
: this(stream, mode, level, false)
{
}
///
/// Create a ZlibStream using the specified CompressionMode, and
/// explicitly specify whether the captive stream should be left open after
/// Deflation or Inflation.
///
///
///
///
///
/// When mode is CompressionMode.Compress, the ZlibStream will use
/// the default compression level.
///
///
///
/// 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.
///
///
///
/// 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 ZlibStream will compress or decompress.
/// true if the application would like the stream to remain
/// open after inflation/deflation.
public ZlibStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
: this(stream, mode, CompressionLevel.Default, leaveOpen)
{
}
///
/// Create a ZlibStream using the specified CompressionMode
/// and the specified CompressionLevel, 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 that will be
/// re-read after compression. Specify true for the parameter to leave the stream open.
///
///
///
/// When mode is CompressionMode.Decompress, the level parameter is
/// ignored.
///
///
///
///
///
///
/// This example shows how to use a ZlibStream to compress the data from a file,
/// and store the result into another file. The filestream remains open to allow
/// additional data to be written to it.
///
///
/// using (var output = System.IO.File.Create(fileToCompress + ".zlib"))
/// {
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
/// {
/// using (Stream compressor = new ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
/// {
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
/// int n;
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
/// {
/// compressor.Write(buffer, 0, n);
/// }
/// }
/// }
/// // can write additional data to the output stream here
/// }
///
///
/// Using output As FileStream = File.Create(fileToCompress & ".zlib")
/// Using input As Stream = File.OpenRead(fileToCompress)
/// Using compressor As Stream = New ZlibStream(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 ZlibStream 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. This parameter is
/// effective only when mode is CompressionMode.Compress.
///
public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
{
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen);
}
#region Zlib properties
///
/// This property sets the flush behavior on the stream.
/// Sorry, though, not sure exactly how to describe all the various settings.
///
virtual public FlushType FlushMode
{
get { return (this._baseStream._flushMode); }
set
{
if (_disposed) throw new ObjectDisposedException("ZlibStream");
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("ZlibStream");
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;
}
}
/// 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.
///
///
/// 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.
///
///
///
/// indicates whether 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("ZlibStream");
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("ZlibStream");
return _baseStream._stream.CanWrite;
}
}
///
/// Flush the stream.
///
public override void Flush()
{
if (_disposed) throw new ObjectDisposedException("ZlibStream");
_baseStream.Flush();
}
///
/// Reading this property always throws a .
///
public override long Length
{
get { throw new NotSupportedException(); }
}
///
/// 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 == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer)
return this._baseStream._z.TotalBytesOut;
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader)
return this._baseStream._z.TotalBytesIn;
return 0;
}
set { throw new NotSupportedException(); }
}
///
/// Read data from the stream.
///
///
///
///
///
/// If you wish to use the ZlibStream to compress data while reading,
/// you can create a ZlibStream with CompressionMode.Compress,
/// providing an uncompressed data stream. Then call Read() on that
/// ZlibStream, and the data read will be compressed. If you wish to
/// use the ZlibStream to decompress data while reading, you can create
/// a ZlibStream with CompressionMode.Decompress, providing a
/// readable compressed data stream. Then call Read() on that
/// ZlibStream, and the data will be decompressed as it is read.
///
///
///
/// A ZlibStream 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 read
public override int Read(byte[] buffer, int offset, int count)
{
if (_disposed) throw new ObjectDisposedException("ZlibStream");
return _baseStream.Read(buffer, offset, count);
}
///
/// Calling this method always throws a .
///
///
/// The offset to seek to....
/// IF THIS METHOD ACTUALLY DID ANYTHING.
///
///
/// The reference specifying how to apply the offset.... IF
/// THIS METHOD ACTUALLY DID ANYTHING.
///
///
/// nothing. This method always throws.
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
throw new NotSupportedException();
}
///
/// Calling this method always throws a .
///
///
/// The new value for the stream length.... IF
/// THIS METHOD ACTUALLY DID ANYTHING.
///
public override void SetLength(long value)
{
throw new NotSupportedException();
}
///
/// Write data to the stream.
///
///
///
///
///
/// If you wish to use the ZlibStream to compress data while writing,
/// you can create a ZlibStream with CompressionMode.Compress,
/// and a writable output stream. Then call Write() on that
/// ZlibStream, 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 ZlibStream to decompress data while writing, you
/// can create a ZlibStream 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 ZlibStream 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("ZlibStream");
_baseStream.Write(buffer, offset, count);
}
#endregion
///
/// Compress a string into a byte array using ZLIB.
///
///
///
/// Uncompress it with .
///
///
///
///
///
///
///
/// 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 MemoryStream())
{
Stream compressor =
new ZlibStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
ZlibBaseStream.CompressString(s, compressor);
return ms.ToArray();
}
}
///
/// Compress a byte array into a new byte array using ZLIB.
///
///
///
/// Uncompress it with .
///
///
///
///
///
///
/// A buffer to compress.
///
///
/// The data in compressed form
public static byte[] CompressBuffer(byte[] b)
{
using (var ms = new MemoryStream())
{
Stream compressor =
new ZlibStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
ZlibBaseStream.CompressBuffer(b, compressor);
return ms.ToArray();
}
}
///
/// Uncompress a ZLIB-compressed byte array into a single string.
///
///
///
///
///
///
/// A buffer containing ZLIB-compressed data.
///
///
/// The uncompressed string
public static String UncompressString(byte[] compressed)
{
using (var input = new MemoryStream(compressed))
{
Stream decompressor =
new ZlibStream(input, CompressionMode.Decompress);
return ZlibBaseStream.UncompressString(compressed, decompressor);
}
}
///
/// Uncompress a ZLIB-compressed byte array into a byte array.
///
///
///
///
///
///
/// A buffer containing ZLIB-compressed data.
///
///
/// The data in uncompressed form
public static byte[] UncompressBuffer(byte[] compressed)
{
using (var input = new MemoryStream(compressed))
{
Stream decompressor =
new ZlibStream( input, CompressionMode.Decompress );
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
}
}
}
}