DeflaterEngine.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. using ICSharpCode.SharpZipLib.Checksum;
  2. using System;
  3. namespace ICSharpCode.SharpZipLib.Zip.Compression
  4. {
  5. /// <summary>
  6. /// Strategies for deflater
  7. /// </summary>
  8. public enum DeflateStrategy
  9. {
  10. /// <summary>
  11. /// The default strategy
  12. /// </summary>
  13. Default = 0,
  14. /// <summary>
  15. /// This strategy will only allow longer string repetitions. It is
  16. /// useful for random data with a small character set.
  17. /// </summary>
  18. Filtered = 1,
  19. /// <summary>
  20. /// This strategy will not look for string repetitions at all. It
  21. /// only encodes with Huffman trees (which means, that more common
  22. /// characters get a smaller encoding.
  23. /// </summary>
  24. HuffmanOnly = 2
  25. }
  26. // DEFLATE ALGORITHM:
  27. //
  28. // The uncompressed stream is inserted into the window array. When
  29. // the window array is full the first half is thrown away and the
  30. // second half is copied to the beginning.
  31. //
  32. // The head array is a hash table. Three characters build a hash value
  33. // and they the value points to the corresponding index in window of
  34. // the last string with this hash. The prev array implements a
  35. // linked list of matches with the same hash: prev[index & WMASK] points
  36. // to the previous index with the same hash.
  37. //
  38. /// <summary>
  39. /// Low level compression engine for deflate algorithm which uses a 32K sliding window
  40. /// with secondary compression from Huffman/Shannon-Fano codes.
  41. /// </summary>
  42. public class DeflaterEngine
  43. {
  44. #region Constants
  45. private const int TooFar = 4096;
  46. #endregion Constants
  47. #region Constructors
  48. /// <summary>
  49. /// Construct instance with pending buffer
  50. /// Adler calculation will be peformed
  51. /// </summary>
  52. /// <param name="pending">
  53. /// Pending buffer to use
  54. /// </param>
  55. public DeflaterEngine(DeflaterPending pending)
  56. : this (pending, false)
  57. {
  58. }
  59. /// <summary>
  60. /// Construct instance with pending buffer
  61. /// </summary>
  62. /// <param name="pending">
  63. /// Pending buffer to use
  64. /// </param>
  65. /// <param name="noAdlerCalculation">
  66. /// If no adler calculation should be performed
  67. /// </param>
  68. public DeflaterEngine(DeflaterPending pending, bool noAdlerCalculation)
  69. {
  70. this.pending = pending;
  71. huffman = new DeflaterHuffman(pending);
  72. if (!noAdlerCalculation)
  73. adler = new Adler32();
  74. window = new byte[2 * DeflaterConstants.WSIZE];
  75. head = new short[DeflaterConstants.HASH_SIZE];
  76. prev = new short[DeflaterConstants.WSIZE];
  77. // We start at index 1, to avoid an implementation deficiency, that
  78. // we cannot build a repeat pattern at index 0.
  79. blockStart = strstart = 1;
  80. }
  81. #endregion Constructors
  82. /// <summary>
  83. /// Deflate drives actual compression of data
  84. /// </summary>
  85. /// <param name="flush">True to flush input buffers</param>
  86. /// <param name="finish">Finish deflation with the current input.</param>
  87. /// <returns>Returns true if progress has been made.</returns>
  88. public bool Deflate(bool flush, bool finish)
  89. {
  90. bool progress;
  91. do
  92. {
  93. FillWindow();
  94. bool canFlush = flush && (inputOff == inputEnd);
  95. #if DebugDeflation
  96. if (DeflaterConstants.DEBUGGING) {
  97. Console.WriteLine("window: [" + blockStart + "," + strstart + ","
  98. + lookahead + "], " + compressionFunction + "," + canFlush);
  99. }
  100. #endif
  101. switch (compressionFunction)
  102. {
  103. case DeflaterConstants.DEFLATE_STORED:
  104. progress = DeflateStored(canFlush, finish);
  105. break;
  106. case DeflaterConstants.DEFLATE_FAST:
  107. progress = DeflateFast(canFlush, finish);
  108. break;
  109. case DeflaterConstants.DEFLATE_SLOW:
  110. progress = DeflateSlow(canFlush, finish);
  111. break;
  112. default:
  113. throw new InvalidOperationException("unknown compressionFunction");
  114. }
  115. } while (pending.IsFlushed && progress); // repeat while we have no pending output and progress was made
  116. return progress;
  117. }
  118. /// <summary>
  119. /// Sets input data to be deflated. Should only be called when <code>NeedsInput()</code>
  120. /// returns true
  121. /// </summary>
  122. /// <param name="buffer">The buffer containing input data.</param>
  123. /// <param name="offset">The offset of the first byte of data.</param>
  124. /// <param name="count">The number of bytes of data to use as input.</param>
  125. public void SetInput(byte[] buffer, int offset, int count)
  126. {
  127. if (buffer == null)
  128. {
  129. throw new ArgumentNullException(nameof(buffer));
  130. }
  131. if (offset < 0)
  132. {
  133. throw new ArgumentOutOfRangeException(nameof(offset));
  134. }
  135. if (count < 0)
  136. {
  137. throw new ArgumentOutOfRangeException(nameof(count));
  138. }
  139. if (inputOff < inputEnd)
  140. {
  141. throw new InvalidOperationException("Old input was not completely processed");
  142. }
  143. int end = offset + count;
  144. /* We want to throw an ArrayIndexOutOfBoundsException early. The
  145. * check is very tricky: it also handles integer wrap around.
  146. */
  147. if ((offset > end) || (end > buffer.Length))
  148. {
  149. throw new ArgumentOutOfRangeException(nameof(count));
  150. }
  151. inputBuf = buffer;
  152. inputOff = offset;
  153. inputEnd = end;
  154. }
  155. /// <summary>
  156. /// Determines if more <see cref="SetInput">input</see> is needed.
  157. /// </summary>
  158. /// <returns>Return true if input is needed via <see cref="SetInput">SetInput</see></returns>
  159. public bool NeedsInput()
  160. {
  161. return (inputEnd == inputOff);
  162. }
  163. /// <summary>
  164. /// Set compression dictionary
  165. /// </summary>
  166. /// <param name="buffer">The buffer containing the dictionary data</param>
  167. /// <param name="offset">The offset in the buffer for the first byte of data</param>
  168. /// <param name="length">The length of the dictionary data.</param>
  169. public void SetDictionary(byte[] buffer, int offset, int length)
  170. {
  171. #if DebugDeflation
  172. if (DeflaterConstants.DEBUGGING && (strstart != 1) )
  173. {
  174. throw new InvalidOperationException("strstart not 1");
  175. }
  176. #endif
  177. adler?.Update(new ArraySegment<byte>(buffer, offset, length));
  178. if (length < DeflaterConstants.MIN_MATCH)
  179. {
  180. return;
  181. }
  182. if (length > DeflaterConstants.MAX_DIST)
  183. {
  184. offset += length - DeflaterConstants.MAX_DIST;
  185. length = DeflaterConstants.MAX_DIST;
  186. }
  187. System.Array.Copy(buffer, offset, window, strstart, length);
  188. UpdateHash();
  189. --length;
  190. while (--length > 0)
  191. {
  192. InsertString();
  193. strstart++;
  194. }
  195. strstart += 2;
  196. blockStart = strstart;
  197. }
  198. /// <summary>
  199. /// Reset internal state
  200. /// </summary>
  201. public void Reset()
  202. {
  203. huffman.Reset();
  204. adler?.Reset();
  205. blockStart = strstart = 1;
  206. lookahead = 0;
  207. totalIn = 0;
  208. prevAvailable = false;
  209. matchLen = DeflaterConstants.MIN_MATCH - 1;
  210. for (int i = 0; i < DeflaterConstants.HASH_SIZE; i++)
  211. {
  212. head[i] = 0;
  213. }
  214. for (int i = 0; i < DeflaterConstants.WSIZE; i++)
  215. {
  216. prev[i] = 0;
  217. }
  218. }
  219. /// <summary>
  220. /// Reset Adler checksum
  221. /// </summary>
  222. public void ResetAdler()
  223. {
  224. adler?.Reset();
  225. }
  226. /// <summary>
  227. /// Get current value of Adler checksum
  228. /// </summary>
  229. public int Adler
  230. {
  231. get
  232. {
  233. return (adler != null) ? unchecked((int)adler.Value) : 0;
  234. }
  235. }
  236. /// <summary>
  237. /// Total data processed
  238. /// </summary>
  239. public long TotalIn
  240. {
  241. get
  242. {
  243. return totalIn;
  244. }
  245. }
  246. /// <summary>
  247. /// Get/set the <see cref="DeflateStrategy">deflate strategy</see>
  248. /// </summary>
  249. public DeflateStrategy Strategy
  250. {
  251. get
  252. {
  253. return strategy;
  254. }
  255. set
  256. {
  257. strategy = value;
  258. }
  259. }
  260. /// <summary>
  261. /// Set the deflate level (0-9)
  262. /// </summary>
  263. /// <param name="level">The value to set the level to.</param>
  264. public void SetLevel(int level)
  265. {
  266. if ((level < 0) || (level > 9))
  267. {
  268. throw new ArgumentOutOfRangeException(nameof(level));
  269. }
  270. goodLength = DeflaterConstants.GOOD_LENGTH[level];
  271. max_lazy = DeflaterConstants.MAX_LAZY[level];
  272. niceLength = DeflaterConstants.NICE_LENGTH[level];
  273. max_chain = DeflaterConstants.MAX_CHAIN[level];
  274. if (DeflaterConstants.COMPR_FUNC[level] != compressionFunction)
  275. {
  276. #if DebugDeflation
  277. if (DeflaterConstants.DEBUGGING) {
  278. Console.WriteLine("Change from " + compressionFunction + " to "
  279. + DeflaterConstants.COMPR_FUNC[level]);
  280. }
  281. #endif
  282. switch (compressionFunction)
  283. {
  284. case DeflaterConstants.DEFLATE_STORED:
  285. if (strstart > blockStart)
  286. {
  287. huffman.FlushStoredBlock(window, blockStart,
  288. strstart - blockStart, false);
  289. blockStart = strstart;
  290. }
  291. UpdateHash();
  292. break;
  293. case DeflaterConstants.DEFLATE_FAST:
  294. if (strstart > blockStart)
  295. {
  296. huffman.FlushBlock(window, blockStart, strstart - blockStart,
  297. false);
  298. blockStart = strstart;
  299. }
  300. break;
  301. case DeflaterConstants.DEFLATE_SLOW:
  302. if (prevAvailable)
  303. {
  304. huffman.TallyLit(window[strstart - 1] & 0xff);
  305. }
  306. if (strstart > blockStart)
  307. {
  308. huffman.FlushBlock(window, blockStart, strstart - blockStart, false);
  309. blockStart = strstart;
  310. }
  311. prevAvailable = false;
  312. matchLen = DeflaterConstants.MIN_MATCH - 1;
  313. break;
  314. }
  315. compressionFunction = DeflaterConstants.COMPR_FUNC[level];
  316. }
  317. }
  318. /// <summary>
  319. /// Fill the window
  320. /// </summary>
  321. public void FillWindow()
  322. {
  323. /* If the window is almost full and there is insufficient lookahead,
  324. * move the upper half to the lower one to make room in the upper half.
  325. */
  326. if (strstart >= DeflaterConstants.WSIZE + DeflaterConstants.MAX_DIST)
  327. {
  328. SlideWindow();
  329. }
  330. /* If there is not enough lookahead, but still some input left,
  331. * read in the input
  332. */
  333. if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd)
  334. {
  335. int more = 2 * DeflaterConstants.WSIZE - lookahead - strstart;
  336. if (more > inputEnd - inputOff)
  337. {
  338. more = inputEnd - inputOff;
  339. }
  340. System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more);
  341. adler?.Update(new ArraySegment<byte>(inputBuf, inputOff, more));
  342. inputOff += more;
  343. totalIn += more;
  344. lookahead += more;
  345. }
  346. if (lookahead >= DeflaterConstants.MIN_MATCH)
  347. {
  348. UpdateHash();
  349. }
  350. }
  351. private void UpdateHash()
  352. {
  353. /*
  354. if (DEBUGGING) {
  355. Console.WriteLine("updateHash: "+strstart);
  356. }
  357. */
  358. ins_h = (window[strstart] << DeflaterConstants.HASH_SHIFT) ^ window[strstart + 1];
  359. }
  360. /// <summary>
  361. /// Inserts the current string in the head hash and returns the previous
  362. /// value for this hash.
  363. /// </summary>
  364. /// <returns>The previous hash value</returns>
  365. private int InsertString()
  366. {
  367. short match;
  368. int hash = ((ins_h << DeflaterConstants.HASH_SHIFT) ^ window[strstart + (DeflaterConstants.MIN_MATCH - 1)]) & DeflaterConstants.HASH_MASK;
  369. #if DebugDeflation
  370. if (DeflaterConstants.DEBUGGING)
  371. {
  372. if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^
  373. (window[strstart + 1] << HASH_SHIFT) ^
  374. (window[strstart + 2])) & HASH_MASK)) {
  375. throw new SharpZipBaseException("hash inconsistent: " + hash + "/"
  376. +window[strstart] + ","
  377. +window[strstart + 1] + ","
  378. +window[strstart + 2] + "," + HASH_SHIFT);
  379. }
  380. }
  381. #endif
  382. prev[strstart & DeflaterConstants.WMASK] = match = head[hash];
  383. head[hash] = unchecked((short)strstart);
  384. ins_h = hash;
  385. return match & 0xffff;
  386. }
  387. private void SlideWindow()
  388. {
  389. Array.Copy(window, DeflaterConstants.WSIZE, window, 0, DeflaterConstants.WSIZE);
  390. matchStart -= DeflaterConstants.WSIZE;
  391. strstart -= DeflaterConstants.WSIZE;
  392. blockStart -= DeflaterConstants.WSIZE;
  393. // Slide the hash table (could be avoided with 32 bit values
  394. // at the expense of memory usage).
  395. for (int i = 0; i < DeflaterConstants.HASH_SIZE; ++i)
  396. {
  397. int m = head[i] & 0xffff;
  398. head[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0);
  399. }
  400. // Slide the prev table.
  401. for (int i = 0; i < DeflaterConstants.WSIZE; i++)
  402. {
  403. int m = prev[i] & 0xffff;
  404. prev[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0);
  405. }
  406. }
  407. /// <summary>
  408. /// Find the best (longest) string in the window matching the
  409. /// string starting at strstart.
  410. ///
  411. /// Preconditions:
  412. /// <code>
  413. /// strstart + DeflaterConstants.MAX_MATCH &lt;= window.length.</code>
  414. /// </summary>
  415. /// <param name="curMatch"></param>
  416. /// <returns>True if a match greater than the minimum length is found</returns>
  417. private bool FindLongestMatch(int curMatch)
  418. {
  419. int match;
  420. int scan = strstart;
  421. // scanMax is the highest position that we can look at
  422. int scanMax = scan + Math.Min(DeflaterConstants.MAX_MATCH, lookahead) - 1;
  423. int limit = Math.Max(scan - DeflaterConstants.MAX_DIST, 0);
  424. byte[] window = this.window;
  425. short[] prev = this.prev;
  426. int chainLength = this.max_chain;
  427. int niceLength = Math.Min(this.niceLength, lookahead);
  428. matchLen = Math.Max(matchLen, DeflaterConstants.MIN_MATCH - 1);
  429. if (scan + matchLen > scanMax) return false;
  430. byte scan_end1 = window[scan + matchLen - 1];
  431. byte scan_end = window[scan + matchLen];
  432. // Do not waste too much time if we already have a good match:
  433. if (matchLen >= this.goodLength) chainLength >>= 2;
  434. do
  435. {
  436. match = curMatch;
  437. scan = strstart;
  438. if (window[match + matchLen] != scan_end
  439. || window[match + matchLen - 1] != scan_end1
  440. || window[match] != window[scan]
  441. || window[++match] != window[++scan])
  442. {
  443. continue;
  444. }
  445. // scan is set to strstart+1 and the comparison passed, so
  446. // scanMax - scan is the maximum number of bytes we can compare.
  447. // below we compare 8 bytes at a time, so first we compare
  448. // (scanMax - scan) % 8 bytes, so the remainder is a multiple of 8
  449. switch ((scanMax - scan) % 8)
  450. {
  451. case 1:
  452. if (window[++scan] == window[++match]) break;
  453. break;
  454. case 2:
  455. if (window[++scan] == window[++match]
  456. && window[++scan] == window[++match]) break;
  457. break;
  458. case 3:
  459. if (window[++scan] == window[++match]
  460. && window[++scan] == window[++match]
  461. && window[++scan] == window[++match]) break;
  462. break;
  463. case 4:
  464. if (window[++scan] == window[++match]
  465. && window[++scan] == window[++match]
  466. && window[++scan] == window[++match]
  467. && window[++scan] == window[++match]) break;
  468. break;
  469. case 5:
  470. if (window[++scan] == window[++match]
  471. && window[++scan] == window[++match]
  472. && window[++scan] == window[++match]
  473. && window[++scan] == window[++match]
  474. && window[++scan] == window[++match]) break;
  475. break;
  476. case 6:
  477. if (window[++scan] == window[++match]
  478. && window[++scan] == window[++match]
  479. && window[++scan] == window[++match]
  480. && window[++scan] == window[++match]
  481. && window[++scan] == window[++match]
  482. && window[++scan] == window[++match]) break;
  483. break;
  484. case 7:
  485. if (window[++scan] == window[++match]
  486. && window[++scan] == window[++match]
  487. && window[++scan] == window[++match]
  488. && window[++scan] == window[++match]
  489. && window[++scan] == window[++match]
  490. && window[++scan] == window[++match]
  491. && window[++scan] == window[++match]) break;
  492. break;
  493. }
  494. if (window[scan] == window[match])
  495. {
  496. /* We check for insufficient lookahead only every 8th comparison;
  497. * the 256th check will be made at strstart + 258 unless lookahead is
  498. * exhausted first.
  499. */
  500. do
  501. {
  502. if (scan == scanMax)
  503. {
  504. ++scan; // advance to first position not matched
  505. ++match;
  506. break;
  507. }
  508. }
  509. while (window[++scan] == window[++match]
  510. && window[++scan] == window[++match]
  511. && window[++scan] == window[++match]
  512. && window[++scan] == window[++match]
  513. && window[++scan] == window[++match]
  514. && window[++scan] == window[++match]
  515. && window[++scan] == window[++match]
  516. && window[++scan] == window[++match]);
  517. }
  518. if (scan - strstart > matchLen)
  519. {
  520. #if DebugDeflation
  521. if (DeflaterConstants.DEBUGGING && (ins_h == 0) )
  522. Console.Error.WriteLine("Found match: " + curMatch + "-" + (scan - strstart));
  523. #endif
  524. matchStart = curMatch;
  525. matchLen = scan - strstart;
  526. if (matchLen >= niceLength)
  527. break;
  528. scan_end1 = window[scan - 1];
  529. scan_end = window[scan];
  530. }
  531. } while ((curMatch = (prev[curMatch & DeflaterConstants.WMASK] & 0xffff)) > limit && 0 != --chainLength);
  532. return matchLen >= DeflaterConstants.MIN_MATCH;
  533. }
  534. private bool DeflateStored(bool flush, bool finish)
  535. {
  536. if (!flush && (lookahead == 0))
  537. {
  538. return false;
  539. }
  540. strstart += lookahead;
  541. lookahead = 0;
  542. int storedLength = strstart - blockStart;
  543. if ((storedLength >= DeflaterConstants.MAX_BLOCK_SIZE) || // Block is full
  544. (blockStart < DeflaterConstants.WSIZE && storedLength >= DeflaterConstants.MAX_DIST) || // Block may move out of window
  545. flush)
  546. {
  547. bool lastBlock = finish;
  548. if (storedLength > DeflaterConstants.MAX_BLOCK_SIZE)
  549. {
  550. storedLength = DeflaterConstants.MAX_BLOCK_SIZE;
  551. lastBlock = false;
  552. }
  553. #if DebugDeflation
  554. if (DeflaterConstants.DEBUGGING)
  555. {
  556. Console.WriteLine("storedBlock[" + storedLength + "," + lastBlock + "]");
  557. }
  558. #endif
  559. huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock);
  560. blockStart += storedLength;
  561. return !(lastBlock || storedLength == 0);
  562. }
  563. return true;
  564. }
  565. private bool DeflateFast(bool flush, bool finish)
  566. {
  567. if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush)
  568. {
  569. return false;
  570. }
  571. while (lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush)
  572. {
  573. if (lookahead == 0)
  574. {
  575. // We are flushing everything
  576. huffman.FlushBlock(window, blockStart, strstart - blockStart, finish);
  577. blockStart = strstart;
  578. return false;
  579. }
  580. if (strstart > 2 * DeflaterConstants.WSIZE - DeflaterConstants.MIN_LOOKAHEAD)
  581. {
  582. /* slide window, as FindLongestMatch needs this.
  583. * This should only happen when flushing and the window
  584. * is almost full.
  585. */
  586. SlideWindow();
  587. }
  588. int hashHead;
  589. if (lookahead >= DeflaterConstants.MIN_MATCH &&
  590. (hashHead = InsertString()) != 0 &&
  591. strategy != DeflateStrategy.HuffmanOnly &&
  592. strstart - hashHead <= DeflaterConstants.MAX_DIST &&
  593. FindLongestMatch(hashHead))
  594. {
  595. // longestMatch sets matchStart and matchLen
  596. #if DebugDeflation
  597. if (DeflaterConstants.DEBUGGING)
  598. {
  599. for (int i = 0 ; i < matchLen; i++) {
  600. if (window[strstart + i] != window[matchStart + i]) {
  601. throw new SharpZipBaseException("Match failure");
  602. }
  603. }
  604. }
  605. #endif
  606. bool full = huffman.TallyDist(strstart - matchStart, matchLen);
  607. lookahead -= matchLen;
  608. if (matchLen <= max_lazy && lookahead >= DeflaterConstants.MIN_MATCH)
  609. {
  610. while (--matchLen > 0)
  611. {
  612. ++strstart;
  613. InsertString();
  614. }
  615. ++strstart;
  616. }
  617. else
  618. {
  619. strstart += matchLen;
  620. if (lookahead >= DeflaterConstants.MIN_MATCH - 1)
  621. {
  622. UpdateHash();
  623. }
  624. }
  625. matchLen = DeflaterConstants.MIN_MATCH - 1;
  626. if (!full)
  627. {
  628. continue;
  629. }
  630. }
  631. else
  632. {
  633. // No match found
  634. huffman.TallyLit(window[strstart] & 0xff);
  635. ++strstart;
  636. --lookahead;
  637. }
  638. if (huffman.IsFull())
  639. {
  640. bool lastBlock = finish && (lookahead == 0);
  641. huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);
  642. blockStart = strstart;
  643. return !lastBlock;
  644. }
  645. }
  646. return true;
  647. }
  648. private bool DeflateSlow(bool flush, bool finish)
  649. {
  650. if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush)
  651. {
  652. return false;
  653. }
  654. while (lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush)
  655. {
  656. if (lookahead == 0)
  657. {
  658. if (prevAvailable)
  659. {
  660. huffman.TallyLit(window[strstart - 1] & 0xff);
  661. }
  662. prevAvailable = false;
  663. // We are flushing everything
  664. #if DebugDeflation
  665. if (DeflaterConstants.DEBUGGING && !flush)
  666. {
  667. throw new SharpZipBaseException("Not flushing, but no lookahead");
  668. }
  669. #endif
  670. huffman.FlushBlock(window, blockStart, strstart - blockStart,
  671. finish);
  672. blockStart = strstart;
  673. return false;
  674. }
  675. if (strstart >= 2 * DeflaterConstants.WSIZE - DeflaterConstants.MIN_LOOKAHEAD)
  676. {
  677. /* slide window, as FindLongestMatch needs this.
  678. * This should only happen when flushing and the window
  679. * is almost full.
  680. */
  681. SlideWindow();
  682. }
  683. int prevMatch = matchStart;
  684. int prevLen = matchLen;
  685. if (lookahead >= DeflaterConstants.MIN_MATCH)
  686. {
  687. int hashHead = InsertString();
  688. if (strategy != DeflateStrategy.HuffmanOnly &&
  689. hashHead != 0 &&
  690. strstart - hashHead <= DeflaterConstants.MAX_DIST &&
  691. FindLongestMatch(hashHead))
  692. {
  693. // longestMatch sets matchStart and matchLen
  694. // Discard match if too small and too far away
  695. if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == DeflaterConstants.MIN_MATCH && strstart - matchStart > TooFar)))
  696. {
  697. matchLen = DeflaterConstants.MIN_MATCH - 1;
  698. }
  699. }
  700. }
  701. // previous match was better
  702. if ((prevLen >= DeflaterConstants.MIN_MATCH) && (matchLen <= prevLen))
  703. {
  704. #if DebugDeflation
  705. if (DeflaterConstants.DEBUGGING)
  706. {
  707. for (int i = 0 ; i < matchLen; i++) {
  708. if (window[strstart-1+i] != window[prevMatch + i])
  709. throw new SharpZipBaseException();
  710. }
  711. }
  712. #endif
  713. huffman.TallyDist(strstart - 1 - prevMatch, prevLen);
  714. prevLen -= 2;
  715. do
  716. {
  717. strstart++;
  718. lookahead--;
  719. if (lookahead >= DeflaterConstants.MIN_MATCH)
  720. {
  721. InsertString();
  722. }
  723. } while (--prevLen > 0);
  724. strstart++;
  725. lookahead--;
  726. prevAvailable = false;
  727. matchLen = DeflaterConstants.MIN_MATCH - 1;
  728. }
  729. else
  730. {
  731. if (prevAvailable)
  732. {
  733. huffman.TallyLit(window[strstart - 1] & 0xff);
  734. }
  735. prevAvailable = true;
  736. strstart++;
  737. lookahead--;
  738. }
  739. if (huffman.IsFull())
  740. {
  741. int len = strstart - blockStart;
  742. if (prevAvailable)
  743. {
  744. len--;
  745. }
  746. bool lastBlock = (finish && (lookahead == 0) && !prevAvailable);
  747. huffman.FlushBlock(window, blockStart, len, lastBlock);
  748. blockStart += len;
  749. return !lastBlock;
  750. }
  751. }
  752. return true;
  753. }
  754. #region Instance Fields
  755. // Hash index of string to be inserted
  756. private int ins_h;
  757. /// <summary>
  758. /// Hashtable, hashing three characters to an index for window, so
  759. /// that window[index]..window[index+2] have this hash code.
  760. /// Note that the array should really be unsigned short, so you need
  761. /// to and the values with 0xffff.
  762. /// </summary>
  763. private short[] head;
  764. /// <summary>
  765. /// <code>prev[index &amp; WMASK]</code> points to the previous index that has the
  766. /// same hash code as the string starting at index. This way
  767. /// entries with the same hash code are in a linked list.
  768. /// Note that the array should really be unsigned short, so you need
  769. /// to and the values with 0xffff.
  770. /// </summary>
  771. private short[] prev;
  772. private int matchStart;
  773. // Length of best match
  774. private int matchLen;
  775. // Set if previous match exists
  776. private bool prevAvailable;
  777. private int blockStart;
  778. /// <summary>
  779. /// Points to the current character in the window.
  780. /// </summary>
  781. private int strstart;
  782. /// <summary>
  783. /// lookahead is the number of characters starting at strstart in
  784. /// window that are valid.
  785. /// So window[strstart] until window[strstart+lookahead-1] are valid
  786. /// characters.
  787. /// </summary>
  788. private int lookahead;
  789. /// <summary>
  790. /// This array contains the part of the uncompressed stream that
  791. /// is of relevance. The current character is indexed by strstart.
  792. /// </summary>
  793. private byte[] window;
  794. private DeflateStrategy strategy;
  795. private int max_chain, max_lazy, niceLength, goodLength;
  796. /// <summary>
  797. /// The current compression function.
  798. /// </summary>
  799. private int compressionFunction;
  800. /// <summary>
  801. /// The input data for compression.
  802. /// </summary>
  803. private byte[] inputBuf;
  804. /// <summary>
  805. /// The total bytes of input read.
  806. /// </summary>
  807. private long totalIn;
  808. /// <summary>
  809. /// The offset into inputBuf, where input data starts.
  810. /// </summary>
  811. private int inputOff;
  812. /// <summary>
  813. /// The end offset of the input data.
  814. /// </summary>
  815. private int inputEnd;
  816. private DeflaterPending pending;
  817. private DeflaterHuffman huffman;
  818. /// <summary>
  819. /// The adler checksum
  820. /// </summary>
  821. private Adler32 adler;
  822. #endregion Instance Fields
  823. }
  824. }