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