Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/lzma/src/Ppmd7.c
4253 views
1
/* Ppmd7.c -- PPMdH codec
2
2023-09-07 : 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 PPMD7_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 * PPMD7_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
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
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
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 Ppmd7_GlueFreeBlocks().
111
So we use simple CPpmd_Void_Ref instead of CPpmd7_Node in Ppmd7_InsertNode() / Ppmd7_RemoveNode()
112
*/
113
114
#define EMPTY_NODE 0
115
116
117
static void Ppmd7_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
127
static void *Ppmd7_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
137
static void Ppmd7_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
Ppmd7_InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
145
}
146
Ppmd7_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
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 Ppmd7_GlueFreeBlocks()
159
we use single linked list similar to Ppmd8 code */
160
161
162
static void Ppmd7_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
Ppmd7_InsertNode(p, node, PPMD_NUM_INDEXES - 1);
243
if (I2U(i = U2I(nu)) != nu)
244
{
245
unsigned k = I2U(--i);
246
Ppmd7_InsertNode(p, node + k, (unsigned)nu - k - 1);
247
}
248
Ppmd7_InsertNode(p, node, i);
249
}
250
}
251
252
253
Z7_NO_INLINE
254
static void *Ppmd7_AllocUnitsRare(CPpmd7 *p, unsigned indx)
255
{
256
unsigned i;
257
258
if (p->GlueCount == 0)
259
{
260
Ppmd7_GlueFreeBlocks(p);
261
if (p->FreeList[indx] != 0)
262
return Ppmd7_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 = Ppmd7_RemoveNode(p, i);
281
Ppmd7_SplitBlock(p, block, i, indx);
282
return block;
283
}
284
}
285
286
287
static void *Ppmd7_AllocUnits(CPpmd7 *p, unsigned indx)
288
{
289
if (p->FreeList[indx] != 0)
290
return Ppmd7_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 Ppmd7_AllocUnitsRare(p, indx);
301
}
302
303
304
#define MEM_12_CPY(dest, src, num) \
305
{ UInt32 *d = (UInt32 *)(dest); \
306
const UInt32 *z = (const UInt32 *)(src); \
307
unsigned n = (num); \
308
do { \
309
d[0] = z[0]; \
310
d[1] = z[1]; \
311
d[2] = z[2]; \
312
z += 3; \
313
d += 3; \
314
} while (--n); \
315
}
316
317
318
/*
319
static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
320
{
321
unsigned i0 = U2I(oldNU);
322
unsigned i1 = U2I(newNU);
323
if (i0 == i1)
324
return oldPtr;
325
if (p->FreeList[i1] != 0)
326
{
327
void *ptr = Ppmd7_RemoveNode(p, i1);
328
MEM_12_CPY(ptr, oldPtr, newNU)
329
Ppmd7_InsertNode(p, oldPtr, i0);
330
return ptr;
331
}
332
Ppmd7_SplitBlock(p, oldPtr, i0, i1);
333
return oldPtr;
334
}
335
*/
336
337
338
#define SUCCESSOR(p) Ppmd_GET_SUCCESSOR(p)
339
static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
340
{
341
Ppmd_SET_SUCCESSOR(p, v)
342
}
343
344
345
346
Z7_NO_INLINE
347
static
348
void Ppmd7_RestartModel(CPpmd7 *p)
349
{
350
unsigned i, k;
351
352
memset(p->FreeList, 0, sizeof(p->FreeList));
353
354
p->Text = p->Base + p->AlignOffset;
355
p->HiUnit = p->Text + p->Size;
356
p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
357
p->GlueCount = 0;
358
359
p->OrderFall = p->MaxOrder;
360
p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
361
p->PrevSuccess = 0;
362
363
{
364
CPpmd7_Context *mc = (PPMD7_CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
365
CPpmd_State *s = (CPpmd_State *)p->LoUnit; /* Ppmd7_AllocUnits(p, PPMD_NUM_INDEXES - 1); */
366
367
p->LoUnit += U2B(256 / 2);
368
p->MaxContext = p->MinContext = mc;
369
p->FoundState = s;
370
371
mc->NumStats = 256;
372
mc->Union2.SummFreq = 256 + 1;
373
mc->Union4.Stats = REF(s);
374
mc->Suffix = 0;
375
376
for (i = 0; i < 256; i++, s++)
377
{
378
s->Symbol = (Byte)i;
379
s->Freq = 1;
380
SetSuccessor(s, 0);
381
}
382
383
#ifdef PPMD7_ORDER_0_SUPPPORT
384
if (p->MaxOrder == 0)
385
{
386
CPpmd_Void_Ref r = REF(mc);
387
s = p->FoundState;
388
for (i = 0; i < 256; i++, s++)
389
SetSuccessor(s, r);
390
return;
391
}
392
#endif
393
}
394
395
for (i = 0; i < 128; i++)
396
397
398
399
for (k = 0; k < 8; k++)
400
{
401
unsigned m;
402
UInt16 *dest = p->BinSumm[i] + k;
403
const UInt16 val = (UInt16)(PPMD_BIN_SCALE - PPMD7_kInitBinEsc[k] / (i + 2));
404
for (m = 0; m < 64; m += 8)
405
dest[m] = val;
406
}
407
408
409
for (i = 0; i < 25; i++)
410
{
411
412
CPpmd_See *s = p->See[i];
413
414
415
416
unsigned summ = ((5 * i + 10) << (PPMD_PERIOD_BITS - 4));
417
for (k = 0; k < 16; k++, s++)
418
{
419
s->Summ = (UInt16)summ;
420
s->Shift = (PPMD_PERIOD_BITS - 4);
421
s->Count = 4;
422
}
423
}
424
425
p->DummySee.Summ = 0; /* unused */
426
p->DummySee.Shift = PPMD_PERIOD_BITS;
427
p->DummySee.Count = 64; /* unused */
428
}
429
430
431
void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
432
{
433
p->MaxOrder = maxOrder;
434
435
Ppmd7_RestartModel(p);
436
}
437
438
439
440
/*
441
Ppmd7_CreateSuccessors()
442
It's called when (FoundState->Successor) is RAW-Successor,
443
that is the link to position in Raw text.
444
So we create Context records and write the links to
445
FoundState->Successor and to identical RAW-Successors in suffix
446
contexts of MinContex.
447
448
The function returns:
449
if (OrderFall == 0) then MinContext is already at MAX order,
450
{ return pointer to new or existing context of same MAX order }
451
else
452
{ return pointer to new real context that will be (Order+1) in comparison with MinContext
453
454
also it can return pointer to real context of same order,
455
*/
456
457
Z7_NO_INLINE
458
static PPMD7_CTX_PTR Ppmd7_CreateSuccessors(CPpmd7 *p)
459
{
460
PPMD7_CTX_PTR c = p->MinContext;
461
CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
462
Byte newSym, newFreq;
463
unsigned numPs = 0;
464
CPpmd_State *ps[PPMD7_MAX_ORDER];
465
466
if (p->OrderFall != 0)
467
ps[numPs++] = p->FoundState;
468
469
while (c->Suffix)
470
{
471
CPpmd_Void_Ref successor;
472
CPpmd_State *s;
473
c = SUFFIX(c);
474
475
476
if (c->NumStats != 1)
477
{
478
Byte sym = p->FoundState->Symbol;
479
for (s = STATS(c); s->Symbol != sym; s++);
480
481
}
482
else
483
{
484
s = ONE_STATE(c);
485
486
}
487
successor = SUCCESSOR(s);
488
if (successor != upBranch)
489
{
490
// (c) is real record Context here,
491
c = CTX(successor);
492
if (numPs == 0)
493
{
494
// (c) is real record MAX Order Context here,
495
// So we don't need to create any new contexts.
496
return c;
497
}
498
break;
499
}
500
ps[numPs++] = s;
501
}
502
503
// All created contexts will have single-symbol with new RAW-Successor
504
// All new RAW-Successors will point to next position in RAW text
505
// after FoundState->Successor
506
507
newSym = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
508
upBranch++;
509
510
511
if (c->NumStats == 1)
512
newFreq = ONE_STATE(c)->Freq;
513
else
514
{
515
UInt32 cf, s0;
516
CPpmd_State *s;
517
for (s = STATS(c); s->Symbol != newSym; s++);
518
cf = (UInt32)s->Freq - 1;
519
s0 = (UInt32)c->Union2.SummFreq - c->NumStats - cf;
520
/*
521
cf - is frequency of symbol that will be Successor in new context records.
522
s0 - is commulative frequency sum of another symbols from parent context.
523
max(newFreq)= (s->Freq + 1), when (s0 == 1)
524
we have requirement (Ppmd7Context_OneState()->Freq <= 128) in BinSumm[]
525
so (s->Freq < 128) - is requirement for multi-symbol contexts
526
*/
527
newFreq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : (2 * cf + s0 - 1) / (2 * s0) + 1));
528
}
529
530
// Create new single-symbol contexts from low order to high order in loop
531
532
do
533
{
534
PPMD7_CTX_PTR c1;
535
/* = AllocContext(p); */
536
if (p->HiUnit != p->LoUnit)
537
c1 = (PPMD7_CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE);
538
else if (p->FreeList[0] != 0)
539
c1 = (PPMD7_CTX_PTR)Ppmd7_RemoveNode(p, 0);
540
else
541
{
542
c1 = (PPMD7_CTX_PTR)Ppmd7_AllocUnitsRare(p, 0);
543
if (!c1)
544
return NULL;
545
}
546
547
c1->NumStats = 1;
548
ONE_STATE(c1)->Symbol = newSym;
549
ONE_STATE(c1)->Freq = newFreq;
550
SetSuccessor(ONE_STATE(c1), upBranch);
551
c1->Suffix = REF(c);
552
SetSuccessor(ps[--numPs], REF(c1));
553
c = c1;
554
}
555
while (numPs != 0);
556
557
return c;
558
}
559
560
561
562
#define SWAP_STATES(s) \
563
{ CPpmd_State tmp = s[0]; s[0] = s[-1]; s[-1] = tmp; }
564
565
566
void Ppmd7_UpdateModel(CPpmd7 *p);
567
Z7_NO_INLINE
568
void Ppmd7_UpdateModel(CPpmd7 *p)
569
{
570
CPpmd_Void_Ref maxSuccessor, minSuccessor;
571
PPMD7_CTX_PTR c, mc;
572
unsigned s0, ns;
573
574
575
576
if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
577
{
578
/* Update Freqs in Suffix Context */
579
580
c = SUFFIX(p->MinContext);
581
582
if (c->NumStats == 1)
583
{
584
CPpmd_State *s = ONE_STATE(c);
585
if (s->Freq < 32)
586
s->Freq++;
587
}
588
else
589
{
590
CPpmd_State *s = STATS(c);
591
Byte sym = p->FoundState->Symbol;
592
593
if (s->Symbol != sym)
594
{
595
do
596
{
597
// s++; if (s->Symbol == sym) break;
598
s++;
599
}
600
while (s->Symbol != sym);
601
602
if (s[0].Freq >= s[-1].Freq)
603
{
604
SWAP_STATES(s)
605
s--;
606
}
607
}
608
609
if (s->Freq < MAX_FREQ - 9)
610
{
611
s->Freq = (Byte)(s->Freq + 2);
612
c->Union2.SummFreq = (UInt16)(c->Union2.SummFreq + 2);
613
}
614
}
615
}
616
617
618
if (p->OrderFall == 0)
619
{
620
/* MAX ORDER context */
621
/* (FoundState->Successor) is RAW-Successor. */
622
p->MaxContext = p->MinContext = Ppmd7_CreateSuccessors(p);
623
if (!p->MinContext)
624
{
625
Ppmd7_RestartModel(p);
626
return;
627
}
628
SetSuccessor(p->FoundState, REF(p->MinContext));
629
return;
630
}
631
632
633
/* NON-MAX ORDER context */
634
635
{
636
Byte *text = p->Text;
637
*text++ = p->FoundState->Symbol;
638
p->Text = text;
639
if (text >= p->UnitsStart)
640
{
641
Ppmd7_RestartModel(p);
642
return;
643
}
644
maxSuccessor = REF(text);
645
}
646
647
minSuccessor = SUCCESSOR(p->FoundState);
648
649
if (minSuccessor)
650
{
651
// there is Successor for FoundState in MinContext.
652
// So the next context will be one order higher than MinContext.
653
654
if (minSuccessor <= maxSuccessor)
655
{
656
// minSuccessor is RAW-Successor. So we will create real contexts records:
657
PPMD7_CTX_PTR cs = Ppmd7_CreateSuccessors(p);
658
if (!cs)
659
{
660
Ppmd7_RestartModel(p);
661
return;
662
}
663
minSuccessor = REF(cs);
664
}
665
666
// minSuccessor now is real Context pointer that points to existing (Order+1) context
667
668
if (--p->OrderFall == 0)
669
{
670
/*
671
if we move to MaxOrder context, then minSuccessor will be common Succesor for both:
672
MinContext that is (MaxOrder - 1)
673
MaxContext that is (MaxOrder)
674
so we don't need new RAW-Successor, and we can use real minSuccessor
675
as succssors for both MinContext and MaxContext.
676
*/
677
maxSuccessor = minSuccessor;
678
679
/*
680
if (MaxContext != MinContext)
681
{
682
there was order fall from MaxOrder and we don't need current symbol
683
to transfer some RAW-Succesors to real contexts.
684
So we roll back pointer in raw data for one position.
685
}
686
*/
687
p->Text -= (p->MaxContext != p->MinContext);
688
}
689
}
690
else
691
{
692
/*
693
FoundState has NULL-Successor here.
694
And only root 0-order context can contain NULL-Successors.
695
We change Successor in FoundState to RAW-Successor,
696
And next context will be same 0-order root Context.
697
*/
698
SetSuccessor(p->FoundState, maxSuccessor);
699
minSuccessor = REF(p->MinContext);
700
}
701
702
mc = p->MinContext;
703
c = p->MaxContext;
704
705
p->MaxContext = p->MinContext = CTX(minSuccessor);
706
707
if (c == mc)
708
return;
709
710
// s0 : is pure Escape Freq
711
s0 = mc->Union2.SummFreq - (ns = mc->NumStats) - ((unsigned)p->FoundState->Freq - 1);
712
713
do
714
{
715
unsigned ns1;
716
UInt32 sum;
717
718
if ((ns1 = c->NumStats) != 1)
719
{
720
if ((ns1 & 1) == 0)
721
{
722
/* Expand for one UNIT */
723
const unsigned oldNU = ns1 >> 1;
724
const unsigned i = U2I(oldNU);
725
if (i != U2I((size_t)oldNU + 1))
726
{
727
void *ptr = Ppmd7_AllocUnits(p, i + 1);
728
void *oldPtr;
729
if (!ptr)
730
{
731
Ppmd7_RestartModel(p);
732
return;
733
}
734
oldPtr = STATS(c);
735
MEM_12_CPY(ptr, oldPtr, oldNU)
736
Ppmd7_InsertNode(p, oldPtr, i);
737
c->Union4.Stats = STATS_REF(ptr);
738
}
739
}
740
sum = c->Union2.SummFreq;
741
/* max increase of Escape_Freq is 3 here.
742
total increase of Union2.SummFreq for all symbols is less than 256 here */
743
sum += (UInt32)(unsigned)((2 * ns1 < ns) + 2 * ((unsigned)(4 * ns1 <= ns) & (sum <= 8 * ns1)));
744
/* original PPMdH uses 16-bit variable for (sum) here.
745
But (sum < 0x9000). So we don't truncate (sum) to 16-bit */
746
// sum = (UInt16)sum;
747
}
748
else
749
{
750
// instead of One-symbol context we create 2-symbol context
751
CPpmd_State *s = (CPpmd_State*)Ppmd7_AllocUnits(p, 0);
752
if (!s)
753
{
754
Ppmd7_RestartModel(p);
755
return;
756
}
757
{
758
unsigned freq = c->Union2.State2.Freq;
759
// s = *ONE_STATE(c);
760
s->Symbol = c->Union2.State2.Symbol;
761
s->Successor_0 = c->Union4.State4.Successor_0;
762
s->Successor_1 = c->Union4.State4.Successor_1;
763
// SetSuccessor(s, c->Union4.Stats); // call it only for debug purposes to check the order of
764
// (Successor_0 and Successor_1) in LE/BE.
765
c->Union4.Stats = REF(s);
766
if (freq < MAX_FREQ / 4 - 1)
767
freq <<= 1;
768
else
769
freq = MAX_FREQ - 4;
770
// (max(s->freq) == 120), when we convert from 1-symbol into 2-symbol context
771
s->Freq = (Byte)freq;
772
// max(InitEsc = PPMD7_kExpEscape[*]) is 25. So the max(escapeFreq) is 26 here
773
sum = (UInt32)(freq + p->InitEsc + (ns > 3));
774
}
775
}
776
777
{
778
CPpmd_State *s = STATS(c) + ns1;
779
UInt32 cf = 2 * (sum + 6) * (UInt32)p->FoundState->Freq;
780
UInt32 sf = (UInt32)s0 + sum;
781
s->Symbol = p->FoundState->Symbol;
782
c->NumStats = (UInt16)(ns1 + 1);
783
SetSuccessor(s, maxSuccessor);
784
785
if (cf < 6 * sf)
786
{
787
cf = (UInt32)1 + (cf > sf) + (cf >= 4 * sf);
788
sum += 3;
789
/* It can add (0, 1, 2) to Escape_Freq */
790
}
791
else
792
{
793
cf = (UInt32)4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
794
sum += cf;
795
}
796
797
c->Union2.SummFreq = (UInt16)sum;
798
s->Freq = (Byte)cf;
799
}
800
c = SUFFIX(c);
801
}
802
while (c != mc);
803
}
804
805
806
807
Z7_NO_INLINE
808
static void Ppmd7_Rescale(CPpmd7 *p)
809
{
810
unsigned i, adder, sumFreq, escFreq;
811
CPpmd_State *stats = STATS(p->MinContext);
812
CPpmd_State *s = p->FoundState;
813
814
/* Sort the list by Freq */
815
if (s != stats)
816
{
817
CPpmd_State tmp = *s;
818
do
819
s[0] = s[-1];
820
while (--s != stats);
821
*s = tmp;
822
}
823
824
sumFreq = s->Freq;
825
escFreq = p->MinContext->Union2.SummFreq - sumFreq;
826
827
/*
828
if (p->OrderFall == 0), adder = 0 : it's allowed to remove symbol from MAX Order context
829
if (p->OrderFall != 0), adder = 1 : it's NOT allowed to remove symbol from NON-MAX Order context
830
*/
831
832
adder = (p->OrderFall != 0);
833
834
#ifdef PPMD7_ORDER_0_SUPPPORT
835
adder |= (p->MaxOrder == 0); // we don't remove symbols from order-0 context
836
#endif
837
838
sumFreq = (sumFreq + 4 + adder) >> 1;
839
i = (unsigned)p->MinContext->NumStats - 1;
840
s->Freq = (Byte)sumFreq;
841
842
do
843
{
844
unsigned freq = (++s)->Freq;
845
escFreq -= freq;
846
freq = (freq + adder) >> 1;
847
sumFreq += freq;
848
s->Freq = (Byte)freq;
849
if (freq > s[-1].Freq)
850
{
851
CPpmd_State tmp = *s;
852
CPpmd_State *s1 = s;
853
do
854
{
855
s1[0] = s1[-1];
856
}
857
while (--s1 != stats && freq > s1[-1].Freq);
858
*s1 = tmp;
859
}
860
}
861
while (--i);
862
863
if (s->Freq == 0)
864
{
865
/* Remove all items with Freq == 0 */
866
CPpmd7_Context *mc;
867
unsigned numStats, numStatsNew, n0, n1;
868
869
i = 0; do { i++; } while ((--s)->Freq == 0);
870
871
/* We increase (escFreq) for the number of removed symbols.
872
So we will have (0.5) increase for Escape_Freq in avarage per
873
removed symbol after Escape_Freq halving */
874
escFreq += i;
875
mc = p->MinContext;
876
numStats = mc->NumStats;
877
numStatsNew = numStats - i;
878
mc->NumStats = (UInt16)(numStatsNew);
879
n0 = (numStats + 1) >> 1;
880
881
if (numStatsNew == 1)
882
{
883
/* Create Single-Symbol context */
884
unsigned freq = stats->Freq;
885
886
do
887
{
888
escFreq >>= 1;
889
freq = (freq + 1) >> 1;
890
}
891
while (escFreq > 1);
892
893
s = ONE_STATE(mc);
894
*s = *stats;
895
s->Freq = (Byte)freq; // (freq <= 260 / 4)
896
p->FoundState = s;
897
Ppmd7_InsertNode(p, stats, U2I(n0));
898
return;
899
}
900
901
n1 = (numStatsNew + 1) >> 1;
902
if (n0 != n1)
903
{
904
// p->MinContext->Union4.Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
905
unsigned i0 = U2I(n0);
906
unsigned i1 = U2I(n1);
907
if (i0 != i1)
908
{
909
if (p->FreeList[i1] != 0)
910
{
911
void *ptr = Ppmd7_RemoveNode(p, i1);
912
p->MinContext->Union4.Stats = STATS_REF(ptr);
913
MEM_12_CPY(ptr, (const void *)stats, n1)
914
Ppmd7_InsertNode(p, stats, i0);
915
}
916
else
917
Ppmd7_SplitBlock(p, stats, i0, i1);
918
}
919
}
920
}
921
{
922
CPpmd7_Context *mc = p->MinContext;
923
mc->Union2.SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
924
// Escape_Freq halving here
925
p->FoundState = STATS(mc);
926
}
927
}
928
929
930
CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
931
{
932
CPpmd_See *see;
933
const CPpmd7_Context *mc = p->MinContext;
934
unsigned numStats = mc->NumStats;
935
if (numStats != 256)
936
{
937
unsigned nonMasked = numStats - numMasked;
938
see = p->See[(unsigned)p->NS2Indx[(size_t)nonMasked - 1]]
939
+ (nonMasked < (unsigned)SUFFIX(mc)->NumStats - numStats)
940
+ 2 * (unsigned)(mc->Union2.SummFreq < 11 * numStats)
941
+ 4 * (unsigned)(numMasked > nonMasked) +
942
p->HiBitsFlag;
943
{
944
// if (see->Summ) field is larger than 16-bit, we need only low 16 bits of Summ
945
const unsigned summ = (UInt16)see->Summ; // & 0xFFFF
946
const unsigned r = (summ >> see->Shift);
947
see->Summ = (UInt16)(summ - r);
948
*escFreq = (UInt32)(r + (r == 0));
949
}
950
}
951
else
952
{
953
see = &p->DummySee;
954
*escFreq = 1;
955
}
956
return see;
957
}
958
959
960
static void Ppmd7_NextContext(CPpmd7 *p)
961
{
962
PPMD7_CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
963
if (p->OrderFall == 0 && (const Byte *)c > p->Text)
964
p->MaxContext = p->MinContext = c;
965
else
966
Ppmd7_UpdateModel(p);
967
}
968
969
970
void Ppmd7_Update1(CPpmd7 *p)
971
{
972
CPpmd_State *s = p->FoundState;
973
unsigned freq = s->Freq;
974
freq += 4;
975
p->MinContext->Union2.SummFreq = (UInt16)(p->MinContext->Union2.SummFreq + 4);
976
s->Freq = (Byte)freq;
977
if (freq > s[-1].Freq)
978
{
979
SWAP_STATES(s)
980
p->FoundState = --s;
981
if (freq > MAX_FREQ)
982
Ppmd7_Rescale(p);
983
}
984
Ppmd7_NextContext(p);
985
}
986
987
988
void Ppmd7_Update1_0(CPpmd7 *p)
989
{
990
CPpmd_State *s = p->FoundState;
991
CPpmd7_Context *mc = p->MinContext;
992
unsigned freq = s->Freq;
993
const unsigned summFreq = mc->Union2.SummFreq;
994
p->PrevSuccess = (2 * freq > summFreq);
995
p->RunLength += (Int32)p->PrevSuccess;
996
mc->Union2.SummFreq = (UInt16)(summFreq + 4);
997
freq += 4;
998
s->Freq = (Byte)freq;
999
if (freq > MAX_FREQ)
1000
Ppmd7_Rescale(p);
1001
Ppmd7_NextContext(p);
1002
}
1003
1004
1005
/*
1006
void Ppmd7_UpdateBin(CPpmd7 *p)
1007
{
1008
unsigned freq = p->FoundState->Freq;
1009
p->FoundState->Freq = (Byte)(freq + (freq < 128));
1010
p->PrevSuccess = 1;
1011
p->RunLength++;
1012
Ppmd7_NextContext(p);
1013
}
1014
*/
1015
1016
void Ppmd7_Update2(CPpmd7 *p)
1017
{
1018
CPpmd_State *s = p->FoundState;
1019
unsigned freq = s->Freq;
1020
freq += 4;
1021
p->RunLength = p->InitRL;
1022
p->MinContext->Union2.SummFreq = (UInt16)(p->MinContext->Union2.SummFreq + 4);
1023
s->Freq = (Byte)freq;
1024
if (freq > MAX_FREQ)
1025
Ppmd7_Rescale(p);
1026
Ppmd7_UpdateModel(p);
1027
}
1028
1029
1030
1031
/*
1032
PPMd Memory Map:
1033
{
1034
[ 0 ] contains subset of original raw text, that is required to create context
1035
records, Some symbols are not written, when max order context was reached
1036
[ Text ] free area
1037
[ UnitsStart ] CPpmd_State vectors and CPpmd7_Context records
1038
[ LoUnit ] free area for CPpmd_State and CPpmd7_Context items
1039
[ HiUnit ] CPpmd7_Context records
1040
[ Size ] end of array
1041
}
1042
1043
These addresses don't cross at any time.
1044
And the following condtions is true for addresses:
1045
(0 <= Text < UnitsStart <= LoUnit <= HiUnit <= Size)
1046
1047
Raw text is BYTE--aligned.
1048
the data in block [ UnitsStart ... Size ] contains 12-bytes aligned UNITs.
1049
1050
Last UNIT of array at offset (Size - 12) is root order-0 CPpmd7_Context record.
1051
The code can free UNITs memory blocks that were allocated to store CPpmd_State vectors.
1052
The code doesn't free UNITs allocated for CPpmd7_Context records.
1053
1054
The code calls Ppmd7_RestartModel(), when there is no free memory for allocation.
1055
And Ppmd7_RestartModel() changes the state to orignal start state, with full free block.
1056
1057
1058
The code allocates UNITs with the following order:
1059
1060
Allocation of 1 UNIT for Context record
1061
- from free space (HiUnit) down to (LoUnit)
1062
- from FreeList[0]
1063
- Ppmd7_AllocUnitsRare()
1064
1065
Ppmd7_AllocUnits() for CPpmd_State vectors:
1066
- from FreeList[i]
1067
- from free space (LoUnit) up to (HiUnit)
1068
- Ppmd7_AllocUnitsRare()
1069
1070
Ppmd7_AllocUnitsRare()
1071
- if (GlueCount == 0)
1072
{ Glue lists, GlueCount = 255, allocate from FreeList[i]] }
1073
- loop for all higher sized FreeList[...] lists
1074
- from (UnitsStart - Text), GlueCount--
1075
- ERROR
1076
1077
1078
Each Record with Context contains the CPpmd_State vector, where each
1079
CPpmd_State contains the link to Successor.
1080
There are 3 types of Successor:
1081
1) NULL-Successor - NULL pointer. NULL-Successor links can be stored
1082
only in 0-order Root Context Record.
1083
We use 0 value as NULL-Successor
1084
2) RAW-Successor - the link to position in raw text,
1085
that "RAW-Successor" is being created after first
1086
occurrence of new symbol for some existing context record.
1087
(RAW-Successor > 0).
1088
3) RECORD-Successor - the link to CPpmd7_Context record of (Order+1),
1089
that record is being created when we go via RAW-Successor again.
1090
1091
For any successors at any time: the following condtions are true for Successor links:
1092
(NULL-Successor < RAW-Successor < UnitsStart <= RECORD-Successor)
1093
1094
1095
---------- Symbol Frequency, SummFreq and Range in Range_Coder ----------
1096
1097
CPpmd7_Context::SummFreq = Sum(Stats[].Freq) + Escape_Freq
1098
1099
The PPMd code tries to fulfill the condition:
1100
(SummFreq <= (256 * 128 = RC::kBot))
1101
1102
We have (Sum(Stats[].Freq) <= 256 * 124), because of (MAX_FREQ = 124)
1103
So (4 = 128 - 124) is average reserve for Escape_Freq for each symbol.
1104
If (CPpmd_State::Freq) is not aligned for 4, the reserve can be 5, 6 or 7.
1105
SummFreq and Escape_Freq can be changed in Ppmd7_Rescale() and *Update*() functions.
1106
Ppmd7_Rescale() can remove symbols only from max-order contexts. So Escape_Freq can increase after multiple calls of Ppmd7_Rescale() for
1107
max-order context.
1108
1109
When the PPMd code still break (Total <= RC::Range) condition in range coder,
1110
we have two ways to resolve that problem:
1111
1) we can report error, if we want to keep compatibility with original PPMd code that has no fix for such cases.
1112
2) we can reduce (Total) value to (RC::Range) by reducing (Escape_Freq) part of (Total) value.
1113
*/
1114
1115
#undef MAX_FREQ
1116
#undef UNIT_SIZE
1117
#undef U2B
1118
#undef U2I
1119
#undef I2U
1120
#undef I2U_UInt16
1121
#undef REF
1122
#undef STATS_REF
1123
#undef CTX
1124
#undef STATS
1125
#undef ONE_STATE
1126
#undef SUFFIX
1127
#undef NODE
1128
#undef EMPTY_NODE
1129
#undef MEM_12_CPY
1130
#undef SUCCESSOR
1131
#undef SWAP_STATES
1132
1133