├── README.md ├── c.bat ├── common.inc ├── coro3b_fake.inc ├── g.bat ├── icl64.cfg ├── libpmd ├── alloc_node.inc ├── alloc_units.inc ├── libpmd.inc ├── mod_context.inc ├── mod_cutoff.inc ├── mod_rescale.inc ├── mod_see.inc ├── model_def.inc ├── ppmd_byte.inc ├── ppmd_flush.inc ├── ppmd_init.inc ├── ppmd_proc0.inc ├── ppmd_proc1.inc ├── ppmd_proc2.inc ├── ppmd_update.inc └── sh_v1x.inc ├── pmd.cpp ├── t.bat └── timer.inc /README.md: -------------------------------------------------------------------------------- 1 | # ppmd_sh v9 by Eugene Shelwien. 2 | 3 | ppmd_sh is a statistical compressor based on Dmitry Shkarin's ppmd vJr1 - see http://compression.ru/ds/ 4 | 5 | There's a benchmark: http://web.archive.org/web/20160729232814/http://compressionratings.com/i_ppmd.html 6 | 7 | Basically ppmd is the only practical implementation of that method. 8 | There were a few others, but they were severely limited by file size and had other quirks. 9 | Also ppmd is fairly fast - its encoding is actually faster than lzma encoding, for example, 10 | and at least text compression is much better at that. 11 | But its a symmetric method, so decoding has the same speed while lzma is much faster. 12 | 13 | Unfortunately, original source didn't provide a way to work with multiple codec instances at once 14 | (lots of global variables), and had a fairly inefficient file i/o hardcoded in it, so large parts 15 | of it ended up rewritten. 16 | Unlike original, ppmd_sh codec is a normal object, and works as a coroutine - which means 17 | that coding function returns to the caller with a status code asking to replace input or 18 | output buffer, and then next call resumes the process. Thus, there's no need to write global 19 | callback functions, or compress small blocks independently. 20 | 21 | To compile: 22 | > g++ -O3 -s pmd.cpp -ILib -Ilibpmd -o pmd 23 | 24 | To use: 25 | > Compress: pmd c input_file output_file 26 | > Decompress: pmd d input_file output_file 27 | 28 | Full commandline: 29 | > pmd c/d input_file output_file [order] [memory] [reset_flag] 30 | 31 | Contacts: shelwien.san@gmail.com 32 | -------------------------------------------------------------------------------- /c.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | del *.exe 4 | 5 | set ICLCFG=icl64.cfg 6 | set icl=C:\IntelJ2190\bin-intel64\icl2d.bat 7 | 8 | call %icl% pmd.cpp 9 | 10 | del *.obj 11 | -------------------------------------------------------------------------------- /common.inc: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #undef EOF 10 | 11 | #pragma pack(1) 12 | 13 | typedef unsigned short word; 14 | typedef unsigned int uint; 15 | typedef unsigned char byte; 16 | typedef unsigned long long qword; 17 | typedef signed long long sqword; 18 | 19 | 20 | template void bzero( T &_p ) { int i; byte* p = (byte*)&_p; for( i=0; i void bzero( T (&p)[N] ) { int i; for( i=0; i void bzero( T (&p)[N] ) { 24 | char* q = (char*)&p[0]; uint n=N*sizeof(T); 25 | uint i; for( i=0; i void bzero( T* p, int N ) { int i; for( i=0; i void bzero( T (&p)[N][M] ) { int i; for( i=0; i T Min( T x, T y ) { return (x T Max( T x, T y ) { return (x>y) ? x : y; } 34 | 35 | #define macro_Min( x, y ) (((x)<(y)) ? (x) : (y)) 36 | #define macro_Max( x, y ) (((x)>(y)) ? (x) : (y)) 37 | 38 | template constexpr int DIM( T (&wr)[N] ) { return sizeof(wr)/sizeof(wr[0]); }; 39 | #define AlignUp(x,r) ((x)+((r)-1))/(r)*(r) 40 | template struct wc { 41 | static const unsigned int n=(d<<24)+(c<<16)+(b<<8)+a; 42 | static const unsigned int x=(a<<24)+(b<<16)+(c<<8)+d; 43 | }; 44 | 45 | #ifdef __GNUC__ 46 | #define INLINE __attribute__((always_inline)) 47 | #define NOINLINE __attribute__((noinline)) 48 | #define ALIGN(n) __attribute__((aligned(n))) 49 | // #define __assume_aligned(x,y) x=(byte*)__builtin_assume_aligned((void*)x,y) 50 | #define __assume_aligned(x,y) (x=decltype(x)(__builtin_assume_aligned((void*)x,y))) 51 | #define restrict __restrict 52 | #else 53 | #define INLINE __forceinline 54 | #define NOINLINE __declspec(noinline) 55 | #define ALIGN(n) __declspec(align(n)) 56 | #endif 57 | 58 | #define if_e0(x) if(__builtin_expect((x),0)) 59 | #define if_e1(x) if(__builtin_expect((x),1)) 60 | #define for_e0(x,y,z) for( (x); __builtin_expect((y),0); (z) ) 61 | #define for_e1(x,y,z) for( (x); __builtin_expect((y),1); (z) ) 62 | 63 | #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 64 | #define __builtin_expect(x,y) (x) 65 | // #define __assume_aligned(x,y) 66 | #define __assume_aligned(x,y) __assume( (((byte*)x)-((byte*)0))%(y)==0 ) 67 | #define restrict __restrict 68 | //#include "intrin.h" 69 | #ifndef COMMON_SKIP_BSF 70 | extern "C" { 71 | byte __cdecl _BitScanForward( uint* _Index, uint _Mask); 72 | byte __cdecl _BitScanForward64( uint* _Index, qword _Mask ); 73 | } 74 | #endif 75 | #endif 76 | 77 | #if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) 78 | #define __assume(x) (x) 79 | #endif 80 | 81 | #ifdef INC_FLEN 82 | static uint flen( FILE* f ) { 83 | fseek( f, 0, SEEK_END ); 84 | uint len = ftell(f); 85 | fseek( f, 0, SEEK_SET ); 86 | return len; 87 | } 88 | #endif 89 | 90 | #ifdef INC_LOG2I 91 | static uint log2i( uint x ) { 92 | #if ((defined __GNUC__) || (defined __INTEL_COMPILER)) 93 | #ifdef __GNUC__ 94 | return 31-__builtin_clz(x); 95 | #else 96 | return _bit_scan_reverse(x); 97 | #endif 98 | #else 99 | uint i; 100 | for( i=0; i<32; i++,x>>=1 ) if( x==0 ) break; 101 | return i-1; 102 | #endif 103 | } 104 | #endif 105 | 106 | #if defined(__x86_64) || defined(_M_X64) 107 | #define X64 108 | #define X64flag 1 109 | #else 110 | #undef X64 111 | #define X64flag 0 112 | #endif 113 | -------------------------------------------------------------------------------- /coro3b_fake.inc: -------------------------------------------------------------------------------- 1 | 2 | int g_getc( FILE* f, FILE* g ); 3 | void g_putc( int c, FILE* f, FILE* g ); 4 | 5 | struct Coroutine { 6 | uint f_quit; 7 | FILE* f; 8 | FILE* g; 9 | 10 | template 11 | uint coro_call( T* that ) { 12 | that->do_process(); 13 | return 0; 14 | } 15 | 16 | void chkinp( void ) { } 17 | 18 | void chkout( uint d=0 ) {} 19 | 20 | uint get( void ) { return g_getc(f,g); } 21 | void put( uint c ) { g_putc(c,f,g); } 22 | 23 | void coro_init( void ) { 24 | f_quit=0; 25 | } 26 | 27 | uint getinplen() { return 0; } 28 | uint getoutlen() { return 0; } 29 | uint getinpleft() { return 0; } 30 | uint getinpsize() { return 0; } 31 | uint getoutleft() { return 0; } 32 | uint getoutsize() { return 0; } 33 | 34 | void addinp( byte* inp,uint inplen ) {} 35 | 36 | void addout( byte* out,uint outlen ) {} 37 | 38 | void yield( void* p, int value ) {} 39 | 40 | }; 41 | -------------------------------------------------------------------------------- /g.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set incs=-DNDEBUG -DSTRICT 4 | 5 | set opts=-fstrict-aliasing -fomit-frame-pointer -ffast-math -fpermissive -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-check-new -fpermissive 6 | 7 | set gcc=C:\MinGW820x\bin\g++.exe -march=skylake 8 | set path=%gcc%\..\ 9 | 10 | del *.exe 11 | 12 | %gcc% -std=gnu++11 -O9 -s %incs% %opts% -static pmd.cpp -o pmd.exe 13 | 14 | del *.o 15 | -------------------------------------------------------------------------------- /icl64.cfg: -------------------------------------------------------------------------------- 1 | #icnewfeat.lib 2 | 3 | #/FAs 4 | #/Qopt-report9 5 | 6 | #/Qopt-prefetch:5 7 | 8 | /I..\Lib3 9 | #/I.\Lib 10 | #/I./zstd144 11 | #/I./libpmd 12 | 13 | /MP8 14 | /Qoverride-limits 15 | 16 | #/DNOASM 17 | /DNDEBUG 18 | #/DCONTEXT=void 19 | #/DPCONTEXT=void* 20 | 21 | /wd161 22 | /wd864 23 | /wd94 24 | /wd264 25 | /wd791 26 | /wd47 27 | /wd3199 28 | /wd3920 29 | 30 | /MT 31 | /arch:avx 32 | #/arch:ia32 33 | #/QxSSE2 34 | #/QxHOST 35 | #/QxSSE4.2 36 | #/arch:SSE4.1 37 | #/tune:corei7 38 | #/QxN 39 | #/arch:SSSE3 40 | #/QxSSE3 41 | 42 | #/D_X86_ 43 | 44 | #/FAs 45 | 46 | #/Og 47 | /Ob2ity 48 | /O3 /Qipo 49 | /GF /GA /GL- 50 | /Gy 51 | /GS- /GR- 52 | #/GX- 53 | /EHs-c-a- 54 | #/EHsc /Qsafeseh- 55 | /Gr 56 | #/Oa 57 | /Qvla 58 | /Qintel-extensions 59 | /Qopt-mem-layout-trans:3 60 | /Qopt-dynamic-align 61 | /Qinline-calloc 62 | /Qfnalign:16 63 | /Qcomplex_limited_range 64 | /Qunroll-aggressive 65 | /Qalias-const 66 | /Qopt-multi-version-aggressive 67 | /Qopt-subscript-in-range 68 | #/Qopt-ra-region-strategy:routine 69 | 70 | 71 | /Qlong_double 72 | /Qrestrict 73 | /Qvc14.1 74 | /Qstd=c++14 75 | 76 | /wd791 77 | /wd1913 78 | 79 | #/Qvec_report2 80 | 81 | /D_CRT_SECURE_NO_WARNINGS 82 | /D_CRT_SECURE_NO_DEPRECATE 83 | /D_CRT_DISABLE_PERFCRIT_LOCKS 84 | /D_CRT_NONSTDC_NO_DEPRECATE 85 | /D_SECURE_SCL=0 86 | /D_ITERATOR_DEBUG_LEVEL=0 87 | /D_SECURE_SCL_THROWS=0 88 | /D_HAS_ITERATOR_DEBUGGING=0 89 | #/D_HAS_EXCEPTIONS=0 90 | /DVC_EXTRALEAN 91 | #/DWIN32_LEAN_AND_MEAN 92 | #/DWIN32_EXTRA_LEAN 93 | /DSTRICT 94 | /DNDEBUG 95 | /DWIN32 96 | /D_WIN32_WINNT=0x0500 97 | #/D_WIN64 98 | #/D_AMD64_ 99 | 100 | 101 | 102 | #/Qip 103 | #/Oa 104 | #/Qip 105 | #/Qipo 106 | #/fast 107 | #/Qip-no-inlining 108 | #/Qip-no-pinlining 109 | #/Qopt-prefetch:4 110 | #/Qprof-psa-use 111 | #/Qalign 112 | #/align 113 | #/Qsfalign16 114 | #/Qfnalign 115 | #/Qnobss-init 116 | #/Qprof-src-root-cwd 117 | #/Qssp 118 | 119 | /Qprof-src-dir- 120 | #/Qprof-dir OPT 121 | #/Qprof-gen 122 | #/Qprof-use 123 | 124 | /QIfist 125 | #/Qparallel 126 | 127 | #/IC:\IntelH0721\include\icc 128 | /IC:\IntelJ2190\include\icc 129 | #/IC:\IntelH0048\include 130 | /IC:\VC2019\include 131 | /IC:\VC2019\ucrt\include 132 | /IC:\VC2019\sdk\Include 133 | 134 | #/IC:\VC71\include 135 | #/IC:\VC71\INCLUDE1 136 | 137 | /link /OPT:REF /OPT:ICF=99 /LARGEADDRESSAWARE /SAFESEH:NO /incremental:no /fixed /base:0x400000 138 | 139 | # 140 | #/link /FIXED:NO 141 | #/link /MERGE:.rdata=.data 142 | #/link /MERGE:.data1=.data 143 | 144 | #user32.lib 145 | #gdi32.lib 146 | #comctl32.lib 147 | #comdlg32.lib 148 | #advapi32.lib 149 | #shell32.lib 150 | -------------------------------------------------------------------------------- /libpmd/alloc_node.inc: -------------------------------------------------------------------------------- 1 | 2 | struct _MEM_BLK { 3 | uint Stamp; 4 | uint NextIndx; 5 | uint NU; 6 | }; 7 | 8 | 9 | struct BLK_NODE { 10 | uint Stamp; 11 | uint NextIndx; 12 | int avail() const { return (NextIndx!=0); } 13 | }; 14 | 15 | 16 | BLK_NODE* getNext( BLK_NODE* This ) { 17 | return (BLK_NODE*)Indx2Ptr(This->NextIndx); 18 | } 19 | 20 | void setNext( BLK_NODE* This, BLK_NODE* p ) { 21 | This->NextIndx = Ptr2Indx(p); 22 | } 23 | 24 | void link( BLK_NODE* This, BLK_NODE* p ) { 25 | p->NextIndx = This->NextIndx; 26 | setNext( This, p ); 27 | } 28 | 29 | void unlink( BLK_NODE* This ) { 30 | This->NextIndx = getNext(This)->NextIndx; 31 | } 32 | 33 | void* remove( BLK_NODE* This ) { 34 | BLK_NODE* p = getNext(This); 35 | unlink(This); 36 | This->Stamp--; 37 | return p; 38 | } 39 | 40 | void insert( BLK_NODE* This, void* pv, int NU ) { 41 | BLK_NODE* p = (BLK_NODE*)pv; 42 | link(This,p); 43 | p->Stamp = ~uint(0); 44 | ((_MEM_BLK&)*p).NU = NU; 45 | This->Stamp++; 46 | } 47 | 48 | 49 | struct MEM_BLK : public BLK_NODE { 50 | uint NU; 51 | }; 52 | 53 | typedef BLK_NODE* pBLK_NODE; 54 | 55 | typedef MEM_BLK* pMEM_BLK; 56 | 57 | BLK_NODE BList[N_INDEXES+1]; 58 | -------------------------------------------------------------------------------- /libpmd/alloc_units.inc: -------------------------------------------------------------------------------- 1 | 2 | uint U2B( uint NU ) { 3 | return 8*NU+4*NU; 4 | } 5 | 6 | int StartSubAllocator( uint SASize ) { 7 | uint t = SASize << 20U; 8 | HeapStart = new byte[t]; 9 | // HeapStart = mAlloc(t); 10 | // HeapStart = (byte*)VirtualAlloc( 0, t, MEM_COMMIT, PAGE_READWRITE ); 11 | if( HeapStart==NULL ) return 0; 12 | SubAllocatorSize = t; 13 | return 1; 14 | } 15 | 16 | void InitSubAllocator() { 17 | memset( BList, 0, sizeof(BList) ); 18 | HiUnit = (pText=HeapStart) + SubAllocatorSize; 19 | uint Diff = U2B(SubAllocatorSize/8/UNIT_SIZE*7); 20 | LoUnit=UnitsStart = HiUnit-Diff; 21 | GlueCount=GlueCount1=0; 22 | } 23 | 24 | uint GetUsedMemory() { 25 | int i; 26 | uint RetVal = SubAllocatorSize - (HiUnit-LoUnit) - (UnitsStart-pText); 27 | for( i=0; iNextIndx=0,i=0; i<=N_INDEXES; i++ ) { 47 | while( BList[i].avail() ) { 48 | p = (MEM_BLK*)remove(&BList[i]); 49 | if( p->NU ) { 50 | while( p1 = p + p->NU, p1->Stamp==~uint(0) ) { 51 | p->NU += p1->NU; 52 | p1->NU = 0; 53 | } 54 | link(p0,p); p0=p; 55 | } 56 | } 57 | } 58 | 59 | while( s0.avail() ) { 60 | p = (MEM_BLK*)remove(&s0); 61 | sz= p->NU; 62 | if( sz ) { 63 | for(; sz>128; sz-=128, p+=128 ) insert(&BList[N_INDEXES-1],p,128); 64 | i = Units2Indx[sz-1]; 65 | if( Indx2Units[i] != sz ) { 66 | k = sz - Indx2Units[--i]; 67 | insert( &BList[k-1], p+(sz-k) , k ); 68 | } 69 | insert( &BList[i], p, Indx2Units[i] ); 70 | } 71 | } 72 | 73 | GlueCount = 1 << (13+GlueCount1++); 74 | } 75 | 76 | void SplitBlock( void* pv, uint OldIndx, uint NewIndx ) { 77 | uint i, k, UDiff=Indx2Units[OldIndx]-Indx2Units[NewIndx]; 78 | byte* p = ((byte*)pv)+U2B(Indx2Units[NewIndx]); 79 | i = Units2Indx[UDiff-1]; 80 | if( Indx2Units[i]!=UDiff ) { 81 | k=Indx2Units[--i]; 82 | insert(&BList[i],p,k); 83 | p += U2B(k); 84 | UDiff -= k; 85 | } 86 | insert( &BList[Units2Indx[UDiff-1]], p, UDiff ); 87 | } 88 | 89 | void* AllocUnitsRare( uint indx ) { 90 | uint i = indx; 91 | do { 92 | if( ++i == N_INDEXES ) { 93 | if( !GlueCount-- ) { 94 | GlueFreeBlocks(); 95 | if( BList[i=indx].avail() ) return remove(&BList[i]); 96 | } else { 97 | i = U2B(Indx2Units[indx]); 98 | return (UnitsStart-pText>i) ? UnitsStart-=i : NULL; 99 | } 100 | } 101 | } while( !BList[i].avail() ); 102 | 103 | void* RetVal=remove(&BList[i]); 104 | SplitBlock( RetVal, i, indx ); 105 | 106 | return RetVal; 107 | } 108 | 109 | void* AllocUnits( uint NU ) { 110 | uint indx = Units2Indx[NU-1]; 111 | if( BList[indx].avail() ) return remove(&BList[indx]); 112 | void* RetVal=LoUnit; 113 | LoUnit += U2B(Indx2Units[indx]); 114 | if( LoUnit<=HiUnit ) return RetVal; 115 | LoUnit -= U2B(Indx2Units[indx]); 116 | return AllocUnitsRare(indx); 117 | } 118 | 119 | void* AllocContext() { 120 | if( HiUnit!=LoUnit ) return HiUnit-=UNIT_SIZE; 121 | return BList->avail() ? remove(BList) : AllocUnitsRare(0); 122 | } 123 | 124 | void FreeUnits( void* ptr, uint NU ) { 125 | uint indx = Units2Indx[NU-1]; 126 | insert( &BList[indx], ptr, Indx2Units[indx] ); 127 | } 128 | 129 | void FreeUnit( void* ptr ) { 130 | int i = (byte*)ptr > UnitsStart+128*1024 ? 0 : N_INDEXES; 131 | insert( &BList[i], ptr, 1 ); 132 | } 133 | 134 | //---------------------------------------- 135 | 136 | void UnitsCpy( void* Dest, void* Src, uint NU ) { 137 | memcpy( Dest, Src, 12*NU ); 138 | } 139 | 140 | void* ExpandUnits( void* OldPtr, uint OldNU ) { 141 | uint i0 = Units2Indx[OldNU-1]; 142 | uint i1 = Units2Indx[OldNU-1+1]; 143 | if( i0==i1 ) return OldPtr; 144 | void* ptr = AllocUnits(OldNU+1); 145 | if( ptr ) { 146 | UnitsCpy( ptr, OldPtr, OldNU ); 147 | insert( &BList[i0], OldPtr, OldNU ); 148 | } 149 | return ptr; 150 | } 151 | 152 | void* ShrinkUnits( void* OldPtr, uint OldNU, uint NewNU ) { 153 | uint i0 = Units2Indx[OldNU-1]; 154 | uint i1 = Units2Indx[NewNU-1]; 155 | if( i0==i1 ) return OldPtr; 156 | if( BList[i1].avail() ) { 157 | void* ptr = remove(&BList[i1]); 158 | UnitsCpy( ptr, OldPtr, NewNU ); 159 | insert( &BList[i0], OldPtr, Indx2Units[i0] ); 160 | return ptr; 161 | } else { 162 | SplitBlock(OldPtr,i0,i1); 163 | return OldPtr; 164 | } 165 | } 166 | 167 | void* MoveUnitsUp( void* OldPtr, uint NU ) { 168 | uint indx = Units2Indx[NU-1]; 169 | PrefetchData(OldPtr); 170 | if( (byte*)OldPtr > UnitsStart+128*1024 || 171 | (BLK_NODE*)OldPtr > getNext(&BList[indx]) ) return OldPtr; 172 | 173 | void* ptr = remove(&BList[indx]); 174 | UnitsCpy( ptr, OldPtr, NU ); 175 | 176 | insert( &BList[N_INDEXES], OldPtr, Indx2Units[indx] ); 177 | 178 | return ptr; 179 | } 180 | 181 | void PrepareTextArea() { 182 | AuxUnit = (byte*)AllocContext(); 183 | if( !AuxUnit ) { 184 | AuxUnit = UnitsStart; 185 | } else { 186 | if( AuxUnit==UnitsStart) AuxUnit = (UnitsStart+=UNIT_SIZE); 187 | } 188 | } 189 | 190 | void ExpandTextArea() { 191 | BLK_NODE* p; 192 | uint Count[N_INDEXES], i=0; 193 | memset( Count, 0, sizeof(Count) ); 194 | 195 | if( AuxUnit!=UnitsStart ) { 196 | if( *(uint*)AuxUnit != ~uint(0) ) 197 | UnitsStart += UNIT_SIZE; 198 | else 199 | insert( BList, AuxUnit, 1 ); 200 | } 201 | 202 | while( (p=(BLK_NODE*)UnitsStart)->Stamp == ~uint(0) ) { 203 | MEM_BLK* pm = (MEM_BLK*)p; 204 | UnitsStart = (byte*)(pm + pm->NU); 205 | Count[Units2Indx[pm->NU-1]]++; 206 | i++; 207 | pm->Stamp = 0; 208 | } 209 | 210 | if( i ) { 211 | 212 | for( p=BList+N_INDEXES; p->NextIndx; p=getNext(p) ) { 213 | while( p->NextIndx && !getNext(p)->Stamp ) { 214 | Count[Units2Indx[((MEM_BLK*)getNext(p))->NU-1]]--; 215 | unlink(p); 216 | BList[N_INDEXES].Stamp--; 217 | } 218 | if( !p->NextIndx ) break; 219 | } 220 | 221 | for( i=0; iStamp ) { 224 | unlink(p); BList[i].Stamp--; 225 | if ( !--Count[i] ) break; 226 | } 227 | } 228 | } 229 | 230 | } 231 | 232 | } 233 | 234 | -------------------------------------------------------------------------------- /libpmd/libpmd.inc: -------------------------------------------------------------------------------- 1 | 2 | //namespace { 3 | 4 | #include "model_def.inc" 5 | 6 | struct pmd_codec : Coroutine { 7 | 8 | struct DecWrap : Model<1> {}; 9 | struct EncWrap : Model<0> {}; 10 | 11 | #define Max(x,y) ((x)>(y)?(x):(y)) 12 | enum { 13 | MSize0 = sizeof(EncWrap), 14 | MSize1 = sizeof(DecWrap), 15 | MSize2 = Max( MSize0, MSize1 ) - sizeof(Coroutine) 16 | }; 17 | #undef Max 18 | 19 | ALIGN(32) 20 | byte pad[ MSize2 ]; 21 | 22 | uint f_DEC; 23 | 24 | uint ppmd_order; 25 | uint ppmd_memory; 26 | uint ppmd_restore; // flag 27 | uint ppmd_filesize; 28 | 29 | // uint Init( int MaxOrder, int CutOff, int MMAX ) { 30 | 31 | uint Init( uint _mode, uint* _args, uint f_noflush=1 ) { 32 | DecWrap& M1 = *(DecWrap*)this; 33 | EncWrap& M0 = *(EncWrap*)this; 34 | uint r=0; 35 | if( f_noflush ) coro_init(), f_quit=0; 36 | f_DEC = _mode; 37 | ppmd_order = _args[0]; 38 | ppmd_memory = _args[1]; 39 | ppmd_restore = _args[2]; 40 | ppmd_filesize = _args[3]; 41 | 42 | r = f_DEC ? M1.Init(ppmd_order,ppmd_memory,ppmd_restore,ppmd_filesize) 43 | : M0.Init(ppmd_order,ppmd_memory,ppmd_restore,ppmd_filesize); 44 | return r; 45 | } 46 | 47 | void Flush( uint _mode, uint* _args ) { 48 | Quit(); 49 | Init(_mode,_args,0); 50 | } 51 | 52 | void Quit( void ) { 53 | DecWrap& M1 = *(DecWrap*)this; 54 | EncWrap& M0 = *(EncWrap*)this; 55 | f_DEC ? M1.Quit() : M0.Quit(); 56 | } 57 | 58 | uint GetUsedMemory() { 59 | DecWrap& M1 = *(DecWrap*)this; 60 | EncWrap& M0 = *(EncWrap*)this; 61 | return f_DEC ? M1.GetUsedMemory() : M0.GetUsedMemory(); 62 | } 63 | 64 | NOINLINE 65 | void do_process( void ) { 66 | 67 | DecWrap& M1 = *(DecWrap*)this; 68 | EncWrap& M0 = *(EncWrap*)this; 69 | 70 | f_DEC ? M1.do_process() : M0.do_process(); 71 | 72 | yield(this,0); 73 | } 74 | 75 | }; 76 | 77 | 78 | //#include "pmd_api_.inc" 79 | 80 | //} 81 | 82 | 83 | -------------------------------------------------------------------------------- /libpmd/mod_context.inc: -------------------------------------------------------------------------------- 1 | 2 | enum { 3 | MAX_FREQ = 124, 4 | O_BOUND = 9 5 | }; 6 | 7 | struct PPM_CONTEXT; 8 | 9 | struct STATE { 10 | byte Symbol; 11 | byte Freq; 12 | uint iSuccessor; 13 | }; 14 | 15 | PPM_CONTEXT* getSucc( STATE* This ) { 16 | return (PPM_CONTEXT*)Indx2Ptr( This->iSuccessor ); 17 | } 18 | 19 | 20 | void SWAP( STATE& s1, STATE& s2 ) { 21 | word t1 = (word&)s1; 22 | uint t2 = s1.iSuccessor; 23 | (word&)s1 = (word&)s2; 24 | s1.iSuccessor = s2.iSuccessor; 25 | (word&)s2 = t1; 26 | s2.iSuccessor = t2; 27 | } 28 | 29 | struct PPM_CONTEXT { 30 | 31 | byte NumStats; 32 | byte Flags; 33 | word SummFreq; 34 | uint iStats; 35 | uint iSuffix; 36 | 37 | STATE& oneState() const { return (STATE&) SummFreq; } 38 | }; 39 | 40 | STATE* getStats( PPM_CONTEXT* This ) { return (STATE*)Indx2Ptr(This->iStats); } 41 | 42 | PPM_CONTEXT* suff( PPM_CONTEXT* This ) { return (PPM_CONTEXT*)Indx2Ptr(This->iSuffix); } 43 | 44 | -------------------------------------------------------------------------------- /libpmd/mod_cutoff.inc: -------------------------------------------------------------------------------- 1 | 2 | void AuxCutOff( STATE* p, int Order, int MaxOrder ) { 3 | if( OrderiSuccessor = cutOff( getSucc(p)[0], Order+1,MaxOrder); 6 | } else { 7 | p->iSuccessor=0; 8 | } 9 | } 10 | 11 | uint cutOff( PPM_CONTEXT& q, int Order, int MaxOrder ) { 12 | int i, tmp, EscFreq, Scale; 13 | STATE* p; 14 | STATE* p0; 15 | 16 | // for binary context, just cut off the successors 17 | if( q.NumStats==0 ) { 18 | 19 | int flag = 1; 20 | p = &q.oneState(); 21 | if( (byte*)getSucc(p) >= UnitsStart ) { 22 | AuxCutOff( p, Order, MaxOrder ); 23 | if( p->iSuccessor || Order>1; 33 | p0 = (STATE*)MoveUnitsUp(getStats(&q),tmp); 34 | q.iStats = Ptr2Indx(p0); 35 | 36 | // cut the branches with links to text 37 | for( i=q.NumStats, p=&p0[i]; p>=p0; p-- ) { 38 | if( (byte*)getSucc(p) < UnitsStart ) { 39 | p[0].iSuccessor=0; 40 | SWAP( p[0], p0[i--] ); 41 | } else AuxCutOff( p, Order, MaxOrder ); 42 | } 43 | 44 | // if something was cut 45 | if( i!=q.NumStats && Order>0 ) { 46 | q.NumStats = i; 47 | p = p0; 48 | if( i<0 ) { 49 | FreeUnits( p, tmp ); 50 | FreeUnit( &q ); 51 | return 0; 52 | } 53 | if( i==0 ) { 54 | q.Flags = (q.Flags & 0x10) + 0x08*(p[0].Symbol>=0x40); 55 | p[0].Freq = 1+(2*(p[0].Freq-1))/(q.SummFreq-p[0].Freq); 56 | q.oneState() = p[0]; 57 | FreeUnits( p, tmp ); 58 | } else { 59 | p = (STATE*)ShrinkUnits( p0, tmp, (i+2)>>1 ); 60 | q.iStats = Ptr2Indx(p); 61 | Scale = (q.SummFreq>16*i); // av.freq > 16 62 | q.Flags = (q.Flags & (0x10+0x04*Scale)); 63 | if( Scale ) { 64 | EscFreq = q.SummFreq; 65 | q.SummFreq = 0; 66 | for( i=0; i<=q.NumStats; i++ ) { 67 | EscFreq -= p[i].Freq; 68 | p[i].Freq = (p[i].Freq+1)>>1; 69 | q.SummFreq += p[i].Freq; 70 | q.Flags |= 0x08*(p[i].Symbol>=0x40); 71 | }; 72 | EscFreq = (EscFreq+1)>>1; 73 | q.SummFreq += EscFreq; 74 | } else { 75 | for( i=0; i<=q.NumStats; i++ ) q.Flags |= 0x08*(p[i].Symbol>=0x40); 76 | } 77 | } 78 | } 79 | 80 | } 81 | 82 | if( (byte*)&q==UnitsStart ) { 83 | // if this is a root, copy it 84 | UnitsCpy( AuxUnit, &q, 1 ); 85 | return Ptr2Indx(AuxUnit); 86 | } else { 87 | // if suffix is root, switch the pointer 88 | if( (byte*)suff(&q)==UnitsStart ) q.iSuffix=Ptr2Indx(AuxUnit); 89 | } 90 | 91 | return Ptr2Indx(&q); 92 | } 93 | -------------------------------------------------------------------------------- /libpmd/mod_rescale.inc: -------------------------------------------------------------------------------- 1 | 2 | STATE* rescale( PPM_CONTEXT& q, int OrderFall, STATE* FoundState ) { 3 | STATE tmp; STATE* p; STATE* p1; 4 | 5 | q.Flags &= 0x14; 6 | 7 | // move the current node to rank0 8 | p1 = getStats(&q); 9 | tmp = FoundState[0]; 10 | for( p=FoundState; p!=p1; p-- ) p[0]=p[-1]; 11 | p1[0] = tmp; 12 | 13 | int of = (OrderFall != 0); 14 | int a, i; 15 | int f0 = p->Freq; 16 | int sf = q.SummFreq; 17 | int EscFreq = sf-f0; 18 | q.SummFreq = p->Freq = (f0+of)>>1; 19 | 20 | // sort symbols by freqs 21 | for( i=0; iFreq; 24 | EscFreq -= a; 25 | a = (a+of)>>1; 26 | p->Freq = a; 27 | q.SummFreq += a; 28 | if( a ) q.Flags |= 0x08*(p->Symbol>=0x40); 29 | if( a > p[-1].Freq ) { 30 | tmp = p[0]; 31 | for( p1=p; tmp.Freq>p1[-1].Freq; p1-- ) p1[0]=p1[-1]; 32 | p1[0] = tmp; 33 | } 34 | } 35 | 36 | // remove the zero freq nodes 37 | if( p->Freq==0 ) { 38 | for( i=0; p->Freq==0; i++,p-- ); 39 | EscFreq += i; 40 | a = (q.NumStats+2) >> 1; 41 | if( (q.NumStats-=i)==0 ) { 42 | tmp = getStats(&q)[0]; 43 | tmp.Freq = Min( MAX_FREQ/3, (2*tmp.Freq+EscFreq-1)/EscFreq ); 44 | q.Flags &= 0x18; 45 | FreeUnits( getStats(&q), a ); 46 | q.oneState() = tmp; 47 | FoundState = &q.oneState(); 48 | return FoundState; 49 | } 50 | q.iStats = Ptr2Indx( ShrinkUnits(getStats(&q),a,(q.NumStats+2)>>1) ); 51 | } 52 | 53 | // some weird magic 54 | q.SummFreq += (EscFreq+1) >> 1; 55 | if( OrderFall || (q.Flags & 0x04)==0 ) { 56 | a = (sf-=EscFreq) - f0; 57 | a = CLAMP( uint( ( f0*q.SummFreq - sf*getStats(&q)->Freq + a-1 ) / a ), 2U, MAX_FREQ/2U-18U ); 58 | } else { 59 | a = 2; 60 | } 61 | 62 | (FoundState=getStats(&q))->Freq += a; 63 | q.SummFreq += a; 64 | q.Flags |= 0x04; 65 | 66 | return FoundState; 67 | } 68 | -------------------------------------------------------------------------------- /libpmd/mod_see.inc: -------------------------------------------------------------------------------- 1 | 2 | enum { 3 | INT_BITS = 7, 4 | PERIOD_BITS = 7, 5 | TOT_BITS = INT_BITS + PERIOD_BITS, 6 | INTERVAL = 1 << INT_BITS, 7 | BIN_SCALE = 1 << TOT_BITS, 8 | ROUND = 16 9 | }; 10 | 11 | // SEE-contexts for PPM-contexts with masked symbols 12 | struct SEE2_CONTEXT { 13 | word Summ; 14 | byte Shift; 15 | byte Count; 16 | 17 | void init( uint InitVal ) { 18 | Shift = PERIOD_BITS-4; 19 | Summ = InitVal << Shift; 20 | Count = 7; 21 | } 22 | 23 | uint getMean() { 24 | return Summ >> Shift; 25 | } 26 | 27 | void update() { 28 | if( --Count==0 ) setShift_rare(); 29 | } 30 | 31 | void setShift_rare() { 32 | uint i = Summ >> Shift; 33 | i = PERIOD_BITS - (i>40) - (i>280) - (i>1020); 34 | if( i>= 1; Shift--; } else 35 | if( i>Shift ) { Summ <<= 1; Shift++; } 36 | Count = 5 << Shift; 37 | } 38 | }; 39 | 40 | -------------------------------------------------------------------------------- /libpmd/model_def.inc: -------------------------------------------------------------------------------- 1 | 2 | //#pragma pack(1) 3 | 4 | const int ORealMAX=256; 5 | 6 | #include "sh_v1x.inc" 7 | 8 | static signed char EscCoef[12] = { 16, -10, 1, 51, 14, 89, 23, 35, 64, 26, -42, 43 }; 9 | 10 | // Tabulated escapes for exponential symbol distribution 11 | static const byte ExpEscape[16]={ 51,43,18,12,11,9,8,7,6,5,4,3,3,2,2,2 }; 12 | 13 | 14 | template< int ProcMode > 15 | struct Model: Rangecoder_SH1x { 16 | 17 | typedef Rangecoder_SH1x Base; 18 | 19 | using Base::rc_BProcess; 20 | using Base::rc_Arrange; 21 | using Base::rc_GetFreq; 22 | using Base::rc_Process; 23 | using Base::rc_Init; 24 | using Base::rc_Quit; 25 | using Base::get; 26 | using Base::put; 27 | using Base::f_quit; 28 | 29 | typedef byte* pbyte; 30 | byte* HeapStart; 31 | uint Ptr2Indx( void* p ) { return pbyte(p)-HeapStart; } 32 | void* Indx2Ptr(uint indx) { return indx + HeapStart; } 33 | 34 | enum{ 35 | UNIT_SIZE=12, 36 | N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4, 37 | N_INDEXES=N1+N2+N3+N4 38 | }; 39 | 40 | #include "alloc_node.inc" 41 | 42 | uint GlueCount; 43 | uint GlueCount1; 44 | uint SubAllocatorSize; 45 | byte* pText; 46 | byte* UnitsStart; 47 | byte* LoUnit; 48 | byte* HiUnit; 49 | byte* AuxUnit; 50 | 51 | #include "alloc_units.inc" 52 | 53 | #include "ppmd_init.inc" 54 | #include "mod_context.inc" 55 | 56 | int _MaxOrder, _CutOff, _MMAX; 57 | uint _filesize; 58 | int OrderFall; 59 | 60 | STATE* FoundState; // found next state transition 61 | PPM_CONTEXT* MaxContext; 62 | 63 | uint EscCount; 64 | uint CharMask[256]; 65 | 66 | int BSumm; 67 | int RunLength; 68 | int InitRL; 69 | 70 | #include "mod_see.inc" 71 | 72 | int NumMasked; 73 | 74 | #include "mod_rescale.inc" 75 | #include "mod_cutoff.inc" 76 | 77 | #include "ppmd_flush.inc" 78 | #include "ppmd_update.inc" 79 | 80 | #include "ppmd_proc0.inc" 81 | #include "ppmd_proc1.inc" 82 | #include "ppmd_proc2.inc" 83 | 84 | 85 | uint Init( uint MaxOrder, uint MMAX, uint CutOff, uint filesize ) { 86 | _MaxOrder = MaxOrder; 87 | _CutOff = CutOff; 88 | _MMAX = MMAX; 89 | _filesize = filesize; 90 | 91 | PPMD_STARTUP(); 92 | 93 | //f_quit=0; coro_init(); 94 | 95 | if( !StartSubAllocator( _MMAX ) ) return 1; 96 | 97 | StartModelRare(); 98 | 99 | //printf( "f_DEC=%i ord=%i mem=%i cutoff=%i\n", ProcMode, _MaxOrder, _MMAX, _CutOff ); 100 | 101 | return 0; 102 | } 103 | 104 | void Quit( void ) { 105 | StopSubAllocator(); 106 | } 107 | 108 | void do_process( void ) { 109 | uint c,i; 110 | 111 | if( ProcMode==0 ) { 112 | for( c=24; c!=-8; c-=8 ) put( byte(_filesize>>c) ); 113 | } else { 114 | for( c=0,i=24; i!=-8; i-=8 ) c |= get()<yield(this,0); 147 | } 148 | 149 | #include "ppmd_byte.inc" 150 | 151 | }; 152 | 153 | //#pragma pack() 154 | 155 | //typedef Model<0> Model0; 156 | //typedef Model<1> Model1; 157 | -------------------------------------------------------------------------------- /libpmd/ppmd_byte.inc: -------------------------------------------------------------------------------- 1 | 2 | uint ProcessByte( uint c ) { 3 | 4 | PPM_CONTEXT* MinContext = MaxContext; 5 | if( MinContext->NumStats ) { 6 | processSymbol1( MinContext[0], c ); 7 | } else { 8 | processBinSymbol( MinContext[0], c ); 9 | } 10 | 11 | while( !FoundState ) { 12 | do { 13 | // if( !MinContext->iSuffix ) { return -1; }; 14 | OrderFall++; 15 | MinContext = suff(MinContext); 16 | } while( MinContext->NumStats==NumMasked ); 17 | processSymbol2( MinContext[0], c ); 18 | } 19 | 20 | if( ProcMode ) c = FoundState->Symbol; 21 | 22 | PPM_CONTEXT* p; 23 | if( (OrderFall!=0) || ((byte*)getSucc(FoundState)iSuffix!=0; pc=suff(pc) ) OrderFall--; 14 | return; 15 | } 16 | 17 | OrderFall = _MaxOrder; 18 | 19 | InitSubAllocator(); 20 | 21 | InitRL = -( (_MaxOrder<13) ? _MaxOrder : 13 ); 22 | RunLength = InitRL; 23 | 24 | // alloc and init order0 context 25 | MaxContext = (PPM_CONTEXT*)AllocContext(); 26 | MaxContext->NumStats = 255; 27 | MaxContext->SummFreq = 255+2; 28 | MaxContext->iStats = Ptr2Indx(AllocUnits(256/2)); 29 | MaxContext->Flags = 0; 30 | MaxContext->iSuffix = 0; 31 | PrevSuccess = 0; 32 | 33 | for( i=0; i<256; i++ ) { 34 | getStats(MaxContext)[i].Symbol = i; 35 | getStats(MaxContext)[i].Freq = 1; 36 | getStats(MaxContext)[i].iSuccessor = 0; 37 | } 38 | 39 | // _InitSEE 40 | if( 1 ) { 41 | // a freq for quant? 42 | for( k=i=0; i<25; i2f[i++]=k+1 ) while( QTable[k]==i ) k++; 43 | 44 | // bin SEE init 45 | for( k=0; k<64; k++ ) { 46 | for( s=i=0; i<6; i++ ) s += EscCoef[2*i+((k>>i)&1)]; 47 | s = 128*CLAMP( s, 32, 256-32 ); 48 | for( i=0; i<25; i++ ) BinSumm[i][k] = BIN_SCALE - s/i2f[i]; 49 | } 50 | 51 | // masked SEE init 52 | for( i=0; i<23; i++ ) for( k=0; k<32; k++ ) SEE2Cont[i][k].init(8*i+5); 53 | DummySEE2Cont.init(0); 54 | } 55 | } 56 | 57 | 58 | // model flush 59 | NOINLINE 60 | void RestoreModelRare( void ) { 61 | STATE* p; 62 | pText = HeapStart; 63 | PPM_CONTEXT* pc = saved_pc; 64 | 65 | // from maxorder down, while there 2 symbols and 2nd symbol has a text pointer 66 | for(;; MaxContext=suff(MaxContext) ) { 67 | if( (MaxContext->NumStats==1) && (MaxContext!=pc) ) { 68 | p = getStats(MaxContext); 69 | if( (byte*)(getSucc(p+1))>=UnitsStart ) break; 70 | } else break; 71 | // turn a context with 2 symbols into a context with 1 symbol 72 | MaxContext->Flags = (MaxContext->Flags & 0x10) + 0x08*(p->Symbol>=0x40); 73 | p[0].Freq = (p[0].Freq+1) >> 1; 74 | MaxContext->oneState() = p[0]; 75 | MaxContext->NumStats=0; 76 | FreeUnits( p, 1 ); 77 | } 78 | 79 | // go all the way down 80 | while( MaxContext->iSuffix ) MaxContext=suff(MaxContext); 81 | 82 | AuxUnit = UnitsStart; 83 | 84 | ExpandTextArea(); 85 | 86 | // free up 25% of memory 87 | do { 88 | PrepareTextArea(); 89 | cutOff( MaxContext[0], 0, _MaxOrder ); // MaxContext is a tree root here, order0 90 | ExpandTextArea(); 91 | } while( GetUsedMemory()>3*(SubAllocatorSize>>2) ); 92 | 93 | GlueCount = GlueCount1 = 0; 94 | OrderFall = _MaxOrder; 95 | } 96 | 97 | 98 | -------------------------------------------------------------------------------- /libpmd/ppmd_init.inc: -------------------------------------------------------------------------------- 1 | 2 | static const int MAX_O=ORealMAX; // maximum allowed model order 3 | 4 | //enum { FALSE=0,TRUE=1 }; 5 | 6 | template 7 | T CLAMP( const T& X, const T& LoX, const T& HiX ) { return (X >= LoX)?((X <= HiX)?(X):(HiX)):(LoX); } 8 | 9 | template 10 | void SWAP( T& t1, T& t2 ) { T tmp=t1; t1=t2; t2=tmp; } 11 | 12 | void PrefetchData(void* Addr) { 13 | byte Prefetchbyte = *(volatile byte*)Addr; 14 | } 15 | 16 | enum { 17 | UP_FREQ = 5 18 | }; 19 | 20 | byte Indx2Units[N_INDEXES]; 21 | byte Units2Indx[128]; // constants 22 | 23 | byte NS2BSIndx[256]; 24 | byte QTable[260]; 25 | 26 | 27 | // constants initialization 28 | void PPMD_STARTUP( void ) { 29 | int i, k, m, Step; 30 | 31 | for( i=0,k=1; iNumStats] + PrevSuccess + q.Flags + ((RunLength>>26) & 0x20); 8 | word& bs = BinSumm[QTable[rs.Freq-1]][i]; 9 | BSumm = bs; 10 | bs -= (BSumm+64) >> PERIOD_BITS; 11 | 12 | int flag = ProcMode ? 0 : rs.Symbol!=symbol; 13 | rc_BProcess( BSumm+BSumm, flag ); 14 | 15 | if( flag ) { 16 | CharMask[rs.Symbol] = EscCount; 17 | NumMasked = 0; 18 | PrevSuccess = 0; 19 | FoundState = 0; 20 | } else { 21 | bs += INTERVAL; 22 | rs.Freq += (rs.Freq<196); 23 | RunLength++; 24 | PrevSuccess = 1; 25 | FoundState = &rs; 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /libpmd/ppmd_proc1.inc: -------------------------------------------------------------------------------- 1 | 2 | // encode in unmasked (maxorder) context 3 | void processSymbol1( PPM_CONTEXT& q, int symbol ) { 4 | STATE* p = getStats(&q); 5 | 6 | int cnum = q.NumStats; 7 | int i = p[0].Symbol; 8 | int low = 0; 9 | int freq = p[0].Freq; 10 | int total = q.SummFreq; 11 | int flag; 12 | // int mode; 13 | int count; 14 | 15 | rc_Arrange( total ); 16 | if( ProcMode ) { 17 | count = rc_GetFreq( total ); 18 | flag = count1*total); 27 | p[0].Freq += 4; 28 | q.SummFreq += 4; 29 | 30 | } else { 31 | 32 | PrevSuccess = 0; 33 | 34 | for( low=freq,i=1; i<=cnum; i++ ) { 35 | freq = p[i].Freq; 36 | flag = ProcMode ? low+freq>count : p[i].Symbol==symbol; 37 | if( flag ) break; 38 | low += freq; 39 | } 40 | 41 | // mode = 2+1+flag; 42 | 43 | if( flag ) { 44 | p[i].Freq += 4; 45 | q.SummFreq += 4; 46 | if( p[i].Freq > p[i-1].Freq ) SWAP( p[i], p[i-1] ), i--; 47 | p = &p[i]; 48 | } else { 49 | if( q.iSuffix ) PrefetchData( suff(&q) ); 50 | freq = total-low; 51 | NumMasked = cnum; 52 | for( i=0; i<=cnum; i++ ) CharMask[p[i].Symbol]=EscCount; 53 | p = NULL; 54 | } 55 | } 56 | 57 | rc_Process( low, freq, total ); 58 | 59 | FoundState = p; 60 | if( p && (p[0].Freq>MAX_FREQ) ) FoundState=rescale(q,OrderFall,FoundState); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /libpmd/ppmd_proc2.inc: -------------------------------------------------------------------------------- 1 | 2 | SEE2_CONTEXT SEE2Cont[23][32]; 3 | SEE2_CONTEXT DummySEE2Cont; 4 | 5 | // encode in masked context 6 | void processSymbol2( PPM_CONTEXT& q, int symbol ) { 7 | byte px[256]; 8 | STATE* p = getStats(&q); 9 | 10 | int c; 11 | int count; 12 | int low; 13 | int see_freq; 14 | int freq; 15 | int cnum = q.NumStats; 16 | 17 | SEE2_CONTEXT* psee2c; 18 | if( cnum != 0xFF ) { 19 | psee2c = SEE2Cont[ QTable[cnum+3]-4 ]; 20 | psee2c+= (q.SummFreq > 10*(cnum+1)); 21 | psee2c+= 2*(2*cnum < suff(&q)->NumStats+NumMasked) + q.Flags; 22 | see_freq = psee2c->getMean()+1; 23 | // if( see_freq==0 ) psee2c->Summ+=1, see_freq=1; 24 | } else { 25 | psee2c = &DummySEE2Cont; 26 | see_freq = 1; 27 | } 28 | 29 | int flag=0,pj,pl; 30 | 31 | int i,j; 32 | for( i=0,j=0,low=0; i<=cnum; i++ ) { 33 | c = p[i].Symbol; 34 | if( CharMask[c]!=EscCount ) { 35 | CharMask[c]=EscCount; 36 | low += p[i].Freq; 37 | if( ProcMode ) 38 | px[j++] = i; 39 | else 40 | if( c==symbol ) flag=1,j=i,pl=low; 41 | } 42 | } 43 | 44 | int Total = see_freq + low; 45 | 46 | rc_Arrange( Total ); 47 | if( ProcMode ) { 48 | count = rc_GetFreq( Total ); 49 | flag = count2 ) psee2c->Summ -= see_freq; 63 | psee2c->update(); 64 | 65 | FoundState = p; 66 | p[0].Freq += 4; 67 | q.SummFreq += 4; if( p[0].Freq > MAX_FREQ ) FoundState=rescale(q,OrderFall,FoundState); 68 | RunLength = InitRL; 69 | EscCount++; 70 | 71 | } else { 72 | 73 | low = Total; 74 | freq = see_freq; 75 | 76 | NumMasked = cnum; 77 | psee2c->Summ += Total-see_freq; 78 | 79 | } 80 | 81 | rc_Process( low-freq, freq, Total ); 82 | 83 | } 84 | -------------------------------------------------------------------------------- /libpmd/ppmd_update.inc: -------------------------------------------------------------------------------- 1 | 2 | PPM_CONTEXT* saved_pc; 3 | 4 | PPM_CONTEXT* UpdateModel( PPM_CONTEXT* MinContext ) { 5 | byte Flag, sym, FSymbol; 6 | uint ns1, ns, cf, sf, s0, FFreq; 7 | uint iSuccessor, iFSuccessor; 8 | PPM_CONTEXT* pc; 9 | STATE* p = NULL; 10 | 11 | FSymbol = FoundState->Symbol; 12 | FFreq = FoundState->Freq; 13 | iFSuccessor = FoundState->iSuccessor; 14 | 15 | // partial update for the suffix context 16 | if( MinContext->iSuffix ) { 17 | pc = suff(MinContext); 18 | // is it binary? 19 | if( pc[0].NumStats ) { 20 | p = getStats(pc); 21 | if( p[0].Symbol!=FSymbol ) { 22 | for( p++; p[0].Symbol!=FSymbol; p++ ); 23 | if( p[0].Freq >= p[-1].Freq ) SWAP( p[0], p[-1] ), p--; 24 | } 25 | if( p[0].FreqiSuccessor = CreateSuccessors( 1, p, MinContext ); 39 | if( !FoundState->iSuccessor ) { saved_pc=pc; return 0; }; 40 | MaxContext = getSucc(FoundState); 41 | return MaxContext; 42 | } 43 | 44 | *pText++ = FSymbol; 45 | iSuccessor = Ptr2Indx(pText); 46 | if( pText>=UnitsStart ) { saved_pc=pc; return 0; }; 47 | 48 | if( iFSuccessor ) { 49 | if( (byte*)Indx2Ptr(iFSuccessor) < UnitsStart ) 50 | iFSuccessor = CreateSuccessors( 0, p, MinContext ); 51 | else 52 | PrefetchData( Indx2Ptr(iFSuccessor) ); 53 | } else 54 | iFSuccessor = ReduceOrder( p, MinContext ); 55 | 56 | if( !iFSuccessor ) { saved_pc=pc; return 0; }; 57 | 58 | if( !--OrderFall ) { 59 | iSuccessor = iFSuccessor; 60 | pText -= (MaxContext!=MinContext); 61 | } 62 | 63 | s0 = MinContext->SummFreq - FFreq; 64 | ns = MinContext->NumStats; 65 | Flag = 0x08*(FSymbol>=0x40); 66 | for( pc=MaxContext; pc!=MinContext; pc=suff(pc) ) { 67 | ns1 = pc[0].NumStats; 68 | // non-binary context? 69 | if( ns1 ) { 70 | // realloc table with alphabet size is odd 71 | if( ns1&1 ) { 72 | p = (STATE*)ExpandUnits( getStats(pc),(ns1+1)>>1 ); 73 | if( !p ) { saved_pc=pc; return 0; }; 74 | pc[0].iStats = Ptr2Indx(p); 75 | } 76 | // increase escape freq (more for larger alphabet) 77 | pc[0].SummFreq += QTable[ns+4] >> 3; 78 | } else { 79 | // escaped binary context 80 | p = (STATE*)AllocUnits(1); 81 | if( !p ) { saved_pc=pc; return 0; }; 82 | p[0] = pc[0].oneState(); 83 | pc[0].iStats = Ptr2Indx(p); 84 | p[0].Freq = (p[0].Freq<=MAX_FREQ/3) ? (2*p[0].Freq-1) : (MAX_FREQ-15); 85 | // update escape 86 | pc[0].SummFreq = p[0].Freq + (ns>1) + ExpEscape[QTable[BSumm>>8]]; //-V602 87 | } 88 | 89 | // inheritance 90 | cf = (FFreq-1)*(5 + pc[0].SummFreq); 91 | sf = s0 + pc[0].SummFreq; 92 | // this is a weighted rescaling of symbol's freq into a new context (cf/sf) 93 | if( cf<=3*sf ) { 94 | // if the new freq is too small the we increase the escape freq too 95 | cf = 1 + (2*cf>sf) + (2*cf>3*sf); 96 | pc[0].SummFreq += 4; 97 | } else { 98 | cf = 5 + (cf>5*sf) + (cf>6*sf) + (cf>8*sf) + (cf>10*sf) + (cf>12*sf); 99 | pc[0].SummFreq += cf; 100 | } 101 | 102 | p = getStats(pc) + (++pc[0].NumStats); 103 | p[0].iSuccessor = iSuccessor; 104 | p[0].Symbol = FSymbol; 105 | p[0].Freq = cf; 106 | pc[0].Flags |= Flag; // flag if last added symbol was >=0x40 107 | } 108 | 109 | MaxContext = (PPM_CONTEXT*)Indx2Ptr(iFSuccessor); 110 | return MaxContext; 111 | } 112 | 113 | 114 | uint CreateSuccessors( uint Skip, STATE* p, PPM_CONTEXT* pc ) { 115 | byte tmp; 116 | uint cf, s0; 117 | STATE* ps[MAX_O]; 118 | STATE** pps=ps; 119 | 120 | byte sym = FoundState->Symbol; 121 | uint iUpBranch = FoundState->iSuccessor; 122 | 123 | if( !Skip ) { 124 | *pps++ = FoundState; 125 | if( !pc[0].iSuffix ) goto NO_LOOP; 126 | } 127 | 128 | if( p ) { pc = suff(pc); goto LOOP_ENTRY; } 129 | 130 | do { 131 | pc = suff(pc); 132 | 133 | // increment current symbol's freq in lower order contexts 134 | // more partial updates? 135 | if( pc[0].NumStats ) { 136 | // find sym node 137 | for( p=getStats(pc); p[0].Symbol!=sym; p++ ); 138 | // increment freq if limit allows 139 | tmp = 2*(p[0].FreqNumStats & (p[0].Freq<16)); 146 | } 147 | 148 | LOOP_ENTRY: 149 | if( p[0].iSuccessor!=iUpBranch ) { 150 | pc = getSucc(p); 151 | break; 152 | } 153 | *pps++ = p; 154 | } while ( pc[0].iSuffix ); 155 | 156 | NO_LOOP: 157 | if( pps==ps ) return Ptr2Indx(pc); 158 | 159 | // fetch a following symbol from the text buffer 160 | PPM_CONTEXT ct; 161 | ct.NumStats = 0; 162 | ct.Flags = 0x10*(sym>=0x40); 163 | sym = *(byte*)Indx2Ptr(iUpBranch); 164 | ct.oneState().iSuccessor = Ptr2Indx((byte*)Indx2Ptr(iUpBranch)+1); 165 | ct.oneState().Symbol = sym; 166 | ct.Flags |= 0x08*(sym>=0x40); 167 | 168 | // pc is MinContext, the context used for encoding 169 | if( pc[0].NumStats ) { 170 | for( p=getStats(pc); p[0].Symbol!=sym; p++ ); 171 | cf = p[0].Freq - 1; 172 | s0 = pc[0].SummFreq - pc[0].NumStats - cf; 173 | cf = 1 + ((2*cfs0) : 2+cf/s0); 174 | ct.oneState().Freq = Min( 7, cf ); 175 | } else { 176 | ct.oneState().Freq = pc[0].oneState().Freq; 177 | } 178 | 179 | // attach the new node to all orders 180 | do { 181 | PPM_CONTEXT* pc1 = (PPM_CONTEXT*)AllocContext(); 182 | if( !pc1 ) return 0; 183 | ((uint*)pc1)[0] = ((uint*)&ct)[0]; 184 | ((uint*)pc1)[1] = ((uint*)&ct)[1]; 185 | pc1->iSuffix = Ptr2Indx(pc); 186 | pc = pc1; pps--; 187 | pps[0][0].iSuccessor = Ptr2Indx(pc); 188 | } while( pps!=ps ); 189 | 190 | return Ptr2Indx(pc); 191 | } 192 | 193 | 194 | uint ReduceOrder( STATE* p, PPM_CONTEXT* pc ) { 195 | byte tmp; 196 | STATE* p1; 197 | PPM_CONTEXT* pc1=pc; 198 | FoundState->iSuccessor = Ptr2Indx(pText); 199 | byte sym = FoundState->Symbol; 200 | uint iUpBranch = FoundState->iSuccessor; 201 | OrderFall++; 202 | 203 | if( p ) { pc=suff(pc); goto LOOP_ENTRY; } 204 | 205 | while(1) { 206 | if( !pc->iSuffix ) return Ptr2Indx(pc); 207 | pc = suff(pc); 208 | 209 | if( pc->NumStats ) { 210 | for( p=getStats(pc); p[0].Symbol!=sym; p++ ); 211 | tmp = 2*(p->FreqFreq += tmp; 213 | pc->SummFreq += tmp; 214 | } else { 215 | p = &(pc->oneState()); 216 | p->Freq += (p->Freq<11); 217 | } 218 | 219 | LOOP_ENTRY: 220 | if( p->iSuccessor ) break; 221 | p->iSuccessor = iUpBranch; 222 | OrderFall++; 223 | } 224 | 225 | if( p->iSuccessor<=iUpBranch ) { 226 | p1 = FoundState; 227 | FoundState = p; 228 | p->iSuccessor = CreateSuccessors(0,0,pc); 229 | FoundState = p1; 230 | } 231 | 232 | if( OrderFall==1 && pc1==MaxContext ) { 233 | FoundState->iSuccessor = p->iSuccessor; 234 | pText--; 235 | } 236 | 237 | return p->iSuccessor; 238 | } 239 | -------------------------------------------------------------------------------- /libpmd/sh_v1x.inc: -------------------------------------------------------------------------------- 1 | 2 | template< int ProcMode > 3 | struct Rangecoder_SH1x : Coroutine { 4 | 5 | enum { 6 | SCALElog = 15, 7 | SCALE = 1<>32; 63 | // uint rnew = ( range>=16*sTOP ) ? freq*(range>>SCALElog) : freq*(range>>(SCALElog-4))>>4; 64 | uint rnew = (range>>SCALElog)*freq; 65 | 66 | // if( ProcMode ) bit = 1 + uint((qword(code)-rnew)>>32); 67 | if( ProcMode ) bit = (code>=rnew); 68 | 69 | range = ((range-rnew-rnew)&(-bit)) + rnew; 70 | rnew &= -bit; 71 | 72 | if( ProcMode ) code -= rnew; else lowc += rnew; 73 | 74 | Renorm(); 75 | } 76 | 77 | void ShiftLow( void ) { 78 | if( low>24; 82 | Carry = 0; 83 | } else FFNum++; 84 | low<<=8; 85 | } 86 | 87 | void ShiftLow2( void ) { 88 | if( low>24; 92 | Carry = 0; 93 | } else FFNum++; 94 | low &= sTOP-1; 95 | if( low>16; 99 | } else FFNum++; 100 | low<<=16; 101 | } 102 | 103 | void rcInit( void ) { 104 | range = 0xFFFFFFFF; 105 | low = 0; 106 | FFNum = 0; 107 | Carry = 0; 108 | Cache = 0; 109 | } 110 | 111 | void rc_Init( void ) { 112 | rcInit(); 113 | if( ProcMode==1 ) { 114 | for(int _=0; _0 ? 8*gn/fn : 8; 19 | double tm = curtick-starttick; tm/=1000; 20 | float sp = tm>0 ? fn/tm/1024 : 0; 21 | if( Mode ) { double t=gn; gn=fn; fn=t; } // SWAP( fn, gn ); 22 | fprintf(stderr, " %.0lf > %.0lf, %4.2f bpb, %6.1fM RAM, %4.0fKb/s\r", fn,gn, bpb, ram/(1<<20), sp ); 23 | // fflush(stdout); 24 | } 25 | 26 | #include "coro3b_fake.inc" 27 | #include "libpmd/libpmd.inc" 28 | 29 | ALIGN(4096) pmd_codec C; 30 | 31 | uint f_DEC = 0; 32 | 33 | int g_getc( FILE* f, FILE* g ) { 34 | static int g_block = 1000000; 35 | if_e0( --g_block<0 ) { CheckTimer(0); g_block=1000000; PrintInfo( f, g, C.GetUsedMemory(), f_DEC ); } 36 | return getc(f); 37 | } 38 | 39 | void g_putc( int c, FILE* f, FILE* g ) { 40 | putc( c, g ); 41 | } 42 | 43 | int main( int argc, char** argv ) { 44 | 45 | if( argc<4 ) return 1; 46 | 47 | FILE* f = fopen(argv[2],"rb"); if( f==0 ) return 2; 48 | FILE* g = fopen(argv[3],"wb"); if( g==0 ) return 3; 49 | 50 | f_DEC = (argv[1][0]=='d'); 51 | 52 | { 53 | int i; 54 | for( i=0; i(argc-4,DIM(pmd_args1)); i++ ) { 55 | char* p = argv[4+i]; p += (p[0]=='-'); 56 | pmd_args1[i]=atoi(p); 57 | } 58 | } 59 | 60 | pmd_args1[3] = (f_DEC==0) ? flen(f) : -1; 61 | 62 | if( C.Init(f_DEC,pmd_args1) ) return 5; 63 | 64 | C.f = f; 65 | C.g = g; 66 | 67 | C.addout( outbuf, outbufsize ); 68 | 69 | StartTimer(); 70 | 71 | while(1) { 72 | if( CheckTimer(500) ) PrintInfo( f, g, C.GetUsedMemory(), f_DEC ); 73 | 74 | uint l,r = C.coro_call(&C); //-V678 75 | if( r==1 ) { 76 | l = fread( inpbuf, 1, inpbufsize, f ); 77 | C.addinp( inpbuf, l ); 78 | if( l==0 ) C.f_quit=1; 79 | } else { 80 | l = C.getoutsize(); //C.outptr-C.outbeg; //C.getoutlen(); 81 | fwrite( outbuf, 1, l, g ); 82 | C.addout( outbuf, outbufsize ); 83 | if( r!=2 ) break; 84 | } 85 | } 86 | 87 | BreakTimer(); 88 | PrintInfo( f, g, C.GetUsedMemory(), f_DEC ); 89 | printf( "\nProcessing time: " ); PrintTimer(); 90 | printf( "\n" ); 91 | 92 | C.Quit(); 93 | 94 | fclose(f); 95 | fclose(g); 96 | 97 | return 0; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /t.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | del 1 2 1w 2w 4 | 5 | pmd.exe c ..\book1 1 6 | pmd.exe d 1 2 7 | 8 | for %%a in (1,2) do ( 9 | echo %%a - %%~za 10 | ) 11 | 12 | md5sum ../book1 2 13 | 14 | del 1 2 1w 2w 15 | -------------------------------------------------------------------------------- /timer.inc: -------------------------------------------------------------------------------- 1 | 2 | //extern "C" __declspec(dllimport) void __stdcall Sleep( uint ); 3 | 4 | //extern "C" __declspec(dllimport) uint __stdcall GetTickCount( void ); 5 | 6 | #ifdef __GNUC__ 7 | 8 | #include 9 | 10 | #include 11 | int GetTickCount(void) { 12 | timeval t; 13 | gettimeofday( &t, 0 ); 14 | return t.tv_sec*1000 + t.tv_usec/1000; 15 | } 16 | 17 | #else 18 | 19 | //#ifndef _INC_WINDOWS 20 | #ifndef _WINDOWS_ 21 | extern "C" __declspec(dllimport) unsigned __stdcall GetTickCount( void ); 22 | #endif 23 | //#endif 24 | 25 | #endif 26 | 27 | uint starttick,lasttick,curtick,fintick; 28 | 29 | #define StartTimer() (starttick=lasttick=GetTickCount()) 30 | //#define StartTimer(xxx) (starttick=GetTickCount(),lasttick=starttick+xxx) 31 | 32 | #define CheckTimer(xxx) (curtick=GetTickCount(),(lasttick<=curtick)?lasttick=curtick+(xxx),1:0) 33 | 34 | #define BreakTimer() (fintick=GetTickCount()-starttick) 35 | 36 | #define PrintTimer() (printf("%i.%03is",fintick/1000,fintick%1000)) 37 | --------------------------------------------------------------------------------