(* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is TurboPower Abbrevia * * The Initial Developer of the Original Code is * TurboPower Software * * Portions created by the Initial Developer are Copyright (C) 1997-2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * ***** END LICENSE BLOCK ***** *) {*********************************************************} {* ABBREVIA: AbDfEnc.pas *} {*********************************************************} {* Deflate encoding unit *} {*********************************************************} unit AbDfEnc; {$I AbDefine.inc} interface uses Classes, AbDfBase; function Deflate(aSource : TStream; aDest : TStream; aHelper : TAbDeflateHelper) : longint; implementation uses AbDfInW, AbDfHufD, AbDfStrm, AbDfCryS, AbDfPkMg; {====================================================================} function CalcDynamicBitCount(aUseDeflate64: boolean; aLitBuckets : PAbDfLitBuckets; aDistBuckets : PAbDfDistBuckets; aCodeBuckets : PAbDfCodeLenBuckets; const aCodeLens : array of integer; const aCLCodeLens : array of integer; aLitCount : integer; aDistCount : integer; aCodeCount : integer) : longint; var Symbol : integer; LastSymbol : integer; Inx : integer; begin {note: this routine calculates the number of bits required to compress a given block} {a dynamic block starts off with 5 bits of literal symbol count, 5 bits of distance symbol count, 4 bits of codelength symbol count, and then 3 bits for every codelength symbol used} Result := 5 + 5 + 4 + (aCodeCount * 3); {add in the bits needed to compress the literal and distance trees} inc(Result, aCodeBuckets^[16] * (aCLCodeLens[16] + 2)); inc(Result, aCodeBuckets^[17] * (aCLCodeLens[16] + 3)); inc(Result, aCodeBuckets^[18] * (aCLCodeLens[16] + 7)); for Symbol := 3 to pred(aCodeCount) do begin Inx := dfc_CodeLengthIndex[Symbol]; Assert(Inx <=15, 'CalcDynamicBitCount: the index array of codelengths is corrupted'); inc(Result, aCodeBuckets^[Inx] * aCLCodeLens[Inx]) end; {make the literal symbol 285 a special case} LastSymbol := pred(aLitCount); if (LastSymbol = 285) then LastSymbol := 284; {add in all the bits needed to compress the literals (except 285)} for Symbol := 0 to LastSymbol do if (Symbol < dfc_LitExtraOffset) then inc(Result, aLitBuckets^[Symbol] * aCodeLens[Symbol]) else inc(Result, aLitBuckets^[Symbol] * (aCodeLens[Symbol] + dfc_LitExtraBits[Symbol - dfc_LitExtraOffset])); {add in all the bits needed to compress the literal symbol 285} if (pred(aLitCount) = 285) then if (not aUseDeflate64) then inc(Result, aLitBuckets^[285] * aCodeLens[285]) else inc(Result, aLitBuckets^[285] * (aCodeLens[285] + 16)); {add in all the bits needed to compress the distances} for Symbol := 0 to pred(aDistCount) do inc(Result, aDistBuckets^[Symbol] * (aCodeLens[aLitCount + Symbol] + dfc_DistExtraBits[Symbol])); end; {====================================================================} {====================================================================} procedure OutputEndOfBlock(aBitStrm : TAbDfOutBitStream; aLitTree : TAbDfDecodeHuffmanTree); var Code : longint; begin {note: this routine encodes the end-of-block character (symbol 256) and then writes out the code to the bit stream} {encode the end-of-block character as a symbol} {$IFOPT C+} {if Assertions are on } Code := aLitTree.Encode(256); {$ELSE} Code := aLitTree.Encodes^[256]; {$ENDIF} {write the code out to the bit stream} aBitStrm.WriteBits(Code and $FFFF, (Code shr 16) and $FF); end; {--------} procedure EncodeLZStreamStored(aFinalBlock : boolean; aStream : TAbDfLZStream; aBitStrm : TAbDfOutBitStream; aDataSize : integer; aLog : TAbLogger); var BlockHeader : packed record bhSize : word; bhNotSize : word; end; Buffer : pointer; Code : integer; BlockSize : integer; begin {note: this routine writes out an incompressible block to the bit stream (the store algorithm)} {allocate the maximum buffer we can write at once} GetMem(Buffer, 64 * 1024); try {while there's more incompressible data to store...} while (aDataSize <> 0) do begin {calculate the block size to write this time} if (aDataSize > $FFFF) then begin BlockSize := $FFFF; dec(aDataSize, $FFFF); end else begin BlockSize := aDataSize; aDataSize := 0; end; {$IFDEF UseLogging} {log it} if (aLog <> nil) then begin aLog.WriteLine('..Writing new block...'); aLog.WriteLine(Format('..final block? %d', [ord(aFinalBlock)])); aLog.WriteLine('..block type? 0'); aLog.WriteLine(Format('..block size: %d', [BlockSize])); end; {$ENDIF} {output the block information to the bit stream} if aFinalBlock then Code := 1 + (0 shl 1) else Code := 0 + (0 shl 1); aBitStrm.WriteBits(Code, 3); {align the bit stream to the nearest byte} aBitStrm.AlignToByte; {write the stored block header} BlockHeader.bhSize := BlockSize; BlockHeader.bhNotSize := not BlockHeader.bhSize; aBitStrm.WriteBuffer(BlockHeader, sizeof(BlockHeader)); {get and write this block} aStream.ReadStoredBuffer(Buffer^, BlockSize); aBitStrm.WriteBuffer(Buffer^, BlockSize); end; finally FreeMem(Buffer); end; {clear the stream, ready for the next block} aStream.Clear; end; {--------} procedure EncodeLZStreamStatic(aFinalBlock : boolean; aUseDeflate64 : boolean; aStream : TAbDfLZStream; aBitStrm : TAbDfOutBitStream; aLog : TAbLogger); var Code : integer; begin {note: this routine writes out the stream of LZ77 tokens for the current block to the bit stream, using the static huffman trees to encode the token symbols} {$IFDEF UseLogging} {log it} if (aLog <> nil) then begin aLog.WriteLine('..Writing new block...'); aLog.WriteLine(Format('..final block? %d', [ord(aFinalBlock)])); aLog.WriteLine('..block type? 1'); end; {$ENDIF} {output the block information to the bit stream} if aFinalBlock then Code := 1 + (1 shl 1) else Code := 0 + (1 shl 1); aBitStrm.WriteBits(Code, 3); {encode the LZ77 stream} aStream.Encode(aBitStrm, AbStaticLiteralTree, AbStaticDistanceTree, aUseDeflate64); {output the end-of-block marker to the bit stream} OutputEndOfBlock(aBitStrm, AbStaticLiteralTree); {$IFDEF UseLogging} if (aLog <> nil) then aLog.WriteLine('Char: end-of-block marker (#256)'); {$ENDIF} end; {--------} procedure EncodeLZStreamDynamic(aFinalBlock : boolean; aUseDeflate64 : boolean; aUseBest : boolean; aStream : TAbDfLZStream; aBitStrm : TAbDfOutBitStream; aLog : TAbLogger); var i : integer; LitTree : TAbDfDecodeHuffmanTree; DistTree : TAbDfDecodeHuffmanTree; CodeLenTree : TAbDfDecodeHuffmanTree; CodeLenStream : TAbDfCodeLenStream; CodeLens : array [0..285+32] of integer; CLCodeLens : array [0..18] of integer; LitCodeCount : integer; DistCodeCount : integer; LenCodeCount : integer; BitCount : integer; Code : integer; StaticSize : integer; StoredSize : integer; begin {note: this routine writes out the stream of LZ77 tokens for the current block to the bit stream, using the dynamic huffman trees to encode the token symbols; if the routine determines that the data can better be compressed using the static huffman trees or should be stored as is, it'll switch algorithms} {prepare for the try..finally} LitTree := nil; DistTree := nil; CodeLenTree := nil; CodeLenStream := nil; try {calculate the code lengths for the literal symbols} GenerateCodeLengths(15, aStream.LitBuckets^, CodeLens, 0, aLog); {calculate the number of the used codelengths for the literals} LitCodeCount := 286; repeat dec(LitCodeCount); until (CodeLens[LitCodeCount] <> 0); inc(LitCodeCount); {calculate the code lengths for the distance symbols} GenerateCodeLengths(15, aStream.DistBuckets^, CodeLens, LitCodeCount, aLog); {calculate the number of the used codelengths for the distances} DistCodeCount := 32; repeat dec(DistCodeCount); until (CodeLens[DistCodeCount + LitCodeCount] <> 0); inc(DistCodeCount); {calculate the code lengths array as a stream of items} CodeLenStream := TAbDfCodeLenStream.Create(aLog); CodeLenStream.Build(CodeLens, LitCodeCount + DistCodeCount); {calculate the codelengths for the code lengths} GenerateCodeLengths(7, CodeLenStream.Buckets^, CLCodeLens, 0, nil); {calculate the number of the used codelengths for the code lengths} LenCodeCount := 19; repeat dec(LenCodeCount); until (CLCodeLens[dfc_CodeLengthIndex[LenCodeCount]] <> 0); inc(LenCodeCount); {..there's a minimum of four, though} if (LenCodeCount < 4) then LenCodeCount := 4; {if we have to work out and use the best method...} if aUseBest then begin {calculate the number of bits required for the compressed data using dynamic huffman trees} BitCount := CalcDynamicBitCount(aUseDeflate64, aStream.LitBuckets, aStream.DistBuckets, CodeLenStream.Buckets, CodeLens, CLCodeLens, LitCodeCount, DistCodeCount, LenCodeCount); {choose the algorithm with the smallest size} StaticSize := aStream.StaticSize; StoredSize := (aStream.StoredSize + 4) * 8; if (StaticSize < BitCount) then begin if (StoredSize < StaticSize) then EncodeLZStreamStored(aFinalBlock, aStream, aBitStrm, (StoredSize div 8) - 4, aLog) else EncodeLZStreamStatic(aFinalBlock, aUseDeflate64, aStream, aBitStrm, aLog); Exit; end else if (StoredSize < BitCount) then begin EncodeLZStreamStored(aFinalBlock, aStream, aBitStrm, (StoredSize div 8) - 4, aLog); Exit; end; end; {create the code lengths tree} CodeLenTree := TAbDfDecodeHuffmanTree.Create(19, 7, huEncoding); CodeLenTree.Build(CLCodeLens, 0, 19, [0], $FFFF); {$IFDEF UseLogging} {log the tree} if (aLog <> nil) then begin aLog.WriteLine('Code lengths tree'); CodeLenTree.DebugPrint(aLog); end; {$ENDIF} {calculate the literal encoding tree} LitTree := TAbDfDecodeHuffmanTree.Create(286, 15, huEncoding); LitTree.Build(CodeLens, 0, LitCodeCount, dfc_LitExtraBits, dfc_LitExtraOffset); {$IFDEF UseLogging} {log the tree} if (aLog <> nil) then begin aLog.WriteLine('Literal/length tree'); LitTree.DebugPrint(aLog); end; {$ENDIF} {calculate the distance tree} if aUseDeflate64 then DistTree := TAbDfDecodeHuffmanTree.Create(32, 15, huEncoding) else DistTree := TAbDfDecodeHuffmanTree.Create(30, 15, huEncoding); DistTree.Build(CodeLens, LitCodeCount, DistCodeCount, dfc_DistExtraBits, dfc_DistExtraOffset); {$IFDEF UseLogging} if (aLog <> nil) then begin {log the tree} aLog.WriteLine('Distance tree'); DistTree.DebugPrint(aLog); {log the new block} aLog.WriteLine('..Writing new block...'); aLog.WriteLine(Format('..final block? %d', [ord(aFinalBlock)])); aLog.WriteLine('..block type? 2'); aLog.WriteLine(Format('Count of literals: %d', [LitCodeCount])); aLog.WriteLine(Format('Count of distances: %d', [DistCodeCount])); aLog.WriteLine(Format('Count of code lengths: %d', [LenCodeCount])); end; {$ENDIF} {output the block information to the bit stream} if aFinalBlock then Code := 1 + (2 shl 1) else Code := 0 + (2 shl 1); aBitStrm.WriteBits(Code, 3); {output the various counts to the bit stream} Code := (LitCodeCount - 257) + ((DistCodeCount - 1) shl 5) + ((LenCodeCount - 4) shl 10); aBitStrm.WriteBits(Code, 14); {output the code length codelengths to the bit stream} for i := 0 to pred(LenCodeCount) do aBitStrm.WriteBits(CLCodeLens[dfc_CodeLengthIndex[i]], 3); {encode and write the codelength stream to the bit stream} CodeLenStream.Encode(aBitStrm, CodeLenTree); {encode and write the LZ77 stream to the bit stream} aStream.Encode(aBitStrm, LitTree, DistTree, aUseDeflate64); {output the end-of-block marker to the bit stream} OutputEndOfBlock(aBitStrm, LitTree); {$IFDEF UseLogging} if (aLog <> nil) then aLog.WriteLine('Char: end-of-block marker (#256)'); {$ENDIF} finally LitTree.Free; DistTree.Free; CodeLenTree.Free; CodeLenStream.Free; end; end; {====================================================================} {===Single algorithm Static/Dynamic Huffman tree deflate=============} function DeflateStaticDynamic(aStatic : boolean; aUseBest: boolean; aSource : TStream; aDest : TStream; aHelper : TAbDeflateHelper; aLog : TAbLogger) : longint; var i : integer; SlideWin : TAbDfInputWindow; BitStrm : TAbDfOutBitStream; LZ77Stream : TAbDfLZStream; KeyLen : integer; Match : TAbDfMatch; PrevMatch : TAbDfMatch; UseDeflate64 : boolean; UseCRC32 : boolean; GotMatch : boolean; LZStrmIsFull : boolean; TestForBinary: boolean; begin {note: turn on the following define to see when and how the lazy matching algorithm works} {$IFDEF UseLogging} {$DEFINE UseLazyMatchLogging} {$ENDIF} {$IFDEF UseLogging} if (aLog <> nil) then if aStatic then aLog.WriteLine('..compressing source data with static huffman trees') else aLog.WriteLine('..compressing source data with dynamic huffman trees'); {$ENDIF} {prepare for the try..finally} SlideWin := nil; BitStrm := nil; LZ77Stream := nil; try {create the sliding window} UseDeflate64 := (aHelper.Options and dfc_UseDeflate64) <> 0; UseCRC32 := (aHelper.Options and dfc_UseAdler32) = 0; SlideWin := TAbDfInputWindow.Create(aSource, aHelper.StreamSize, aHelper.WindowSize, aHelper.ChainLength, UseDeflate64, UseCRC32); SlideWin.OnProgress := aHelper.OnProgressStep; {create the bit stream} BitStrm := TAbDfOutBitStream.Create(aDest); {create the LZ77 stream} LZ77Stream := TAbDfLZStream.Create(SlideWin, UseDeflate64, aLog); LZStrmIsFull := false; TestForBinary := true; {set the previous match to be a literal character: this will ensure that no lazy matching goes on with the first key read} PrevMatch.maLen := 0; {get the first key length} KeyLen := SlideWin.GetNextKeyLength; {while the current key is three characters long...} while (KeyLen = 3) do begin {tweak for binary/text} {note: the test for whether a stream is binary or not is to check whether there are any #0 characters in the first 1024 bytes: if there are the stream is binary. this test and tweaking is based on experimentation compression ratios for binary and text files based on the PKZIP 'n' option.} if TestForBinary and (LZ77Stream.StoredSize = 1024) then begin if (aHelper.PKZipOption = 'n') then if (LZ77Stream.LitBuckets^[0] = 0) then begin aHelper.AmpleLength := aHelper.AmpleLength * 2; aHelper.MaxLazyLength := aHelper.MaxLazyLength * 2; aHelper.ChainLength := aHelper.ChainLength * 2; SlideWin.ChainLen := aHelper.ChainLength; end; TestForBinary := false; end; {if the LZ77 stream is full, empty it} if LZStrmIsFull then begin if aStatic then EncodeLZStreamStatic(false, UseDeflate64, LZ77Stream, BitStrm, aLog) else EncodeLZStreamDynamic(false, UseDeflate64, aUseBest, LZ77Stream, BitStrm, aLog); LZ77Stream.Clear; LZStrmIsFull := false; end; {try and find a match of three or more characters (note: this has the side effect of adding the current key to the internal hash table); this routine will only return true if it finds a match greater than the previous match} GotMatch := SlideWin.FindLongestMatch(aHelper.AmpleLength, Match, PrevMatch); {if the maximum match length were three and the distance exceeds 4096 bytes, it's most likely that we'll get better compression by outputting the three literal bytes rather than by outputting a length symbol, a distance symbol, and at least ten extra bits for the extra distance value} if (Match.maLen = 3) and (Match.maDist > 4096) then GotMatch := false; {if we found a match...} if GotMatch then begin {if there were no previous match, we can't do any lazy match processing now, so save the current match details ready for lazy matching the next time through, and advance the sliding window} if (PrevMatch.maLen = 0) then begin PrevMatch.maLen := Match.maLen; PrevMatch.maDist := Match.maDist; PrevMatch.maLit := Match.maLit; SlideWin.AdvanceByOne; end {otherwise the previous match is smaller than this one, so we're going to accept this match in preference; throw away the previous match, output the previous literal character instead and save these match details} else begin {$IFDEF UseLazyMatchLogging} if (aLog <> nil) then aLog.WriteLine( Format( '..this match longer, rejecting previous one (%d,%d)', [PrevMatch.maLen, PrevMatch.maDist])); {$ENDIF} LZStrmIsFull := LZ77Stream.AddLiteral(PrevMatch.maLit); PrevMatch.maLen := Match.maLen; PrevMatch.maDist := Match.maDist; PrevMatch.maLit := Match.maLit; SlideWin.AdvanceByOne; end; {if, by this point, we're storing up a match, check to see if it equals or exceeds the maximum lazy match length; if it does then output the match right now and avoid checking for a lazy match} if (PrevMatch.maLen >= aHelper.MaxLazyLength) then begin {$IFDEF UseLazyMatchLogging} if (aLog <> nil) then if ((aHelper.Options and dfc_UseLazyMatch) <> 0) then aLog.WriteLine('..match longer than max lazy match, using it'); {$ENDIF} LZStrmIsFull := LZ77Stream.AddLenDist(PrevMatch.maLen, PrevMatch.maDist); SlideWin.Advance(PrevMatch.maLen - 1, PrevMatch.maLen - 1); PrevMatch.maLen := 0; end; end {otherwise, we don't have a match at all: so we possibly just need to output a literal character} else begin {if there was a previous match, output it and discard the results of this match} if (PrevMatch.maLen <> 0) then begin LZStrmIsFull := LZ77Stream.AddLenDist(PrevMatch.maLen, PrevMatch.maDist); SlideWin.Advance(PrevMatch.maLen - 1, PrevMatch.maLen - 2); PrevMatch.maLen := 0; end {otherwise there was no previous match or it's already been output, so output this literal} else begin LZStrmIsFull := LZ77Stream.AddLiteral(Match.maLit); SlideWin.AdvanceByOne; PrevMatch.maLen := 0; end; end; {get the next key} KeyLen := SlideWin.GetNextKeyLength; end; {if the last key read were one or two characters in length, save them as literal character encodings} if (KeyLen > 0) then begin {if there's a match pending, it'll be of length 3: output it} if (PrevMatch.maLen <> 0) then begin Assert(PrevMatch.maLen = 3, 'DeflateStaticDynamic: previous match should be length 3'); LZ77Stream.AddLenDist(PrevMatch.maLen, PrevMatch.maDist); end {otherwise, output the one or two final literals} else for i := 1 to KeyLen do LZ77Stream.AddLiteral(SlideWin.GetNextChar); end; {empty the LZ77 stream} if aStatic then EncodeLZStreamStatic(true, UseDeflate64, LZ77Stream, BitStrm, aLog) else EncodeLZStreamDynamic(true, UseDeflate64, aUseBest, LZ77Stream, BitStrm, aLog); {calculate the checksum of the input stream} Result := SlideWin.Checksum; finally {free the objects} SlideWin.Free; BitStrm.Free; LZ77Stream.Free; end;{try..finally} {$IFDEF UseLogging} {log it} if (aLog <> nil) then aLog.WriteLine(Format('..checksum: %8x', [Result])) {$ENDIF} end; {====================================================================} {===Simple storing===================================================} function DeflateStored(aSource : TStream; aDest : TStream; aHelper : TAbDeflateHelper; aLog : TAbLogger) : longint; const StoredBlockSize = $FFFF; var Buffer : PAnsiChar; BytesRead : LongWord; ByteCount : Int64; BytesToGo : Int64; CurPos : Int64; Size : Int64; Percent : longint; CheckSum : longint; UseCRC32 : boolean; BlockHeader : packed record bhInfo : byte; bhSize : word; bhNotSize : word; end; begin {note: this routine merely stores the aSource stream data, no compression is attempted or done} {$IFDEF UseLogging} if (aLog <> nil) then aLog.WriteLine('..storing source data to destination, no compression'); {$ENDIF} {initialize} ByteCount := 0; UseCRC32 := (aHelper.Options and dfc_UseAdler32) = 0; if UseCRC32 then Checksum := -1 { CRC32 starts off with all bits set} else CheckSum := 1; { Adler32 starts off with a value of 1} if (aHelper.StreamSize > 0) then BytesToGo := aHelper.StreamSize else begin CurPos := aSource.Seek(0, soCurrent); Size := aSource.Seek(0, soEnd); aSource.Seek(CurPos, soBeginning); BytesToGo := Size - CurPos; end; {get a buffer} GetMem(Buffer, StoredBlockSize); try {while there is still data to be stored...} while (BytesToGo <> 0) do begin {read the next block} BytesRead := aSource.Read(Buffer^, StoredBlockSize); {fire the progress event} if Assigned(aHelper.OnProgressStep) then begin inc(ByteCount, BytesRead); Percent := Round((100.0 * ByteCount) / aHelper.StreamSize); aHelper.OnProgressStep(Percent); end; {update the checksum} if UseCRC32 then AbUpdateCRCBuffer(Checksum, Buffer^, BytesRead) else AbUpdateAdlerBuffer(Checksum, Buffer^, BytesRead); {write the block header} if (BytesRead = BytesToGo) then BlockHeader.bhInfo := 1 {ie, final block, stored} else BlockHeader.bhInfo := 0; {ie, not final block, stored} BlockHeader.bhSize := BytesRead; BlockHeader.bhNotSize := not BlockHeader.bhSize; aDest.WriteBuffer(BlockHeader, sizeof(BlockHeader)); {write the block of data} aDest.WriteBuffer(Buffer^, BytesRead); {$IFDEF UseLogging} {log it} if (aLog <> nil) then begin if (BlockHeader.bhInfo = 0) then aLog.WriteLine(Format('..block size: %d', [BytesRead])) else aLog.WriteLine(Format('..block size: %d (final block)', [BytesRead])); end; {$ENDIF} {decrement the number of bytes to go} dec(BytesToGo, BytesRead); end; finally FreeMem(Buffer); end; {return the checksum} {note: the CRC32 checksum algorithm requires a post-conditioning step after being calculated (the result is NOTted), whereas Adler32 does not} if UseCRC32 then Result := not Checksum else Result := Checksum; {$IFDEF UseLogging} {log it} if (aLog <> nil) then aLog.WriteLine(Format('..checksum: %8x', [Result])) {$ENDIF} end; {====================================================================} {===Interfaced routine===============================================} function Deflate(aSource : TStream; aDest : TStream; aHelper : TAbDeflateHelper) : longint; var Helper : TAbDeflateHelper; Log : TAbLogger; SourceStartPos : longint; DestStartPos : longint; begin {pre-conditions: streams are allocated, options enable some kind of archiving} Assert(aSource <> nil, 'Deflate: aSource stream cannot be nil'); Assert(aDest <> nil, 'Deflate: aDest stream cannot be nil'); Assert((aHelper = nil) or ((aHelper.Options and $07) <> 0), 'Deflate: aHelper.Options must enable some kind of archiving'); {$IFDEF DefeatWarnings} Result := 0; {$ENDIF} {prepare for the try..finally} Helper := nil; Log := nil; try {finally} try {except} {create our helper; assign the passed one to it} Helper := TAbDeflateHelper.Create; if (aHelper <> nil) then Helper.Assign(aHelper); {save the current positions of both streams} SourceStartPos := aSource.Position; DestStartPos := aDest.Position; {if the helper's stream size is -1, and it has a progress event handler, calculate the stream size from the stream itself} if Assigned(Helper.OnProgressStep) then begin if (Helper.StreamSize = -1) then Helper.StreamSize := aSource.Size; end {otherwise we certainly can't do any progress reporting} else begin Helper.OnProgressStep := nil; Helper.StreamSize := 0; end; {if lazy matching is not requested, ensure the maximum lazy match length is zero: this make the LZ77 code a little easier to understand} if ((Helper.Options and dfc_UseLazyMatch) = 0) then Helper.MaxLazyLength := 0; {patch up the various lengths in the helper if they specify the maximum (that is, are equal to -1)} if (Helper.AmpleLength = -1) then Helper.AmpleLength := MaxLongInt; if (Helper.MaxLazyLength = -1) then Helper.MaxLazyLength := MaxLongInt; if (Helper.ChainLength = -1) then Helper.ChainLength := MaxLongInt; {create the logger, if requested} if (Helper.LogFile <> '') then begin Log := TAbLogger.Create(Helper.LogFile); Log.WriteLine('DEFLATING STREAM...'); {$IFNDEF UseLogging} Log.WriteLine('Need to recompile the app with UseLogging turned on'); {$ENDIF} end; {use the helper's options property to decide what to do} case (Helper.Options and $07) of dfc_CanUseStored : Result := DeflateStored(aSource, aDest, Helper, Log); dfc_CanUseStatic : Result := DeflateStaticDynamic(true, false, aSource, aDest, Helper, Log); dfc_CanUseDynamic : Result := DeflateStaticDynamic(false, false, aSource, aDest, Helper, Log); else Result := DeflateStaticDynamic(false, true, aSource, aDest, Helper, Log); end; {save the uncompressed and compressed sizes} if (aHelper <> nil) then begin aHelper.NormalSize := aSource.Position - SourceStartPos; aHelper.CompressedSize := aDest.Position - DestStartPos; end; except on E : EAbInternalDeflateError do begin {$IFDEF UseLogging} if (Log <> nil) then Log.WriteLine(Format('Internal exception raised: %s', [E.Message])); {$ENDIF} raise EAbDeflateError.Create(E.Message); end; end; finally Helper.Free; Log.Free; end; {WARNING NOTE: the compiler will warn that the return value of this function might be undefined. However, it is wrong: it has been fooled by the code. If you don't want to see this warning again, enable the DefeatWarnings compiler define in AbDefine.inc.} end; {====================================================================} end.