1 // PpmdDecoder.cpp 2 // 2009-03-11 : Igor Pavlov : Public domain 3 4 #include "StdAfx.h" 5 6 #include "../../../C/Alloc.h" 7 #include "../../../C/CpuArch.h" 8 9 #include "../Common/StreamUtils.h" 10 11 #include "PpmdDecoder.h" 12 13 namespace NCompress { 14 namespace NPpmd { 15 16 static const UInt32 kBufSize = (1 << 20); 17 18 enum 19 { 20 kStatus_NeedInit, 21 kStatus_Normal, 22 kStatus_Finished, 23 kStatus_Error 24 }; 25 26 static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } 27 static void SzBigFree(void *, void *address) { BigFree(address); } 28 static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; 29 30 CDecoder::~CDecoder() 31 { 32 ::MidFree(_outBuf); 33 Ppmd7_Free(&_ppmd, &g_BigAlloc); 34 } 35 36 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *props, UInt32 size) 37 { 38 if (size < 5) 39 return E_INVALIDARG; 40 _order = props[0]; 41 UInt32 memSize = GetUi32(props + 1); 42 if (_order < PPMD7_MIN_ORDER || 43 _order > PPMD7_MAX_ORDER || 44 memSize < PPMD7_MIN_MEM_SIZE || 45 memSize > PPMD7_MAX_MEM_SIZE) 46 return E_NOTIMPL; 47 if (!_inStream.Alloc(1 << 20)) 48 return E_OUTOFMEMORY; 49 if (!Ppmd7_Alloc(&_ppmd, memSize, &g_BigAlloc)) 50 return E_OUTOFMEMORY; 51 return S_OK; 52 } 53 54 HRESULT CDecoder::CodeSpec(Byte *memStream, UInt32 size) 55 { 56 switch(_status) 57 { 58 case kStatus_Finished: return S_OK; 59 case kStatus_Error: return S_FALSE; 60 case kStatus_NeedInit: 61 _inStream.Init(); 62 if (!Ppmd7z_RangeDec_Init(&_rangeDec)) 63 { 64 _status = kStatus_Error; 65 return S_FALSE; 66 } 67 _status = kStatus_Normal; 68 Ppmd7_Init(&_ppmd, _order); 69 break; 70 } 71 if (_outSizeDefined) 72 { 73 const UInt64 rem = _outSize - _processedSize; 74 if (size > rem) 75 size = (UInt32)rem; 76 } 77 78 UInt32 i; 79 int sym = 0; 80 for (i = 0; i != size; i++) 81 { 82 sym = Ppmd7_DecodeSymbol(&_ppmd, &_rangeDec.p); 83 if (_inStream.Extra || sym < 0) 84 break; 85 memStream[i] = (Byte)sym; 86 } 87 88 _processedSize += i; 89 if (_inStream.Extra) 90 { 91 _status = kStatus_Error; 92 return _inStream.Res; 93 } 94 if (sym < 0) 95 _status = (sym < -1) ? kStatus_Error : kStatus_Finished; 96 return S_OK; 97 } 98 99 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, 100 const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) 101 { 102 if (!_outBuf) 103 { 104 _outBuf = (Byte *)::MidAlloc(kBufSize); 105 if (!_outBuf) 106 return E_OUTOFMEMORY; 107 } 108 109 _inStream.Stream = inStream; 110 SetOutStreamSize(outSize); 111 112 do 113 { 114 const UInt64 startPos = _processedSize; 115 HRESULT res = CodeSpec(_outBuf, kBufSize); 116 size_t processed = (size_t)(_processedSize - startPos); 117 RINOK(WriteStream(outStream, _outBuf, processed)); 118 RINOK(res); 119 if (_status == kStatus_Finished) 120 break; 121 if (progress) 122 { 123 UInt64 inSize = _inStream.GetProcessed(); 124 RINOK(progress->SetRatioInfo(&inSize, &_processedSize)); 125 } 126 } 127 while (!_outSizeDefined || _processedSize < _outSize); 128 return S_OK; 129 } 130 131 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) 132 { 133 _outSizeDefined = (outSize != NULL); 134 if (_outSizeDefined) 135 _outSize = *outSize; 136 _processedSize = 0; 137 _status = kStatus_NeedInit; 138 return S_OK; 139 } 140 141 #ifndef NO_READ_FROM_CODER 142 143 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) 144 { 145 InSeqStream = inStream; 146 _inStream.Stream = inStream; 147 return S_OK; 148 } 149 150 STDMETHODIMP CDecoder::ReleaseInStream() 151 { 152 InSeqStream.Release(); 153 return S_OK; 154 } 155 156 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) 157 { 158 const UInt64 startPos = _processedSize; 159 HRESULT res = CodeSpec((Byte *)data, size); 160 if (processedSize) 161 *processedSize = (UInt32)(_processedSize - startPos); 162 return res; 163 } 164 165 #endif 166 167 }} 168