Files
easy7zip/CPP/7zip/Compress/XzDecoder.cpp
Tino Reichardt ef790b5209 Update to 7-Zip 17.01 Beta from Igor Pavlov
- Minor speed optimization for LZMA2 (xz and 7z) multi-threading compression.
  7-Zip now uses additional memory buffers for multi-block LZMA2 compression.
  CPU utilization was slightly improved.
- 7-zip now creates multi-block xz archives by default. Block size can be
  specified with -ms[Size]{m|g} switch.
- xz decoder now can unpack random block from multi-block xz archives.  7-Zip
  File Manager now can open nested multi-block xz archives (for example,
  image.iso.xz) without full unpacking of xz archive.
- 7-Zip now can create zip archives from stdin to stdout.
- 7-Zip command line: @listfile now doesn't work after -- switch.  Use
  -i@listfile before -- switch instead.

fixed bugs:
- 7-Zip could add unrequired alternate file streams to WIM archives, for
  commands that contain filename wildcards and -sns switch.
- 7-Zip 17.00 beta crashed for commands that write anti-item to 7z archive.
- 7-Zip 17.00 beta ignored "Use large memory pages" option.
2017-08-28 16:40:24 +02:00

264 lines
6.0 KiB
C++

// XzDecoder.cpp
#include "StdAfx.h"
#include "../../../C/Alloc.h"
#include "../Common/StreamUtils.h"
#include "../Archive/IArchive.h"
#include "XzDecoder.h"
using namespace NArchive;
namespace NCompress {
namespace NXz {
CXzUnpackerCPP::CXzUnpackerCPP(): InBuf(NULL), OutBuf(NULL)
{
XzUnpacker_Construct(&p, &g_Alloc);
}
CXzUnpackerCPP::~CXzUnpackerCPP()
{
XzUnpacker_Free(&p);
MidFree(InBuf);
MidFree(OutBuf);
}
void CStatInfo::Clear()
{
InSize = 0;
OutSize = 0;
PhySize = 0;
NumStreams = 0;
NumBlocks = 0;
UnpackSize_Defined = false;
NumStreams_Defined = false;
NumBlocks_Defined = false;
IsArc = false;
UnexpectedEnd = false;
DataAfterEnd = false;
Unsupported = false;
HeadersError = false;
DataError = false;
CrcError = false;
}
HRESULT CDecoder::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream,
const UInt64 *outSizeLimit, bool finishStream, ICompressProgressInfo *progress)
{
const size_t kInBufSize = (size_t)1 << 20;
const size_t kOutBufSize = (size_t)1 << 21;
Clear();
DecodeRes = SZ_OK;
XzUnpacker_Init(&xzu.p);
if (!xzu.InBuf)
{
xzu.InBuf = (Byte *)MidAlloc(kInBufSize);
if (!xzu.InBuf)
return E_OUTOFMEMORY;
}
if (!xzu.OutBuf)
{
xzu.OutBuf = (Byte *)MidAlloc(kOutBufSize);
if (!xzu.OutBuf)
return E_OUTOFMEMORY;
}
UInt32 inSize = 0;
UInt32 inPos = 0;
SizeT outPos = 0;
HRESULT readRes = S_OK;
for (;;)
{
if (inPos == inSize && readRes == S_OK)
{
inPos = inSize = 0;
readRes = seqInStream->Read(xzu.InBuf, kInBufSize, &inSize);
}
SizeT inLen = inSize - inPos;
SizeT outLen = kOutBufSize - outPos;
ECoderFinishMode finishMode = CODER_FINISH_ANY;
/*
// 17.01 : the code was disabled:
if (inSize == 0)
finishMode = CODER_FINISH_END;
*/
if (outSizeLimit)
{
const UInt64 rem = *outSizeLimit - OutSize;
if (outLen >= rem)
{
outLen = (SizeT)rem;
if (finishStream)
finishMode = CODER_FINISH_END;
}
}
ECoderStatus status;
const SizeT outLenRequested = outLen;
SRes res = XzUnpacker_Code(&xzu.p,
xzu.OutBuf + outPos, &outLen,
xzu.InBuf + inPos, &inLen,
finishMode, &status);
DecodeRes = res;
inPos += (UInt32)inLen;
outPos += outLen;
InSize += inLen;
OutSize += outLen;
bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK);
if (outLen >= outLenRequested || finished)
{
if (outStream && outPos != 0)
{
RINOK(WriteStream(outStream, xzu.OutBuf, outPos));
}
outPos = 0;
}
if (progress)
{
RINOK(progress->SetRatioInfo(&InSize, &OutSize));
}
if (!finished)
continue;
{
PhySize = InSize;
NumStreams = xzu.p.numStartedStreams;
if (NumStreams > 0)
IsArc = true;
NumBlocks = xzu.p.numTotalBlocks;
UnpackSize_Defined = true;
NumStreams_Defined = true;
NumBlocks_Defined = true;
UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p);
if (res == SZ_OK)
{
if (status == CODER_STATUS_NEEDS_MORE_INPUT)
{
extraSize = 0;
if (!XzUnpacker_IsStreamWasFinished(&xzu.p))
{
// finished at padding bytes, but padding is not aligned for 4
UnexpectedEnd = true;
res = SZ_ERROR_DATA;
}
}
else // status == CODER_STATUS_NOT_FINISHED
res = SZ_ERROR_DATA;
}
else if (res == SZ_ERROR_NO_ARCHIVE)
{
if (InSize == extraSize)
IsArc = false;
else
{
if (extraSize != 0 || inPos != inSize)
{
DataAfterEnd = true;
res = SZ_OK;
}
}
}
DecodeRes = res;
PhySize -= extraSize;
switch (res)
{
case SZ_OK: break;
case SZ_ERROR_NO_ARCHIVE: IsArc = false; break;
case SZ_ERROR_ARCHIVE: HeadersError = true; break;
case SZ_ERROR_UNSUPPORTED: Unsupported = true; break;
case SZ_ERROR_CRC: CrcError = true; break;
case SZ_ERROR_DATA: DataError = true; break;
default: DataError = true; break;
}
return readRes;
}
}
}
Int32 CDecoder::Get_Extract_OperationResult() const
{
Int32 opRes;
if (!IsArc)
opRes = NExtract::NOperationResult::kIsNotArc;
else if (UnexpectedEnd)
opRes = NExtract::NOperationResult::kUnexpectedEnd;
else if (DataAfterEnd)
opRes = NExtract::NOperationResult::kDataAfterEnd;
else if (CrcError)
opRes = NExtract::NOperationResult::kCRCError;
else if (Unsupported)
opRes = NExtract::NOperationResult::kUnsupportedMethod;
else if (HeadersError)
opRes = NExtract::NOperationResult::kDataError;
else if (DataError)
opRes = NExtract::NOperationResult::kDataError;
else if (DecodeRes != SZ_OK)
opRes = NExtract::NOperationResult::kDataError;
else
opRes = NExtract::NOperationResult::kOK;
return opRes;
}
HRESULT CComDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
{
RINOK(_decoder.Decode(inStream, outStream, outSize, _finishStream, progress));
Int32 opRes = _decoder.Get_Extract_OperationResult();
if (opRes == NArchive::NExtract::NOperationResult::kUnsupportedMethod)
return E_NOTIMPL;
if (opRes != NArchive::NExtract::NOperationResult::kOK)
return S_FALSE;
return S_OK;
}
STDMETHODIMP CComDecoder::SetFinishMode(UInt32 finishMode)
{
_finishStream = (finishMode != 0);
return S_OK;
}
STDMETHODIMP CComDecoder::GetInStreamProcessedSize(UInt64 *value)
{
*value = _decoder.InSize;
return S_OK;
}
}}