1 /* Ppmd7.c -- PPMdH codec
2 2021-04-13 : Igor Pavlov : Public domain
3 This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
4
5 #include "Precomp.h"
6
7 #include <string.h>
8
9 #include "Ppmd7.h"
10
11 /* define PPMD7_ORDER_0_SUPPPORT to suport order-0 mode, unsupported by orignal PPMd var.H. code */
12 // #define PPMD7_ORDER_0_SUPPPORT
13
14 MY_ALIGN(16)
15 static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
16 MY_ALIGN(16)
17 static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
18
19 #define MAX_FREQ 124
20 #define UNIT_SIZE 12
21
22 #define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
23 #define U2I(nu) (p->Units2Indx[(size_t)(nu) - 1])
24 #define I2U(indx) ((unsigned)p->Indx2Units[indx])
25 #define I2U_UInt16(indx) ((UInt16)p->Indx2Units[indx])
26
27 #define REF(ptr) Ppmd_GetRef(p, ptr)
28
29 #define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
30
31 #define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
32 #define STATS(ctx) Ppmd7_GetStats(p, ctx)
33 #define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
34 #define SUFFIX(ctx) CTX((ctx)->Suffix)
35
36 typedef CPpmd7_Context * CTX_PTR;
37
38 struct CPpmd7_Node_;
39
40 typedef Ppmd_Ref_Type(struct CPpmd7_Node_) CPpmd7_Node_Ref;
41
42 typedef struct CPpmd7_Node_
43 {
44 UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
45 UInt16 NU;
46 CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
47 CPpmd7_Node_Ref Prev;
48 } CPpmd7_Node;
49
50 #define NODE(r) Ppmd_GetPtr_Type(p, r, CPpmd7_Node)
51
Ppmd7_Construct(CPpmd7 * p)52 void Ppmd7_Construct(CPpmd7 *p)
53 {
54 unsigned i, k, m;
55
56 p->Base = NULL;
57
58 for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
59 {
60 unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
61 do { p->Units2Indx[k++] = (Byte)i; } while (--step);
62 p->Indx2Units[i] = (Byte)k;
63 }
64
65 p->NS2BSIndx[0] = (0 << 1);
66 p->NS2BSIndx[1] = (1 << 1);
67 memset(p->NS2BSIndx + 2, (2 << 1), 9);
68 memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
69
70 for (i = 0; i < 3; i++)
71 p->NS2Indx[i] = (Byte)i;
72
73 for (m = i, k = 1; i < 256; i++)
74 {
75 p->NS2Indx[i] = (Byte)m;
76 if (--k == 0)
77 k = (++m) - 2;
78 }
79
80 memcpy(p->ExpEscape, PPMD7_kExpEscape, 16);
81 }
82
83
Ppmd7_Free(CPpmd7 * p,ISzAllocPtr alloc)84 void Ppmd7_Free(CPpmd7 *p, ISzAllocPtr alloc)
85 {
86 ISzAlloc_Free(alloc, p->Base);
87 p->Size = 0;
88 p->Base = NULL;
89 }
90
91
Ppmd7_Alloc(CPpmd7 * p,UInt32 size,ISzAllocPtr alloc)92 BoolInt Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAllocPtr alloc)
93 {
94 if (!p->Base || p->Size != size)
95 {
96 Ppmd7_Free(p, alloc);
97 p->AlignOffset = (4 - size) & 3;
98 if ((p->Base = (Byte *)ISzAlloc_Alloc(alloc, p->AlignOffset + size)) == NULL)
99 return False;
100 p->Size = size;
101 }
102 return True;
103 }
104
105
106
107 // ---------- Internal Memory Allocator ----------
108
109 /* We can use CPpmd7_Node in list of free units (as in Ppmd8)
110 But we still need one additional list walk pass in GlueFreeBlocks().
111 So we use simple CPpmd_Void_Ref instead of CPpmd7_Node in InsertNode() / RemoveNode()
112 */
113
114 #define EMPTY_NODE 0
115
116
InsertNode(CPpmd7 * p,void * node,unsigned indx)117 static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
118 {
119 *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
120 // ((CPpmd7_Node *)node)->Next = (CPpmd7_Node_Ref)p->FreeList[indx];
121
122 p->FreeList[indx] = REF(node);
123
124 }
125
126
RemoveNode(CPpmd7 * p,unsigned indx)127 static void *RemoveNode(CPpmd7 *p, unsigned indx)
128 {
129 CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
130 p->FreeList[indx] = *node;
131 // CPpmd7_Node *node = NODE((CPpmd7_Node_Ref)p->FreeList[indx]);
132 // p->FreeList[indx] = node->Next;
133 return node;
134 }
135
136
SplitBlock(CPpmd7 * p,void * ptr,unsigned oldIndx,unsigned newIndx)137 static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
138 {
139 unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
140 ptr = (Byte *)ptr + U2B(I2U(newIndx));
141 if (I2U(i = U2I(nu)) != nu)
142 {
143 unsigned k = I2U(--i);
144 InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
145 }
146 InsertNode(p, ptr, i);
147 }
148
149
150 /* we use CPpmd7_Node_Union union to solve XLC -O2 strict pointer aliasing problem */
151
152 typedef union _CPpmd7_Node_Union
153 {
154 CPpmd7_Node Node;
155 CPpmd7_Node_Ref NextRef;
156 } CPpmd7_Node_Union;
157
158 /* Original PPmdH (Ppmd7) code uses doubly linked list in GlueFreeBlocks()
159 we use single linked list similar to Ppmd8 code */
160
161
GlueFreeBlocks(CPpmd7 * p)162 static void GlueFreeBlocks(CPpmd7 *p)
163 {
164 /*
165 we use first UInt16 field of 12-bytes UNITs as record type stamp
166 CPpmd_State { Byte Symbol; Byte Freq; : Freq != 0
167 CPpmd7_Context { UInt16 NumStats; : NumStats != 0
168 CPpmd7_Node { UInt16 Stamp : Stamp == 0 for free record
169 : Stamp == 1 for head record and guard
170 Last 12-bytes UNIT in array is always contains 12-bytes order-0 CPpmd7_Context record.
171 */
172 CPpmd7_Node_Ref head, n = 0;
173
174 p->GlueCount = 255;
175
176
177 /* we set guard NODE at LoUnit */
178 if (p->LoUnit != p->HiUnit)
179 ((CPpmd7_Node *)(void *)p->LoUnit)->Stamp = 1;
180
181 {
182 /* Create list of free blocks.
183 We still need one additional list walk pass before Glue. */
184 unsigned i;
185 for (i = 0; i < PPMD_NUM_INDEXES; i++)
186 {
187 const UInt16 nu = I2U_UInt16(i);
188 CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
189 p->FreeList[i] = 0;
190 while (next != 0)
191 {
192 /* Don't change the order of the following commands: */
193 CPpmd7_Node_Union *un = (CPpmd7_Node_Union *)NODE(next);
194 const CPpmd7_Node_Ref tmp = next;
195 next = un->NextRef;
196 un->Node.Stamp = EMPTY_NODE;
197 un->Node.NU = nu;
198 un->Node.Next = n;
199 n = tmp;
200 }
201 }
202 }
203
204 head = n;
205 /* Glue and Fill must walk the list in same direction */
206 {
207 /* Glue free blocks */
208 CPpmd7_Node_Ref *prev = &head;
209 while (n)
210 {
211 CPpmd7_Node *node = NODE(n);
212 UInt32 nu = node->NU;
213 n = node->Next;
214 if (nu == 0)
215 {
216 *prev = n;
217 continue;
218 }
219 prev = &node->Next;
220 for (;;)
221 {
222 CPpmd7_Node *node2 = node + nu;
223 nu += node2->NU;
224 if (node2->Stamp != EMPTY_NODE || nu >= 0x10000)
225 break;
226 node->NU = (UInt16)nu;
227 node2->NU = 0;
228 }
229 }
230 }
231
232 /* Fill lists of free blocks */
233 for (n = head; n != 0;)
234 {
235 CPpmd7_Node *node = NODE(n);
236 UInt32 nu = node->NU;
237 unsigned i;
238 n = node->Next;
239 if (nu == 0)
240 continue;
241 for (; nu > 128; nu -= 128, node += 128)
242 InsertNode(p, node, PPMD_NUM_INDEXES - 1);
243 if (I2U(i = U2I(nu)) != nu)
244 {
245 unsigned k = I2U(--i);
246 InsertNode(p, node + k, (unsigned)nu - k - 1);
247 }
248 InsertNode(p, node, i);
249 }
250 }
251
252
253 MY_NO_INLINE
AllocUnitsRare(CPpmd7 * p,unsigned indx)254 static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
255 {
256 unsigned i;
257
258 if (p->GlueCount == 0)
259 {
260 GlueFreeBlocks(p);
261 if (p->FreeList[indx] != 0)
262 return RemoveNode(p, indx);
263 }
264
265 i = indx;
266
267 do
268 {
269 if (++i == PPMD_NUM_INDEXES)
270 {
271 UInt32 numBytes = U2B(I2U(indx));
272 Byte *us = p->UnitsStart;
273 p->GlueCount--;
274 return ((UInt32)(us - p->Text) > numBytes) ? (p->UnitsStart = us - numBytes) : NULL;
275 }
276 }
277 while (p->FreeList[i] == 0);
278
279 {
280 void *block = RemoveNode(p, i);
281 SplitBlock(p, block, i, indx);
282 return block;
283 }
284 }
285
286
AllocUnits(CPpmd7 * p,unsigned indx)287 static void *AllocUnits(CPpmd7 *p, unsigned indx)
288 {
289 if (p->FreeList[indx] != 0)
290 return RemoveNode(p, indx);
291 {
292 UInt32 numBytes = U2B(I2U(indx));
293 Byte *lo = p->LoUnit;
294 if ((UInt32)(p->HiUnit - lo) >= numBytes)
295 {
296 p->LoUnit = lo + numBytes;
297 return lo;
298 }
299 }
300 return AllocUnitsRare(p, indx);
301 }
302
303
304 #define MyMem12Cpy(dest, src, num) \
305 { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \
306 do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }
307
308
309 /*
310 static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
311 {
312 unsigned i0 = U2I(oldNU);
313 unsigned i1 = U2I(newNU);
314 if (i0 == i1)
315 return oldPtr;
316 if (p->FreeList[i1] != 0)
317 {
318 void *ptr = RemoveNode(p, i1);
319 MyMem12Cpy(ptr, oldPtr, newNU);
320 InsertNode(p, oldPtr, i0);
321 return ptr;
322 }
323 SplitBlock(p, oldPtr, i0, i1);
324 return oldPtr;
325 }
326 */
327
328
329 #define SUCCESSOR(p) Ppmd_GET_SUCCESSOR(p)
SetSuccessor(CPpmd_State * p,CPpmd_Void_Ref v)330 static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
331 {
332 Ppmd_SET_SUCCESSOR(p, v);
333 }
334
335
336
337 MY_NO_INLINE
338 static
RestartModel(CPpmd7 * p)339 void RestartModel(CPpmd7 *p)
340 {
341 unsigned i, k;
342
343 memset(p->FreeList, 0, sizeof(p->FreeList));
344
345 p->Text = p->Base + p->AlignOffset;
346 p->HiUnit = p->Text + p->Size;
347 p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
348 p->GlueCount = 0;
349
350 p->OrderFall = p->MaxOrder;
351 p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
352 p->PrevSuccess = 0;
353
354 {
355 CPpmd7_Context *mc = (CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
356 CPpmd_State *s = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
357
358 p->LoUnit += U2B(256 / 2);
359 p->MaxContext = p->MinContext = mc;
360 p->FoundState = s;
361
362 mc->NumStats = 256;
363 mc->Union2.SummFreq = 256 + 1;
364 mc->Union4.Stats = REF(s);
365 mc->Suffix = 0;
366
367 for (i = 0; i < 256; i++, s++)
368 {
369 s->Symbol = (Byte)i;
370 s->Freq = 1;
371 SetSuccessor(s, 0);
372 }
373
374 #ifdef PPMD7_ORDER_0_SUPPPORT
375 if (p->MaxOrder == 0)
376 {
377 CPpmd_Void_Ref r = REF(mc);
378 s = p->FoundState;
379 for (i = 0; i < 256; i++, s++)
380 SetSuccessor(s, r);
381 return;
382 }
383 #endif
384 }
385
386 for (i = 0; i < 128; i++)
387
388
389
390 for (k = 0; k < 8; k++)
391 {
392 unsigned m;
393 UInt16 *dest = p->BinSumm[i] + k;
394 UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
395 for (m = 0; m < 64; m += 8)
396 dest[m] = val;
397 }
398
399
400 for (i = 0; i < 25; i++)
401 {
402
403 CPpmd_See *s = p->See[i];
404
405
406
407 unsigned summ = ((5 * i + 10) << (PPMD_PERIOD_BITS - 4));
408 for (k = 0; k < 16; k++, s++)
409 {
410 s->Summ = (UInt16)summ;
411 s->Shift = (PPMD_PERIOD_BITS - 4);
412 s->Count = 4;
413 }
414 }
415
416 p->DummySee.Summ = 0; /* unused */
417 p->DummySee.Shift = PPMD_PERIOD_BITS;
418 p->DummySee.Count = 64; /* unused */
419 }
420
421
Ppmd7_Init(CPpmd7 * p,unsigned maxOrder)422 void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
423 {
424 p->MaxOrder = maxOrder;
425
426 RestartModel(p);
427 }
428
429
430
431 /*
432 CreateSuccessors()
433 It's called when (FoundState->Successor) is RAW-Successor,
434 that is the link to position in Raw text.
435 So we create Context records and write the links to
436 FoundState->Successor and to identical RAW-Successors in suffix
437 contexts of MinContex.
438
439 The function returns:
440 if (OrderFall == 0) then MinContext is already at MAX order,
441 { return pointer to new or existing context of same MAX order }
442 else
443 { return pointer to new real context that will be (Order+1) in comparison with MinContext
444
445 also it can return pointer to real context of same order,
446 */
447
448 MY_NO_INLINE
CreateSuccessors(CPpmd7 * p)449 static CTX_PTR CreateSuccessors(CPpmd7 *p)
450 {
451 CTX_PTR c = p->MinContext;
452 CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
453 Byte newSym, newFreq;
454 unsigned numPs = 0;
455 CPpmd_State *ps[PPMD7_MAX_ORDER];
456
457 if (p->OrderFall != 0)
458 ps[numPs++] = p->FoundState;
459
460 while (c->Suffix)
461 {
462 CPpmd_Void_Ref successor;
463 CPpmd_State *s;
464 c = SUFFIX(c);
465
466
467 if (c->NumStats != 1)
468 {
469 Byte sym = p->FoundState->Symbol;
470 for (s = STATS(c); s->Symbol != sym; s++);
471
472 }
473 else
474 {
475 s = ONE_STATE(c);
476
477 }
478 successor = SUCCESSOR(s);
479 if (successor != upBranch)
480 {
481 // (c) is real record Context here,
482 c = CTX(successor);
483 if (numPs == 0)
484 {
485 // (c) is real record MAX Order Context here,
486 // So we don't need to create any new contexts.
487 return c;
488 }
489 break;
490 }
491 ps[numPs++] = s;
492 }
493
494 // All created contexts will have single-symbol with new RAW-Successor
495 // All new RAW-Successors will point to next position in RAW text
496 // after FoundState->Successor
497
498 newSym = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
499 upBranch++;
500
501
502 if (c->NumStats == 1)
503 newFreq = ONE_STATE(c)->Freq;
504 else
505 {
506 UInt32 cf, s0;
507 CPpmd_State *s;
508 for (s = STATS(c); s->Symbol != newSym; s++);
509 cf = (UInt32)s->Freq - 1;
510 s0 = (UInt32)c->Union2.SummFreq - c->NumStats - cf;
511 /*
512 cf - is frequency of symbol that will be Successor in new context records.
513 s0 - is commulative frequency sum of another symbols from parent context.
514 max(newFreq)= (s->Freq + 1), when (s0 == 1)
515 we have requirement (Ppmd7Context_OneState()->Freq <= 128) in BinSumm[]
516 so (s->Freq < 128) - is requirement for multi-symbol contexts
517 */
518 newFreq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : (2 * cf + s0 - 1) / (2 * s0) + 1));
519 }
520
521 // Create new single-symbol contexts from low order to high order in loop
522
523 do
524 {
525 CTX_PTR c1;
526 /* = AllocContext(p); */
527 if (p->HiUnit != p->LoUnit)
528 c1 = (CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE);
529 else if (p->FreeList[0] != 0)
530 c1 = (CTX_PTR)RemoveNode(p, 0);
531 else
532 {
533 c1 = (CTX_PTR)AllocUnitsRare(p, 0);
534 if (!c1)
535 return NULL;
536 }
537
538 c1->NumStats = 1;
539 ONE_STATE(c1)->Symbol = newSym;
540 ONE_STATE(c1)->Freq = newFreq;
541 SetSuccessor(ONE_STATE(c1), upBranch);
542 c1->Suffix = REF(c);
543 SetSuccessor(ps[--numPs], REF(c1));
544 c = c1;
545 }
546 while (numPs != 0);
547
548 return c;
549 }
550
551
552
553 #define SwapStates(s) \
554 { CPpmd_State tmp = s[0]; s[0] = s[-1]; s[-1] = tmp; }
555
556
557 void Ppmd7_UpdateModel(CPpmd7 *p);
558 MY_NO_INLINE
Ppmd7_UpdateModel(CPpmd7 * p)559 void Ppmd7_UpdateModel(CPpmd7 *p)
560 {
561 CPpmd_Void_Ref maxSuccessor, minSuccessor;
562 CTX_PTR c, mc;
563 unsigned s0, ns;
564
565
566
567 if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
568 {
569 /* Update Freqs in Suffix Context */
570
571 c = SUFFIX(p->MinContext);
572
573 if (c->NumStats == 1)
574 {
575 CPpmd_State *s = ONE_STATE(c);
576 if (s->Freq < 32)
577 s->Freq++;
578 }
579 else
580 {
581 CPpmd_State *s = STATS(c);
582 Byte sym = p->FoundState->Symbol;
583
584 if (s->Symbol != sym)
585 {
586 do
587 {
588 // s++; if (s->Symbol == sym) break;
589 s++;
590 }
591 while (s->Symbol != sym);
592
593 if (s[0].Freq >= s[-1].Freq)
594 {
595 SwapStates(s);
596 s--;
597 }
598 }
599
600 if (s->Freq < MAX_FREQ - 9)
601 {
602 s->Freq = (Byte)(s->Freq + 2);
603 c->Union2.SummFreq = (UInt16)(c->Union2.SummFreq + 2);
604 }
605 }
606 }
607
608
609 if (p->OrderFall == 0)
610 {
611 /* MAX ORDER context */
612 /* (FoundState->Successor) is RAW-Successor. */
613 p->MaxContext = p->MinContext = CreateSuccessors(p);
614 if (!p->MinContext)
615 {
616 RestartModel(p);
617 return;
618 }
619 SetSuccessor(p->FoundState, REF(p->MinContext));
620 return;
621 }
622
623
624 /* NON-MAX ORDER context */
625
626 {
627 Byte *text = p->Text;
628 *text++ = p->FoundState->Symbol;
629 p->Text = text;
630 if (text >= p->UnitsStart)
631 {
632 RestartModel(p);
633 return;
634 }
635 maxSuccessor = REF(text);
636 }
637
638 minSuccessor = SUCCESSOR(p->FoundState);
639
640 if (minSuccessor)
641 {
642 // there is Successor for FoundState in MinContext.
643 // So the next context will be one order higher than MinContext.
644
645 if (minSuccessor <= maxSuccessor)
646 {
647 // minSuccessor is RAW-Successor. So we will create real contexts records:
648 CTX_PTR cs = CreateSuccessors(p);
649 if (!cs)
650 {
651 RestartModel(p);
652 return;
653 }
654 minSuccessor = REF(cs);
655 }
656
657 // minSuccessor now is real Context pointer that points to existing (Order+1) context
658
659 if (--p->OrderFall == 0)
660 {
661 /*
662 if we move to MaxOrder context, then minSuccessor will be common Succesor for both:
663 MinContext that is (MaxOrder - 1)
664 MaxContext that is (MaxOrder)
665 so we don't need new RAW-Successor, and we can use real minSuccessor
666 as succssors for both MinContext and MaxContext.
667 */
668 maxSuccessor = minSuccessor;
669
670 /*
671 if (MaxContext != MinContext)
672 {
673 there was order fall from MaxOrder and we don't need current symbol
674 to transfer some RAW-Succesors to real contexts.
675 So we roll back pointer in raw data for one position.
676 }
677 */
678 p->Text -= (p->MaxContext != p->MinContext);
679 }
680 }
681 else
682 {
683 /*
684 FoundState has NULL-Successor here.
685 And only root 0-order context can contain NULL-Successors.
686 We change Successor in FoundState to RAW-Successor,
687 And next context will be same 0-order root Context.
688 */
689 SetSuccessor(p->FoundState, maxSuccessor);
690 minSuccessor = REF(p->MinContext);
691 }
692
693 mc = p->MinContext;
694 c = p->MaxContext;
695
696 p->MaxContext = p->MinContext = CTX(minSuccessor);
697
698 if (c == mc)
699 return;
700
701 // s0 : is pure Escape Freq
702 s0 = mc->Union2.SummFreq - (ns = mc->NumStats) - ((unsigned)p->FoundState->Freq - 1);
703
704 do
705 {
706 unsigned ns1;
707 UInt32 sum;
708
709 if ((ns1 = c->NumStats) != 1)
710 {
711 if ((ns1 & 1) == 0)
712 {
713 /* Expand for one UNIT */
714 unsigned oldNU = ns1 >> 1;
715 unsigned i = U2I(oldNU);
716 if (i != U2I((size_t)oldNU + 1))
717 {
718 void *ptr = AllocUnits(p, i + 1);
719 void *oldPtr;
720 if (!ptr)
721 {
722 RestartModel(p);
723 return;
724 }
725 oldPtr = STATS(c);
726 MyMem12Cpy(ptr, oldPtr, oldNU);
727 InsertNode(p, oldPtr, i);
728 c->Union4.Stats = STATS_REF(ptr);
729 }
730 }
731 sum = c->Union2.SummFreq;
732 /* max increase of Escape_Freq is 3 here.
733 total increase of Union2.SummFreq for all symbols is less than 256 here */
734 sum += (UInt32)(2 * ns1 < ns) + 2 * ((unsigned)(4 * ns1 <= ns) & (sum <= 8 * ns1));
735 /* original PPMdH uses 16-bit variable for (sum) here.
736 But (sum < 0x9000). So we don't truncate (sum) to 16-bit */
737 // sum = (UInt16)sum;
738 }
739 else
740 {
741 // instead of One-symbol context we create 2-symbol context
742 CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
743 if (!s)
744 {
745 RestartModel(p);
746 return;
747 }
748 {
749 unsigned freq = c->Union2.State2.Freq;
750 // s = *ONE_STATE(c);
751 s->Symbol = c->Union2.State2.Symbol;
752 s->Successor_0 = c->Union4.State4.Successor_0;
753 s->Successor_1 = c->Union4.State4.Successor_1;
754 // SetSuccessor(s, c->Union4.Stats); // call it only for debug purposes to check the order of
755 // (Successor_0 and Successor_1) in LE/BE.
756 c->Union4.Stats = REF(s);
757 if (freq < MAX_FREQ / 4 - 1)
758 freq <<= 1;
759 else
760 freq = MAX_FREQ - 4;
761 // (max(s->freq) == 120), when we convert from 1-symbol into 2-symbol context
762 s->Freq = (Byte)freq;
763 // max(InitEsc = PPMD7_kExpEscape[*]) is 25. So the max(escapeFreq) is 26 here
764 sum = freq + p->InitEsc + (ns > 3);
765 }
766 }
767
768 {
769 CPpmd_State *s = STATS(c) + ns1;
770 UInt32 cf = 2 * (sum + 6) * (UInt32)p->FoundState->Freq;
771 UInt32 sf = (UInt32)s0 + sum;
772 s->Symbol = p->FoundState->Symbol;
773 c->NumStats = (UInt16)(ns1 + 1);
774 SetSuccessor(s, maxSuccessor);
775
776 if (cf < 6 * sf)
777 {
778 cf = (UInt32)1 + (cf > sf) + (cf >= 4 * sf);
779 sum += 3;
780 /* It can add (0, 1, 2) to Escape_Freq */
781 }
782 else
783 {
784 cf = (UInt32)4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
785 sum += cf;
786 }
787
788 c->Union2.SummFreq = (UInt16)sum;
789 s->Freq = (Byte)cf;
790 }
791 c = SUFFIX(c);
792 }
793 while (c != mc);
794 }
795
796
797
798 MY_NO_INLINE
Rescale(CPpmd7 * p)799 static void Rescale(CPpmd7 *p)
800 {
801 unsigned i, adder, sumFreq, escFreq;
802 CPpmd_State *stats = STATS(p->MinContext);
803 CPpmd_State *s = p->FoundState;
804
805 /* Sort the list by Freq */
806 if (s != stats)
807 {
808 CPpmd_State tmp = *s;
809 do
810 s[0] = s[-1];
811 while (--s != stats);
812 *s = tmp;
813 }
814
815 sumFreq = s->Freq;
816 escFreq = p->MinContext->Union2.SummFreq - sumFreq;
817
818 /*
819 if (p->OrderFall == 0), adder = 0 : it's allowed to remove symbol from MAX Order context
820 if (p->OrderFall != 0), adder = 1 : it's NOT allowed to remove symbol from NON-MAX Order context
821 */
822
823 adder = (p->OrderFall != 0);
824
825 #ifdef PPMD7_ORDER_0_SUPPPORT
826 adder |= (p->MaxOrder == 0); // we don't remove symbols from order-0 context
827 #endif
828
829 sumFreq = (sumFreq + 4 + adder) >> 1;
830 i = (unsigned)p->MinContext->NumStats - 1;
831 s->Freq = (Byte)sumFreq;
832
833 do
834 {
835 unsigned freq = (++s)->Freq;
836 escFreq -= freq;
837 freq = (freq + adder) >> 1;
838 sumFreq += freq;
839 s->Freq = (Byte)freq;
840 if (freq > s[-1].Freq)
841 {
842 CPpmd_State tmp = *s;
843 CPpmd_State *s1 = s;
844 do
845 {
846 s1[0] = s1[-1];
847 }
848 while (--s1 != stats && freq > s1[-1].Freq);
849 *s1 = tmp;
850 }
851 }
852 while (--i);
853
854 if (s->Freq == 0)
855 {
856 /* Remove all items with Freq == 0 */
857 CPpmd7_Context *mc;
858 unsigned numStats, numStatsNew, n0, n1;
859
860 i = 0; do { i++; } while ((--s)->Freq == 0);
861
862 /* We increase (escFreq) for the number of removed symbols.
863 So we will have (0.5) increase for Escape_Freq in avarage per
864 removed symbol after Escape_Freq halving */
865 escFreq += i;
866 mc = p->MinContext;
867 numStats = mc->NumStats;
868 numStatsNew = numStats - i;
869 mc->NumStats = (UInt16)(numStatsNew);
870 n0 = (numStats + 1) >> 1;
871
872 if (numStatsNew == 1)
873 {
874 /* Create Single-Symbol context */
875 unsigned freq = stats->Freq;
876
877 do
878 {
879 escFreq >>= 1;
880 freq = (freq + 1) >> 1;
881 }
882 while (escFreq > 1);
883
884 s = ONE_STATE(mc);
885 *s = *stats;
886 s->Freq = (Byte)freq; // (freq <= 260 / 4)
887 p->FoundState = s;
888 InsertNode(p, stats, U2I(n0));
889 return;
890 }
891
892 n1 = (numStatsNew + 1) >> 1;
893 if (n0 != n1)
894 {
895 // p->MinContext->Union4.Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
896 unsigned i0 = U2I(n0);
897 unsigned i1 = U2I(n1);
898 if (i0 != i1)
899 {
900 if (p->FreeList[i1] != 0)
901 {
902 void *ptr = RemoveNode(p, i1);
903 p->MinContext->Union4.Stats = STATS_REF(ptr);
904 MyMem12Cpy(ptr, (const void *)stats, n1);
905 InsertNode(p, stats, i0);
906 }
907 else
908 SplitBlock(p, stats, i0, i1);
909 }
910 }
911 }
912 {
913 CPpmd7_Context *mc = p->MinContext;
914 mc->Union2.SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
915 // Escape_Freq halving here
916 p->FoundState = STATS(mc);
917 }
918 }
919
920
Ppmd7_MakeEscFreq(CPpmd7 * p,unsigned numMasked,UInt32 * escFreq)921 CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
922 {
923 CPpmd_See *see;
924 const CPpmd7_Context *mc = p->MinContext;
925 unsigned numStats = mc->NumStats;
926 if (numStats != 256)
927 {
928 unsigned nonMasked = numStats - numMasked;
929 see = p->See[(unsigned)p->NS2Indx[(size_t)nonMasked - 1]]
930 + (nonMasked < (unsigned)SUFFIX(mc)->NumStats - numStats)
931 + 2 * (unsigned)(mc->Union2.SummFreq < 11 * numStats)
932 + 4 * (unsigned)(numMasked > nonMasked) +
933 p->HiBitsFlag;
934 {
935 // if (see->Summ) field is larger than 16-bit, we need only low 16 bits of Summ
936 unsigned summ = (UInt16)see->Summ; // & 0xFFFF
937 unsigned r = (summ >> see->Shift);
938 see->Summ = (UInt16)(summ - r);
939 *escFreq = r + (r == 0);
940 }
941 }
942 else
943 {
944 see = &p->DummySee;
945 *escFreq = 1;
946 }
947 return see;
948 }
949
950
NextContext(CPpmd7 * p)951 static void NextContext(CPpmd7 *p)
952 {
953 CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
954 if (p->OrderFall == 0 && (const Byte *)c > p->Text)
955 p->MaxContext = p->MinContext = c;
956 else
957 Ppmd7_UpdateModel(p);
958 }
959
960
Ppmd7_Update1(CPpmd7 * p)961 void Ppmd7_Update1(CPpmd7 *p)
962 {
963 CPpmd_State *s = p->FoundState;
964 unsigned freq = s->Freq;
965 freq += 4;
966 p->MinContext->Union2.SummFreq = (UInt16)(p->MinContext->Union2.SummFreq + 4);
967 s->Freq = (Byte)freq;
968 if (freq > s[-1].Freq)
969 {
970 SwapStates(s);
971 p->FoundState = --s;
972 if (freq > MAX_FREQ)
973 Rescale(p);
974 }
975 NextContext(p);
976 }
977
978
Ppmd7_Update1_0(CPpmd7 * p)979 void Ppmd7_Update1_0(CPpmd7 *p)
980 {
981 CPpmd_State *s = p->FoundState;
982 CPpmd7_Context *mc = p->MinContext;
983 unsigned freq = s->Freq;
984 unsigned summFreq = mc->Union2.SummFreq;
985 p->PrevSuccess = (2 * freq > summFreq);
986 p->RunLength += (int)p->PrevSuccess;
987 mc->Union2.SummFreq = (UInt16)(summFreq + 4);
988 freq += 4;
989 s->Freq = (Byte)freq;
990 if (freq > MAX_FREQ)
991 Rescale(p);
992 NextContext(p);
993 }
994
995
996 /*
997 void Ppmd7_UpdateBin(CPpmd7 *p)
998 {
999 unsigned freq = p->FoundState->Freq;
1000 p->FoundState->Freq = (Byte)(freq + (freq < 128));
1001 p->PrevSuccess = 1;
1002 p->RunLength++;
1003 NextContext(p);
1004 }
1005 */
1006
Ppmd7_Update2(CPpmd7 * p)1007 void Ppmd7_Update2(CPpmd7 *p)
1008 {
1009 CPpmd_State *s = p->FoundState;
1010 unsigned freq = s->Freq;
1011 freq += 4;
1012 p->RunLength = p->InitRL;
1013 p->MinContext->Union2.SummFreq = (UInt16)(p->MinContext->Union2.SummFreq + 4);
1014 s->Freq = (Byte)freq;
1015 if (freq > MAX_FREQ)
1016 Rescale(p);
1017 Ppmd7_UpdateModel(p);
1018 }
1019
1020
1021
1022 /*
1023 PPMd Memory Map:
1024 {
1025 [ 0 ] contains subset of original raw text, that is required to create context
1026 records, Some symbols are not written, when max order context was reached
1027 [ Text ] free area
1028 [ UnitsStart ] CPpmd_State vectors and CPpmd7_Context records
1029 [ LoUnit ] free area for CPpmd_State and CPpmd7_Context items
1030 [ HiUnit ] CPpmd7_Context records
1031 [ Size ] end of array
1032 }
1033
1034 These addresses don't cross at any time.
1035 And the following condtions is true for addresses:
1036 (0 <= Text < UnitsStart <= LoUnit <= HiUnit <= Size)
1037
1038 Raw text is BYTE--aligned.
1039 the data in block [ UnitsStart ... Size ] contains 12-bytes aligned UNITs.
1040
1041 Last UNIT of array at offset (Size - 12) is root order-0 CPpmd7_Context record.
1042 The code can free UNITs memory blocks that were allocated to store CPpmd_State vectors.
1043 The code doesn't free UNITs allocated for CPpmd7_Context records.
1044
1045 The code calls RestartModel(), when there is no free memory for allocation.
1046 And RestartModel() changes the state to orignal start state, with full free block.
1047
1048
1049 The code allocates UNITs with the following order:
1050
1051 Allocation of 1 UNIT for Context record
1052 - from free space (HiUnit) down to (LoUnit)
1053 - from FreeList[0]
1054 - AllocUnitsRare()
1055
1056 AllocUnits() for CPpmd_State vectors:
1057 - from FreeList[i]
1058 - from free space (LoUnit) up to (HiUnit)
1059 - AllocUnitsRare()
1060
1061 AllocUnitsRare()
1062 - if (GlueCount == 0)
1063 { Glue lists, GlueCount = 255, allocate from FreeList[i]] }
1064 - loop for all higher sized FreeList[...] lists
1065 - from (UnitsStart - Text), GlueCount--
1066 - ERROR
1067
1068
1069 Each Record with Context contains the CPpmd_State vector, where each
1070 CPpmd_State contains the link to Successor.
1071 There are 3 types of Successor:
1072 1) NULL-Successor - NULL pointer. NULL-Successor links can be stored
1073 only in 0-order Root Context Record.
1074 We use 0 value as NULL-Successor
1075 2) RAW-Successor - the link to position in raw text,
1076 that "RAW-Successor" is being created after first
1077 occurrence of new symbol for some existing context record.
1078 (RAW-Successor > 0).
1079 3) RECORD-Successor - the link to CPpmd7_Context record of (Order+1),
1080 that record is being created when we go via RAW-Successor again.
1081
1082 For any successors at any time: the following condtions are true for Successor links:
1083 (NULL-Successor < RAW-Successor < UnitsStart <= RECORD-Successor)
1084
1085
1086 ---------- Symbol Frequency, SummFreq and Range in Range_Coder ----------
1087
1088 CPpmd7_Context::SummFreq = Sum(Stats[].Freq) + Escape_Freq
1089
1090 The PPMd code tries to fulfill the condition:
1091 (SummFreq <= (256 * 128 = RC::kBot))
1092
1093 We have (Sum(Stats[].Freq) <= 256 * 124), because of (MAX_FREQ = 124)
1094 So (4 = 128 - 124) is average reserve for Escape_Freq for each symbol.
1095 If (CPpmd_State::Freq) is not aligned for 4, the reserve can be 5, 6 or 7.
1096 SummFreq and Escape_Freq can be changed in Rescale() and *Update*() functions.
1097 Rescale() can remove symbols only from max-order contexts. So Escape_Freq can increase after multiple calls of Rescale() for
1098 max-order context.
1099
1100 When the PPMd code still break (Total <= RC::Range) condition in range coder,
1101 we have two ways to resolve that problem:
1102 1) we can report error, if we want to keep compatibility with original PPMd code that has no fix for such cases.
1103 2) we can reduce (Total) value to (RC::Range) by reducing (Escape_Freq) part of (Total) value.
1104 */
1105