Files
kolmck/Addons/ULZMADecoder.pas

429 lines
14 KiB
ObjectPascal
Raw Normal View History

unit ULZMADecoder;
{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
interface
uses ULZMABase,UBitTreeDecoder,ULZOutWindow,URangeDecoder,KOL,ULZMACommon;
type PLZMALenDecoder = ^TLZMALenDecoder;
PLZMALiteralDecoder = ^TLZMALiteralDecoder;
PLZMADecoder = ^TLZMADecoder;
TLZMADecoder = object(TObj)
private
FOnProgress:TLZMAProgress;
procedure DoProgress(const Action:TLZMAProgressAction;const Value:integer);
public
m_OutWindow:PLZOutWindow;
m_RangeDecoder:PRangeDecoder;
m_IsMatchDecoders: array [0..ULZMABase.kNumStates shl ULZMABase.kNumPosStatesBitsMax-1] of smallint;
m_IsRepDecoders: array [0..ULZMABase.kNumStates-1] of smallint;
m_IsRepG0Decoders: array [0..ULZMABase.kNumStates-1] of smallint;
m_IsRepG1Decoders: array [0..ULZMABase.kNumStates-1] of smallint;
m_IsRepG2Decoders: array [0..ULZMABase.kNumStates-1] of smallint;
m_IsRep0LongDecoders: array [0..ULZMABase.kNumStates shl ULZMABase.kNumPosStatesBitsMax-1] of smallint;
m_PosSlotDecoder: array [0..ULZMABase.kNumLenToPosStates-1] of PBitTreeDecoder;
m_PosDecoders: array [0..ULZMABase.kNumFullDistances - ULZMABase.kEndPosModelIndex-1] of smallint;
m_PosAlignDecoder:PBitTreeDecoder;
m_LenDecoder:PLZMALenDecoder;
m_RepLenDecoder:PLZMALenDecoder;
m_LiteralDecoder:PLZMALiteralDecoder;
m_DictionarySize:integer;
m_DictionarySizeCheck:integer;
m_PosStateMask:integer;
constructor Create;
destructor Destroy;virtual;
function SetDictionarySize(const dictionarySize:integer):boolean;
function SetLcLpPb(const lc,lp,pb:integer):boolean;
procedure _Init;
function Code(const inStream,outStream:PStream;outSize:int64):boolean;
function SetDecoderProperties(const properties:array of byte):boolean;
property OnProgress:TLZMAProgress read FOnProgress write FOnProgress;
end;
TLZMALenDecoder = object(TObj)
public
m_Choice:array [0..1] of smallint;
m_LowCoder: array[0..ULZMABase.kNumPosStatesMax-1] of PBitTreeDecoder;
m_MidCoder: array[0..ULZMABase.kNumPosStatesMax-1] of PBitTreeDecoder;
m_HighCoder: PBitTreeDecoder;
m_NumPosStates:integer;
constructor Create;
destructor Destroy;virtual;
procedure _Create(const numPosStates:integer);
procedure _Init;
function Decode(const rangeDecoder:PRangeDecoder;const posState:integer):integer;
end;
PLZMADecoder2 = ^TLZMADecoder2;
TLZMADecoder2 = object(TObj)
public
m_Decoders: array [0..$300-1] of smallint;
procedure _Init;
function DecodeNormal(const ArangeDecoder:PRangeDecoder):byte;
function DecodeWithMatchByte(const ArangeDecoder:PRangeDecoder;AmatchByte:byte):byte;
end;
TLZMALiteralDecoder = object(TObj)
public
m_Coders: array of PLZMADecoder2;
m_NumPrevBits:integer;
m_NumPosBits:integer;
m_PosMask:integer;
procedure _Create(const AnumPosBits, AnumPrevBits:integer);
procedure _Init;
function GetDecoder(const Apos:integer;const AprevByte:byte):PLZMADecoder2;
destructor Destroy;virtual;
end;
implementation
constructor TLZMALenDecoder.Create;
begin
New(m_HighCoder, Create(ULZMABase.kNumHighLenBits));
m_NumPosStates:=0;
end;
destructor TLZMALenDecoder.Destroy;
var i:integer;
begin
m_HighCoder.free;
for i:=low(m_LowCoder) to high(m_LowCoder) do begin
if m_LowCoder[i]<>nil then m_LowCoder[i].free;
if m_MidCoder[i]<>nil then m_MidCoder[i].free;
end;
inherited;
end;
procedure TLZMALenDecoder._Create(const numPosStates:integer);
begin
while m_NumPosStates < numPosStates do begin
New(m_LowCoder[m_NumPosStates], Create(ULZMABase.kNumLowLenBits));
New(m_MidCoder[m_NumPosStates], Create(ULZMABase.kNumMidLenBits));
inc(m_NumPosStates);
end;
end;
procedure TLZMALenDecoder._Init;
var posState:integer;
begin
URangeDecoder.InitBitModels(m_Choice);
for posState := 0 to m_NumPosStates-1 do begin
m_LowCoder[posState]._Init;
m_MidCoder[posState]._Init;
end;
m_HighCoder._Init;
end;
function TLZMALenDecoder.Decode(const rangeDecoder:PRangeDecoder;const posState:integer):integer;
var symbol:integer;
begin
if (rangeDecoder.DecodeBit(m_Choice, 0) = 0) then begin
result:=m_LowCoder[posState].Decode(rangeDecoder);
exit;
end;
symbol := ULZMABase.kNumLowLenSymbols;
if (rangeDecoder.DecodeBit(m_Choice, 1) = 0) then
symbol := symbol + m_MidCoder[posState].Decode(rangeDecoder)
else symbol := symbol + ULZMABase.kNumMidLenSymbols + m_HighCoder.Decode(rangeDecoder);
result:=symbol;
end;
procedure TLZMADecoder2._Init;
begin
URangeDecoder.InitBitModels(m_Decoders);
end;
function TLZMADecoder2.DecodeNormal(const ArangeDecoder:PRangeDecoder):byte;
var symbol:integer;
begin
symbol := 1;
repeat
symbol := (symbol shl 1) or ArangeDecoder.DecodeBit(m_Decoders, symbol);
until not (symbol < $100);
result:=symbol;
end;
function TLZMADecoder2.DecodeWithMatchByte(const ArangeDecoder:PRangeDecoder;AmatchByte:byte):byte;
var symbol:integer;
matchbit:integer;
bit:integer;
begin
symbol := 1;
repeat
matchBit := (AmatchByte shr 7) and 1;
AmatchByte := AmatchByte shl 1;
bit := ArangeDecoder.DecodeBit(m_Decoders, ((1 + matchBit) shl 8) + symbol);
symbol := (symbol shl 1) or bit;
if (matchBit <> bit) then begin
while (symbol < $100) do begin
symbol := (symbol shl 1) or ArangeDecoder.DecodeBit(m_Decoders, symbol);
end;
break;
end;
until not (symbol < $100);
result:=symbol;
end;
procedure TLZMALiteralDecoder._Create(const AnumPosBits, AnumPrevBits:integer);
var numStates,i:integer;
begin
if (length(m_Coders) <> 0) and (m_NumPrevBits = AnumPrevBits) and (m_NumPosBits = AnumPosBits) then
exit;
m_NumPosBits := AnumPosBits;
m_PosMask := (1 shl AnumPosBits) - 1;
m_NumPrevBits := AnumPrevBits;
numStates := 1 shl (m_NumPrevBits + m_NumPosBits);
setlength(m_Coders,numStates);
for i :=0 to numStates-1 do
New(m_Coders[i], Create);
end;
destructor TLZMALiteralDecoder.Destroy;
var i:integer;
begin
for i :=low(m_Coders) to high(m_Coders) do
if m_Coders[i]<>nil then m_Coders[i].Free;
inherited;
end;
procedure TLZMALiteralDecoder._Init;
var numStates,i:integer;
begin
numStates := 1 shl (m_NumPrevBits + m_NumPosBits);
for i := 0 to numStates -1 do
m_Coders[i]._Init;
end;
function TLZMALiteralDecoder.GetDecoder(const Apos:integer;const AprevByte:byte):PLZMADecoder2;
begin
result:=m_Coders[((Apos and m_PosMask) shl m_NumPrevBits) + ((AprevByte and $FF) shr (8 - m_NumPrevBits))];
end;
constructor TLZMADecoder.Create;
var i:integer;
begin
FOnProgress:=nil;
New(m_OutWindow, Create);
New(m_RangeDecoder, Create);
New(m_PosAlignDecoder, Create(ULZMABase.kNumAlignBits));
New(m_LenDecoder, Create);
New(m_RepLenDecoder, Create);
New(m_LiteralDecoder, Create);
m_DictionarySize:= -1;
m_DictionarySizeCheck:= -1;
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
New(m_PosSlotDecoder[i], Create(ULZMABase.kNumPosSlotBits));
end;
destructor TLZMADecoder.Destroy;
var i:integer;
begin
m_OutWindow.Free;
m_RangeDecoder.Free;
m_PosAlignDecoder.Free;
m_LenDecoder.Free;
m_RepLenDecoder.Free;
m_LiteralDecoder.Free;
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
m_PosSlotDecoder[i].Free;
end;
function TLZMADecoder.SetDictionarySize(const dictionarySize:integer):boolean;
begin
if dictionarySize < 0 then
result:=false
else begin
if m_DictionarySize <> dictionarySize then begin
m_DictionarySize := dictionarySize;
m_DictionarySizeCheck := max(m_DictionarySize, 1);
m_OutWindow._Create(max(m_DictionarySizeCheck, (1 shl 12)));
end;
result:=true;
end;
end;
function TLZMADecoder.SetLcLpPb(const lc,lp,pb:integer):boolean;
var numPosStates:integer;
begin
if (lc > ULZMABase.kNumLitContextBitsMax) or (lp > 4) or (pb > ULZMABase.kNumPosStatesBitsMax) then begin
result:=false;
exit;
end;
m_LiteralDecoder._Create(lp, lc);
numPosStates := 1 shl pb;
m_LenDecoder._Create(numPosStates);
m_RepLenDecoder._Create(numPosStates);
m_PosStateMask := numPosStates - 1;
result:=true;
end;
procedure TLZMADecoder._Init;
var i:integer;
begin
m_OutWindow._Init(false);
URangeDecoder.InitBitModels(m_IsMatchDecoders);
URangeDecoder.InitBitModels(m_IsRep0LongDecoders);
URangeDecoder.InitBitModels(m_IsRepDecoders);
URangeDecoder.InitBitModels(m_IsRepG0Decoders);
URangeDecoder.InitBitModels(m_IsRepG1Decoders);
URangeDecoder.InitBitModels(m_IsRepG2Decoders);
URangeDecoder.InitBitModels(m_PosDecoders);
m_LiteralDecoder._Init();
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
m_PosSlotDecoder[i]._Init;
m_LenDecoder._Init;
m_RepLenDecoder._Init;
m_PosAlignDecoder._Init;
m_RangeDecoder._Init;
end;
function TLZMADecoder.Code(const inStream,outStream:PStream;outSize:int64):boolean;
var state,rep0,rep1,rep2,rep3:integer;
nowPos64:int64;
prevByte:byte;
posState:integer;
decoder2:PLZMADecoder2;
len,distance,posSlot,numDirectBits:integer;
lpos:int64;
progint:int64;
begin
DoProgress(LPAMax,outSize);
m_RangeDecoder.SetStream(inStream);
m_OutWindow.SetStream(outStream);
_Init;
state := ULZMABase.StateInit;
rep0 := 0; rep1 := 0; rep2 := 0; rep3 := 0;
nowPos64 := 0;
prevByte := 0;
progint:=outsize div CodeProgressInterval;
lpos:=progint;
while (outSize < 0) or (nowPos64 < outSize) do begin
if (nowPos64 >=lpos) then begin
DoProgress(LPAPos,nowPos64);
lpos:=lpos+progint;
end;
posState := nowPos64 and m_PosStateMask;
if (m_RangeDecoder.DecodeBit(m_IsMatchDecoders, (state shl ULZMABase.kNumPosStatesBitsMax) + posState) = 0) then begin
decoder2 := m_LiteralDecoder.GetDecoder(nowPos64, prevByte);
if not ULZMABase.StateIsCharState(state) then
prevByte := decoder2.DecodeWithMatchByte(m_RangeDecoder, m_OutWindow.GetByte(rep0))
else prevByte := decoder2.DecodeNormal(m_RangeDecoder);
m_OutWindow.PutByte(prevByte);
state := ULZMABase.StateUpdateChar(state);
inc(nowPos64);
end else begin
if (m_RangeDecoder.DecodeBit(m_IsRepDecoders, state) = 1) then begin
len := 0;
if (m_RangeDecoder.DecodeBit(m_IsRepG0Decoders, state) = 0) then begin
if (m_RangeDecoder.DecodeBit(m_IsRep0LongDecoders, (state shl ULZMABase.kNumPosStatesBitsMax) + posState) = 0) then begin
state := ULZMABase.StateUpdateShortRep(state);
len := 1;
end;
end else begin
if m_RangeDecoder.DecodeBit(m_IsRepG1Decoders, state) = 0 then
distance := rep1
else begin
if (m_RangeDecoder.DecodeBit(m_IsRepG2Decoders, state) = 0) then
distance := rep2
else begin
distance := rep3;
rep3 := rep2;
end;
rep2 := rep1;
end;
rep1 := rep0;
rep0 := distance;
end;
if len = 0 then begin
len := m_RepLenDecoder.Decode(m_RangeDecoder, posState) + ULZMABase.kMatchMinLen;
state := ULZMABase.StateUpdateRep(state);
end;
end else begin
rep3 := rep2;
rep2 := rep1;
rep1 := rep0;
len := ULZMABase.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
state := ULZMABase.StateUpdateMatch(state);
posSlot := m_PosSlotDecoder[ULZMABase.GetLenToPosState(len)].Decode(m_RangeDecoder);
if posSlot >= ULZMABase.kStartPosModelIndex then begin
numDirectBits := (posSlot shr 1) - 1;
rep0 := ((2 or (posSlot and 1)) shl numDirectBits);
if posSlot < ULZMABase.kEndPosModelIndex then
rep0 := rep0 + UBitTreeDecoder.ReverseDecode(m_PosDecoders,
rep0 - posSlot - 1, m_RangeDecoder, numDirectBits)
else begin
rep0 := rep0 + (m_RangeDecoder.DecodeDirectBits(
numDirectBits - ULZMABase.kNumAlignBits) shl ULZMABase.kNumAlignBits);
rep0 := rep0 + m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
if rep0 < 0 then begin
if rep0 = -1 then
break;
result:=false;
exit;
end;
end;
end else rep0 := posSlot;
end;
if (rep0 >= nowPos64) or (rep0 >= m_DictionarySizeCheck) then begin
m_OutWindow.Flush();
result:=false;
exit;
end;
m_OutWindow.CopyBlock(rep0, len);
nowPos64 := nowPos64 + len;
prevByte := m_OutWindow.GetByte(0);
end;
end;
m_OutWindow.Flush();
m_OutWindow.ReleaseStream();
m_RangeDecoder.ReleaseStream();
DoProgress(LPAPos,nowPos64);
result:=true;
end;
function TLZMADecoder.SetDecoderProperties(const properties:array of byte):boolean;
var val,lc,remainder,lp,pb,dictionarysize,i:integer;
begin
if length(properties) < 5 then begin
result:=false;
exit;
end;
val := properties[0] and $FF;
lc := val mod 9;
remainder := val div 9;
lp := remainder mod 5;
pb := remainder div 5;
dictionarySize := 0;
for i := 0 to 3 do
dictionarySize := dictionarysize + ((properties[1 + i]) and $FF) shl (i * 8);
if (not SetLcLpPb(lc, lp, pb)) then begin
result:=false;
exit;
end;
result:=SetDictionarySize(dictionarySize);
end;
procedure TLZMADecoder.DoProgress(const Action:TLZMAProgressAction;const Value:integer);
begin
if assigned(fonprogress) then
fonprogress(action,value);
end;
end.