/* XzDec.c -- Xz Decode | |
2018-12-29 : Igor Pavlov : Public domain */ | |
#include "Precomp.h" | |
// #include <stdio.h> | |
// #define XZ_DUMP | |
/* #define XZ_DUMP */ | |
#ifdef XZ_DUMP | |
#include <stdio.h> | |
#endif | |
// #define SHOW_DEBUG_INFO | |
#ifdef SHOW_DEBUG_INFO | |
#include <stdio.h> | |
#endif | |
#ifdef SHOW_DEBUG_INFO | |
#define PRF(x) x | |
#else | |
#define PRF(x) | |
#endif | |
#define PRF_STR(s) PRF(printf("\n" s "\n")) | |
#define PRF_STR_INT(s, d) PRF(printf("\n" s " %d\n", (unsigned)d)) | |
#include <stdlib.h> | |
#include <string.h> | |
#include "7zCrc.h" | |
#include "Alloc.h" | |
#include "Bra.h" | |
#include "CpuArch.h" | |
#include "Delta.h" | |
#include "Lzma2Dec.h" | |
// #define USE_SUBBLOCK | |
#ifdef USE_SUBBLOCK | |
#include "Bcj3Dec.c" | |
#include "SbDec.h" | |
#endif | |
#include "Xz.h" | |
#define XZ_CHECK_SIZE_MAX 64 | |
#define CODER_BUF_SIZE ((size_t)1 << 17) | |
unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value) | |
{ | |
unsigned i, limit; | |
*value = 0; | |
limit = (maxSize > 9) ? 9 : (unsigned)maxSize; | |
for (i = 0; i < limit;) | |
{ | |
Byte b = p[i]; | |
*value |= (UInt64)(b & 0x7F) << (7 * i++); | |
if ((b & 0x80) == 0) | |
return (b == 0 && i != 1) ? 0 : i; | |
} | |
return 0; | |
} | |
/* ---------- BraState ---------- */ | |
#define BRA_BUF_SIZE (1 << 14) | |
typedef struct | |
{ | |
size_t bufPos; | |
size_t bufConv; | |
size_t bufTotal; | |
int encodeMode; | |
UInt32 methodId; | |
UInt32 delta; | |
UInt32 ip; | |
UInt32 x86State; | |
Byte deltaState[DELTA_STATE_SIZE]; | |
Byte buf[BRA_BUF_SIZE]; | |
} CBraState; | |
static void BraState_Free(void *pp, ISzAllocPtr alloc) | |
{ | |
ISzAlloc_Free(alloc, pp); | |
} | |
static SRes BraState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAllocPtr alloc) | |
{ | |
CBraState *p = ((CBraState *)pp); | |
UNUSED_VAR(alloc); | |
p->ip = 0; | |
if (p->methodId == XZ_ID_Delta) | |
{ | |
if (propSize != 1) | |
return SZ_ERROR_UNSUPPORTED; | |
p->delta = (unsigned)props[0] + 1; | |
} | |
else | |
{ | |
if (propSize == 4) | |
{ | |
UInt32 v = GetUi32(props); | |
switch (p->methodId) | |
{ | |
case XZ_ID_PPC: | |
case XZ_ID_ARM: | |
case XZ_ID_SPARC: | |
if ((v & 3) != 0) | |
return SZ_ERROR_UNSUPPORTED; | |
break; | |
case XZ_ID_ARMT: | |
if ((v & 1) != 0) | |
return SZ_ERROR_UNSUPPORTED; | |
break; | |
case XZ_ID_IA64: | |
if ((v & 0xF) != 0) | |
return SZ_ERROR_UNSUPPORTED; | |
break; | |
} | |
p->ip = v; | |
} | |
else if (propSize != 0) | |
return SZ_ERROR_UNSUPPORTED; | |
} | |
return SZ_OK; | |
} | |
static void BraState_Init(void *pp) | |
{ | |
CBraState *p = ((CBraState *)pp); | |
p->bufPos = p->bufConv = p->bufTotal = 0; | |
x86_Convert_Init(p->x86State); | |
if (p->methodId == XZ_ID_Delta) | |
Delta_Init(p->deltaState); | |
} | |
#define CASE_BRA_CONV(isa) case XZ_ID_ ## isa: size = isa ## _Convert(data, size, p->ip, p->encodeMode); break; | |
static SizeT BraState_Filter(void *pp, Byte *data, SizeT size) | |
{ | |
CBraState *p = ((CBraState *)pp); | |
switch (p->methodId) | |
{ | |
case XZ_ID_Delta: | |
if (p->encodeMode) | |
Delta_Encode(p->deltaState, p->delta, data, size); | |
else | |
Delta_Decode(p->deltaState, p->delta, data, size); | |
break; | |
case XZ_ID_X86: | |
size = x86_Convert(data, size, p->ip, &p->x86State, p->encodeMode); | |
break; | |
CASE_BRA_CONV(PPC) | |
CASE_BRA_CONV(IA64) | |
CASE_BRA_CONV(ARM) | |
CASE_BRA_CONV(ARMT) | |
CASE_BRA_CONV(SPARC) | |
} | |
p->ip += (UInt32)size; | |
return size; | |
} | |
static SRes BraState_Code2(void *pp, | |
Byte *dest, SizeT *destLen, | |
const Byte *src, SizeT *srcLen, int srcWasFinished, | |
ECoderFinishMode finishMode, | |
// int *wasFinished | |
ECoderStatus *status) | |
{ | |
CBraState *p = ((CBraState *)pp); | |
SizeT destRem = *destLen; | |
SizeT srcRem = *srcLen; | |
UNUSED_VAR(finishMode); | |
*destLen = 0; | |
*srcLen = 0; | |
// *wasFinished = False; | |
*status = CODER_STATUS_NOT_FINISHED; | |
while (destRem > 0) | |
{ | |
if (p->bufPos != p->bufConv) | |
{ | |
size_t size = p->bufConv - p->bufPos; | |
if (size > destRem) | |
size = destRem; | |
memcpy(dest, p->buf + p->bufPos, size); | |
p->bufPos += size; | |
*destLen += size; | |
dest += size; | |
destRem -= size; | |
continue; | |
} | |
p->bufTotal -= p->bufPos; | |
memmove(p->buf, p->buf + p->bufPos, p->bufTotal); | |
p->bufPos = 0; | |
p->bufConv = 0; | |
{ | |
size_t size = BRA_BUF_SIZE - p->bufTotal; | |
if (size > srcRem) | |
size = srcRem; | |
memcpy(p->buf + p->bufTotal, src, size); | |
*srcLen += size; | |
src += size; | |
srcRem -= size; | |
p->bufTotal += size; | |
} | |
if (p->bufTotal == 0) | |
break; | |
p->bufConv = BraState_Filter(pp, p->buf, p->bufTotal); | |
if (p->bufConv == 0) | |
{ | |
if (!srcWasFinished) | |
break; | |
p->bufConv = p->bufTotal; | |
} | |
} | |
if (p->bufTotal == p->bufPos && srcRem == 0 && srcWasFinished) | |
{ | |
*status = CODER_STATUS_FINISHED_WITH_MARK; | |
// *wasFinished = 1; | |
} | |
return SZ_OK; | |
} | |
SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAllocPtr alloc) | |
{ | |
CBraState *decoder; | |
if (id < XZ_ID_Delta || id > XZ_ID_SPARC) | |
return SZ_ERROR_UNSUPPORTED; | |
decoder = p->p; | |
if (!decoder) | |
{ | |
decoder = (CBraState *)ISzAlloc_Alloc(alloc, sizeof(CBraState)); | |
if (!decoder) | |
return SZ_ERROR_MEM; | |
p->p = decoder; | |
p->Free = BraState_Free; | |
p->SetProps = BraState_SetProps; | |
p->Init = BraState_Init; | |
p->Code2 = BraState_Code2; | |
p->Filter = BraState_Filter; | |
} | |
decoder->methodId = (UInt32)id; | |
decoder->encodeMode = encodeMode; | |
return SZ_OK; | |
} | |
/* ---------- SbState ---------- */ | |
#ifdef USE_SUBBLOCK | |
static void SbState_Free(void *pp, ISzAllocPtr alloc) | |
{ | |
CSbDec *p = (CSbDec *)pp; | |
SbDec_Free(p); | |
ISzAlloc_Free(alloc, pp); | |
} | |
static SRes SbState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAllocPtr alloc) | |
{ | |
UNUSED_VAR(pp); | |
UNUSED_VAR(props); | |
UNUSED_VAR(alloc); | |
return (propSize == 0) ? SZ_OK : SZ_ERROR_UNSUPPORTED; | |
} | |
static void SbState_Init(void *pp) | |
{ | |
SbDec_Init((CSbDec *)pp); | |
} | |
static SRes SbState_Code2(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, | |
int srcWasFinished, ECoderFinishMode finishMode, | |
// int *wasFinished | |
ECoderStatus *status) | |
{ | |
CSbDec *p = (CSbDec *)pp; | |
SRes res; | |
UNUSED_VAR(srcWasFinished); | |
p->dest = dest; | |
p->destLen = *destLen; | |
p->src = src; | |
p->srcLen = *srcLen; | |
p->finish = finishMode; /* change it */ | |
res = SbDec_Decode((CSbDec *)pp); | |
*destLen -= p->destLen; | |
*srcLen -= p->srcLen; | |
// *wasFinished = (*destLen == 0 && *srcLen == 0); /* change it */ | |
*status = (*destLen == 0 && *srcLen == 0) ? | |
CODER_STATUS_FINISHED_WITH_MARK : | |
CODER_STATUS_NOT_FINISHED; | |
return res; | |
} | |
static SRes SbState_SetFromMethod(IStateCoder *p, ISzAllocPtr alloc) | |
{ | |
CSbDec *decoder = (CSbDec *)p->p; | |
if (!decoder) | |
{ | |
decoder = (CSbDec *)ISzAlloc_Alloc(alloc, sizeof(CSbDec)); | |
if (!decoder) | |
return SZ_ERROR_MEM; | |
p->p = decoder; | |
p->Free = SbState_Free; | |
p->SetProps = SbState_SetProps; | |
p->Init = SbState_Init; | |
p->Code2 = SbState_Code2; | |
p->Filter = NULL; | |
} | |
SbDec_Construct(decoder); | |
SbDec_SetAlloc(decoder, alloc); | |
return SZ_OK; | |
} | |
#endif | |
/* ---------- Lzma2 ---------- */ | |
typedef struct | |
{ | |
CLzma2Dec decoder; | |
BoolInt outBufMode; | |
} CLzma2Dec_Spec; | |
static void Lzma2State_Free(void *pp, ISzAllocPtr alloc) | |
{ | |
CLzma2Dec_Spec *p = (CLzma2Dec_Spec *)pp; | |
if (p->outBufMode) | |
Lzma2Dec_FreeProbs(&p->decoder, alloc); | |
else | |
Lzma2Dec_Free(&p->decoder, alloc); | |
ISzAlloc_Free(alloc, pp); | |
} | |
static SRes Lzma2State_SetProps(void *pp, const Byte *props, size_t propSize, ISzAllocPtr alloc) | |
{ | |
if (propSize != 1) | |
return SZ_ERROR_UNSUPPORTED; | |
{ | |
CLzma2Dec_Spec *p = (CLzma2Dec_Spec *)pp; | |
if (p->outBufMode) | |
return Lzma2Dec_AllocateProbs(&p->decoder, props[0], alloc); | |
else | |
return Lzma2Dec_Allocate(&p->decoder, props[0], alloc); | |
} | |
} | |
static void Lzma2State_Init(void *pp) | |
{ | |
Lzma2Dec_Init(&((CLzma2Dec_Spec *)pp)->decoder); | |
} | |
/* | |
if (outBufMode), then (dest) is not used. Use NULL. | |
Data is unpacked to (spec->decoder.decoder.dic) output buffer. | |
*/ | |
static SRes Lzma2State_Code2(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, | |
int srcWasFinished, ECoderFinishMode finishMode, | |
// int *wasFinished, | |
ECoderStatus *status) | |
{ | |
CLzma2Dec_Spec *spec = (CLzma2Dec_Spec *)pp; | |
ELzmaStatus status2; | |
/* ELzmaFinishMode fm = (finishMode == LZMA_FINISH_ANY) ? LZMA_FINISH_ANY : LZMA_FINISH_END; */ | |
SRes res; | |
UNUSED_VAR(srcWasFinished); | |
if (spec->outBufMode) | |
{ | |
SizeT dicPos = spec->decoder.decoder.dicPos; | |
SizeT dicLimit = dicPos + *destLen; | |
res = Lzma2Dec_DecodeToDic(&spec->decoder, dicLimit, src, srcLen, (ELzmaFinishMode)finishMode, &status2); | |
*destLen = spec->decoder.decoder.dicPos - dicPos; | |
} | |
else | |
res = Lzma2Dec_DecodeToBuf(&spec->decoder, dest, destLen, src, srcLen, (ELzmaFinishMode)finishMode, &status2); | |
// *wasFinished = (status2 == LZMA_STATUS_FINISHED_WITH_MARK); | |
// ECoderStatus values are identical to ELzmaStatus values of LZMA2 decoder | |
*status = status2; | |
return res; | |
} | |
static SRes Lzma2State_SetFromMethod(IStateCoder *p, Byte *outBuf, size_t outBufSize, ISzAllocPtr alloc) | |
{ | |
CLzma2Dec_Spec *spec = (CLzma2Dec_Spec *)p->p; | |
if (!spec) | |
{ | |
spec = (CLzma2Dec_Spec *)ISzAlloc_Alloc(alloc, sizeof(CLzma2Dec_Spec)); | |
if (!spec) | |
return SZ_ERROR_MEM; | |
p->p = spec; | |
p->Free = Lzma2State_Free; | |
p->SetProps = Lzma2State_SetProps; | |
p->Init = Lzma2State_Init; | |
p->Code2 = Lzma2State_Code2; | |
p->Filter = NULL; | |
Lzma2Dec_Construct(&spec->decoder); | |
} | |
spec->outBufMode = False; | |
if (outBuf) | |
{ | |
spec->outBufMode = True; | |
spec->decoder.decoder.dic = outBuf; | |
spec->decoder.decoder.dicBufSize = outBufSize; | |
} | |
return SZ_OK; | |
} | |
static SRes Lzma2State_ResetOutBuf(IStateCoder *p, Byte *outBuf, size_t outBufSize) | |
{ | |
CLzma2Dec_Spec *spec = (CLzma2Dec_Spec *)p->p; | |
if ((spec->outBufMode && !outBuf) || (!spec->outBufMode && outBuf)) | |
return SZ_ERROR_FAIL; | |
if (outBuf) | |
{ | |
spec->decoder.decoder.dic = outBuf; | |
spec->decoder.decoder.dicBufSize = outBufSize; | |
} | |
return SZ_OK; | |
} | |
static void MixCoder_Construct(CMixCoder *p, ISzAllocPtr alloc) | |
{ | |
unsigned i; | |
p->alloc = alloc; | |
p->buf = NULL; | |
p->numCoders = 0; | |
p->outBufSize = 0; | |
p->outBuf = NULL; | |
// p->SingleBufMode = False; | |
for (i = 0; i < MIXCODER_NUM_FILTERS_MAX; i++) | |
p->coders[i].p = NULL; | |
} | |
static void MixCoder_Free(CMixCoder *p) | |
{ | |
unsigned i; | |
p->numCoders = 0; | |
for (i = 0; i < MIXCODER_NUM_FILTERS_MAX; i++) | |
{ | |
IStateCoder *sc = &p->coders[i]; | |
if (sc->p) | |
{ | |
sc->Free(sc->p, p->alloc); | |
sc->p = NULL; | |
} | |
} | |
if (p->buf) | |
{ | |
ISzAlloc_Free(p->alloc, p->buf); | |
p->buf = NULL; /* 9.31: the BUG was fixed */ | |
} | |
} | |
static void MixCoder_Init(CMixCoder *p) | |
{ | |
unsigned i; | |
for (i = 0; i < MIXCODER_NUM_FILTERS_MAX - 1; i++) | |
{ | |
p->size[i] = 0; | |
p->pos[i] = 0; | |
p->finished[i] = 0; | |
} | |
for (i = 0; i < p->numCoders; i++) | |
{ | |
IStateCoder *coder = &p->coders[i]; | |
coder->Init(coder->p); | |
p->results[i] = SZ_OK; | |
} | |
p->outWritten = 0; | |
p->wasFinished = False; | |
p->res = SZ_OK; | |
p->status = CODER_STATUS_NOT_SPECIFIED; | |
} | |
static SRes MixCoder_SetFromMethod(CMixCoder *p, unsigned coderIndex, UInt64 methodId, Byte *outBuf, size_t outBufSize) | |
{ | |
IStateCoder *sc = &p->coders[coderIndex]; | |
p->ids[coderIndex] = methodId; | |
switch (methodId) | |
{ | |
case XZ_ID_LZMA2: return Lzma2State_SetFromMethod(sc, outBuf, outBufSize, p->alloc); | |
#ifdef USE_SUBBLOCK | |
case XZ_ID_Subblock: return SbState_SetFromMethod(sc, p->alloc); | |
#endif | |
} | |
if (coderIndex == 0) | |
return SZ_ERROR_UNSUPPORTED; | |
return BraState_SetFromMethod(sc, methodId, 0, p->alloc); | |
} | |
static SRes MixCoder_ResetFromMethod(CMixCoder *p, unsigned coderIndex, UInt64 methodId, Byte *outBuf, size_t outBufSize) | |
{ | |
IStateCoder *sc = &p->coders[coderIndex]; | |
switch (methodId) | |
{ | |
case XZ_ID_LZMA2: return Lzma2State_ResetOutBuf(sc, outBuf, outBufSize); | |
} | |
return SZ_ERROR_UNSUPPORTED; | |
} | |
/* | |
if (destFinish) - then unpack data block is finished at (*destLen) position, | |
and we can return data that were not processed by filter | |
output (status) can be : | |
CODER_STATUS_NOT_FINISHED | |
CODER_STATUS_FINISHED_WITH_MARK | |
CODER_STATUS_NEEDS_MORE_INPUT - not implemented still | |
*/ | |
static SRes MixCoder_Code(CMixCoder *p, | |
Byte *dest, SizeT *destLen, int destFinish, | |
const Byte *src, SizeT *srcLen, int srcWasFinished, | |
ECoderFinishMode finishMode) | |
{ | |
SizeT destLenOrig = *destLen; | |
SizeT srcLenOrig = *srcLen; | |
*destLen = 0; | |
*srcLen = 0; | |
if (p->wasFinished) | |
return p->res; | |
p->status = CODER_STATUS_NOT_FINISHED; | |
// if (p->SingleBufMode) | |
if (p->outBuf) | |
{ | |
SRes res; | |
SizeT destLen2, srcLen2; | |
int wasFinished; | |
PRF_STR("------- MixCoder Single ----------"); | |
srcLen2 = srcLenOrig; | |
destLen2 = destLenOrig; | |
{ | |
IStateCoder *coder = &p->coders[0]; | |
res = coder->Code2(coder->p, NULL, &destLen2, src, &srcLen2, srcWasFinished, finishMode, | |
// &wasFinished, | |
&p->status); | |
wasFinished = (p->status == CODER_STATUS_FINISHED_WITH_MARK); | |
} | |
p->res = res; | |
/* | |
if (wasFinished) | |
p->status = CODER_STATUS_FINISHED_WITH_MARK; | |
else | |
{ | |
if (res == SZ_OK) | |
if (destLen2 != destLenOrig) | |
p->status = CODER_STATUS_NEEDS_MORE_INPUT; | |
} | |
*/ | |
*srcLen = srcLen2; | |
src += srcLen2; | |
p->outWritten += destLen2; | |
if (res != SZ_OK || srcWasFinished || wasFinished) | |
p->wasFinished = True; | |
if (p->numCoders == 1) | |
*destLen = destLen2; | |
else if (p->wasFinished) | |
{ | |
unsigned i; | |
size_t processed = p->outWritten; | |
for (i = 1; i < p->numCoders; i++) | |
{ | |
IStateCoder *coder = &p->coders[i]; | |
processed = coder->Filter(coder->p, p->outBuf, processed); | |
if (wasFinished || (destFinish && p->outWritten == destLenOrig)) | |
processed = p->outWritten; | |
PRF_STR_INT("filter", i); | |
} | |
*destLen = processed; | |
} | |
return res; | |
} | |
PRF_STR("standard mix"); | |
if (p->numCoders != 1) | |
{ | |
if (!p->buf) | |
{ | |
p->buf = (Byte *)ISzAlloc_Alloc(p->alloc, CODER_BUF_SIZE * (MIXCODER_NUM_FILTERS_MAX - 1)); | |
if (!p->buf) | |
return SZ_ERROR_MEM; | |
} | |
finishMode = CODER_FINISH_ANY; | |
} | |
for (;;) | |
{ | |
BoolInt processed = False; | |
BoolInt allFinished = True; | |
SRes resMain = SZ_OK; | |
unsigned i; | |
p->status = CODER_STATUS_NOT_FINISHED; | |
/* | |
if (p->numCoders == 1 && *destLen == destLenOrig && finishMode == LZMA_FINISH_ANY) | |
break; | |
*/ | |
for (i = 0; i < p->numCoders; i++) | |
{ | |
SRes res; | |
IStateCoder *coder = &p->coders[i]; | |
Byte *dest2; | |
SizeT destLen2, srcLen2; // destLen2_Orig; | |
const Byte *src2; | |
int srcFinished2; | |
int encodingWasFinished; | |
ECoderStatus status2; | |
if (i == 0) | |
{ | |
src2 = src; | |
srcLen2 = srcLenOrig - *srcLen; | |
srcFinished2 = srcWasFinished; | |
} | |
else | |
{ | |
size_t k = i - 1; | |
src2 = p->buf + (CODER_BUF_SIZE * k) + p->pos[k]; | |
srcLen2 = p->size[k] - p->pos[k]; | |
srcFinished2 = p->finished[k]; | |
} | |
if (i == p->numCoders - 1) | |
{ | |
dest2 = dest; | |
destLen2 = destLenOrig - *destLen; | |
} | |
else | |
{ | |
if (p->pos[i] != p->size[i]) | |
continue; | |
dest2 = p->buf + (CODER_BUF_SIZE * i); | |
destLen2 = CODER_BUF_SIZE; | |
} | |
// destLen2_Orig = destLen2; | |
if (p->results[i] != SZ_OK) | |
{ | |
if (resMain == SZ_OK) | |
resMain = p->results[i]; | |
continue; | |
} | |
res = coder->Code2(coder->p, | |
dest2, &destLen2, | |
src2, &srcLen2, srcFinished2, | |
finishMode, | |
// &encodingWasFinished, | |
&status2); | |
if (res != SZ_OK) | |
{ | |
p->results[i] = res; | |
if (resMain == SZ_OK) | |
resMain = res; | |
} | |
encodingWasFinished = (status2 == CODER_STATUS_FINISHED_WITH_MARK); | |
if (!encodingWasFinished) | |
{ | |
allFinished = False; | |
if (p->numCoders == 1 && res == SZ_OK) | |
p->status = status2; | |
} | |
if (i == 0) | |
{ | |
*srcLen += srcLen2; | |
src += srcLen2; | |
} | |
else | |
p->pos[(size_t)i - 1] += srcLen2; | |
if (i == p->numCoders - 1) | |
{ | |
*destLen += destLen2; | |
dest += destLen2; | |
} | |
else | |
{ | |
p->size[i] = destLen2; | |
p->pos[i] = 0; | |
p->finished[i] = encodingWasFinished; | |
} | |
if (destLen2 != 0 || srcLen2 != 0) | |
processed = True; | |
} | |
if (!processed) | |
{ | |
if (allFinished) | |
p->status = CODER_STATUS_FINISHED_WITH_MARK; | |
return resMain; | |
} | |
} | |
} | |
SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf) | |
{ | |
*p = (CXzStreamFlags)GetBe16(buf + XZ_SIG_SIZE); | |
if (CrcCalc(buf + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE) != | |
GetUi32(buf + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE)) | |
return SZ_ERROR_NO_ARCHIVE; | |
return XzFlags_IsSupported(*p) ? SZ_OK : SZ_ERROR_UNSUPPORTED; | |
} | |
static BoolInt Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf) | |
{ | |
return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2) | |
&& GetUi32(buf) == CrcCalc(buf + 4, 6) | |
&& flags == GetBe16(buf + 8) | |
&& buf[10] == XZ_FOOTER_SIG_0 | |
&& buf[11] == XZ_FOOTER_SIG_1; | |
} | |
#define READ_VARINT_AND_CHECK(buf, pos, size, res) \ | |
{ unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ | |
if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } | |
static BoolInt XzBlock_AreSupportedFilters(const CXzBlock *p) | |
{ | |
unsigned numFilters = XzBlock_GetNumFilters(p) - 1; | |
unsigned i; | |
{ | |
const CXzFilter *f = &p->filters[numFilters]; | |
if (f->id != XZ_ID_LZMA2 || f->propsSize != 1 || f->props[0] > 40) | |
return False; | |
} | |
for (i = 0; i < numFilters; i++) | |
{ | |
const CXzFilter *f = &p->filters[i]; | |
if (f->id == XZ_ID_Delta) | |
{ | |
if (f->propsSize != 1) | |
return False; | |
} | |
else if (f->id < XZ_ID_Delta | |
|| f->id > XZ_ID_SPARC | |
|| (f->propsSize != 0 && f->propsSize != 4)) | |
return False; | |
} | |
return True; | |
} | |
SRes XzBlock_Parse(CXzBlock *p, const Byte *header) | |
{ | |
unsigned pos; | |
unsigned numFilters, i; | |
unsigned headerSize = (unsigned)header[0] << 2; | |
/* (headerSize != 0) : another code checks */ | |
if (CrcCalc(header, headerSize) != GetUi32(header + headerSize)) | |
return SZ_ERROR_ARCHIVE; | |
pos = 1; | |
p->flags = header[pos++]; | |
p->packSize = (UInt64)(Int64)-1; | |
if (XzBlock_HasPackSize(p)) | |
{ | |
READ_VARINT_AND_CHECK(header, pos, headerSize, &p->packSize); | |
if (p->packSize == 0 || p->packSize + headerSize >= (UInt64)1 << 63) | |
return SZ_ERROR_ARCHIVE; | |
} | |
p->unpackSize = (UInt64)(Int64)-1; | |
if (XzBlock_HasUnpackSize(p)) | |
READ_VARINT_AND_CHECK(header, pos, headerSize, &p->unpackSize); | |
numFilters = XzBlock_GetNumFilters(p); | |
for (i = 0; i < numFilters; i++) | |
{ | |
CXzFilter *filter = p->filters + i; | |
UInt64 size; | |
READ_VARINT_AND_CHECK(header, pos, headerSize, &filter->id); | |
READ_VARINT_AND_CHECK(header, pos, headerSize, &size); | |
if (size > headerSize - pos || size > XZ_FILTER_PROPS_SIZE_MAX) | |
return SZ_ERROR_ARCHIVE; | |
filter->propsSize = (UInt32)size; | |
memcpy(filter->props, header + pos, (size_t)size); | |
pos += (unsigned)size; | |
#ifdef XZ_DUMP | |
printf("\nf[%u] = %2X: ", i, (unsigned)filter->id); | |
{ | |
unsigned i; | |
for (i = 0; i < size; i++) | |
printf(" %2X", filter->props[i]); | |
} | |
#endif | |
} | |
if (XzBlock_HasUnsupportedFlags(p)) | |
return SZ_ERROR_UNSUPPORTED; | |
while (pos < headerSize) | |
if (header[pos++] != 0) | |
return SZ_ERROR_ARCHIVE; | |
return SZ_OK; | |
} | |
static SRes XzDecMix_Init(CMixCoder *p, const CXzBlock *block, Byte *outBuf, size_t outBufSize) | |
{ | |
unsigned i; | |
BoolInt needReInit = True; | |
unsigned numFilters = XzBlock_GetNumFilters(block); | |
if (numFilters == p->numCoders && ((p->outBuf && outBuf) || (!p->outBuf && !outBuf))) | |
{ | |
needReInit = False; | |
for (i = 0; i < numFilters; i++) | |
if (p->ids[i] != block->filters[numFilters - 1 - i].id) | |
{ | |
needReInit = True; | |
break; | |
} | |
} | |
// p->SingleBufMode = (outBuf != NULL); | |
p->outBuf = outBuf; | |
p->outBufSize = outBufSize; | |
// p->SingleBufMode = False; | |
// outBuf = NULL; | |
if (needReInit) | |
{ | |
MixCoder_Free(p); | |
for (i = 0; i < numFilters; i++) | |
{ | |
RINOK(MixCoder_SetFromMethod(p, i, block->filters[numFilters - 1 - i].id, outBuf, outBufSize)); | |
} | |
p->numCoders = numFilters; | |
} | |
else | |
{ | |
RINOK(MixCoder_ResetFromMethod(p, 0, block->filters[numFilters - 1].id, outBuf, outBufSize)); | |
} | |
for (i = 0; i < numFilters; i++) | |
{ | |
const CXzFilter *f = &block->filters[numFilters - 1 - i]; | |
IStateCoder *sc = &p->coders[i]; | |
RINOK(sc->SetProps(sc->p, f->props, f->propsSize, p->alloc)); | |
} | |
MixCoder_Init(p); | |
return SZ_OK; | |
} | |
void XzUnpacker_Init(CXzUnpacker *p) | |
{ | |
p->state = XZ_STATE_STREAM_HEADER; | |
p->pos = 0; | |
p->numStartedStreams = 0; | |
p->numFinishedStreams = 0; | |
p->numTotalBlocks = 0; | |
p->padSize = 0; | |
p->decodeOnlyOneBlock = 0; | |
p->parseMode = False; | |
p->decodeToStreamSignature = False; | |
// p->outBuf = NULL; | |
// p->outBufSize = 0; | |
p->outDataWritten = 0; | |
} | |
void XzUnpacker_SetOutBuf(CXzUnpacker *p, Byte *outBuf, size_t outBufSize) | |
{ | |
p->outBuf = outBuf; | |
p->outBufSize = outBufSize; | |
} | |
void XzUnpacker_Construct(CXzUnpacker *p, ISzAllocPtr alloc) | |
{ | |
MixCoder_Construct(&p->decoder, alloc); | |
p->outBuf = NULL; | |
p->outBufSize = 0; | |
XzUnpacker_Init(p); | |
} | |
void XzUnpacker_Free(CXzUnpacker *p) | |
{ | |
MixCoder_Free(&p->decoder); | |
} | |
void XzUnpacker_PrepareToRandomBlockDecoding(CXzUnpacker *p) | |
{ | |
p->indexSize = 0; | |
p->numBlocks = 0; | |
Sha256_Init(&p->sha); | |
p->state = XZ_STATE_BLOCK_HEADER; | |
p->pos = 0; | |
p->decodeOnlyOneBlock = 1; | |
} | |
static void XzUnpacker_UpdateIndex(CXzUnpacker *p, UInt64 packSize, UInt64 unpackSize) | |
{ | |
Byte temp[32]; | |
unsigned num = Xz_WriteVarInt(temp, packSize); | |
num += Xz_WriteVarInt(temp + num, unpackSize); | |
Sha256_Update(&p->sha, temp, num); | |
p->indexSize += num; | |
p->numBlocks++; | |
} | |
SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen, | |
const Byte *src, SizeT *srcLen, int srcFinished, | |
ECoderFinishMode finishMode, ECoderStatus *status) | |
{ | |
SizeT destLenOrig = *destLen; | |
SizeT srcLenOrig = *srcLen; | |
*destLen = 0; | |
*srcLen = 0; | |
*status = CODER_STATUS_NOT_SPECIFIED; | |
for (;;) | |
{ | |
SizeT srcRem; | |
if (p->state == XZ_STATE_BLOCK) | |
{ | |
SizeT destLen2 = destLenOrig - *destLen; | |
SizeT srcLen2 = srcLenOrig - *srcLen; | |
SRes res; | |
ECoderFinishMode finishMode2 = finishMode; | |
BoolInt srcFinished2 = srcFinished; | |
BoolInt destFinish = False; | |
if (p->block.packSize != (UInt64)(Int64)-1) | |
{ | |
UInt64 rem = p->block.packSize - p->packSize; | |
if (srcLen2 >= rem) | |
{ | |
srcFinished2 = True; | |
srcLen2 = (SizeT)rem; | |
} | |
if (rem == 0 && p->block.unpackSize == p->unpackSize) | |
return SZ_ERROR_DATA; | |
} | |
if (p->block.unpackSize != (UInt64)(Int64)-1) | |
{ | |
UInt64 rem = p->block.unpackSize - p->unpackSize; | |
if (destLen2 >= rem) | |
{ | |
destFinish = True; | |
finishMode2 = CODER_FINISH_END; | |
destLen2 = (SizeT)rem; | |
} | |
} | |
/* | |
if (srcLen2 == 0 && destLen2 == 0) | |
{ | |
*status = CODER_STATUS_NOT_FINISHED; | |
return SZ_OK; | |
} | |
*/ | |
{ | |
res = MixCoder_Code(&p->decoder, | |
(p->outBuf ? NULL : dest), &destLen2, destFinish, | |
src, &srcLen2, srcFinished2, | |
finishMode2); | |
*status = p->decoder.status; | |
XzCheck_Update(&p->check, (p->outBuf ? p->outBuf + p->outDataWritten : dest), destLen2); | |
if (!p->outBuf) | |
dest += destLen2; | |
p->outDataWritten += destLen2; | |
} | |
(*srcLen) += srcLen2; | |
src += srcLen2; | |
p->packSize += srcLen2; | |
(*destLen) += destLen2; | |
p->unpackSize += destLen2; | |
RINOK(res); | |
if (*status != CODER_STATUS_FINISHED_WITH_MARK) | |
{ | |
if (p->block.packSize == p->packSize | |
&& *status == CODER_STATUS_NEEDS_MORE_INPUT) | |
{ | |
PRF_STR("CODER_STATUS_NEEDS_MORE_INPUT"); | |
*status = CODER_STATUS_NOT_SPECIFIED; | |
return SZ_ERROR_DATA; | |
} | |
return SZ_OK; | |
} | |
{ | |
XzUnpacker_UpdateIndex(p, XzUnpacker_GetPackSizeForIndex(p), p->unpackSize); | |
p->state = XZ_STATE_BLOCK_FOOTER; | |
p->pos = 0; | |
p->alignPos = 0; | |
*status = CODER_STATUS_NOT_SPECIFIED; | |
if ((p->block.packSize != (UInt64)(Int64)-1 && p->block.packSize != p->packSize) | |
|| (p->block.unpackSize != (UInt64)(Int64)-1 && p->block.unpackSize != p->unpackSize)) | |
{ | |
PRF_STR("ERROR: block.size mismatch"); | |
return SZ_ERROR_DATA; | |
} | |
} | |
// continue; | |
} | |
srcRem = srcLenOrig - *srcLen; | |
// XZ_STATE_BLOCK_FOOTER can transit to XZ_STATE_BLOCK_HEADER without input bytes | |
if (srcRem == 0 && p->state != XZ_STATE_BLOCK_FOOTER) | |
{ | |
*status = CODER_STATUS_NEEDS_MORE_INPUT; | |
return SZ_OK; | |
} | |
switch (p->state) | |
{ | |
case XZ_STATE_STREAM_HEADER: | |
{ | |
if (p->pos < XZ_STREAM_HEADER_SIZE) | |
{ | |
if (p->pos < XZ_SIG_SIZE && *src != XZ_SIG[p->pos]) | |
return SZ_ERROR_NO_ARCHIVE; | |
if (p->decodeToStreamSignature) | |
return SZ_OK; | |
p->buf[p->pos++] = *src++; | |
(*srcLen)++; | |
} | |
else | |
{ | |
RINOK(Xz_ParseHeader(&p->streamFlags, p->buf)); | |
p->numStartedStreams++; | |
p->indexSize = 0; | |
p->numBlocks = 0; | |
Sha256_Init(&p->sha); | |
p->state = XZ_STATE_BLOCK_HEADER; | |
p->pos = 0; | |
} | |
break; | |
} | |
case XZ_STATE_BLOCK_HEADER: | |
{ | |
if (p->pos == 0) | |
{ | |
p->buf[p->pos++] = *src++; | |
(*srcLen)++; | |
if (p->buf[0] == 0) | |
{ | |
if (p->decodeOnlyOneBlock) | |
return SZ_ERROR_DATA; | |
p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks); | |
p->indexPos = p->indexPreSize; | |
p->indexSize += p->indexPreSize; | |
Sha256_Final(&p->sha, p->shaDigest); | |
Sha256_Init(&p->sha); | |
p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize); | |
p->state = XZ_STATE_STREAM_INDEX; | |
break; | |
} | |
p->blockHeaderSize = ((UInt32)p->buf[0] << 2) + 4; | |
break; | |
} | |
if (p->pos != p->blockHeaderSize) | |
{ | |
UInt32 cur = p->blockHeaderSize - p->pos; | |
if (cur > srcRem) | |
cur = (UInt32)srcRem; | |
memcpy(p->buf + p->pos, src, cur); | |
p->pos += cur; | |
(*srcLen) += cur; | |
src += cur; | |
} | |
else | |
{ | |
RINOK(XzBlock_Parse(&p->block, p->buf)); | |
if (!XzBlock_AreSupportedFilters(&p->block)) | |
return SZ_ERROR_UNSUPPORTED; | |
p->numTotalBlocks++; | |
p->state = XZ_STATE_BLOCK; | |
p->packSize = 0; | |
p->unpackSize = 0; | |
XzCheck_Init(&p->check, XzFlags_GetCheckType(p->streamFlags)); | |
if (p->parseMode) | |
{ | |
p->headerParsedOk = True; | |
return SZ_OK; | |
} | |
RINOK(XzDecMix_Init(&p->decoder, &p->block, p->outBuf, p->outBufSize)); | |
} | |
break; | |
} | |
case XZ_STATE_BLOCK_FOOTER: | |
{ | |
if ((((unsigned)p->packSize + p->alignPos) & 3) != 0) | |
{ | |
if (srcRem == 0) | |
{ | |
*status = CODER_STATUS_NEEDS_MORE_INPUT; | |
return SZ_OK; | |
} | |
(*srcLen)++; | |
p->alignPos++; | |
if (*src++ != 0) | |
return SZ_ERROR_CRC; | |
} | |
else | |
{ | |
UInt32 checkSize = XzFlags_GetCheckSize(p->streamFlags); | |
UInt32 cur = checkSize - p->pos; | |
if (cur != 0) | |
{ | |
if (srcRem == 0) | |
{ | |
*status = CODER_STATUS_NEEDS_MORE_INPUT; | |
return SZ_OK; | |
} | |
if (cur > srcRem) | |
cur = (UInt32)srcRem; | |
memcpy(p->buf + p->pos, src, cur); | |
p->pos += cur; | |
(*srcLen) += cur; | |
src += cur; | |
if (checkSize != p->pos) | |
break; | |
} | |
{ | |
Byte digest[XZ_CHECK_SIZE_MAX]; | |
p->state = XZ_STATE_BLOCK_HEADER; | |
p->pos = 0; | |
if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0) | |
return SZ_ERROR_CRC; | |
if (p->decodeOnlyOneBlock) | |
{ | |
*status = CODER_STATUS_FINISHED_WITH_MARK; | |
return SZ_OK; | |
} | |
} | |
} | |
break; | |
} | |
case XZ_STATE_STREAM_INDEX: | |
{ | |
if (p->pos < p->indexPreSize) | |
{ | |
(*srcLen)++; | |
if (*src++ != p->buf[p->pos++]) | |
return SZ_ERROR_CRC; | |
} | |
else | |
{ | |
if (p->indexPos < p->indexSize) | |
{ | |
UInt64 cur = p->indexSize - p->indexPos; | |
if (srcRem > cur) | |
srcRem = (SizeT)cur; | |
p->crc = CrcUpdate(p->crc, src, srcRem); | |
Sha256_Update(&p->sha, src, srcRem); | |
(*srcLen) += srcRem; | |
src += srcRem; | |
p->indexPos += srcRem; | |
} | |
else if ((p->indexPos & 3) != 0) | |
{ | |
Byte b = *src++; | |
p->crc = CRC_UPDATE_BYTE(p->crc, b); | |
(*srcLen)++; | |
p->indexPos++; | |
p->indexSize++; | |
if (b != 0) | |
return SZ_ERROR_CRC; | |
} | |
else | |
{ | |
Byte digest[SHA256_DIGEST_SIZE]; | |
p->state = XZ_STATE_STREAM_INDEX_CRC; | |
p->indexSize += 4; | |
p->pos = 0; | |
Sha256_Final(&p->sha, digest); | |
if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0) | |
return SZ_ERROR_CRC; | |
} | |
} | |
break; | |
} | |
case XZ_STATE_STREAM_INDEX_CRC: | |
{ | |
if (p->pos < 4) | |
{ | |
(*srcLen)++; | |
p->buf[p->pos++] = *src++; | |
} | |
else | |
{ | |
p->state = XZ_STATE_STREAM_FOOTER; | |
p->pos = 0; | |
if (CRC_GET_DIGEST(p->crc) != GetUi32(p->buf)) | |
return SZ_ERROR_CRC; | |
} | |
break; | |
} | |
case XZ_STATE_STREAM_FOOTER: | |
{ | |
UInt32 cur = XZ_STREAM_FOOTER_SIZE - p->pos; | |
if (cur > srcRem) | |
cur = (UInt32)srcRem; | |
memcpy(p->buf + p->pos, src, cur); | |
p->pos += cur; | |
(*srcLen) += cur; | |
src += cur; | |
if (p->pos == XZ_STREAM_FOOTER_SIZE) | |
{ | |
p->state = XZ_STATE_STREAM_PADDING; | |
p->numFinishedStreams++; | |
p->padSize = 0; | |
if (!Xz_CheckFooter(p->streamFlags, p->indexSize, p->buf)) | |
return SZ_ERROR_CRC; | |
} | |
break; | |
} | |
case XZ_STATE_STREAM_PADDING: | |
{ | |
if (*src != 0) | |
{ | |
if (((UInt32)p->padSize & 3) != 0) | |
return SZ_ERROR_NO_ARCHIVE; | |
p->pos = 0; | |
p->state = XZ_STATE_STREAM_HEADER; | |
} | |
else | |
{ | |
(*srcLen)++; | |
src++; | |
p->padSize++; | |
} | |
break; | |
} | |
case XZ_STATE_BLOCK: break; /* to disable GCC warning */ | |
} | |
} | |
/* | |
if (p->state == XZ_STATE_FINISHED) | |
*status = CODER_STATUS_FINISHED_WITH_MARK; | |
return SZ_OK; | |
*/ | |
} | |
SRes XzUnpacker_CodeFull(CXzUnpacker *p, Byte *dest, SizeT *destLen, | |
const Byte *src, SizeT *srcLen, | |
ECoderFinishMode finishMode, ECoderStatus *status) | |
{ | |
XzUnpacker_Init(p); | |
XzUnpacker_SetOutBuf(p, dest, *destLen); | |
return XzUnpacker_Code(p, | |
NULL, destLen, | |
src, srcLen, True, | |
finishMode, status); | |
} | |
BoolInt XzUnpacker_IsBlockFinished(const CXzUnpacker *p) | |
{ | |
return (p->state == XZ_STATE_BLOCK_HEADER) && (p->pos == 0); | |
} | |
BoolInt XzUnpacker_IsStreamWasFinished(const CXzUnpacker *p) | |
{ | |
return (p->state == XZ_STATE_STREAM_PADDING) && (((UInt32)p->padSize & 3) == 0); | |
} | |
UInt64 XzUnpacker_GetExtraSize(const CXzUnpacker *p) | |
{ | |
UInt64 num = 0; | |
if (p->state == XZ_STATE_STREAM_PADDING) | |
num = p->padSize; | |
else if (p->state == XZ_STATE_STREAM_HEADER) | |
num = p->padSize + p->pos; | |
return num; | |
} | |
#ifndef _7ZIP_ST | |
#include "MtDec.h" | |
#endif | |
void XzDecMtProps_Init(CXzDecMtProps *p) | |
{ | |
p->inBufSize_ST = 1 << 18; | |
p->outStep_ST = 1 << 20; | |
p->ignoreErrors = False; | |
#ifndef _7ZIP_ST | |
p->numThreads = 1; | |
p->inBufSize_MT = 1 << 18; | |
p->memUseMax = sizeof(size_t) << 28; | |
#endif | |
} | |
#ifndef _7ZIP_ST | |
/* ---------- CXzDecMtThread ---------- */ | |
typedef struct | |
{ | |
Byte *outBuf; | |
size_t outBufSize; | |
size_t outPreSize; | |
size_t inPreSize; | |
size_t inPreHeaderSize; | |
size_t blockPackSize_for_Index; // including block header and checksum. | |
size_t blockPackTotal; // including stream header, block header and checksum. | |
size_t inCodeSize; | |
size_t outCodeSize; | |
ECoderStatus status; | |
SRes codeRes; | |
BoolInt skipMode; | |
// BoolInt finishedWithMark; | |
EMtDecParseState parseState; | |
BoolInt parsing_Truncated; | |
BoolInt atBlockHeader; | |
CXzStreamFlags streamFlags; | |
// UInt64 numFinishedStreams | |
UInt64 numStreams; | |
UInt64 numTotalBlocks; | |
UInt64 numBlocks; | |
BoolInt dec_created; | |
CXzUnpacker dec; | |
Byte mtPad[1 << 7]; | |
} CXzDecMtThread; | |
#endif | |
/* ---------- CXzDecMt ---------- */ | |
typedef struct | |
{ | |
CAlignOffsetAlloc alignOffsetAlloc; | |
ISzAllocPtr allocMid; | |
CXzDecMtProps props; | |
size_t unpackBlockMaxSize; | |
ISeqInStream *inStream; | |
ISeqOutStream *outStream; | |
ICompressProgress *progress; | |
// CXzStatInfo *stat; | |
BoolInt finishMode; | |
BoolInt outSize_Defined; | |
UInt64 outSize; | |
UInt64 outProcessed; | |
UInt64 inProcessed; | |
UInt64 readProcessed; | |
BoolInt readWasFinished; | |
SRes readRes; | |
SRes writeRes; | |
Byte *outBuf; | |
size_t outBufSize; | |
Byte *inBuf; | |
size_t inBufSize; | |
CXzUnpacker dec; | |
ECoderStatus status; | |
SRes codeRes; | |
#ifndef _7ZIP_ST | |
BoolInt mainDecoderWasCalled; | |
// int statErrorDefined; | |
int finishedDecoderIndex; | |
// global values that are used in Parse stage | |
CXzStreamFlags streamFlags; | |
// UInt64 numFinishedStreams | |
UInt64 numStreams; | |
UInt64 numTotalBlocks; | |
UInt64 numBlocks; | |
// UInt64 numBadBlocks; | |
SRes mainErrorCode; | |
BoolInt isBlockHeaderState_Parse; | |
BoolInt isBlockHeaderState_Write; | |
UInt64 outProcessed_Parse; | |
BoolInt parsing_Truncated; | |
BoolInt mtc_WasConstructed; | |
CMtDec mtc; | |
CXzDecMtThread coders[MTDEC__THREADS_MAX]; | |
#endif | |
} CXzDecMt; | |
CXzDecMtHandle XzDecMt_Create(ISzAllocPtr alloc, ISzAllocPtr allocMid) | |
{ | |
CXzDecMt *p = (CXzDecMt *)ISzAlloc_Alloc(alloc, sizeof(CXzDecMt)); | |
if (!p) | |
return NULL; | |
AlignOffsetAlloc_CreateVTable(&p->alignOffsetAlloc); | |
p->alignOffsetAlloc.baseAlloc = alloc; | |
p->alignOffsetAlloc.numAlignBits = 7; | |
p->alignOffsetAlloc.offset = 0; | |
p->allocMid = allocMid; | |
p->outBuf = NULL; | |
p->outBufSize = 0; | |
p->inBuf = NULL; | |
p->inBufSize = 0; | |
XzUnpacker_Construct(&p->dec, &p->alignOffsetAlloc.vt); | |
p->unpackBlockMaxSize = 0; | |
XzDecMtProps_Init(&p->props); | |
#ifndef _7ZIP_ST | |
p->mtc_WasConstructed = False; | |
{ | |
unsigned i; | |
for (i = 0; i < MTDEC__THREADS_MAX; i++) | |
{ | |
CXzDecMtThread *coder = &p->coders[i]; | |
coder->dec_created = False; | |
coder->outBuf = NULL; | |
coder->outBufSize = 0; | |
} | |
} | |
#endif | |
return p; | |
} | |
#ifndef _7ZIP_ST | |
static void XzDecMt_FreeOutBufs(CXzDecMt *p) | |
{ | |
unsigned i; | |
for (i = 0; i < MTDEC__THREADS_MAX; i++) | |
{ | |
CXzDecMtThread *coder = &p->coders[i]; | |
if (coder->outBuf) | |
{ | |
ISzAlloc_Free(p->allocMid, coder->outBuf); | |
coder->outBuf = NULL; | |
coder->outBufSize = 0; | |
} | |
} | |
p->unpackBlockMaxSize = 0; | |
} | |
#endif | |
static void XzDecMt_FreeSt(CXzDecMt *p) | |
{ | |
XzUnpacker_Free(&p->dec); | |
if (p->outBuf) | |
{ | |
ISzAlloc_Free(p->allocMid, p->outBuf); | |
p->outBuf = NULL; | |
} | |
p->outBufSize = 0; | |
if (p->inBuf) | |
{ | |
ISzAlloc_Free(p->allocMid, p->inBuf); | |
p->inBuf = NULL; | |
} | |
p->inBufSize = 0; | |
} | |
void XzDecMt_Destroy(CXzDecMtHandle pp) | |
{ | |
CXzDecMt *p = (CXzDecMt *)pp; | |
XzDecMt_FreeSt(p); | |
#ifndef _7ZIP_ST | |
if (p->mtc_WasConstructed) | |
{ | |
MtDec_Destruct(&p->mtc); | |
p->mtc_WasConstructed = False; | |
} | |
{ | |
unsigned i; | |
for (i = 0; i < MTDEC__THREADS_MAX; i++) | |
{ | |
CXzDecMtThread *t = &p->coders[i]; | |
if (t->dec_created) | |
{ | |
// we don't need to free dict here | |
XzUnpacker_Free(&t->dec); | |
t->dec_created = False; | |
} | |
} | |
} | |
XzDecMt_FreeOutBufs(p); | |
#endif | |
ISzAlloc_Free(p->alignOffsetAlloc.baseAlloc, pp); | |
} | |
#ifndef _7ZIP_ST | |
static void XzDecMt_Callback_Parse(void *obj, unsigned coderIndex, CMtDecCallbackInfo *cc) | |
{ | |
CXzDecMt *me = (CXzDecMt *)obj; | |
CXzDecMtThread *coder = &me->coders[coderIndex]; | |
size_t srcSize = cc->srcSize; | |
cc->srcSize = 0; | |
cc->outPos = 0; | |
cc->state = MTDEC_PARSE_CONTINUE; | |
cc->canCreateNewThread = True; | |
if (cc->startCall) | |
{ | |
coder->outPreSize = 0; | |
coder->inPreSize = 0; | |
coder->inPreHeaderSize = 0; | |
coder->parseState = MTDEC_PARSE_CONTINUE; | |
coder->parsing_Truncated = False; | |
coder->skipMode = False; | |
coder->codeRes = SZ_OK; | |
coder->status = CODER_STATUS_NOT_SPECIFIED; | |
coder->inCodeSize = 0; | |
coder->outCodeSize = 0; | |
coder->numStreams = me->numStreams; | |
coder->numTotalBlocks = me->numTotalBlocks; | |
coder->numBlocks = me->numBlocks; | |
if (!coder->dec_created) | |
{ | |
XzUnpacker_Construct(&coder->dec, &me->alignOffsetAlloc.vt); | |
coder->dec_created = True; | |
} | |
XzUnpacker_Init(&coder->dec); | |
if (me->isBlockHeaderState_Parse) | |
{ | |
coder->dec.streamFlags = me->streamFlags; | |
coder->atBlockHeader = True; | |
XzUnpacker_PrepareToRandomBlockDecoding(&coder->dec); | |
} | |
else | |
{ | |
coder->atBlockHeader = False; | |
me->isBlockHeaderState_Parse = True; | |
} | |
coder->dec.numStartedStreams = me->numStreams; | |
coder->dec.numTotalBlocks = me->numTotalBlocks; | |
coder->dec.numBlocks = me->numBlocks; | |
} | |
while (!coder->skipMode) | |
{ | |
ECoderStatus status; | |
SRes res; | |
size_t srcSize2 = srcSize; | |
size_t destSize = (size_t)0 - 1; | |
coder->dec.parseMode = True; | |
coder->dec.headerParsedOk = False; | |
PRF_STR_INT("Parse", srcSize2); | |
res = XzUnpacker_Code(&coder->dec, | |
NULL, &destSize, | |
cc->src, &srcSize2, cc->srcFinished, | |
CODER_FINISH_END, &status); | |
// PRF(printf(" res = %d, srcSize2 = %d", res, (unsigned)srcSize2)); | |
coder->codeRes = res; | |
coder->status = status; | |
cc->srcSize += srcSize2; | |
srcSize -= srcSize2; | |
coder->inPreHeaderSize += srcSize2; | |
coder->inPreSize = coder->inPreHeaderSize; | |
if (res != SZ_OK) | |
{ | |
cc->state = | |
coder->parseState = MTDEC_PARSE_END; | |
/* | |
if (res == SZ_ERROR_MEM) | |
return res; | |
return SZ_OK; | |
*/ | |
return; // res; | |
} | |
if (coder->dec.headerParsedOk) | |
{ | |
const CXzBlock *block = &coder->dec.block; | |
if (XzBlock_HasUnpackSize(block) | |
// && block->unpackSize <= me->props.outBlockMax | |
&& XzBlock_HasPackSize(block)) | |
{ | |
{ | |
if (block->unpackSize * 2 * me->mtc.numStartedThreads > me->props.memUseMax) | |
{ | |
cc->state = MTDEC_PARSE_OVERFLOW; | |
return; // SZ_OK; | |
} | |
} | |
{ | |
UInt64 packSize = block->packSize; | |
UInt64 packSizeAligned = packSize + ((0 - (unsigned)packSize) & 3); | |
UInt32 checkSize = XzFlags_GetCheckSize(coder->dec.streamFlags); | |
UInt64 blockPackSum = coder->inPreSize + packSizeAligned + checkSize; | |
// if (blockPackSum <= me->props.inBlockMax) | |
// unpackBlockMaxSize | |
{ | |
coder->blockPackSize_for_Index = (size_t)(coder->dec.blockHeaderSize + packSize + checkSize); | |
coder->blockPackTotal = (size_t)blockPackSum; | |
coder->outPreSize = (size_t)block->unpackSize; | |
coder->streamFlags = coder->dec.streamFlags; | |
me->streamFlags = coder->dec.streamFlags; | |
coder->skipMode = True; | |
break; | |
} | |
} | |
} | |
} | |
else | |
// if (coder->inPreSize <= me->props.inBlockMax) | |
{ | |
if (!cc->srcFinished) | |
return; // SZ_OK; | |
cc->state = | |
coder->parseState = MTDEC_PARSE_END; | |
return; // SZ_OK; | |
} | |
cc->state = MTDEC_PARSE_OVERFLOW; | |
return; // SZ_OK; | |
} | |
// ---------- skipMode ---------- | |
{ | |
UInt64 rem = coder->blockPackTotal - coder->inPreSize; | |
size_t cur = srcSize; | |
if (cur > rem) | |
cur = (size_t)rem; | |
cc->srcSize += cur; | |
coder->inPreSize += cur; | |
srcSize -= cur; | |
if (coder->inPreSize == coder->blockPackTotal) | |
{ | |
if (srcSize == 0) | |
{ | |
if (!cc->srcFinished) | |
return; // SZ_OK; | |
cc->state = MTDEC_PARSE_END; | |
} | |
else if ((cc->src)[cc->srcSize] == 0) // we check control byte of next block | |
cc->state = MTDEC_PARSE_END; | |
else | |
{ | |
cc->state = MTDEC_PARSE_NEW; | |
{ | |
size_t blockMax = me->unpackBlockMaxSize; | |
if (blockMax < coder->outPreSize) | |
blockMax = coder->outPreSize; | |
{ | |
UInt64 required = (UInt64)blockMax * (me->mtc.numStartedThreads + 1) * 2; | |
if (me->props.memUseMax < required) | |
cc->canCreateNewThread = False; | |
} | |
} | |
if (me->outSize_Defined) | |
{ | |
// next block can be zero size | |
const UInt64 rem2 = me->outSize - me->outProcessed_Parse; | |
if (rem2 < coder->outPreSize) | |
{ | |
coder->parsing_Truncated = True; | |
cc->state = MTDEC_PARSE_END; | |
} | |
me->outProcessed_Parse += coder->outPreSize; | |
} | |
} | |
} | |
else if (cc->srcFinished) | |
cc->state = MTDEC_PARSE_END; | |
else | |
return; // SZ_OK; | |
coder->parseState = cc->state; | |
cc->outPos = coder->outPreSize; | |
me->numStreams = coder->dec.numStartedStreams; | |
me->numTotalBlocks = coder->dec.numTotalBlocks; | |
me->numBlocks = coder->dec.numBlocks + 1; | |
return; // SZ_OK; | |
} | |
} | |
static SRes XzDecMt_Callback_PreCode(void *pp, unsigned coderIndex) | |
{ | |
CXzDecMt *me = (CXzDecMt *)pp; | |
CXzDecMtThread *coder = &me->coders[coderIndex]; | |
Byte *dest; | |
if (!coder->dec.headerParsedOk) | |
return SZ_OK; | |
dest = coder->outBuf; | |
if (!dest || coder->outBufSize < coder->outPreSize) | |
{ | |
if (dest) | |
{ | |
ISzAlloc_Free(me->allocMid, dest); | |
coder->outBuf = NULL; | |
coder->outBufSize = 0; | |
} | |
{ | |
size_t outPreSize = coder->outPreSize; | |
if (outPreSize == 0) | |
outPreSize = 1; | |
dest = (Byte *)ISzAlloc_Alloc(me->allocMid, outPreSize); | |
} | |
if (!dest) | |
return SZ_ERROR_MEM; | |
coder->outBuf = dest; | |
coder->outBufSize = coder->outPreSize; | |
if (coder->outBufSize > me->unpackBlockMaxSize) | |
me->unpackBlockMaxSize = coder->outBufSize; | |
} | |
// return SZ_ERROR_MEM; | |
XzUnpacker_SetOutBuf(&coder->dec, coder->outBuf, coder->outBufSize); | |
{ | |
SRes res = XzDecMix_Init(&coder->dec.decoder, &coder->dec.block, coder->outBuf, coder->outBufSize); | |
// res = SZ_ERROR_UNSUPPORTED; // to test | |
coder->codeRes = res; | |
if (res != SZ_OK) | |
{ | |
// if (res == SZ_ERROR_MEM) return res; | |
if (me->props.ignoreErrors && res != SZ_ERROR_MEM) | |
return S_OK; | |
return res; | |
} | |
} | |
return SZ_OK; | |
} | |
static SRes XzDecMt_Callback_Code(void *pp, unsigned coderIndex, | |
const Byte *src, size_t srcSize, int srcFinished, | |
// int finished, int blockFinished, | |
UInt64 *inCodePos, UInt64 *outCodePos, int *stop) | |
{ | |
CXzDecMt *me = (CXzDecMt *)pp; | |
CXzDecMtThread *coder = &me->coders[coderIndex]; | |
*inCodePos = coder->inCodeSize; | |
*outCodePos = coder->outCodeSize; | |
*stop = True; | |
if (coder->inCodeSize < coder->inPreHeaderSize) | |
{ | |
UInt64 rem = coder->inPreHeaderSize - coder->inCodeSize; | |
size_t step = srcSize; | |
if (step > rem) | |
step = (size_t)rem; | |
src += step; | |
srcSize -= step; | |
coder->inCodeSize += step; | |
if (coder->inCodeSize < coder->inPreHeaderSize) | |
{ | |
*stop = False; | |
return SZ_OK; | |
} | |
} | |
if (!coder->dec.headerParsedOk) | |
return SZ_OK; | |
if (!coder->outBuf) | |
return SZ_OK; | |
if (coder->codeRes == SZ_OK) | |
{ | |
ECoderStatus status; | |
SRes res; | |
size_t srcProcessed = srcSize; | |
size_t outSizeCur = coder->outPreSize - coder->dec.outDataWritten; | |
// PRF(printf("\nCallback_Code: Code %d %d\n", (unsigned)srcSize, (unsigned)outSizeCur)); | |
res = XzUnpacker_Code(&coder->dec, | |
NULL, &outSizeCur, | |
src, &srcProcessed, srcFinished, | |
// coder->finishedWithMark ? CODER_FINISH_END : CODER_FINISH_ANY, | |
CODER_FINISH_END, | |
&status); | |
// PRF(printf(" res = %d, srcSize2 = %d, outSizeCur = %d", res, (unsigned)srcProcessed, (unsigned)outSizeCur)); | |
coder->codeRes = res; | |
coder->status = status; | |
coder->inCodeSize += srcProcessed; | |
coder->outCodeSize = coder->dec.outDataWritten; | |
*inCodePos = coder->inCodeSize; | |
*outCodePos = coder->outCodeSize; | |
if (res == SZ_OK) | |
{ | |
if (srcProcessed == srcSize) | |
*stop = False; | |
return SZ_OK; | |
} | |
} | |
if (me->props.ignoreErrors && coder->codeRes != SZ_ERROR_MEM) | |
{ | |
*inCodePos = coder->inPreSize; | |
*outCodePos = coder->outPreSize; | |
return S_OK; | |
} | |
return coder->codeRes; | |
} | |
#define XZDECMT_STREAM_WRITE_STEP (1 << 24) | |
static SRes XzDecMt_Callback_Write(void *pp, unsigned coderIndex, | |
BoolInt needWriteToStream, | |
const Byte *src, size_t srcSize, | |
// int srcFinished, | |
BoolInt *needContinue, | |
BoolInt *canRecode) | |
{ | |
CXzDecMt *me = (CXzDecMt *)pp; | |
const CXzDecMtThread *coder = &me->coders[coderIndex]; | |
// PRF(printf("\nWrite processed = %d srcSize = %d\n", (unsigned)me->mtc.inProcessed, (unsigned)srcSize)); | |
*needContinue = False; | |
*canRecode = True; | |
if (!needWriteToStream) | |
return SZ_OK; | |
if (!coder->dec.headerParsedOk || !coder->outBuf) | |
{ | |
if (me->finishedDecoderIndex < 0) | |
me->finishedDecoderIndex = coderIndex; | |
return SZ_OK; | |
} | |
if (me->finishedDecoderIndex >= 0) | |
return SZ_OK; | |
me->mtc.inProcessed += coder->inCodeSize; | |
*canRecode = False; | |
{ | |
SRes res; | |
size_t size = coder->outCodeSize; | |
Byte *data = coder->outBuf; | |
// we use in me->dec: sha, numBlocks, indexSize | |
if (!me->isBlockHeaderState_Write) | |
{ | |
XzUnpacker_PrepareToRandomBlockDecoding(&me->dec); | |
me->dec.decodeOnlyOneBlock = False; | |
me->dec.numStartedStreams = coder->dec.numStartedStreams; | |
me->dec.streamFlags = coder->streamFlags; | |
me->isBlockHeaderState_Write = True; | |
} | |
me->dec.numTotalBlocks = coder->dec.numTotalBlocks; | |
XzUnpacker_UpdateIndex(&me->dec, coder->blockPackSize_for_Index, coder->outPreSize); | |
if (coder->outPreSize != size) | |
{ | |
if (me->props.ignoreErrors) | |
{ | |
memset(data + size, 0, coder->outPreSize - size); | |
size = coder->outPreSize; | |
} | |
// me->numBadBlocks++; | |
if (me->mainErrorCode == SZ_OK) | |
{ | |
if ((int)coder->status == LZMA_STATUS_NEEDS_MORE_INPUT) | |
me->mainErrorCode = SZ_ERROR_INPUT_EOF; | |
else | |
me->mainErrorCode = SZ_ERROR_DATA; | |
} | |
} | |
if (me->writeRes != SZ_OK) | |
return me->writeRes; | |
res = SZ_OK; | |
{ | |
if (me->outSize_Defined) | |
{ | |
const UInt64 rem = me->outSize - me->outProcessed; | |
if (size > rem) | |
size = (SizeT)rem; | |
} | |
for (;;) | |
{ | |
size_t cur = size; | |
size_t written; | |
if (cur > XZDECMT_STREAM_WRITE_STEP) | |
cur = XZDECMT_STREAM_WRITE_STEP; | |
written = ISeqOutStream_Write(me->outStream, data, cur); | |
// PRF(printf("\nWritten ask = %d written = %d\n", (unsigned)cur, (unsigned)written)); | |
me->outProcessed += written; | |
if (written != cur) | |
{ | |
me->writeRes = SZ_ERROR_WRITE; | |
res = me->writeRes; | |
break; | |
} | |
data += cur; | |
size -= cur; | |
// PRF_STR_INT("Written size =", size); | |
if (size == 0) | |
break; | |
res = MtProgress_ProgressAdd(&me->mtc.mtProgress, 0, 0); | |
if (res != SZ_OK) | |
break; | |
} | |
} | |
if (coder->codeRes != SZ_OK) | |
if (!me->props.ignoreErrors) | |
{ | |
me->finishedDecoderIndex = coderIndex; | |
return res; | |
} | |
RINOK(res); | |
if (coder->inPreSize != coder->inCodeSize | |
|| coder->blockPackTotal != coder->inCodeSize) | |
{ | |
me->finishedDecoderIndex = coderIndex; | |
return SZ_OK; | |
} | |
if (coder->parseState != MTDEC_PARSE_END) | |
{ | |
*needContinue = True; | |
return SZ_OK; | |
} | |
} | |
// (coder->state == MTDEC_PARSE_END) means that there are no other working threads | |
// so we can use mtc variables without lock | |
PRF_STR_INT("Write MTDEC_PARSE_END", me->mtc.inProcessed); | |
me->mtc.mtProgress.totalInSize = me->mtc.inProcessed; | |
{ | |
CXzUnpacker *dec = &me->dec; | |
PRF_STR_INT("PostSingle", srcSize); | |
{ | |
size_t srcProcessed = srcSize; | |
ECoderStatus status; | |
size_t outSizeCur = 0; | |
SRes res; | |
// dec->decodeOnlyOneBlock = False; | |
dec->decodeToStreamSignature = True; | |
me->mainDecoderWasCalled = True; | |
if (coder->parsing_Truncated) | |
{ | |
me->parsing_Truncated = True; | |
return SZ_OK; | |
} | |
res = XzUnpacker_Code(dec, | |
NULL, &outSizeCur, | |
src, &srcProcessed, | |
me->mtc.readWasFinished, // srcFinished | |
CODER_FINISH_END, // CODER_FINISH_ANY, | |
&status); | |
me->status = status; | |
me->codeRes = res; | |
me->mtc.inProcessed += srcProcessed; | |
me->mtc.mtProgress.totalInSize = me->mtc.inProcessed; | |
if (res != SZ_OK) | |
{ | |
return S_OK; | |
// return res; | |
} | |
if (dec->state == XZ_STATE_STREAM_HEADER) | |
{ | |
*needContinue = True; | |
me->isBlockHeaderState_Parse = False; | |
me->isBlockHeaderState_Write = False; | |
{ | |
Byte *crossBuf = MtDec_GetCrossBuff(&me->mtc); | |
if (!crossBuf) | |
return SZ_ERROR_MEM; | |
memcpy(crossBuf, src + srcProcessed, srcSize - srcProcessed); | |
} | |
me->mtc.crossStart = 0; | |
me->mtc.crossEnd = srcSize - srcProcessed; | |
return SZ_OK; | |
} | |
if (status != CODER_STATUS_NEEDS_MORE_INPUT) | |
{ | |
return E_FAIL; | |
} | |
if (me->mtc.readWasFinished) | |
{ | |
return SZ_OK; | |
} | |
} | |
{ | |
size_t inPos; | |
size_t inLim; | |
const Byte *inData; | |
UInt64 inProgressPrev = me->mtc.inProcessed; | |
// XzDecMt_Prepare_InBuf_ST(p); | |
Byte *crossBuf = MtDec_GetCrossBuff(&me->mtc); | |
if (!crossBuf) | |
return SZ_ERROR_MEM; | |
inPos = 0; | |
inLim = 0; | |
// outProcessed = 0; | |
inData = crossBuf; | |
for (;;) | |
{ | |
SizeT inProcessed; | |
SizeT outProcessed; | |
ECoderStatus status; | |
SRes res; | |
if (inPos == inLim) | |
{ | |
if (!me->mtc.readWasFinished) | |
{ | |
inPos = 0; | |
inLim = me->mtc.inBufSize; | |
me->mtc.readRes = ISeqInStream_Read(me->inStream, (void *)inData, &inLim); | |
me->mtc.readProcessed += inLim; | |
if (inLim == 0 || me->mtc.readRes != SZ_OK) | |
me->mtc.readWasFinished = True; | |
} | |
} | |
inProcessed = inLim - inPos; | |
outProcessed = 0; | |
res = XzUnpacker_Code(dec, | |
NULL, &outProcessed, | |
inData + inPos, &inProcessed, | |
(inProcessed == 0), // srcFinished | |
CODER_FINISH_END, &status); | |
me->codeRes = res; | |
me->status = status; | |
inPos += inProcessed; | |
me->mtc.inProcessed += inProcessed; | |
me->mtc.mtProgress.totalInSize = me->mtc.inProcessed; | |
if (res != SZ_OK) | |
{ | |
return S_OK; | |
// return res; | |
} | |
if (dec->state == XZ_STATE_STREAM_HEADER) | |
{ | |
*needContinue = True; | |
me->mtc.crossStart = inPos; | |
me->mtc.crossEnd = inLim; | |
me->isBlockHeaderState_Parse = False; | |
me->isBlockHeaderState_Write = False; | |
return SZ_OK; | |
} | |
if (status != CODER_STATUS_NEEDS_MORE_INPUT) | |
return E_FAIL; | |
if (me->mtc.progress) | |
{ | |
UInt64 inDelta = me->mtc.inProcessed - inProgressPrev; | |
if (inDelta >= (1 << 22)) | |
{ | |
RINOK(MtProgress_Progress_ST(&me->mtc.mtProgress)); | |
inProgressPrev = me->mtc.inProcessed; | |
} | |
} | |
if (me->mtc.readWasFinished) | |
return SZ_OK; | |
} | |
} | |
} | |
} | |
#endif | |
void XzStatInfo_Clear(CXzStatInfo *p) | |
{ | |
p->InSize = 0; | |
p->OutSize = 0; | |
p->NumStreams = 0; | |
p->NumBlocks = 0; | |
p->UnpackSize_Defined = False; | |
p->NumStreams_Defined = False; | |
p->NumBlocks_Defined = False; | |
// p->IsArc = False; | |
// p->UnexpectedEnd = False; | |
// p->Unsupported = False; | |
// p->HeadersError = False; | |
// p->DataError = False; | |
// p->CrcError = False; | |
p->DataAfterEnd = False; | |
p->DecodingTruncated = False; | |
p->DecodeRes = SZ_OK; | |
p->ReadRes = SZ_OK; | |
p->ProgressRes = SZ_OK; | |
p->CombinedRes = SZ_OK; | |
p->CombinedRes_Type = SZ_OK; | |
} | |
static SRes XzDecMt_Decode_ST(CXzDecMt *p | |
#ifndef _7ZIP_ST | |
, BoolInt tMode | |
#endif | |
, CXzStatInfo *stat) | |
{ | |
size_t outPos; | |
size_t inPos, inLim; | |
const Byte *inData; | |
UInt64 inPrev, outPrev; | |
CXzUnpacker *dec; | |
#ifndef _7ZIP_ST | |
if (tMode) | |
{ | |
XzDecMt_FreeOutBufs(p); | |
tMode = MtDec_PrepareRead(&p->mtc); | |
} | |
#endif | |
if (!p->outBuf || p->outBufSize != p->props.outStep_ST) | |
{ | |
ISzAlloc_Free(p->allocMid, p->outBuf); | |
p->outBufSize = 0; | |
p->outBuf = (Byte *)ISzAlloc_Alloc(p->allocMid, p->props.outStep_ST); | |
if (!p->outBuf) | |
return SZ_ERROR_MEM; | |
p->outBufSize = p->props.outStep_ST; | |
} | |
if (!p->inBuf || p->inBufSize != p->props.inBufSize_ST) | |
{ | |
ISzAlloc_Free(p->allocMid, p->inBuf); | |
p->inBufSize = 0; | |
p->inBuf = (Byte *)ISzAlloc_Alloc(p->allocMid, p->props.inBufSize_ST); | |
if (!p->inBuf) | |
return SZ_ERROR_MEM; | |
p->inBufSize = p->props.inBufSize_ST; | |
} | |
dec = &p->dec; | |
dec->decodeToStreamSignature = False; | |
// dec->decodeOnlyOneBlock = False; | |
XzUnpacker_SetOutBuf(dec, NULL, 0); | |
inPrev = p->inProcessed; | |
outPrev = p->outProcessed; | |
inPos = 0; | |
inLim = 0; | |
inData = NULL; | |
outPos = 0; | |
for (;;) | |
{ | |
SizeT outSize; | |
BoolInt finished; | |
ECoderFinishMode finishMode; | |
SizeT inProcessed; | |
ECoderStatus status; | |
SRes res; | |
SizeT outProcessed; | |
if (inPos == inLim) | |
{ | |
#ifndef _7ZIP_ST | |
if (tMode) | |
{ | |
inData = MtDec_Read(&p->mtc, &inLim); | |
inPos = 0; | |
if (inData) | |
continue; | |
tMode = False; | |
inLim = 0; | |
} | |
#endif | |
if (!p->readWasFinished) | |
{ | |
inPos = 0; | |
inLim = p->inBufSize; | |
inData = p->inBuf; | |
p->readRes = ISeqInStream_Read(p->inStream, (void *)inData, &inLim); | |
p->readProcessed += inLim; | |
if (inLim == 0 || p->readRes != SZ_OK) | |
p->readWasFinished = True; | |
} | |
} | |
outSize = p->props.outStep_ST - outPos; | |
finishMode = CODER_FINISH_ANY; | |
if (p->outSize_Defined) | |
{ | |
const UInt64 rem = p->outSize - p->outProcessed; | |
if (outSize >= rem) | |
{ | |
outSize = (SizeT)rem; | |
if (p->finishMode) | |
finishMode = CODER_FINISH_END; | |
} | |
} | |
inProcessed = inLim - inPos; | |
outProcessed = outSize; | |
res = XzUnpacker_Code(dec, p->outBuf + outPos, &outProcessed, | |
inData + inPos, &inProcessed, | |
(inPos == inLim), // srcFinished | |
finishMode, &status); | |
p->codeRes = res; | |
p->status = status; | |
inPos += inProcessed; | |
outPos += outProcessed; | |
p->inProcessed += inProcessed; | |
p->outProcessed += outProcessed; | |
finished = ((inProcessed == 0 && outProcessed == 0) || res != SZ_OK); | |
if (finished || outProcessed >= outSize) | |
if (outPos != 0) | |
{ | |
size_t written = ISeqOutStream_Write(p->outStream, p->outBuf, outPos); | |
p->outProcessed += written; | |
if (written != outPos) | |
{ | |
stat->CombinedRes_Type = SZ_ERROR_WRITE; | |
return SZ_ERROR_WRITE; | |
} | |
outPos = 0; | |
} | |
if (p->progress && res == SZ_OK) | |
{ | |
UInt64 inDelta = p->inProcessed - inPrev; | |
UInt64 outDelta = p->outProcessed - outPrev; | |
if (inDelta >= (1 << 22) || outDelta >= (1 << 22)) | |
{ | |
res = ICompressProgress_Progress(p->progress, p->inProcessed, p->outProcessed); | |
if (res != SZ_OK) | |
{ | |
stat->CombinedRes_Type = SZ_ERROR_PROGRESS; | |
stat->ProgressRes = res; | |
return res; | |
} | |
inPrev = p->inProcessed; | |
outPrev = p->outProcessed; | |
} | |
} | |
if (finished) | |
return res; | |
} | |
} | |
static SRes XzStatInfo_SetStat(const CXzUnpacker *dec, | |
int finishMode, | |
UInt64 readProcessed, UInt64 inProcessed, | |
SRes res, ECoderStatus status, | |
BoolInt decodingTruncated, | |
CXzStatInfo *stat) | |
{ | |
UInt64 extraSize; | |
stat->DecodingTruncated = (Byte)(decodingTruncated ? 1 : 0); | |
stat->InSize = inProcessed; | |
stat->NumStreams = dec->numStartedStreams; | |
stat->NumBlocks = dec->numTotalBlocks; | |
stat->UnpackSize_Defined = True; | |
stat->NumStreams_Defined = True; | |
stat->NumBlocks_Defined = True; | |
extraSize = XzUnpacker_GetExtraSize(dec); | |
if (res == SZ_OK) | |
{ | |
if (status == CODER_STATUS_NEEDS_MORE_INPUT) | |
{ | |
// CODER_STATUS_NEEDS_MORE_INPUT is expected status for correct xz streams | |
extraSize = 0; | |
if (!XzUnpacker_IsStreamWasFinished(dec)) | |
res = SZ_ERROR_INPUT_EOF; | |
} | |
else if (!decodingTruncated || finishMode) // (status == CODER_STATUS_NOT_FINISHED) | |
res = SZ_ERROR_DATA; | |
} | |
else if (res == SZ_ERROR_NO_ARCHIVE) | |
{ | |
/* | |
SZ_ERROR_NO_ARCHIVE is possible for 2 states: | |
XZ_STATE_STREAM_HEADER - if bad signature or bad CRC | |
XZ_STATE_STREAM_PADDING - if non-zero padding data | |
extraSize / inProcessed don't include "bad" byte | |
*/ | |
if (inProcessed != extraSize) // if good streams before error | |
if (extraSize != 0 || readProcessed != inProcessed) | |
{ | |
stat->DataAfterEnd = True; | |
// there is some good xz stream before. So we set SZ_OK | |
res = SZ_OK; | |
} | |
} | |
stat->DecodeRes = res; | |
stat->InSize -= extraSize; | |
return res; | |
} | |
SRes XzDecMt_Decode(CXzDecMtHandle pp, | |
const CXzDecMtProps *props, | |
const UInt64 *outDataSize, int finishMode, | |
ISeqOutStream *outStream, | |
// Byte *outBuf, size_t *outBufSize, | |
ISeqInStream *inStream, | |
// const Byte *inData, size_t inDataSize, | |
CXzStatInfo *stat, | |
int *isMT, | |
ICompressProgress *progress) | |
{ | |
CXzDecMt *p = (CXzDecMt *)pp; | |
#ifndef _7ZIP_ST | |
BoolInt tMode; | |
#endif | |
XzStatInfo_Clear(stat); | |
p->props = *props; | |
p->inStream = inStream; | |
p->outStream = outStream; | |
p->progress = progress; | |
// p->stat = stat; | |
p->outSize = 0; | |
p->outSize_Defined = False; | |
if (outDataSize) | |
{ | |
p->outSize_Defined = True; | |
p->outSize = *outDataSize; | |
} | |
p->finishMode = finishMode; | |
// p->outSize = 457; p->outSize_Defined = True; p->finishMode = False; // for test | |
p->writeRes = SZ_OK; | |
p->outProcessed = 0; | |
p->inProcessed = 0; | |
p->readProcessed = 0; | |
p->readWasFinished = False; | |
p->codeRes = 0; | |
p->status = CODER_STATUS_NOT_SPECIFIED; | |
XzUnpacker_Init(&p->dec); | |
*isMT = False; | |
/* | |
p->outBuf = NULL; | |
p->outBufSize = 0; | |
if (!outStream) | |
{ | |
p->outBuf = outBuf; | |
p->outBufSize = *outBufSize; | |
*outBufSize = 0; | |
} | |
*/ | |
#ifndef _7ZIP_ST | |
p->isBlockHeaderState_Parse = False; | |
p->isBlockHeaderState_Write = False; | |
// p->numBadBlocks = 0; | |
p->mainErrorCode = SZ_OK; | |
p->mainDecoderWasCalled = False; | |
tMode = False; | |
if (p->props.numThreads > 1) | |
{ | |
IMtDecCallback vt; | |
// we just free ST buffers here | |
// but we still keep state variables, that was set in XzUnpacker_Init() | |
XzDecMt_FreeSt(p); | |
p->outProcessed_Parse = 0; | |
p->parsing_Truncated = False; | |
p->numStreams = 0; | |
p->numTotalBlocks = 0; | |
p->numBlocks = 0; | |
p->finishedDecoderIndex = -1; | |
if (!p->mtc_WasConstructed) | |
{ | |
p->mtc_WasConstructed = True; | |
MtDec_Construct(&p->mtc); | |
} | |
p->mtc.mtCallback = &vt; | |
p->mtc.mtCallbackObject = p; | |
p->mtc.progress = progress; | |
p->mtc.inStream = inStream; | |
p->mtc.alloc = &p->alignOffsetAlloc.vt; | |
// p->mtc.inData = inData; | |
// p->mtc.inDataSize = inDataSize; | |
p->mtc.inBufSize = p->props.inBufSize_MT; | |
// p->mtc.inBlockMax = p->props.inBlockMax; | |
p->mtc.numThreadsMax = p->props.numThreads; | |
*isMT = True; | |
vt.Parse = XzDecMt_Callback_Parse; | |
vt.PreCode = XzDecMt_Callback_PreCode; | |
vt.Code = XzDecMt_Callback_Code; | |
vt.Write = XzDecMt_Callback_Write; | |
{ | |
BoolInt needContinue; | |
SRes res = MtDec_Code(&p->mtc); | |
stat->InSize = p->mtc.inProcessed; | |
p->inProcessed = p->mtc.inProcessed; | |
p->readRes = p->mtc.readRes; | |
p->readWasFinished = p->mtc.readWasFinished; | |
p->readProcessed = p->mtc.readProcessed; | |
tMode = True; | |
needContinue = False; | |
if (res == SZ_OK) | |
{ | |
if (p->mtc.mtProgress.res != SZ_OK) | |
{ | |
res = p->mtc.mtProgress.res; | |
stat->ProgressRes = res; | |
stat->CombinedRes_Type = SZ_ERROR_PROGRESS; | |
} | |
else | |
needContinue = p->mtc.needContinue; | |
} | |
if (!needContinue) | |
{ | |
SRes codeRes; | |
BoolInt truncated = False; | |
ECoderStatus status; | |
CXzUnpacker *dec; | |
stat->OutSize = p->outProcessed; | |
if (p->finishedDecoderIndex >= 0) | |
{ | |
CXzDecMtThread *coder = &p->coders[(unsigned)p->finishedDecoderIndex]; | |
codeRes = coder->codeRes; | |
dec = &coder->dec; | |
status = coder->status; | |
} | |
else if (p->mainDecoderWasCalled) | |
{ | |
codeRes = p->codeRes; | |
dec = &p->dec; | |
status = p->status; | |
truncated = p->parsing_Truncated; | |
} | |
else | |
return E_FAIL; | |
XzStatInfo_SetStat(dec, p->finishMode, | |
p->mtc.readProcessed, p->mtc.inProcessed, | |
codeRes, status, | |
truncated, | |
stat); | |
if (res == SZ_OK) | |
{ | |
if (p->writeRes != SZ_OK) | |
{ | |
res = p->writeRes; | |
stat->CombinedRes_Type = SZ_ERROR_WRITE; | |
} | |
else if (p->mtc.readRes != SZ_OK && p->mtc.inProcessed == p->mtc.readProcessed) | |
{ | |
res = p->mtc.readRes; | |
stat->ReadRes = res; | |
stat->CombinedRes_Type = SZ_ERROR_READ; | |
} | |
else if (p->mainErrorCode != SZ_OK) | |
{ | |
res = p->mainErrorCode; | |
} | |
} | |
stat->CombinedRes = res; | |
if (stat->CombinedRes_Type == SZ_OK) | |
stat->CombinedRes_Type = res; | |
return res; | |
} | |
PRF_STR("----- decoding ST -----"); | |
} | |
} | |
#endif | |
*isMT = False; | |
{ | |
SRes res = XzDecMt_Decode_ST(p | |
#ifndef _7ZIP_ST | |
, tMode | |
#endif | |
, stat | |
); | |
XzStatInfo_SetStat(&p->dec, | |
p->finishMode, | |
p->readProcessed, p->inProcessed, | |
p->codeRes, p->status, | |
False, // truncated | |
stat); | |
if (res == SZ_OK) | |
{ | |
/* | |
if (p->writeRes != SZ_OK) | |
{ | |
res = p->writeRes; | |
stat->CombinedRes_Type = SZ_ERROR_WRITE; | |
} | |
else | |
*/ | |
if (p->readRes != SZ_OK && p->inProcessed == p->readProcessed) | |
{ | |
res = p->readRes; | |
stat->ReadRes = res; | |
stat->CombinedRes_Type = SZ_ERROR_READ; | |
} | |
#ifndef _7ZIP_ST | |
else if (p->mainErrorCode != SZ_OK) | |
res = p->mainErrorCode; | |
#endif | |
} | |
stat->CombinedRes = res; | |
if (stat->CombinedRes_Type == SZ_OK) | |
stat->CombinedRes_Type = res; | |
return res; | |
} | |
} |