git-svn-id: https://svn.code.sf.net/p/kolmck/code@67 91bb2d04-0c0c-4d2d-88a5-bbb6f4c1fa07
1519 lines
55 KiB
ObjectPascal
1519 lines
55 KiB
ObjectPascal
unit ULZMAEncoder;
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE Delphi}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses UBitTreeEncoder,ULZMABase,ULZBinTree,URangeEncoder,KOL,ULZMACommon;
|
|
|
|
const EMatchFinderTypeBT2 = 0;
|
|
EMatchFinderTypeBT4 = 1;
|
|
kIfinityPrice:integer = $FFFFFFF;
|
|
kDefaultDictionaryLogSize = 22;
|
|
kNumFastBytesDefault = $20;
|
|
kNumLenSpecSymbols = ULZMABase.kNumLowLenSymbols + ULZMABase.kNumMidLenSymbols;
|
|
kNumOpts = 1 shl 12;
|
|
kPropSize = 5;
|
|
|
|
type PLZMAEncoder2=^TLZMAEncoder2;
|
|
PLZMALiteralEncoder=^TLZMALiteralEncoder;
|
|
PLZMAOptimal=^TLZMAOptimal;
|
|
PLZMALenPriceTableEncoder=^TLZMALenPriceTableEncoder;
|
|
|
|
PLZMAEncoder=^TLZMAEncoder;
|
|
TLZMAEncoder=object(TObj)
|
|
private
|
|
FOnProgress:TLZMAProgress;
|
|
procedure DoProgress(const Action:TLZMAProgressAction;const Value:integer);
|
|
public
|
|
g_FastPos:array [0..1 shl 11-1] of byte;
|
|
_state:integer;
|
|
_previousByte:byte;
|
|
_repDistances:array [0..ULZMABase.kNumRepDistances-1] of integer;
|
|
|
|
_optimum: array [0..kNumOpts-1] of PLZMAOptimal;
|
|
_matchFinder:PLZBinTree;
|
|
_rangeEncoder:PRangeEncoder;
|
|
|
|
_isMatch:array [0..ULZMABase.kNumStates shl ULZMABase.kNumPosStatesBitsMax-1]of smallint;
|
|
_isRep:array [0..ULZMABase.kNumStates-1] of smallint;
|
|
_isRepG0:array [0..ULZMABase.kNumStates-1] of smallint;
|
|
_isRepG1:array [0..ULZMABase.kNumStates-1] of smallint;
|
|
_isRepG2:array [0..ULZMABase.kNumStates-1] of smallint;
|
|
_isRep0Long:array [0..ULZMABase.kNumStates shl ULZMABase.kNumPosStatesBitsMax-1]of smallint;
|
|
|
|
_posSlotEncoder:array [0..ULZMABase.kNumLenToPosStates-1] of PBitTreeEncoder; // kNumPosSlotBits
|
|
|
|
_posEncoders:array [0..ULZMABase.kNumFullDistances-ULZMABase.kEndPosModelIndex-1]of smallint;
|
|
_posAlignEncoder:PBitTreeEncoder;
|
|
|
|
_lenEncoder:PLZMALenPriceTableEncoder;
|
|
_repMatchLenEncoder:PLZMALenPriceTableEncoder;
|
|
|
|
_literalEncoder:PLZMALiteralEncoder;
|
|
|
|
_matchDistances:array [0..ULZMABase.kMatchMaxLen*2+1] of integer;
|
|
|
|
_numFastBytes:integer;
|
|
_longestMatchLength:integer;
|
|
_numDistancePairs:integer;
|
|
|
|
_additionalOffset:integer;
|
|
|
|
_optimumEndIndex:integer;
|
|
_optimumCurrentIndex:integer;
|
|
|
|
_longestMatchWasFound:boolean;
|
|
|
|
_posSlotPrices:array [0..1 shl (ULZMABase.kNumPosSlotBits+ULZMABase.kNumLenToPosStatesBits)-1] of integer;
|
|
_distancesPrices:array [0..ULZMABase.kNumFullDistances shl ULZMABase.kNumLenToPosStatesBits-1] of integer;
|
|
_alignPrices:array [0..ULZMABase.kAlignTableSize-1] of integer;
|
|
_alignPriceCount:integer;
|
|
|
|
_distTableSize:integer;
|
|
|
|
_posStateBits:integer;
|
|
_posStateMask:integer;
|
|
_numLiteralPosStateBits:integer;
|
|
_numLiteralContextBits:integer;
|
|
|
|
_dictionarySize:integer;
|
|
_dictionarySizePrev:integer;
|
|
_numFastBytesPrev:integer;
|
|
|
|
nowPos64:int64;
|
|
_finished:boolean;
|
|
_inStream:PStream;
|
|
|
|
_matchFinderType:integer;
|
|
_writeEndMark:boolean;
|
|
|
|
_needReleaseMFStream:boolean;
|
|
|
|
reps:array [0..ULZMABase.kNumRepDistances-1]of integer;
|
|
repLens:array [0..ULZMABase.kNumRepDistances-1] of integer;
|
|
backRes:integer;
|
|
processedInSize:int64;
|
|
processedOutSize:int64;
|
|
finished:boolean;
|
|
properties:array [0..kPropSize] of byte;
|
|
tempPrices:array [0..ULZMABase.kNumFullDistances-1]of integer;
|
|
_matchPriceCount:integer;
|
|
constructor Create;
|
|
destructor Destroy;virtual;
|
|
function GetPosSlot(const pos:integer):integer;
|
|
function GetPosSlot2(const pos:integer):integer;
|
|
procedure BaseInit;
|
|
procedure _Create;
|
|
procedure SetWriteEndMarkerMode(const AwriteEndMarker:boolean);
|
|
procedure _Init;
|
|
function ReadMatchDistances:integer;
|
|
procedure MovePos(const num:integer);
|
|
function GetRepLen1Price(const state,posState:integer):integer;
|
|
function GetPureRepPrice(const repIndex, state, posState:integer):integer;
|
|
function GetRepPrice(const repIndex, len, state, posState:integer):integer;
|
|
function GetPosLenPrice(const pos, len, posState:integer):integer;
|
|
function Backward(cur:integer):integer;
|
|
function GetOptimum(position:integer):integer;
|
|
function ChangePair(const smallDist, bigDist:integer):boolean;
|
|
procedure WriteEndMarker(const posState:integer);
|
|
procedure Flush(const nowPos:integer);
|
|
procedure ReleaseMFStream;
|
|
procedure CodeOneBlock(var inSize,outSize:int64;var Afinished:boolean);
|
|
procedure FillDistancesPrices;
|
|
procedure FillAlignPrices;
|
|
procedure SetOutStream(const outStream:PStream);
|
|
procedure ReleaseOutStream;
|
|
procedure ReleaseStreams;
|
|
procedure SetStreams(const inStream, outStream:PStream;const inSize, outSize:int64);
|
|
procedure Code(const inStream, outStream:PStream;const inSize, outSize:int64);
|
|
procedure WriteCoderProperties(const outStream:PStream);
|
|
function SetAlgorithm(const algorithm:integer):boolean;
|
|
function SetDictionarySize(dictionarySize:Cardinal):boolean;
|
|
function SeNumFastBytes(const numFastBytes:integer):boolean;
|
|
function SetMatchFinder(const matchFinderIndex:integer):boolean;
|
|
function SetLcLpPb(const lc,lp,pb:integer):boolean;
|
|
procedure SetEndMarkerMode(const endMarkerMode:boolean);
|
|
property OnProgress:TLZMAProgress read FOnProgress write FOnProgress;
|
|
end;
|
|
|
|
TLZMALiteralEncoder=object(TObj)
|
|
public
|
|
m_Coders: array of PLZMAEncoder2;
|
|
m_NumPrevBits:integer;
|
|
m_NumPosBits:integer;
|
|
m_PosMask:integer;
|
|
procedure _Create(const numPosBits,numPrevBits:integer);
|
|
destructor Destroy;virtual;
|
|
procedure _Init;
|
|
function GetSubCoder(const pos:integer;const prevByte:byte):PLZMAEncoder2;
|
|
end;
|
|
|
|
TLZMAEncoder2=object(TObj)
|
|
public
|
|
m_Encoders: array[0..$300-1] of smallint;
|
|
procedure _Init;
|
|
procedure Encode(const rangeEncoder:PRangeEncoder;const symbol:byte);
|
|
procedure EncodeMatched(const rangeEncoder:PRangeEncoder;const matchByte,symbol:byte);
|
|
function GetPrice(const matchMode:boolean;const matchByte,symbol:byte):integer;
|
|
end;
|
|
|
|
TLZMALenEncoder=object(TObj)
|
|
public
|
|
_choice:array[0..1] of smallint;
|
|
_lowCoder: array [0..ULZMABase.kNumPosStatesEncodingMax-1] of PBitTreeEncoder;
|
|
_midCoder: array [0..ULZMABase.kNumPosStatesEncodingMax-1] of PBitTreeEncoder;
|
|
_highCoder:PBitTreeEncoder;
|
|
constructor Create;
|
|
destructor Destroy;virtual;
|
|
procedure _Init(const numPosStates:integer);
|
|
procedure Encode(const rangeEncoder:PRangeEncoder;symbol:integer;const posState:integer);virtual;
|
|
procedure SetPrices(const posState,numSymbols:integer;var prices:array of integer;const st:integer);
|
|
end;
|
|
|
|
TLZMALenPriceTableEncoder=object(TLZMALenEncoder)
|
|
public
|
|
_prices: array [0..ULZMABase.kNumLenSymbols shl ULZMABase.kNumPosStatesBitsEncodingMax-1] of integer;
|
|
_tableSize:integer;
|
|
_counters: array [0..ULZMABase.kNumPosStatesEncodingMax-1] of integer;
|
|
procedure SetTableSize(const tableSize:integer);
|
|
function GetPrice(const symbol,posState:integer):integer;
|
|
procedure UpdateTable(const posState:integer);
|
|
procedure UpdateTables(const numPosStates:integer);
|
|
procedure Encode(const rangeEncoder:PRangeEncoder;symbol:integer;const posState:integer);virtual;
|
|
end;
|
|
|
|
TLZMAOptimal=object(TObj)
|
|
public
|
|
State:integer;
|
|
|
|
Prev1IsChar:boolean;
|
|
Prev2:boolean;
|
|
|
|
PosPrev2:integer;
|
|
BackPrev2:integer;
|
|
|
|
Price:integer;
|
|
PosPrev:integer;
|
|
BackPrev:integer;
|
|
|
|
Backs0:integer;
|
|
Backs1:integer;
|
|
Backs2:integer;
|
|
Backs3:integer;
|
|
|
|
procedure MakeAsChar;
|
|
procedure MakeAsShortRep;
|
|
function IsShortRep:boolean;
|
|
end;
|
|
|
|
implementation
|
|
|
|
constructor TLZMAEncoder.Create;
|
|
var kFastSlots,c,slotFast,j,k:integer;
|
|
begin
|
|
kFastSlots := 22;
|
|
c := 2;
|
|
g_FastPos[0] := 0;
|
|
g_FastPos[1] := 1;
|
|
for slotFast := 2 to kFastSlots -1 do begin
|
|
k := (1 shl ((slotFast shr 1) - 1));
|
|
for j := 0 to k -1 do begin
|
|
g_FastPos[c] := slotFast;
|
|
inc(c);
|
|
end;
|
|
end;
|
|
_state := ULZMABase.StateInit();
|
|
_matchFinder:=nil;
|
|
New(_rangeEncoder, Create);
|
|
New(_posAlignEncoder, Create(ULZMABase.kNumAlignBits));
|
|
New(_lenEncoder, Create);
|
|
New(_repMatchLenEncoder, Create);
|
|
New(_literalEncoder, Create);
|
|
_numFastBytes:= kNumFastBytesDefault;
|
|
_distTableSize:= (kDefaultDictionaryLogSize * 2);
|
|
_posStateBits:= 2;
|
|
_posStateMask:= (4 - 1);
|
|
_numLiteralPosStateBits:= 0;
|
|
_numLiteralContextBits:= 3;
|
|
|
|
_dictionarySize:= (1 shl kDefaultDictionaryLogSize);
|
|
_dictionarySizePrev:= -1;
|
|
_numFastBytesPrev:= -1;
|
|
_matchFinderType:= EMatchFinderTypeBT4;
|
|
_writeEndMark:= false;
|
|
|
|
_needReleaseMFStream:= false;
|
|
end;
|
|
|
|
destructor TLZMAEncoder.Destroy;
|
|
var i:integer;
|
|
begin
|
|
_rangeEncoder.Free;
|
|
_posAlignEncoder.Free;
|
|
_lenEncoder.Free;
|
|
_repMatchLenEncoder.Free;
|
|
_literalEncoder.Free;
|
|
if _matchFinder<>nil then _matchFinder.Free;
|
|
for i := 0 to kNumOpts -1 do
|
|
_optimum[i].Free;
|
|
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
|
|
_posSlotEncoder[i].Free;
|
|
end;
|
|
|
|
procedure TLZMAEncoder._Create;
|
|
var bt:PLZBinTree;
|
|
numHashBytes,i:integer;
|
|
begin
|
|
if _matchFinder = nil then begin
|
|
New(bt, Create);
|
|
numHashBytes:= 4;
|
|
if _matchFinderType = EMatchFinderTypeBT2 then
|
|
numHashBytes := 2;
|
|
bt.SetType(numHashBytes);
|
|
_matchFinder := bt;
|
|
end;
|
|
_literalEncoder._Create(_numLiteralPosStateBits, _numLiteralContextBits);
|
|
|
|
if (_dictionarySize = _dictionarySizePrev) and (_numFastBytesPrev = _numFastBytes) then
|
|
exit;
|
|
_matchFinder._Create(_dictionarySize, kNumOpts, _numFastBytes, ULZMABase.kMatchMaxLen + 1);
|
|
_dictionarySizePrev := _dictionarySize;
|
|
_numFastBytesPrev := _numFastBytes;
|
|
|
|
for i := 0 to kNumOpts -1 do
|
|
New(_optimum[i], Create);
|
|
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
|
|
New(_posSlotEncoder[i], Create(ULZMABase.kNumPosSlotBits));
|
|
end;
|
|
|
|
function TLZMAEncoder.GetPosSlot(const pos:integer):integer;
|
|
begin
|
|
if (pos < (1 shl 11)) then
|
|
result:=g_FastPos[pos]
|
|
else if (pos < (1 shl 21)) then
|
|
result:=(g_FastPos[pos shr 10] + 20)
|
|
else result:=(g_FastPos[pos shr 20] + 40);
|
|
end;
|
|
|
|
function TLZMAEncoder.GetPosSlot2(const pos:integer):integer;
|
|
begin
|
|
if (pos < (1 shl 17)) then
|
|
result:=(g_FastPos[pos shr 6] + 12)
|
|
else if (pos < (1 shl 27)) then
|
|
result:=(g_FastPos[pos shr 16] + 32)
|
|
else result:=(g_FastPos[pos shr 26] + 52);
|
|
end;
|
|
|
|
procedure TLZMAEncoder.BaseInit;
|
|
var i:integer;
|
|
begin
|
|
_state := ulzmaBase.StateInit;
|
|
_previousByte := 0;
|
|
for i := 0 to ULZMABase.kNumRepDistances -1 do
|
|
_repDistances[i] := 0;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.SetWriteEndMarkerMode(const AwriteEndMarker:boolean);
|
|
begin
|
|
_writeEndMark := AwriteEndMarker;
|
|
end;
|
|
|
|
procedure TLZMAEncoder._Init;
|
|
var i:integer;
|
|
begin
|
|
BaseInit;
|
|
_rangeEncoder._Init;
|
|
|
|
URangeEncoder.InitBitModels(_isMatch);
|
|
URangeEncoder.InitBitModels(_isRep0Long);
|
|
URangeEncoder.InitBitModels(_isRep);
|
|
URangeEncoder.InitBitModels(_isRepG0);
|
|
URangeEncoder.InitBitModels(_isRepG1);
|
|
URangeEncoder.InitBitModels(_isRepG2);
|
|
URangeEncoder.InitBitModels(_posEncoders);
|
|
|
|
|
|
_literalEncoder._Init();
|
|
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
|
|
_posSlotEncoder[i]._Init;
|
|
|
|
_lenEncoder._Init(1 shl _posStateBits);
|
|
_repMatchLenEncoder._Init(1 shl _posStateBits);
|
|
|
|
_posAlignEncoder._Init;
|
|
|
|
_longestMatchWasFound := false;
|
|
_optimumEndIndex := 0;
|
|
_optimumCurrentIndex := 0;
|
|
_additionalOffset := 0;
|
|
end;
|
|
|
|
function TLZMAEncoder.ReadMatchDistances:integer;
|
|
var lenRes:integer;
|
|
begin
|
|
lenRes := 0;
|
|
_numDistancePairs := _matchFinder.GetMatches(_matchDistances);
|
|
|
|
if _numDistancePairs > 0 then begin
|
|
lenRes := _matchDistances[_numDistancePairs - 2];
|
|
if lenRes = _numFastBytes then
|
|
lenRes := lenRes + _matchFinder.GetMatchLen(lenRes - 1, _matchDistances[_numDistancePairs - 1], ULZMABase.kMatchMaxLen - lenRes);
|
|
end;
|
|
inc(_additionalOffset);
|
|
result:=lenRes;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.MovePos(const num:integer);
|
|
begin
|
|
if num > 0 then begin
|
|
_matchFinder.Skip(num);
|
|
_additionalOffset := _additionalOffset + num;
|
|
end;
|
|
end;
|
|
|
|
function TLZMAEncoder.GetRepLen1Price(const state,posState:integer):integer;
|
|
begin
|
|
result:=RangeEncoder.GetPrice0(_isRepG0[state]) +
|
|
RangeEncoder.GetPrice0(_isRep0Long[(state shl ULZMABase.kNumPosStatesBitsMax) + posState]);
|
|
end;
|
|
|
|
function TLZMAEncoder.GetPureRepPrice(const repIndex, state, posState:integer):integer;
|
|
var price:integer;
|
|
begin
|
|
if repIndex = 0 then begin
|
|
price := RangeEncoder.GetPrice0(_isRepG0[state]);
|
|
price := price + RangeEncoder.GetPrice1(_isRep0Long[(state shl ULZMABase.kNumPosStatesBitsMax) + posState]);
|
|
end else begin
|
|
price := RangeEncoder.GetPrice1(_isRepG0[state]);
|
|
if repIndex = 1 then
|
|
price := price + RangeEncoder.GetPrice0(_isRepG1[state])
|
|
else begin
|
|
price := price + RangeEncoder.GetPrice1(_isRepG1[state]);
|
|
price := price + RangeEncoder.GetPrice(_isRepG2[state], repIndex - 2);
|
|
end;
|
|
end;
|
|
result:=price;
|
|
end;
|
|
|
|
function TLZMAEncoder.GetRepPrice(const repIndex, len, state, posState:integer):integer;
|
|
var price:integer;
|
|
begin
|
|
price := _repMatchLenEncoder.GetPrice(len - ULZMABase.kMatchMinLen, posState);
|
|
result := price + GetPureRepPrice(repIndex, state, posState);
|
|
end;
|
|
|
|
function TLZMAEncoder.GetPosLenPrice(const pos, len, posState:integer):integer;
|
|
var price,lenToPosState:integer;
|
|
begin
|
|
lenToPosState := ULZMABase.GetLenToPosState(len);
|
|
if pos < ULZMABase.kNumFullDistances then
|
|
price := _distancesPrices[(lenToPosState * ULZMABase.kNumFullDistances) + pos]
|
|
else price := _posSlotPrices[(lenToPosState shl ULZMABase.kNumPosSlotBits) + GetPosSlot2(pos)] +
|
|
_alignPrices[pos and ULZMABase.kAlignMask];
|
|
result := price + _lenEncoder.GetPrice(len - ULZMABase.kMatchMinLen, posState);
|
|
end;
|
|
|
|
function TLZMAEncoder.Backward(cur:integer):integer;
|
|
var posMem,backMem,posPrev,backCur:integer;
|
|
begin
|
|
_optimumEndIndex := cur;
|
|
posMem := _optimum[cur].PosPrev;
|
|
backMem := _optimum[cur].BackPrev;
|
|
repeat
|
|
if _optimum[cur].Prev1IsChar then begin
|
|
_optimum[posMem].MakeAsChar;
|
|
_optimum[posMem].PosPrev := posMem - 1;
|
|
if _optimum[cur].Prev2 then begin
|
|
_optimum[posMem - 1].Prev1IsChar := false;
|
|
_optimum[posMem - 1].PosPrev := _optimum[cur].PosPrev2;
|
|
_optimum[posMem - 1].BackPrev := _optimum[cur].BackPrev2;
|
|
end;
|
|
end;
|
|
posPrev := posMem;
|
|
backCur := backMem;
|
|
|
|
backMem := _optimum[posPrev].BackPrev;
|
|
posMem := _optimum[posPrev].PosPrev;
|
|
|
|
_optimum[posPrev].BackPrev := backCur;
|
|
_optimum[posPrev].PosPrev := cur;
|
|
cur := posPrev;
|
|
until not (cur > 0);
|
|
backRes := _optimum[0].BackPrev;
|
|
_optimumCurrentIndex := _optimum[0].PosPrev;
|
|
result:=_optimumCurrentIndex;
|
|
end;
|
|
|
|
function TLZMAEncoder.GetOptimum(position:integer):integer;
|
|
var lenRes,lenMain,numDistancePairs,numAvailableBytes,repMaxIndex,i:integer;
|
|
matchPrice,repMatchPrice,shortRepPrice,lenEnd,len,repLen,price:integer;
|
|
curAndLenPrice,normalMatchPrice,Offs,distance,cur,newLen:integer;
|
|
posPrev,state,pos,curPrice,curAnd1Price,numAvailableBytesFull:integer;
|
|
lenTest2,t,state2,posStateNext,nextRepMatchPrice,offset:integer;
|
|
startLen,repIndex,lenTest,lenTestTemp,curAndLenCharPrice:integer;
|
|
nextMatchPrice,curBack:integer;
|
|
optimum,opt,nextOptimum:PLZMAOptimal;
|
|
currentByte,matchByte,posState:byte;
|
|
nextIsChar:boolean;
|
|
begin
|
|
if (_optimumEndIndex <> _optimumCurrentIndex) then begin
|
|
lenRes := _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex;
|
|
backRes := _optimum[_optimumCurrentIndex].BackPrev;
|
|
_optimumCurrentIndex := _optimum[_optimumCurrentIndex].PosPrev;
|
|
result:=lenRes;
|
|
exit;
|
|
end;//if optimumendindex
|
|
_optimumCurrentIndex := 0;
|
|
_optimumEndIndex := 0;
|
|
|
|
if not _longestMatchWasFound then begin
|
|
lenMain := ReadMatchDistances();
|
|
end else begin //if not longest
|
|
lenMain := _longestMatchLength;
|
|
_longestMatchWasFound := false;
|
|
end;//if not longest else
|
|
numDistancePairs := _numDistancePairs;
|
|
|
|
numAvailableBytes := _matchFinder.GetNumAvailableBytes + 1;
|
|
if numAvailableBytes < 2 then begin
|
|
backRes := -1;
|
|
result:=1;
|
|
exit;
|
|
end;//if numavailable
|
|
{if numAvailableBytes > ULZMABase.kMatchMaxLen then
|
|
numAvailableBytes := ULZMABase.kMatchMaxLen;}
|
|
|
|
repMaxIndex := 0;
|
|
for i := 0 to ULZMABase.kNumRepDistances-1 do begin
|
|
reps[i] := _repDistances[i];
|
|
repLens[i] := _matchFinder.GetMatchLen(0 - 1, reps[i], ULZMABase.kMatchMaxLen);
|
|
if repLens[i] > repLens[repMaxIndex] then
|
|
repMaxIndex := i;
|
|
end;//for i
|
|
if repLens[repMaxIndex] >= _numFastBytes then begin
|
|
backRes := repMaxIndex;
|
|
lenRes := repLens[repMaxIndex];
|
|
MovePos(lenRes - 1);
|
|
result:=lenRes;
|
|
exit;
|
|
end;//if replens[]
|
|
|
|
if lenMain >= _numFastBytes then begin
|
|
backRes := _matchDistances[numDistancePairs - 1] + ULZMABase.kNumRepDistances;
|
|
MovePos(lenMain - 1);
|
|
result:=lenMain;
|
|
exit;
|
|
end;//if lenMain
|
|
|
|
currentByte := _matchFinder.GetIndexByte(0 - 1);
|
|
matchByte := _matchFinder.GetIndexByte(0 - _repDistances[0] - 1 - 1);
|
|
|
|
if (lenMain < 2) and (currentByte <> matchByte) and (repLens[repMaxIndex] < 2) then begin
|
|
backRes := -1;
|
|
result:=1;
|
|
exit;
|
|
end;//if lenmain<2
|
|
|
|
_optimum[0].State := _state;
|
|
|
|
posState := (position and _posStateMask);
|
|
|
|
_optimum[1].Price := RangeEncoder.GetPrice0(_isMatch[(_state shl ULZMABase.kNumPosStatesBitsMax) + posState]) +
|
|
_literalEncoder.GetSubCoder(position, _previousByte).GetPrice(not ULZMABase.StateIsCharState(_state), matchByte, currentByte);
|
|
_optimum[1].MakeAsChar();
|
|
|
|
matchPrice := RangeEncoder.GetPrice1(_isMatch[(_state shl ULZMABase.kNumPosStatesBitsMax) + posState]);
|
|
repMatchPrice := matchPrice + RangeEncoder.GetPrice1(_isRep[_state]);
|
|
|
|
if matchByte = currentByte then begin
|
|
shortRepPrice := repMatchPrice + GetRepLen1Price(_state, posState);
|
|
if shortRepPrice < _optimum[1].Price then begin
|
|
_optimum[1].Price := shortRepPrice;
|
|
_optimum[1].MakeAsShortRep;
|
|
end;//if shortrepprice
|
|
end;//if matchbyte
|
|
|
|
if lenMain >= repLens[repMaxIndex] then lenEnd:=lenMain
|
|
else lenEnd:=repLens[repMaxIndex];
|
|
|
|
if lenEnd < 2 then begin
|
|
backRes := _optimum[1].BackPrev;
|
|
result:=1;
|
|
exit;
|
|
end;//if lenend<2
|
|
|
|
_optimum[1].PosPrev := 0;
|
|
|
|
_optimum[0].Backs0 := reps[0];
|
|
_optimum[0].Backs1 := reps[1];
|
|
_optimum[0].Backs2 := reps[2];
|
|
_optimum[0].Backs3 := reps[3];
|
|
|
|
len := lenEnd;
|
|
repeat
|
|
_optimum[len].Price := kIfinityPrice;
|
|
dec(len);
|
|
until not (len >= 2);
|
|
|
|
for i := 0 to ULZMABase.kNumRepDistances -1 do begin
|
|
repLen := repLens[i];
|
|
if repLen < 2 then
|
|
continue;
|
|
price := repMatchPrice + GetPureRepPrice(i, _state, posState);
|
|
repeat
|
|
curAndLenPrice := price + _repMatchLenEncoder.GetPrice(repLen - 2, posState);
|
|
optimum := _optimum[repLen];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := 0;
|
|
optimum.BackPrev := i;
|
|
optimum.Prev1IsChar := false;
|
|
end;//if curandlenprice
|
|
dec(replen);
|
|
until not (repLen >= 2);
|
|
end;//for i
|
|
|
|
normalMatchPrice := matchPrice + RangeEncoder.GetPrice0(_isRep[_state]);
|
|
|
|
if repLens[0] >= 2 then len:=repLens[0] + 1
|
|
else len:=2;
|
|
|
|
if len <= lenMain then begin
|
|
offs := 0;
|
|
while len > _matchDistances[offs] do
|
|
offs := offs + 2;
|
|
while (true) do begin
|
|
distance := _matchDistances[offs + 1];
|
|
curAndLenPrice := normalMatchPrice + GetPosLenPrice(distance, len, posState);
|
|
optimum := _optimum[len];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := 0;
|
|
optimum.BackPrev := distance + ULZMABase.kNumRepDistances;
|
|
optimum.Prev1IsChar := false;
|
|
end;//if curlenandprice
|
|
if len = _matchDistances[offs] then begin
|
|
offs := offs + 2;
|
|
if offs = numDistancePairs then
|
|
break;
|
|
end;//if len=_match
|
|
inc(len);
|
|
end;//while (true)
|
|
end;//if len<=lenmain
|
|
|
|
cur := 0;
|
|
|
|
while (true) do begin
|
|
inc(cur);
|
|
if cur = lenEnd then begin
|
|
result:=Backward(cur);
|
|
exit;
|
|
end;//if cur=lenEnd
|
|
newLen := ReadMatchDistances;
|
|
numDistancePairs := _numDistancePairs;
|
|
if newLen >= _numFastBytes then begin
|
|
_longestMatchLength := newLen;
|
|
_longestMatchWasFound := true;
|
|
result:=Backward(cur);
|
|
exit;
|
|
end;//if newlen=_numfast
|
|
inc(position);
|
|
posPrev := _optimum[cur].PosPrev;
|
|
if _optimum[cur].Prev1IsChar then begin
|
|
dec(posPrev);
|
|
if _optimum[cur].Prev2 then begin
|
|
state := _optimum[_optimum[cur].PosPrev2].State;
|
|
if _optimum[cur].BackPrev2 < ULZMABase.kNumRepDistances then
|
|
state := ULZMABase.StateUpdateRep(state)
|
|
else state := ULZMABase.StateUpdateMatch(state);
|
|
end//if _optimum[cur].Prev2
|
|
else state := _optimum[posPrev].State;
|
|
state := ULZMABase.StateUpdateChar(state);
|
|
end//if _optimum[cur].Prev1IsChar
|
|
else state := _optimum[posPrev].State;
|
|
if posPrev = cur - 1 then begin
|
|
if _optimum[cur].IsShortRep then
|
|
state := ULZMABase.StateUpdateShortRep(state)
|
|
else state := ULZMABase.StateUpdateChar(state);
|
|
end //if posPrev = cur - 1
|
|
else begin
|
|
if _optimum[cur].Prev1IsChar and _optimum[cur].Prev2 then begin
|
|
posPrev := _optimum[cur].PosPrev2;
|
|
pos := _optimum[cur].BackPrev2;
|
|
state := ULZMABase.StateUpdateRep(state);
|
|
end//if _optimum[cur].Prev1IsChar
|
|
else begin
|
|
pos := _optimum[cur].BackPrev;
|
|
if pos < ULZMABase.kNumRepDistances then
|
|
state := ULZMABase.StateUpdateRep(state)
|
|
else state := ULZMABase.StateUpdateMatch(state);
|
|
end;//if else _optimum[cur].Prev1IsChar
|
|
opt := _optimum[posPrev];
|
|
if pos < ULZMABase.kNumRepDistances then begin
|
|
if pos = 0 then begin
|
|
reps[0] := opt.Backs0;
|
|
reps[1] := opt.Backs1;
|
|
reps[2] := opt.Backs2;
|
|
reps[3] := opt.Backs3;
|
|
end//if pos=0
|
|
else if pos = 1 then begin
|
|
reps[0] := opt.Backs1;
|
|
reps[1] := opt.Backs0;
|
|
reps[2] := opt.Backs2;
|
|
reps[3] := opt.Backs3;
|
|
end //if pos=1
|
|
else if pos = 2 then begin
|
|
reps[0] := opt.Backs2;
|
|
reps[1] := opt.Backs0;
|
|
reps[2] := opt.Backs1;
|
|
reps[3] := opt.Backs3;
|
|
end//if pos=2
|
|
else begin
|
|
reps[0] := opt.Backs3;
|
|
reps[1] := opt.Backs0;
|
|
reps[2] := opt.Backs1;
|
|
reps[3] := opt.Backs2;
|
|
end;//else if pos=
|
|
end// if pos < ULZMABase.kNumRepDistances
|
|
else begin
|
|
reps[0] := (pos - ULZMABase.kNumRepDistances);
|
|
reps[1] := opt.Backs0;
|
|
reps[2] := opt.Backs1;
|
|
reps[3] := opt.Backs2;
|
|
end;//if else pos < ULZMABase.kNumRepDistances
|
|
end;//if else posPrev = cur - 1
|
|
_optimum[cur].State := state;
|
|
_optimum[cur].Backs0 := reps[0];
|
|
_optimum[cur].Backs1 := reps[1];
|
|
_optimum[cur].Backs2 := reps[2];
|
|
_optimum[cur].Backs3 := reps[3];
|
|
curPrice := _optimum[cur].Price;
|
|
|
|
currentByte := _matchFinder.GetIndexByte(0 - 1);
|
|
matchByte := _matchFinder.GetIndexByte(0 - reps[0] - 1 - 1);
|
|
|
|
posState := (position and _posStateMask);
|
|
|
|
curAnd1Price := curPrice +
|
|
RangeEncoder.GetPrice0(_isMatch[(state shl ULZMABase.kNumPosStatesBitsMax) + posState]) +
|
|
_literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)).
|
|
GetPrice(not ULZMABase.StateIsCharState(state), matchByte, currentByte);
|
|
|
|
nextOptimum := _optimum[cur + 1];
|
|
|
|
nextIsChar := false;
|
|
if curAnd1Price < nextOptimum.Price then begin
|
|
nextOptimum.Price := curAnd1Price;
|
|
nextOptimum.PosPrev := cur;
|
|
nextOptimum.MakeAsChar;
|
|
nextIsChar := true;
|
|
end;//if curand1price
|
|
|
|
matchPrice := curPrice + RangeEncoder.GetPrice1(_isMatch[(state shl ULZMABase.kNumPosStatesBitsMax) + posState]);
|
|
repMatchPrice := matchPrice + RangeEncoder.GetPrice1(_isRep[state]);
|
|
|
|
if (matchByte = currentByte) and
|
|
(not ((nextOptimum.PosPrev < cur) and (nextOptimum.BackPrev = 0))) then begin
|
|
shortRepPrice := repMatchPrice + GetRepLen1Price(state, posState);
|
|
if shortRepPrice <= nextOptimum.Price then begin
|
|
nextOptimum.Price := shortRepPrice;
|
|
nextOptimum.PosPrev := cur;
|
|
nextOptimum.MakeAsShortRep;
|
|
nextIsChar := true;
|
|
end;//if shortRepPrice <= nextOptimum.Price
|
|
end;//if (matchByte = currentByte) and
|
|
|
|
numAvailableBytesFull := _matchFinder.GetNumAvailableBytes + 1;
|
|
numAvailableBytesFull := min(kNumOpts - 1 - cur, numAvailableBytesFull);
|
|
numAvailableBytes := numAvailableBytesFull;
|
|
|
|
if numAvailableBytes < 2 then
|
|
continue;
|
|
if numAvailableBytes > _numFastBytes then
|
|
numAvailableBytes := _numFastBytes;
|
|
if (not nextIsChar) and (matchByte <> currentByte) then begin
|
|
// try Literal + rep0
|
|
t := min(numAvailableBytesFull - 1, _numFastBytes);
|
|
lenTest2 := _matchFinder.GetMatchLen(0, reps[0], t);
|
|
if lenTest2 >= 2 then begin
|
|
state2 := ULZMABase.StateUpdateChar(state);
|
|
|
|
posStateNext := (position + 1) and _posStateMask;
|
|
nextRepMatchPrice := curAnd1Price +
|
|
RangeEncoder.GetPrice1(_isMatch[(state2 shl ULZMABase.kNumPosStatesBitsMax) + posStateNext]) +
|
|
RangeEncoder.GetPrice1(_isRep[state2]);
|
|
begin
|
|
offset := cur + 1 + lenTest2;
|
|
while lenEnd < offset do begin
|
|
inc(lenEnd);
|
|
_optimum[lenEnd].Price := kIfinityPrice;
|
|
end;//while lenend
|
|
curAndLenPrice := nextRepMatchPrice + GetRepPrice(
|
|
0, lenTest2, state2, posStateNext);
|
|
optimum := _optimum[offset];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := cur + 1;
|
|
optimum.BackPrev := 0;
|
|
optimum.Prev1IsChar := true;
|
|
optimum.Prev2 := false;
|
|
end;//if curandlenprice
|
|
end;//none
|
|
end;//if lentest
|
|
end;//if not nextischar and ...
|
|
|
|
startLen := 2; // speed optimization
|
|
|
|
for repIndex := 0 to ULZMABase.kNumRepDistances -1 do begin
|
|
lenTest := _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes);
|
|
if lenTest < 2 then
|
|
continue;
|
|
lenTestTemp := lenTest;
|
|
repeat
|
|
while lenEnd < cur + lenTest do begin
|
|
inc(lenEnd);
|
|
_optimum[lenEnd].Price := kIfinityPrice;
|
|
end;//while lenEnd
|
|
curAndLenPrice := repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState);
|
|
optimum := _optimum[cur + lenTest];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := cur;
|
|
optimum.BackPrev := repIndex;
|
|
optimum.Prev1IsChar := false;
|
|
end;//if curandlen
|
|
dec(lenTest);
|
|
until not (lenTest >= 2);
|
|
lenTest := lenTestTemp;
|
|
|
|
if repIndex = 0 then
|
|
startLen := lenTest + 1;
|
|
|
|
// if (_maxMode)
|
|
if lenTest < numAvailableBytesFull then begin
|
|
t := min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
|
|
lenTest2 := _matchFinder.GetMatchLen(lenTest, reps[repIndex], t);
|
|
if lenTest2 >= 2 then begin
|
|
state2 := ULZMABase.StateUpdateRep(state);
|
|
|
|
posStateNext := (position + lenTest) and _posStateMask;
|
|
curAndLenCharPrice :=
|
|
repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) +
|
|
RangeEncoder.GetPrice0(_isMatch[(state2 shl ULZMABase.kNumPosStatesBitsMax) + posStateNext]) +
|
|
_literalEncoder.GetSubCoder(position + lenTest,
|
|
_matchFinder.GetIndexByte(lenTest - 1 - 1)).GetPrice(true,
|
|
_matchFinder.GetIndexByte(lenTest - 1 - (reps[repIndex] + 1)),
|
|
_matchFinder.GetIndexByte(lenTest - 1));
|
|
state2 := ULZMABase.StateUpdateChar(state2);
|
|
posStateNext := (position + lenTest + 1) and _posStateMask;
|
|
nextMatchPrice := curAndLenCharPrice + RangeEncoder.GetPrice1(_isMatch[(state2 shl ULZMABase.kNumPosStatesBitsMax) + posStateNext]);
|
|
nextRepMatchPrice := nextMatchPrice + RangeEncoder.GetPrice1(_isRep[state2]);
|
|
|
|
// for(; lenTest2 >= 2; lenTest2--)
|
|
begin
|
|
offset := lenTest + 1 + lenTest2;
|
|
while lenEnd < cur + offset do begin
|
|
inc(lenEnd);
|
|
_optimum[lenEnd].Price := kIfinityPrice;
|
|
end;//while lenEnd
|
|
curAndLenPrice := nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
|
|
optimum := _optimum[cur + offset];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := cur + lenTest + 1;
|
|
optimum.BackPrev := 0;
|
|
optimum.Prev1IsChar := true;
|
|
optimum.Prev2 := true;
|
|
optimum.PosPrev2 := cur;
|
|
optimum.BackPrev2 := repIndex;
|
|
end;//if curAndLenPrice < optimum.Price
|
|
end;//none
|
|
end;//if lenTest2 >= 2
|
|
end;//if lenTest < numAvailableBytesFull
|
|
end;//for repIndex
|
|
|
|
if newLen > numAvailableBytes then begin
|
|
newLen := numAvailableBytes;
|
|
numDistancePairs := 0;
|
|
while newLen > _matchDistances[numDistancePairs] do
|
|
numDistancePairs := numDistancePairs + 2;
|
|
_matchDistances[numDistancePairs] := newLen;
|
|
numDistancePairs := numDistancePairs + 2;
|
|
end;//if newLen > numAvailableBytes
|
|
if newLen >= startLen then begin
|
|
normalMatchPrice := matchPrice + RangeEncoder.GetPrice0(_isRep[state]);
|
|
while lenEnd < cur + newLen do begin
|
|
inc(lenEnd);
|
|
_optimum[lenEnd].Price := kIfinityPrice;
|
|
end;//while lenEnd
|
|
|
|
offs := 0;
|
|
while startLen > _matchDistances[offs] do
|
|
offs := offs + 2;
|
|
|
|
lenTest := startLen;
|
|
while (true) do begin
|
|
curBack := _matchDistances[offs + 1];
|
|
curAndLenPrice := normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState);
|
|
optimum := _optimum[cur + lenTest];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := cur;
|
|
optimum.BackPrev := curBack + ULZMABase.kNumRepDistances;
|
|
optimum.Prev1IsChar := false;
|
|
end;//if curAndLenPrice < optimum.Price
|
|
|
|
if lenTest = _matchDistances[offs] then begin
|
|
if lenTest < numAvailableBytesFull then begin
|
|
t := min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
|
|
lenTest2 := _matchFinder.GetMatchLen(lenTest, curBack, t);
|
|
if lenTest2 >= 2 then begin
|
|
state2 := ULZMABase.StateUpdateMatch(state);
|
|
|
|
posStateNext := (position + lenTest) and _posStateMask;
|
|
curAndLenCharPrice := curAndLenPrice +
|
|
RangeEncoder.GetPrice0(_isMatch[(state2 shl ULZMABase.kNumPosStatesBitsMax) + posStateNext]) +
|
|
_literalEncoder.GetSubCoder(position + lenTest,
|
|
_matchFinder.GetIndexByte(lenTest - 1 - 1)).
|
|
GetPrice(true,
|
|
_matchFinder.GetIndexByte(lenTest - (curBack + 1) - 1),
|
|
_matchFinder.GetIndexByte(lenTest - 1));
|
|
state2 := ULZMABase.StateUpdateChar(state2);
|
|
posStateNext := (position + lenTest + 1) and _posStateMask;
|
|
nextMatchPrice := curAndLenCharPrice + RangeEncoder.GetPrice1(_isMatch[(state2 shl ULZMABase.kNumPosStatesBitsMax) + posStateNext]);
|
|
nextRepMatchPrice := nextMatchPrice + RangeEncoder.GetPrice1(_isRep[state2]);
|
|
|
|
offset := lenTest + 1 + lenTest2;
|
|
while lenEnd < cur + offset do begin
|
|
inc(lenEnd);
|
|
_optimum[lenEnd].Price := kIfinityPrice;
|
|
end;//while lenEnd
|
|
curAndLenPrice := nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
|
|
optimum := _optimum[cur + offset];
|
|
if curAndLenPrice < optimum.Price then begin
|
|
optimum.Price := curAndLenPrice;
|
|
optimum.PosPrev := cur + lenTest + 1;
|
|
optimum.BackPrev := 0;
|
|
optimum.Prev1IsChar := true;
|
|
optimum.Prev2 := true;
|
|
optimum.PosPrev2 := cur;
|
|
optimum.BackPrev2 := curBack + ULZMABase.kNumRepDistances;
|
|
end;//if curAndLenPrice < optimum.Price
|
|
end;//if lenTest2 >= 2
|
|
end;//lenTest < numAvailableBytesFull
|
|
offs :=offs + 2;
|
|
if offs = numDistancePairs then
|
|
break;
|
|
end;//if lenTest = _matchDistances[offs]
|
|
inc(lenTest);
|
|
end;//while(true)
|
|
end;//if newLen >= startLen
|
|
end;//while (true)
|
|
end;
|
|
|
|
function TLZMAEncoder.ChangePair(const smallDist, bigDist:integer):boolean;
|
|
var kDif:integer;
|
|
begin
|
|
kDif := 7;
|
|
result:= (smallDist < (1 shl (32 - kDif))) and (bigDist >= (smallDist shl kDif));
|
|
end;
|
|
|
|
procedure TLZMAEncoder.WriteEndMarker(const posState:integer);
|
|
var len,posSlot,lenToPosState,footerBits,posReduced:integer;
|
|
begin
|
|
if not _writeEndMark then
|
|
exit;
|
|
|
|
_rangeEncoder.Encode(_isMatch, (_state shl ULZMABase.kNumPosStatesBitsMax) + posState, 1);
|
|
_rangeEncoder.Encode(_isRep, _state, 0);
|
|
_state := ULZMABase.StateUpdateMatch(_state);
|
|
len := ULZMABase.kMatchMinLen;
|
|
_lenEncoder.Encode(_rangeEncoder, len - ULZMABase.kMatchMinLen, posState);
|
|
posSlot := (1 shl ULZMABase.kNumPosSlotBits) - 1;
|
|
lenToPosState := ULZMABase.GetLenToPosState(len);
|
|
_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
|
|
footerBits := 30;
|
|
posReduced := (1 shl footerBits) - 1;
|
|
_rangeEncoder.EncodeDirectBits(posReduced shr ULZMABase.kNumAlignBits, footerBits - ULZMABase.kNumAlignBits);
|
|
_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced and ULZMABase.kAlignMask);
|
|
end;
|
|
|
|
procedure TLZMAEncoder.Flush(const nowPos:integer);
|
|
begin
|
|
ReleaseMFStream;
|
|
WriteEndMarker(nowPos and _posStateMask);
|
|
_rangeEncoder.FlushData();
|
|
_rangeEncoder.FlushStream();
|
|
end;
|
|
|
|
procedure TLZMAEncoder.CodeOneBlock(var inSize,outSize:int64;var Afinished:boolean);
|
|
var progressPosValuePrev:int64;
|
|
posState,len,pos,complexState,distance,i,posSlot,lenToPosState:integer;
|
|
footerBits,baseVal,posReduced:integer;
|
|
curByte,matchByte:byte;
|
|
subcoder:PLZMAEncoder2;
|
|
begin
|
|
inSize := 0;
|
|
outSize := 0;
|
|
Afinished := true;
|
|
|
|
if _inStream <>nil then begin
|
|
_matchFinder.SetStream(_inStream);
|
|
_matchFinder._Init;
|
|
_needReleaseMFStream := true;
|
|
_inStream := nil;
|
|
end;
|
|
|
|
if _finished then
|
|
exit;
|
|
_finished := true;
|
|
|
|
progressPosValuePrev := nowPos64;
|
|
if nowPos64 = 0 then begin
|
|
if _matchFinder.GetNumAvailableBytes = 0 then begin
|
|
Flush(nowPos64);
|
|
exit;
|
|
end;
|
|
|
|
ReadMatchDistances;
|
|
posState := integer(nowPos64) and _posStateMask;
|
|
_rangeEncoder.Encode(_isMatch, (_state shl ULZMABase.kNumPosStatesBitsMax) + posState, 0);
|
|
_state := ULZMABase.StateUpdateChar(_state);
|
|
curByte := _matchFinder.GetIndexByte(0 - _additionalOffset);
|
|
_literalEncoder.GetSubCoder(integer(nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
|
|
_previousByte := curByte;
|
|
dec(_additionalOffset);
|
|
inc(nowPos64);
|
|
end;
|
|
if _matchFinder.GetNumAvailableBytes = 0 then begin
|
|
Flush(integer(nowPos64));
|
|
exit;
|
|
end;
|
|
while true do begin
|
|
len := GetOptimum(integer(nowPos64));
|
|
pos := backRes;
|
|
posState := integer(nowPos64) and _posStateMask;
|
|
complexState := (_state shl ULZMABase.kNumPosStatesBitsMax) + posState;
|
|
if (len = 1) and (pos = -1) then begin
|
|
_rangeEncoder.Encode(_isMatch, complexState, 0);
|
|
curByte := _matchFinder.GetIndexByte(0 - _additionalOffset);
|
|
subCoder := _literalEncoder.GetSubCoder(integer(nowPos64), _previousByte);
|
|
if not ULZMABase.StateIsCharState(_state) then begin
|
|
matchByte := _matchFinder.GetIndexByte(0 - _repDistances[0] - 1 - _additionalOffset);
|
|
subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
|
|
end else subCoder.Encode(_rangeEncoder, curByte);
|
|
_previousByte := curByte;
|
|
_state := ULZMABase.StateUpdateChar(_state);
|
|
end else begin
|
|
_rangeEncoder.Encode(_isMatch, complexState, 1);
|
|
if pos < ULZMABase.kNumRepDistances then begin
|
|
_rangeEncoder.Encode(_isRep, _state, 1);
|
|
if pos = 0 then begin
|
|
_rangeEncoder.Encode(_isRepG0, _state, 0);
|
|
if len = 1 then
|
|
_rangeEncoder.Encode(_isRep0Long, complexState, 0)
|
|
else _rangeEncoder.Encode(_isRep0Long, complexState, 1);
|
|
end else begin
|
|
_rangeEncoder.Encode(_isRepG0, _state, 1);
|
|
if pos = 1 then
|
|
_rangeEncoder.Encode(_isRepG1, _state, 0)
|
|
else begin
|
|
_rangeEncoder.Encode(_isRepG1, _state, 1);
|
|
_rangeEncoder.Encode(_isRepG2, _state, pos - 2);
|
|
end;
|
|
end;
|
|
if len = 1 then
|
|
_state := ULZMABase.StateUpdateShortRep(_state)
|
|
else begin
|
|
_repMatchLenEncoder.Encode(_rangeEncoder, len - ULZMABase.kMatchMinLen, posState);
|
|
_state := ULZMABase.StateUpdateRep(_state);
|
|
end;
|
|
distance := _repDistances[pos];
|
|
if pos <> 0 then begin
|
|
for i := pos downto 1 do
|
|
_repDistances[i] := _repDistances[i - 1];
|
|
_repDistances[0] := distance;
|
|
end;
|
|
end else begin
|
|
_rangeEncoder.Encode(_isRep, _state, 0);
|
|
_state := ULZMABase.StateUpdateMatch(_state);
|
|
_lenEncoder.Encode(_rangeEncoder, len - ULZMABase.kMatchMinLen, posState);
|
|
pos := pos - ULZMABase.kNumRepDistances;
|
|
posSlot := GetPosSlot(pos);
|
|
lenToPosState := ULZMABase.GetLenToPosState(len);
|
|
_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
|
|
|
|
if posSlot >= ULZMABase.kStartPosModelIndex then begin
|
|
footerBits := integer((posSlot shr 1) - 1);
|
|
baseVal := ((2 or (posSlot and 1)) shl footerBits);
|
|
posReduced := pos - baseVal;
|
|
|
|
if posSlot < ULZMABase.kEndPosModelIndex then
|
|
UBitTreeEncoder.ReverseEncode(_posEncoders,
|
|
baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced)
|
|
else begin
|
|
_rangeEncoder.EncodeDirectBits(posReduced shr ULZMABase.kNumAlignBits, footerBits - ULZMABase.kNumAlignBits);
|
|
_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced and ULZMABase.kAlignMask);
|
|
inc(_alignPriceCount);
|
|
end;
|
|
end;
|
|
distance := pos;
|
|
for i := ULZMABase.kNumRepDistances - 1 downto 1 do
|
|
_repDistances[i] := _repDistances[i - 1];
|
|
_repDistances[0] := distance;
|
|
inc(_matchPriceCount);
|
|
end;
|
|
_previousByte := _matchFinder.GetIndexByte(len - 1 - _additionalOffset);
|
|
end;
|
|
_additionalOffset := _additionalOffset - len;
|
|
nowPos64 := nowPos64 + len;
|
|
if _additionalOffset = 0 then begin
|
|
// if (!_fastMode)
|
|
if _matchPriceCount >= (1 shl 7) then
|
|
FillDistancesPrices;
|
|
if _alignPriceCount >= ULZMABase.kAlignTableSize then
|
|
FillAlignPrices;
|
|
inSize := nowPos64;
|
|
outSize := _rangeEncoder.GetProcessedSizeAdd;
|
|
if _matchFinder.GetNumAvailableBytes = 0 then begin
|
|
Flush(integer(nowPos64));
|
|
exit;
|
|
end;
|
|
|
|
if (nowPos64 - progressPosValuePrev >= (1 shl 12)) then begin
|
|
_finished := false;
|
|
Afinished := false;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.ReleaseMFStream;
|
|
begin
|
|
if (_matchFinder <>nil) and _needReleaseMFStream then begin
|
|
_matchFinder.ReleaseStream;
|
|
_needReleaseMFStream := false;
|
|
end;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.SetOutStream(const outStream:PStream);
|
|
begin
|
|
_rangeEncoder.SetStream(outStream);
|
|
end;
|
|
|
|
procedure TLZMAEncoder.ReleaseOutStream;
|
|
begin
|
|
_rangeEncoder.ReleaseStream;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.ReleaseStreams;
|
|
begin
|
|
ReleaseMFStream;
|
|
ReleaseOutStream;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.SetStreams(const inStream, outStream:PStream;const inSize, outSize:int64);
|
|
begin
|
|
_inStream := inStream;
|
|
_finished := false;
|
|
_Create();
|
|
SetOutStream(outStream);
|
|
_Init();
|
|
|
|
// if (!_fastMode)
|
|
FillDistancesPrices;
|
|
FillAlignPrices;
|
|
|
|
_lenEncoder.SetTableSize(_numFastBytes + 1 - ULZMABase.kMatchMinLen);
|
|
_lenEncoder.UpdateTables(1 shl _posStateBits);
|
|
_repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - ULZMABase.kMatchMinLen);
|
|
_repMatchLenEncoder.UpdateTables(1 shl _posStateBits);
|
|
|
|
nowPos64 := 0;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.Code(const inStream, outStream:PStream;const inSize, outSize:int64);
|
|
var lpos:int64;
|
|
progint:int64;
|
|
inputsize:int64;
|
|
begin
|
|
if insize=-1 then
|
|
inputsize:=instream.Size-instream.Position
|
|
else inputsize:=insize;
|
|
progint:=inputsize div CodeProgressInterval;
|
|
lpos:=progint;
|
|
|
|
_needReleaseMFStream := false;
|
|
DoProgress(LPAMax,inputsize);
|
|
try
|
|
SetStreams(inStream, outStream, inSize, outSize);
|
|
while true do begin
|
|
CodeOneBlock(processedInSize, processedOutSize, finished);
|
|
if finished then begin
|
|
DoProgress(LPAPos,inputsize);
|
|
exit;
|
|
end;
|
|
if (processedInSize>=lpos) then begin
|
|
DoProgress(LPAPos,processedInSize);
|
|
lpos:=lpos+progint;
|
|
end;
|
|
end;
|
|
finally
|
|
ReleaseStreams();
|
|
end;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.WriteCoderProperties(const outStream:PStream);
|
|
var i:integer;
|
|
begin
|
|
properties[0] := (_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits;
|
|
for i := 0 to 3 do
|
|
properties[1 + i] := (_dictionarySize shr (8 * i));
|
|
outStream.write(properties, kPropSize);
|
|
end;
|
|
|
|
procedure TLZMAEncoder.FillDistancesPrices;
|
|
var i,posSlot,footerBits,baseVal,lenToPosState,st,st2:integer;
|
|
encoder:PBitTreeEncoder;
|
|
begin
|
|
for i := ULZMABase.kStartPosModelIndex to ULZMABase.kNumFullDistances -1 do begin
|
|
posSlot := GetPosSlot(i);
|
|
footerBits := integer((posSlot shr 1) - 1);
|
|
baseVal := (2 or (posSlot and 1)) shl footerBits;
|
|
tempPrices[i] := ReverseGetPrice(_posEncoders,
|
|
baseVal - posSlot - 1, footerBits, i - baseVal);
|
|
end;
|
|
|
|
for lenToPosState := 0 to ULZMABase.kNumLenToPosStates -1 do begin
|
|
encoder := _posSlotEncoder[lenToPosState];
|
|
|
|
st := (lenToPosState shl ULZMABase.kNumPosSlotBits);
|
|
for posSlot := 0 to _distTableSize -1 do
|
|
_posSlotPrices[st + posSlot] := encoder.GetPrice(posSlot);
|
|
for posSlot := ULZMABase.kEndPosModelIndex to _distTableSize -1 do
|
|
_posSlotPrices[st + posSlot] := _posSlotPrices[st + posSlot] + ((((posSlot shr 1) - 1) - ULZMABase.kNumAlignBits) shl kNumBitPriceShiftBits);
|
|
|
|
st2 := lenToPosState * ULZMABase.kNumFullDistances;
|
|
for i := 0 to ULZMABase.kStartPosModelIndex -1 do
|
|
_distancesPrices[st2 + i] := _posSlotPrices[st + i];
|
|
for i := ULZMABase.kStartPosModelIndex to ULZMABase.kNumFullDistances-1 do
|
|
_distancesPrices[st2 + i] := _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i];
|
|
end;
|
|
_matchPriceCount := 0;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.FillAlignPrices;
|
|
var i:integer;
|
|
begin
|
|
for i := 0 to ULZMABase.kAlignTableSize -1 do
|
|
_alignPrices[i] := _posAlignEncoder.ReverseGetPrice(i);
|
|
_alignPriceCount := 0;
|
|
end;
|
|
|
|
function TLZMAEncoder.SetAlgorithm(const algorithm:integer):boolean;
|
|
begin
|
|
{
|
|
_fastMode = (algorithm == 0);
|
|
_maxMode = (algorithm >= 2);
|
|
}
|
|
result:=true;
|
|
end;
|
|
|
|
function TLZMAEncoder.SetDictionarySize(dictionarySize:Cardinal):boolean;
|
|
var kDicLogSizeMaxCompress,dicLogSize:integer;
|
|
begin
|
|
kDicLogSizeMaxCompress := 29;
|
|
if (dictionarySize < (1 shl ULZMABase.kDicLogSizeMin)) or (dictionarySize > (1 shl kDicLogSizeMaxCompress)) then begin
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
_dictionarySize := dictionarySize;
|
|
dicLogSize := 0;
|
|
while dictionarySize > (1 shl dicLogSize) do
|
|
inc(dicLogSize);
|
|
_distTableSize := dicLogSize * 2;
|
|
result:=true;
|
|
end;
|
|
|
|
function TLZMAEncoder.SeNumFastBytes(const numFastBytes:integer):boolean;
|
|
begin
|
|
if (numFastBytes < 5) or (numFastBytes > ULZMABase.kMatchMaxLen) then begin
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
_numFastBytes := numFastBytes;
|
|
result:=true;
|
|
end;
|
|
|
|
function TLZMAEncoder.SetMatchFinder(const matchFinderIndex:integer):boolean;
|
|
var matchFinderIndexPrev:integer;
|
|
begin
|
|
if (matchFinderIndex < 0) or (matchFinderIndex > 2) then begin
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
matchFinderIndexPrev := _matchFinderType;
|
|
_matchFinderType := matchFinderIndex;
|
|
if (_matchFinder <> nil) and (matchFinderIndexPrev <> _matchFinderType) then begin
|
|
_dictionarySizePrev := -1;
|
|
_matchFinder := nil;
|
|
end;
|
|
result:=true;
|
|
end;
|
|
|
|
function TLZMAEncoder.SetLcLpPb(const lc,lp,pb:integer):boolean;
|
|
begin
|
|
if (lp < 0) or (lp > ULZMABase.kNumLitPosStatesBitsEncodingMax) or
|
|
(lc < 0) or (lc > ULZMABase.kNumLitContextBitsMax) or
|
|
(pb < 0) or (pb > ULZMABase.kNumPosStatesBitsEncodingMax) then begin
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
_numLiteralPosStateBits := lp;
|
|
_numLiteralContextBits := lc;
|
|
_posStateBits := pb;
|
|
_posStateMask := ((1) shl _posStateBits) - 1;
|
|
result:=true;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.SetEndMarkerMode(const endMarkerMode:boolean);
|
|
begin
|
|
_writeEndMark := endMarkerMode;
|
|
end;
|
|
|
|
procedure TLZMAEncoder2._Init;
|
|
begin
|
|
URangeEncoder.InitBitModels(m_Encoders);
|
|
end;
|
|
|
|
procedure TLZMAEncoder2.Encode(const rangeEncoder:PRangeEncoder;const symbol:byte);
|
|
var context:integer;
|
|
bit,i:integer;
|
|
begin
|
|
context := 1;
|
|
for i := 7 downto 0 do begin
|
|
bit := ((symbol shr i) and 1);
|
|
rangeEncoder.Encode(m_Encoders, context, bit);
|
|
context := (context shl 1) or bit;
|
|
end;
|
|
end;
|
|
|
|
procedure TLZMAEncoder2.EncodeMatched(const rangeEncoder:PRangeEncoder;const matchByte,symbol:byte);
|
|
var context,i,bit,state,matchbit:integer;
|
|
same:boolean;
|
|
begin
|
|
context := 1;
|
|
same := true;
|
|
for i := 7 downto 0 do begin
|
|
bit := ((symbol shr i) and 1);
|
|
state := context;
|
|
if same then begin
|
|
matchBit := ((matchByte shr i) and 1);
|
|
state :=state + ((1 + matchBit) shl 8);
|
|
same := (matchBit = bit);
|
|
end;
|
|
rangeEncoder.Encode(m_Encoders, state, bit);
|
|
context := (context shl 1) or bit;
|
|
end;
|
|
end;
|
|
|
|
function TLZMAEncoder2.GetPrice(const matchMode:boolean;const matchByte,symbol:byte):integer;
|
|
var price,context,i,matchbit,bit:integer;
|
|
begin
|
|
price := 0;
|
|
context := 1;
|
|
i := 7;
|
|
if matchMode then
|
|
while i>=0 do begin
|
|
matchBit := (matchByte shr i) and 1;
|
|
bit := (symbol shr i) and 1;
|
|
price := price + RangeEncoder.GetPrice(m_Encoders[((1 + matchBit) shl 8) + context], bit);
|
|
context := (context shl 1) or bit;
|
|
if (matchBit <> bit) then begin
|
|
dec(i);
|
|
break;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
while i>=0 do begin
|
|
bit := (symbol shr i) and 1;
|
|
price := price + RangeEncoder.GetPrice(m_Encoders[context], bit);
|
|
context := (context shl 1) or bit;
|
|
dec(i);
|
|
end;
|
|
result:=price;
|
|
end;
|
|
|
|
procedure TLZMALiteralEncoder._Create(const numPosBits,numPrevBits:integer);
|
|
var numstates:integer;
|
|
i:integer;
|
|
begin
|
|
if (length(m_Coders)<>0) and (m_NumPrevBits = numPrevBits) and (m_NumPosBits = numPosBits) then
|
|
exit;
|
|
m_NumPosBits := numPosBits;
|
|
m_PosMask := (1 shl numPosBits) - 1;
|
|
m_NumPrevBits := numPrevBits;
|
|
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 TLZMALiteralEncoder.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 TLZMALiteralEncoder._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 TLZMALiteralEncoder.GetSubCoder(const pos:integer;const prevByte:byte):PLZMAEncoder2;
|
|
begin
|
|
result:=m_Coders[((pos and m_PosMask) shl m_NumPrevBits) + ((prevByte and $FF) shr (8 - m_NumPrevBits))];
|
|
end;
|
|
|
|
constructor TLZMALenEncoder.Create;
|
|
var posState:integer;
|
|
begin
|
|
New(_highCoder, Create(ULZMABase.kNumHighLenBits));
|
|
for posState := 0 to ULZMABase.kNumPosStatesEncodingMax-1 do begin
|
|
New(_lowCoder[posState], Create(ULZMABase.kNumLowLenBits));
|
|
New(_midCoder[posState], Create(ULZMABase.kNumMidLenBits));
|
|
end;
|
|
end;
|
|
|
|
destructor TLZMALenEncoder.Destroy;
|
|
var posState:integer;
|
|
begin
|
|
_highCoder.Free;
|
|
for posState := 0 to ULZMABase.kNumPosStatesEncodingMax-1 do begin
|
|
_lowCoder[posState].Free;
|
|
_midCoder[posState].Free;
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TLZMALenEncoder._Init(const numPosStates:integer);
|
|
var posState:integer;
|
|
begin
|
|
URangeEncoder.InitBitModels(_choice);
|
|
|
|
for posState := 0 to numPosStates -1 do begin
|
|
_lowCoder[posState]._Init;
|
|
_midCoder[posState]._Init;
|
|
end;
|
|
_highCoder._Init;
|
|
end;
|
|
|
|
procedure TLZMALenEncoder.Encode(const rangeEncoder:PRangeEncoder;symbol:integer;const posState:integer);
|
|
begin
|
|
if (symbol < ULZMABase.kNumLowLenSymbols) then begin
|
|
rangeEncoder.Encode(_choice, 0, 0);
|
|
_lowCoder[posState].Encode(rangeEncoder, symbol);
|
|
end else begin
|
|
symbol := symbol - ULZMABase.kNumLowLenSymbols;
|
|
rangeEncoder.Encode(_choice, 0, 1);
|
|
if symbol < ULZMABase.kNumMidLenSymbols then begin
|
|
rangeEncoder.Encode(_choice, 1, 0);
|
|
_midCoder[posState].Encode(rangeEncoder, symbol);
|
|
end else begin
|
|
rangeEncoder.Encode(_choice, 1, 1);
|
|
_highCoder.Encode(rangeEncoder, symbol - ULZMABase.kNumMidLenSymbols);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLZMALenEncoder.SetPrices(const posState,numSymbols:integer;var prices:array of integer;const st:integer);
|
|
var a0,a1,b0,b1,i:integer;
|
|
begin
|
|
a0 := RangeEncoder.GetPrice0(_choice[0]);
|
|
a1 := RangeEncoder.GetPrice1(_choice[0]);
|
|
b0 := a1 + RangeEncoder.GetPrice0(_choice[1]);
|
|
b1 := a1 + RangeEncoder.GetPrice1(_choice[1]);
|
|
i:=0;
|
|
while i<ULZMABase.kNumLowLenSymbols do begin
|
|
if i >= numSymbols then
|
|
exit;
|
|
prices[st + i] := a0 + _lowCoder[posState].GetPrice(i);
|
|
inc(i);
|
|
end;
|
|
while i < ULZMABase.kNumLowLenSymbols + ULZMABase.kNumMidLenSymbols do begin
|
|
if i >= numSymbols then
|
|
exit;
|
|
prices[st + i] := b0 + _midCoder[posState].GetPrice(i - ULZMABase.kNumLowLenSymbols);
|
|
inc(i);
|
|
end;
|
|
while i < numSymbols do begin
|
|
prices[st + i] := b1 + _highCoder.GetPrice(i - ULZMABase.kNumLowLenSymbols - ULZMABase.kNumMidLenSymbols);
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
procedure TLZMALenPriceTableEncoder.SetTableSize(const tableSize:integer);
|
|
begin
|
|
_tableSize := tableSize;
|
|
end;
|
|
|
|
function TLZMALenPriceTableEncoder.GetPrice(const symbol,posState:integer):integer;
|
|
begin
|
|
result:=_prices[posState * ULZMABase.kNumLenSymbols + symbol]
|
|
end;
|
|
|
|
procedure TLZMALenPriceTableEncoder.UpdateTable(const posState:integer);
|
|
begin
|
|
SetPrices(posState, _tableSize, _prices, posState * ULZMABase.kNumLenSymbols);
|
|
_counters[posState] := _tableSize;
|
|
end;
|
|
|
|
procedure TLZMALenPriceTableEncoder.UpdateTables(const numPosStates:integer);
|
|
var posState:integer;
|
|
begin
|
|
for posState := 0 to numPosStates -1 do
|
|
UpdateTable(posState);
|
|
end;
|
|
|
|
procedure TLZMALenPriceTableEncoder.Encode(const rangeEncoder:PRangeEncoder;symbol:integer;const posState:integer);
|
|
begin
|
|
inherited Encode(rangeEncoder, symbol, posState);
|
|
dec(_counters[posState]);
|
|
if (_counters[posState] = 0) then
|
|
UpdateTable(posState);
|
|
end;
|
|
|
|
procedure TLZMAOptimal.MakeAsChar;
|
|
begin
|
|
BackPrev := -1;
|
|
Prev1IsChar := false;
|
|
end;
|
|
|
|
procedure TLZMAOptimal.MakeAsShortRep;
|
|
begin
|
|
BackPrev := 0;
|
|
Prev1IsChar := false;
|
|
end;
|
|
|
|
function TLZMAOptimal.IsShortRep:boolean;
|
|
begin
|
|
result:=BackPrev = 0;
|
|
end;
|
|
|
|
procedure TLZMAEncoder.DoProgress(const Action:TLZMAProgressAction;const Value:integer);
|
|
begin
|
|
if assigned(fonprogress) then
|
|
fonprogress(action,value);
|
|
end;
|
|
|
|
end.
|