1520 lines
45 KiB
ObjectPascal
1520 lines
45 KiB
ObjectPascal
(* ***** 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: AbDfStrm.pas *}
|
|
{*********************************************************}
|
|
{* Deflate streams unit for various streams *}
|
|
{*********************************************************}
|
|
|
|
unit AbDfStrm;
|
|
|
|
{$I AbDefine.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
AbDfBase,
|
|
AbDfInW,
|
|
AbDfHufD;
|
|
|
|
type
|
|
TAb32bit = longint; { a 32-bit type}
|
|
|
|
PAbDfLitBuckets = ^TAbDfLitBuckets;
|
|
TAbDfLitBuckets = array [0..285] of integer;
|
|
PAbDfDistBuckets = ^TAbDfDistBuckets;
|
|
TAbDfDistBuckets = array [0..31] of integer;
|
|
PAbDfCodeLenBuckets = ^TAbDfCodeLenBuckets;
|
|
TAbDfCodeLenBuckets = array [0..18] of integer;
|
|
|
|
|
|
const
|
|
AbExtractMask : array [1..31] of TAb32bit =
|
|
($00000001, $00000003, $00000007, $0000000F,
|
|
$0000001F, $0000003F, $0000007F, $000000FF,
|
|
$000001FF, $000003FF, $000007FF, $00000FFF,
|
|
$00001FFF, $00003FFF, $00007FFF, $0000FFFF,
|
|
$0001FFFF, $0003FFFF, $0007FFFF, $000FFFFF,
|
|
$001FFFFF, $003FFFFF, $007FFFFF, $00FFFFFF,
|
|
$01FFFFFF, $03FFFFFF, $07FFFFFF, $0FFFFFFF,
|
|
$1FFFFFFF, $3FFFFFFF, $7FFFFFFF);
|
|
|
|
type
|
|
TAbDfInBitStream = class { input bit stream}
|
|
private
|
|
FBitBuffer : TAb32bit;
|
|
FBitsLeft : integer;
|
|
FBufEnd : PAnsiChar;
|
|
FBuffer : PAnsiChar;
|
|
FBufPos : PAnsiChar;
|
|
FByteCount : longint;
|
|
FFakeCount : integer;
|
|
FOnProgress: TAbProgressStep;
|
|
{$IFOPT C+}
|
|
FPeekCount : integer;
|
|
{$ENDIF}
|
|
FStream : TStream;
|
|
FStreamSize: longint;
|
|
protected
|
|
function ibsFillBuffer : boolean;
|
|
public
|
|
constructor Create(aStream : TStream;
|
|
aOnProgress : TAbProgressStep;
|
|
aStreamSize : longint);
|
|
destructor Destroy; override;
|
|
|
|
procedure AlignToByte;
|
|
procedure DiscardBits(aCount : integer);
|
|
procedure DiscardMoreBits(aCount : integer);
|
|
function PeekBits(aCount : integer) : integer;
|
|
function PeekMoreBits(aCount : integer) : integer;
|
|
function ReadBit : boolean;
|
|
function ReadBits(aCount : integer) : integer;
|
|
procedure ReadBuffer(var aBuffer; aCount : integer);
|
|
|
|
property BitBuffer : TAb32bit read FBitBuffer write FBitBuffer;
|
|
property BitsLeft : integer read FBitsLeft write FBitsLeft;
|
|
end;
|
|
|
|
type
|
|
TAbDfOutBitStream = class { output bit stream}
|
|
private
|
|
FBitBuffer : TAb32bit;
|
|
FBitsUsed : integer;
|
|
FBufEnd : PAnsiChar;
|
|
FBuffer : PAnsiChar;
|
|
FBufPos : PAnsiChar;
|
|
FStream : TStream;
|
|
protected
|
|
procedure obsEmptyBuffer;
|
|
public
|
|
constructor Create(aStream : TStream);
|
|
destructor Destroy; override;
|
|
|
|
procedure AlignToByte;
|
|
function Position : longint;
|
|
procedure WriteBit(aBit : boolean);
|
|
procedure WriteBits(aBits : integer; aCount : integer);
|
|
procedure WriteBuffer(var aBuffer; aCount : integer);
|
|
procedure WriteMoreBits(aBits : integer; aCount : integer);
|
|
|
|
property BitBuffer : TAb32bit read FBitBuffer write FBitBuffer;
|
|
property BitsUsed : integer read FBitsUsed write FBitsUsed;
|
|
end;
|
|
|
|
type
|
|
TAbDfLZStream = class { LZ77 token stream}
|
|
private
|
|
FCurPos : PAnsiChar;
|
|
FDistBuckets : PAbDfDistBuckets;
|
|
FDistCount : integer;
|
|
FLitBuckets : PAbDfLitBuckets;
|
|
FLitCount : integer;
|
|
FLog : TAbLogger;
|
|
FSlideWin : TAbDfInputWindow;
|
|
FStartOfs : Int64;
|
|
FStoredSize : LongWord;
|
|
FStream : PAnsiChar;
|
|
FStrmEnd : PAnsiChar;
|
|
{$IFDEF UseLogging}
|
|
FSWPos : longint;
|
|
{$ENDIF}
|
|
FUseDeflate64: boolean;
|
|
protected
|
|
function lzsGetApproxSize : LongWord;
|
|
function lzsGetStaticSize : integer;
|
|
function lzsGetStoredSize : integer;
|
|
function lzsIsFull : boolean;
|
|
public
|
|
constructor Create(aSlideWin : TAbDfInputWindow;
|
|
aUseDeflate64 : boolean;
|
|
aLog : TAbLogger);
|
|
destructor Destroy; override;
|
|
|
|
function AddLenDist(aLen : integer; aDist : integer) : boolean;
|
|
{ returns true if the stream is "full"}
|
|
function AddLiteral(aCh : AnsiChar) : boolean;
|
|
{ returns true if the stream is "full"}
|
|
|
|
procedure Clear;
|
|
procedure Encode(aBitStrm : TAbDfOutBitStream;
|
|
aLitTree : TAbDfDecodeHuffmanTree;
|
|
aDistTree : TAbDfDecodeHuffmanTree;
|
|
aUseDeflate64 : boolean);
|
|
procedure Rewind;
|
|
|
|
procedure ReadStoredBuffer(var aBuffer; aCount : integer);
|
|
|
|
property LenDistCount : integer read FDistCount;
|
|
property LiteralCount : integer read FLitCount;
|
|
|
|
property DistBuckets : PAbDfDistBuckets read FDistBuckets;
|
|
property LitBuckets : PAbDfLitBuckets read FLitBuckets;
|
|
|
|
property StaticSize : integer read lzsGetStaticSize;{ in bits}
|
|
property StoredSize : integer read lzsGetStoredSize;{ in bytes}
|
|
end;
|
|
|
|
type
|
|
TAbDfCodeLenStream = class { codelength token stream}
|
|
private
|
|
FBuckets : PAbDfCodeLenBuckets;
|
|
FPosition : PAnsiChar;
|
|
FStream : PAnsiChar; {array [0..285+32*2] of byte;}
|
|
FStrmEnd : PAnsiChar;
|
|
protected
|
|
public
|
|
constructor Create(aLog : TAbLogger);
|
|
destructor Destroy; override;
|
|
|
|
procedure Build(const aCodeLens : array of integer;
|
|
aCount : integer);
|
|
procedure Encode(aBitStrm : TAbDfOutBitStream;
|
|
aTree : TAbDfDecodeHuffmanTree);
|
|
|
|
property Buckets : PAbDfCodeLenBuckets read FBuckets;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils,
|
|
AbDfXlat;
|
|
|
|
type
|
|
PAb32bit = ^TAb32bit;
|
|
|
|
const
|
|
BitStreamBufferSize = 16*1024;
|
|
|
|
{===TAbDfInBitStream=================================================}
|
|
constructor TAbDfInBitStream.Create(aStream : TStream;
|
|
aOnProgress : TAbProgressStep;
|
|
aStreamSize : longint);
|
|
begin
|
|
{protect against dumb programming mistakes}
|
|
Assert(aStream <> nil,
|
|
'TAbDfInBitStream.Create: Cannot create a bit stream wrapping a nil stream');
|
|
|
|
{create the ancestor}
|
|
inherited Create;
|
|
|
|
{save the stream instance, allocate the buffer}
|
|
FStream := aStream;
|
|
GetMem(FBuffer, BitStreamBufferSize);
|
|
|
|
{save the on progress handler}
|
|
if Assigned(aOnProgress) and (aStreamSize > 0) then begin
|
|
FOnProgress := aOnProgress;
|
|
FStreamSize := aStreamSize;
|
|
end;
|
|
end;
|
|
{--------}
|
|
destructor TAbDfInBitStream.Destroy;
|
|
begin
|
|
{if we did some work...}
|
|
if (FBuffer <> nil) then begin
|
|
|
|
{reposition the underlying stream to the point where we stopped;
|
|
this position is equal to...
|
|
the position of the underlying stream, PLUS
|
|
the number of fake bytes we added, LESS
|
|
the number of bytes in the buffer, PLUS
|
|
the position in the buffer, PLUS
|
|
the number of complete bytes in the bit buffer}
|
|
FStream.Seek(FStream.Position +
|
|
FFakeCount -
|
|
(FBufEnd - FBuffer) +
|
|
(FBufPos - FBuffer) -
|
|
(FBitsLeft div 8), soBeginning);
|
|
|
|
{free the buffer}
|
|
FreeMem(FBuffer);
|
|
end;
|
|
|
|
{destroy the ancestor}
|
|
inherited Destroy;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfInBitStream.AlignToByte;
|
|
begin
|
|
{get rid of the odd bits by shifting them out of the bit cache}
|
|
FBitBuffer := FBitBuffer shr (FBitsLeft mod 8);
|
|
dec(FBitsLeft, FBitsLeft mod 8);
|
|
end;
|
|
{--------}
|
|
procedure TAbDfInBitStream.DiscardBits(aCount : integer);
|
|
var
|
|
BitsToGo : integer;
|
|
begin
|
|
{aCount comes from a (possibly corrupt) stream, so check that it is
|
|
the correct range, 1..32}
|
|
if (aCount <= 0) or (aCount > 32) then
|
|
raise EAbInternalInflateError.Create(
|
|
'count of bits must be between 1 and 32 inclusive [TAbDfInBitStream.DiscardBits]');
|
|
|
|
{$IFOPT C+}
|
|
{verify that the count of bits to discard is less than or equal to
|
|
the recent count from PeekBits--a programming error}
|
|
Assert((aCount <= FPeekCount),
|
|
'TAbDfInBitStream.DiscardBits: discarding more bits than peeked');
|
|
{since we're discarding bits already peeked, reset the peek count}
|
|
FPeekCount := 0;
|
|
{$ENDIF}
|
|
|
|
{if we have more than enough bits in our bit buffer, update the
|
|
bitbuffer and the number of bits left}
|
|
if (aCount <= FBitsLeft) then begin
|
|
FBitBuffer := FBitBuffer shr aCount;
|
|
dec(FBitsLeft, aCount);
|
|
end
|
|
|
|
{otherwise we shall have to read another integer out of the buffer
|
|
to satisfy the request}
|
|
else begin
|
|
{check that there is data in the buffer, if not it's indicates a
|
|
corrupted stream: PeekBits should have filled it}
|
|
if (FBufPos = FBufEnd) then
|
|
raise EAbInternalInflateError.Create(
|
|
'no more compressed data in stream [TAbDfInBitStream.DiscardBits]');
|
|
|
|
{refill the bit buffer}
|
|
BitsToGo := aCount - FBitsLeft;
|
|
FBitBuffer := PAb32bit(FBufPos)^;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
FBitBuffer := FBitBuffer shr BitsToGo;
|
|
FBitsLeft := 32 - BitsToGo;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfInBitStream.DiscardMoreBits(aCount : integer);
|
|
var
|
|
BitsToGo : integer;
|
|
begin
|
|
{aCount comes from a (possibly corrupt) stream, so check that it is
|
|
the correct range, 1..32}
|
|
if (aCount <= 0) or (aCount > 32) then
|
|
raise EAbInternalInflateError.Create(
|
|
'count of bits must be between 1 and 32 inclusive [TAbDfInBitStream.DiscardMoreBits]');
|
|
|
|
{$IFOPT C+}
|
|
{verify that the count of bits to discard is less than or equal to
|
|
the recent count from PeekBits--a programming error}
|
|
Assert((aCount <= FPeekCount),
|
|
'TAbDfInBitStream.DiscardBits: discarding more bits than peeked');
|
|
{since we're discarding bits already peeked, reset the peek count}
|
|
FPeekCount := 0;
|
|
{$ENDIF}
|
|
|
|
{check that there is data in the buffer, if not it's indicates a
|
|
corrupted stream: PeekBits/PeekMoreBits should have filled it}
|
|
if (FBufPos = FBufEnd) then
|
|
raise EAbInternalInflateError.Create(
|
|
'no more compressed data in stream [TAbDfInBitStream.DiscardBits]');
|
|
|
|
{refill the bit buffer}
|
|
BitsToGo := aCount - FBitsLeft;
|
|
FBitBuffer := PAb32bit(FBufPos)^;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
FBitBuffer := FBitBuffer shr BitsToGo;
|
|
FBitsLeft := 32 - BitsToGo;
|
|
end;
|
|
{--------}
|
|
function TAbDfInBitStream.ibsFillBuffer : boolean;
|
|
var
|
|
BytesRead : longint;
|
|
BytesToRead : longint;
|
|
i : integer;
|
|
Percent : integer;
|
|
Buffer : PAnsiChar;
|
|
BufferCount : integer;
|
|
begin
|
|
{check for dumb programming mistakes: this routine should only be
|
|
called if there are less than 4 bytes unused in the buffer}
|
|
Assert((FBufEnd - FBufPos) < sizeof(longint),
|
|
'TAbDfInBitStream.ibsFillBuffer: the buffer should be almost empty');
|
|
|
|
{if there are still 1, 2, or three bytes unused, move them to the
|
|
front of the buffer}
|
|
Buffer := FBuffer;
|
|
while (FBufPos <> FBufEnd) do begin
|
|
Buffer^ := FBufPos^;
|
|
inc(FBufPos);
|
|
inc(Buffer);
|
|
end;
|
|
|
|
{fill the buffer}
|
|
BytesToRead := BitStreamBufferSize - (Buffer - FBuffer);
|
|
BytesRead := FStream.Read(Buffer^, BytesToRead);
|
|
|
|
{reset the internal pointers}
|
|
FBufPos := FBuffer;
|
|
FBufEnd := Buffer + BytesRead;
|
|
BufferCount := FBufEnd - FBuffer;
|
|
|
|
{if, as a result of the read, no data is in the buffer, return
|
|
false; the caller will decide what to do about the problem}
|
|
if (BufferCount = 0) then
|
|
Result := false
|
|
|
|
{otherwise there is data to be processed}
|
|
else begin
|
|
Result := true;
|
|
|
|
{if we didn't read anything from the stream, we need to make sure
|
|
that enough buffer is zeroed out so that reading longint values
|
|
don't produce (dreadfully) bogus values}
|
|
if (BytesRead = 0) and ((BufferCount mod 4) <> 0) then begin
|
|
FFakeCount := 4 - (BufferCount mod 4);
|
|
for i := 0 to pred(FFakeCount) do begin
|
|
FBufEnd^ := #0;
|
|
inc(FBufEnd);
|
|
end;
|
|
end;
|
|
|
|
{fire the progress event}
|
|
if Assigned(FOnProgress) then begin
|
|
inc(FByteCount, BytesRead);
|
|
Percent := Round((100.0 * FByteCount) / FStreamSize);
|
|
FOnProgress(Percent);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TAbDfInBitStream.PeekBits(aCount : integer) : integer;
|
|
var
|
|
BitsToGo : integer;
|
|
TempBuffer : integer;
|
|
begin
|
|
{check that aCount is in the correct range 1..32}
|
|
Assert((0 <= aCount) and (aCount <= 32),
|
|
'TAbDfInBitStream.PeekBits: count of bits must be between 1 and 32 inclusive');
|
|
|
|
{if we have more than enough bits in our bit buffer, return as many
|
|
as needed}
|
|
if (aCount <= FBitsLeft) then
|
|
Result := FBitBuffer and AbExtractMask[aCount]
|
|
|
|
{otherwise we shall have to read another integer out of the buffer
|
|
to satisfy the request; note that this will fill the stream buffer
|
|
if required}
|
|
else begin
|
|
BitsToGo := aCount - FBitsLeft;
|
|
Result := FBitBuffer;
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
if not ibsFillBuffer then
|
|
TempBuffer := 0
|
|
else
|
|
TempBuffer := PAb32bit(FBufPos)^
|
|
else
|
|
TempBuffer := PAb32bit(FBufPos)^;
|
|
Result := Result +
|
|
((TempBuffer and AbExtractMask[BitsToGo]) shl FBitsLeft);
|
|
end;
|
|
|
|
{$IFOPT C+}
|
|
{save the number of bits peeked for an assertion check later}
|
|
FPeekCount := aCount;
|
|
{$ENDIF}
|
|
end;
|
|
{--------}
|
|
function TAbDfInBitStream.PeekMoreBits(aCount : integer) : integer;
|
|
var
|
|
BitsToGo : integer;
|
|
TempBuffer : integer;
|
|
begin
|
|
BitsToGo := aCount - FBitsLeft;
|
|
Result := FBitBuffer;
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
if not ibsFillBuffer then
|
|
TempBuffer := 0
|
|
else
|
|
TempBuffer := PAb32bit(FBufPos)^
|
|
else
|
|
TempBuffer := PAb32bit(FBufPos)^;
|
|
Result := Result +
|
|
((TempBuffer and AbExtractMask[BitsToGo]) shl FBitsLeft);
|
|
end;
|
|
{--------}
|
|
function TAbDfInBitStream.ReadBit : boolean;
|
|
begin
|
|
if (FBitsLeft = 0) then begin
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
if not ibsFillBuffer then
|
|
raise EAbInternalInflateError.Create(
|
|
'no more compressed data in stream [TAbDfInBitStream.ReadBit]');
|
|
FBitBuffer := PAb32bit(FBufPos)^;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
FBitsLeft := 32;
|
|
end;
|
|
Result := Odd(FBitBuffer);
|
|
FBitBuffer := FBitBuffer shr 1;
|
|
dec(FBitsLeft);
|
|
end;
|
|
{--------}
|
|
function TAbDfInBitStream.ReadBits(aCount : integer) : integer;
|
|
var
|
|
BitsToGo : integer;
|
|
begin
|
|
{aCount comes from a (possibly corrupt) stream, so check that it is
|
|
the correct range, 1..16}
|
|
if (aCount <= 0) or (aCount > 16) then
|
|
raise EAbInternalInflateError.Create(
|
|
'count of bits must be between 1 and 16 inclusive [TAbDfInBitStream.ReadBits]');
|
|
|
|
{if we have more than enough bits in our bit buffer, return as many
|
|
as needed, and update the bitbuffer and the number of bits left}
|
|
if (aCount <= FBitsLeft) then begin
|
|
Result := FBitBuffer and AbExtractMask[aCount];
|
|
FBitBuffer := FBitBuffer shr aCount;
|
|
dec(FBitsLeft, aCount);
|
|
end
|
|
|
|
{if we have exactly enough bits in our bit buffer, return them all,
|
|
and update the bitbuffer and the number of bits left}
|
|
else if (aCount = FBitsLeft) then begin
|
|
Result := FBitBuffer;
|
|
FBitBuffer := 0;
|
|
FBitsLeft := 0;
|
|
end
|
|
|
|
{otherwise we shall have to read another integer out of the buffer
|
|
to satisfy the request}
|
|
else begin
|
|
BitsToGo := aCount - FBitsLeft;
|
|
Result := FBitBuffer;
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
if not ibsFillBuffer then
|
|
raise EAbInternalInflateError.Create(
|
|
'no more compressed data in stream [TAbDfInBitStream.ReadBits]');
|
|
FBitBuffer := PAb32bit(FBufPos)^;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
Result := Result +
|
|
((FBitBuffer and AbExtractMask[BitsToGo]) shl FBitsLeft);
|
|
FBitBuffer := FBitBuffer shr BitsToGo;
|
|
FBitsLeft := 32 - BitsToGo;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfInBitStream.ReadBuffer(var aBuffer; aCount : integer);
|
|
var
|
|
i : integer;
|
|
Buffer : PAnsiChar;
|
|
BytesToRead : integer;
|
|
BytesInBuffer : integer;
|
|
begin
|
|
{this method is designed to read a set of bytes and this can only be
|
|
done if the stream has been byte aligned--if it isn't, it's a
|
|
programming error}
|
|
Assert((FBitsLeft mod 8) = 0,
|
|
'TAbDfInBitStream.ReadBuffer. cannot read a buffer unless the stream is byte-aligned');
|
|
|
|
{get the address of the user buffer as a PChar: easier arithmetic}
|
|
Buffer := @aBuffer;
|
|
|
|
{if we have some bits left in the bit buffer, we need to copy those
|
|
first}
|
|
if (FBitsLeft > 0) then begin
|
|
BytesToRead := FBitsLeft div 8;
|
|
for i := 0 to pred(BytesToRead) do begin
|
|
Buffer^ := AnsiChar(FBitBuffer and $FF);
|
|
inc(Buffer);
|
|
FBitBuffer := FBitBuffer shr 8;
|
|
end;
|
|
{calculate the count of bytes still to read}
|
|
dec(aCount, BytesToRead);
|
|
end;
|
|
|
|
{calculate the number of bytes to copy}
|
|
BytesInBuffer := FBufEnd - FBufPos;
|
|
if (aCount <= BytesInBuffer) then
|
|
BytesToRead := aCount
|
|
else
|
|
BytesToRead := BytesInBuffer;
|
|
|
|
{copy the data from our buffer to the user buffer}
|
|
Move(FBufPos^, Buffer^, BytesToRead);
|
|
|
|
{update variables}
|
|
dec(aCount, BytesToRead);
|
|
inc(FBufPos, BytesToRead);
|
|
|
|
{while there is still data to copy, keep on filling our internal
|
|
buffer and copy it to the user buffer}
|
|
while (aCount <> 0) do begin
|
|
|
|
{increment the user buffer pointer past the data just copied}
|
|
inc(Buffer, BytesToRead);
|
|
|
|
{fill our buffer}
|
|
if not ibsFillBuffer then
|
|
raise EAbInternalInflateError.Create(
|
|
'no more compressed data in stream [TAbDfInBitStream.ReadBuffer]');
|
|
|
|
{calculate the number of bytes to copy}
|
|
BytesInBuffer := FBufEnd - FBufPos;
|
|
if (aCount <= BytesInBuffer) then
|
|
BytesToRead := aCount
|
|
else
|
|
BytesToRead := BytesInBuffer;
|
|
|
|
{copy the data from our buffer to the user buffer}
|
|
Move(FBufPos^, Buffer^, BytesToRead);
|
|
|
|
{update variables}
|
|
dec(aCount, BytesToRead);
|
|
inc(FBufPos, BytesToRead);
|
|
end;
|
|
|
|
{now we've copied everything over, reset the bit variables: they're
|
|
empty and need refilling}
|
|
FBitBuffer := 0;
|
|
FBitsLeft := 0;
|
|
end;
|
|
{====================================================================}
|
|
|
|
|
|
{===TAbDfOutBitStream================================================}
|
|
constructor TAbDfOutBitStream.Create(aStream : TStream);
|
|
begin
|
|
{protect against dumb programming mistakes}
|
|
Assert(aStream <> nil,
|
|
'TAbDfOutBitStream.Create: Cannot create a bit stream wrapping a nil stream');
|
|
|
|
{create the ancestor}
|
|
inherited Create;
|
|
|
|
{save the stream instance, allocate the buffer}
|
|
FStream := aStream;
|
|
GetMem(FBuffer, BitStreamBufferSize);
|
|
FBufEnd := FBuffer + BitStreamBufferSize;
|
|
FBufPos := FBuffer;
|
|
end;
|
|
{--------}
|
|
destructor TAbDfOutBitStream.Destroy;
|
|
var
|
|
i : integer;
|
|
begin
|
|
{if the buffer was allocated...}
|
|
if (FBuffer <> nil) then begin
|
|
|
|
{if there are still some bits in the bit buffer...}
|
|
if (FBitsUsed <> 0) then begin
|
|
|
|
{pad the bit buffer to a byte boundary}
|
|
AlignToByte;
|
|
|
|
{empty the main buffer if there isn't enough room to copy over
|
|
the 1 to 4 bytes in the bit buffer}
|
|
if ((FBufEnd - FBufPos) < FBitsUsed div 8) then
|
|
obsEmptyBuffer;
|
|
|
|
{flush the bit buffer}
|
|
for i := 1 to (FBitsUsed div 8) do begin
|
|
FBufPos^ := AnsiChar(FBitBuffer);
|
|
FBitBuffer := FBitBuffer shr 8;
|
|
inc(FBufPos);
|
|
end;
|
|
end;
|
|
|
|
{if there is some data in the main buffer, empty it}
|
|
if (FBufPos <> FBuffer) then
|
|
obsEmptyBuffer;
|
|
|
|
{free the buffer}
|
|
FreeMem(FBuffer);
|
|
end;
|
|
|
|
{destroy the ancestor}
|
|
inherited Destroy;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfOutBitStream.AlignToByte;
|
|
begin
|
|
{round up the number of bits used to the nearest 8}
|
|
FBitsUsed := (FBitsUsed + 7) and $F8;
|
|
|
|
{if the bit buffer is now full, flush it to the main buffer}
|
|
if (FBitsUsed = 32) then begin
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
obsEmptyBuffer;
|
|
PAb32bit(FBufPos)^ := FBitBuffer;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
FBitBuffer := 0;
|
|
FBitsUsed := 0;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfOutBitStream.obsEmptyBuffer;
|
|
var
|
|
ByteCount : integer;
|
|
BytesWritten : longint;
|
|
begin
|
|
{empty the buffer}
|
|
ByteCount := FBufPos - FBuffer;
|
|
BytesWritten := FStream.Write(FBuffer^, ByteCount);
|
|
|
|
{if we couldn't write the correct number of bytes, it's an error}
|
|
if (BytesWritten <> ByteCount) then
|
|
raise EAbInternalDeflateError.Create(
|
|
'could not write to the output stream [TAbDfInBitStream.obsEmptyBuffer]');
|
|
|
|
{reset the pointers}
|
|
FBufPos := FBuffer;
|
|
end;
|
|
{--------}
|
|
function TAbDfOutBitStream.Position : longint;
|
|
begin
|
|
Assert(false,
|
|
'TAbDfOutBitStream.Position: not implemented yet!');
|
|
Result := -1;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfOutBitStream.WriteBit(aBit : boolean);
|
|
begin
|
|
{only set the corresponding bit in the bit buffer if the passed bit
|
|
is set (the bit buffer is set to zero when emptied, so we don't
|
|
actually have to record clear bits)}
|
|
if aBit then
|
|
FBitBuffer := FBitBuffer or (1 shl FBitsUsed);
|
|
|
|
{we've now got one more bit}
|
|
inc(FBitsUsed);
|
|
|
|
{if the bit buffer is now full, flush it to the main buffer}
|
|
if (FBitsUsed = 32) then begin
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
obsEmptyBuffer;
|
|
PAb32bit(FBufPos)^ := FBitBuffer;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
FBitBuffer := 0;
|
|
FBitsUsed := 0;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfOutBitStream.WriteBits(aBits : integer; aCount : integer);
|
|
begin
|
|
{protect against programming mistakes...}
|
|
{..the count should be in the range 1 to 16 (BTW, the latter is only
|
|
used once: Deflate64 with length symbol 258)}
|
|
Assert((0 < aCount) and (aCount <= 16),
|
|
'TAbDfOutBitStream.WriteBits: aCount should be from 1 to 16');
|
|
{..there shouldn't be more than aCount bits}
|
|
Assert((aBits shr aCount) = 0,
|
|
'TAbDfOutBitStream.WriteBits: aBits has more than aCount bits');
|
|
|
|
{copy as many bits as we can to the bit buffer}
|
|
FBitBuffer := FBitBuffer or (aBits shl FBitsUsed);
|
|
|
|
{increment the number of bits used}
|
|
inc(FBitsUsed, aCount);
|
|
|
|
{if we've overshot...}
|
|
if (FBitsUsed >= 32) then begin
|
|
|
|
{the bit buffer is now full, so flush it}
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
obsEmptyBuffer;
|
|
PAb32bit(FBufPos)^ := FBitBuffer;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
|
|
{patch up the bit buffer and the number of bits used}
|
|
dec(FBitsUsed, 32);
|
|
FBitBuffer := aBits shr (aCount - FBitsUsed);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfOutBitStream.WriteBuffer(var aBuffer; aCount : integer);
|
|
var
|
|
Buffer : PAnsiChar;
|
|
BytesToCopy : integer;
|
|
begin
|
|
{guard against dumb programming errors: we must be byte aligned}
|
|
Assert((FBitsUsed and $7) = 0,
|
|
'TAbDfOutBitStream.WriteBuffer: must be byte aligned');
|
|
|
|
{use the user buffer as a PChar}
|
|
Buffer := @aBuffer;
|
|
|
|
{flush the bit buffer to the underlying stream}
|
|
while (FBitsUsed <> 0) do begin
|
|
if (FBufEnd = FBufPos) then
|
|
obsEmptyBuffer;
|
|
FBufPos^ := AnsiChar(FBitBuffer and $FF);
|
|
inc(FBufPos);
|
|
FBitBuffer := FBitBuffer shr 8;
|
|
dec(FBitsUsed, 8);
|
|
end;
|
|
|
|
{copy over the data to the underlying stream}
|
|
BytesToCopy := FBufEnd - FBufPos;
|
|
if (BytesToCopy > aCount) then
|
|
BytesToCopy := aCount;
|
|
Move(Buffer^, FBufPos^, BytesToCopy);
|
|
inc(FBufPos, BytesToCopy);
|
|
dec(aCount, BytesToCopy);
|
|
while (aCount <> 0) do begin
|
|
inc(Buffer, BytesToCopy);
|
|
obsEmptyBuffer;
|
|
BytesToCopy := FBufEnd - FBufPos;
|
|
if (BytesToCopy > aCount) then
|
|
BytesToCopy := aCount;
|
|
Move(Buffer^, FBufPos^, BytesToCopy);
|
|
inc(FBufPos, BytesToCopy);
|
|
dec(aCount, BytesToCopy);
|
|
end;
|
|
|
|
{finish with a flushed buffer}
|
|
obsEmptyBuffer;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfOutBitStream.WriteMoreBits(aBits : integer; aCount : integer);
|
|
begin
|
|
{the bit buffer is now full, so flush it}
|
|
if ((FBufEnd - FBufPos) < sizeof(TAb32bit)) then
|
|
obsEmptyBuffer;
|
|
PAb32bit(FBufPos)^ := FBitBuffer;
|
|
inc(FBufPos, sizeof(TAb32bit));
|
|
|
|
{patch up the bit buffer and the number of bits used}
|
|
dec(FBitsUsed, 32);
|
|
FBitBuffer := aBits shr (aCount - FBitsUsed);
|
|
end;
|
|
{====================================================================}
|
|
|
|
|
|
{===TAbDfLZStream====================================================}
|
|
const
|
|
{Implementation note: this stream size has been chosen so that if
|
|
the data must be stored, a block size of about 64K will result}
|
|
StreamSize = 160 * 1024;
|
|
type
|
|
PWord = ^word;
|
|
{--------}
|
|
constructor TAbDfLZStream.Create(aSlideWin : TAbDfInputWindow;
|
|
aUseDeflate64 : boolean;
|
|
aLog : TAbLogger);
|
|
begin
|
|
{create the ancestor}
|
|
inherited Create;
|
|
|
|
{save the sliding window and the logger}
|
|
FSlideWin := aSlideWin;
|
|
FUseDeflate64 := aUseDeflate64;
|
|
FLog := aLog;
|
|
|
|
{create the buckets}
|
|
New(FDistBuckets);
|
|
New(FLitBuckets);
|
|
|
|
{create the memory stream, allocate its buffer, position at start}
|
|
GetMem(FStream, StreamSize);
|
|
Clear;
|
|
end;
|
|
{--------}
|
|
destructor TAbDfLZStream.Destroy;
|
|
begin
|
|
{free the buckets}
|
|
if (FDistBuckets <> nil) then
|
|
Dispose(FDistBuckets);
|
|
if (FLitBuckets <> nil) then
|
|
Dispose(FLitBuckets);
|
|
|
|
{free the memory stream}
|
|
if (FStream <> nil) then
|
|
FreeMem(FStream);
|
|
|
|
{destroy the ancestor}
|
|
inherited Destroy;
|
|
end;
|
|
{--------}
|
|
{$IFDEF UseLogging}
|
|
procedure AddLenDistToLog(aLog : TAbLogger;
|
|
aPosn : longint;
|
|
aLen : integer;
|
|
aDist : integer;
|
|
aOverLap : boolean);
|
|
begin
|
|
{NOTE the reason for this separate routine is to avoid string
|
|
allocations and try..finally blocks in the main method: an
|
|
optimization issue}
|
|
if aOverLap then
|
|
aLog.WriteLine(Format('%8x Len: %-3d, Dist: %-5d **overlap**',
|
|
[aPosn, aLen, aDist]))
|
|
else
|
|
aLog.WriteLine(Format('%8x Len: %-3d, Dist: %-5d',
|
|
[aPosn, aLen, aDist]));
|
|
end;
|
|
{$ENDIF}
|
|
{--------}
|
|
function TAbDfLZStream.AddLenDist(aLen : integer; aDist : integer)
|
|
: boolean;
|
|
var
|
|
LenSymbol : integer;
|
|
DistSymbol : integer;
|
|
CurPos : PAnsiChar;
|
|
begin
|
|
{$IFDEF UseLogging}
|
|
{log it}
|
|
if (FLog <> nil) then begin
|
|
if (aLen > aDist) then
|
|
AddLenDistToLog(FLog, FSWPos, aLen, aDist, true)
|
|
else
|
|
AddLenDistToLog(FLog, FSWPos, aLen, aDist, false);
|
|
inc(FSWPos, aLen);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{write a length/distance record to the stream}
|
|
CurPos := FCurPos;
|
|
CurPos^ := AnsiChar(false);
|
|
inc(CurPos);
|
|
PWord(CurPos)^ := word(aLen - 1);
|
|
inc(CurPos, sizeof(word));
|
|
PWord(CurPos)^ := word(aDist - 1);
|
|
inc(CurPos, sizeof(word));
|
|
FCurPos := CurPos;
|
|
|
|
{increment the various counters}
|
|
inc(FDistCount);
|
|
inc(FStoredSize, aLen);
|
|
|
|
{convert the length and distance to their symbols}
|
|
{$IFOPT C+} {if Assertions are on}
|
|
LenSymbol := AbSymbolTranslator.TranslateLength(aLen);
|
|
DistSymbol := AbSymbolTranslator.TranslateDistance(aDist);
|
|
{$ELSE}
|
|
if (3 <= aLen) and (aLen <= 258) then
|
|
LenSymbol := AbSymbolTranslator.LenSymbols[aLen-3] + 257
|
|
else
|
|
LenSymbol := 285;
|
|
if (aDist <= 256) then
|
|
DistSymbol := AbSymbolTranslator.ShortDistSymbols[aDist - 1]
|
|
else if (aDist <= 32768) then
|
|
DistSymbol := AbSymbolTranslator.MediumDistSymbols[((aDist - 1) div 128) - 2]
|
|
else
|
|
DistSymbol := AbSymbolTranslator.LongDistSymbols[((aDist - 1) div 16384) - 2];
|
|
{$ENDIF}
|
|
|
|
{increment the buckets}
|
|
inc(FLitBuckets^[LenSymbol]);
|
|
inc(FDistBuckets^[DistSymbol]);
|
|
|
|
{return whether the stream is full and needs encoding}
|
|
Result := lzsIsFull;
|
|
end;
|
|
{--------}
|
|
{$IFDEF UseLogging}
|
|
procedure AddLiteralToLog(aLog : TAbLogger;
|
|
aPosn : longint;
|
|
aCh : AnsiChar);
|
|
begin
|
|
{NOTE the reason for this separate routine is to avoid string
|
|
allocations and try..finally blocks in the main method: an
|
|
optimization issue}
|
|
if (' ' < aCh) and (aCh <= '~') then
|
|
aLog.WriteLine(Format('%8x Char: %3d $%2x [%s]', [aPosn, ord(aCh), ord(aCh), aCh]))
|
|
else
|
|
aLog.WriteLine(Format('%8x Char: %3d $%2x', [aPosn, ord(aCh), ord(aCh)]));
|
|
end;
|
|
{$ENDIF}
|
|
{--------}
|
|
function TAbDfLZStream.AddLiteral(aCh : AnsiChar) : boolean;
|
|
var
|
|
CurPos : PAnsiChar;
|
|
begin
|
|
{$IFDEF UseLogging}
|
|
{log it}
|
|
if (FLog <> nil) then begin
|
|
AddLiteralToLog(FLog, FSWPos, aCh);
|
|
inc(FSWPos);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{write a literal to the internal stream}
|
|
CurPos := FCurPos;
|
|
CurPos^ := AnsiChar(true);
|
|
inc(CurPos);
|
|
CurPos^ := aCh;
|
|
inc(CurPos);
|
|
FCurPos := CurPos;
|
|
|
|
{increment the various counters}
|
|
inc(FLitCount);
|
|
inc(FLitBuckets^[byte(aCh)]);
|
|
inc(FStoredSize);
|
|
|
|
{return whether the stream is full and needs encoding}
|
|
Result := lzsIsFull;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfLZStream.Clear;
|
|
begin
|
|
{position the stream at the start}
|
|
Rewind;
|
|
|
|
{reset all variables}
|
|
FStrmEnd := nil;
|
|
FLitCount := 0;
|
|
FDistCount := 0;
|
|
FStartOfs := FSlideWin.Position;
|
|
FStoredSize := 0;
|
|
{$IFDEF UseLogging}
|
|
FSWPos := FStartOfs;
|
|
{$ENDIF}
|
|
|
|
{reset the buckets}
|
|
FillChar(FLitBuckets^, sizeof(FLitBuckets^), 0);
|
|
FLitBuckets^[256] := 1; { end-of-block marker: it's always there...}
|
|
FillChar(FDistBuckets^, sizeof(FDistBuckets^), 0);
|
|
end;
|
|
{--------}
|
|
procedure TAbDfLZStream.Encode(aBitStrm : TAbDfOutBitStream;
|
|
aLitTree : TAbDfDecodeHuffmanTree;
|
|
aDistTree : TAbDfDecodeHuffmanTree;
|
|
aUseDeflate64 : boolean);
|
|
var
|
|
Len : integer;
|
|
Dist : integer;
|
|
Symbol : integer;
|
|
CurPos : PAnsiChar;
|
|
StrmEnd : PAnsiChar;
|
|
Code : longint;
|
|
ExtraBits : longint;
|
|
begin
|
|
{rewind the LZ77 stream}
|
|
Rewind;
|
|
|
|
{for speed use local variables}
|
|
CurPos := FCurPos;
|
|
StrmEnd := FStrmEnd;
|
|
|
|
{while there are still items in the stream...}
|
|
while (CurPos < StrmEnd) do begin
|
|
|
|
{if the next item is a literal...}
|
|
if boolean(CurPos^) then begin
|
|
|
|
{encode the literal character as a symbol}
|
|
inc(CurPos);
|
|
{$IFOPT C+} {if Assertions are on}
|
|
Code := aLitTree.Encode(byte(CurPos^));
|
|
{$ELSE}
|
|
Code := aLitTree.Encodes^[byte(CurPos^)];
|
|
{$ENDIF}
|
|
inc(CurPos);
|
|
|
|
{write the code out to the bit stream}
|
|
{$IFOPT C+}
|
|
aBitStrm.WriteBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
{$ELSE}
|
|
with aBitStrm do begin
|
|
BitBuffer := BitBuffer or ((Code and $FFFF) shl BitsUsed);
|
|
BitsUsed := BitsUsed + ((Code shr 16) and $FF);
|
|
if (BitsUsed >= 32) then
|
|
WriteMoreBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
end;
|
|
{$ENDIF}
|
|
end
|
|
|
|
{otherwise it's a length/distance pair}
|
|
else begin
|
|
|
|
{DO THE LENGTH FIRST-------------------------------------------}
|
|
{get the length from the stream}
|
|
inc(CurPos);
|
|
Len := integer(PWord(CurPos)^) + 1;
|
|
inc(CurPos, sizeof(word));
|
|
|
|
{translate it to a symbol and convert that to a code using the
|
|
literal/length huffman tree}
|
|
{$IFOPT C+} {if Assertions are on}
|
|
Symbol := AbSymbolTranslator.TranslateLength(Len);
|
|
Code := aLitTree.Encode(Symbol);
|
|
{$ELSE}
|
|
if (3 <= Len) and (Len <= 258) then
|
|
Symbol := AbSymbolTranslator.LenSymbols[Len-3] + 257
|
|
else
|
|
Symbol := 285;
|
|
Code := aLitTree.Encodes^[Symbol];
|
|
{$ENDIF}
|
|
|
|
{output the length code}
|
|
{$IFOPT C+}
|
|
aBitStrm.WriteBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
{$ELSE}
|
|
with aBitStrm do begin
|
|
BitBuffer := BitBuffer or ((Code and $FFFF) shl BitsUsed);
|
|
BitsUsed := BitsUsed + ((Code shr 16) and $FF);
|
|
if (BitsUsed >= 32) then
|
|
WriteMoreBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{if the length symbol were 285, its definition changes from Deflate
|
|
to Deflate64, so make it a special case: for Deflate there are no
|
|
extra bits, for Deflate64 output the (length - 3) as 16 bits}
|
|
if (Symbol = 285) then begin
|
|
if aUseDeflate64 then begin
|
|
{$IFOPT C+}
|
|
aBitStrm.WriteBits(Len - 3, 16);
|
|
{$ELSE}
|
|
with aBitStrm do begin
|
|
BitBuffer := BitBuffer or ((Len - 3) shl BitsUsed);
|
|
BitsUsed := BitsUsed + 16;
|
|
if (BitsUsed >= 32) then
|
|
WriteMoreBits(Len - 3, 16);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
end
|
|
|
|
{otherwise if there are extra bits to be output for this length,
|
|
calculate them and output them}
|
|
else begin
|
|
ExtraBits := Code shr 24;
|
|
if (ExtraBits <> 0) then begin
|
|
{$IFOPT C+}
|
|
aBitStrm.WriteBits((Len - dfc_LengthBase[Symbol - 257]),
|
|
ExtraBits);
|
|
{$ELSE}
|
|
with aBitStrm do begin
|
|
BitBuffer := BitBuffer or
|
|
((Len - dfc_LengthBase[Symbol - 257]) shl BitsUsed);
|
|
BitsUsed := BitsUsed + ExtraBits;
|
|
if (BitsUsed >= 32) then
|
|
WriteMoreBits((Len - dfc_LengthBase[Symbol - 257]),
|
|
ExtraBits);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
{DO THE DISTANCE NEXT------------------------------------------}
|
|
{get the distance from the stream}
|
|
Dist := integer(PWord(CurPos)^) + 1;
|
|
inc(CurPos, sizeof(word));
|
|
|
|
{translate it to a symbol and convert that to a code using the
|
|
distance huffman tree}
|
|
{$IFOPT C+} {if Assertions are on}
|
|
Symbol := AbSymbolTranslator.TranslateDistance(Dist);
|
|
Assert(aUseDeflate64 or (Symbol < 30),
|
|
'TAbDfLZStream.Encode: a Deflate64 distance symbol has been generated for Deflate');
|
|
Code := aDistTree.Encode(Symbol);
|
|
{$ELSE}
|
|
if (Dist <= 256) then
|
|
Symbol := AbSymbolTranslator.ShortDistSymbols[Dist - 1]
|
|
else if (Dist <= 32768) then
|
|
Symbol := AbSymbolTranslator.MediumDistSymbols[((Dist - 1) div 128) - 2]
|
|
else
|
|
Symbol := AbSymbolTranslator.LongDistSymbols[((Dist - 1) div 16384) - 2];
|
|
Code := aDistTree.Encodes^[Symbol];
|
|
{$ENDIF}
|
|
|
|
{output the distance code}
|
|
{$IFOPT C+}
|
|
aBitStrm.WriteBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
{$ELSE}
|
|
with aBitStrm do begin
|
|
BitBuffer := BitBuffer or ((Code and $FFFF) shl BitsUsed);
|
|
BitsUsed := BitsUsed + ((Code shr 16) and $FF);
|
|
if (BitsUsed >= 32) then
|
|
WriteMoreBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{if there are extra bits to be output for this distance, calculate
|
|
them and output them}
|
|
ExtraBits := Code shr 24;
|
|
if (ExtraBits <> 0) then begin
|
|
{$IFOPT C+}
|
|
aBitStrm.WriteBits((Dist - dfc_DistanceBase[Symbol]),
|
|
ExtraBits);
|
|
{$ELSE}
|
|
with aBitStrm do begin
|
|
BitBuffer := BitBuffer or
|
|
((Dist - dfc_DistanceBase[Symbol]) shl BitsUsed);
|
|
BitsUsed := BitsUsed + ExtraBits;
|
|
if (BitsUsed >= 32) then
|
|
WriteMoreBits((Dist - dfc_DistanceBase[Symbol]),
|
|
ExtraBits);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{clear the stream; ready for some more items}
|
|
{ Clear;}
|
|
end;
|
|
{--------}
|
|
function TAbDfLZStream.lzsGetApproxSize : LongWord;
|
|
var
|
|
i : integer;
|
|
begin
|
|
{note: calculates an approximate compressed size without taking too
|
|
long about it. The average encoded bit length for literals
|
|
and lengths is assumed to be 8. Distances are assumed to
|
|
follow the static tree definition (ie, 5 bits per distance,
|
|
plus any extra bits).
|
|
There are FLitCount literals, FDistCount lengths, and
|
|
FDistCount distances}
|
|
Result := (13 * FDistCount) + (8 * FLitCount);
|
|
for i := 4 to 31 do
|
|
inc(Result, FDistBuckets^[i] * dfc_DistExtraBits[i]);
|
|
Result := Result div 8;
|
|
end;
|
|
{--------}
|
|
function TAbDfLZStream.lzsGetStaticSize : integer;
|
|
var
|
|
i : integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to 143 do
|
|
inc(Result, FLitBuckets^[i] * 8);
|
|
for i := 144 to 255 do
|
|
inc(Result, FLitBuckets^[i] * 9);
|
|
inc(Result, FLitBuckets^[256] * 7);
|
|
for i := 257 to 279 do
|
|
inc(Result, FLitBuckets^[i] *
|
|
(7 + dfc_LitExtraBits[i - dfc_LitExtraOffset]));
|
|
for i := 280 to 284 do
|
|
inc(Result, FLitBuckets^[i] *
|
|
(8 + dfc_LitExtraBits[i - dfc_LitExtraOffset]));
|
|
if FUseDeflate64 then
|
|
inc(Result, FLitBuckets^[285] * (8 + 16))
|
|
else
|
|
inc(Result, FLitBuckets^[285] * 8);
|
|
|
|
for i := 0 to 31 do
|
|
inc(Result, FDistBuckets^[i] * (5 + dfc_DistExtraBits[i]));
|
|
end;
|
|
{--------}
|
|
function TAbDfLZStream.lzsGetStoredSize : integer;
|
|
begin
|
|
Result := FStoredSize;
|
|
{Result := FSlideWin.Position - FStartOfs;}
|
|
end;
|
|
{--------}
|
|
function TAbDfLZStream.lzsIsFull : boolean;
|
|
begin
|
|
{if the number of hits on the (eventual) literal tree is a multiple
|
|
of 8192, the stream is full if the majority were straight literals
|
|
and we're getting approx 50% compression}
|
|
if (((FLitCount + FDistCount) and $1FFF) = 0) then begin
|
|
Result := (FDistCount < FLitCount) and
|
|
(lzsGetApproxSize < (FStoredSize div 2));
|
|
if Result then
|
|
Exit;
|
|
end;
|
|
|
|
{otherwise the stream is full if the number of hits on the literal
|
|
tree or on the distance tree is 32768}
|
|
{ Result := (FCurPos - FStream) > (StreamSIze - 100);}
|
|
Result := (FDistCount >= 32768) or
|
|
((FLitCount + FDistCount) >= 32768);
|
|
end;
|
|
{--------}
|
|
procedure TAbDfLZStream.ReadStoredBuffer(var aBuffer; aCount : integer);
|
|
begin
|
|
FSlideWin.ReadBuffer(aBuffer, aCount, FStartOfs);
|
|
inc(FStartOfs, aCount);
|
|
end;
|
|
{--------}
|
|
procedure TAbDfLZStream.Rewind;
|
|
begin
|
|
{position the stream at the beginning}
|
|
FStrmEnd := FCurPos;
|
|
FCurPos := FStream;
|
|
end;
|
|
{====================================================================}
|
|
|
|
|
|
{===TAbDfCodeLenStream===============================================}
|
|
constructor TAbDfCodeLenStream.Create(aLog : TAbLogger);
|
|
begin
|
|
{create the ancestor}
|
|
inherited Create;
|
|
|
|
{allocate the stream (to contain all literals and distances and
|
|
possible extra data}
|
|
GetMem(FStream, (285 + 32) * 2);
|
|
FPosition := FStream;
|
|
|
|
{allocate the buckets}
|
|
FBuckets := AllocMem(sizeof(TAbDfCodeLenBuckets));
|
|
end;
|
|
{--------}
|
|
destructor TAbDfCodeLenStream.Destroy;
|
|
begin
|
|
{free the stream}
|
|
if (FStream <> nil) then
|
|
FreeMem(FStream);
|
|
|
|
{free the buckets}
|
|
if (FBuckets <> nil) then
|
|
Dispose(FBuckets);
|
|
|
|
{destroy the ancestor}
|
|
inherited Destroy;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfCodeLenStream.Build(const aCodeLens : array of integer;
|
|
aCount : integer);
|
|
var
|
|
i : integer;
|
|
State : (ScanStart, ScanNormal, Got2nd, Got3rd);
|
|
Count : integer;
|
|
ThisCount : integer;
|
|
CodeLen : integer;
|
|
PrevCodeLen : integer;
|
|
CurPos : PAnsiChar;
|
|
Buckets : PAbDfCodeLenBuckets;
|
|
begin
|
|
{start the automaton}
|
|
State := ScanStart;
|
|
CurPos := FStream;
|
|
Buckets := FBuckets;
|
|
Count := 0;
|
|
PrevCodeLen := 0;
|
|
|
|
{for all the codelengths in the array (plus a fake one at the end to
|
|
ensure all codeslengths are counted)...}
|
|
for i := 0 to aCount do begin
|
|
|
|
{get the current codelength}
|
|
if (i = aCount) then
|
|
CodeLen := -1
|
|
else
|
|
CodeLen := aCodeLens[i];
|
|
|
|
{switch based on the state...}
|
|
case State of
|
|
ScanStart :
|
|
begin
|
|
PrevCodeLen := CodeLen;
|
|
State := ScanNormal;
|
|
end;
|
|
|
|
ScanNormal :
|
|
begin
|
|
{if the current code is the same as the previous, move to
|
|
the next state}
|
|
if (CodeLen = PrevCodeLen) then
|
|
State := Got2nd
|
|
|
|
{otherwise output the previous code}
|
|
else begin
|
|
CurPos^ := AnsiChar(PrevCodeLen);
|
|
inc(CurPos);
|
|
inc(Buckets^[PrevCodeLen]);
|
|
PrevCodeLen := CodeLen;
|
|
end;
|
|
end;
|
|
|
|
Got2nd :
|
|
begin
|
|
{if the current code is the same as the previous, move to
|
|
the next state; we now have three similar codes in a row}
|
|
if (CodeLen = PrevCodeLen) then begin
|
|
State := Got3rd;
|
|
Count := 3;
|
|
end
|
|
|
|
{otherwise output the previous two similar codes, move back
|
|
to the initial state}
|
|
else begin
|
|
CurPos^ := AnsiChar(PrevCodeLen);
|
|
inc(CurPos);
|
|
CurPos^ := AnsiChar(PrevCodeLen);
|
|
inc(CurPos);
|
|
inc(Buckets^[PrevCodeLen], 2);
|
|
PrevCodeLen := CodeLen;
|
|
State := ScanNormal;
|
|
end;
|
|
end;
|
|
|
|
Got3rd:
|
|
begin
|
|
{if the current code is the same as the previous, increment
|
|
the count of similar codes}
|
|
if (CodeLen = PrevCodeLen) then
|
|
inc(Count)
|
|
|
|
{otherwise we need to output the repeat values...}
|
|
else begin
|
|
|
|
{if the previous code were a zero code...}
|
|
if (PrevCodeLen = 0) then begin
|
|
|
|
{while there are zero codes to be output...}
|
|
while (Count <> 0) do begin
|
|
|
|
{if there are less than three zero codes, output them
|
|
individually}
|
|
if (Count < 3) then begin
|
|
while (Count <> 0) do begin
|
|
CurPos^ := #0;
|
|
inc(CurPos);
|
|
inc(Buckets^[0]);
|
|
dec(Count);
|
|
end;
|
|
end
|
|
|
|
{if there are less than 11 successive zero codes,
|
|
output a 17 code and the count of zeros}
|
|
else if (Count < 11) then begin
|
|
CurPos^ := #17;
|
|
inc(CurPos);
|
|
inc(Buckets^[17]);
|
|
CurPos^ := AnsiChar(Count - 3);
|
|
inc(CurPos);
|
|
Count := 0;
|
|
end
|
|
|
|
{otherwise output an 18 code and the count of zeros}
|
|
else begin
|
|
ThisCount := Count;
|
|
if (ThisCount > 138) then
|
|
ThisCount := 138;
|
|
CurPos^ := #18;
|
|
inc(CurPos);
|
|
inc(Buckets^[18]);
|
|
CurPos^ := AnsiChar(ThisCount - 11);
|
|
inc(CurPos);
|
|
dec(Count, ThisCount);
|
|
end;
|
|
end;
|
|
end
|
|
|
|
{otherwise the previous code was a non-zero code...}
|
|
else begin
|
|
|
|
{output the first code}
|
|
CurPos^ := AnsiChar(PrevCodeLen);
|
|
inc(CurPos);
|
|
inc(Buckets^[PrevCodeLen]);
|
|
dec(Count);
|
|
|
|
{while there are more codes to be output...}
|
|
while (Count <> 0) do begin
|
|
|
|
{if there are less than three codes, output them
|
|
individually}
|
|
if (Count < 3) then begin
|
|
while (Count <> 0) do begin
|
|
CurPos^ := AnsiChar(PrevCodeLen);
|
|
inc(CurPos);
|
|
inc(Buckets^[PrevCodeLen]);
|
|
dec(Count);
|
|
end;
|
|
end
|
|
|
|
{otherwise output an 16 code and the count}
|
|
else begin
|
|
ThisCount := Count;
|
|
if (ThisCount > 6) then
|
|
ThisCount := 6;
|
|
CurPos^ := #16;
|
|
inc(CurPos);
|
|
inc(Buckets^[16]);
|
|
CurPos^ := AnsiChar(ThisCount - 3);
|
|
inc(CurPos);
|
|
dec(Count, ThisCount);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{move back to the initial state}
|
|
PrevCodeLen := CodeLen;
|
|
State := ScanNormal;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{set the read position}
|
|
FStrmEnd := CurPos;
|
|
FPosition := FStream;
|
|
end;
|
|
{--------}
|
|
procedure TAbDfCodeLenStream.Encode(aBitStrm : TAbDfOutBitStream;
|
|
aTree : TAbDfDecodeHuffmanTree);
|
|
var
|
|
Symbol : integer;
|
|
ExtraData : integer;
|
|
Code : longint;
|
|
CurPos : PAnsiChar;
|
|
StrmEnd : PAnsiChar;
|
|
begin
|
|
{prepare for the loop}
|
|
CurPos := FPosition;
|
|
StrmEnd := FStrmEnd;
|
|
|
|
{while there are tokens in the stream...}
|
|
while (CurPos <> StrmEnd) do begin
|
|
|
|
{get the next symbol}
|
|
Symbol := ord(CurPos^);
|
|
inc(CurPos);
|
|
|
|
{if the symbol is 0..15, get the code and output it}
|
|
if (Symbol <= 15) then begin
|
|
{$IFOPT C+} {if Assertions are on}
|
|
Code := aTree.Encode(Symbol);
|
|
{$ELSE}
|
|
Code:= aTree.Encodes^[Symbol];
|
|
{$ENDIF}
|
|
aBitStrm.WriteBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
end
|
|
|
|
{otherwise the symbol is 16, 17, or 18}
|
|
else begin
|
|
{get the extra data}
|
|
ExtraData := ord(CurPos^);
|
|
inc(CurPos);
|
|
{get the code and output it}
|
|
{$IFOPT C+} {if Assertions are on}
|
|
Code := aTree.Encode(Symbol);
|
|
{$ELSE}
|
|
Code:= aTree.Encodes^[Symbol];
|
|
{$ENDIF}
|
|
aBitStrm.WriteBits(Code and $FFFF, (Code shr 16) and $FF);
|
|
if (Symbol = 16) then
|
|
aBitStrm.WriteBits(ExtraData, 2)
|
|
else if (Symbol = 17) then
|
|
aBitStrm.WriteBits(ExtraData, 3)
|
|
else {Symbol = 18}
|
|
aBitStrm.WriteBits(ExtraData, 7);
|
|
end;
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
|
|
end.
|