Summary

Class:ICSharpCode.SharpZipLib.Zip.Compression.Inflater
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\Compression\Inflater.cs
Covered lines:173
Uncovered lines:57
Coverable lines:230
Total lines:788
Line coverage:75.2%
Branch coverage:59.8%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor()1100100
.ctor(...)3100100
Reset()310066.67
DecodeHeader()573.3366.67
DecodeDict()300
DecodeHuffman()1477.0861.54
DecodeChksum()481.8271.43
Decode()2487.2771.43
SetDictionary(...)100
SetDictionary(...)600
SetInput(...)100
SetInput(...)1100100
Inflate(...)200
Inflate(...)138480
.cctor()1100100

File(s)

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

#LineLine coverage
 1using System;
 2using ICSharpCode.SharpZipLib.Checksum;
 3using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
 4
 5namespace ICSharpCode.SharpZipLib.Zip.Compression
 6{
 7  /// <summary>
 8  /// Inflater is used to decompress data that has been compressed according
 9  /// to the "deflate" standard described in rfc1951.
 10  ///
 11  /// By default Zlib (rfc1950) headers and footers are expected in the input.
 12  /// You can use constructor <code> public Inflater(bool noHeader)</code> passing true
 13  /// if there is no Zlib header information
 14  ///
 15  /// The usage is as following.  First you have to set some input with
 16  /// <code>SetInput()</code>, then Inflate() it.  If inflate doesn't
 17  /// inflate any bytes there may be three reasons:
 18  /// <ul>
 19  /// <li>IsNeedingInput() returns true because the input buffer is empty.
 20  /// You have to provide more input with <code>SetInput()</code>.
 21  /// NOTE: IsNeedingInput() also returns true when, the stream is finished.
 22  /// </li>
 23  /// <li>IsNeedingDictionary() returns true, you have to provide a preset
 24  ///    dictionary with <code>SetDictionary()</code>.</li>
 25  /// <li>IsFinished returns true, the inflater has finished.</li>
 26  /// </ul>
 27  /// Once the first output byte is produced, a dictionary will not be
 28  /// needed at a later stage.
 29  ///
 30  /// author of the original java version : John Leuner, Jochen Hoenicke
 31  /// </summary>
 32  public class Inflater
 33  {
 34    #region Constants/Readonly
 35    /// <summary>
 36    /// Copy lengths for literal codes 257..285
 37    /// </summary>
 138    static readonly int[] CPLENS = {
 139                  3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
 140                  35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
 141                };
 42
 43    /// <summary>
 44    /// Extra bits for literal codes 257..285
 45    /// </summary>
 146    static readonly int[] CPLEXT = {
 147                  0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
 148                  3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
 149                };
 50
 51    /// <summary>
 52    /// Copy offsets for distance codes 0..29
 53    /// </summary>
 154    static readonly int[] CPDIST = {
 155                1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
 156                257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
 157                8193, 12289, 16385, 24577
 158                };
 59
 60    /// <summary>
 61    /// Extra bits for distance codes
 62    /// </summary>
 163    static readonly int[] CPDEXT = {
 164                0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
 165                7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
 166                12, 12, 13, 13
 167                };
 68
 69    /// <summary>
 70    /// These are the possible states for an inflater
 71    /// </summary>
 72    const int DECODE_HEADER = 0;
 73    const int DECODE_DICT = 1;
 74    const int DECODE_BLOCKS = 2;
 75    const int DECODE_STORED_LEN1 = 3;
 76    const int DECODE_STORED_LEN2 = 4;
 77    const int DECODE_STORED = 5;
 78    const int DECODE_DYN_HEADER = 6;
 79    const int DECODE_HUFFMAN = 7;
 80    const int DECODE_HUFFMAN_LENBITS = 8;
 81    const int DECODE_HUFFMAN_DIST = 9;
 82    const int DECODE_HUFFMAN_DISTBITS = 10;
 83    const int DECODE_CHKSUM = 11;
 84    const int FINISHED = 12;
 85    #endregion
 86
 87    #region Instance Fields
 88    /// <summary>
 89    /// This variable contains the current state.
 90    /// </summary>
 91    int mode;
 92
 93    /// <summary>
 94    /// The adler checksum of the dictionary or of the decompressed
 95    /// stream, as it is written in the header resp. footer of the
 96    /// compressed stream.
 97    /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
 98    /// </summary>
 99    int readAdler;
 100
 101    /// <summary>
 102    /// The number of bits needed to complete the current state.  This
 103    /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
 104    /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
 105    /// </summary>
 106    int neededBits;
 107    int repLength;
 108    int repDist;
 109    int uncomprLen;
 110
 111    /// <summary>
 112    /// True, if the last block flag was set in the last block of the
 113    /// inflated stream.  This means that the stream ends after the
 114    /// current block.
 115    /// </summary>
 116    bool isLastBlock;
 117
 118    /// <summary>
 119    /// The total number of inflated bytes.
 120    /// </summary>
 121    long totalOut;
 122
 123    /// <summary>
 124    /// The total number of bytes set with setInput().  This is not the
 125    /// value returned by the TotalIn property, since this also includes the
 126    /// unprocessed input.
 127    /// </summary>
 128    long totalIn;
 129
 130    /// <summary>
 131    /// This variable stores the noHeader flag that was given to the constructor.
 132    /// True means, that the inflated stream doesn't contain a Zlib header or
 133    /// footer.
 134    /// </summary>
 135    bool noHeader;
 136    readonly StreamManipulator input;
 137    OutputWindow outputWindow;
 138    InflaterDynHeader dynHeader;
 139    InflaterHuffmanTree litlenTree, distTree;
 140    Adler32 adler;
 141    #endregion
 142
 143    #region Constructors
 144    /// <summary>
 145    /// Creates a new inflater or RFC1951 decompressor
 146    /// RFC1950/Zlib headers and footers will be expected in the input data
 147    /// </summary>
 3148    public Inflater() : this(false)
 149    {
 3150    }
 151
 152    /// <summary>
 153    /// Creates a new inflater.
 154    /// </summary>
 155    /// <param name="noHeader">
 156    /// True if no RFC1950/Zlib header and footer fields are expected in the input data
 157    ///
 158    /// This is used for GZIPed/Zipped input.
 159    ///
 160    /// For compatibility with
 161    /// Sun JDK you should provide one byte of input more than needed in
 162    /// this case.
 163    /// </param>
 435164    public Inflater(bool noHeader)
 165    {
 435166      this.noHeader = noHeader;
 435167      this.adler = new Adler32();
 435168      input = new StreamManipulator();
 435169      outputWindow = new OutputWindow();
 435170       mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER;
 435171    }
 172    #endregion
 173
 174    /// <summary>
 175    /// Resets the inflater so that a new stream can be decompressed.  All
 176    /// pending input and output will be discarded.
 177    /// </summary>
 178    public void Reset()
 179    {
 41180       mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER;
 41181      totalIn = 0;
 41182      totalOut = 0;
 41183      input.Reset();
 41184      outputWindow.Reset();
 41185      dynHeader = null;
 41186      litlenTree = null;
 41187      distTree = null;
 41188      isLastBlock = false;
 41189      adler.Reset();
 41190    }
 191
 192    /// <summary>
 193    /// Decodes a zlib/RFC1950 header.
 194    /// </summary>
 195    /// <returns>
 196    /// False if more input is needed.
 197    /// </returns>
 198    /// <exception cref="SharpZipBaseException">
 199    /// The header is invalid.
 200    /// </exception>
 201    private bool DecodeHeader()
 202    {
 22203      int header = input.PeekBits(16);
 22204       if (header < 0) {
 11205        return false;
 206      }
 11207      input.DropBits(16);
 208
 209      // The header is written in "wrong" byte order
 11210      header = ((header << 8) | (header >> 8)) & 0xffff;
 11211       if (header % 31 != 0) {
 0212        throw new SharpZipBaseException("Header checksum illegal");
 213      }
 214
 11215       if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) {
 0216        throw new SharpZipBaseException("Compression Method unknown");
 217      }
 218
 219      /* Maximum size of the backwards window in bits.
 220      * We currently ignore this, but we could use it to make the
 221      * inflater window more space efficient. On the other hand the
 222      * full window (15 bits) is needed most times, anyway.
 223      int max_wbits = ((header & 0x7000) >> 12) + 8;
 224      */
 225
 11226       if ((header & 0x0020) == 0) { // Dictionary flag?
 11227        mode = DECODE_BLOCKS;
 11228      } else {
 0229        mode = DECODE_DICT;
 0230        neededBits = 32;
 231      }
 11232      return true;
 233    }
 234
 235    /// <summary>
 236    /// Decodes the dictionary checksum after the deflate header.
 237    /// </summary>
 238    /// <returns>
 239    /// False if more input is needed.
 240    /// </returns>
 241    private bool DecodeDict()
 242    {
 0243       while (neededBits > 0) {
 0244        int dictByte = input.PeekBits(8);
 0245         if (dictByte < 0) {
 0246          return false;
 247        }
 0248        input.DropBits(8);
 0249        readAdler = (readAdler << 8) | dictByte;
 0250        neededBits -= 8;
 251      }
 0252      return false;
 253    }
 254
 255    /// <summary>
 256    /// Decodes the huffman encoded symbols in the input stream.
 257    /// </summary>
 258    /// <returns>
 259    /// false if more input is needed, true if output window is
 260    /// full or the current block ends.
 261    /// </returns>
 262    /// <exception cref="SharpZipBaseException">
 263    /// if deflated stream is invalid.
 264    /// </exception>
 265    private bool DecodeHuffman()
 266    {
 371267      int free = outputWindow.GetFreeSpace();
 468268       while (free >= 258) {
 269        int symbol;
 468270         switch (mode) {
 271          case DECODE_HUFFMAN:
 272            // This is the inner loop so it is optimized a bit
 9697273             while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) {
 9229274              outputWindow.Write(symbol);
 9229275               if (--free < 258) {
 0276                return true;
 277              }
 278            }
 279
 468280             if (symbol < 257) {
 371281               if (symbol < 0) {
 0282                return false;
 283              } else {
 284                // symbol == 256: end of block
 371285                distTree = null;
 371286                litlenTree = null;
 371287                mode = DECODE_BLOCKS;
 371288                return true;
 289              }
 290            }
 291
 292            try {
 97293              repLength = CPLENS[symbol - 257];
 97294              neededBits = CPLEXT[symbol - 257];
 97295            } catch (Exception) {
 0296              throw new SharpZipBaseException("Illegal rep length code");
 297            }
 298            goto case DECODE_HUFFMAN_LENBITS; // fall through
 299
 300          case DECODE_HUFFMAN_LENBITS:
 97301             if (neededBits > 0) {
 25302              mode = DECODE_HUFFMAN_LENBITS;
 25303              int i = input.PeekBits(neededBits);
 25304               if (i < 0) {
 0305                return false;
 306              }
 25307              input.DropBits(neededBits);
 25308              repLength += i;
 309            }
 97310            mode = DECODE_HUFFMAN_DIST;
 311            goto case DECODE_HUFFMAN_DIST; // fall through
 312
 313          case DECODE_HUFFMAN_DIST:
 97314            symbol = distTree.GetSymbol(input);
 97315             if (symbol < 0) {
 0316              return false;
 317            }
 318
 319            try {
 97320              repDist = CPDIST[symbol];
 97321              neededBits = CPDEXT[symbol];
 97322            } catch (Exception) {
 0323              throw new SharpZipBaseException("Illegal rep dist code");
 324            }
 325
 326            goto case DECODE_HUFFMAN_DISTBITS; // fall through
 327
 328          case DECODE_HUFFMAN_DISTBITS:
 97329             if (neededBits > 0) {
 68330              mode = DECODE_HUFFMAN_DISTBITS;
 68331              int i = input.PeekBits(neededBits);
 68332               if (i < 0) {
 0333                return false;
 334              }
 68335              input.DropBits(neededBits);
 68336              repDist += i;
 337            }
 338
 97339            outputWindow.Repeat(repLength, repDist);
 97340            free -= repLength;
 97341            mode = DECODE_HUFFMAN;
 97342            break;
 343
 344          default:
 0345            throw new SharpZipBaseException("Inflater unknown mode");
 346        }
 347      }
 0348      return true;
 349    }
 350
 351    /// <summary>
 352    /// Decodes the adler checksum after the deflate stream.
 353    /// </summary>
 354    /// <returns>
 355    /// false if more input is needed.
 356    /// </returns>
 357    /// <exception cref="SharpZipBaseException">
 358    /// If checksum doesn't match.
 359    /// </exception>
 360    private bool DecodeChksum()
 361    {
 5362       while (neededBits > 0) {
 4363        int chkByte = input.PeekBits(8);
 4364         if (chkByte < 0) {
 0365          return false;
 366        }
 4367        input.DropBits(8);
 4368        readAdler = (readAdler << 8) | chkByte;
 4369        neededBits -= 8;
 370      }
 371
 1372       if ((int)adler.Value != readAdler) {
 0373        throw new SharpZipBaseException("Adler chksum doesn't match: " + (int)adler.Value + " vs. " + readAdler);
 374      }
 375
 1376      mode = FINISHED;
 1377      return false;
 378    }
 379
 380    /// <summary>
 381    /// Decodes the deflated stream.
 382    /// </summary>
 383    /// <returns>
 384    /// false if more input is needed, or if finished.
 385    /// </returns>
 386    /// <exception cref="SharpZipBaseException">
 387    /// if deflated stream is invalid.
 388    /// </exception>
 389    private bool Decode()
 390    {
 4422391       switch (mode) {
 392        case DECODE_HEADER:
 22393          return DecodeHeader();
 394
 395        case DECODE_DICT:
 0396          return DecodeDict();
 397
 398        case DECODE_CHKSUM:
 1399          return DecodeChksum();
 400
 401        case DECODE_BLOCKS:
 1412402           if (isLastBlock) {
 387403             if (noHeader) {
 376404              mode = FINISHED;
 376405              return false;
 406            } else {
 11407              input.SkipToByteBoundary();
 11408              neededBits = 32;
 11409              mode = DECODE_CHKSUM;
 11410              return true;
 411            }
 412          }
 413
 1025414          int type = input.PeekBits(3);
 1025415           if (type < 0) {
 362416            return false;
 417          }
 663418          input.DropBits(3);
 419
 663420          isLastBlock |= (type & 1) != 0;
 663421           switch (type >> 1) {
 422            case DeflaterConstants.STORED_BLOCK:
 292423              input.SkipToByteBoundary();
 292424              mode = DECODE_STORED_LEN1;
 292425              break;
 426            case DeflaterConstants.STATIC_TREES:
 370427              litlenTree = InflaterHuffmanTree.defLitLenTree;
 370428              distTree = InflaterHuffmanTree.defDistTree;
 370429              mode = DECODE_HUFFMAN;
 370430              break;
 431            case DeflaterConstants.DYN_TREES:
 1432              dynHeader = new InflaterDynHeader();
 1433              mode = DECODE_DYN_HEADER;
 1434              break;
 435            default:
 0436              throw new SharpZipBaseException("Unknown block type " + type);
 437          }
 663438          return true;
 439
 440        case DECODE_STORED_LEN1: {
 292441             if ((uncomprLen = input.PeekBits(16)) < 0) {
 0442              return false;
 443            }
 292444            input.DropBits(16);
 292445            mode = DECODE_STORED_LEN2;
 446          }
 447          goto case DECODE_STORED_LEN2; // fall through
 448
 449        case DECODE_STORED_LEN2: {
 292450            int nlen = input.PeekBits(16);
 292451             if (nlen < 0) {
 0452              return false;
 453            }
 292454            input.DropBits(16);
 292455             if (nlen != (uncomprLen ^ 0xffff)) {
 0456              throw new SharpZipBaseException("broken uncompressed block");
 457            }
 292458            mode = DECODE_STORED;
 459          }
 460          goto case DECODE_STORED; // fall through
 461
 462        case DECODE_STORED: {
 2280463            int more = outputWindow.CopyStored(input, uncomprLen);
 2280464            uncomprLen -= more;
 2280465             if (uncomprLen == 0) {
 292466              mode = DECODE_BLOCKS;
 292467              return true;
 468            }
 1988469            return !input.IsNeedingInput;
 470          }
 471
 472        case DECODE_DYN_HEADER:
 1473           if (!dynHeader.Decode(input)) {
 0474            return false;
 475          }
 476
 1477          litlenTree = dynHeader.BuildLitLenTree();
 1478          distTree = dynHeader.BuildDistTree();
 1479          mode = DECODE_HUFFMAN;
 480          goto case DECODE_HUFFMAN; // fall through
 481
 482        case DECODE_HUFFMAN:
 483        case DECODE_HUFFMAN_LENBITS:
 484        case DECODE_HUFFMAN_DIST:
 485        case DECODE_HUFFMAN_DISTBITS:
 371486          return DecodeHuffman();
 487
 488        case FINISHED:
 336489          return false;
 490
 491        default:
 0492          throw new SharpZipBaseException("Inflater.Decode unknown mode");
 493      }
 494    }
 495
 496    /// <summary>
 497    /// Sets the preset dictionary.  This should only be called, if
 498    /// needsDictionary() returns true and it should set the same
 499    /// dictionary, that was used for deflating.  The getAdler()
 500    /// function returns the checksum of the dictionary needed.
 501    /// </summary>
 502    /// <param name="buffer">
 503    /// The dictionary.
 504    /// </param>
 505    public void SetDictionary(byte[] buffer)
 506    {
 0507      SetDictionary(buffer, 0, buffer.Length);
 0508    }
 509
 510    /// <summary>
 511    /// Sets the preset dictionary.  This should only be called, if
 512    /// needsDictionary() returns true and it should set the same
 513    /// dictionary, that was used for deflating.  The getAdler()
 514    /// function returns the checksum of the dictionary needed.
 515    /// </summary>
 516    /// <param name="buffer">
 517    /// The dictionary.
 518    /// </param>
 519    /// <param name="index">
 520    /// The index into buffer where the dictionary starts.
 521    /// </param>
 522    /// <param name="count">
 523    /// The number of bytes in the dictionary.
 524    /// </param>
 525    /// <exception cref="System.InvalidOperationException">
 526    /// No dictionary is needed.
 527    /// </exception>
 528    /// <exception cref="SharpZipBaseException">
 529    /// The adler checksum for the buffer is invalid
 530    /// </exception>
 531    public void SetDictionary(byte[] buffer, int index, int count)
 532    {
 0533       if (buffer == null) {
 0534        throw new ArgumentNullException(nameof(buffer));
 535      }
 536
 0537       if (index < 0) {
 0538        throw new ArgumentOutOfRangeException(nameof(index));
 539      }
 540
 0541       if (count < 0) {
 0542        throw new ArgumentOutOfRangeException(nameof(count));
 543      }
 544
 0545       if (!IsNeedingDictionary) {
 0546        throw new InvalidOperationException("Dictionary is not needed");
 547      }
 548
 0549      adler.Update(buffer, index, count);
 550
 0551       if ((int)adler.Value != readAdler) {
 0552        throw new SharpZipBaseException("Wrong adler checksum");
 553      }
 0554      adler.Reset();
 0555      outputWindow.CopyDict(buffer, index, count);
 0556      mode = DECODE_BLOCKS;
 0557    }
 558
 559    /// <summary>
 560    /// Sets the input.  This should only be called, if needsInput()
 561    /// returns true.
 562    /// </summary>
 563    /// <param name="buffer">
 564    /// the input.
 565    /// </param>
 566    public void SetInput(byte[] buffer)
 567    {
 0568      SetInput(buffer, 0, buffer.Length);
 0569    }
 570
 571    /// <summary>
 572    /// Sets the input.  This should only be called, if needsInput()
 573    /// returns true.
 574    /// </summary>
 575    /// <param name="buffer">
 576    /// The source of input data
 577    /// </param>
 578    /// <param name="index">
 579    /// The index into buffer where the input starts.
 580    /// </param>
 581    /// <param name="count">
 582    /// The number of bytes of input to use.
 583    /// </param>
 584    /// <exception cref="System.InvalidOperationException">
 585    /// No input is needed.
 586    /// </exception>
 587    /// <exception cref="System.ArgumentOutOfRangeException">
 588    /// The index and/or count are wrong.
 589    /// </exception>
 590    public void SetInput(byte[] buffer, int index, int count)
 591    {
 1433592      input.SetInput(buffer, index, count);
 1433593      totalIn += (long)count;
 1433594    }
 595
 596    /// <summary>
 597    /// Inflates the compressed stream to the output buffer.  If this
 598    /// returns 0, you should check, whether IsNeedingDictionary(),
 599    /// IsNeedingInput() or IsFinished() returns true, to determine why no
 600    /// further output is produced.
 601    /// </summary>
 602    /// <param name="buffer">
 603    /// the output buffer.
 604    /// </param>
 605    /// <returns>
 606    /// The number of bytes written to the buffer, 0 if no further
 607    /// output can be produced.
 608    /// </returns>
 609    /// <exception cref="System.ArgumentOutOfRangeException">
 610    /// if buffer has length 0.
 611    /// </exception>
 612    /// <exception cref="System.FormatException">
 613    /// if deflated stream is invalid.
 614    /// </exception>
 615    public int Inflate(byte[] buffer)
 616    {
 0617       if (buffer == null) {
 0618        throw new ArgumentNullException(nameof(buffer));
 619      }
 620
 0621      return Inflate(buffer, 0, buffer.Length);
 622    }
 623
 624    /// <summary>
 625    /// Inflates the compressed stream to the output buffer.  If this
 626    /// returns 0, you should check, whether needsDictionary(),
 627    /// needsInput() or finished() returns true, to determine why no
 628    /// further output is produced.
 629    /// </summary>
 630    /// <param name="buffer">
 631    /// the output buffer.
 632    /// </param>
 633    /// <param name="offset">
 634    /// the offset in buffer where storing starts.
 635    /// </param>
 636    /// <param name="count">
 637    /// the maximum number of bytes to output.
 638    /// </param>
 639    /// <returns>
 640    /// the number of bytes written to the buffer, 0 if no further output can be produced.
 641    /// </returns>
 642    /// <exception cref="System.ArgumentOutOfRangeException">
 643    /// if count is less than 0.
 644    /// </exception>
 645    /// <exception cref="System.ArgumentOutOfRangeException">
 646    /// if the index and / or count are wrong.
 647    /// </exception>
 648    /// <exception cref="System.FormatException">
 649    /// if deflated stream is invalid.
 650    /// </exception>
 651    public int Inflate(byte[] buffer, int offset, int count)
 652    {
 2212653       if (buffer == null) {
 0654        throw new ArgumentNullException(nameof(buffer));
 655      }
 656
 2212657       if (count < 0) {
 0658        throw new ArgumentOutOfRangeException(nameof(count), "count cannot be negative");
 659      }
 660
 2212661       if (offset < 0) {
 0662        throw new ArgumentOutOfRangeException(nameof(offset), "offset cannot be negative");
 663      }
 664
 2212665       if (offset + count > buffer.Length) {
 0666        throw new ArgumentException("count exceeds buffer bounds");
 667      }
 668
 669      // Special case: count may be zero
 2212670       if (count == 0) {
 20671         if (!IsFinished) { // -jr- 08-Nov-2003 INFLATE_BUG fix..
 20672          Decode();
 673        }
 20674        return 0;
 675      }
 676
 2192677      int bytesCopied = 0;
 678
 679      do {
 4524680         if (mode != DECODE_CHKSUM) {
 681          /* Don't give away any output, if we are waiting for the
 682          * checksum in the input stream.
 683          *
 684          * With this trick we have always:
 685          *   IsNeedingInput() and not IsFinished()
 686          *   implies more output can be produced.
 687          */
 4523688          int more = outputWindow.CopyOutput(buffer, offset, count);
 4523689           if (more > 0) {
 1679690            adler.Update(buffer, offset, more);
 1679691            offset += more;
 1679692            bytesCopied += more;
 1679693            totalOut += (long)more;
 1679694            count -= more;
 1679695             if (count == 0) {
 122696              return bytesCopied;
 697            }
 698          }
 699        }
 4402700       } while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM)));
 2070701      return bytesCopied;
 702    }
 703
 704    /// <summary>
 705    /// Returns true, if the input buffer is empty.
 706    /// You should then call setInput().
 707    /// NOTE: This method also returns true when the stream is finished.
 708    /// </summary>
 709    public bool IsNeedingInput {
 710      get {
 1367711        return input.IsNeedingInput;
 712      }
 713    }
 714
 715    /// <summary>
 716    /// Returns true, if a preset dictionary is needed to inflate the input.
 717    /// </summary>
 718    public bool IsNeedingDictionary {
 719      get {
 845720        return mode == DECODE_DICT && neededBits == 0;
 721      }
 722    }
 723
 724    /// <summary>
 725    /// Returns true, if the inflater has finished.  This means, that no
 726    /// input is needed and no output can be produced.
 727    /// </summary>
 728    public bool IsFinished {
 729      get {
 2106730        return mode == FINISHED && outputWindow.GetAvailable() == 0;
 731      }
 732    }
 733
 734    /// <summary>
 735    /// Gets the adler checksum.  This is either the checksum of all
 736    /// uncompressed bytes returned by inflate(), or if needsDictionary()
 737    /// returns true (and thus no output was yet produced) this is the
 738    /// adler checksum of the expected dictionary.
 739    /// </summary>
 740    /// <returns>
 741    /// the adler checksum.
 742    /// </returns>
 743    public int Adler {
 744      get {
 0745        return IsNeedingDictionary ? readAdler : (int)adler.Value;
 746      }
 747    }
 748
 749    /// <summary>
 750    /// Gets the total number of output bytes returned by Inflate().
 751    /// </summary>
 752    /// <returns>
 753    /// the total number of output bytes.
 754    /// </returns>
 755    public long TotalOut {
 756      get {
 13757        return totalOut;
 758      }
 759    }
 760
 761    /// <summary>
 762    /// Gets the total number of processed compressed input bytes.
 763    /// </summary>
 764    /// <returns>
 765    /// The total number of bytes of processed input bytes.
 766    /// </returns>
 767    public long TotalIn {
 768      get {
 22769        return totalIn - (long)RemainingInput;
 770      }
 771    }
 772
 773    /// <summary>
 774    /// Gets the number of unprocessed input bytes.  Useful, if the end of the
 775    /// stream is reached and you want to further process the bytes after
 776    /// the deflate stream.
 777    /// </summary>
 778    /// <returns>
 779    /// The number of bytes of the input which have not been processed.
 780    /// </returns>
 781    public int RemainingInput {
 782      // TODO: This should be a long?
 783      get {
 47784        return input.AvailableBytes;
 785      }
 786    }
 787  }
 788}