Imported Upstream version 9.20
[platform/upstream/7zip.git] / C / Ppmd8.c
1 /* Ppmd8.c -- PPMdI codec\r
2 2010-03-24 : Igor Pavlov : Public domain\r
3 This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */\r
4 \r
5 #include <memory.h>\r
6 \r
7 #include "Ppmd8.h"\r
8 \r
9 const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };\r
10 static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};\r
11 \r
12 #define MAX_FREQ 124\r
13 #define UNIT_SIZE 12\r
14 \r
15 #define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)\r
16 #define U2I(nu) (p->Units2Indx[(nu) - 1])\r
17 #define I2U(indx) (p->Indx2Units[indx])\r
18 \r
19 #ifdef PPMD_32BIT\r
20   #define REF(ptr) (ptr)\r
21 #else\r
22   #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))\r
23 #endif\r
24 \r
25 #define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))\r
26 \r
27 #define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))\r
28 #define STATS(ctx) Ppmd8_GetStats(p, ctx)\r
29 #define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)\r
30 #define SUFFIX(ctx) CTX((ctx)->Suffix)\r
31 \r
32 typedef CPpmd8_Context * CTX_PTR;\r
33 \r
34 struct CPpmd8_Node_;\r
35 \r
36 typedef\r
37   #ifdef PPMD_32BIT\r
38     struct CPpmd8_Node_ *\r
39   #else\r
40     UInt32\r
41   #endif\r
42   CPpmd8_Node_Ref;\r
43 \r
44 typedef struct CPpmd8_Node_\r
45 {\r
46   UInt32 Stamp;\r
47   CPpmd8_Node_Ref Next;\r
48   UInt32 NU;\r
49 } CPpmd8_Node;\r
50 \r
51 #ifdef PPMD_32BIT\r
52   #define NODE(ptr) (ptr)\r
53 #else\r
54   #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs)))\r
55 #endif\r
56 \r
57 #define EMPTY_NODE 0xFFFFFFFF\r
58 \r
59 void Ppmd8_Construct(CPpmd8 *p)\r
60 {\r
61   unsigned i, k, m;\r
62 \r
63   p->Base = 0;\r
64 \r
65   for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)\r
66   {\r
67     unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);\r
68     do { p->Units2Indx[k++] = (Byte)i; } while(--step);\r
69     p->Indx2Units[i] = (Byte)k;\r
70   }\r
71 \r
72   p->NS2BSIndx[0] = (0 << 1);\r
73   p->NS2BSIndx[1] = (1 << 1);\r
74   memset(p->NS2BSIndx + 2, (2 << 1), 9);\r
75   memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);\r
76 \r
77   for (i = 0; i < 5; i++)\r
78     p->NS2Indx[i] = (Byte)i;\r
79   for (m = i, k = 1; i < 260; i++)\r
80   {\r
81     p->NS2Indx[i] = (Byte)m;\r
82     if (--k == 0)\r
83       k = (++m) - 4;\r
84   }\r
85 }\r
86 \r
87 void Ppmd8_Free(CPpmd8 *p, ISzAlloc *alloc)\r
88 {\r
89   alloc->Free(alloc, p->Base);\r
90   p->Size = 0;\r
91   p->Base = 0;\r
92 }\r
93 \r
94 Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size, ISzAlloc *alloc)\r
95 {\r
96   if (p->Base == 0 || p->Size != size)\r
97   {\r
98     Ppmd8_Free(p, alloc);\r
99     p->AlignOffset =\r
100       #ifdef PPMD_32BIT\r
101         (4 - size) & 3;\r
102       #else\r
103         4 - (size & 3);\r
104       #endif\r
105     if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size)) == 0)\r
106       return False;\r
107     p->Size = size;\r
108   }\r
109   return True;\r
110 }\r
111 \r
112 static void InsertNode(CPpmd8 *p, void *node, unsigned indx)\r
113 {\r
114   ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;\r
115   ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];\r
116   ((CPpmd8_Node *)node)->NU = I2U(indx);\r
117   p->FreeList[indx] = REF(node);\r
118   p->Stamps[indx]++;\r
119 }\r
120 \r
121 static void *RemoveNode(CPpmd8 *p, unsigned indx)\r
122 {\r
123   CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);\r
124   p->FreeList[indx] = node->Next;\r
125   p->Stamps[indx]--;\r
126   return node;\r
127 }\r
128 \r
129 static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)\r
130 {\r
131   unsigned i, nu = I2U(oldIndx) - I2U(newIndx);\r
132   ptr = (Byte *)ptr + U2B(I2U(newIndx));\r
133   if (I2U(i = U2I(nu)) != nu)\r
134   {\r
135     unsigned k = I2U(--i);\r
136     InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);\r
137   }\r
138   InsertNode(p, ptr, i);\r
139 }\r
140 \r
141 static void GlueFreeBlocks(CPpmd8 *p)\r
142 {\r
143   CPpmd8_Node_Ref head = 0;\r
144   CPpmd8_Node_Ref *prev = &head;\r
145   unsigned i;\r
146 \r
147   p->GlueCount = 1 << 13;\r
148   memset(p->Stamps, 0, sizeof(p->Stamps));\r
149   \r
150   /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end.\r
151      All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */\r
152   if (p->LoUnit != p->HiUnit)\r
153     ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;\r
154 \r
155   /* Glue free blocks */\r
156   for (i = 0; i < PPMD_NUM_INDEXES; i++)\r
157   {\r
158     CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];\r
159     p->FreeList[i] = 0;\r
160     while (next != 0)\r
161     {\r
162       CPpmd8_Node *node = NODE(next);\r
163       if (node->NU != 0)\r
164       {\r
165         CPpmd8_Node *node2;\r
166         *prev = next;\r
167         prev = &(node->Next);\r
168         while ((node2 = node + node->NU)->Stamp == EMPTY_NODE)\r
169         {\r
170           node->NU += node2->NU;\r
171           node2->NU = 0;\r
172         }\r
173       }\r
174       next = node->Next;\r
175     }\r
176   }\r
177   *prev = 0;\r
178   \r
179   /* Fill lists of free blocks */\r
180   while (head != 0)\r
181   {\r
182     CPpmd8_Node *node = NODE(head);\r
183     unsigned nu;\r
184     head = node->Next;\r
185     nu = node->NU;\r
186     if (nu == 0)\r
187       continue;\r
188     for (; nu > 128; nu -= 128, node += 128)\r
189       InsertNode(p, node, PPMD_NUM_INDEXES - 1);\r
190     if (I2U(i = U2I(nu)) != nu)\r
191     {\r
192       unsigned k = I2U(--i);\r
193       InsertNode(p, node + k, nu - k - 1);\r
194     }\r
195     InsertNode(p, node, i);\r
196   }\r
197 }\r
198 \r
199 static void *AllocUnitsRare(CPpmd8 *p, unsigned indx)\r
200 {\r
201   unsigned i;\r
202   void *retVal;\r
203   if (p->GlueCount == 0)\r
204   {\r
205     GlueFreeBlocks(p);\r
206     if (p->FreeList[indx] != 0)\r
207       return RemoveNode(p, indx);\r
208   }\r
209   i = indx;\r
210   do\r
211   {\r
212     if (++i == PPMD_NUM_INDEXES)\r
213     {\r
214       UInt32 numBytes = U2B(I2U(indx));\r
215       p->GlueCount--;\r
216       return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);\r
217     }\r
218   }\r
219   while (p->FreeList[i] == 0);\r
220   retVal = RemoveNode(p, i);\r
221   SplitBlock(p, retVal, i, indx);\r
222   return retVal;\r
223 }\r
224 \r
225 static void *AllocUnits(CPpmd8 *p, unsigned indx)\r
226 {\r
227   UInt32 numBytes;\r
228   if (p->FreeList[indx] != 0)\r
229     return RemoveNode(p, indx);\r
230   numBytes = U2B(I2U(indx));\r
231   if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))\r
232   {\r
233     void *retVal = p->LoUnit;\r
234     p->LoUnit += numBytes;\r
235     return retVal;\r
236   }\r
237   return AllocUnitsRare(p, indx);\r
238 }\r
239 \r
240 #define MyMem12Cpy(dest, src, num) \\r
241   { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \\r
242     do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }\r
243 \r
244 static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)\r
245 {\r
246   unsigned i0 = U2I(oldNU);\r
247   unsigned i1 = U2I(newNU);\r
248   if (i0 == i1)\r
249     return oldPtr;\r
250   if (p->FreeList[i1] != 0)\r
251   {\r
252     void *ptr = RemoveNode(p, i1);\r
253     MyMem12Cpy(ptr, oldPtr, newNU);\r
254     InsertNode(p, oldPtr, i0);\r
255     return ptr;\r
256   }\r
257   SplitBlock(p, oldPtr, i0, i1);\r
258   return oldPtr;\r
259 }\r
260 \r
261 static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)\r
262 {\r
263   InsertNode(p, ptr, U2I(nu));\r
264 }\r
265 \r
266 static void SpecialFreeUnit(CPpmd8 *p, void *ptr)\r
267 {\r
268   if ((Byte *)ptr != p->UnitsStart)\r
269     InsertNode(p, ptr, 0);\r
270   else\r
271   {\r
272     #ifdef PPMD8_FREEZE_SUPPORT\r
273     *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */\r
274     #endif\r
275     p->UnitsStart += UNIT_SIZE;\r
276   }\r
277 }\r
278 \r
279 static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)\r
280 {\r
281   unsigned indx = U2I(nu);\r
282   void *ptr;\r
283   if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx])\r
284     return oldPtr;\r
285   ptr = RemoveNode(p, indx);\r
286   MyMem12Cpy(ptr, oldPtr, nu);\r
287   if ((Byte*)oldPtr != p->UnitsStart)\r
288     InsertNode(p, oldPtr, indx);\r
289   else\r
290     p->UnitsStart += U2B(I2U(indx));\r
291   return ptr;\r
292 }\r
293 \r
294 static void ExpandTextArea(CPpmd8 *p)\r
295 {\r
296   UInt32 count[PPMD_NUM_INDEXES];\r
297   unsigned i;\r
298   memset(count, 0, sizeof(count));\r
299   if (p->LoUnit != p->HiUnit)\r
300     ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;\r
301   \r
302   {\r
303     CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart;\r
304     for (; node->Stamp == EMPTY_NODE; node += node->NU)\r
305     {\r
306       node->Stamp = 0;\r
307       count[U2I(node->NU)]++;\r
308     }\r
309     p->UnitsStart = (Byte *)node;\r
310   }\r
311   \r
312   for (i = 0; i < PPMD_NUM_INDEXES; i++)\r
313   {\r
314     CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i];\r
315     while (count[i] != 0)\r
316     {\r
317       CPpmd8_Node *node = NODE(*next);\r
318       while (node->Stamp == 0)\r
319       {\r
320         *next = node->Next;\r
321         node = NODE(*next);\r
322         p->Stamps[i]--;\r
323         if (--count[i] == 0)\r
324           break;\r
325       }\r
326       next = &node->Next;\r
327     }\r
328   }\r
329 }\r
330 \r
331 #define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))\r
332 \r
333 static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)\r
334 {\r
335   (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);\r
336   (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);\r
337 }\r
338 \r
339 #define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }\r
340 \r
341 static void RestartModel(CPpmd8 *p)\r
342 {\r
343   unsigned i, k, m, r;\r
344 \r
345   memset(p->FreeList, 0, sizeof(p->FreeList));\r
346   memset(p->Stamps, 0, sizeof(p->Stamps));\r
347   RESET_TEXT(0);\r
348   p->HiUnit = p->Text + p->Size;\r
349   p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;\r
350   p->GlueCount = 0;\r
351 \r
352   p->OrderFall = p->MaxOrder;\r
353   p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;\r
354   p->PrevSuccess = 0;\r
355 \r
356   p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */\r
357   p->MinContext->Suffix = 0;\r
358   p->MinContext->NumStats = 255;\r
359   p->MinContext->Flags = 0;\r
360   p->MinContext->SummFreq = 256 + 1;\r
361   p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */\r
362   p->LoUnit += U2B(256 / 2);\r
363   p->MinContext->Stats = REF(p->FoundState);\r
364   for (i = 0; i < 256; i++)\r
365   {\r
366     CPpmd_State *s = &p->FoundState[i];\r
367     s->Symbol = (Byte)i;\r
368     s->Freq = 1;\r
369     SetSuccessor(s, 0);\r
370   }\r
371 \r
372   for (i = m = 0; m < 25; m++)\r
373   {\r
374     while (p->NS2Indx[i] == m)\r
375       i++;\r
376     for (k = 0; k < 8; k++)\r
377     {\r
378       UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1));\r
379       UInt16 *dest = p->BinSumm[m] + k;\r
380       for (r = 0; r < 64; r += 8)\r
381         dest[r] = val;\r
382     }\r
383   }\r
384 \r
385   for (i = m = 0; m < 24; m++)\r
386   {\r
387     while (p->NS2Indx[i + 3] == m + 3)\r
388       i++;\r
389     for (k = 0; k < 32; k++)\r
390     {\r
391       CPpmd_See *s = &p->See[m][k];\r
392       s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4));\r
393       s->Count = 7;\r
394     }\r
395   }\r
396 }\r
397 \r
398 void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)\r
399 {\r
400   p->MaxOrder = maxOrder;\r
401   p->RestoreMethod = restoreMethod;\r
402   RestartModel(p);\r
403   p->DummySee.Shift = PPMD_PERIOD_BITS;\r
404   p->DummySee.Summ = 0; /* unused */\r
405   p->DummySee.Count = 64; /* unused */\r
406 }\r
407 \r
408 static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale)\r
409 {\r
410   unsigned i = ctx->NumStats, escFreq, sumFreq, flags;\r
411   CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);\r
412   ctx->Stats = REF(s);\r
413   #ifdef PPMD8_FREEZE_SUPPORT\r
414   /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */\r
415   scale |= (ctx->SummFreq >= ((UInt32)1 << 15));\r
416   #endif\r
417   flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40);\r
418   escFreq = ctx->SummFreq - s->Freq;\r
419   sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale));\r
420   do\r
421   {\r
422     escFreq -= (++s)->Freq;\r
423     sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale));\r
424     flags |= 0x08 * (s->Symbol >= 0x40);\r
425   }\r
426   while (--i);\r
427   ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));\r
428   ctx->Flags = (Byte)flags;\r
429 }\r
430 \r
431 static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)\r
432 {\r
433   CPpmd_State tmp = *t1;\r
434   *t1 = *t2;\r
435   *t2 = tmp;\r
436 }\r
437 \r
438 static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order)\r
439 {\r
440   int i;\r
441   unsigned tmp;\r
442   CPpmd_State *s;\r
443   \r
444   if (!ctx->NumStats)\r
445   {\r
446     s = ONE_STATE(ctx);\r
447     if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart)\r
448     {\r
449       if (order < p->MaxOrder)\r
450         SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));\r
451       else\r
452         SetSuccessor(s, 0);\r
453       if (SUCCESSOR(s) || order <= 9) /* O_BOUND */\r
454         return REF(ctx);\r
455     }\r
456     SpecialFreeUnit(p, ctx);\r
457     return 0;\r
458   }\r
459 \r
460   ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1));\r
461 \r
462   for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--)\r
463     if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart)\r
464     {\r
465       CPpmd_State *s2 = STATS(ctx) + (i--);\r
466       SetSuccessor(s, 0);\r
467       SwapStates(s, s2);\r
468     }\r
469     else if (order < p->MaxOrder)\r
470       SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));\r
471     else\r
472       SetSuccessor(s, 0);\r
473     \r
474   if (i != ctx->NumStats && order)\r
475   {\r
476     ctx->NumStats = (Byte)i;\r
477     s = STATS(ctx);\r
478     if (i < 0)\r
479     {\r
480       FreeUnits(p, s, tmp);\r
481       SpecialFreeUnit(p, ctx);\r
482       return 0;\r
483     }\r
484     if (i == 0)\r
485     {\r
486       ctx->Flags = (ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40);\r
487       *ONE_STATE(ctx) = *s;\r
488       FreeUnits(p, s, tmp);\r
489       ONE_STATE(ctx)->Freq = (Byte)((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3;\r
490     }\r
491     else\r
492       Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i);\r
493   }\r
494   return REF(ctx);\r
495 }\r
496 \r
497 #ifdef PPMD8_FREEZE_SUPPORT\r
498 static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order)\r
499 {\r
500   CPpmd_State *s;\r
501   if (!ctx->NumStats)\r
502   {\r
503     s = ONE_STATE(ctx);\r
504     if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)\r
505       SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));\r
506     else\r
507       SetSuccessor(s, 0);\r
508     /* Suffix context can be removed already, since different (high-order)\r
509        Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */\r
510     if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))\r
511     {\r
512       FreeUnits(p, ctx, 1);\r
513       return 0;\r
514     }\r
515     else\r
516       return REF(ctx);\r
517   }\r
518 \r
519   for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--)\r
520     if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)\r
521       SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));\r
522     else\r
523       SetSuccessor(s, 0);\r
524   \r
525   return REF(ctx);\r
526 }\r
527 #endif\r
528 \r
529 static UInt32 GetUsedMemory(const CPpmd8 *p)\r
530 {\r
531   UInt32 v = 0;\r
532   unsigned i;\r
533   for (i = 0; i < PPMD_NUM_INDEXES; i++)\r
534     v += p->Stamps[i] * I2U(i);\r
535   return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);\r
536 }\r
537 \r
538 #ifdef PPMD8_FREEZE_SUPPORT\r
539   #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)\r
540 #else\r
541   #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)\r
542 #endif\r
543 \r
544 static void RestoreModel(CPpmd8 *p, CTX_PTR c1\r
545     #ifdef PPMD8_FREEZE_SUPPORT\r
546     , CTX_PTR fSuccessor\r
547     #endif\r
548     )\r
549 {\r
550   CTX_PTR c;\r
551   CPpmd_State *s;\r
552   RESET_TEXT(0);\r
553   for (c = p->MaxContext; c != c1; c = SUFFIX(c))\r
554     if (--(c->NumStats) == 0)\r
555     {\r
556       s = STATS(c);\r
557       c->Flags = (c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40);\r
558       *ONE_STATE(c) = *s;\r
559       SpecialFreeUnit(p, s);\r
560       ONE_STATE(c)->Freq = (ONE_STATE(c)->Freq + 11) >> 3;\r
561     }\r
562     else\r
563       Refresh(p, c, (c->NumStats+3) >> 1, 0);\r
564  \r
565   for (; c != p->MinContext; c = SUFFIX(c))\r
566     if (!c->NumStats)\r
567       ONE_STATE(c)->Freq -= ONE_STATE(c)->Freq >> 1;\r
568     else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats)\r
569       Refresh(p, c, (c->NumStats + 2) >> 1, 1);\r
570 \r
571   #ifdef PPMD8_FREEZE_SUPPORT\r
572   if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)\r
573   {\r
574     p->MaxContext = fSuccessor;\r
575     p->GlueCount += !(p->Stamps[1] & 1);\r
576   }\r
577   else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)\r
578   {\r
579     while (p->MaxContext->Suffix)\r
580       p->MaxContext = SUFFIX(p->MaxContext);\r
581     RemoveBinContexts(p, p->MaxContext, 0);\r
582     p->RestoreMethod++;\r
583     p->GlueCount = 0;\r
584     p->OrderFall = p->MaxOrder;\r
585   }\r
586   else\r
587   #endif\r
588   if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))\r
589     RestartModel(p);\r
590   else\r
591   {\r
592     while (p->MaxContext->Suffix)\r
593       p->MaxContext = SUFFIX(p->MaxContext);\r
594     do\r
595     {\r
596       CutOff(p, p->MaxContext, 0);\r
597       ExpandTextArea(p);\r
598     }\r
599     while (GetUsedMemory(p) > 3 * (p->Size >> 2));\r
600     p->GlueCount = 0;\r
601     p->OrderFall = p->MaxOrder;\r
602   }\r
603 }\r
604 \r
605 static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c)\r
606 {\r
607   CPpmd_State upState;\r
608   Byte flags;\r
609   CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);\r
610   /* fixed over Shkarin's code. Maybe it could work without + 1 too. */\r
611   CPpmd_State *ps[PPMD8_MAX_ORDER + 1];\r
612   unsigned numPs = 0;\r
613   \r
614   if (!skip)\r
615     ps[numPs++] = p->FoundState;\r
616   \r
617   while (c->Suffix)\r
618   {\r
619     CPpmd_Void_Ref successor;\r
620     CPpmd_State *s;\r
621     c = SUFFIX(c);\r
622     if (s1)\r
623     {\r
624       s = s1;\r
625       s1 = NULL;\r
626     }\r
627     else if (c->NumStats != 0)\r
628     {\r
629       for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);\r
630       if (s->Freq < MAX_FREQ - 9)\r
631       {\r
632         s->Freq++;\r
633         c->SummFreq++;\r
634       }\r
635     }\r
636     else\r
637     {\r
638       s = ONE_STATE(c);\r
639       s->Freq += (!SUFFIX(c)->NumStats & (s->Freq < 24));\r
640     }\r
641     successor = SUCCESSOR(s);\r
642     if (successor != upBranch)\r
643     {\r
644       c = CTX(successor);\r
645       if (numPs == 0)\r
646         return c;\r
647       break;\r
648     }\r
649     ps[numPs++] = s;\r
650   }\r
651   \r
652   upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch);\r
653   SetSuccessor(&upState, upBranch + 1);\r
654   flags = 0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40);\r
655 \r
656   if (c->NumStats == 0)\r
657     upState.Freq = ONE_STATE(c)->Freq;\r
658   else\r
659   {\r
660     UInt32 cf, s0;\r
661     CPpmd_State *s;\r
662     for (s = STATS(c); s->Symbol != upState.Symbol; s++);\r
663     cf = s->Freq - 1;\r
664     s0 = c->SummFreq - c->NumStats - cf;\r
665     upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));\r
666   }\r
667 \r
668   do\r
669   {\r
670     /* Create Child */\r
671     CTX_PTR c1; /* = AllocContext(p); */\r
672     if (p->HiUnit != p->LoUnit)\r
673       c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);\r
674     else if (p->FreeList[0] != 0)\r
675       c1 = (CTX_PTR)RemoveNode(p, 0);\r
676     else\r
677     {\r
678       c1 = (CTX_PTR)AllocUnitsRare(p, 0);\r
679       if (!c1)\r
680         return NULL;\r
681     }\r
682     c1->NumStats = 0;\r
683     c1->Flags = flags;\r
684     *ONE_STATE(c1) = upState;\r
685     c1->Suffix = REF(c);\r
686     SetSuccessor(ps[--numPs], REF(c1));\r
687     c = c1;\r
688   }\r
689   while (numPs != 0);\r
690   \r
691   return c;\r
692 }\r
693 \r
694 static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c)\r
695 {\r
696   CPpmd_State *s = NULL;\r
697   CTX_PTR c1 = c;\r
698   CPpmd_Void_Ref upBranch = REF(p->Text);\r
699   \r
700   #ifdef PPMD8_FREEZE_SUPPORT\r
701   /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */\r
702   CPpmd_State *ps[PPMD8_MAX_ORDER + 1];\r
703   unsigned numPs = 0;\r
704   ps[numPs++] = p->FoundState;\r
705   #endif\r
706 \r
707   SetSuccessor(p->FoundState, upBranch);\r
708   p->OrderFall++;\r
709 \r
710   for (;;)\r
711   {\r
712     if (s1)\r
713     {\r
714       c = SUFFIX(c);\r
715       s = s1;\r
716       s1 = NULL;\r
717     }\r
718     else\r
719     {\r
720       if (!c->Suffix)\r
721       {\r
722         #ifdef PPMD8_FREEZE_SUPPORT\r
723         if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)\r
724         {\r
725           do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);\r
726           RESET_TEXT(1);\r
727           p->OrderFall = 1;\r
728         }\r
729         #endif\r
730         return c;\r
731       }\r
732       c = SUFFIX(c);\r
733       if (c->NumStats)\r
734       {\r
735         if ((s = STATS(c))->Symbol != p->FoundState->Symbol)\r
736           do { s++; } while (s->Symbol != p->FoundState->Symbol);\r
737         if (s->Freq < MAX_FREQ - 9)\r
738         {\r
739           s->Freq += 2;\r
740           c->SummFreq += 2;\r
741         }\r
742       }\r
743       else\r
744       {\r
745         s = ONE_STATE(c);\r
746         s->Freq += (s->Freq < 32);\r
747       }\r
748     }\r
749     if (SUCCESSOR(s))\r
750       break;\r
751     #ifdef PPMD8_FREEZE_SUPPORT\r
752     ps[numPs++] = s;\r
753     #endif\r
754     SetSuccessor(s, upBranch);\r
755     p->OrderFall++;\r
756   }\r
757   \r
758   #ifdef PPMD8_FREEZE_SUPPORT\r
759   if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)\r
760   {\r
761     c = CTX(SUCCESSOR(s));\r
762     do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);\r
763     RESET_TEXT(1);\r
764     p->OrderFall = 1;\r
765     return c;\r
766   }\r
767   else\r
768   #endif\r
769   if (SUCCESSOR(s) <= upBranch)\r
770   {\r
771     CTX_PTR successor;\r
772     CPpmd_State *s1 = p->FoundState;\r
773     p->FoundState = s;\r
774 \r
775     successor = CreateSuccessors(p, False, NULL, c);\r
776     if (successor == NULL)\r
777       SetSuccessor(s, 0);\r
778     else\r
779       SetSuccessor(s, REF(successor));\r
780     p->FoundState = s1;\r
781   }\r
782   \r
783   if (p->OrderFall == 1 && c1 == p->MaxContext)\r
784   {\r
785     SetSuccessor(p->FoundState, SUCCESSOR(s));\r
786     p->Text--;\r
787   }\r
788   if (SUCCESSOR(s) == 0)\r
789     return NULL;\r
790   return CTX(SUCCESSOR(s));\r
791 }\r
792 \r
793 static void UpdateModel(CPpmd8 *p)\r
794 {\r
795   CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);\r
796   CTX_PTR c;\r
797   unsigned s0, ns, fFreq = p->FoundState->Freq;\r
798   Byte flag, fSymbol = p->FoundState->Symbol;\r
799   CPpmd_State *s = NULL;\r
800   \r
801   if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)\r
802   {\r
803     c = SUFFIX(p->MinContext);\r
804     \r
805     if (c->NumStats == 0)\r
806     {\r
807       s = ONE_STATE(c);\r
808       if (s->Freq < 32)\r
809         s->Freq++;\r
810     }\r
811     else\r
812     {\r
813       s = STATS(c);\r
814       if (s->Symbol != p->FoundState->Symbol)\r
815       {\r
816         do { s++; } while (s->Symbol != p->FoundState->Symbol);\r
817         if (s[0].Freq >= s[-1].Freq)\r
818         {\r
819           SwapStates(&s[0], &s[-1]);\r
820           s--;\r
821         }\r
822       }\r
823       if (s->Freq < MAX_FREQ - 9)\r
824       {\r
825         s->Freq += 2;\r
826         c->SummFreq += 2;\r
827       }\r
828     }\r
829   }\r
830   \r
831   c = p->MaxContext;\r
832   if (p->OrderFall == 0 && fSuccessor)\r
833   {\r
834     CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext);\r
835     if (cs == 0)\r
836     {\r
837       SetSuccessor(p->FoundState, 0);\r
838       RESTORE_MODEL(c, CTX(fSuccessor));\r
839     }\r
840     else\r
841     {\r
842       SetSuccessor(p->FoundState, REF(cs));\r
843       p->MaxContext = cs;\r
844     }\r
845     return;\r
846   }\r
847   \r
848   *p->Text++ = p->FoundState->Symbol;\r
849   successor = REF(p->Text);\r
850   if (p->Text >= p->UnitsStart)\r
851   {\r
852     RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */\r
853     return;\r
854   }\r
855   \r
856   if (!fSuccessor)\r
857   {\r
858     CTX_PTR cs = ReduceOrder(p, s, p->MinContext);\r
859     if (cs == NULL)\r
860     {\r
861       RESTORE_MODEL(c, 0);\r
862       return;\r
863     }\r
864     fSuccessor = REF(cs);\r
865   }\r
866   else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart)\r
867   {\r
868     CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext);\r
869     if (cs == NULL)\r
870     {\r
871       RESTORE_MODEL(c, 0);\r
872       return;\r
873     }\r
874     fSuccessor = REF(cs);\r
875   }\r
876   \r
877   if (--p->OrderFall == 0)\r
878   {\r
879     successor = fSuccessor;\r
880     p->Text -= (p->MaxContext != p->MinContext);\r
881   }\r
882   #ifdef PPMD8_FREEZE_SUPPORT\r
883   else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)\r
884   {\r
885     successor = fSuccessor;\r
886     RESET_TEXT(0);\r
887     p->OrderFall = 0;\r
888   }\r
889   #endif\r
890   \r
891   s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq;\r
892   flag = 0x08 * (fSymbol >= 0x40);\r
893   \r
894   for (; c != p->MinContext; c = SUFFIX(c))\r
895   {\r
896     unsigned ns1;\r
897     UInt32 cf, sf;\r
898     if ((ns1 = c->NumStats) != 0)\r
899     {\r
900       if ((ns1 & 1) != 0)\r
901       {\r
902         /* Expand for one UNIT */\r
903         unsigned oldNU = (ns1 + 1) >> 1;\r
904         unsigned i = U2I(oldNU);\r
905         if (i != U2I(oldNU + 1))\r
906         {\r
907           void *ptr = AllocUnits(p, i + 1);\r
908           void *oldPtr;\r
909           if (!ptr)\r
910           {\r
911             RESTORE_MODEL(c, CTX(fSuccessor));\r
912             return;\r
913           }\r
914           oldPtr = STATS(c);\r
915           MyMem12Cpy(ptr, oldPtr, oldNU);\r
916           InsertNode(p, oldPtr, i);\r
917           c->Stats = STATS_REF(ptr);\r
918         }\r
919       }\r
920       c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns));\r
921     }\r
922     else\r
923     {\r
924       CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);\r
925       if (!s)\r
926       {\r
927         RESTORE_MODEL(c, CTX(fSuccessor));\r
928         return;\r
929       }\r
930       *s = *ONE_STATE(c);\r
931       c->Stats = REF(s);\r
932       if (s->Freq < MAX_FREQ / 4 - 1)\r
933         s->Freq <<= 1;\r
934       else\r
935         s->Freq = MAX_FREQ - 4;\r
936       c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 2));\r
937     }\r
938     cf = 2 * fFreq * (c->SummFreq + 6);\r
939     sf = (UInt32)s0 + c->SummFreq;\r
940     if (cf < 6 * sf)\r
941     {\r
942       cf = 1 + (cf > sf) + (cf >= 4 * sf);\r
943       c->SummFreq += 4;\r
944     }\r
945     else\r
946     {\r
947       cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);\r
948       c->SummFreq = (UInt16)(c->SummFreq + cf);\r
949     }\r
950     {\r
951       CPpmd_State *s = STATS(c) + ns1 + 1;\r
952       SetSuccessor(s, successor);\r
953       s->Symbol = fSymbol;\r
954       s->Freq = (Byte)cf;\r
955       c->Flags |= flag;\r
956       c->NumStats = (Byte)(ns1 + 1);\r
957     }\r
958   }\r
959   p->MaxContext = p->MinContext = CTX(fSuccessor);\r
960 }\r
961   \r
962 static void Rescale(CPpmd8 *p)\r
963 {\r
964   unsigned i, adder, sumFreq, escFreq;\r
965   CPpmd_State *stats = STATS(p->MinContext);\r
966   CPpmd_State *s = p->FoundState;\r
967   {\r
968     CPpmd_State tmp = *s;\r
969     for (; s != stats; s--)\r
970       s[0] = s[-1];\r
971     *s = tmp;\r
972   }\r
973   escFreq = p->MinContext->SummFreq - s->Freq;\r
974   s->Freq += 4;\r
975   adder = (p->OrderFall != 0\r
976       #ifdef PPMD8_FREEZE_SUPPORT\r
977       || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE\r
978       #endif\r
979       );\r
980   s->Freq = (Byte)((s->Freq + adder) >> 1);\r
981   sumFreq = s->Freq;\r
982   \r
983   i = p->MinContext->NumStats;\r
984   do\r
985   {\r
986     escFreq -= (++s)->Freq;\r
987     s->Freq = (Byte)((s->Freq + adder) >> 1);\r
988     sumFreq += s->Freq;\r
989     if (s[0].Freq > s[-1].Freq)\r
990     {\r
991       CPpmd_State *s1 = s;\r
992       CPpmd_State tmp = *s1;\r
993       do\r
994         s1[0] = s1[-1];\r
995       while (--s1 != stats && tmp.Freq > s1[-1].Freq);\r
996       *s1 = tmp;\r
997     }\r
998   }\r
999   while (--i);\r
1000   \r
1001   if (s->Freq == 0)\r
1002   {\r
1003     unsigned numStats = p->MinContext->NumStats;\r
1004     unsigned n0, n1;\r
1005     do { i++; } while ((--s)->Freq == 0);\r
1006     escFreq += i;\r
1007     p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i);\r
1008     if (p->MinContext->NumStats == 0)\r
1009     {\r
1010       CPpmd_State tmp = *stats;\r
1011       tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq);\r
1012       if (tmp.Freq > MAX_FREQ / 3)\r
1013         tmp.Freq = MAX_FREQ / 3;\r
1014       InsertNode(p, stats, U2I((numStats + 2) >> 1));\r
1015       p->MinContext->Flags = (p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40);\r
1016       *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;\r
1017       return;\r
1018     }\r
1019     n0 = (numStats + 2) >> 1;\r
1020     n1 = (p->MinContext->NumStats + 2) >> 1;\r
1021     if (n0 != n1)\r
1022       p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));\r
1023     p->MinContext->Flags &= ~0x08;\r
1024     p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40);\r
1025     i = p->MinContext->NumStats;\r
1026     do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i);\r
1027   }\r
1028   p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));\r
1029   p->MinContext->Flags |= 0x4;\r
1030   p->FoundState = STATS(p->MinContext);\r
1031 }\r
1032 \r
1033 CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)\r
1034 {\r
1035   CPpmd_See *see;\r
1036   if (p->MinContext->NumStats != 0xFF)\r
1037   {\r
1038     see = p->See[p->NS2Indx[p->MinContext->NumStats + 2] - 3] +\r
1039         (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) +\r
1040         2 * (2 * (unsigned)p->MinContext->NumStats <\r
1041         ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) +\r
1042         p->MinContext->Flags;\r
1043     {\r
1044       unsigned r = (see->Summ >> see->Shift);\r
1045       see->Summ = (UInt16)(see->Summ - r);\r
1046       *escFreq = r + (r == 0);\r
1047     }\r
1048   }\r
1049   else\r
1050   {\r
1051     see = &p->DummySee;\r
1052     *escFreq = 1;\r
1053   }\r
1054   return see;\r
1055 }\r
1056 \r
1057 static void NextContext(CPpmd8 *p)\r
1058 {\r
1059   CTX_PTR c = CTX(SUCCESSOR(p->FoundState));\r
1060   if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart)\r
1061     p->MinContext = p->MaxContext = c;\r
1062   else\r
1063   {\r
1064     UpdateModel(p);\r
1065     p->MinContext = p->MaxContext;\r
1066   }\r
1067 }\r
1068 \r
1069 void Ppmd8_Update1(CPpmd8 *p)\r
1070 {\r
1071   CPpmd_State *s = p->FoundState;\r
1072   s->Freq += 4;\r
1073   p->MinContext->SummFreq += 4;\r
1074   if (s[0].Freq > s[-1].Freq)\r
1075   {\r
1076     SwapStates(&s[0], &s[-1]);\r
1077     p->FoundState = --s;\r
1078     if (s->Freq > MAX_FREQ)\r
1079       Rescale(p);\r
1080   }\r
1081   NextContext(p);\r
1082 }\r
1083 \r
1084 void Ppmd8_Update1_0(CPpmd8 *p)\r
1085 {\r
1086   p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);\r
1087   p->RunLength += p->PrevSuccess;\r
1088   p->MinContext->SummFreq += 4;\r
1089   if ((p->FoundState->Freq += 4) > MAX_FREQ)\r
1090     Rescale(p);\r
1091   NextContext(p);\r
1092 }\r
1093 \r
1094 void Ppmd8_UpdateBin(CPpmd8 *p)\r
1095 {\r
1096   p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));\r
1097   p->PrevSuccess = 1;\r
1098   p->RunLength++;\r
1099   NextContext(p);\r
1100 }\r
1101 \r
1102 void Ppmd8_Update2(CPpmd8 *p)\r
1103 {\r
1104   p->MinContext->SummFreq += 4;\r
1105   if ((p->FoundState->Freq += 4) > MAX_FREQ)\r
1106     Rescale(p);\r
1107   p->RunLength = p->InitRL;\r
1108   UpdateModel(p);\r
1109   p->MinContext = p->MaxContext;\r
1110 }\r
1111 \r
1112 /* H->I changes:\r
1113   NS2Indx\r
1114   GlewCount, and Glue method\r
1115   BinSum\r
1116   See / EscFreq\r
1117   CreateSuccessors updates more suffix contexts\r
1118   UpdateModel consts.\r
1119   PrevSuccess Update\r
1120 */\r