1/* Ppmd7.c -- PPMdH codec 22023-04-02 : Igor Pavlov : Public domain 3This 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 14MY_ALIGN(16) 15static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; 16MY_ALIGN(16) 17static 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 36typedef CPpmd7_Context * PPMD7_CTX_PTR; 37 38struct CPpmd7_Node_; 39 40typedef Ppmd_Ref_Type(struct CPpmd7_Node_) CPpmd7_Node_Ref; 41 42typedef 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 52void 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 84void Ppmd7_Free(CPpmd7 *p, ISzAllocPtr alloc) 85{ 86 ISzAlloc_Free(alloc, p->Base); 87 p->Size = 0; 88 p->Base = NULL; 89} 90 91 92BoolInt 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 117static 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 127static 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 137static 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 152typedef 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 162static 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 253Z7_NO_INLINE 254static 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 287static 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; 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/* 310static 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 = Ppmd7_RemoveNode(p, i1); 319 MEM_12_CPY(ptr, oldPtr, newNU) 320 Ppmd7_InsertNode(p, oldPtr, i0); 321 return ptr; 322 } 323 Ppmd7_SplitBlock(p, oldPtr, i0, i1); 324 return oldPtr; 325} 326*/ 327 328 329#define SUCCESSOR(p) Ppmd_GET_SUCCESSOR(p) 330static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) 331{ 332 Ppmd_SET_SUCCESSOR(p, v) 333} 334 335 336 337Z7_NO_INLINE 338static 339void Ppmd7_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 = (PPMD7_CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ 356 CPpmd_State *s = (CPpmd_State *)p->LoUnit; /* Ppmd7_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 const UInt16 val = (UInt16)(PPMD_BIN_SCALE - PPMD7_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 422void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder) 423{ 424 p->MaxOrder = maxOrder; 425 426 Ppmd7_RestartModel(p); 427} 428 429 430 431/* 432 Ppmd7_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 448Z7_NO_INLINE 449static PPMD7_CTX_PTR Ppmd7_CreateSuccessors(CPpmd7 *p) 450{ 451 PPMD7_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 PPMD7_CTX_PTR c1; 526 /* = AllocContext(p); */ 527 if (p->HiUnit != p->LoUnit) 528 c1 = (PPMD7_CTX_PTR)(void *)(p->HiUnit -= UNIT_SIZE); 529 else if (p->FreeList[0] != 0) 530 c1 = (PPMD7_CTX_PTR)Ppmd7_RemoveNode(p, 0); 531 else 532 { 533 c1 = (PPMD7_CTX_PTR)Ppmd7_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 SWAP_STATES(s) \ 554 { CPpmd_State tmp = s[0]; s[0] = s[-1]; s[-1] = tmp; } 555 556 557void Ppmd7_UpdateModel(CPpmd7 *p); 558Z7_NO_INLINE 559void Ppmd7_UpdateModel(CPpmd7 *p) 560{ 561 CPpmd_Void_Ref maxSuccessor, minSuccessor; 562 PPMD7_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 SWAP_STATES(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 = Ppmd7_CreateSuccessors(p); 614 if (!p->MinContext) 615 { 616 Ppmd7_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 Ppmd7_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 PPMD7_CTX_PTR cs = Ppmd7_CreateSuccessors(p); 649 if (!cs) 650 { 651 Ppmd7_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 = Ppmd7_AllocUnits(p, i + 1); 719 void *oldPtr; 720 if (!ptr) 721 { 722 Ppmd7_RestartModel(p); 723 return; 724 } 725 oldPtr = STATS(c); 726 MEM_12_CPY(ptr, oldPtr, oldNU) 727 Ppmd7_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*)Ppmd7_AllocUnits(p, 0); 743 if (!s) 744 { 745 Ppmd7_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 798Z7_NO_INLINE 799static void Ppmd7_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 Ppmd7_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 = Ppmd7_RemoveNode(p, i1); 903 p->MinContext->Union4.Stats = STATS_REF(ptr); 904 MEM_12_CPY(ptr, (const void *)stats, n1) 905 Ppmd7_InsertNode(p, stats, i0); 906 } 907 else 908 Ppmd7_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 921CPpmd_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 951static void Ppmd7_NextContext(CPpmd7 *p) 952{ 953 PPMD7_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 961void 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 SWAP_STATES(s) 971 p->FoundState = --s; 972 if (freq > MAX_FREQ) 973 Ppmd7_Rescale(p); 974 } 975 Ppmd7_NextContext(p); 976} 977 978 979void 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 Ppmd7_Rescale(p); 992 Ppmd7_NextContext(p); 993} 994 995 996/* 997void 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 Ppmd7_NextContext(p); 1004} 1005*/ 1006 1007void 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 Ppmd7_Rescale(p); 1017 Ppmd7_UpdateModel(p); 1018} 1019 1020 1021 1022/* 1023PPMd 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 1034These addresses don't cross at any time. 1035And the following condtions is true for addresses: 1036 (0 <= Text < UnitsStart <= LoUnit <= HiUnit <= Size) 1037 1038Raw text is BYTE--aligned. 1039the data in block [ UnitsStart ... Size ] contains 12-bytes aligned UNITs. 1040 1041Last UNIT of array at offset (Size - 12) is root order-0 CPpmd7_Context record. 1042The code can free UNITs memory blocks that were allocated to store CPpmd_State vectors. 1043The code doesn't free UNITs allocated for CPpmd7_Context records. 1044 1045The code calls Ppmd7_RestartModel(), when there is no free memory for allocation. 1046And Ppmd7_RestartModel() changes the state to orignal start state, with full free block. 1047 1048 1049The code allocates UNITs with the following order: 1050 1051Allocation of 1 UNIT for Context record 1052 - from free space (HiUnit) down to (LoUnit) 1053 - from FreeList[0] 1054 - Ppmd7_AllocUnitsRare() 1055 1056Ppmd7_AllocUnits() for CPpmd_State vectors: 1057 - from FreeList[i] 1058 - from free space (LoUnit) up to (HiUnit) 1059 - Ppmd7_AllocUnitsRare() 1060 1061Ppmd7_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 1069Each Record with Context contains the CPpmd_State vector, where each 1070CPpmd_State contains the link to Successor. 1071There 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 1082For 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 1088CPpmd7_Context::SummFreq = Sum(Stats[].Freq) + Escape_Freq 1089 1090The PPMd code tries to fulfill the condition: 1091 (SummFreq <= (256 * 128 = RC::kBot)) 1092 1093We have (Sum(Stats[].Freq) <= 256 * 124), because of (MAX_FREQ = 124) 1094So (4 = 128 - 124) is average reserve for Escape_Freq for each symbol. 1095If (CPpmd_State::Freq) is not aligned for 4, the reserve can be 5, 6 or 7. 1096SummFreq and Escape_Freq can be changed in Ppmd7_Rescale() and *Update*() functions. 1097Ppmd7_Rescale() can remove symbols only from max-order contexts. So Escape_Freq can increase after multiple calls of Ppmd7_Rescale() for 1098max-order context. 1099 1100When the PPMd code still break (Total <= RC::Range) condition in range coder, 1101we 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 1106#undef MAX_FREQ 1107#undef UNIT_SIZE 1108#undef U2B 1109#undef U2I 1110#undef I2U 1111#undef I2U_UInt16 1112#undef REF 1113#undef STATS_REF 1114#undef CTX 1115#undef STATS 1116#undef ONE_STATE 1117#undef SUFFIX 1118#undef NODE 1119#undef EMPTY_NODE 1120#undef MEM_12_CPY 1121#undef SUCCESSOR 1122#undef SWAP_STATES 1123