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