PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Utilities/Compression/Inflaters/Inflater.cs

#
C# | 873 lines | 496 code | 84 blank | 293 comment | 59 complexity | 572f27cac0a398b8f8780cc5d3ce924c MD5 | raw file
Possible License(s): Apache-2.0
  1. // Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
  2. // Authors of the original java version: Jochen Hoenicke, John Leuner
  3. // See http://www.ISeeSharpCode.com for more information.
  4. using System;
  5. using Delta.Utilities.Compression.Checksums;
  6. using Delta.Utilities.Compression.Deflaters;
  7. using Delta.Utilities.Compression.Streams;
  8. namespace Delta.Utilities.Compression.Inflaters
  9. {
  10. /// <summary>
  11. /// Inflater is used to decompress data that has been compressed according
  12. /// to the "deflate" standard described in rfc1950.
  13. /// <para />
  14. /// The usage is as following. First you have to set some input with
  15. /// <code>setInput()</code>, then inflate() it. If inflate doesn't
  16. /// inflate any bytes there may be three reasons:
  17. /// <ul>
  18. /// <li>needsInput() returns true because the input buffer is empty.
  19. /// You have to provide more input with <code>setInput()</code>.
  20. /// NOTE: needsInput() also returns true when, the stream is finished.
  21. /// </li>
  22. /// <li>needsDictionary() returns true, you have to provide a preset
  23. /// dictionary with <code>setDictionary()</code>.</li>
  24. /// <li>finished() returns true, the inflater has finished.</li>
  25. /// </ul>
  26. /// Once the first output byte is produced, a dictionary will not be
  27. /// needed at a later stage.
  28. /// </summary>
  29. public class Inflater
  30. {
  31. #region Constants
  32. /// <summary>
  33. /// Copy lengths for literal codes 257..285
  34. /// </summary>
  35. private static readonly int[] CopyLengths =
  36. {
  37. 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
  38. 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
  39. };
  40. /// <summary>
  41. /// Extra bits for literal codes 257..285
  42. /// </summary>
  43. private static readonly int[] CopyLengthExtraBits =
  44. {
  45. 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
  46. 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
  47. };
  48. /// <summary>
  49. /// Copy offsets for distance codes 0..29
  50. /// </summary>
  51. private static readonly int[] CopyDistanceOffsets =
  52. {
  53. 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
  54. 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
  55. 8193, 12289, 16385, 24577
  56. };
  57. /// <summary>
  58. /// Extra bits for distance codes
  59. /// </summary>
  60. private static readonly int[] CopyDistanceExtraBits =
  61. {
  62. 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
  63. 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
  64. 12, 12, 13, 13
  65. };
  66. /// <summary>
  67. /// These are the possible states for an inflater to perform decoding.
  68. /// </summary>
  69. private const int
  70. DecodeHeaderState = 0,
  71. DecodeDictionaryState = 1,
  72. DecodeBlocksState = 2,
  73. DecodeStoredLength1 = 3,
  74. DecodeStoredLength2 = 4,
  75. DecodeStored = 5,
  76. DecodeDynamicHeader = 6,
  77. DecodeHuffmanState = 7,
  78. DecodeHuffmanLengthBits = 8,
  79. DecodeHuffmanDistance = 9,
  80. DecodeHuffmanDistanceBits = 10,
  81. DecodeChecksum = 11,
  82. DecodeFinished = 12;
  83. #endregion
  84. #region Public
  85. /// <summary>
  86. /// Returns true, if the input buffer is empty.
  87. /// You should then call setInput().
  88. /// NOTE: This method also returns true when the stream is finished.
  89. /// </summary>
  90. public bool IsNeedingInput
  91. {
  92. get
  93. {
  94. return input.IsNeedingInput;
  95. } // get
  96. }
  97. /// <summary>
  98. /// Returns true, if a preset dictionary is needed to inflate the input.
  99. /// </summary>
  100. public bool IsNeedingDictionary
  101. {
  102. get
  103. {
  104. return mode == DecodeDictionaryState && neededBits == 0;
  105. } // get
  106. }
  107. /// <summary>
  108. /// Returns true, if the inflater has finished. This means, that no
  109. /// input is needed and no output can be produced.
  110. /// </summary>
  111. public bool IsFinished
  112. {
  113. get
  114. {
  115. return mode == DecodeFinished && outputWindow.GetAvailable() == 0;
  116. } // get
  117. }
  118. /// <summary>
  119. /// Gets the adler checksum. This is either the checksum of all
  120. /// uncompressed bytes returned by inflate(), or if needsDictionary()
  121. /// returns true (and thus no output was yet produced) this is the
  122. /// adler checksum of the expected dictionary.
  123. /// </summary>
  124. public int Adler
  125. {
  126. get
  127. {
  128. return
  129. IsNeedingDictionary
  130. ? readAdler
  131. : (int)adler.Value;
  132. } // get
  133. }
  134. /// <summary>
  135. /// Gets the total number of output bytes returned by inflate().
  136. /// </summary>
  137. /// <returns>
  138. /// the total number of output bytes.
  139. /// </returns>
  140. public int TotalOut
  141. {
  142. get
  143. {
  144. return totalOut;
  145. } // get
  146. }
  147. /// <summary>
  148. /// Gets the total number of processed compressed input bytes.
  149. /// </summary>
  150. /// <returns>
  151. /// The total number of bytes of processed input bytes.
  152. /// </returns>
  153. public int TotalIn
  154. {
  155. get
  156. {
  157. return totalIn - RemainingInput;
  158. } // get
  159. }
  160. /// <summary>
  161. /// Gets the number of unprocessed input bytes. Useful, if the end of the
  162. /// stream is reached and you want to further process the bytes after
  163. /// the deflate stream.
  164. /// </summary>
  165. /// <returns>
  166. /// The number of bytes of the input which have not been processed.
  167. /// </returns>
  168. public int RemainingInput
  169. {
  170. get
  171. {
  172. return input.AvailableBytes;
  173. } // get
  174. }
  175. #endregion
  176. #region Private
  177. /// <summary>
  178. /// This variable contains the current state.
  179. /// </summary>
  180. private int mode;
  181. /// <summary>
  182. /// The adler checksum of the dictionary or of the decompressed
  183. /// stream, as it is written in the header resp. footer of the
  184. /// compressed stream.
  185. /// Only valid if mode is DecodeDictionaryState or DecodeChecksum.
  186. /// </summary>
  187. private int readAdler;
  188. /// <summary>
  189. /// The number of bits needed to complete the current state. This
  190. /// is valid, if mode is DecodeDictionaryState, DecodeChecksum,
  191. /// DecodeHuffmanLengthBits or DecodeHuffmanDistanceBits.
  192. /// </summary>
  193. private int neededBits;
  194. private int repLength;
  195. private int repDist;
  196. private int uncomprLen;
  197. /// <summary>
  198. /// True, if the last block flag was set in the last block of the
  199. /// inflated stream. This means that the stream ends after the
  200. /// current block.
  201. /// </summary>
  202. private bool isLastBlock;
  203. /// <summary>
  204. /// The total number of inflated bytes.
  205. /// </summary>
  206. private int totalOut;
  207. /// <summary>
  208. /// The total number of bytes set with setInput(). This is not the
  209. /// value returned by the TotalIn property, since this also includes the
  210. /// unprocessed input.
  211. /// </summary>
  212. private int totalIn;
  213. /// <summary>
  214. /// This variable stores the noHeader flag that was given to the
  215. /// constructor. True means, that the inflated stream doesn't contain a
  216. /// Zlib header or footer.
  217. /// </summary>
  218. private readonly bool noHeader;
  219. private readonly StreamManipulator input;
  220. private readonly OutputWindow outputWindow;
  221. private InflaterDynHeader dynHeader;
  222. private InflaterHuffmanTree litlenTree, distTree;
  223. private readonly Adler32 adler;
  224. #endregion
  225. #region Constructors
  226. /// <summary>
  227. /// Creates a new inflater or RFC1951 decompressor
  228. /// RFC1950/Zlib headers and footers will be expected in the input data
  229. /// </summary>
  230. public Inflater()
  231. : this(false)
  232. {
  233. }
  234. // Inflater()
  235. /// <summary>
  236. /// Creates a new inflater.
  237. /// </summary>
  238. /// <param name="noHeader">
  239. /// True if no RFC1950/Zlib header and footer fields are expected in the
  240. /// input data. This is used for GZIPed/Zipped input. For compatibility
  241. /// with Sun JDK you should provide one byte of input more than needed in
  242. /// this case.
  243. /// </param>
  244. public Inflater(bool noHeader)
  245. {
  246. this.noHeader = noHeader;
  247. adler = new Adler32();
  248. input = new StreamManipulator();
  249. outputWindow = new OutputWindow();
  250. mode = noHeader
  251. ? DecodeBlocksState
  252. : DecodeHeaderState;
  253. }
  254. // Inflater(noHeader)
  255. #endregion
  256. #region Methods
  257. /// <summary>
  258. /// Resets the inflater so that a new stream can be decompressed.
  259. /// All pending input and output will be discarded.
  260. /// </summary>
  261. public void Reset()
  262. {
  263. mode = noHeader
  264. ? DecodeBlocksState
  265. : DecodeHeaderState;
  266. totalIn = totalOut = 0;
  267. input.Reset();
  268. outputWindow.Reset();
  269. dynHeader = null;
  270. litlenTree = null;
  271. distTree = null;
  272. isLastBlock = false;
  273. adler.Reset();
  274. }
  275. // Reset()
  276. /// <summary>
  277. /// Decodes a zlib/RFC1950 header.
  278. /// </summary>
  279. /// <returns>False if more input is needed.</returns>
  280. /// <exception cref="CompressionException">The header is invalid.
  281. /// </exception>
  282. private bool DecodeHeader()
  283. {
  284. int header = input.PeekBits(16);
  285. if (header < 0)
  286. {
  287. return false;
  288. } // if (header)
  289. input.DropBits(16);
  290. // The header is written in "wrong" byte order
  291. header = ((header << 8) | (header >> 8)) & 0xffff;
  292. if (header % 31 != 0)
  293. {
  294. throw new CompressionException("Header checksum illegal");
  295. } // if (header)
  296. if ((header & 0x0f00) != (Deflater.Deflated << 8))
  297. {
  298. throw new CompressionException("Compression Method unknown");
  299. } // if (header)
  300. // Maximum size of the backwards window in bits.
  301. // We currently ignore this, but we could use it to make the
  302. // inflater window more space efficient. On the other hand the
  303. // full window (15 bits) is needed most times, anyway.
  304. //int max_wbits = ((header & 0x7000) >> 12) + 8;
  305. if ((header & 0x0020) == 0)
  306. {
  307. // Dictionary flag?
  308. mode = DecodeBlocksState;
  309. } // if (header)
  310. else
  311. {
  312. mode = DecodeDictionaryState;
  313. neededBits = 32;
  314. } // else
  315. return true;
  316. }
  317. // DecodeHeader()
  318. /// <summary>
  319. /// Decodes the dictionary checksum after the deflate header.
  320. /// </summary>
  321. /// <returns>
  322. /// False if more input is needed.
  323. /// </returns>
  324. private bool DecodeDict()
  325. {
  326. while (neededBits > 0)
  327. {
  328. int dictByte = input.PeekBits(8);
  329. if (dictByte < 0)
  330. {
  331. return false;
  332. } // if (dictByte)
  333. input.DropBits(8);
  334. readAdler = (readAdler << 8) | dictByte;
  335. neededBits -= 8;
  336. } // while (neededBits)
  337. return false;
  338. }
  339. // DecodeDict()
  340. /// <summary>
  341. /// Decodes the huffman encoded symbols in the input stream.
  342. /// </summary>
  343. /// <returns>
  344. /// false if more input is needed, true if output window is
  345. /// full or the current block ends.
  346. /// </returns>
  347. /// <exception cref="CompressionException">
  348. /// if deflated stream is invalid.
  349. /// </exception>
  350. private bool DecodeHuffman()
  351. {
  352. int free = outputWindow.GetFreeSpace();
  353. while (free >= 258)
  354. {
  355. int symbol;
  356. switch (mode)
  357. {
  358. case DecodeHuffmanState:
  359. // This is the inner loop so it is optimized a bit
  360. while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0)
  361. {
  362. outputWindow.Write(symbol);
  363. if (--free < 258)
  364. {
  365. return true;
  366. } // if (--free)
  367. } // while ()
  368. if (symbol < 257)
  369. {
  370. if (symbol < 0)
  371. {
  372. return false;
  373. } // if (symbol)
  374. else
  375. {
  376. // symbol == 256: end of block
  377. distTree = null;
  378. litlenTree = null;
  379. mode = DecodeBlocksState;
  380. return true;
  381. } // else
  382. } // if (symbol)
  383. try
  384. {
  385. repLength = CopyLengths[symbol - 257];
  386. neededBits = CopyLengthExtraBits[symbol - 257];
  387. } // try
  388. catch (Exception)
  389. {
  390. throw new CompressionException(
  391. "Illegal rep length code");
  392. } // catch (Exception)
  393. // fall through
  394. goto case DecodeHuffmanLengthBits;
  395. case DecodeHuffmanLengthBits:
  396. if (neededBits > 0)
  397. {
  398. mode = DecodeHuffmanLengthBits;
  399. int i = input.PeekBits(neededBits);
  400. if (i < 0)
  401. {
  402. return false;
  403. } // if (i)
  404. input.DropBits(neededBits);
  405. repLength += i;
  406. } // if (neededBits)
  407. mode = DecodeHuffmanDistance;
  408. // fall through
  409. goto case DecodeHuffmanDistance;
  410. case DecodeHuffmanDistance:
  411. symbol = distTree.GetSymbol(input);
  412. if (symbol < 0)
  413. {
  414. return false;
  415. } // if (symbol)
  416. try
  417. {
  418. repDist = CopyDistanceOffsets[symbol];
  419. neededBits = CopyDistanceExtraBits[symbol];
  420. } // try
  421. catch (Exception)
  422. {
  423. throw new CompressionException("Illegal rep dist code");
  424. } // catch (Exception)
  425. // fall through
  426. goto case DecodeHuffmanDistanceBits;
  427. case DecodeHuffmanDistanceBits:
  428. if (neededBits > 0)
  429. {
  430. mode = DecodeHuffmanDistanceBits;
  431. int i = input.PeekBits(neededBits);
  432. if (i < 0)
  433. {
  434. return false;
  435. } // if (i)
  436. input.DropBits(neededBits);
  437. repDist += i;
  438. } // if (neededBits)
  439. outputWindow.Repeat(repLength, repDist);
  440. free -= repLength;
  441. mode = DecodeHuffmanState;
  442. break;
  443. default:
  444. throw new CompressionException("Inflater unknown mode");
  445. } // switch
  446. } // while (free)
  447. return true;
  448. }
  449. // DecodeHuffman()
  450. /// <summary>
  451. /// Decodes the adler checksum after the deflate stream.
  452. /// </summary>
  453. /// <returns>False if more input is needed.</returns>
  454. /// <exception cref="CompressionException">If checksum doesn't match.
  455. /// </exception>
  456. private bool DecodeChksum()
  457. {
  458. while (neededBits > 0)
  459. {
  460. int chkByte = input.PeekBits(8);
  461. if (chkByte < 0)
  462. {
  463. return false;
  464. } // if (chkByte)
  465. input.DropBits(8);
  466. readAdler = (readAdler << 8) | chkByte;
  467. neededBits -= 8;
  468. } // while (neededBits)
  469. if ((int)adler.Value != readAdler)
  470. {
  471. throw new CompressionException(
  472. "Adler chksum doesn't match: " + (int)adler.Value +
  473. " vs. " + readAdler);
  474. } // if (int)
  475. mode = DecodeFinished;
  476. return false;
  477. }
  478. // DecodeChksum()
  479. /// <summary>
  480. /// Decodes the deflated stream.
  481. /// </summary>
  482. /// <returns>False if more input is needed, or if finished.</returns>
  483. /// <exception cref="CompressionException">If deflated stream is invalid.
  484. /// </exception>
  485. private bool Decode()
  486. {
  487. switch (mode)
  488. {
  489. case DecodeHeaderState:
  490. return DecodeHeader();
  491. case DecodeDictionaryState:
  492. return DecodeDict();
  493. case DecodeChecksum:
  494. return DecodeChksum();
  495. case DecodeBlocksState:
  496. if (isLastBlock)
  497. {
  498. if (noHeader)
  499. {
  500. mode = DecodeFinished;
  501. return false;
  502. } // if (noHeader)
  503. else
  504. {
  505. input.SkipToByteBoundary();
  506. neededBits = 32;
  507. mode = DecodeChecksum;
  508. return true;
  509. } // else
  510. } // if (isLastBlock)
  511. int type = input.PeekBits(3);
  512. if (type < 0)
  513. {
  514. return false;
  515. } // if (type)
  516. input.DropBits(3);
  517. DecodeLastBlockType(type);
  518. return true;
  519. case DecodeStoredLength1:
  520. {
  521. if ((uncomprLen = input.PeekBits(16)) < 0)
  522. {
  523. return false;
  524. } // if (uncomprLen)
  525. input.DropBits(16);
  526. mode = DecodeStoredLength2;
  527. } // block
  528. // fall through
  529. goto case DecodeStoredLength2;
  530. case DecodeStoredLength2:
  531. {
  532. int nlen = input.PeekBits(16);
  533. if (nlen < 0)
  534. {
  535. return false;
  536. } // if (nlen)
  537. input.DropBits(16);
  538. if (nlen != (uncomprLen ^ 0xffff))
  539. {
  540. throw new CompressionException(
  541. "broken uncompressed block");
  542. } // if (nlen)
  543. mode = DecodeStored;
  544. } // block
  545. // fall through
  546. goto case DecodeStored;
  547. case DecodeStored:
  548. {
  549. int more = outputWindow.CopyStored(input, uncomprLen);
  550. uncomprLen -= more;
  551. if (uncomprLen == 0)
  552. {
  553. mode = DecodeBlocksState;
  554. return true;
  555. } // if (uncomprLen)
  556. return !input.IsNeedingInput;
  557. } // block
  558. case DecodeDynamicHeader:
  559. if (!dynHeader.Decode(input))
  560. {
  561. return false;
  562. } // if ()
  563. litlenTree = dynHeader.BuildLitLenTree();
  564. distTree = dynHeader.BuildDistTree();
  565. mode = DecodeHuffmanState;
  566. // fall through
  567. goto case DecodeHuffmanState;
  568. case DecodeHuffmanState:
  569. case DecodeHuffmanLengthBits:
  570. case DecodeHuffmanDistance:
  571. case DecodeHuffmanDistanceBits:
  572. return DecodeHuffman();
  573. case DecodeFinished:
  574. return false;
  575. default:
  576. throw new CompressionException(
  577. "Inflater.Decode unknown mode");
  578. } // switch
  579. }
  580. // Decode()
  581. /// <summary>
  582. /// Decode last block type
  583. /// </summary>
  584. /// <param name="type">Type</param>
  585. private void DecodeLastBlockType(int type)
  586. {
  587. if ((type & 1) != 0)
  588. {
  589. isLastBlock = true;
  590. } // if (type)
  591. switch (type >> 1)
  592. {
  593. case DeflaterConstants.StoredBlock:
  594. input.SkipToByteBoundary();
  595. mode = DecodeStoredLength1;
  596. break;
  597. case DeflaterConstants.StaticTrees:
  598. litlenTree = InflaterHuffmanTree.defLitLenTree;
  599. distTree = InflaterHuffmanTree.defDistTree;
  600. mode = DecodeHuffmanState;
  601. break;
  602. case DeflaterConstants.DynamicTrees:
  603. dynHeader = new InflaterDynHeader();
  604. mode = DecodeDynamicHeader;
  605. break;
  606. default:
  607. throw new CompressionException(
  608. "Unknown block type " + type);
  609. } // switch
  610. }
  611. // DecodeLastBlockType(type)
  612. /// <summary>
  613. /// Sets the preset dictionary. This should only be called, if
  614. /// needsDictionary() returns true and it should set the same
  615. /// dictionary, that was used for deflating. The getAdler()
  616. /// function returns the checksum of the dictionary needed.
  617. /// </summary>
  618. /// <param name="buffer">
  619. /// The dictionary.
  620. /// </param>
  621. public void SetDictionary(byte[] buffer)
  622. {
  623. SetDictionary(buffer, 0, buffer.Length);
  624. }
  625. // SetDictionary(buffer)
  626. /// <summary>
  627. /// Sets the preset dictionary. This should only be called, if
  628. /// needsDictionary() returns true and it should set the same
  629. /// dictionary, that was used for deflating. The getAdler()
  630. /// function returns the checksum of the dictionary needed.
  631. /// </summary>
  632. /// <param name="buffer">The dictionary.</param>
  633. /// <param name="offset">The offset into buffer where the dictionary
  634. /// starts.</param>
  635. /// <param name="len">The length of the dictionary.</param>
  636. /// <exception cref="System.InvalidOperationException">
  637. /// No dictionary is needed.
  638. /// </exception>
  639. /// <exception cref="CompressionException">
  640. /// The adler checksum for the buffer is invalid
  641. /// </exception>
  642. public void SetDictionary(byte[] buffer, int offset, int len)
  643. {
  644. if (!IsNeedingDictionary)
  645. {
  646. throw new InvalidOperationException();
  647. } // if ()
  648. adler.Update(buffer, offset, len);
  649. if ((int)adler.Value != readAdler)
  650. {
  651. throw new CompressionException("Wrong adler checksum");
  652. } // if (int)
  653. adler.Reset();
  654. outputWindow.CopyDict(buffer, offset, len);
  655. mode = DecodeBlocksState;
  656. }
  657. // SetDictionary(buffer, offset, len)
  658. /// <summary>
  659. /// Sets the input. This should only be called, if needsInput()
  660. /// returns true.
  661. /// </summary>
  662. /// <param name="buffer">The input buffer.</param>
  663. public void SetInput(byte[] buffer)
  664. {
  665. SetInput(buffer, 0, buffer.Length);
  666. }
  667. // SetInput(buffer)
  668. /// <summary>
  669. /// Sets the input. This should only be called, if needsInput()
  670. /// returns true.
  671. /// </summary>
  672. /// <param name="buffer">
  673. /// The source of input data
  674. /// </param>
  675. /// <param name="offset">
  676. /// The offset into buffer where the input starts.
  677. /// </param>
  678. /// <param name="length">
  679. /// The number of bytes of input to use.
  680. /// </param>
  681. /// <exception cref="System.InvalidOperationException">
  682. /// No input is needed.
  683. /// </exception>
  684. /// <exception cref="System.ArgumentOutOfRangeException">
  685. /// The off and/or len are wrong.
  686. /// </exception>
  687. public void SetInput(byte[] buffer, int offset, int length)
  688. {
  689. input.SetInput(buffer, offset, length);
  690. totalIn += length;
  691. }
  692. // SetInput(buffer, offset, length)
  693. /// <summary>
  694. /// Inflates the compressed stream to the output buffer. If this
  695. /// returns 0, you should check, whether needsDictionary(),
  696. /// needsInput() or finished() returns true, to determine why no
  697. /// further output is produced.
  698. /// </summary>
  699. /// <param name="buffer">The output buffer.</param>
  700. /// <returns>
  701. /// the number of bytes written to the buffer, 0 if no further
  702. /// output can be produced.
  703. /// </returns>
  704. /// <exception cref="System.ArgumentOutOfRangeException">
  705. /// if buf has length 0.
  706. /// </exception>
  707. /// <exception cref="System.FormatException">
  708. /// if deflated stream is invalid.
  709. /// </exception>
  710. public int Inflate(byte[] buffer)
  711. {
  712. return Inflate(buffer, 0, buffer.Length);
  713. }
  714. // Inflate(buffer)
  715. /// <summary>
  716. /// Inflates the compressed stream to the output buffer. If this
  717. /// returns 0, you should check, whether needsDictionary(),
  718. /// needsInput() or finished() returns true, to determine why no
  719. /// further output is produced.
  720. /// </summary>
  721. /// <param name="buffer">The output buffer.</param>
  722. /// <param name="offset">The offset into buffer where the output should
  723. /// start.</param>
  724. /// <param name="len">The maximum length of the output.</param>
  725. /// <returns>The number of bytes written to the buffer, 0 if no further
  726. /// output can be produced.</returns>
  727. /// <exception cref="System.ArgumentOutOfRangeException">
  728. /// if len is &lt;= 0.
  729. /// </exception>
  730. /// <exception cref="System.ArgumentOutOfRangeException">
  731. /// if the offset and/or len are wrong.
  732. /// </exception>
  733. /// <exception cref="System.FormatException">
  734. /// if deflated stream is invalid.
  735. /// </exception>
  736. public int Inflate(byte[] buffer, int offset, int len)
  737. {
  738. if (len < 0)
  739. {
  740. throw new ArgumentOutOfRangeException("len < 0");
  741. } // if (len)
  742. // Special case: len may be zero
  743. if (len == 0)
  744. {
  745. if (IsFinished == false)
  746. {
  747. // -jr- 08-Nov-2003 INFLATE_BUG fix..
  748. Decode();
  749. } // if (IsFinished)
  750. return 0;
  751. } // if (len)
  752. /*
  753. // Check for correct buff, off, len triple
  754. if (off < 0 || off + len >= buf.Length) {
  755. throw new ArgumentException("off/len outside buf bounds");
  756. }
  757. */
  758. int count = 0;
  759. int more;
  760. do
  761. {
  762. if (mode != DecodeChecksum)
  763. {
  764. // Don't give away any output, if we are waiting for the
  765. // checksum in the input stream.
  766. //
  767. // With this trick we have always:
  768. // needsInput() and not finished()
  769. // implies more output can be produced.
  770. more = outputWindow.CopyOutput(buffer, offset, len);
  771. adler.Update(buffer, offset, more);
  772. offset += more;
  773. count += more;
  774. totalOut += more;
  775. len -= more;
  776. if (len == 0)
  777. {
  778. return count;
  779. } // if (len)
  780. } // if (mode)
  781. } while (Decode() ||
  782. (outputWindow.GetAvailable() > 0 &&
  783. mode != DecodeChecksum));
  784. return count;
  785. }
  786. // Inflate(buf, offset, len)
  787. #endregion
  788. }
  789. }