| // ShrinkDecoder.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "../../../C/Alloc.h" |
| |
| #include "../Common/InBuffer.h" |
| #include "../Common/OutBuffer.h" |
| |
| #include "BitlDecoder.h" |
| #include "ShrinkDecoder.h" |
| |
| namespace NCompress { |
| namespace NShrink { |
| |
| static const UInt32 kBufferSize = (1 << 20); |
| static const int kNumMinBits = 9; |
| |
| HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, |
| const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) |
| { |
| NBitl::CBaseDecoder<CInBuffer> inBuffer; |
| COutBuffer outBuffer; |
| |
| if (!inBuffer.Create(kBufferSize)) |
| return E_OUTOFMEMORY; |
| inBuffer.SetStream(inStream); |
| inBuffer.Init(); |
| |
| if (!outBuffer.Create(kBufferSize)) |
| return E_OUTOFMEMORY; |
| outBuffer.SetStream(outStream); |
| outBuffer.Init(); |
| |
| UInt64 prevPos = 0; |
| int numBits = kNumMinBits; |
| UInt32 head = 257; |
| bool needPrev = false; |
| UInt32 lastSymbol = 0; |
| |
| int i; |
| for (i = 0; i < kNumItems; i++) |
| _parents[i] = 0; |
| for (i = 0; i < kNumItems; i++) |
| _suffixes[i] = 0; |
| for (i = 0; i < 257; i++) |
| _isFree[i] = false; |
| for (; i < kNumItems; i++) |
| _isFree[i] = true; |
| |
| for (;;) |
| { |
| UInt32 symbol = inBuffer.ReadBits(numBits); |
| if (inBuffer.ExtraBitsWereRead()) |
| break; |
| if (_isFree[symbol]) |
| return S_FALSE; |
| if (symbol == 256) |
| { |
| UInt32 symbol = inBuffer.ReadBits(numBits); |
| if (symbol == 1) |
| { |
| if (numBits < kNumMaxBits) |
| numBits++; |
| } |
| else if (symbol == 2) |
| { |
| if (needPrev) |
| _isFree[head - 1] = true; |
| for (i = 257; i < kNumItems; i++) |
| _isParent[i] = false; |
| for (i = 257; i < kNumItems; i++) |
| if (!_isFree[i]) |
| _isParent[_parents[i]] = true; |
| for (i = 257; i < kNumItems; i++) |
| if (!_isParent[i]) |
| _isFree[i] = true; |
| head = 257; |
| while (head < kNumItems && !_isFree[head]) |
| head++; |
| if (head < kNumItems) |
| { |
| needPrev = true; |
| _isFree[head] = false; |
| _parents[head] = (UInt16)lastSymbol; |
| head++; |
| } |
| } |
| else |
| return S_FALSE; |
| continue; |
| } |
| UInt32 cur = symbol; |
| i = 0; |
| int corectionIndex = -1; |
| while (cur >= 256) |
| { |
| if (cur == head - 1) |
| corectionIndex = i; |
| _stack[i++] = _suffixes[cur]; |
| cur = _parents[cur]; |
| } |
| _stack[i++] = (Byte)cur; |
| if (needPrev) |
| { |
| _suffixes[head - 1] = (Byte)cur; |
| if (corectionIndex >= 0) |
| _stack[corectionIndex] = (Byte)cur; |
| } |
| while (i > 0) |
| outBuffer.WriteByte((_stack[--i])); |
| while (head < kNumItems && !_isFree[head]) |
| head++; |
| if (head < kNumItems) |
| { |
| needPrev = true; |
| _isFree[head] = false; |
| _parents[head] = (UInt16)symbol; |
| head++; |
| } |
| else |
| needPrev = false; |
| lastSymbol = symbol; |
| |
| UInt64 nowPos = outBuffer.GetProcessedSize(); |
| if (progress != NULL && nowPos - prevPos > (1 << 18)) |
| { |
| prevPos = nowPos; |
| UInt64 packSize = inBuffer.GetProcessedSize(); |
| RINOK(progress->SetRatioInfo(&packSize, &nowPos)); |
| } |
| } |
| return outBuffer.Flush(); |
| } |
| |
| STDMETHODIMP CDecoder ::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, |
| const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress) |
| { |
| try { return CodeReal(inStream, outStream, inSize, outSize, progress); } |
| catch(const CInBufferException &e) { return e.ErrorCode; } |
| catch(const COutBufferException &e) { return e.ErrorCode; } |
| catch(...) { return S_FALSE; } |
| } |
| |
| }} |