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 13namespace NCompress { 14namespace NPpmd { 15 16static const UInt32 kBufSize = (1 << 20); 17 18enum 19{ 20 kStatus_NeedInit, 21 kStatus_Normal, 22 kStatus_Finished, 23 kStatus_Error 24}; 25 26CDecoder::~CDecoder() 27{ 28 ::MidFree(_outBuf); 29 Ppmd7_Free(&_ppmd, &g_BigAlloc); 30} 31 32STDMETHODIMP 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 50HRESULT 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.p); 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 95STDMETHODIMP 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 127STDMETHODIMP 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#ifndef NO_READ_FROM_CODER 138 139STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) 140{ 141 InSeqStream = inStream; 142 _inStream.Stream = inStream; 143 return S_OK; 144} 145 146STDMETHODIMP CDecoder::ReleaseInStream() 147{ 148 InSeqStream.Release(); 149 return S_OK; 150} 151 152STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) 153{ 154 const UInt64 startPos = _processedSize; 155 HRESULT res = CodeSpec((Byte *)data, size); 156 if (processedSize) 157 *processedSize = (UInt32)(_processedSize - startPos); 158 return res; 159} 160 161#endif 162 163}} 164