1 /* Ppmd7.c -- PPMdH codec
2 2018-07-04 : Igor Pavlov : Public domain
3 This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
11 const Byte PPMD7_kExpEscape
[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
12 static const UInt16 kInitBinEsc
[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
17 #define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
18 #define U2I(nu) (p->Units2Indx[(size_t)(nu) - 1])
19 #define I2U(indx) (p->Indx2Units[indx])
22 #define REF(ptr) (ptr)
24 #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
27 #define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
29 #define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
30 #define STATS(ctx) Ppmd7_GetStats(p, ctx)
31 #define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
32 #define SUFFIX(ctx) CTX((ctx)->Suffix)
34 typedef CPpmd7_Context
* CTX_PTR
;
46 typedef struct CPpmd7_Node_
48 UInt16 Stamp
; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
50 CPpmd7_Node_Ref Next
; /* must be at offset >= 4 */
55 #define NODE(ptr) (ptr)
57 #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
60 void Ppmd7_Construct(CPpmd7
*p
)
66 for (i
= 0, k
= 0; i
< PPMD_NUM_INDEXES
; i
++)
68 unsigned step
= (i
>= 12 ? 4 : (i
>> 2) + 1);
69 do { p
->Units2Indx
[k
++] = (Byte
)i
; } while (--step
);
70 p
->Indx2Units
[i
] = (Byte
)k
;
73 p
->NS2BSIndx
[0] = (0 << 1);
74 p
->NS2BSIndx
[1] = (1 << 1);
75 memset(p
->NS2BSIndx
+ 2, (2 << 1), 9);
76 memset(p
->NS2BSIndx
+ 11, (3 << 1), 256 - 11);
78 for (i
= 0; i
< 3; i
++)
79 p
->NS2Indx
[i
] = (Byte
)i
;
80 for (m
= i
, k
= 1; i
< 256; i
++)
82 p
->NS2Indx
[i
] = (Byte
)m
;
87 memset(p
->HB2Flag
, 0, 0x40);
88 memset(p
->HB2Flag
+ 0x40, 8, 0x100 - 0x40);
91 void Ppmd7_Free(CPpmd7
*p
, ISzAllocPtr alloc
)
93 ISzAlloc_Free(alloc
, p
->Base
);
98 BoolInt
Ppmd7_Alloc(CPpmd7
*p
, UInt32 size
, ISzAllocPtr alloc
)
100 if (!p
->Base
|| p
->Size
!= size
)
103 Ppmd7_Free(p
, alloc
);
115 if ((p
->Base
= (Byte
*)ISzAlloc_Alloc(alloc
, p
->AlignOffset
+ size
+ size2
)) == 0)
122 static void InsertNode(CPpmd7
*p
, void *node
, unsigned indx
)
124 *((CPpmd_Void_Ref
*)node
) = p
->FreeList
[indx
];
125 p
->FreeList
[indx
] = REF(node
);
128 static void *RemoveNode(CPpmd7
*p
, unsigned indx
)
130 CPpmd_Void_Ref
*node
= (CPpmd_Void_Ref
*)Ppmd7_GetPtr(p
, p
->FreeList
[indx
]);
131 p
->FreeList
[indx
] = *node
;
135 static void SplitBlock(CPpmd7
*p
, void *ptr
, unsigned oldIndx
, unsigned newIndx
)
137 unsigned i
, nu
= I2U(oldIndx
) - I2U(newIndx
);
138 ptr
= (Byte
*)ptr
+ U2B(I2U(newIndx
));
139 if (I2U(i
= U2I(nu
)) != nu
)
141 unsigned k
= I2U(--i
);
142 InsertNode(p
, ((Byte
*)ptr
) + U2B(k
), nu
- k
- 1);
144 InsertNode(p
, ptr
, i
);
147 static void GlueFreeBlocks(CPpmd7
*p
)
150 CPpmd7_Node headItem
;
151 CPpmd7_Node_Ref head
= &headItem
;
153 CPpmd7_Node_Ref head
= p
->AlignOffset
+ p
->Size
;
156 CPpmd7_Node_Ref n
= head
;
161 /* create doubly-linked list of free blocks */
162 for (i
= 0; i
< PPMD_NUM_INDEXES
; i
++)
165 CPpmd7_Node_Ref next
= (CPpmd7_Node_Ref
)p
->FreeList
[i
];
169 CPpmd7_Node
*node
= NODE(next
);
171 n
= NODE(n
)->Prev
= next
;
172 next
= *(const CPpmd7_Node_Ref
*)node
;
174 node
->NU
= (UInt16
)nu
;
177 NODE(head
)->Stamp
= 1;
178 NODE(head
)->Next
= n
;
179 NODE(n
)->Prev
= head
;
180 if (p
->LoUnit
!= p
->HiUnit
)
181 ((CPpmd7_Node
*)p
->LoUnit
)->Stamp
= 1;
183 /* Glue free blocks */
186 CPpmd7_Node
*node
= NODE(n
);
187 UInt32 nu
= (UInt32
)node
->NU
;
190 CPpmd7_Node
*node2
= NODE(n
) + nu
;
192 if (node2
->Stamp
!= 0 || nu
>= 0x10000)
194 NODE(node2
->Prev
)->Next
= node2
->Next
;
195 NODE(node2
->Next
)->Prev
= node2
->Prev
;
196 node
->NU
= (UInt16
)nu
;
201 /* Fill lists of free blocks */
202 for (n
= NODE(head
)->Next
; n
!= head
;)
204 CPpmd7_Node
*node
= NODE(n
);
206 CPpmd7_Node_Ref next
= node
->Next
;
207 for (nu
= node
->NU
; nu
> 128; nu
-= 128, node
+= 128)
208 InsertNode(p
, node
, PPMD_NUM_INDEXES
- 1);
209 if (I2U(i
= U2I(nu
)) != nu
)
211 unsigned k
= I2U(--i
);
212 InsertNode(p
, node
+ k
, nu
- k
- 1);
214 InsertNode(p
, node
, i
);
219 static void *AllocUnitsRare(CPpmd7
*p
, unsigned indx
)
223 if (p
->GlueCount
== 0)
226 if (p
->FreeList
[indx
] != 0)
227 return RemoveNode(p
, indx
);
232 if (++i
== PPMD_NUM_INDEXES
)
234 UInt32 numBytes
= U2B(I2U(indx
));
236 return ((UInt32
)(p
->UnitsStart
- p
->Text
) > numBytes
) ? (p
->UnitsStart
-= numBytes
) : (NULL
);
239 while (p
->FreeList
[i
] == 0);
240 retVal
= RemoveNode(p
, i
);
241 SplitBlock(p
, retVal
, i
, indx
);
245 static void *AllocUnits(CPpmd7
*p
, unsigned indx
)
248 if (p
->FreeList
[indx
] != 0)
249 return RemoveNode(p
, indx
);
250 numBytes
= U2B(I2U(indx
));
251 if (numBytes
<= (UInt32
)(p
->HiUnit
- p
->LoUnit
))
253 void *retVal
= p
->LoUnit
;
254 p
->LoUnit
+= numBytes
;
257 return AllocUnitsRare(p
, indx
);
260 #define MyMem12Cpy(dest, src, num) \
261 { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
262 do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while (--n); }
264 static void *ShrinkUnits(CPpmd7
*p
, void *oldPtr
, unsigned oldNU
, unsigned newNU
)
266 unsigned i0
= U2I(oldNU
);
267 unsigned i1
= U2I(newNU
);
270 if (p
->FreeList
[i1
] != 0)
272 void *ptr
= RemoveNode(p
, i1
);
273 MyMem12Cpy(ptr
, oldPtr
, newNU
);
274 InsertNode(p
, oldPtr
, i0
);
277 SplitBlock(p
, oldPtr
, i0
, i1
);
281 #define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
283 static void SetSuccessor(CPpmd_State
*p
, CPpmd_Void_Ref v
)
285 (p
)->SuccessorLow
= (UInt16
)((UInt32
)(v
) & 0xFFFF);
286 (p
)->SuccessorHigh
= (UInt16
)(((UInt32
)(v
) >> 16) & 0xFFFF);
289 static void RestartModel(CPpmd7
*p
)
293 memset(p
->FreeList
, 0, sizeof(p
->FreeList
));
294 p
->Text
= p
->Base
+ p
->AlignOffset
;
295 p
->HiUnit
= p
->Text
+ p
->Size
;
296 p
->LoUnit
= p
->UnitsStart
= p
->HiUnit
- p
->Size
/ 8 / UNIT_SIZE
* 7 * UNIT_SIZE
;
299 p
->OrderFall
= p
->MaxOrder
;
300 p
->RunLength
= p
->InitRL
= -(Int32
)((p
->MaxOrder
< 12) ? p
->MaxOrder
: 12) - 1;
303 p
->MinContext
= p
->MaxContext
= (CTX_PTR
)(p
->HiUnit
-= UNIT_SIZE
); /* AllocContext(p); */
304 p
->MinContext
->Suffix
= 0;
305 p
->MinContext
->NumStats
= 256;
306 p
->MinContext
->SummFreq
= 256 + 1;
307 p
->FoundState
= (CPpmd_State
*)p
->LoUnit
; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
308 p
->LoUnit
+= U2B(256 / 2);
309 p
->MinContext
->Stats
= REF(p
->FoundState
);
310 for (i
= 0; i
< 256; i
++)
312 CPpmd_State
*s
= &p
->FoundState
[i
];
318 for (i
= 0; i
< 128; i
++)
319 for (k
= 0; k
< 8; k
++)
321 UInt16
*dest
= p
->BinSumm
[i
] + k
;
322 UInt16 val
= (UInt16
)(PPMD_BIN_SCALE
- kInitBinEsc
[k
] / (i
+ 2));
323 for (m
= 0; m
< 64; m
+= 8)
327 for (i
= 0; i
< 25; i
++)
328 for (k
= 0; k
< 16; k
++)
330 CPpmd_See
*s
= &p
->See
[i
][k
];
331 s
->Summ
= (UInt16
)((5 * i
+ 10) << (s
->Shift
= PPMD_PERIOD_BITS
- 4));
336 void Ppmd7_Init(CPpmd7
*p
, unsigned maxOrder
)
338 p
->MaxOrder
= maxOrder
;
340 p
->DummySee
.Shift
= PPMD_PERIOD_BITS
;
341 p
->DummySee
.Summ
= 0; /* unused */
342 p
->DummySee
.Count
= 64; /* unused */
345 static CTX_PTR
CreateSuccessors(CPpmd7
*p
, BoolInt skip
)
348 CTX_PTR c
= p
->MinContext
;
349 CPpmd_Byte_Ref upBranch
= (CPpmd_Byte_Ref
)SUCCESSOR(p
->FoundState
);
350 CPpmd_State
*ps
[PPMD7_MAX_ORDER
];
354 ps
[numPs
++] = p
->FoundState
;
358 CPpmd_Void_Ref successor
;
361 if (c
->NumStats
!= 1)
363 for (s
= STATS(c
); s
->Symbol
!= p
->FoundState
->Symbol
; s
++);
367 successor
= SUCCESSOR(s
);
368 if (successor
!= upBranch
)
378 upState
.Symbol
= *(const Byte
*)Ppmd7_GetPtr(p
, upBranch
);
379 SetSuccessor(&upState
, upBranch
+ 1);
381 if (c
->NumStats
== 1)
382 upState
.Freq
= ONE_STATE(c
)->Freq
;
387 for (s
= STATS(c
); s
->Symbol
!= upState
.Symbol
; s
++);
389 s0
= c
->SummFreq
- c
->NumStats
- cf
;
390 upState
.Freq
= (Byte
)(1 + ((2 * cf
<= s0
) ? (5 * cf
> s0
) : ((2 * cf
+ 3 * s0
- 1) / (2 * s0
))));
396 CTX_PTR c1
; /* = AllocContext(p); */
397 if (p
->HiUnit
!= p
->LoUnit
)
398 c1
= (CTX_PTR
)(p
->HiUnit
-= UNIT_SIZE
);
399 else if (p
->FreeList
[0] != 0)
400 c1
= (CTX_PTR
)RemoveNode(p
, 0);
403 c1
= (CTX_PTR
)AllocUnitsRare(p
, 0);
408 *ONE_STATE(c1
) = upState
;
410 SetSuccessor(ps
[--numPs
], REF(c1
));
418 static void SwapStates(CPpmd_State
*t1
, CPpmd_State
*t2
)
420 CPpmd_State tmp
= *t1
;
425 static void UpdateModel(CPpmd7
*p
)
427 CPpmd_Void_Ref successor
, fSuccessor
= SUCCESSOR(p
->FoundState
);
431 if (p
->FoundState
->Freq
< MAX_FREQ
/ 4 && p
->MinContext
->Suffix
!= 0)
433 c
= SUFFIX(p
->MinContext
);
435 if (c
->NumStats
== 1)
437 CPpmd_State
*s
= ONE_STATE(c
);
443 CPpmd_State
*s
= STATS(c
);
444 if (s
->Symbol
!= p
->FoundState
->Symbol
)
446 do { s
++; } while (s
->Symbol
!= p
->FoundState
->Symbol
);
447 if (s
[0].Freq
>= s
[-1].Freq
)
449 SwapStates(&s
[0], &s
[-1]);
453 if (s
->Freq
< MAX_FREQ
- 9)
461 if (p
->OrderFall
== 0)
463 p
->MinContext
= p
->MaxContext
= CreateSuccessors(p
, True
);
464 if (p
->MinContext
== 0)
469 SetSuccessor(p
->FoundState
, REF(p
->MinContext
));
473 *p
->Text
++ = p
->FoundState
->Symbol
;
474 successor
= REF(p
->Text
);
475 if (p
->Text
>= p
->UnitsStart
)
483 if (fSuccessor
<= successor
)
485 CTX_PTR cs
= CreateSuccessors(p
, False
);
491 fSuccessor
= REF(cs
);
493 if (--p
->OrderFall
== 0)
495 successor
= fSuccessor
;
496 p
->Text
-= (p
->MaxContext
!= p
->MinContext
);
501 SetSuccessor(p
->FoundState
, successor
);
502 fSuccessor
= REF(p
->MinContext
);
505 s0
= p
->MinContext
->SummFreq
- (ns
= p
->MinContext
->NumStats
) - (p
->FoundState
->Freq
- 1);
507 for (c
= p
->MaxContext
; c
!= p
->MinContext
; c
= SUFFIX(c
))
511 if ((ns1
= c
->NumStats
) != 1)
515 /* Expand for one UNIT */
516 unsigned oldNU
= ns1
>> 1;
517 unsigned i
= U2I(oldNU
);
518 if (i
!= U2I((size_t)oldNU
+ 1))
520 void *ptr
= AllocUnits(p
, i
+ 1);
528 MyMem12Cpy(ptr
, oldPtr
, oldNU
);
529 InsertNode(p
, oldPtr
, i
);
530 c
->Stats
= STATS_REF(ptr
);
533 c
->SummFreq
= (UInt16
)(c
->SummFreq
+ (2 * ns1
< ns
) + 2 * ((4 * ns1
<= ns
) & (c
->SummFreq
<= 8 * ns1
)));
537 CPpmd_State
*s
= (CPpmd_State
*)AllocUnits(p
, 0);
545 if (s
->Freq
< MAX_FREQ
/ 4 - 1)
548 s
->Freq
= MAX_FREQ
- 4;
549 c
->SummFreq
= (UInt16
)(s
->Freq
+ p
->InitEsc
+ (ns
> 3));
551 cf
= 2 * (UInt32
)p
->FoundState
->Freq
* (c
->SummFreq
+ 6);
552 sf
= (UInt32
)s0
+ c
->SummFreq
;
555 cf
= 1 + (cf
> sf
) + (cf
>= 4 * sf
);
560 cf
= 4 + (cf
>= 9 * sf
) + (cf
>= 12 * sf
) + (cf
>= 15 * sf
);
561 c
->SummFreq
= (UInt16
)(c
->SummFreq
+ cf
);
564 CPpmd_State
*s
= STATS(c
) + ns1
;
565 SetSuccessor(s
, successor
);
566 s
->Symbol
= p
->FoundState
->Symbol
;
568 c
->NumStats
= (UInt16
)(ns1
+ 1);
571 p
->MaxContext
= p
->MinContext
= CTX(fSuccessor
);
574 static void Rescale(CPpmd7
*p
)
576 unsigned i
, adder
, sumFreq
, escFreq
;
577 CPpmd_State
*stats
= STATS(p
->MinContext
);
578 CPpmd_State
*s
= p
->FoundState
;
580 CPpmd_State tmp
= *s
;
581 for (; s
!= stats
; s
--)
585 escFreq
= p
->MinContext
->SummFreq
- s
->Freq
;
587 adder
= (p
->OrderFall
!= 0);
588 s
->Freq
= (Byte
)((s
->Freq
+ adder
) >> 1);
591 i
= p
->MinContext
->NumStats
- 1;
594 escFreq
-= (++s
)->Freq
;
595 s
->Freq
= (Byte
)((s
->Freq
+ adder
) >> 1);
597 if (s
[0].Freq
> s
[-1].Freq
)
600 CPpmd_State tmp
= *s1
;
603 while (--s1
!= stats
&& tmp
.Freq
> s1
[-1].Freq
);
611 unsigned numStats
= p
->MinContext
->NumStats
;
613 do { i
++; } while ((--s
)->Freq
== 0);
615 p
->MinContext
->NumStats
= (UInt16
)(p
->MinContext
->NumStats
- i
);
616 if (p
->MinContext
->NumStats
== 1)
618 CPpmd_State tmp
= *stats
;
621 tmp
.Freq
= (Byte
)(tmp
.Freq
- (tmp
.Freq
>> 1));
625 InsertNode(p
, stats
, U2I(((numStats
+ 1) >> 1)));
626 *(p
->FoundState
= ONE_STATE(p
->MinContext
)) = tmp
;
629 n0
= (numStats
+ 1) >> 1;
630 n1
= (p
->MinContext
->NumStats
+ 1) >> 1;
632 p
->MinContext
->Stats
= STATS_REF(ShrinkUnits(p
, stats
, n0
, n1
));
634 p
->MinContext
->SummFreq
= (UInt16
)(sumFreq
+ escFreq
- (escFreq
>> 1));
635 p
->FoundState
= STATS(p
->MinContext
);
638 CPpmd_See
*Ppmd7_MakeEscFreq(CPpmd7
*p
, unsigned numMasked
, UInt32
*escFreq
)
641 unsigned nonMasked
= p
->MinContext
->NumStats
- numMasked
;
642 if (p
->MinContext
->NumStats
!= 256)
644 see
= p
->See
[(unsigned)p
->NS2Indx
[(size_t)nonMasked
- 1]] +
645 (nonMasked
< (unsigned)SUFFIX(p
->MinContext
)->NumStats
- p
->MinContext
->NumStats
) +
646 2 * (unsigned)(p
->MinContext
->SummFreq
< 11 * p
->MinContext
->NumStats
) +
647 4 * (unsigned)(numMasked
> nonMasked
) +
650 unsigned r
= (see
->Summ
>> see
->Shift
);
651 see
->Summ
= (UInt16
)(see
->Summ
- r
);
652 *escFreq
= r
+ (r
== 0);
663 static void NextContext(CPpmd7
*p
)
665 CTX_PTR c
= CTX(SUCCESSOR(p
->FoundState
));
666 if (p
->OrderFall
== 0 && (Byte
*)c
> p
->Text
)
667 p
->MinContext
= p
->MaxContext
= c
;
672 void Ppmd7_Update1(CPpmd7
*p
)
674 CPpmd_State
*s
= p
->FoundState
;
676 p
->MinContext
->SummFreq
+= 4;
677 if (s
[0].Freq
> s
[-1].Freq
)
679 SwapStates(&s
[0], &s
[-1]);
681 if (s
->Freq
> MAX_FREQ
)
687 void Ppmd7_Update1_0(CPpmd7
*p
)
689 p
->PrevSuccess
= (2 * p
->FoundState
->Freq
> p
->MinContext
->SummFreq
);
690 p
->RunLength
+= p
->PrevSuccess
;
691 p
->MinContext
->SummFreq
+= 4;
692 if ((p
->FoundState
->Freq
+= 4) > MAX_FREQ
)
697 void Ppmd7_UpdateBin(CPpmd7
*p
)
699 p
->FoundState
->Freq
= (Byte
)(p
->FoundState
->Freq
+ (p
->FoundState
->Freq
< 128 ? 1: 0));
705 void Ppmd7_Update2(CPpmd7
*p
)
707 p
->MinContext
->SummFreq
+= 4;
708 if ((p
->FoundState
->Freq
+= 4) > MAX_FREQ
)
710 p
->RunLength
= p
->InitRL
;