Summary

Class:ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\Compression\Streams\InflaterInputStream.cs
Covered lines:38
Uncovered lines:36
Coverable lines:74
Total lines:662
Line coverage:51.3%
Branch coverage:39.4%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)1100100
.ctor(...)1100100
.ctor(...)47557.14
Skip(...)700
StopDecrypting()1100100
Fill()383.3360
Flush()100
Seek(...)100
SetLength(...)100
Write(...)100
WriteByte(...)100
BeginWrite(...)100
Close()3100100
Read(...)676.9263.64

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\Compression\Streams\InflaterInputStream.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Security.Cryptography;
 4
 5namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
 6{
 7  /// <summary>
 8  /// An input buffer customised for use by <see cref="InflaterInputStream"/>
 9  /// </summary>
 10  /// <remarks>
 11  /// The buffer supports decryption of incoming data.
 12  /// </remarks>
 13  public class InflaterInputBuffer
 14  {
 15    #region Constructors
 16    /// <summary>
 17    /// Initialise a new instance of <see cref="InflaterInputBuffer"/> with a default buffer size
 18    /// </summary>
 19    /// <param name="stream">The stream to buffer.</param>
 20    public InflaterInputBuffer(Stream stream) : this(stream, 4096)
 21    {
 22    }
 23
 24    /// <summary>
 25    /// Initialise a new instance of <see cref="InflaterInputBuffer"/>
 26    /// </summary>
 27    /// <param name="stream">The stream to buffer.</param>
 28    /// <param name="bufferSize">The size to use for the buffer</param>
 29    /// <remarks>A minimum buffer size of 1KB is permitted.  Lower sizes are treated as 1KB.</remarks>
 30    public InflaterInputBuffer(Stream stream, int bufferSize)
 31    {
 32      inputStream = stream;
 33      if (bufferSize < 1024) {
 34        bufferSize = 1024;
 35      }
 36      rawData = new byte[bufferSize];
 37      clearText = rawData;
 38    }
 39    #endregion
 40
 41    /// <summary>
 42    /// Get the length of bytes bytes in the <see cref="RawData"/>
 43    /// </summary>
 44    public int RawLength {
 45      get {
 46        return rawLength;
 47      }
 48    }
 49
 50    /// <summary>
 51    /// Get the contents of the raw data buffer.
 52    /// </summary>
 53    /// <remarks>This may contain encrypted data.</remarks>
 54    public byte[] RawData {
 55      get {
 56        return rawData;
 57      }
 58    }
 59
 60    /// <summary>
 61    /// Get the number of useable bytes in <see cref="ClearText"/>
 62    /// </summary>
 63    public int ClearTextLength {
 64      get {
 65        return clearTextLength;
 66      }
 67    }
 68
 69    /// <summary>
 70    /// Get the contents of the clear text buffer.
 71    /// </summary>
 72    public byte[] ClearText {
 73      get {
 74        return clearText;
 75      }
 76    }
 77
 78    /// <summary>
 79    /// Get/set the number of bytes available
 80    /// </summary>
 81    public int Available {
 82      get { return available; }
 83      set { available = value; }
 84    }
 85
 86    /// <summary>
 87    /// Call <see cref="Inflater.SetInput(byte[], int, int)"/> passing the current clear text buffer contents.
 88    /// </summary>
 89    /// <param name="inflater">The inflater to set input for.</param>
 90    public void SetInflaterInput(Inflater inflater)
 91    {
 92      if (available > 0) {
 93        inflater.SetInput(clearText, clearTextLength - available, available);
 94        available = 0;
 95      }
 96    }
 97
 98    /// <summary>
 99    /// Fill the buffer from the underlying input stream.
 100    /// </summary>
 101    public void Fill()
 102    {
 103      rawLength = 0;
 104      int toRead = rawData.Length;
 105
 106      while (toRead > 0) {
 107        int count = inputStream.Read(rawData, rawLength, toRead);
 108        if (count <= 0) {
 109          break;
 110        }
 111        rawLength += count;
 112        toRead -= count;
 113      }
 114
 115      if (cryptoTransform != null) {
 116        clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
 117      } else {
 118        clearTextLength = rawLength;
 119      }
 120
 121      available = clearTextLength;
 122    }
 123
 124    /// <summary>
 125    /// Read a buffer directly from the input stream
 126    /// </summary>
 127    /// <param name="buffer">The buffer to fill</param>
 128    /// <returns>Returns the number of bytes read.</returns>
 129    public int ReadRawBuffer(byte[] buffer)
 130    {
 131      return ReadRawBuffer(buffer, 0, buffer.Length);
 132    }
 133
 134    /// <summary>
 135    /// Read a buffer directly from the input stream
 136    /// </summary>
 137    /// <param name="outBuffer">The buffer to read into</param>
 138    /// <param name="offset">The offset to start reading data into.</param>
 139    /// <param name="length">The number of bytes to read.</param>
 140    /// <returns>Returns the number of bytes read.</returns>
 141    public int ReadRawBuffer(byte[] outBuffer, int offset, int length)
 142    {
 143      if (length < 0) {
 144        throw new ArgumentOutOfRangeException(nameof(length));
 145      }
 146
 147      int currentOffset = offset;
 148      int currentLength = length;
 149
 150      while (currentLength > 0) {
 151        if (available <= 0) {
 152          Fill();
 153          if (available <= 0) {
 154            return 0;
 155          }
 156        }
 157        int toCopy = Math.Min(currentLength, available);
 158        System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
 159        currentOffset += toCopy;
 160        currentLength -= toCopy;
 161        available -= toCopy;
 162      }
 163      return length;
 164    }
 165
 166    /// <summary>
 167    /// Read clear text data from the input stream.
 168    /// </summary>
 169    /// <param name="outBuffer">The buffer to add data to.</param>
 170    /// <param name="offset">The offset to start adding data at.</param>
 171    /// <param name="length">The number of bytes to read.</param>
 172    /// <returns>Returns the number of bytes actually read.</returns>
 173    public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length)
 174    {
 175      if (length < 0) {
 176        throw new ArgumentOutOfRangeException(nameof(length));
 177      }
 178
 179      int currentOffset = offset;
 180      int currentLength = length;
 181
 182      while (currentLength > 0) {
 183        if (available <= 0) {
 184          Fill();
 185          if (available <= 0) {
 186            return 0;
 187          }
 188        }
 189
 190        int toCopy = Math.Min(currentLength, available);
 191        Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy);
 192        currentOffset += toCopy;
 193        currentLength -= toCopy;
 194        available -= toCopy;
 195      }
 196      return length;
 197    }
 198
 199    /// <summary>
 200    /// Read a <see cref="byte"/> from the input stream.
 201    /// </summary>
 202    /// <returns>Returns the byte read.</returns>
 203    public int ReadLeByte()
 204    {
 205      if (available <= 0) {
 206        Fill();
 207        if (available <= 0) {
 208          throw new ZipException("EOF in header");
 209        }
 210      }
 211      byte result = rawData[rawLength - available];
 212      available -= 1;
 213      return result;
 214    }
 215
 216    /// <summary>
 217    /// Read an <see cref="short"/> in little endian byte order.
 218    /// </summary>
 219    /// <returns>The short value read case to an int.</returns>
 220    public int ReadLeShort()
 221    {
 222      return ReadLeByte() | (ReadLeByte() << 8);
 223    }
 224
 225    /// <summary>
 226    /// Read an <see cref="int"/> in little endian byte order.
 227    /// </summary>
 228    /// <returns>The int value read.</returns>
 229    public int ReadLeInt()
 230    {
 231      return ReadLeShort() | (ReadLeShort() << 16);
 232    }
 233
 234    /// <summary>
 235    /// Read a <see cref="long"/> in little endian byte order.
 236    /// </summary>
 237    /// <returns>The long value read.</returns>
 238    public long ReadLeLong()
 239    {
 240      return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
 241    }
 242
 243    /// <summary>
 244    /// Get/set the <see cref="ICryptoTransform"/> to apply to any data.
 245    /// </summary>
 246    /// <remarks>Set this value to null to have no transform applied.</remarks>
 247    public ICryptoTransform CryptoTransform {
 248      set {
 249        cryptoTransform = value;
 250        if (cryptoTransform != null) {
 251          if (rawData == clearText) {
 252            if (internalClearText == null) {
 253              internalClearText = new byte[rawData.Length];
 254            }
 255            clearText = internalClearText;
 256          }
 257          clearTextLength = rawLength;
 258          if (available > 0) {
 259            cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
 260          }
 261        } else {
 262          clearText = rawData;
 263          clearTextLength = rawLength;
 264        }
 265      }
 266    }
 267
 268    #region Instance Fields
 269    int rawLength;
 270    byte[] rawData;
 271
 272    int clearTextLength;
 273    byte[] clearText;
 274    byte[] internalClearText;
 275
 276    int available;
 277
 278    ICryptoTransform cryptoTransform;
 279    Stream inputStream;
 280    #endregion
 281  }
 282
 283  /// <summary>
 284  /// This filter stream is used to decompress data compressed using the "deflate"
 285  /// format. The "deflate" format is described in RFC 1951.
 286  ///
 287  /// This stream may form the basis for other decompression filters, such
 288  /// as the <see cref="ICSharpCode.SharpZipLib.GZip.GZipInputStream">GZipInputStream</see>.
 289  ///
 290  /// Author of the original java version : John Leuner.
 291  /// </summary>
 292  public class InflaterInputStream : Stream
 293  {
 294    #region Constructors
 295    /// <summary>
 296    /// Create an InflaterInputStream with the default decompressor
 297    /// and a default buffer size of 4KB.
 298    /// </summary>
 299    /// <param name = "baseInputStream">
 300    /// The InputStream to read bytes from
 301    /// </param>
 302    public InflaterInputStream(Stream baseInputStream)
 3303      : this(baseInputStream, new Inflater(), 4096)
 304    {
 3305    }
 306
 307    /// <summary>
 308    /// Create an InflaterInputStream with the specified decompressor
 309    /// and a default buffer size of 4KB.
 310    /// </summary>
 311    /// <param name = "baseInputStream">
 312    /// The source of input data
 313    /// </param>
 314    /// <param name = "inf">
 315    /// The decompressor used to decompress data read from baseInputStream
 316    /// </param>
 317    public InflaterInputStream(Stream baseInputStream, Inflater inf)
 430318      : this(baseInputStream, inf, 4096)
 319    {
 430320    }
 321
 322    /// <summary>
 323    /// Create an InflaterInputStream with the specified decompressor
 324    /// and the specified buffer size.
 325    /// </summary>
 326    /// <param name = "baseInputStream">
 327    /// The InputStream to read bytes from
 328    /// </param>
 329    /// <param name = "inflater">
 330    /// The decompressor to use
 331    /// </param>
 332    /// <param name = "bufferSize">
 333    /// Size of the buffer to use
 334    /// </param>
 435335    public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize)
 336    {
 435337       if (baseInputStream == null) {
 0338        throw new ArgumentNullException(nameof(baseInputStream));
 339      }
 340
 435341       if (inflater == null) {
 0342        throw new ArgumentNullException(nameof(inflater));
 343      }
 344
 435345       if (bufferSize <= 0) {
 0346        throw new ArgumentOutOfRangeException(nameof(bufferSize));
 347      }
 348
 435349      this.baseInputStream = baseInputStream;
 435350      this.inf = inflater;
 351
 435352      inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize);
 435353    }
 354
 355    #endregion
 356
 357    /// <summary>
 358    /// Get/set flag indicating ownership of underlying stream.
 359    /// When the flag is true <see cref="Close"/> will close the underlying stream also.
 360    /// </summary>
 361    /// <remarks>
 362    /// The default value is true.
 363    /// </remarks>
 364    public bool IsStreamOwner {
 0365      get { return isStreamOwner; }
 4366      set { isStreamOwner = value; }
 367    }
 368
 369    /// <summary>
 370    /// Skip specified number of bytes of uncompressed data
 371    /// </summary>
 372    /// <param name ="count">
 373    /// Number of bytes to skip
 374    /// </param>
 375    /// <returns>
 376    /// The number of bytes skipped, zero if the end of
 377    /// stream has been reached
 378    /// </returns>
 379    /// <exception cref="ArgumentOutOfRangeException">
 380    /// <paramref name="count">The number of bytes</paramref> to skip is less than or equal to zero.
 381    /// </exception>
 382    public long Skip(long count)
 383    {
 0384       if (count <= 0) {
 0385        throw new ArgumentOutOfRangeException(nameof(count));
 386      }
 387
 388      // v0.80 Skip by seeking if underlying stream supports it...
 0389       if (baseInputStream.CanSeek) {
 0390        baseInputStream.Seek(count, SeekOrigin.Current);
 0391        return count;
 392      } else {
 0393        int length = 2048;
 0394         if (count < length) {
 0395          length = (int)count;
 396        }
 397
 0398        byte[] tmp = new byte[length];
 0399        int readCount = 1;
 0400        long toSkip = count;
 401
 0402         while ((toSkip > 0) && (readCount > 0)) {
 0403           if (toSkip < length) {
 0404            length = (int)toSkip;
 405          }
 406
 0407          readCount = baseInputStream.Read(tmp, 0, length);
 0408          toSkip -= readCount;
 409        }
 410
 0411        return count - toSkip;
 412      }
 413    }
 414
 415    /// <summary>
 416    /// Clear any cryptographic state.
 417    /// </summary>
 418    protected void StopDecrypting()
 419    {
 32420      inputBuffer.CryptoTransform = null;
 32421    }
 422
 423    /// <summary>
 424    /// Returns 0 once the end of the stream (EOF) has been reached.
 425    /// Otherwise returns 1.
 426    /// </summary>
 427    public virtual int Available {
 428      get {
 0429         return inf.IsFinished ? 0 : 1;
 430      }
 431    }
 432
 433    /// <summary>
 434    /// Fills the buffer with more data to decompress.
 435    /// </summary>
 436    /// <exception cref="SharpZipBaseException">
 437    /// Stream ends early
 438    /// </exception>
 439    protected void Fill()
 440    {
 441      // Protect against redundant calls
 1367442       if (inputBuffer.Available <= 0) {
 1367443        inputBuffer.Fill();
 1367444         if (inputBuffer.Available <= 0) {
 0445          throw new SharpZipBaseException("Unexpected EOF");
 446        }
 447      }
 1367448      inputBuffer.SetInflaterInput(inf);
 1367449    }
 450
 451    #region Stream Overrides
 452    /// <summary>
 453    /// Gets a value indicating whether the current stream supports reading
 454    /// </summary>
 455    public override bool CanRead {
 456      get {
 8457        return baseInputStream.CanRead;
 458      }
 459    }
 460
 461    /// <summary>
 462    /// Gets a value of false indicating seeking is not supported for this stream.
 463    /// </summary>
 464    public override bool CanSeek {
 465      get {
 2466        return false;
 467      }
 468    }
 469
 470    /// <summary>
 471    /// Gets a value of false indicating that this stream is not writeable.
 472    /// </summary>
 473    public override bool CanWrite {
 474      get {
 0475        return false;
 476      }
 477    }
 478
 479    /// <summary>
 480    /// A value representing the length of the stream in bytes.
 481    /// </summary>
 482    public override long Length {
 483      get {
 0484        return inputBuffer.RawLength;
 485      }
 486    }
 487
 488    /// <summary>
 489    /// The current position within the stream.
 490    /// Throws a NotSupportedException when attempting to set the position
 491    /// </summary>
 492    /// <exception cref="NotSupportedException">Attempting to set the position</exception>
 493    public override long Position {
 494      get {
 0495        return baseInputStream.Position;
 496      }
 497      set {
 0498        throw new NotSupportedException("InflaterInputStream Position not supported");
 499      }
 500    }
 501
 502    /// <summary>
 503    /// Flushes the baseInputStream
 504    /// </summary>
 505    public override void Flush()
 506    {
 0507      baseInputStream.Flush();
 0508    }
 509
 510    /// <summary>
 511    /// Sets the position within the current stream
 512    /// Always throws a NotSupportedException
 513    /// </summary>
 514    /// <param name="offset">The relative offset to seek to.</param>
 515    /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
 516    /// <returns>The new position in the stream.</returns>
 517    /// <exception cref="NotSupportedException">Any access</exception>
 518    public override long Seek(long offset, SeekOrigin origin)
 519    {
 0520      throw new NotSupportedException("Seek not supported");
 521    }
 522
 523    /// <summary>
 524    /// Set the length of the current stream
 525    /// Always throws a NotSupportedException
 526    /// </summary>
 527    /// <param name="value">The new length value for the stream.</param>
 528    /// <exception cref="NotSupportedException">Any access</exception>
 529    public override void SetLength(long value)
 530    {
 0531      throw new NotSupportedException("InflaterInputStream SetLength not supported");
 532    }
 533
 534    /// <summary>
 535    /// Writes a sequence of bytes to stream and advances the current position
 536    /// This method always throws a NotSupportedException
 537    /// </summary>
 538    /// <param name="buffer">Thew buffer containing data to write.</param>
 539    /// <param name="offset">The offset of the first byte to write.</param>
 540    /// <param name="count">The number of bytes to write.</param>
 541    /// <exception cref="NotSupportedException">Any access</exception>
 542    public override void Write(byte[] buffer, int offset, int count)
 543    {
 0544      throw new NotSupportedException("InflaterInputStream Write not supported");
 545    }
 546
 547    /// <summary>
 548    /// Writes one byte to the current stream and advances the current position
 549    /// Always throws a NotSupportedException
 550    /// </summary>
 551    /// <param name="value">The byte to write.</param>
 552    /// <exception cref="NotSupportedException">Any access</exception>
 553    public override void WriteByte(byte value)
 554    {
 0555      throw new NotSupportedException("InflaterInputStream WriteByte not supported");
 556    }
 557
 558    /// <summary>
 559    /// Entry point to begin an asynchronous write.  Always throws a NotSupportedException.
 560    /// </summary>
 561    /// <param name="buffer">The buffer to write data from</param>
 562    /// <param name="offset">Offset of first byte to write</param>
 563    /// <param name="count">The maximum number of bytes to write</param>
 564    /// <param name="callback">The method to be called when the asynchronous write operation is completed</param>
 565    /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from ot
 566    /// <returns>An <see cref="System.IAsyncResult">IAsyncResult</see> that references the asynchronous write</returns>
 567    /// <exception cref="NotSupportedException">Any access</exception>
 568    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
 569    {
 0570      throw new NotSupportedException("InflaterInputStream BeginWrite not supported");
 571    }
 572
 573    /// <summary>
 574    /// Closes the input stream.  When <see cref="IsStreamOwner"></see>
 575    /// is true the underlying stream is also closed.
 576    /// </summary>
 577    public override void Close()
 578    {
 392579       if (!isClosed) {
 383580        isClosed = true;
 383581         if (isStreamOwner) {
 381582          baseInputStream.Close();
 583        }
 584      }
 392585    }
 586
 587    /// <summary>
 588    /// Reads decompressed data into the provided buffer byte array
 589    /// </summary>
 590    /// <param name ="buffer">
 591    /// The array to read and decompress data into
 592    /// </param>
 593    /// <param name ="offset">
 594    /// The offset indicating where the data should be placed
 595    /// </param>
 596    /// <param name ="count">
 597    /// The number of bytes to decompress
 598    /// </param>
 599    /// <returns>The number of bytes read.  Zero signals the end of stream</returns>
 600    /// <exception cref="SharpZipBaseException">
 601    /// Inflater needs a dictionary
 602    /// </exception>
 603    public override int Read(byte[] buffer, int offset, int count)
 604    {
 845605       if (inf.IsNeedingDictionary) {
 0606        throw new SharpZipBaseException("Need a dictionary");
 607      }
 608
 845609      int remainingBytes = count;
 610      while (true) {
 2212611        int bytesRead = inf.Inflate(buffer, offset, remainingBytes);
 2212612        offset += bytesRead;
 2212613        remainingBytes -= bytesRead;
 614
 2212615         if (remainingBytes == 0 || inf.IsFinished) {
 616          break;
 617        }
 618
 1367619         if (inf.IsNeedingInput) {
 1367620          Fill();
 1367621         } else if (bytesRead == 0) {
 0622          throw new ZipException("Dont know what to do");
 623        }
 624      }
 845625      return count - remainingBytes;
 626    }
 627    #endregion
 628
 629    #region Instance Fields
 630    /// <summary>
 631    /// Decompressor for this stream
 632    /// </summary>
 633    protected Inflater inf;
 634
 635    /// <summary>
 636    /// <see cref="InflaterInputBuffer">Input buffer</see> for this stream.
 637    /// </summary>
 638    protected InflaterInputBuffer inputBuffer;
 639
 640    /// <summary>
 641    /// Base stream the inflater reads from.
 642    /// </summary>
 643    private Stream baseInputStream;
 644
 645    /// <summary>
 646    /// The compressed size
 647    /// </summary>
 648    protected long csize;
 649
 650    /// <summary>
 651    /// Flag indicating wether this instance has been closed or not.
 652    /// </summary>
 653    bool isClosed;
 654
 655    /// <summary>
 656    /// Flag indicating wether this instance is designated the stream owner.
 657    /// When closing if this flag is true the underlying stream is closed.
 658    /// </summary>
 435659    bool isStreamOwner = true;
 660    #endregion
 661  }
 662}