| /* XzDec.c -- Xz Decode |
| 2019-02-02 : 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 = (CBraState *)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 = (ECoderStatus)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; |
| } |
| } |