00cc9309cb
de6e324bdseparate emu thread10d3daf86Roms List improvements95d202f37Let's make the rom list process on a separate thread so the emulator doesnt take ages to load.fc306967fWow the ROM Header was just completely busted. Game list view works nowbad1691eefuck this shit2b59e5f46game list in progressd26417b83remappable inputs in progressac4af8106inpute72abc240update readme430139dc9Qt6 frontend3080d4d45Fix this small bug too08cd13b85Cop0 unused functions do not actually pose a threat (as per manual). They don't do anything, so shall we.61bb4fb44make idle loop detection a little more specific with where the load goesb037de4c3SAZDFsdff12e81e73eneed to figure out why n64-systemtest loops indefinitely at some address that appears to be valid (i think it's me not invalidating the cache properly)204f0e13bidle skipping seems to work!cb8bb634asdkfjlasdf58e5c89c1Fix compilation issue on my machine (no idea)24fb2898eattempting more serious idle skipping214719577Place rsp.Step inside cached interpreter. Gains about 3 more fpsbb97dcc23mmmmm920b77d38wjkhasdfjhkasdf430ccdab4it's a start...4f42a673aCached interpreter plays Mario 64. Start looking into RSP as wellc9a030787idle skipping works!5fbda03cenew idea366637abaIdle skipping... maybe?609fa2fb0Cache instructions implemented but broken lmao. Commented out for nowe140a6d12- Stop using inheritance for CPU, instead use composition. - Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like. - More cache work68e613057prep cache impl811b4d809fix clang formatfda755f7didkd5024ebbfsmall MI refactor in preparation of (eventually) implementing the RDRAM interface properly694b45341Merge commit '206dcdedf195fb320913584180edb12c7731e396' as 'external/SDL'206dcdedfSquashed 'external/SDL/' content from commit 4d17b99d0a4d16e1cb4need to update sdl848b19920Fix compilation errordb61b5299Merge commit 'e94a94559f28e49678fbcf72199a5258137b0fe9' as 'external/imgui'e94a94559Squashed 'external/imgui/' content from commit 02e9b8cac52edb3757need to update imguic1a705e86Emulate weird JALR behaviour4b4c32f4bFix exception for "unusable COP1" in 4 instructions i missed accidentally (again)df5828142Bug putting 0s in the log everywheref8b580048Make isviewer a sink to file8241e9735Fix exception for "unusable COP1" in 4 instructions i missed accidentallyb29715f20small changesd9a620bc1make use of my new small utility library0d1aa938eAdd 'external/ircolib/' from commit 'ce3cd726c8df8388d554abf8bb55d55020eb4450'e64eb40b3Fuck git git-subtree-dir: external/ircolib git-subtree-split:de6e324bde
1566 lines
35 KiB
C
1566 lines
35 KiB
C
/* Ppmd8.c -- PPMdI codec
|
|
2023-04-02 : Igor Pavlov : Public domain
|
|
This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */
|
|
|
|
#include "Precomp.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "Ppmd8.h"
|
|
|
|
|
|
|
|
|
|
MY_ALIGN(16)
|
|
static const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
|
|
MY_ALIGN(16)
|
|
static const UInt16 PPMD8_kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
|
|
|
|
#define MAX_FREQ 124
|
|
#define UNIT_SIZE 12
|
|
|
|
#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
|
|
#define U2I(nu) (p->Units2Indx[(size_t)(nu) - 1])
|
|
#define I2U(indx) ((unsigned)p->Indx2Units[indx])
|
|
|
|
|
|
#define REF(ptr) Ppmd_GetRef(p, ptr)
|
|
|
|
#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
|
|
|
|
#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))
|
|
#define STATS(ctx) Ppmd8_GetStats(p, ctx)
|
|
#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)
|
|
#define SUFFIX(ctx) CTX((ctx)->Suffix)
|
|
|
|
typedef CPpmd8_Context * PPMD8_CTX_PTR;
|
|
|
|
struct CPpmd8_Node_;
|
|
|
|
typedef Ppmd_Ref_Type(struct CPpmd8_Node_) CPpmd8_Node_Ref;
|
|
|
|
typedef struct CPpmd8_Node_
|
|
{
|
|
UInt32 Stamp;
|
|
|
|
CPpmd8_Node_Ref Next;
|
|
UInt32 NU;
|
|
} CPpmd8_Node;
|
|
|
|
#define NODE(r) Ppmd_GetPtr_Type(p, r, CPpmd8_Node)
|
|
|
|
void Ppmd8_Construct(CPpmd8 *p)
|
|
{
|
|
unsigned i, k, m;
|
|
|
|
p->Base = NULL;
|
|
|
|
for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
|
|
{
|
|
unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
|
|
do { p->Units2Indx[k++] = (Byte)i; } while (--step);
|
|
p->Indx2Units[i] = (Byte)k;
|
|
}
|
|
|
|
p->NS2BSIndx[0] = (0 << 1);
|
|
p->NS2BSIndx[1] = (1 << 1);
|
|
memset(p->NS2BSIndx + 2, (2 << 1), 9);
|
|
memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
|
|
|
|
for (i = 0; i < 5; i++)
|
|
p->NS2Indx[i] = (Byte)i;
|
|
|
|
for (m = i, k = 1; i < 260; i++)
|
|
{
|
|
p->NS2Indx[i] = (Byte)m;
|
|
if (--k == 0)
|
|
k = (++m) - 4;
|
|
}
|
|
|
|
memcpy(p->ExpEscape, PPMD8_kExpEscape, 16);
|
|
}
|
|
|
|
|
|
void Ppmd8_Free(CPpmd8 *p, ISzAllocPtr alloc)
|
|
{
|
|
ISzAlloc_Free(alloc, p->Base);
|
|
p->Size = 0;
|
|
p->Base = NULL;
|
|
}
|
|
|
|
|
|
BoolInt Ppmd8_Alloc(CPpmd8 *p, UInt32 size, ISzAllocPtr alloc)
|
|
{
|
|
if (!p->Base || p->Size != size)
|
|
{
|
|
Ppmd8_Free(p, alloc);
|
|
p->AlignOffset = (4 - size) & 3;
|
|
if ((p->Base = (Byte *)ISzAlloc_Alloc(alloc, p->AlignOffset + size)) == NULL)
|
|
return False;
|
|
p->Size = size;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
|
|
|
|
// ---------- Internal Memory Allocator ----------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define EMPTY_NODE 0xFFFFFFFF
|
|
|
|
|
|
static void Ppmd8_InsertNode(CPpmd8 *p, void *node, unsigned indx)
|
|
{
|
|
((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;
|
|
((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];
|
|
((CPpmd8_Node *)node)->NU = I2U(indx);
|
|
p->FreeList[indx] = REF(node);
|
|
p->Stamps[indx]++;
|
|
}
|
|
|
|
|
|
static void *Ppmd8_RemoveNode(CPpmd8 *p, unsigned indx)
|
|
{
|
|
CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);
|
|
p->FreeList[indx] = node->Next;
|
|
p->Stamps[indx]--;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static void Ppmd8_SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
|
|
{
|
|
unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
|
|
ptr = (Byte *)ptr + U2B(I2U(newIndx));
|
|
if (I2U(i = U2I(nu)) != nu)
|
|
{
|
|
unsigned k = I2U(--i);
|
|
Ppmd8_InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
|
|
}
|
|
Ppmd8_InsertNode(p, ptr, i);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void Ppmd8_GlueFreeBlocks(CPpmd8 *p)
|
|
{
|
|
/*
|
|
we use first UInt32 field of 12-bytes UNITs as record type stamp
|
|
CPpmd_State { Byte Symbol; Byte Freq; : Freq != 0xFF
|
|
CPpmd8_Context { Byte NumStats; Byte Flags; UInt16 SummFreq; : Flags != 0xFF ???
|
|
CPpmd8_Node { UInt32 Stamp : Stamp == 0xFFFFFFFF for free record
|
|
: Stamp == 0 for guard
|
|
Last 12-bytes UNIT in array is always contains 12-bytes order-0 CPpmd8_Context record
|
|
*/
|
|
CPpmd8_Node_Ref n;
|
|
|
|
p->GlueCount = 1 << 13;
|
|
memset(p->Stamps, 0, sizeof(p->Stamps));
|
|
|
|
/* we set guard NODE at LoUnit */
|
|
if (p->LoUnit != p->HiUnit)
|
|
((CPpmd8_Node *)(void *)p->LoUnit)->Stamp = 0;
|
|
|
|
{
|
|
/* Glue free blocks */
|
|
CPpmd8_Node_Ref *prev = &n;
|
|
unsigned i;
|
|
for (i = 0; i < PPMD_NUM_INDEXES; i++)
|
|
{
|
|
|
|
CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];
|
|
p->FreeList[i] = 0;
|
|
while (next != 0)
|
|
{
|
|
CPpmd8_Node *node = NODE(next);
|
|
UInt32 nu = node->NU;
|
|
*prev = next;
|
|
next = node->Next;
|
|
if (nu != 0)
|
|
{
|
|
CPpmd8_Node *node2;
|
|
prev = &(node->Next);
|
|
while ((node2 = node + nu)->Stamp == EMPTY_NODE)
|
|
{
|
|
nu += node2->NU;
|
|
node2->NU = 0;
|
|
node->NU = nu;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*prev = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Fill lists of free blocks */
|
|
while (n != 0)
|
|
{
|
|
CPpmd8_Node *node = NODE(n);
|
|
UInt32 nu = node->NU;
|
|
unsigned i;
|
|
n = node->Next;
|
|
if (nu == 0)
|
|
continue;
|
|
for (; nu > 128; nu -= 128, node += 128)
|
|
Ppmd8_InsertNode(p, node, PPMD_NUM_INDEXES - 1);
|
|
if (I2U(i = U2I(nu)) != nu)
|
|
{
|
|
unsigned k = I2U(--i);
|
|
Ppmd8_InsertNode(p, node + k, (unsigned)nu - k - 1);
|
|
}
|
|
Ppmd8_InsertNode(p, node, i);
|
|
}
|
|
}
|
|
|
|
|
|
Z7_NO_INLINE
|
|
static void *Ppmd8_AllocUnitsRare(CPpmd8 *p, unsigned indx)
|
|
{
|
|
unsigned i;
|
|
|
|
if (p->GlueCount == 0)
|
|
{
|
|
Ppmd8_GlueFreeBlocks(p);
|
|
if (p->FreeList[indx] != 0)
|
|
return Ppmd8_RemoveNode(p, indx);
|
|
}
|
|
|
|
i = indx;
|
|
|
|
do
|
|
{
|
|
if (++i == PPMD_NUM_INDEXES)
|
|
{
|
|
UInt32 numBytes = U2B(I2U(indx));
|
|
Byte *us = p->UnitsStart;
|
|
p->GlueCount--;
|
|
return ((UInt32)(us - p->Text) > numBytes) ? (p->UnitsStart = us - numBytes) : (NULL);
|
|
}
|
|
}
|
|
while (p->FreeList[i] == 0);
|
|
|
|
{
|
|
void *block = Ppmd8_RemoveNode(p, i);
|
|
Ppmd8_SplitBlock(p, block, i, indx);
|
|
return block;
|
|
}
|
|
}
|
|
|
|
|
|
static void *Ppmd8_AllocUnits(CPpmd8 *p, unsigned indx)
|
|
{
|
|
if (p->FreeList[indx] != 0)
|
|
return Ppmd8_RemoveNode(p, indx);
|
|
{
|
|
UInt32 numBytes = U2B(I2U(indx));
|
|
Byte *lo = p->LoUnit;
|
|
if ((UInt32)(p->HiUnit - lo) >= numBytes)
|
|
{
|
|
p->LoUnit = lo + numBytes;
|
|
return lo;
|
|
}
|
|
}
|
|
return Ppmd8_AllocUnitsRare(p, indx);
|
|
}
|
|
|
|
|
|
#define MEM_12_CPY(dest, src, num) \
|
|
{ UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \
|
|
do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }
|
|
|
|
|
|
|
|
static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
|
|
{
|
|
unsigned i0 = U2I(oldNU);
|
|
unsigned i1 = U2I(newNU);
|
|
if (i0 == i1)
|
|
return oldPtr;
|
|
if (p->FreeList[i1] != 0)
|
|
{
|
|
void *ptr = Ppmd8_RemoveNode(p, i1);
|
|
MEM_12_CPY(ptr, oldPtr, newNU)
|
|
Ppmd8_InsertNode(p, oldPtr, i0);
|
|
return ptr;
|
|
}
|
|
Ppmd8_SplitBlock(p, oldPtr, i0, i1);
|
|
return oldPtr;
|
|
}
|
|
|
|
|
|
static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)
|
|
{
|
|
Ppmd8_InsertNode(p, ptr, U2I(nu));
|
|
}
|
|
|
|
|
|
static void SpecialFreeUnit(CPpmd8 *p, void *ptr)
|
|
{
|
|
if ((Byte *)ptr != p->UnitsStart)
|
|
Ppmd8_InsertNode(p, ptr, 0);
|
|
else
|
|
{
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
*(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts() */
|
|
#endif
|
|
p->UnitsStart += UNIT_SIZE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)
|
|
{
|
|
unsigned indx = U2I(nu);
|
|
void *ptr;
|
|
if ((Byte *)oldPtr > p->UnitsStart + (1 << 14) || REF(oldPtr) > p->FreeList[indx])
|
|
return oldPtr;
|
|
ptr = Ppmd8_RemoveNode(p, indx);
|
|
MEM_12_CPY(ptr, oldPtr, nu)
|
|
if ((Byte *)oldPtr != p->UnitsStart)
|
|
Ppmd8_InsertNode(p, oldPtr, indx);
|
|
else
|
|
p->UnitsStart += U2B(I2U(indx));
|
|
return ptr;
|
|
}
|
|
*/
|
|
|
|
static void ExpandTextArea(CPpmd8 *p)
|
|
{
|
|
UInt32 count[PPMD_NUM_INDEXES];
|
|
unsigned i;
|
|
|
|
memset(count, 0, sizeof(count));
|
|
if (p->LoUnit != p->HiUnit)
|
|
((CPpmd8_Node *)(void *)p->LoUnit)->Stamp = 0;
|
|
|
|
{
|
|
CPpmd8_Node *node = (CPpmd8_Node *)(void *)p->UnitsStart;
|
|
while (node->Stamp == EMPTY_NODE)
|
|
{
|
|
UInt32 nu = node->NU;
|
|
node->Stamp = 0;
|
|
count[U2I(nu)]++;
|
|
node += nu;
|
|
}
|
|
p->UnitsStart = (Byte *)node;
|
|
}
|
|
|
|
for (i = 0; i < PPMD_NUM_INDEXES; i++)
|
|
{
|
|
UInt32 cnt = count[i];
|
|
if (cnt == 0)
|
|
continue;
|
|
{
|
|
CPpmd8_Node_Ref *prev = (CPpmd8_Node_Ref *)&p->FreeList[i];
|
|
CPpmd8_Node_Ref n = *prev;
|
|
p->Stamps[i] -= cnt;
|
|
for (;;)
|
|
{
|
|
CPpmd8_Node *node = NODE(n);
|
|
n = node->Next;
|
|
if (node->Stamp != 0)
|
|
{
|
|
prev = &node->Next;
|
|
continue;
|
|
}
|
|
*prev = n;
|
|
if (--cnt == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define SUCCESSOR(p) Ppmd_GET_SUCCESSOR(p)
|
|
static void Ppmd8State_SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
|
|
{
|
|
Ppmd_SET_SUCCESSOR(p, v)
|
|
}
|
|
|
|
#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }
|
|
|
|
Z7_NO_INLINE
|
|
static
|
|
void Ppmd8_RestartModel(CPpmd8 *p)
|
|
{
|
|
unsigned i, k, m;
|
|
|
|
memset(p->FreeList, 0, sizeof(p->FreeList));
|
|
memset(p->Stamps, 0, sizeof(p->Stamps));
|
|
RESET_TEXT(0)
|
|
p->HiUnit = p->Text + p->Size;
|
|
p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
|
|
p->GlueCount = 0;
|
|
|
|
p->OrderFall = p->MaxOrder;
|
|
p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
|
|
p->PrevSuccess = 0;
|
|
|
|
{
|
|
CPpmd8_Context *mc = (PPMD8_CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
|
|
CPpmd_State *s = (CPpmd_State *)p->LoUnit; /* Ppmd8_AllocUnits(p, PPMD_NUM_INDEXES - 1); */
|
|
|
|
p->LoUnit += U2B(256 / 2);
|
|
p->MaxContext = p->MinContext = mc;
|
|
p->FoundState = s;
|
|
mc->Flags = 0;
|
|
mc->NumStats = 256 - 1;
|
|
mc->Union2.SummFreq = 256 + 1;
|
|
mc->Union4.Stats = REF(s);
|
|
mc->Suffix = 0;
|
|
|
|
for (i = 0; i < 256; i++, s++)
|
|
{
|
|
s->Symbol = (Byte)i;
|
|
s->Freq = 1;
|
|
Ppmd8State_SetSuccessor(s, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i = m = 0; m < 25; m++)
|
|
{
|
|
while (p->NS2Indx[i] == m)
|
|
i++;
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
unsigned r;
|
|
UInt16 *dest = p->BinSumm[m] + k;
|
|
const UInt16 val = (UInt16)(PPMD_BIN_SCALE - PPMD8_kInitBinEsc[k] / (i + 1));
|
|
for (r = 0; r < 64; r += 8)
|
|
dest[r] = val;
|
|
}
|
|
}
|
|
|
|
for (i = m = 0; m < 24; m++)
|
|
{
|
|
unsigned summ;
|
|
CPpmd_See *s;
|
|
while (p->NS2Indx[(size_t)i + 3] == m + 3)
|
|
i++;
|
|
s = p->See[m];
|
|
summ = ((2 * i + 5) << (PPMD_PERIOD_BITS - 4));
|
|
for (k = 0; k < 32; k++, s++)
|
|
{
|
|
s->Summ = (UInt16)summ;
|
|
s->Shift = (PPMD_PERIOD_BITS - 4);
|
|
s->Count = 7;
|
|
}
|
|
}
|
|
|
|
p->DummySee.Summ = 0; /* unused */
|
|
p->DummySee.Shift = PPMD_PERIOD_BITS;
|
|
p->DummySee.Count = 64; /* unused */
|
|
}
|
|
|
|
|
|
void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
|
|
{
|
|
p->MaxOrder = maxOrder;
|
|
p->RestoreMethod = restoreMethod;
|
|
Ppmd8_RestartModel(p);
|
|
}
|
|
|
|
|
|
#define FLAG_RESCALED (1 << 2)
|
|
// #define FLAG_SYM_HIGH (1 << 3)
|
|
#define FLAG_PREV_HIGH (1 << 4)
|
|
|
|
#define HiBits_Prepare(sym) ((unsigned)(sym) + 0xC0)
|
|
|
|
#define HiBits_Convert_3(flags) (((flags) >> (8 - 3)) & (1 << 3))
|
|
#define HiBits_Convert_4(flags) (((flags) >> (8 - 4)) & (1 << 4))
|
|
|
|
#define PPMD8_HiBitsFlag_3(sym) HiBits_Convert_3(HiBits_Prepare(sym))
|
|
#define PPMD8_HiBitsFlag_4(sym) HiBits_Convert_4(HiBits_Prepare(sym))
|
|
|
|
// #define PPMD8_HiBitsFlag_3(sym) (0x08 * ((sym) >= 0x40))
|
|
// #define PPMD8_HiBitsFlag_4(sym) (0x10 * ((sym) >= 0x40))
|
|
|
|
/*
|
|
Refresh() is called when we remove some symbols (successors) in context.
|
|
It increases Escape_Freq for sum of all removed symbols.
|
|
*/
|
|
|
|
static void Refresh(CPpmd8 *p, PPMD8_CTX_PTR ctx, unsigned oldNU, unsigned scale)
|
|
{
|
|
unsigned i = ctx->NumStats, escFreq, sumFreq, flags;
|
|
CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);
|
|
ctx->Union4.Stats = REF(s);
|
|
|
|
// #ifdef PPMD8_FREEZE_SUPPORT
|
|
/*
|
|
(ctx->Union2.SummFreq >= ((UInt32)1 << 15)) can be in FREEZE mode for some files.
|
|
It's not good for range coder. So new versions of support fix:
|
|
- original PPMdI code rev.1
|
|
+ original PPMdI code rev.2
|
|
- 7-Zip default ((PPMD8_FREEZE_SUPPORT is not defined)
|
|
+ 7-Zip (p->RestoreMethod >= PPMD8_RESTORE_METHOD_FREEZE)
|
|
if we use that fixed line, we can lose compatibility with some files created before fix
|
|
if we don't use that fixed line, the program can work incorrectly in FREEZE mode in rare case.
|
|
*/
|
|
// if (p->RestoreMethod >= PPMD8_RESTORE_METHOD_FREEZE)
|
|
{
|
|
scale |= (ctx->Union2.SummFreq >= ((UInt32)1 << 15));
|
|
}
|
|
// #endif
|
|
|
|
|
|
|
|
flags = HiBits_Prepare(s->Symbol);
|
|
{
|
|
unsigned freq = s->Freq;
|
|
escFreq = ctx->Union2.SummFreq - freq;
|
|
freq = (freq + scale) >> scale;
|
|
sumFreq = freq;
|
|
s->Freq = (Byte)freq;
|
|
}
|
|
|
|
do
|
|
{
|
|
unsigned freq = (++s)->Freq;
|
|
escFreq -= freq;
|
|
freq = (freq + scale) >> scale;
|
|
sumFreq += freq;
|
|
s->Freq = (Byte)freq;
|
|
flags |= HiBits_Prepare(s->Symbol);
|
|
}
|
|
while (--i);
|
|
|
|
ctx->Union2.SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));
|
|
ctx->Flags = (Byte)((ctx->Flags & (FLAG_PREV_HIGH + FLAG_RESCALED * scale)) + HiBits_Convert_3(flags));
|
|
}
|
|
|
|
|
|
static void SWAP_STATES(CPpmd_State *t1, CPpmd_State *t2)
|
|
{
|
|
CPpmd_State tmp = *t1;
|
|
*t1 = *t2;
|
|
*t2 = tmp;
|
|
}
|
|
|
|
|
|
/*
|
|
CutOff() reduces contexts:
|
|
It conversts Successors at MaxOrder to another Contexts to NULL-Successors
|
|
It removes RAW-Successors and NULL-Successors that are not Order-0
|
|
and it removes contexts when it has no Successors.
|
|
if the (Union4.Stats) is close to (UnitsStart), it moves it up.
|
|
*/
|
|
|
|
static CPpmd_Void_Ref CutOff(CPpmd8 *p, PPMD8_CTX_PTR ctx, unsigned order)
|
|
{
|
|
int ns = ctx->NumStats;
|
|
unsigned nu;
|
|
CPpmd_State *stats;
|
|
|
|
if (ns == 0)
|
|
{
|
|
CPpmd_State *s = ONE_STATE(ctx);
|
|
CPpmd_Void_Ref successor = SUCCESSOR(s);
|
|
if ((Byte *)Ppmd8_GetPtr(p, successor) >= p->UnitsStart)
|
|
{
|
|
if (order < p->MaxOrder)
|
|
successor = CutOff(p, CTX(successor), order + 1);
|
|
else
|
|
successor = 0;
|
|
Ppmd8State_SetSuccessor(s, successor);
|
|
if (successor || order <= 9) /* O_BOUND */
|
|
return REF(ctx);
|
|
}
|
|
SpecialFreeUnit(p, ctx);
|
|
return 0;
|
|
}
|
|
|
|
nu = ((unsigned)ns + 2) >> 1;
|
|
// ctx->Union4.Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), nu));
|
|
{
|
|
unsigned indx = U2I(nu);
|
|
stats = STATS(ctx);
|
|
|
|
if ((UInt32)((Byte *)stats - p->UnitsStart) <= (1 << 14)
|
|
&& (CPpmd_Void_Ref)ctx->Union4.Stats <= p->FreeList[indx])
|
|
{
|
|
void *ptr = Ppmd8_RemoveNode(p, indx);
|
|
ctx->Union4.Stats = STATS_REF(ptr);
|
|
MEM_12_CPY(ptr, (const void *)stats, nu)
|
|
if ((Byte *)stats != p->UnitsStart)
|
|
Ppmd8_InsertNode(p, stats, indx);
|
|
else
|
|
p->UnitsStart += U2B(I2U(indx));
|
|
stats = ptr;
|
|
}
|
|
}
|
|
|
|
{
|
|
CPpmd_State *s = stats + (unsigned)ns;
|
|
do
|
|
{
|
|
CPpmd_Void_Ref successor = SUCCESSOR(s);
|
|
if ((Byte *)Ppmd8_GetPtr(p, successor) < p->UnitsStart)
|
|
{
|
|
CPpmd_State *s2 = stats + (unsigned)(ns--);
|
|
if (order)
|
|
{
|
|
if (s != s2)
|
|
*s = *s2;
|
|
}
|
|
else
|
|
{
|
|
SWAP_STATES(s, s2);
|
|
Ppmd8State_SetSuccessor(s2, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (order < p->MaxOrder)
|
|
Ppmd8State_SetSuccessor(s, CutOff(p, CTX(successor), order + 1));
|
|
else
|
|
Ppmd8State_SetSuccessor(s, 0);
|
|
}
|
|
}
|
|
while (--s >= stats);
|
|
}
|
|
|
|
if (ns != ctx->NumStats && order)
|
|
{
|
|
if (ns < 0)
|
|
{
|
|
FreeUnits(p, stats, nu);
|
|
SpecialFreeUnit(p, ctx);
|
|
return 0;
|
|
}
|
|
ctx->NumStats = (Byte)ns;
|
|
if (ns == 0)
|
|
{
|
|
const Byte sym = stats->Symbol;
|
|
ctx->Flags = (Byte)((ctx->Flags & FLAG_PREV_HIGH) + PPMD8_HiBitsFlag_3(sym));
|
|
// *ONE_STATE(ctx) = *stats;
|
|
ctx->Union2.State2.Symbol = sym;
|
|
ctx->Union2.State2.Freq = (Byte)(((unsigned)stats->Freq + 11) >> 3);
|
|
ctx->Union4.State4.Successor_0 = stats->Successor_0;
|
|
ctx->Union4.State4.Successor_1 = stats->Successor_1;
|
|
FreeUnits(p, stats, nu);
|
|
}
|
|
else
|
|
{
|
|
Refresh(p, ctx, nu, ctx->Union2.SummFreq > 16 * (unsigned)ns);
|
|
}
|
|
}
|
|
|
|
return REF(ctx);
|
|
}
|
|
|
|
|
|
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
|
|
/*
|
|
RemoveBinContexts()
|
|
It conversts Successors at MaxOrder to another Contexts to NULL-Successors
|
|
It changes RAW-Successors to NULL-Successors
|
|
removes Bin Context without Successor, if suffix of that context is also binary.
|
|
*/
|
|
|
|
static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, PPMD8_CTX_PTR ctx, unsigned order)
|
|
{
|
|
if (!ctx->NumStats)
|
|
{
|
|
CPpmd_State *s = ONE_STATE(ctx);
|
|
CPpmd_Void_Ref successor = SUCCESSOR(s);
|
|
if ((Byte *)Ppmd8_GetPtr(p, successor) >= p->UnitsStart && order < p->MaxOrder)
|
|
successor = RemoveBinContexts(p, CTX(successor), order + 1);
|
|
else
|
|
successor = 0;
|
|
Ppmd8State_SetSuccessor(s, successor);
|
|
/* Suffix context can be removed already, since different (high-order)
|
|
Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */
|
|
if (!successor && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))
|
|
{
|
|
FreeUnits(p, ctx, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPpmd_State *s = STATS(ctx) + ctx->NumStats;
|
|
do
|
|
{
|
|
CPpmd_Void_Ref successor = SUCCESSOR(s);
|
|
if ((Byte *)Ppmd8_GetPtr(p, successor) >= p->UnitsStart && order < p->MaxOrder)
|
|
Ppmd8State_SetSuccessor(s, RemoveBinContexts(p, CTX(successor), order + 1));
|
|
else
|
|
Ppmd8State_SetSuccessor(s, 0);
|
|
}
|
|
while (--s >= STATS(ctx));
|
|
}
|
|
|
|
return REF(ctx);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static UInt32 GetUsedMemory(const CPpmd8 *p)
|
|
{
|
|
UInt32 v = 0;
|
|
unsigned i;
|
|
for (i = 0; i < PPMD_NUM_INDEXES; i++)
|
|
v += p->Stamps[i] * I2U(i);
|
|
return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);
|
|
}
|
|
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
#define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)
|
|
#else
|
|
#define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)
|
|
#endif
|
|
|
|
|
|
static void RestoreModel(CPpmd8 *p, PPMD8_CTX_PTR ctxError
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
, PPMD8_CTX_PTR fSuccessor
|
|
#endif
|
|
)
|
|
{
|
|
PPMD8_CTX_PTR c;
|
|
CPpmd_State *s;
|
|
RESET_TEXT(0)
|
|
|
|
// we go here in cases of error of allocation for context (c1)
|
|
// Order(MinContext) < Order(ctxError) <= Order(MaxContext)
|
|
|
|
// We remove last symbol from each of contexts [p->MaxContext ... ctxError) contexts
|
|
// So we rollback all created (symbols) before error.
|
|
for (c = p->MaxContext; c != ctxError; c = SUFFIX(c))
|
|
if (--(c->NumStats) == 0)
|
|
{
|
|
s = STATS(c);
|
|
c->Flags = (Byte)((c->Flags & FLAG_PREV_HIGH) + PPMD8_HiBitsFlag_3(s->Symbol));
|
|
// *ONE_STATE(c) = *s;
|
|
c->Union2.State2.Symbol = s->Symbol;
|
|
c->Union2.State2.Freq = (Byte)(((unsigned)s->Freq + 11) >> 3);
|
|
c->Union4.State4.Successor_0 = s->Successor_0;
|
|
c->Union4.State4.Successor_1 = s->Successor_1;
|
|
|
|
SpecialFreeUnit(p, s);
|
|
}
|
|
else
|
|
{
|
|
/* Refresh() can increase Escape_Freq on value of Freq of last symbol, that was added before error.
|
|
so the largest possible increase for Escape_Freq is (8) from value before ModelUpoadet() */
|
|
Refresh(p, c, ((unsigned)c->NumStats + 3) >> 1, 0);
|
|
}
|
|
|
|
// increase Escape Freq for context [ctxError ... p->MinContext)
|
|
for (; c != p->MinContext; c = SUFFIX(c))
|
|
if (c->NumStats == 0)
|
|
{
|
|
// ONE_STATE(c)
|
|
c->Union2.State2.Freq = (Byte)(((unsigned)c->Union2.State2.Freq + 1) >> 1);
|
|
}
|
|
else if ((c->Union2.SummFreq = (UInt16)(c->Union2.SummFreq + 4)) > 128 + 4 * c->NumStats)
|
|
Refresh(p, c, ((unsigned)c->NumStats + 2) >> 1, 1);
|
|
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
|
|
{
|
|
p->MaxContext = fSuccessor;
|
|
p->GlueCount += !(p->Stamps[1] & 1); // why?
|
|
}
|
|
else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)
|
|
{
|
|
while (p->MaxContext->Suffix)
|
|
p->MaxContext = SUFFIX(p->MaxContext);
|
|
RemoveBinContexts(p, p->MaxContext, 0);
|
|
// we change the current mode to (PPMD8_RESTORE_METHOD_FREEZE + 1)
|
|
p->RestoreMethod = PPMD8_RESTORE_METHOD_FREEZE + 1;
|
|
p->GlueCount = 0;
|
|
p->OrderFall = p->MaxOrder;
|
|
}
|
|
else
|
|
#endif
|
|
if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))
|
|
Ppmd8_RestartModel(p);
|
|
else
|
|
{
|
|
while (p->MaxContext->Suffix)
|
|
p->MaxContext = SUFFIX(p->MaxContext);
|
|
do
|
|
{
|
|
CutOff(p, p->MaxContext, 0);
|
|
ExpandTextArea(p);
|
|
}
|
|
while (GetUsedMemory(p) > 3 * (p->Size >> 2));
|
|
p->GlueCount = 0;
|
|
p->OrderFall = p->MaxOrder;
|
|
}
|
|
p->MinContext = p->MaxContext;
|
|
}
|
|
|
|
|
|
|
|
Z7_NO_INLINE
|
|
static PPMD8_CTX_PTR Ppmd8_CreateSuccessors(CPpmd8 *p, BoolInt skip, CPpmd_State *s1, PPMD8_CTX_PTR c)
|
|
{
|
|
|
|
CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
|
|
Byte newSym, newFreq, flags;
|
|
unsigned numPs = 0;
|
|
CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; /* fixed over Shkarin's code. Maybe it could work without + 1 too. */
|
|
|
|
if (!skip)
|
|
ps[numPs++] = p->FoundState;
|
|
|
|
while (c->Suffix)
|
|
{
|
|
CPpmd_Void_Ref successor;
|
|
CPpmd_State *s;
|
|
c = SUFFIX(c);
|
|
|
|
if (s1) { s = s1; s1 = NULL; }
|
|
else if (c->NumStats != 0)
|
|
{
|
|
Byte sym = p->FoundState->Symbol;
|
|
for (s = STATS(c); s->Symbol != sym; s++);
|
|
if (s->Freq < MAX_FREQ - 9) { s->Freq++; c->Union2.SummFreq++; }
|
|
}
|
|
else
|
|
{
|
|
s = ONE_STATE(c);
|
|
s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24)));
|
|
}
|
|
successor = SUCCESSOR(s);
|
|
if (successor != upBranch)
|
|
{
|
|
|
|
c = CTX(successor);
|
|
if (numPs == 0)
|
|
{
|
|
|
|
|
|
return c;
|
|
}
|
|
break;
|
|
}
|
|
ps[numPs++] = s;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
newSym = *(const Byte *)Ppmd8_GetPtr(p, upBranch);
|
|
upBranch++;
|
|
flags = (Byte)(PPMD8_HiBitsFlag_4(p->FoundState->Symbol) + PPMD8_HiBitsFlag_3(newSym));
|
|
|
|
if (c->NumStats == 0)
|
|
newFreq = c->Union2.State2.Freq;
|
|
else
|
|
{
|
|
UInt32 cf, s0;
|
|
CPpmd_State *s;
|
|
for (s = STATS(c); s->Symbol != newSym; s++);
|
|
cf = (UInt32)s->Freq - 1;
|
|
s0 = (UInt32)c->Union2.SummFreq - c->NumStats - cf;
|
|
/*
|
|
|
|
|
|
max(newFreq)= (s->Freq - 1), when (s0 == 1)
|
|
|
|
|
|
*/
|
|
newFreq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));
|
|
}
|
|
|
|
|
|
|
|
do
|
|
{
|
|
PPMD8_CTX_PTR c1;
|
|
/* = AllocContext(p); */
|
|
if (p->HiUnit != p->LoUnit)
|
|
c1 = (PPMD8_CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE);
|
|
else if (p->FreeList[0] != 0)
|
|
c1 = (PPMD8_CTX_PTR)Ppmd8_RemoveNode(p, 0);
|
|
else
|
|
{
|
|
c1 = (PPMD8_CTX_PTR)Ppmd8_AllocUnitsRare(p, 0);
|
|
if (!c1)
|
|
return NULL;
|
|
}
|
|
c1->Flags = flags;
|
|
c1->NumStats = 0;
|
|
c1->Union2.State2.Symbol = newSym;
|
|
c1->Union2.State2.Freq = newFreq;
|
|
Ppmd8State_SetSuccessor(ONE_STATE(c1), upBranch);
|
|
c1->Suffix = REF(c);
|
|
Ppmd8State_SetSuccessor(ps[--numPs], REF(c1));
|
|
c = c1;
|
|
}
|
|
while (numPs != 0);
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
static PPMD8_CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, PPMD8_CTX_PTR c)
|
|
{
|
|
CPpmd_State *s = NULL;
|
|
PPMD8_CTX_PTR c1 = c;
|
|
CPpmd_Void_Ref upBranch = REF(p->Text);
|
|
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
/* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */
|
|
CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
|
|
unsigned numPs = 0;
|
|
ps[numPs++] = p->FoundState;
|
|
#endif
|
|
|
|
Ppmd8State_SetSuccessor(p->FoundState, upBranch);
|
|
p->OrderFall++;
|
|
|
|
for (;;)
|
|
{
|
|
if (s1)
|
|
{
|
|
c = SUFFIX(c);
|
|
s = s1;
|
|
s1 = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!c->Suffix)
|
|
{
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
|
|
{
|
|
do { Ppmd8State_SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
|
|
RESET_TEXT(1)
|
|
p->OrderFall = 1;
|
|
}
|
|
#endif
|
|
return c;
|
|
}
|
|
c = SUFFIX(c);
|
|
if (c->NumStats)
|
|
{
|
|
if ((s = STATS(c))->Symbol != p->FoundState->Symbol)
|
|
do { s++; } while (s->Symbol != p->FoundState->Symbol);
|
|
if (s->Freq < MAX_FREQ - 9)
|
|
{
|
|
s->Freq = (Byte)(s->Freq + 2);
|
|
c->Union2.SummFreq = (UInt16)(c->Union2.SummFreq + 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s = ONE_STATE(c);
|
|
s->Freq = (Byte)(s->Freq + (s->Freq < 32));
|
|
}
|
|
}
|
|
if (SUCCESSOR(s))
|
|
break;
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
ps[numPs++] = s;
|
|
#endif
|
|
Ppmd8State_SetSuccessor(s, upBranch);
|
|
p->OrderFall++;
|
|
}
|
|
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
|
|
{
|
|
c = CTX(SUCCESSOR(s));
|
|
do { Ppmd8State_SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
|
|
RESET_TEXT(1)
|
|
p->OrderFall = 1;
|
|
return c;
|
|
}
|
|
else
|
|
#endif
|
|
if (SUCCESSOR(s) <= upBranch)
|
|
{
|
|
PPMD8_CTX_PTR successor;
|
|
CPpmd_State *s2 = p->FoundState;
|
|
p->FoundState = s;
|
|
|
|
successor = Ppmd8_CreateSuccessors(p, False, NULL, c);
|
|
if (!successor)
|
|
Ppmd8State_SetSuccessor(s, 0);
|
|
else
|
|
Ppmd8State_SetSuccessor(s, REF(successor));
|
|
p->FoundState = s2;
|
|
}
|
|
|
|
{
|
|
CPpmd_Void_Ref successor = SUCCESSOR(s);
|
|
if (p->OrderFall == 1 && c1 == p->MaxContext)
|
|
{
|
|
Ppmd8State_SetSuccessor(p->FoundState, successor);
|
|
p->Text--;
|
|
}
|
|
if (successor == 0)
|
|
return NULL;
|
|
return CTX(successor);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Ppmd8_UpdateModel(CPpmd8 *p);
|
|
Z7_NO_INLINE
|
|
void Ppmd8_UpdateModel(CPpmd8 *p)
|
|
{
|
|
CPpmd_Void_Ref maxSuccessor, minSuccessor = SUCCESSOR(p->FoundState);
|
|
PPMD8_CTX_PTR c;
|
|
unsigned s0, ns, fFreq = p->FoundState->Freq;
|
|
Byte flag, fSymbol = p->FoundState->Symbol;
|
|
{
|
|
CPpmd_State *s = NULL;
|
|
if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
|
|
{
|
|
/* Update Freqs in Suffix Context */
|
|
|
|
c = SUFFIX(p->MinContext);
|
|
|
|
if (c->NumStats == 0)
|
|
{
|
|
s = ONE_STATE(c);
|
|
if (s->Freq < 32)
|
|
s->Freq++;
|
|
}
|
|
else
|
|
{
|
|
Byte sym = p->FoundState->Symbol;
|
|
s = STATS(c);
|
|
|
|
if (s->Symbol != sym)
|
|
{
|
|
do
|
|
{
|
|
|
|
s++;
|
|
}
|
|
while (s->Symbol != sym);
|
|
|
|
if (s[0].Freq >= s[-1].Freq)
|
|
{
|
|
SWAP_STATES(&s[0], &s[-1]);
|
|
s--;
|
|
}
|
|
}
|
|
|
|
if (s->Freq < MAX_FREQ - 9)
|
|
{
|
|
s->Freq = (Byte)(s->Freq + 2);
|
|
c->Union2.SummFreq = (UInt16)(c->Union2.SummFreq + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
c = p->MaxContext;
|
|
if (p->OrderFall == 0 && minSuccessor)
|
|
{
|
|
PPMD8_CTX_PTR cs = Ppmd8_CreateSuccessors(p, True, s, p->MinContext);
|
|
if (!cs)
|
|
{
|
|
Ppmd8State_SetSuccessor(p->FoundState, 0);
|
|
RESTORE_MODEL(c, CTX(minSuccessor));
|
|
return;
|
|
}
|
|
Ppmd8State_SetSuccessor(p->FoundState, REF(cs));
|
|
p->MinContext = p->MaxContext = cs;
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
Byte *text = p->Text;
|
|
*text++ = p->FoundState->Symbol;
|
|
p->Text = text;
|
|
if (text >= p->UnitsStart)
|
|
{
|
|
RESTORE_MODEL(c, CTX(minSuccessor)); /* check it */
|
|
return;
|
|
}
|
|
maxSuccessor = REF(text);
|
|
}
|
|
|
|
if (!minSuccessor)
|
|
{
|
|
PPMD8_CTX_PTR cs = ReduceOrder(p, s, p->MinContext);
|
|
if (!cs)
|
|
{
|
|
RESTORE_MODEL(c, NULL);
|
|
return;
|
|
}
|
|
minSuccessor = REF(cs);
|
|
}
|
|
else if ((Byte *)Ppmd8_GetPtr(p, minSuccessor) < p->UnitsStart)
|
|
{
|
|
PPMD8_CTX_PTR cs = Ppmd8_CreateSuccessors(p, False, s, p->MinContext);
|
|
if (!cs)
|
|
{
|
|
RESTORE_MODEL(c, NULL);
|
|
return;
|
|
}
|
|
minSuccessor = REF(cs);
|
|
}
|
|
|
|
if (--p->OrderFall == 0)
|
|
{
|
|
maxSuccessor = minSuccessor;
|
|
p->Text -= (p->MaxContext != p->MinContext);
|
|
}
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
|
|
{
|
|
maxSuccessor = minSuccessor;
|
|
RESET_TEXT(0)
|
|
p->OrderFall = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flag = (Byte)(PPMD8_HiBitsFlag_3(fSymbol));
|
|
s0 = p->MinContext->Union2.SummFreq - (ns = p->MinContext->NumStats) - fFreq;
|
|
|
|
for (; c != p->MinContext; c = SUFFIX(c))
|
|
{
|
|
unsigned ns1;
|
|
UInt32 sum;
|
|
|
|
if ((ns1 = c->NumStats) != 0)
|
|
{
|
|
if ((ns1 & 1) != 0)
|
|
{
|
|
/* Expand for one UNIT */
|
|
unsigned oldNU = (ns1 + 1) >> 1;
|
|
unsigned i = U2I(oldNU);
|
|
if (i != U2I((size_t)oldNU + 1))
|
|
{
|
|
void *ptr = Ppmd8_AllocUnits(p, i + 1);
|
|
void *oldPtr;
|
|
if (!ptr)
|
|
{
|
|
RESTORE_MODEL(c, CTX(minSuccessor));
|
|
return;
|
|
}
|
|
oldPtr = STATS(c);
|
|
MEM_12_CPY(ptr, oldPtr, oldNU)
|
|
Ppmd8_InsertNode(p, oldPtr, i);
|
|
c->Union4.Stats = STATS_REF(ptr);
|
|
}
|
|
}
|
|
sum = c->Union2.SummFreq;
|
|
/* max increase of Escape_Freq is 1 here.
|
|
an average increase is 1/3 per symbol */
|
|
sum += (3 * ns1 + 1 < ns);
|
|
/* original PPMdH uses 16-bit variable for (sum) here.
|
|
But (sum < ???). Do we need to truncate (sum) to 16-bit */
|
|
// sum = (UInt16)sum;
|
|
}
|
|
else
|
|
{
|
|
|
|
CPpmd_State *s = (CPpmd_State*)Ppmd8_AllocUnits(p, 0);
|
|
if (!s)
|
|
{
|
|
RESTORE_MODEL(c, CTX(minSuccessor));
|
|
return;
|
|
}
|
|
{
|
|
unsigned freq = c->Union2.State2.Freq;
|
|
// s = *ONE_STATE(c);
|
|
s->Symbol = c->Union2.State2.Symbol;
|
|
s->Successor_0 = c->Union4.State4.Successor_0;
|
|
s->Successor_1 = c->Union4.State4.Successor_1;
|
|
// Ppmd8State_SetSuccessor(s, c->Union4.Stats); // call it only for debug purposes to check the order of
|
|
// (Successor_0 and Successor_1) in LE/BE.
|
|
c->Union4.Stats = REF(s);
|
|
if (freq < MAX_FREQ / 4 - 1)
|
|
freq <<= 1;
|
|
else
|
|
freq = MAX_FREQ - 4;
|
|
|
|
s->Freq = (Byte)freq;
|
|
|
|
sum = freq + p->InitEsc + (ns > 2); // Ppmd8 (> 2)
|
|
}
|
|
}
|
|
|
|
{
|
|
CPpmd_State *s = STATS(c) + ns1 + 1;
|
|
UInt32 cf = 2 * (sum + 6) * (UInt32)fFreq;
|
|
UInt32 sf = (UInt32)s0 + sum;
|
|
s->Symbol = fSymbol;
|
|
c->NumStats = (Byte)(ns1 + 1);
|
|
Ppmd8State_SetSuccessor(s, maxSuccessor);
|
|
c->Flags |= flag;
|
|
if (cf < 6 * sf)
|
|
{
|
|
cf = (unsigned)1 + (cf > sf) + (cf >= 4 * sf);
|
|
sum += 4;
|
|
/* It can add (1, 2, 3) to Escape_Freq */
|
|
}
|
|
else
|
|
{
|
|
cf = (unsigned)4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);
|
|
sum += cf;
|
|
}
|
|
|
|
c->Union2.SummFreq = (UInt16)sum;
|
|
s->Freq = (Byte)cf;
|
|
}
|
|
|
|
}
|
|
p->MaxContext = p->MinContext = CTX(minSuccessor);
|
|
}
|
|
|
|
|
|
|
|
Z7_NO_INLINE
|
|
static void Ppmd8_Rescale(CPpmd8 *p)
|
|
{
|
|
unsigned i, adder, sumFreq, escFreq;
|
|
CPpmd_State *stats = STATS(p->MinContext);
|
|
CPpmd_State *s = p->FoundState;
|
|
|
|
/* Sort the list by Freq */
|
|
if (s != stats)
|
|
{
|
|
CPpmd_State tmp = *s;
|
|
do
|
|
s[0] = s[-1];
|
|
while (--s != stats);
|
|
*s = tmp;
|
|
}
|
|
|
|
sumFreq = s->Freq;
|
|
escFreq = p->MinContext->Union2.SummFreq - sumFreq;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
adder = (p->OrderFall != 0);
|
|
|
|
#ifdef PPMD8_FREEZE_SUPPORT
|
|
adder |= (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE);
|
|
#endif
|
|
|
|
sumFreq = (sumFreq + 4 + adder) >> 1;
|
|
i = p->MinContext->NumStats;
|
|
s->Freq = (Byte)sumFreq;
|
|
|
|
do
|
|
{
|
|
unsigned freq = (++s)->Freq;
|
|
escFreq -= freq;
|
|
freq = (freq + adder) >> 1;
|
|
sumFreq += freq;
|
|
s->Freq = (Byte)freq;
|
|
if (freq > s[-1].Freq)
|
|
{
|
|
CPpmd_State tmp = *s;
|
|
CPpmd_State *s1 = s;
|
|
do
|
|
{
|
|
s1[0] = s1[-1];
|
|
}
|
|
while (--s1 != stats && freq > s1[-1].Freq);
|
|
*s1 = tmp;
|
|
}
|
|
}
|
|
while (--i);
|
|
|
|
if (s->Freq == 0)
|
|
{
|
|
/* Remove all items with Freq == 0 */
|
|
CPpmd8_Context *mc;
|
|
unsigned numStats, numStatsNew, n0, n1;
|
|
|
|
i = 0; do { i++; } while ((--s)->Freq == 0);
|
|
|
|
|
|
|
|
|
|
escFreq += i;
|
|
mc = p->MinContext;
|
|
numStats = mc->NumStats;
|
|
numStatsNew = numStats - i;
|
|
mc->NumStats = (Byte)(numStatsNew);
|
|
n0 = (numStats + 2) >> 1;
|
|
|
|
if (numStatsNew == 0)
|
|
{
|
|
|
|
unsigned freq = (2 * (unsigned)stats->Freq + escFreq - 1) / escFreq;
|
|
if (freq > MAX_FREQ / 3)
|
|
freq = MAX_FREQ / 3;
|
|
mc->Flags = (Byte)((mc->Flags & FLAG_PREV_HIGH) + PPMD8_HiBitsFlag_3(stats->Symbol));
|
|
|
|
|
|
|
|
|
|
|
|
s = ONE_STATE(mc);
|
|
*s = *stats;
|
|
s->Freq = (Byte)freq;
|
|
p->FoundState = s;
|
|
Ppmd8_InsertNode(p, stats, U2I(n0));
|
|
return;
|
|
}
|
|
|
|
n1 = (numStatsNew + 2) >> 1;
|
|
if (n0 != n1)
|
|
mc->Union4.Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
|
|
{
|
|
// here we are for max order only. So Ppmd8_MakeEscFreq() doesn't use mc->Flags
|
|
// but we still need current (Flags & FLAG_PREV_HIGH), if we will convert context to 1-symbol context later.
|
|
/*
|
|
unsigned flags = HiBits_Prepare((s = STATS(mc))->Symbol);
|
|
i = mc->NumStats;
|
|
do { flags |= HiBits_Prepare((++s)->Symbol); } while (--i);
|
|
mc->Flags = (Byte)((mc->Flags & ~FLAG_SYM_HIGH) + HiBits_Convert_3(flags));
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
CPpmd8_Context *mc = p->MinContext;
|
|
mc->Union2.SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
|
|
mc->Flags |= FLAG_RESCALED;
|
|
p->FoundState = STATS(mc);
|
|
}
|
|
}
|
|
|
|
|
|
CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
|
|
{
|
|
CPpmd_See *see;
|
|
const CPpmd8_Context *mc = p->MinContext;
|
|
unsigned numStats = mc->NumStats;
|
|
if (numStats != 0xFF)
|
|
{
|
|
// (3 <= numStats + 2 <= 256) (3 <= NS2Indx[3] and NS2Indx[256] === 26)
|
|
see = p->See[(size_t)(unsigned)p->NS2Indx[(size_t)numStats + 2] - 3]
|
|
+ (mc->Union2.SummFreq > 11 * (numStats + 1))
|
|
+ 2 * (unsigned)(2 * numStats < ((unsigned)SUFFIX(mc)->NumStats + numMasked1))
|
|
+ mc->Flags;
|
|
|
|
{
|
|
// if (see->Summ) field is larger than 16-bit, we need only low 16 bits of Summ
|
|
unsigned summ = (UInt16)see->Summ; // & 0xFFFF
|
|
unsigned r = (summ >> see->Shift);
|
|
see->Summ = (UInt16)(summ - r);
|
|
*escFreq = r + (r == 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
see = &p->DummySee;
|
|
*escFreq = 1;
|
|
}
|
|
return see;
|
|
}
|
|
|
|
|
|
static void Ppmd8_NextContext(CPpmd8 *p)
|
|
{
|
|
PPMD8_CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
|
|
if (p->OrderFall == 0 && (const Byte *)c >= p->UnitsStart)
|
|
p->MaxContext = p->MinContext = c;
|
|
else
|
|
Ppmd8_UpdateModel(p);
|
|
}
|
|
|
|
|
|
void Ppmd8_Update1(CPpmd8 *p)
|
|
{
|
|
CPpmd_State *s = p->FoundState;
|
|
unsigned freq = s->Freq;
|
|
freq += 4;
|
|
p->MinContext->Union2.SummFreq = (UInt16)(p->MinContext->Union2.SummFreq + 4);
|
|
s->Freq = (Byte)freq;
|
|
if (freq > s[-1].Freq)
|
|
{
|
|
SWAP_STATES(s, &s[-1]);
|
|
p->FoundState = --s;
|
|
if (freq > MAX_FREQ)
|
|
Ppmd8_Rescale(p);
|
|
}
|
|
Ppmd8_NextContext(p);
|
|
}
|
|
|
|
|
|
void Ppmd8_Update1_0(CPpmd8 *p)
|
|
{
|
|
CPpmd_State *s = p->FoundState;
|
|
CPpmd8_Context *mc = p->MinContext;
|
|
unsigned freq = s->Freq;
|
|
unsigned summFreq = mc->Union2.SummFreq;
|
|
p->PrevSuccess = (2 * freq >= summFreq); // Ppmd8 (>=)
|
|
p->RunLength += (int)p->PrevSuccess;
|
|
mc->Union2.SummFreq = (UInt16)(summFreq + 4);
|
|
freq += 4;
|
|
s->Freq = (Byte)freq;
|
|
if (freq > MAX_FREQ)
|
|
Ppmd8_Rescale(p);
|
|
Ppmd8_NextContext(p);
|
|
}
|
|
|
|
|
|
/*
|
|
void Ppmd8_UpdateBin(CPpmd8 *p)
|
|
{
|
|
unsigned freq = p->FoundState->Freq;
|
|
p->FoundState->Freq = (Byte)(freq + (freq < 196)); // Ppmd8 (196)
|
|
p->PrevSuccess = 1;
|
|
p->RunLength++;
|
|
Ppmd8_NextContext(p);
|
|
}
|
|
*/
|
|
|
|
void Ppmd8_Update2(CPpmd8 *p)
|
|
{
|
|
CPpmd_State *s = p->FoundState;
|
|
unsigned freq = s->Freq;
|
|
freq += 4;
|
|
p->RunLength = p->InitRL;
|
|
p->MinContext->Union2.SummFreq = (UInt16)(p->MinContext->Union2.SummFreq + 4);
|
|
s->Freq = (Byte)freq;
|
|
if (freq > MAX_FREQ)
|
|
Ppmd8_Rescale(p);
|
|
Ppmd8_UpdateModel(p);
|
|
}
|
|
|
|
/* H->I changes:
|
|
NS2Indx
|
|
GlueCount, and Glue method
|
|
BinSum
|
|
See / EscFreq
|
|
Ppmd8_CreateSuccessors updates more suffix contexts
|
|
Ppmd8_UpdateModel consts.
|
|
PrevSuccess Update
|
|
|
|
Flags:
|
|
(1 << 2) - the Context was Rescaled
|
|
(1 << 3) - there is symbol in Stats with (sym >= 0x40) in
|
|
(1 << 4) - main symbol of context is (sym >= 0x40)
|
|
*/
|
|
|
|
#undef RESET_TEXT
|
|
#undef FLAG_RESCALED
|
|
#undef FLAG_PREV_HIGH
|
|
#undef HiBits_Prepare
|
|
#undef HiBits_Convert_3
|
|
#undef HiBits_Convert_4
|
|
#undef PPMD8_HiBitsFlag_3
|
|
#undef PPMD8_HiBitsFlag_4
|
|
#undef RESTORE_MODEL
|
|
|
|
#undef MAX_FREQ
|
|
#undef UNIT_SIZE
|
|
#undef U2B
|
|
#undef U2I
|
|
#undef I2U
|
|
|
|
#undef REF
|
|
#undef STATS_REF
|
|
#undef CTX
|
|
#undef STATS
|
|
#undef ONE_STATE
|
|
#undef SUFFIX
|
|
#undef NODE
|
|
#undef EMPTY_NODE
|
|
#undef MEM_12_CPY
|
|
#undef SUCCESSOR
|
|
#undef SWAP_STATES
|