PageRenderTime 26ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Compression/Deflaters/Deflater.cs

#
C# | 655 lines | 283 code | 57 blank | 315 comment | 46 complexity | 2b5ce61d6bec7fd66112f0c1b20b87aa 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.Helpers;
  6. namespace Delta.Utilities.Compression.Deflaters
  7. {
  8. /// <summary>
  9. /// This is the Deflater class. The deflater class compresses input
  10. /// with the deflate algorithm described in RFC 1951. It has several
  11. /// compression levels and three different strategies described below.
  12. /// <para />
  13. /// This class is <i>not</i> thread safe. This is inherent in the API, due
  14. /// to the split of deflate and setInput.
  15. /// <para />
  16. /// author of the original java version : Jochen Hoenicke
  17. /// </summary>
  18. public class Deflater
  19. {
  20. #region Constants
  21. /// <summary>
  22. /// The best and slowest compression level.
  23. /// This tries to find very long and distant string repetitions.
  24. /// </summary>
  25. public const int BestCompression = 9;
  26. /// <summary>
  27. /// The worst but fastest compression level.
  28. /// </summary>
  29. public const int BestSpeed = 1;
  30. /// <summary>
  31. /// The default compression level.
  32. /// </summary>
  33. public const int DefaultCompression = MathHelper.InvalidIndex;
  34. /// <summary>
  35. /// This level won't compress at all but output uncompressed blocks.
  36. /// </summary>
  37. public const int NoCompression = 0;
  38. /// <summary>
  39. /// The compression method. This is the only method supported so far.
  40. /// There is no need to use this constant at all.
  41. /// </summary>
  42. public const int Deflated = 8;
  43. /*
  44. * The Deflater can do the following state transitions:
  45. * (1) -> InitializeState ----> InitializeFinishingState ---.
  46. * / | (2) (5) |
  47. * / v (5) |
  48. * (3)| SetDictionaryState ---> SetDictionaryFinishingState |(3)
  49. * \ | (3) | ,-------'
  50. * | | | (3) /
  51. * v v (5) v v
  52. * (1) -> BusyState ----> FinishingState
  53. * | (6)
  54. * v
  55. * FinishedState
  56. * \_____________________________________/
  57. * | (7)
  58. * v
  59. * ClosedState
  60. * (1) If we should produce a header we start in InitializeState, otherwise
  61. * we start in BusyState.
  62. * (2) A dictionary may be set only when we are in InitializeState, then
  63. * we change the state as indicated.
  64. * (3) Whether a dictionary is set or not, on the first call of deflate
  65. * we change to BusyState.
  66. * (4) -- intentionally left blank -- :)
  67. * (5) FinishingState is entered, when flush() is called to indicate that
  68. * there is no more INPUT. There are also states indicating, that
  69. * the header wasn't written yet.
  70. * (6) FinishedState is entered, when everything has been flushed to the
  71. * internal pending output buffer.
  72. * (7) At any time (7)
  73. */
  74. /// <summary>
  75. /// Is set dictionary
  76. /// </summary>
  77. /// <returns>0x01</returns>
  78. private const int IsSetDictionary = 0x01;
  79. /// <summary>
  80. /// Is flushing
  81. /// </summary>
  82. /// <returns>0x04</returns>
  83. private const int IsFlushing = 0x04;
  84. /// <summary>
  85. /// Is finishing
  86. /// </summary>
  87. /// <returns>0x08</returns>
  88. private const int IsFinishing = 0x08;
  89. /// <summary>
  90. /// Initialize state
  91. /// </summary>
  92. /// <returns>0x00</returns>
  93. private const int InitializeState = 0x00;
  94. /// <summary>
  95. /// Set dictionary state
  96. /// </summary>
  97. /// <returns>0x01</returns>
  98. private const int SetDictionaryState = 0x01;
  99. /*unused
  100. /// <summary>
  101. /// Initialize finishing state
  102. /// </summary>
  103. /// <returns>0x08</returns>
  104. //private static int InitializeFinishingState = 0x08;
  105. /// <summary>
  106. /// Set dictionary finishing state
  107. /// </summary>
  108. /// <returns>0x09</returns>
  109. //private static int SetDictionaryFinishingState = 0x09;
  110. */
  111. /// <summary>
  112. /// Busy state
  113. /// </summary>
  114. /// <returns>0x10</returns>
  115. private const int BusyState = 0x10;
  116. /// <summary>
  117. /// Flushing state
  118. /// </summary>
  119. /// <returns>0x14</returns>
  120. private const int FlushingState = 0x14;
  121. /// <summary>
  122. /// Finishing state
  123. /// </summary>
  124. /// <returns>0x1c</returns>
  125. private const int FinishingState = 0x1c;
  126. /// <summary>
  127. /// Finished state
  128. /// </summary>
  129. /// <returns>0x 1e</returns>
  130. private const int FinishedState = 0x1e;
  131. /// <summary>
  132. /// Closed state
  133. /// </summary>
  134. /// <returns>0x7f</returns>
  135. private const int ClosedState = 0x7f;
  136. #endregion
  137. #region Adler (Public)
  138. /// <summary>
  139. /// Gets the current adler checksum of the data that was processed so far.
  140. /// </summary>
  141. public int Adler
  142. {
  143. get
  144. {
  145. return engine.Adler;
  146. }
  147. }
  148. #endregion
  149. #region TotalIn (Public)
  150. /// <summary>
  151. /// Gets the number of input bytes processed so far.
  152. /// </summary>
  153. public int TotalIn
  154. {
  155. get
  156. {
  157. return engine.TotalIn;
  158. }
  159. }
  160. #endregion
  161. #region TotalOut (Public)
  162. /// <summary>
  163. /// Gets the number of output bytes so far.
  164. /// </summary>
  165. public long TotalOut
  166. {
  167. get
  168. {
  169. return totalOut;
  170. }
  171. }
  172. #endregion
  173. #region IsFinished (Public)
  174. /// <summary>
  175. /// Returns true if the stream was finished and no more output bytes
  176. /// are available.
  177. /// </summary>
  178. public bool IsFinished
  179. {
  180. get
  181. {
  182. return state == FinishedState && pending.IsFlushed;
  183. }
  184. }
  185. #endregion
  186. #region IsNeedingInput (Public)
  187. /// <summary>
  188. /// Returns true, if the input buffer is empty.
  189. /// You should then call setInput().
  190. /// NOTE: This method can also return true when the stream
  191. /// was finished.
  192. /// </summary>
  193. public bool IsNeedingInput
  194. {
  195. get
  196. {
  197. return engine.NeedsInput();
  198. }
  199. }
  200. #endregion
  201. #region Private
  202. #region level (Private)
  203. /// <summary>
  204. /// Compression level.
  205. /// </summary>
  206. private int level;
  207. #endregion
  208. #region noZlibHeaderOrFooter (Private)
  209. /// <summary>
  210. /// If true no Zlib/RFC1950 headers or footers are generated
  211. /// </summary>
  212. private readonly bool noZlibHeaderOrFooter;
  213. #endregion
  214. #region state (Private)
  215. /// <summary>
  216. /// The current state.
  217. /// </summary>
  218. private int state;
  219. #endregion
  220. #region totalOut (Private)
  221. /// <summary>
  222. /// The total bytes of output written.
  223. /// </summary>
  224. private long totalOut;
  225. #endregion
  226. #region pending (Private)
  227. /// <summary>
  228. /// The pending output.
  229. /// </summary>
  230. private readonly DeflaterPending pending;
  231. #endregion
  232. #region engine (Private)
  233. /// <summary>
  234. /// The deflater engine.
  235. /// </summary>
  236. private readonly DeflaterEngine engine;
  237. #endregion
  238. #endregion
  239. #region Constructors
  240. /// <summary>
  241. /// Creates a new deflater with default compression level.
  242. /// </summary>
  243. public Deflater()
  244. : this(DefaultCompression, false)
  245. {
  246. }
  247. /// <summary>
  248. /// Creates a new deflater with given compression level.
  249. /// </summary>
  250. /// <param name="level">The compression level, a value between
  251. /// NoCompression and BestCompression, or DefaultCompression.
  252. /// </param>
  253. /// <exception cref="System.ArgumentOutOfRangeException">
  254. /// If level is out of range.</exception>
  255. public Deflater(int level)
  256. : this(level, false)
  257. {
  258. }
  259. /// <summary>
  260. /// Creates a new deflater with given compression level.
  261. /// </summary>
  262. /// <param name="level">
  263. /// the compression level, a value between NoCompression
  264. /// and BestCompression.
  265. /// </param>
  266. /// <param name="noZlibHeaderOrFooter">
  267. /// true, if we should suppress the Zlib/RFC1950 header at the
  268. /// beginning and the adler checksum at the end of the output.
  269. /// This is useful for the GZIP/PKZIP formats.
  270. /// </param>
  271. /// <exception cref="System.ArgumentOutOfRangeException">
  272. /// If level is out of range.</exception>
  273. public Deflater(int level, bool noZlibHeaderOrFooter)
  274. {
  275. if (level == DefaultCompression)
  276. {
  277. level = 6;
  278. }
  279. else if (level < NoCompression ||
  280. level > BestCompression)
  281. {
  282. throw new ArgumentOutOfRangeException("level");
  283. }
  284. pending = new DeflaterPending();
  285. engine = new DeflaterEngine(pending);
  286. this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
  287. SetStrategy(DeflateStrategy.Default);
  288. SetLevel(level);
  289. Reset();
  290. }
  291. #endregion
  292. #region Reset (Public)
  293. /// <summary>
  294. /// Resets the deflater. The deflater acts afterwards as if it was
  295. /// just created with the same compression level and strategy as it
  296. /// had before.
  297. /// </summary>
  298. public void Reset()
  299. {
  300. state = (noZlibHeaderOrFooter
  301. ? BusyState
  302. : InitializeState);
  303. totalOut = 0;
  304. pending.Reset();
  305. engine.Reset();
  306. }
  307. #endregion
  308. #region Flush (Public)
  309. /// <summary>
  310. /// Flushes the current input block. Further calls to deflate() will
  311. /// produce enough output to inflate everything in the current input
  312. /// block. This is not part of Sun's JDK so I have made it package
  313. /// private. It is used by DeflaterOutputStream to implement
  314. /// flush().
  315. /// </summary>
  316. public void Flush()
  317. {
  318. state |= IsFlushing;
  319. }
  320. #endregion
  321. #region Finish (Public)
  322. /// <summary>
  323. /// Finishes the deflater with the current input block. It is an error
  324. /// to give more input after this method was called. This method must
  325. /// be called to force all bytes to be flushed.
  326. /// </summary>
  327. public void Finish()
  328. {
  329. state |= IsFlushing | IsFinishing;
  330. }
  331. #endregion
  332. #region SetInput (Public)
  333. /// <summary>
  334. /// Sets the data which should be compressed next. This should be only
  335. /// called when needsInput indicates that more input is needed.
  336. /// If you call setInput when needsInput() returns false, the
  337. /// previous input that is still pending will be thrown away.
  338. /// The given byte array should not be changed, before needsInput()
  339. /// returns true again.
  340. /// This call is equivalent to <code>setInput(input, 0, input.length)
  341. /// </code>.
  342. /// </summary>
  343. /// <param name="input">
  344. /// the buffer containing the input data.
  345. /// </param>
  346. /// <exception cref="System.InvalidOperationException">
  347. /// if the buffer was finished() or ended().
  348. /// </exception>
  349. public void SetInput(byte[] input)
  350. {
  351. SetInput(input, 0, input.Length);
  352. }
  353. /// <summary>
  354. /// Sets the data which should be compressed next. This should be
  355. /// only called when needsInput indicates that more input is needed.
  356. /// The given byte array should not be changed, before needsInput() returns
  357. /// true again.
  358. /// </summary>
  359. /// <param name="input">The buffer containing the input data.</param>
  360. /// <param name="off">The start of the data.</param>
  361. /// <param name="len">The length of the data.</param>
  362. /// <exception cref="System.InvalidOperationException">
  363. /// if the buffer was finished() or ended() or if previous input is still
  364. /// pending.
  365. /// </exception>
  366. public void SetInput(byte[] input, int off, int len)
  367. {
  368. if ((state & IsFinishing) != 0)
  369. {
  370. throw new InvalidOperationException("finish()/end() already called");
  371. }
  372. engine.SetInput(input, off, len);
  373. }
  374. #endregion
  375. #region SetLevel (Public)
  376. /// <summary>
  377. /// Sets the compression level. There is no guarantee of the exact
  378. /// position of the change, but if you call this when needsInput is
  379. /// true the change of compression level will occur somewhere near
  380. /// before the end of the so far given input.
  381. /// </summary>
  382. /// <param name="lvl">
  383. /// the new compression level.
  384. /// </param>
  385. public void SetLevel(int lvl)
  386. {
  387. if (lvl == DefaultCompression)
  388. {
  389. lvl = 6;
  390. }
  391. else if (lvl < NoCompression || lvl > BestCompression)
  392. {
  393. throw new ArgumentOutOfRangeException("lvl");
  394. }
  395. if (level != lvl)
  396. {
  397. level = lvl;
  398. engine.SetLevel(lvl);
  399. }
  400. }
  401. #endregion
  402. #region GetLevel (Public)
  403. /// <summary>
  404. /// Get current compression level
  405. /// </summary>
  406. /// <returns>Returns the current compression level</returns>
  407. public int GetLevel()
  408. {
  409. return level;
  410. }
  411. #endregion
  412. #region SetStrategy (Public)
  413. /// <summary>
  414. /// Sets the compression strategy. Strategy is one of
  415. /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
  416. /// position where the strategy is changed, the same as for
  417. /// setLevel() applies.
  418. /// </summary>
  419. /// <param name="strategy">
  420. /// The new compression strategy.
  421. /// </param>
  422. public void SetStrategy(DeflateStrategy strategy)
  423. {
  424. engine.Strategy = strategy;
  425. }
  426. #endregion
  427. #region Deflate (Public)
  428. /// <summary>
  429. /// Deflates the current input block with to the given array.
  430. /// </summary>
  431. /// <param name="output">
  432. /// The buffer where compressed data is stored
  433. /// </param>
  434. /// <returns>
  435. /// The number of compressed bytes added to the output, or 0 if either
  436. /// needsInput() or finished() returns true or length is zero.
  437. /// </returns>
  438. public int Deflate(byte[] output)
  439. {
  440. return Deflate(output, 0, output.Length);
  441. }
  442. /// <summary>
  443. /// Deflates the current input block to the given array.
  444. /// </summary>
  445. /// <param name="output">Buffer to store the compressed data.</param>
  446. /// <param name="offset">Offset into the output array.</param>
  447. /// <param name="length">The maximum number of bytes that may be stored.
  448. /// </param>
  449. /// <returns>
  450. /// The number of compressed bytes added to the output, or 0 if either
  451. /// needsInput() or finished() returns true or length is zero.
  452. /// </returns>
  453. /// <exception cref="System.InvalidOperationException">
  454. /// If end() was previously called.
  455. /// </exception>
  456. /// <exception cref="System.ArgumentOutOfRangeException">
  457. /// If offset and/or length don't match the array length.
  458. /// </exception>
  459. public int Deflate(byte[] output, int offset, int length)
  460. {
  461. int origLength = length;
  462. if (state == ClosedState)
  463. {
  464. throw new InvalidOperationException("Deflater closed");
  465. }
  466. if (state < BusyState)
  467. {
  468. /* output header */
  469. int header = (Deflated +
  470. ((DeflaterConstants.MaxWBits - 8) << 4)) << 8;
  471. int level_flags = (level - 1) >> 1;
  472. if (level_flags < 0 || level_flags > 3)
  473. {
  474. level_flags = 3;
  475. }
  476. header |= level_flags << 6;
  477. if ((state & IsSetDictionary) != 0)
  478. {
  479. // Dictionary was set
  480. header |= DeflaterConstants.PresetDictionary;
  481. }
  482. header += 31 - (header % 31);
  483. pending.WriteShortMsb(header);
  484. if ((state & IsSetDictionary) != 0)
  485. {
  486. int chksum = engine.Adler;
  487. engine.ResetAdler();
  488. pending.WriteShortMsb(chksum >> 16);
  489. pending.WriteShortMsb(chksum & 0xffff);
  490. }
  491. state = BusyState | (state & (IsFlushing | IsFinishing));
  492. }
  493. for (;;)
  494. {
  495. int count = pending.Flush(output, offset, length);
  496. offset += count;
  497. totalOut += count;
  498. length -= count;
  499. if (length == 0 || state == FinishedState)
  500. {
  501. break;
  502. }
  503. if (!engine.Deflate((state & IsFlushing) != 0,
  504. (state & IsFinishing) != 0))
  505. {
  506. if (state == BusyState)
  507. {
  508. /* We need more input now */
  509. return origLength - length;
  510. }
  511. else if (state == FlushingState)
  512. {
  513. if (level != NoCompression)
  514. {
  515. // We have to supply some lookahead. 8 bit lookahead
  516. // is needed by the zlib inflater, and we must fill
  517. // the next byte, so that all bits are flushed.
  518. int neededbits = 8 + ((-pending.BitCount) & 7);
  519. while (neededbits > 0)
  520. {
  521. /* write a static tree block consisting solely of
  522. * an EOF:
  523. */
  524. pending.WriteBits(2, 10);
  525. neededbits -= 10;
  526. }
  527. }
  528. state = BusyState;
  529. }
  530. else if (state == FinishingState)
  531. {
  532. pending.AlignToByte();
  533. // Compressed data is complete.
  534. // Write footer information if required.
  535. if (!noZlibHeaderOrFooter)
  536. {
  537. int adler = engine.Adler;
  538. pending.WriteShortMsb(adler >> 16);
  539. pending.WriteShortMsb(adler & 0xffff);
  540. }
  541. state = FinishedState;
  542. }
  543. }
  544. }
  545. return origLength - length;
  546. }
  547. #endregion
  548. #region SetDictionary (Public)
  549. /// <summary>
  550. /// Sets the dictionary which should be used in the deflate process.
  551. /// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)
  552. /// </code>.
  553. /// </summary>
  554. /// <param name="dict">
  555. /// the dictionary.
  556. /// </param>
  557. /// <exception cref="System.InvalidOperationException">
  558. /// if setInput () or deflate () were already called or another dictionary
  559. /// was already set.
  560. /// </exception>
  561. public void SetDictionary(byte[] dict)
  562. {
  563. SetDictionary(dict, 0, dict.Length);
  564. }
  565. /// <summary>
  566. /// Sets the dictionary which should be used in the deflate process.
  567. /// The dictionary is a byte array containing strings that are
  568. /// likely to occur in the data which should be compressed. The
  569. /// dictionary is not stored in the compressed output, only a
  570. /// checksum. To decompress the output you need to supply the same
  571. /// dictionary again.
  572. /// </summary>
  573. /// <param name="dict">
  574. /// The dictionary data
  575. /// </param>
  576. /// <param name="offset">
  577. /// An offset into the dictionary.
  578. /// </param>
  579. /// <param name="length">
  580. /// The length of the dictionary data to use
  581. /// </param>
  582. /// <exception cref="System.InvalidOperationException">
  583. /// If setInput () or deflate () were already called or another
  584. /// dictionary was already set.
  585. /// </exception>
  586. public void SetDictionary(byte[] dict, int offset, int length)
  587. {
  588. if (state != InitializeState)
  589. {
  590. throw new InvalidOperationException();
  591. }
  592. state = SetDictionaryState;
  593. engine.SetDictionary(dict, offset, length);
  594. }
  595. #endregion
  596. }
  597. }