| // GZipStream.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-August-08 18:14:39> | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This module defines the GZipStream class, which can be used as a replacement for | |
| // the System.IO.Compression.GZipStream class in the .NET BCL. NB: The design is not | |
| // completely OO clean: there is some intelligence in the ZlibBaseStream that reads the | |
| // GZip header. | |
| // | |
| // ------------------------------------------------------------------ | |
| using System; | |
| using System.IO; | |
| namespace OfficeOpenXml.Packaging.Ionic.Zlib | |
| { | |
| /// <summary> | |
| /// A class for compressing and decompressing GZIP streams. | |
| /// </summary> | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// The <c>GZipStream</c> is a <see | |
| /// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a | |
| /// <see cref="Stream"/>. It adds GZIP compression or decompression to any | |
| /// stream. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Like the <c>System.IO.Compression.GZipStream</c> in the .NET Base Class Library, the | |
| /// <c>Ionic.Zlib.GZipStream</c> can compress while writing, or decompress while | |
| /// reading, but not vice versa. The compression method used is GZIP, which is | |
| /// documented in <see href="http://www.ietf.org/rfc/rfc1952.txt">IETF RFC | |
| /// 1952</see>, "GZIP file format specification version 4.3".</para> | |
| /// | |
| /// <para> | |
| /// A <c>GZipStream</c> can be used to decompress data (through <c>Read()</c>) or | |
| /// to compress data (through <c>Write()</c>), but not both. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If you wish to use the <c>GZipStream</c> to compress data, you must wrap it | |
| /// around a write-able stream. As you call <c>Write()</c> on the <c>GZipStream</c>, the | |
| /// data will be compressed into the GZIP format. If you want to decompress data, | |
| /// you must wrap the <c>GZipStream</c> around a readable stream that contains an | |
| /// IETF RFC 1952-compliant stream. The data will be decompressed as you call | |
| /// <c>Read()</c> on the <c>GZipStream</c>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Though the GZIP format allows data from multiple files to be concatenated | |
| /// together, this stream handles only a single segment of GZIP format, typically | |
| /// representing a single file. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// This class is similar to <see cref="ZlibStream"/> and <see cref="DeflateStream"/>. | |
| /// <c>ZlibStream</c> handles RFC1950-compliant streams. <see cref="DeflateStream"/> | |
| /// handles RFC1951-compliant streams. This class handles RFC1952-compliant streams. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <seealso cref="DeflateStream" /> | |
| /// <seealso cref="ZlibStream" /> | |
| public class GZipStream : System.IO.Stream | |
| { | |
| // GZip format | |
| // source: http://tools.ietf.org/html/rfc1952 | |
| // | |
| // header id: 2 bytes 1F 8B | |
| // compress method 1 byte 8= DEFLATE (none other supported) | |
| // flag 1 byte bitfield (See below) | |
| // mtime 4 bytes time_t (seconds since jan 1, 1970 UTC of the file. | |
| // xflg 1 byte 2 = max compress used , 4 = max speed (can be ignored) | |
| // OS 1 byte OS for originating archive. set to 0xFF in compression. | |
| // extra field length 2 bytes optional - only if FEXTRA is set. | |
| // extra field varies | |
| // filename varies optional - if FNAME is set. zero terminated. ISO-8859-1. | |
| // file comment varies optional - if FCOMMENT is set. zero terminated. ISO-8859-1. | |
| // crc16 1 byte optional - present only if FHCRC bit is set | |
| // compressed data varies | |
| // CRC32 4 bytes | |
| // isize 4 bytes data size modulo 2^32 | |
| // | |
| // FLG (FLaGs) | |
| // bit 0 FTEXT - indicates file is ASCII text (can be safely ignored) | |
| // bit 1 FHCRC - there is a CRC16 for the header immediately following the header | |
| // bit 2 FEXTRA - extra fields are present | |
| // bit 3 FNAME - the zero-terminated filename is present. encoding; ISO-8859-1. | |
| // bit 4 FCOMMENT - a zero-terminated file comment is present. encoding: ISO-8859-1 | |
| // bit 5 reserved | |
| // bit 6 reserved | |
| // bit 7 reserved | |
| // | |
| // On consumption: | |
| // Extra field is a bunch of nonsense and can be safely ignored. | |
| // Header CRC and OS, likewise. | |
| // | |
| // on generation: | |
| // all optional fields get 0, except for the OS, which gets 255. | |
| // | |
| /// <summary> | |
| /// The comment on the GZIP stream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// The GZIP format allows for each file to optionally have an associated | |
| /// comment stored with the file. The comment is encoded with the ISO-8859-1 | |
| /// code page. To include a comment in a GZIP stream you create, set this | |
| /// property before calling <c>Write()</c> for the first time on the | |
| /// <c>GZipStream</c>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// When using <c>GZipStream</c> to decompress, you can retrieve this property | |
| /// after the first call to <c>Read()</c>. If no comment has been set in the | |
| /// GZIP bytestream, the Comment property will return <c>null</c> | |
| /// (<c>Nothing</c> in VB). | |
| /// </para> | |
| /// </remarks> | |
| public String Comment | |
| { | |
| get | |
| { | |
| return _Comment; | |
| } | |
| set | |
| { | |
| if (_disposed) throw new ObjectDisposedException("GZipStream"); | |
| _Comment = value; | |
| } | |
| } | |
| /// <summary> | |
| /// The FileName for the GZIP stream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// The GZIP format optionally allows each file to have an associated | |
| /// filename. When compressing data (through <c>Write()</c>), set this | |
| /// FileName before calling <c>Write()</c> the first time on the <c>GZipStream</c>. | |
| /// The actual filename is encoded into the GZIP bytestream with the | |
| /// ISO-8859-1 code page, according to RFC 1952. It is the application's | |
| /// responsibility to insure that the FileName can be encoded and decoded | |
| /// correctly with this code page. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// When decompressing (through <c>Read()</c>), you can retrieve this value | |
| /// any time after the first <c>Read()</c>. In the case where there was no filename | |
| /// encoded into the GZIP bytestream, the property will return <c>null</c> (<c>Nothing</c> | |
| /// in VB). | |
| /// </para> | |
| /// </remarks> | |
| public String FileName | |
| { | |
| get { return _FileName; } | |
| set | |
| { | |
| if (_disposed) throw new ObjectDisposedException("GZipStream"); | |
| _FileName = value; | |
| if (_FileName == null) return; | |
| if (_FileName.IndexOf("/") != -1) | |
| { | |
| _FileName = _FileName.Replace("/", "\\"); | |
| } | |
| if (_FileName.EndsWith("\\")) | |
| throw new Exception("Illegal filename"); | |
| if (_FileName.IndexOf("\\") != -1) | |
| { | |
| // trim any leading path | |
| _FileName = Path.GetFileName(_FileName); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// The last modified time for the GZIP stream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// GZIP allows the storage of a last modified time with each GZIP entry. | |
| /// When compressing data, you can set this before the first call to | |
| /// <c>Write()</c>. When decompressing, you can retrieve this value any time | |
| /// after the first call to <c>Read()</c>. | |
| /// </remarks> | |
| public DateTime? LastModified; | |
| /// <summary> | |
| /// The CRC on the GZIP stream. | |
| /// </summary> | |
| /// <remarks> | |
| /// This is used for internal error checking. You probably don't need to look at this property. | |
| /// </remarks> | |
| public int Crc32 { get { return _Crc32; } } | |
| private int _headerByteCount; | |
| internal ZlibBaseStream _baseStream; | |
| bool _disposed; | |
| bool _firstReadDone; | |
| string _FileName; | |
| string _Comment; | |
| int _Crc32; | |
| /// <summary> | |
| /// Create a <c>GZipStream</c> using the specified <c>CompressionMode</c>. | |
| /// </summary> | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// When mode is <c>CompressionMode.Compress</c>, the <c>GZipStream</c> will use the | |
| /// default compression level. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// As noted in the class documentation, the <c>CompressionMode</c> (Compress | |
| /// or Decompress) also establishes the "direction" of the stream. A | |
| /// <c>GZipStream</c> with <c>CompressionMode.Compress</c> works only through | |
| /// <c>Write()</c>. A <c>GZipStream</c> with | |
| /// <c>CompressionMode.Decompress</c> works only through <c>Read()</c>. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// This example shows how to use a GZipStream to compress data. | |
| /// <code> | |
| /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) | |
| /// { | |
| /// using (var raw = System.IO.File.Create(outputFile)) | |
| /// { | |
| /// using (Stream compressor = new GZipStream(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"> | |
| /// Dim outputFile As String = (fileToCompress & ".compressed") | |
| /// Using input As Stream = File.OpenRead(fileToCompress) | |
| /// Using raw As FileStream = File.Create(outputFile) | |
| /// Using compressor As Stream = New GZipStream(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 | |
| /// </code> | |
| /// </example> | |
| /// | |
| /// <example> | |
| /// This example shows how to use a GZipStream to uncompress a file. | |
| /// <code> | |
| /// private void GunZipFile(string filename) | |
| /// { | |
| /// if (!filename.EndsWith(".gz)) | |
| /// throw new ArgumentException("filename"); | |
| /// var DecompressedFile = filename.Substring(0,filename.Length-3); | |
| /// byte[] working = new byte[WORKING_BUFFER_SIZE]; | |
| /// int n= 1; | |
| /// using (System.IO.Stream input = System.IO.File.OpenRead(filename)) | |
| /// { | |
| /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true)) | |
| /// { | |
| /// using (var output = System.IO.File.Create(DecompressedFile)) | |
| /// { | |
| /// while (n !=0) | |
| /// { | |
| /// n= decompressor.Read(working, 0, working.Length); | |
| /// if (n > 0) | |
| /// { | |
| /// output.Write(working, 0, n); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// </code> | |
| /// | |
| /// <code lang="VB"> | |
| /// Private Sub GunZipFile(ByVal filename as String) | |
| /// If Not (filename.EndsWith(".gz)) Then | |
| /// Throw New ArgumentException("filename") | |
| /// End If | |
| /// Dim DecompressedFile as String = filename.Substring(0,filename.Length-3) | |
| /// Dim working(WORKING_BUFFER_SIZE) as Byte | |
| /// Dim n As Integer = 1 | |
| /// Using input As Stream = File.OpenRead(filename) | |
| /// Using decompressor As Stream = new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, True) | |
| /// Using output As Stream = File.Create(UncompressedFile) | |
| /// Do | |
| /// n= decompressor.Read(working, 0, working.Length) | |
| /// If n > 0 Then | |
| /// output.Write(working, 0, n) | |
| /// End IF | |
| /// Loop While (n > 0) | |
| /// End Using | |
| /// End Using | |
| /// End Using | |
| /// End Sub | |
| /// </code> | |
| /// </example> | |
| /// | |
| /// <param name="stream">The stream which will be read or written.</param> | |
| /// <param name="mode">Indicates whether the GZipStream will compress or decompress.</param> | |
| public GZipStream(Stream stream, CompressionMode mode) | |
| : this(stream, mode, CompressionLevel.Default, false) | |
| { | |
| } | |
| /// <summary> | |
| /// Create a <c>GZipStream</c> using the specified <c>CompressionMode</c> and | |
| /// the specified <c>CompressionLevel</c>. | |
| /// </summary> | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// The <c>CompressionMode</c> (Compress or Decompress) also establishes the | |
| /// "direction" of the stream. A <c>GZipStream</c> with | |
| /// <c>CompressionMode.Compress</c> works only through <c>Write()</c>. A | |
| /// <c>GZipStream</c> with <c>CompressionMode.Decompress</c> works only | |
| /// through <c>Read()</c>. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// | |
| /// This example shows how to use a <c>GZipStream</c> to compress a file into a .gz file. | |
| /// | |
| /// <code> | |
| /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) | |
| /// { | |
| /// using (var raw = System.IO.File.Create(fileToCompress + ".gz")) | |
| /// { | |
| /// using (Stream compressor = new GZipStream(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); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// </code> | |
| /// | |
| /// <code lang="VB"> | |
| /// Using input As Stream = File.OpenRead(fileToCompress) | |
| /// Using raw As FileStream = File.Create(fileToCompress & ".gz") | |
| /// Using compressor As Stream = New GZipStream(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 | |
| /// </code> | |
| /// </example> | |
| /// <param name="stream">The stream to be read or written while deflating or inflating.</param> | |
| /// <param name="mode">Indicates whether the <c>GZipStream</c> will compress or decompress.</param> | |
| /// <param name="level">A tuning knob to trade speed for effectiveness.</param> | |
| public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level) | |
| : this(stream, mode, level, false) | |
| { | |
| } | |
| /// <summary> | |
| /// Create a <c>GZipStream</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 compressed data has been written | |
| /// to it. Specify true for the <paramref name="leaveOpen"/> parameter to leave | |
| /// the stream open. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// The <see cref="CompressionMode"/> (Compress or Decompress) also | |
| /// establishes the "direction" of the stream. A <c>GZipStream</c> with | |
| /// <c>CompressionMode.Compress</c> works only through <c>Write()</c>. A <c>GZipStream</c> | |
| /// with <c>CompressionMode.Decompress</c> works only through <c>Read()</c>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// The <c>GZipStream</c> will use the default compression level. If you want | |
| /// to specify the compression level, see <see cref="GZipStream(Stream, | |
| /// CompressionMode, CompressionLevel, bool)"/>. | |
| /// </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 GZipStream will compress or decompress. | |
| /// </param> | |
| /// | |
| /// <param name="leaveOpen"> | |
| /// true if the application would like the base stream to remain open after | |
| /// inflation/deflation. | |
| /// </param> | |
| public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen) | |
| : this(stream, mode, CompressionLevel.Default, leaveOpen) | |
| { | |
| } | |
| /// <summary> | |
| /// Create a <c>GZipStream</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> | |
| /// 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 compressed data has been written | |
| /// to it. Specify true for the <paramref name="leaveOpen"/> parameter to | |
| /// leave the stream open. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// As noted in the class documentation, the <c>CompressionMode</c> (Compress | |
| /// or Decompress) also establishes the "direction" of the stream. A | |
| /// <c>GZipStream</c> with <c>CompressionMode.Compress</c> works only through | |
| /// <c>Write()</c>. A <c>GZipStream</c> with <c>CompressionMode.Decompress</c> works only | |
| /// through <c>Read()</c>. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// This example shows how to use a <c>GZipStream</c> to compress data. | |
| /// <code> | |
| /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) | |
| /// { | |
| /// using (var raw = System.IO.File.Create(outputFile)) | |
| /// { | |
| /// using (Stream compressor = new GZipStream(raw, 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); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// </code> | |
| /// <code lang="VB"> | |
| /// Dim outputFile As String = (fileToCompress & ".compressed") | |
| /// Using input As Stream = File.OpenRead(fileToCompress) | |
| /// Using raw As FileStream = File.Create(outputFile) | |
| /// Using compressor As Stream = New GZipStream(raw, 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 | |
| /// End Using | |
| /// </code> | |
| /// </example> | |
| /// <param name="stream">The stream which will be read or written.</param> | |
| /// <param name="mode">Indicates whether the GZipStream 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 GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) | |
| { | |
| _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, leaveOpen); | |
| } | |
| #region Zlib properties | |
| /// <summary> | |
| /// This property sets the flush behavior on the stream. | |
| /// </summary> | |
| virtual public FlushType FlushMode | |
| { | |
| get { return (this._baseStream._flushMode); } | |
| set { | |
| if (_disposed) throw new ObjectDisposedException("GZipStream"); | |
| 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("GZipStream"); | |
| 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> 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 Stream methods | |
| /// <summary> | |
| /// Dispose the stream. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// 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. | |
| /// </para> | |
| /// <para> | |
| /// 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. | |
| /// </para> | |
| /// </remarks> | |
| /// <param name="disposing"> | |
| /// indicates whether the Dispose method was invoked by user code. | |
| /// </param> | |
| protected override void Dispose(bool disposing) | |
| { | |
| try | |
| { | |
| if (!_disposed) | |
| { | |
| if (disposing && (this._baseStream != null)) | |
| { | |
| this._baseStream.Close(); | |
| this._Crc32 = _baseStream.Crc32; | |
| } | |
| _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("GZipStream"); | |
| 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("GZipStream"); | |
| return _baseStream._stream.CanWrite; | |
| } | |
| } | |
| /// <summary> | |
| /// Flush the stream. | |
| /// </summary> | |
| public override void Flush() | |
| { | |
| if (_disposed) throw new ObjectDisposedException("GZipStream"); | |
| _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 == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer) | |
| return this._baseStream._z.TotalBytesOut + _headerByteCount; | |
| if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader) | |
| return this._baseStream._z.TotalBytesIn + this._baseStream._gzipHeaderByteCount; | |
| return 0; | |
| } | |
| set { throw new NotImplementedException(); } | |
| } | |
| /// <summary> | |
| /// Read and decompress data from the source stream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// With a <c>GZipStream</c>, decompression is done through reading. | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// <code> | |
| /// byte[] working = new byte[WORKING_BUFFER_SIZE]; | |
| /// using (System.IO.Stream input = System.IO.File.OpenRead(_CompressedFile)) | |
| /// { | |
| /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true)) | |
| /// { | |
| /// using (var output = System.IO.File.Create(_DecompressedFile)) | |
| /// { | |
| /// int n; | |
| /// while ((n= decompressor.Read(working, 0, working.Length)) !=0) | |
| /// { | |
| /// output.Write(working, 0, n); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// </code> | |
| /// </example> | |
| /// <param name="buffer">The buffer into which the decompressed 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("GZipStream"); | |
| int n = _baseStream.Read(buffer, offset, count); | |
| // Console.WriteLine("GZipStream::Read(buffer, off({0}), c({1}) = {2}", offset, count, n); | |
| // Console.WriteLine( Util.FormatByteArray(buffer, offset, n) ); | |
| if (!_firstReadDone) | |
| { | |
| _firstReadDone = true; | |
| FileName = _baseStream._GzipFileName; | |
| Comment = _baseStream._GzipComment; | |
| } | |
| return n; | |
| } | |
| /// <summary> | |
| /// Calling this method always throws a <see cref="NotImplementedException"/>. | |
| /// </summary> | |
| /// <param name="offset">irrelevant; it will always throw!</param> | |
| /// <param name="origin">irrelevant; it will always throw!</param> | |
| /// <returns>irrelevant!</returns> | |
| public override long Seek(long offset, SeekOrigin origin) | |
| { | |
| throw new NotImplementedException(); | |
| } | |
| /// <summary> | |
| /// Calling this method always throws a <see cref="NotImplementedException"/>. | |
| /// </summary> | |
| /// <param name="value">irrelevant; this method will always throw!</param> | |
| public override void SetLength(long value) | |
| { | |
| throw new NotImplementedException(); | |
| } | |
| /// <summary> | |
| /// Write data to the stream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// If you wish to use the <c>GZipStream</c> to compress data while writing, | |
| /// you can create a <c>GZipStream</c> with <c>CompressionMode.Compress</c>, and a | |
| /// writable output stream. Then call <c>Write()</c> on that <c>GZipStream</c>, | |
| /// providing uncompressed data as input. The data sent to the output stream | |
| /// will be the compressed form of the data written. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// A <c>GZipStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not | |
| /// both. Writing implies compression. Reading implies decompression. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// <param name="buffer">The buffer holding data to write to the stream.</param> | |
| /// <param name="offset">the offset within that data array to find the first byte to write.</param> | |
| /// <param name="count">the number of bytes to write.</param> | |
| public override void Write(byte[] buffer, int offset, int count) | |
| { | |
| if (_disposed) throw new ObjectDisposedException("GZipStream"); | |
| if (_baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Undefined) | |
| { | |
| //Console.WriteLine("GZipStream: First write"); | |
| if (_baseStream._wantCompress) | |
| { | |
| // first write in compression, therefore, emit the GZIP header | |
| _headerByteCount = EmitHeader(); | |
| } | |
| else | |
| { | |
| throw new InvalidOperationException(); | |
| } | |
| } | |
| _baseStream.Write(buffer, offset, count); | |
| } | |
| #endregion | |
| internal static readonly System.DateTime _unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |
| #if SILVERLIGHT || NETCF | |
| internal static readonly System.Text.Encoding iso8859dash1 = new Ionic.Encoding.Iso8859Dash1Encoding(); | |
| #else | |
| internal static readonly System.Text.Encoding iso8859dash1 = System.Text.Encoding.GetEncoding("iso-8859-1"); | |
| #endif | |
| private int EmitHeader() | |
| { | |
| byte[] commentBytes = (Comment == null) ? null : iso8859dash1.GetBytes(Comment); | |
| byte[] filenameBytes = (FileName == null) ? null : iso8859dash1.GetBytes(FileName); | |
| int cbLength = (Comment == null) ? 0 : commentBytes.Length + 1; | |
| int fnLength = (FileName == null) ? 0 : filenameBytes.Length + 1; | |
| int bufferLength = 10 + cbLength + fnLength; | |
| byte[] header = new byte[bufferLength]; | |
| int i = 0; | |
| // ID | |
| header[i++] = 0x1F; | |
| header[i++] = 0x8B; | |
| // compression method | |
| header[i++] = 8; | |
| byte flag = 0; | |
| if (Comment != null) | |
| flag ^= 0x10; | |
| if (FileName != null) | |
| flag ^= 0x8; | |
| // flag | |
| header[i++] = flag; | |
| // mtime | |
| if (!LastModified.HasValue) LastModified = DateTime.Now; | |
| System.TimeSpan delta = LastModified.Value - _unixEpoch; | |
| Int32 timet = (Int32)delta.TotalSeconds; | |
| Array.Copy(BitConverter.GetBytes(timet), 0, header, i, 4); | |
| i += 4; | |
| // xflg | |
| header[i++] = 0; // this field is totally useless | |
| // OS | |
| header[i++] = 0xFF; // 0xFF == unspecified | |
| // extra field length - only if FEXTRA is set, which it is not. | |
| //header[i++]= 0; | |
| //header[i++]= 0; | |
| // filename | |
| if (fnLength != 0) | |
| { | |
| Array.Copy(filenameBytes, 0, header, i, fnLength - 1); | |
| i += fnLength - 1; | |
| header[i++] = 0; // terminate | |
| } | |
| // comment | |
| if (cbLength != 0) | |
| { | |
| Array.Copy(commentBytes, 0, header, i, cbLength - 1); | |
| i += cbLength - 1; | |
| header[i++] = 0; // terminate | |
| } | |
| _baseStream._stream.Write(header, 0, header.Length); | |
| return header.Length; // bytes written | |
| } | |
| /// <summary> | |
| /// Compress a string into a byte array using GZip. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// Uncompress it with <see cref="GZipStream.UncompressString(byte[])"/>. | |
| /// </remarks> | |
| /// | |
| /// <seealso cref="GZipStream.UncompressString(byte[])"/> | |
| /// <seealso cref="GZipStream.CompressBuffer(byte[])"/> | |
| /// | |
| /// <param name="s"> | |
| /// A string to compress. The string will first be encoded | |
| /// using UTF8, then compressed. | |
| /// </param> | |
| /// | |
| /// <returns>The string in compressed form</returns> | |
| public static byte[] CompressString(String s) | |
| { | |
| using (var ms = new MemoryStream()) | |
| { | |
| System.IO.Stream compressor = | |
| new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression); | |
| ZlibBaseStream.CompressString(s, compressor); | |
| return ms.ToArray(); | |
| } | |
| } | |
| /// <summary> | |
| /// Compress a byte array into a new byte array using GZip. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// Uncompress it with <see cref="GZipStream.UncompressBuffer(byte[])"/>. | |
| /// </remarks> | |
| /// | |
| /// <seealso cref="GZipStream.CompressString(string)"/> | |
| /// <seealso cref="GZipStream.UncompressBuffer(byte[])"/> | |
| /// | |
| /// <param name="b"> | |
| /// A buffer to compress. | |
| /// </param> | |
| /// | |
| /// <returns>The data in compressed form</returns> | |
| public static byte[] CompressBuffer(byte[] b) | |
| { | |
| using (var ms = new MemoryStream()) | |
| { | |
| System.IO.Stream compressor = | |
| new GZipStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression ); | |
| ZlibBaseStream.CompressBuffer(b, compressor); | |
| return ms.ToArray(); | |
| } | |
| } | |
| /// <summary> | |
| /// Uncompress a GZip'ed byte array into a single string. | |
| /// </summary> | |
| /// | |
| /// <seealso cref="GZipStream.CompressString(String)"/> | |
| /// <seealso cref="GZipStream.UncompressBuffer(byte[])"/> | |
| /// | |
| /// <param name="compressed"> | |
| /// A buffer containing GZIP-compressed data. | |
| /// </param> | |
| /// | |
| /// <returns>The uncompressed string</returns> | |
| public static String UncompressString(byte[] compressed) | |
| { | |
| using (var input = new MemoryStream(compressed)) | |
| { | |
| Stream decompressor = new GZipStream(input, CompressionMode.Decompress); | |
| return ZlibBaseStream.UncompressString(compressed, decompressor); | |
| } | |
| } | |
| /// <summary> | |
| /// Uncompress a GZip'ed byte array into a byte array. | |
| /// </summary> | |
| /// | |
| /// <seealso cref="GZipStream.CompressBuffer(byte[])"/> | |
| /// <seealso cref="GZipStream.UncompressString(byte[])"/> | |
| /// | |
| /// <param name="compressed"> | |
| /// A buffer containing data that has been compressed with GZip. | |
| /// </param> | |
| /// | |
| /// <returns>The data in uncompressed form</returns> | |
| public static byte[] UncompressBuffer(byte[] compressed) | |
| { | |
| using (var input = new System.IO.MemoryStream(compressed)) | |
| { | |
| System.IO.Stream decompressor = | |
| new GZipStream( input, CompressionMode.Decompress ); | |
| return ZlibBaseStream.UncompressBuffer(compressed, decompressor); | |
| } | |
| } | |
| } | |
| } |