diff options
author | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2021-12-27 00:00:00 +0000 |
---|---|---|
committer | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2022-03-18 15:35:13 +0500 |
commit | f19f813537c7aea1c20749c914e756b54a9c3cf5 (patch) | |
tree | 816ba62ca7c0fa19f2eb46d9e9d6f7dd7c3a744d /C/Ppmd7.c | |
parent | 98e06a519b63b81986abe76d28887f6984a7732b (diff) | |
download | 7zip-f19f813537c7aea1c20749c914e756b54a9c3cf5.tar.gz 7zip-f19f813537c7aea1c20749c914e756b54a9c3cf5.tar.bz2 7zip-f19f813537c7aea1c20749c914e756b54a9c3cf5.zip |
'21.07'21.07
Diffstat (limited to 'C/Ppmd7.c')
-rw-r--r-- | C/Ppmd7.c | 1104 |
1 files changed, 1104 insertions, 0 deletions
diff --git a/C/Ppmd7.c b/C/Ppmd7.c new file mode 100644 index 0000000..cf401cb --- /dev/null +++ b/C/Ppmd7.c | |||
@@ -0,0 +1,1104 @@ | |||
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 | |||
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 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 | |||
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 | |||
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 | |||
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 | |||
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 | ||
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 | |||
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) | ||
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 | ||
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 | |||
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 | ||
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 | ||
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 | ||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | */ | ||