Summary

Class:ICSharpCode.SharpZipLib.GZip.GZipInputStream
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\GZip\GzipInputStream.cs
Covered lines:4
Uncovered lines:104
Coverable lines:108
Total lines:330
Line coverage:3.7%
Branch coverage:0%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)1100100
.ctor(...)1100100
Read(...)500
ReadHeader()2800
ReadFooter()500

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\GZip\GzipInputStream.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using ICSharpCode.SharpZipLib.Checksum;
 4using ICSharpCode.SharpZipLib.Zip.Compression;
 5using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
 6
 7namespace ICSharpCode.SharpZipLib.GZip
 8{
 9
 10  /// <summary>
 11  /// This filter stream is used to decompress a "GZIP" format stream.
 12  /// The "GZIP" format is described baseInputStream RFC 1952.
 13  ///
 14  /// author of the original java version : John Leuner
 15  /// </summary>
 16  /// <example> This sample shows how to unzip a gzipped file
 17  /// <code>
 18  /// using System;
 19  /// using System.IO;
 20  ///
 21  /// using ICSharpCode.SharpZipLib.Core;
 22  /// using ICSharpCode.SharpZipLib.GZip;
 23  ///
 24  /// class MainClass
 25  /// {
 26  ///   public static void Main(string[] args)
 27  ///   {
 28  ///      using (Stream inStream = new GZipInputStream(File.OpenRead(args[0])))
 29  ///      using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
 30  ///        byte[] buffer = new byte[4096];
 31  ///        StreamUtils.Copy(inStream, outStream, buffer);
 32  ///     }
 33  ///   }
 34  /// }
 35  /// </code>
 36  /// </example>
 37  public class GZipInputStream : InflaterInputStream
 38  {
 39    #region Instance Fields
 40    /// <summary>
 41    /// CRC-32 value for uncompressed data
 42    /// </summary>
 43    protected Crc32 crc;
 44
 45    /// <summary>
 46    /// Flag to indicate if we've read the GZIP header yet for the current member (block of compressed data).
 47    /// This is tracked per-block as the file is parsed.
 48    /// </summary>
 49    bool readGZIPHeader;
 50    #endregion
 51
 52    #region Constructors
 53    /// <summary>
 54    /// Creates a GZipInputStream with the default buffer size
 55    /// </summary>
 56    /// <param name="baseInputStream">
 57    /// The stream to read compressed data from (baseInputStream GZIP format)
 58    /// </param>
 59    public GZipInputStream(Stream baseInputStream)
 260      : this(baseInputStream, 4096)
 61    {
 262    }
 63
 64    /// <summary>
 65    /// Creates a GZIPInputStream with the specified buffer size
 66    /// </summary>
 67    /// <param name="baseInputStream">
 68    /// The stream to read compressed data from (baseInputStream GZIP format)
 69    /// </param>
 70    /// <param name="size">
 71    /// Size of the buffer to use
 72    /// </param>
 73    public GZipInputStream(Stream baseInputStream, int size)
 274      : base(baseInputStream, new Inflater(true), size)
 75    {
 276    }
 77    #endregion
 78
 79    #region Stream overrides
 80    /// <summary>
 81    /// Reads uncompressed data into an array of bytes
 82    /// </summary>
 83    /// <param name="buffer">
 84    /// The buffer to read uncompressed data into
 85    /// </param>
 86    /// <param name="offset">
 87    /// The offset indicating where the data should be placed
 88    /// </param>
 89    /// <param name="count">
 90    /// The number of uncompressed bytes to be read
 91    /// </param>
 92    /// <returns>Returns the number of bytes actually read.</returns>
 93    public override int Read(byte[] buffer, int offset, int count)
 94    {
 95      // A GZIP file can contain multiple blocks of compressed data, although this is quite rare.
 96      // A compressed block could potentially be empty, so we need to loop until we reach EOF or
 97      // we find data.
 98      while (true) {
 99
 100        // If we haven't read the header for this block, read it
 0101         if (!readGZIPHeader) {
 102
 103          // Try to read header. If there is no header (0 bytes available), this is EOF. If there is
 104          // an incomplete header, this will throw an exception.
 0105           if (!ReadHeader()) {
 0106            return 0;
 107          }
 108        }
 109
 110        // Try to read compressed data
 0111        int bytesRead = base.Read(buffer, offset, count);
 0112         if (bytesRead > 0) {
 0113          crc.Update(buffer, offset, bytesRead);
 114        }
 115
 116        // If this is the end of stream, read the footer
 0117         if (inf.IsFinished) {
 0118          ReadFooter();
 119        }
 120
 0121         if (bytesRead > 0) {
 0122          return bytesRead;
 123        }
 124      }
 125    }
 126    #endregion
 127
 128    #region Support routines
 129    bool ReadHeader()
 130    {
 131      // Initialize CRC for this block
 0132      crc = new Crc32();
 133
 134      // Make sure there is data in file. We can't rely on ReadLeByte() to fill the buffer, as this could be EOF,
 135      // which is fine, but ReadLeByte() throws an exception if it doesn't find data, so we do this part ourselves.
 0136       if (inputBuffer.Available <= 0) {
 0137        inputBuffer.Fill();
 0138         if (inputBuffer.Available <= 0) {
 139          // No header, EOF.
 0140          return false;
 141        }
 142      }
 143
 144      // 1. Check the two magic bytes
 0145      var headCRC = new Crc32();
 0146      int magic = inputBuffer.ReadLeByte();
 147
 0148       if (magic < 0) {
 0149        throw new EndOfStreamException("EOS reading GZIP header");
 150      }
 151
 0152      headCRC.Update(magic);
 0153       if (magic != (GZipConstants.GZIP_MAGIC >> 8)) {
 0154        throw new GZipException("Error GZIP header, first magic byte doesn't match");
 155      }
 156
 157      //magic = baseInputStream.ReadByte();
 0158      magic = inputBuffer.ReadLeByte();
 159
 0160       if (magic < 0) {
 0161        throw new EndOfStreamException("EOS reading GZIP header");
 162      }
 163
 0164       if (magic != (GZipConstants.GZIP_MAGIC & 0xFF)) {
 0165        throw new GZipException("Error GZIP header,  second magic byte doesn't match");
 166      }
 167
 0168      headCRC.Update(magic);
 169
 170      // 2. Check the compression type (must be 8)
 0171      int compressionType = inputBuffer.ReadLeByte();
 172
 0173       if (compressionType < 0) {
 0174        throw new EndOfStreamException("EOS reading GZIP header");
 175      }
 176
 0177       if (compressionType != 8) {
 0178        throw new GZipException("Error GZIP header, data not in deflate format");
 179      }
 0180      headCRC.Update(compressionType);
 181
 182      // 3. Check the flags
 0183      int flags = inputBuffer.ReadLeByte();
 0184       if (flags < 0) {
 0185        throw new EndOfStreamException("EOS reading GZIP header");
 186      }
 0187      headCRC.Update(flags);
 188
 189      /*    This flag byte is divided into individual bits as follows:
 190
 191      bit 0   FTEXT
 192      bit 1   FHCRC
 193      bit 2   FEXTRA
 194      bit 3   FNAME
 195      bit 4   FCOMMENT
 196      bit 5   reserved
 197      bit 6   reserved
 198      bit 7   reserved
 199      */
 200
 201      // 3.1 Check the reserved bits are zero
 202
 0203       if ((flags & 0xE0) != 0) {
 0204        throw new GZipException("Reserved flag bits in GZIP header != 0");
 205      }
 206
 207      // 4.-6. Skip the modification time, extra flags, and OS type
 0208       for (int i = 0; i < 6; i++) {
 0209        int readByte = inputBuffer.ReadLeByte();
 0210         if (readByte < 0) {
 0211          throw new EndOfStreamException("EOS reading GZIP header");
 212        }
 0213        headCRC.Update(readByte);
 214      }
 215
 216      // 7. Read extra field
 0217       if ((flags & GZipConstants.FEXTRA) != 0) {
 218
 219        // XLEN is total length of extra subfields, we will skip them all
 220        int len1, len2;
 0221        len1 = inputBuffer.ReadLeByte();
 0222        len2 = inputBuffer.ReadLeByte();
 0223         if ((len1 < 0) || (len2 < 0)) {
 0224          throw new EndOfStreamException("EOS reading GZIP header");
 225        }
 0226        headCRC.Update(len1);
 0227        headCRC.Update(len2);
 228
 0229        int extraLen = (len2 << 8) | len1;      // gzip is LSB first
 0230         for (int i = 0; i < extraLen; i++) {
 0231          int readByte = inputBuffer.ReadLeByte();
 0232           if (readByte < 0) {
 0233            throw new EndOfStreamException("EOS reading GZIP header");
 234          }
 0235          headCRC.Update(readByte);
 236        }
 237      }
 238
 239      // 8. Read file name
 0240       if ((flags & GZipConstants.FNAME) != 0) {
 241        int readByte;
 0242         while ((readByte = inputBuffer.ReadLeByte()) > 0) {
 0243          headCRC.Update(readByte);
 244        }
 245
 0246         if (readByte < 0) {
 0247          throw new EndOfStreamException("EOS reading GZIP header");
 248        }
 0249        headCRC.Update(readByte);
 250      }
 251
 252      // 9. Read comment
 0253       if ((flags & GZipConstants.FCOMMENT) != 0) {
 254        int readByte;
 0255         while ((readByte = inputBuffer.ReadLeByte()) > 0) {
 0256          headCRC.Update(readByte);
 257        }
 258
 0259         if (readByte < 0) {
 0260          throw new EndOfStreamException("EOS reading GZIP header");
 261        }
 262
 0263        headCRC.Update(readByte);
 264      }
 265
 266      // 10. Read header CRC
 0267       if ((flags & GZipConstants.FHCRC) != 0) {
 268        int tempByte;
 0269        int crcval = inputBuffer.ReadLeByte();
 0270         if (crcval < 0) {
 0271          throw new EndOfStreamException("EOS reading GZIP header");
 272        }
 273
 0274        tempByte = inputBuffer.ReadLeByte();
 0275         if (tempByte < 0) {
 0276          throw new EndOfStreamException("EOS reading GZIP header");
 277        }
 278
 0279        crcval = (crcval << 8) | tempByte;
 0280         if (crcval != ((int)headCRC.Value & 0xffff)) {
 0281          throw new GZipException("Header CRC value mismatch");
 282        }
 283      }
 284
 0285      readGZIPHeader = true;
 0286      return true;
 287    }
 288
 289    void ReadFooter()
 290    {
 0291      byte[] footer = new byte[8];
 292
 293      // End of stream; reclaim all bytes from inf, read the final byte count, and reset the inflator
 0294      long bytesRead = inf.TotalOut & 0xffffffff;
 0295      inputBuffer.Available += inf.RemainingInput;
 0296      inf.Reset();
 297
 298      // Read footer from inputBuffer
 0299      int needed = 8;
 0300       while (needed > 0) {
 0301        int count = inputBuffer.ReadClearTextBuffer(footer, 8 - needed, needed);
 0302         if (count <= 0) {
 0303          throw new EndOfStreamException("EOS reading GZIP footer");
 304        }
 0305        needed -= count; // Jewel Jan 16
 306      }
 307
 308      // Calculate CRC
 0309      int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24);
 0310       if (crcval != (int)crc.Value) {
 0311        throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int)crc.Value);
 312      }
 313
 314      // NOTE The total here is the original total modulo 2 ^ 32.
 0315      uint total =
 0316        (uint)((uint)footer[4] & 0xff) |
 0317        (uint)(((uint)footer[5] & 0xff) << 8) |
 0318        (uint)(((uint)footer[6] & 0xff) << 16) |
 0319        (uint)((uint)footer[7] << 24);
 320
 0321       if (bytesRead != total) {
 0322        throw new GZipException("Number of bytes mismatch in footer");
 323      }
 324
 325      // Mark header read as false so if another header exists, we'll continue reading through the file
 0326      readGZIPHeader = false;
 0327    }
 328    #endregion
 329  }
 330}