Files
TencentOS-tiny/components/ota/common/lzma/3rdparty/XzEnc.c
daishengdong 5b51d50ade add ota algorithm for device
1. effective "Differential Upgrade" patch algorithm with high compression rate
2. effective recovery algorithm support recovery firmware in blocks which has low memory consumption and wear-leveling strategies, especially suitable for embeded devices with low RAM.
3. add sample ota bootloader project, see:
board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_bootloader_recovery
4. add sample application project for download firmware through http, see:
board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_application_download_through_http
5. add sample application project for download firmware through qcloud explorer console, see:
board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_application_download_through_qcloud_iot_explorer
6. an OTA markdown document is pending
2020-06-02 15:03:42 +08:00

1330 lines
31 KiB
C

/* XzEnc.c -- Xz Encode
2019-02-02 : Igor Pavlov : Public domain */
#include "Precomp.h"
#include <stdlib.h>
#include <string.h>
#include "7zCrc.h"
#include "Bra.h"
#include "CpuArch.h"
#ifdef USE_SUBBLOCK
#include "Bcj3Enc.c"
#include "SbFind.c"
#include "SbEnc.c"
#endif
#include "XzEnc.h"
#define _7ZIP_ST
#ifndef _7ZIP_ST
#include "MtCoder.h"
#else
#define MTCODER__THREADS_MAX 1
#define MTCODER__BLOCKS_MAX 1
#endif
#define XZ_GET_PAD_SIZE(dataSize) ((4 - ((unsigned)(dataSize) & 3)) & 3)
/* max pack size for LZMA2 block + check-64bytrs: */
#define XZ_GET_MAX_BLOCK_PACK_SIZE(unpackSize) ((unpackSize) + ((unpackSize) >> 10) + 16 + 64)
#define XZ_GET_ESTIMATED_BLOCK_TOTAL_PACK_SIZE(unpackSize) (XZ_BLOCK_HEADER_SIZE_MAX + XZ_GET_MAX_BLOCK_PACK_SIZE(unpackSize))
#define XzBlock_ClearFlags(p) (p)->flags = 0;
#define XzBlock_SetNumFilters(p, n) (p)->flags |= ((n) - 1);
#define XzBlock_SetHasPackSize(p) (p)->flags |= XZ_BF_PACK_SIZE;
#define XzBlock_SetHasUnpackSize(p) (p)->flags |= XZ_BF_UNPACK_SIZE;
static SRes WriteBytes(ISeqOutStream *s, const void *buf, size_t size)
{
return (ISeqOutStream_Write(s, buf, size) == size) ? SZ_OK : SZ_ERROR_WRITE;
}
static SRes WriteBytesUpdateCrc(ISeqOutStream *s, const void *buf, size_t size, UInt32 *crc)
{
*crc = CrcUpdate(*crc, buf, size);
return WriteBytes(s, buf, size);
}
static SRes Xz_WriteHeader(CXzStreamFlags f, ISeqOutStream *s)
{
UInt32 crc;
Byte header[XZ_STREAM_HEADER_SIZE];
memcpy(header, XZ_SIG, XZ_SIG_SIZE);
header[XZ_SIG_SIZE] = (Byte)(f >> 8);
header[XZ_SIG_SIZE + 1] = (Byte)(f & 0xFF);
crc = CrcCalc(header + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE);
SetUi32(header + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE, crc);
return WriteBytes(s, header, XZ_STREAM_HEADER_SIZE);
}
static SRes XzBlock_WriteHeader(const CXzBlock *p, ISeqOutStream *s)
{
Byte header[XZ_BLOCK_HEADER_SIZE_MAX];
unsigned pos = 1;
unsigned numFilters, i;
header[pos++] = p->flags;
if (XzBlock_HasPackSize(p)) pos += Xz_WriteVarInt(header + pos, p->packSize);
if (XzBlock_HasUnpackSize(p)) pos += Xz_WriteVarInt(header + pos, p->unpackSize);
numFilters = XzBlock_GetNumFilters(p);
for (i = 0; i < numFilters; i++)
{
const CXzFilter *f = &p->filters[i];
pos += Xz_WriteVarInt(header + pos, f->id);
pos += Xz_WriteVarInt(header + pos, f->propsSize);
memcpy(header + pos, f->props, f->propsSize);
pos += f->propsSize;
}
while ((pos & 3) != 0)
header[pos++] = 0;
header[0] = (Byte)(pos >> 2);
SetUi32(header + pos, CrcCalc(header, pos));
return WriteBytes(s, header, pos + 4);
}
typedef struct
{
size_t numBlocks;
size_t size;
size_t allocated;
Byte *blocks;
} CXzEncIndex;
static void XzEncIndex_Construct(CXzEncIndex *p)
{
p->numBlocks = 0;
p->size = 0;
p->allocated = 0;
p->blocks = NULL;
}
static void XzEncIndex_Init(CXzEncIndex *p)
{
p->numBlocks = 0;
p->size = 0;
}
static void XzEncIndex_Free(CXzEncIndex *p, ISzAllocPtr alloc)
{
if (p->blocks)
{
ISzAlloc_Free(alloc, p->blocks);
p->blocks = NULL;
}
p->numBlocks = 0;
p->size = 0;
p->allocated = 0;
}
static SRes XzEncIndex_ReAlloc(CXzEncIndex *p, size_t newSize, ISzAllocPtr alloc)
{
Byte *blocks = (Byte *)ISzAlloc_Alloc(alloc, newSize);
if (!blocks)
return SZ_ERROR_MEM;
if (p->size != 0)
memcpy(blocks, p->blocks, p->size);
if (p->blocks)
ISzAlloc_Free(alloc, p->blocks);
p->blocks = blocks;
p->allocated = newSize;
return SZ_OK;
}
static SRes XzEncIndex_PreAlloc(CXzEncIndex *p, UInt64 numBlocks, UInt64 unpackSize, UInt64 totalSize, ISzAllocPtr alloc)
{
UInt64 pos;
{
Byte buf[32];
unsigned pos2 = Xz_WriteVarInt(buf, totalSize);
pos2 += Xz_WriteVarInt(buf + pos2, unpackSize);
pos = numBlocks * pos2;
}
if (pos <= p->allocated - p->size)
return SZ_OK;
{
UInt64 newSize64 = p->size + pos;
size_t newSize = (size_t)newSize64;
if (newSize != newSize64)
return SZ_ERROR_MEM;
return XzEncIndex_ReAlloc(p, newSize, alloc);
}
}
static SRes XzEncIndex_AddIndexRecord(CXzEncIndex *p, UInt64 unpackSize, UInt64 totalSize, ISzAllocPtr alloc)
{
Byte buf[32];
unsigned pos = Xz_WriteVarInt(buf, totalSize);
pos += Xz_WriteVarInt(buf + pos, unpackSize);
if (pos > p->allocated - p->size)
{
size_t newSize = p->allocated * 2 + 16 * 2;
if (newSize < p->size + pos)
return SZ_ERROR_MEM;
RINOK(XzEncIndex_ReAlloc(p, newSize, alloc));
}
memcpy(p->blocks + p->size, buf, pos);
p->size += pos;
p->numBlocks++;
return SZ_OK;
}
static SRes XzEncIndex_WriteFooter(const CXzEncIndex *p, CXzStreamFlags flags, ISeqOutStream *s)
{
Byte buf[32];
UInt64 globalPos;
UInt32 crc = CRC_INIT_VAL;
unsigned pos = 1 + Xz_WriteVarInt(buf + 1, p->numBlocks);
globalPos = pos;
buf[0] = 0;
RINOK(WriteBytesUpdateCrc(s, buf, pos, &crc));
RINOK(WriteBytesUpdateCrc(s, p->blocks, p->size, &crc));
globalPos += p->size;
pos = XZ_GET_PAD_SIZE(globalPos);
buf[1] = 0;
buf[2] = 0;
buf[3] = 0;
globalPos += pos;
crc = CrcUpdate(crc, buf + 4 - pos, pos);
SetUi32(buf + 4, CRC_GET_DIGEST(crc));
SetUi32(buf + 8 + 4, (UInt32)(globalPos >> 2));
buf[8 + 8] = (Byte)(flags >> 8);
buf[8 + 9] = (Byte)(flags & 0xFF);
SetUi32(buf + 8, CrcCalc(buf + 8 + 4, 6));
buf[8 + 10] = XZ_FOOTER_SIG_0;
buf[8 + 11] = XZ_FOOTER_SIG_1;
return WriteBytes(s, buf + 4 - pos, pos + 4 + 12);
}
/* ---------- CSeqCheckInStream ---------- */
typedef struct
{
ISeqInStream vt;
ISeqInStream *realStream;
const Byte *data;
UInt64 limit;
UInt64 processed;
int realStreamFinished;
CXzCheck check;
} CSeqCheckInStream;
static void SeqCheckInStream_Init(CSeqCheckInStream *p, unsigned checkMode)
{
p->limit = (UInt64)(Int64)-1;
p->processed = 0;
p->realStreamFinished = 0;
XzCheck_Init(&p->check, checkMode);
}
static void SeqCheckInStream_GetDigest(CSeqCheckInStream *p, Byte *digest)
{
XzCheck_Final(&p->check, digest);
}
static SRes SeqCheckInStream_Read(const ISeqInStream *pp, void *data, size_t *size)
{
CSeqCheckInStream *p = CONTAINER_FROM_VTBL(pp, CSeqCheckInStream, vt);
size_t size2 = *size;
SRes res = SZ_OK;
if (p->limit != (UInt64)(Int64)-1)
{
UInt64 rem = p->limit - p->processed;
if (size2 > rem)
size2 = (size_t)rem;
}
if (size2 != 0)
{
if (p->realStream)
{
res = ISeqInStream_Read(p->realStream, data, &size2);
p->realStreamFinished = (size2 == 0) ? 1 : 0;
}
else
memcpy(data, p->data + (size_t)p->processed, size2);
XzCheck_Update(&p->check, data, size2);
p->processed += size2;
}
*size = size2;
return res;
}
/* ---------- CSeqSizeOutStream ---------- */
typedef struct
{
ISeqOutStream vt;
ISeqOutStream *realStream;
Byte *outBuf;
size_t outBufLimit;
UInt64 processed;
} CSeqSizeOutStream;
static size_t SeqSizeOutStream_Write(const ISeqOutStream *pp, const void *data, size_t size)
{
CSeqSizeOutStream *p = CONTAINER_FROM_VTBL(pp, CSeqSizeOutStream, vt);
if (p->realStream)
size = ISeqOutStream_Write(p->realStream, data, size);
else
{
if (size > p->outBufLimit - (size_t)p->processed)
return 0;
memcpy(p->outBuf + (size_t)p->processed, data, size);
}
p->processed += size;
return size;
}
/* ---------- CSeqInFilter ---------- */
#define FILTER_BUF_SIZE (1 << 20)
typedef struct
{
ISeqInStream p;
ISeqInStream *realStream;
IStateCoder StateCoder;
Byte *buf;
size_t curPos;
size_t endPos;
int srcWasFinished;
} CSeqInFilter;
SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAllocPtr alloc);
static SRes SeqInFilter_Init(CSeqInFilter *p, const CXzFilter *props, ISzAllocPtr alloc)
{
if (!p->buf)
{
p->buf = (Byte *)ISzAlloc_Alloc(alloc, FILTER_BUF_SIZE);
if (!p->buf)
return SZ_ERROR_MEM;
}
p->curPos = p->endPos = 0;
p->srcWasFinished = 0;
RINOK(BraState_SetFromMethod(&p->StateCoder, props->id, 1, alloc));
RINOK(p->StateCoder.SetProps(p->StateCoder.p, props->props, props->propsSize, alloc));
p->StateCoder.Init(p->StateCoder.p);
return SZ_OK;
}
static SRes SeqInFilter_Read(const ISeqInStream *pp, void *data, size_t *size)
{
CSeqInFilter *p = CONTAINER_FROM_VTBL(pp, CSeqInFilter, p);
size_t sizeOriginal = *size;
if (sizeOriginal == 0)
return SZ_OK;
*size = 0;
for (;;)
{
if (!p->srcWasFinished && p->curPos == p->endPos)
{
p->curPos = 0;
p->endPos = FILTER_BUF_SIZE;
RINOK(ISeqInStream_Read(p->realStream, p->buf, &p->endPos));
if (p->endPos == 0)
p->srcWasFinished = 1;
}
{
SizeT srcLen = p->endPos - p->curPos;
ECoderStatus status;
SRes res;
*size = sizeOriginal;
res = p->StateCoder.Code2(p->StateCoder.p,
(Byte *)data, size,
p->buf + p->curPos, &srcLen,
p->srcWasFinished, CODER_FINISH_ANY,
&status);
p->curPos += srcLen;
if (*size != 0 || srcLen == 0 || res != SZ_OK)
return res;
}
}
}
static void SeqInFilter_Construct(CSeqInFilter *p)
{
p->buf = NULL;
p->StateCoder.p = NULL;
p->p.Read = SeqInFilter_Read;
}
static void SeqInFilter_Free(CSeqInFilter *p, ISzAllocPtr alloc)
{
if (p->StateCoder.p)
{
p->StateCoder.Free(p->StateCoder.p, alloc);
p->StateCoder.p = NULL;
}
if (p->buf)
{
ISzAlloc_Free(alloc, p->buf);
p->buf = NULL;
}
}
/* ---------- CSbEncInStream ---------- */
#ifdef USE_SUBBLOCK
typedef struct
{
ISeqInStream vt;
ISeqInStream *inStream;
CSbEnc enc;
} CSbEncInStream;
static SRes SbEncInStream_Read(const ISeqInStream *pp, void *data, size_t *size)
{
CSbEncInStream *p = CONTAINER_FROM_VTBL(pp, CSbEncInStream, vt);
size_t sizeOriginal = *size;
if (sizeOriginal == 0)
return SZ_OK;
for (;;)
{
if (p->enc.needRead && !p->enc.readWasFinished)
{
size_t processed = p->enc.needReadSizeMax;
RINOK(p->inStream->Read(p->inStream, p->enc.buf + p->enc.readPos, &processed));
p->enc.readPos += processed;
if (processed == 0)
{
p->enc.readWasFinished = True;
p->enc.isFinalFinished = True;
}
p->enc.needRead = False;
}
*size = sizeOriginal;
RINOK(SbEnc_Read(&p->enc, data, size));
if (*size != 0 || !p->enc.needRead)
return SZ_OK;
}
}
void SbEncInStream_Construct(CSbEncInStream *p, ISzAllocPtr alloc)
{
SbEnc_Construct(&p->enc, alloc);
p->vt.Read = SbEncInStream_Read;
}
SRes SbEncInStream_Init(CSbEncInStream *p)
{
return SbEnc_Init(&p->enc);
}
void SbEncInStream_Free(CSbEncInStream *p)
{
SbEnc_Free(&p->enc);
}
#endif
/* ---------- CXzProps ---------- */
void XzFilterProps_Init(CXzFilterProps *p)
{
p->id = 0;
p->delta = 0;
p->ip = 0;
p->ipDefined = False;
}
void XzProps_Init(CXzProps *p)
{
p->checkId = XZ_CHECK_CRC32;
p->blockSize = XZ_PROPS__BLOCK_SIZE__AUTO;
p->numBlockThreads_Reduced = -1;
p->numBlockThreads_Max = -1;
p->numTotalThreads = -1;
p->reduceSize = (UInt64)(Int64)-1;
p->forceWriteSizesInHeader = 0;
// p->forceWriteSizesInHeader = 1;
XzFilterProps_Init(&p->filterProps);
Lzma2EncProps_Init(&p->lzma2Props);
}
static void XzEncProps_Normalize_Fixed(CXzProps *p)
{
UInt64 fileSize;
int t1, t1n, t2, t2r, t3;
{
CLzma2EncProps tp = p->lzma2Props;
if (tp.numTotalThreads <= 0)
tp.numTotalThreads = p->numTotalThreads;
Lzma2EncProps_Normalize(&tp);
t1n = tp.numTotalThreads;
}
t1 = p->lzma2Props.numTotalThreads;
t2 = p->numBlockThreads_Max;
t3 = p->numTotalThreads;
if (t2 > MTCODER__THREADS_MAX)
t2 = MTCODER__THREADS_MAX;
if (t3 <= 0)
{
if (t2 <= 0)
t2 = 1;
t3 = t1n * t2;
}
else if (t2 <= 0)
{
t2 = t3 / t1n;
if (t2 == 0)
{
t1 = 1;
t2 = t3;
}
if (t2 > MTCODER__THREADS_MAX)
t2 = MTCODER__THREADS_MAX;
}
else if (t1 <= 0)
{
t1 = t3 / t2;
if (t1 == 0)
t1 = 1;
}
else
t3 = t1n * t2;
p->lzma2Props.numTotalThreads = t1;
t2r = t2;
fileSize = p->reduceSize;
if ((p->blockSize < fileSize || fileSize == (UInt64)(Int64)-1))
p->lzma2Props.lzmaProps.reduceSize = p->blockSize;
Lzma2EncProps_Normalize(&p->lzma2Props);
t1 = p->lzma2Props.numTotalThreads;
{
if (t2 > 1 && fileSize != (UInt64)(Int64)-1)
{
UInt64 numBlocks = fileSize / p->blockSize;
if (numBlocks * p->blockSize != fileSize)
numBlocks++;
if (numBlocks < (unsigned)t2)
{
t2r = (unsigned)numBlocks;
if (t2r == 0)
t2r = 1;
t3 = t1 * t2r;
}
}
}
p->numBlockThreads_Max = t2;
p->numBlockThreads_Reduced = t2r;
p->numTotalThreads = t3;
}
static void XzProps_Normalize(CXzProps *p)
{
/* we normalize xzProps properties, but we normalize only some of CXzProps::lzma2Props properties.
Lzma2Enc_SetProps() will normalize lzma2Props later. */
if (p->blockSize == XZ_PROPS__BLOCK_SIZE__SOLID)
{
p->lzma2Props.lzmaProps.reduceSize = p->reduceSize;
p->numBlockThreads_Reduced = 1;
p->numBlockThreads_Max = 1;
if (p->lzma2Props.numTotalThreads <= 0)
p->lzma2Props.numTotalThreads = p->numTotalThreads;
return;
}
else
{
CLzma2EncProps *lzma2 = &p->lzma2Props;
if (p->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO)
{
// xz-auto
p->lzma2Props.lzmaProps.reduceSize = p->reduceSize;
if (lzma2->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID)
{
// if (xz-auto && lzma2-solid) - we use solid for both
p->blockSize = XZ_PROPS__BLOCK_SIZE__SOLID;
p->numBlockThreads_Reduced = 1;
p->numBlockThreads_Max = 1;
if (p->lzma2Props.numTotalThreads <= 0)
p->lzma2Props.numTotalThreads = p->numTotalThreads;
}
else
{
// if (xz-auto && (lzma2-auto || lzma2-fixed_)
// we calculate block size for lzma2 and use that block size for xz, lzma2 uses single-chunk per block
CLzma2EncProps tp = p->lzma2Props;
if (tp.numTotalThreads <= 0)
tp.numTotalThreads = p->numTotalThreads;
Lzma2EncProps_Normalize(&tp);
p->blockSize = tp.blockSize; // fixed or solid
p->numBlockThreads_Reduced = tp.numBlockThreads_Reduced;
p->numBlockThreads_Max = tp.numBlockThreads_Max;
if (lzma2->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO)
lzma2->blockSize = tp.blockSize; // fixed or solid, LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID
if (lzma2->lzmaProps.reduceSize > tp.blockSize && tp.blockSize != LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID)
lzma2->lzmaProps.reduceSize = tp.blockSize;
lzma2->numBlockThreads_Reduced = 1;
lzma2->numBlockThreads_Max = 1;
return;
}
}
else
{
// xz-fixed
// we can use xz::reduceSize or xz::blockSize as base for lzmaProps::reduceSize
p->lzma2Props.lzmaProps.reduceSize = p->reduceSize;
{
UInt64 r = p->reduceSize;
if (r > p->blockSize || r == (UInt64)(Int64)-1)
r = p->blockSize;
lzma2->lzmaProps.reduceSize = r;
}
if (lzma2->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO)
lzma2->blockSize = LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID;
else if (lzma2->blockSize > p->blockSize && lzma2->blockSize != LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID)
lzma2->blockSize = p->blockSize;
XzEncProps_Normalize_Fixed(p);
}
}
}
/* ---------- CLzma2WithFilters ---------- */
typedef struct
{
CLzma2EncHandle lzma2;
CSeqInFilter filter;
#ifdef USE_SUBBLOCK
CSbEncInStream sb;
#endif
} CLzma2WithFilters;
static void Lzma2WithFilters_Construct(CLzma2WithFilters *p)
{
p->lzma2 = NULL;
SeqInFilter_Construct(&p->filter);
#ifdef USE_SUBBLOCK
SbEncInStream_Construct(&p->sb, alloc);
#endif
}
static SRes Lzma2WithFilters_Create(CLzma2WithFilters *p, ISzAllocPtr alloc, ISzAllocPtr bigAlloc)
{
if (!p->lzma2)
{
p->lzma2 = Lzma2Enc_Create(alloc, bigAlloc);
if (!p->lzma2)
return SZ_ERROR_MEM;
}
return SZ_OK;
}
static void Lzma2WithFilters_Free(CLzma2WithFilters *p, ISzAllocPtr alloc)
{
#ifdef USE_SUBBLOCK
SbEncInStream_Free(&p->sb);
#endif
SeqInFilter_Free(&p->filter, alloc);
if (p->lzma2)
{
Lzma2Enc_Destroy(p->lzma2);
p->lzma2 = NULL;
}
}
typedef struct
{
UInt64 unpackSize;
UInt64 totalSize;
size_t headerSize;
} CXzEncBlockInfo;
static SRes Xz_CompressBlock(
CLzma2WithFilters *lzmaf,
ISeqOutStream *outStream,
Byte *outBufHeader,
Byte *outBufData, size_t outBufDataLimit,
ISeqInStream *inStream,
// UInt64 expectedSize,
const Byte *inBuf, // used if (!inStream)
size_t inBufSize, // used if (!inStream), it's block size, props->blockSize is ignored
const CXzProps *props,
ICompressProgress *progress,
int *inStreamFinished, /* only for inStream version */
CXzEncBlockInfo *blockSizes,
ISzAllocPtr alloc,
ISzAllocPtr allocBig)
{
CSeqCheckInStream checkInStream;
CSeqSizeOutStream seqSizeOutStream;
CXzBlock block;
unsigned filterIndex = 0;
CXzFilter *filter = NULL;
const CXzFilterProps *fp = &props->filterProps;
if (fp->id == 0)
fp = NULL;
*inStreamFinished = False;
RINOK(Lzma2WithFilters_Create(lzmaf, alloc, allocBig));
RINOK(Lzma2Enc_SetProps(lzmaf->lzma2, &props->lzma2Props));
XzBlock_ClearFlags(&block);
XzBlock_SetNumFilters(&block, 1 + (fp ? 1 : 0));
if (fp)
{
filter = &block.filters[filterIndex++];
filter->id = fp->id;
filter->propsSize = 0;
if (fp->id == XZ_ID_Delta)
{
filter->props[0] = (Byte)(fp->delta - 1);
filter->propsSize = 1;
}
else if (fp->ipDefined)
{
SetUi32(filter->props, fp->ip);
filter->propsSize = 4;
}
}
{
CXzFilter *f = &block.filters[filterIndex++];
f->id = XZ_ID_LZMA2;
f->propsSize = 1;
f->props[0] = Lzma2Enc_WriteProperties(lzmaf->lzma2);
}
seqSizeOutStream.vt.Write = SeqSizeOutStream_Write;
seqSizeOutStream.realStream = outStream;
seqSizeOutStream.outBuf = outBufData;
seqSizeOutStream.outBufLimit = outBufDataLimit;
seqSizeOutStream.processed = 0;
/*
if (expectedSize != (UInt64)(Int64)-1)
{
block.unpackSize = expectedSize;
if (props->blockSize != (UInt64)(Int64)-1)
if (expectedSize > props->blockSize)
block.unpackSize = props->blockSize;
XzBlock_SetHasUnpackSize(&block);
}
*/
if (outStream)
{
RINOK(XzBlock_WriteHeader(&block, &seqSizeOutStream.vt));
}
checkInStream.vt.Read = SeqCheckInStream_Read;
SeqCheckInStream_Init(&checkInStream, props->checkId);
checkInStream.realStream = inStream;
checkInStream.data = inBuf;
checkInStream.limit = props->blockSize;
if (!inStream)
checkInStream.limit = inBufSize;
if (fp)
{
#ifdef USE_SUBBLOCK
if (fp->id == XZ_ID_Subblock)
{
lzmaf->sb.inStream = &checkInStream.vt;
RINOK(SbEncInStream_Init(&lzmaf->sb));
}
else
#endif
{
lzmaf->filter.realStream = &checkInStream.vt;
RINOK(SeqInFilter_Init(&lzmaf->filter, filter, alloc));
}
}
{
SRes res;
Byte *outBuf = NULL;
size_t outSize = 0;
BoolInt useStream = (fp || inStream);
// useStream = True;
if (!useStream)
{
XzCheck_Update(&checkInStream.check, inBuf, inBufSize);
checkInStream.processed = inBufSize;
}
if (!outStream)
{
outBuf = seqSizeOutStream.outBuf; // + (size_t)seqSizeOutStream.processed;
outSize = seqSizeOutStream.outBufLimit; // - (size_t)seqSizeOutStream.processed;
}
res = Lzma2Enc_Encode2(lzmaf->lzma2,
outBuf ? NULL : &seqSizeOutStream.vt,
outBuf,
outBuf ? &outSize : NULL,
useStream ?
(fp ?
(
#ifdef USE_SUBBLOCK
(fp->id == XZ_ID_Subblock) ? &lzmaf->sb.vt:
#endif
&lzmaf->filter.p) :
&checkInStream.vt) : NULL,
useStream ? NULL : inBuf,
useStream ? 0 : inBufSize,
progress);
if (outBuf)
seqSizeOutStream.processed += outSize;
RINOK(res);
blockSizes->unpackSize = checkInStream.processed;
}
{
Byte buf[4 + 64];
unsigned padSize = XZ_GET_PAD_SIZE(seqSizeOutStream.processed);
UInt64 packSize = seqSizeOutStream.processed;
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
buf[3] = 0;
SeqCheckInStream_GetDigest(&checkInStream, buf + 4);
RINOK(WriteBytes(&seqSizeOutStream.vt, buf + (4 - padSize), padSize + XzFlags_GetCheckSize((CXzStreamFlags)props->checkId)));
blockSizes->totalSize = seqSizeOutStream.processed - padSize;
if (!outStream)
{
seqSizeOutStream.outBuf = outBufHeader;
seqSizeOutStream.outBufLimit = XZ_BLOCK_HEADER_SIZE_MAX;
seqSizeOutStream.processed = 0;
block.unpackSize = blockSizes->unpackSize;
XzBlock_SetHasUnpackSize(&block);
block.packSize = packSize;
XzBlock_SetHasPackSize(&block);
RINOK(XzBlock_WriteHeader(&block, &seqSizeOutStream.vt));
blockSizes->headerSize = (size_t)seqSizeOutStream.processed;
blockSizes->totalSize += seqSizeOutStream.processed;
}
}
if (inStream)
*inStreamFinished = checkInStream.realStreamFinished;
else
{
*inStreamFinished = False;
if (checkInStream.processed != inBufSize)
return SZ_ERROR_FAIL;
}
return SZ_OK;
}
typedef struct
{
ICompressProgress vt;
ICompressProgress *progress;
UInt64 inOffset;
UInt64 outOffset;
} CCompressProgress_XzEncOffset;
static SRes CompressProgress_XzEncOffset_Progress(const ICompressProgress *pp, UInt64 inSize, UInt64 outSize)
{
const CCompressProgress_XzEncOffset *p = CONTAINER_FROM_VTBL(pp, CCompressProgress_XzEncOffset, vt);
inSize += p->inOffset;
outSize += p->outOffset;
return ICompressProgress_Progress(p->progress, inSize, outSize);
}
typedef struct
{
ISzAllocPtr alloc;
ISzAllocPtr allocBig;
CXzProps xzProps;
UInt64 expectedDataSize;
CXzEncIndex xzIndex;
CLzma2WithFilters lzmaf_Items[MTCODER__THREADS_MAX];
size_t outBufSize; /* size of allocated outBufs[i] */
Byte *outBufs[MTCODER__BLOCKS_MAX];
#ifndef _7ZIP_ST
unsigned checkType;
ISeqOutStream *outStream;
BoolInt mtCoder_WasConstructed;
CMtCoder mtCoder;
CXzEncBlockInfo EncBlocks[MTCODER__BLOCKS_MAX];
#endif
} CXzEnc;
static void XzEnc_Construct(CXzEnc *p)
{
unsigned i;
XzEncIndex_Construct(&p->xzIndex);
for (i = 0; i < MTCODER__THREADS_MAX; i++)
Lzma2WithFilters_Construct(&p->lzmaf_Items[i]);
#ifndef _7ZIP_ST
p->mtCoder_WasConstructed = False;
{
for (i = 0; i < MTCODER__BLOCKS_MAX; i++)
p->outBufs[i] = NULL;
p->outBufSize = 0;
}
#endif
}
static void XzEnc_FreeOutBufs(CXzEnc *p)
{
unsigned i;
for (i = 0; i < MTCODER__BLOCKS_MAX; i++)
if (p->outBufs[i])
{
ISzAlloc_Free(p->alloc, p->outBufs[i]);
p->outBufs[i] = NULL;
}
p->outBufSize = 0;
}
static void XzEnc_Free(CXzEnc *p, ISzAllocPtr alloc)
{
unsigned i;
XzEncIndex_Free(&p->xzIndex, alloc);
for (i = 0; i < MTCODER__THREADS_MAX; i++)
Lzma2WithFilters_Free(&p->lzmaf_Items[i], alloc);
#ifndef _7ZIP_ST
if (p->mtCoder_WasConstructed)
{
MtCoder_Destruct(&p->mtCoder);
p->mtCoder_WasConstructed = False;
}
XzEnc_FreeOutBufs(p);
#endif
}
CXzEncHandle XzEnc_Create(ISzAllocPtr alloc, ISzAllocPtr allocBig)
{
CXzEnc *p = (CXzEnc *)ISzAlloc_Alloc(alloc, sizeof(CXzEnc));
if (!p)
return NULL;
XzEnc_Construct(p);
XzProps_Init(&p->xzProps);
XzProps_Normalize(&p->xzProps);
p->expectedDataSize = (UInt64)(Int64)-1;
p->alloc = alloc;
p->allocBig = allocBig;
return p;
}
void XzEnc_Destroy(CXzEncHandle pp)
{
CXzEnc *p = (CXzEnc *)pp;
XzEnc_Free(p, p->alloc);
ISzAlloc_Free(p->alloc, p);
}
SRes XzEnc_SetProps(CXzEncHandle pp, const CXzProps *props)
{
CXzEnc *p = (CXzEnc *)pp;
p->xzProps = *props;
XzProps_Normalize(&p->xzProps);
return SZ_OK;
}
void XzEnc_SetDataSize(CXzEncHandle pp, UInt64 expectedDataSiize)
{
CXzEnc *p = (CXzEnc *)pp;
p->expectedDataSize = expectedDataSiize;
}
#ifndef _7ZIP_ST
static SRes XzEnc_MtCallback_Code(void *pp, unsigned coderIndex, unsigned outBufIndex,
const Byte *src, size_t srcSize, int finished)
{
CXzEnc *me = (CXzEnc *)pp;
SRes res;
CMtProgressThunk progressThunk;
Byte *dest = me->outBufs[outBufIndex];
UNUSED_VAR(finished)
{
CXzEncBlockInfo *bInfo = &me->EncBlocks[outBufIndex];
bInfo->totalSize = 0;
bInfo->unpackSize = 0;
bInfo->headerSize = 0;
}
if (!dest)
{
dest = (Byte *)ISzAlloc_Alloc(me->alloc, me->outBufSize);
if (!dest)
return SZ_ERROR_MEM;
me->outBufs[outBufIndex] = dest;
}
MtProgressThunk_CreateVTable(&progressThunk);
progressThunk.mtProgress = &me->mtCoder.mtProgress;
MtProgressThunk_Init(&progressThunk);
{
CXzEncBlockInfo blockSizes;
int inStreamFinished;
res = Xz_CompressBlock(
&me->lzmaf_Items[coderIndex],
NULL,
dest,
dest + XZ_BLOCK_HEADER_SIZE_MAX, me->outBufSize - XZ_BLOCK_HEADER_SIZE_MAX,
NULL,
// srcSize, // expectedSize
src, srcSize,
&me->xzProps,
&progressThunk.vt,
&inStreamFinished,
&blockSizes,
me->alloc,
me->allocBig);
if (res == SZ_OK)
me->EncBlocks[outBufIndex] = blockSizes;
return res;
}
}
static SRes XzEnc_MtCallback_Write(void *pp, unsigned outBufIndex)
{
CXzEnc *me = (CXzEnc *)pp;
const CXzEncBlockInfo *bInfo = &me->EncBlocks[outBufIndex];
const Byte *data = me->outBufs[outBufIndex];
RINOK(WriteBytes(me->outStream, data, bInfo->headerSize));
{
UInt64 totalPackFull = bInfo->totalSize + XZ_GET_PAD_SIZE(bInfo->totalSize);
RINOK(WriteBytes(me->outStream, data + XZ_BLOCK_HEADER_SIZE_MAX, (size_t)totalPackFull - bInfo->headerSize));
}
return XzEncIndex_AddIndexRecord(&me->xzIndex, bInfo->unpackSize, bInfo->totalSize, me->alloc);
}
#endif
SRes XzEnc_Encode(CXzEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress)
{
CXzEnc *p = (CXzEnc *)pp;
const CXzProps *props = &p->xzProps;
XzEncIndex_Init(&p->xzIndex);
{
UInt64 numBlocks = 1;
UInt64 blockSize = props->blockSize;
if (blockSize != XZ_PROPS__BLOCK_SIZE__SOLID
&& props->reduceSize != (UInt64)(Int64)-1)
{
numBlocks = props->reduceSize / blockSize;
if (numBlocks * blockSize != props->reduceSize)
numBlocks++;
}
else
blockSize = (UInt64)1 << 62;
RINOK(XzEncIndex_PreAlloc(&p->xzIndex, numBlocks, blockSize, XZ_GET_ESTIMATED_BLOCK_TOTAL_PACK_SIZE(blockSize), p->alloc));
}
RINOK(Xz_WriteHeader((CXzStreamFlags)props->checkId, outStream));
#ifndef _7ZIP_ST
if (props->numBlockThreads_Reduced > 1)
{
IMtCoderCallback2 vt;
if (!p->mtCoder_WasConstructed)
{
p->mtCoder_WasConstructed = True;
MtCoder_Construct(&p->mtCoder);
}
vt.Code = XzEnc_MtCallback_Code;
vt.Write = XzEnc_MtCallback_Write;
p->checkType = props->checkId;
p->xzProps = *props;
p->outStream = outStream;
p->mtCoder.allocBig = p->allocBig;
p->mtCoder.progress = progress;
p->mtCoder.inStream = inStream;
p->mtCoder.inData = NULL;
p->mtCoder.inDataSize = 0;
p->mtCoder.mtCallback = &vt;
p->mtCoder.mtCallbackObject = p;
if ( props->blockSize == XZ_PROPS__BLOCK_SIZE__SOLID
|| props->blockSize == XZ_PROPS__BLOCK_SIZE__AUTO)
return SZ_ERROR_FAIL;
p->mtCoder.blockSize = (size_t)props->blockSize;
if (p->mtCoder.blockSize != props->blockSize)
return SZ_ERROR_PARAM; /* SZ_ERROR_MEM */
{
size_t destBlockSize = XZ_BLOCK_HEADER_SIZE_MAX + XZ_GET_MAX_BLOCK_PACK_SIZE(p->mtCoder.blockSize);
if (destBlockSize < p->mtCoder.blockSize)
return SZ_ERROR_PARAM;
if (p->outBufSize != destBlockSize)
XzEnc_FreeOutBufs(p);
p->outBufSize = destBlockSize;
}
p->mtCoder.numThreadsMax = props->numBlockThreads_Max;
p->mtCoder.expectedDataSize = p->expectedDataSize;
RINOK(MtCoder_Code(&p->mtCoder));
}
else
#endif
{
int writeStartSizes;
CCompressProgress_XzEncOffset progress2;
Byte *bufData = NULL;
size_t bufSize = 0;
progress2.vt.Progress = CompressProgress_XzEncOffset_Progress;
progress2.inOffset = 0;
progress2.outOffset = 0;
progress2.progress = progress;
writeStartSizes = 0;
if (props->blockSize != XZ_PROPS__BLOCK_SIZE__SOLID)
{
writeStartSizes = (props->forceWriteSizesInHeader > 0);
if (writeStartSizes)
{
size_t t2;
size_t t = (size_t)props->blockSize;
if (t != props->blockSize)
return SZ_ERROR_PARAM;
t = XZ_GET_MAX_BLOCK_PACK_SIZE(t);
if (t < props->blockSize)
return SZ_ERROR_PARAM;
t2 = XZ_BLOCK_HEADER_SIZE_MAX + t;
if (!p->outBufs[0] || t2 != p->outBufSize)
{
XzEnc_FreeOutBufs(p);
p->outBufs[0] = (Byte *)ISzAlloc_Alloc(p->alloc, t2);
if (!p->outBufs[0])
return SZ_ERROR_MEM;
p->outBufSize = t2;
}
bufData = p->outBufs[0] + XZ_BLOCK_HEADER_SIZE_MAX;
bufSize = t;
}
}
for (;;)
{
CXzEncBlockInfo blockSizes;
int inStreamFinished;
/*
UInt64 rem = (UInt64)(Int64)-1;
if (props->reduceSize != (UInt64)(Int64)-1
&& props->reduceSize >= progress2.inOffset)
rem = props->reduceSize - progress2.inOffset;
*/
blockSizes.headerSize = 0; // for GCC
RINOK(Xz_CompressBlock(
&p->lzmaf_Items[0],
writeStartSizes ? NULL : outStream,
writeStartSizes ? p->outBufs[0] : NULL,
bufData, bufSize,
inStream,
// rem,
NULL, 0,
props,
progress ? &progress2.vt : NULL,
&inStreamFinished,
&blockSizes,
p->alloc,
p->allocBig));
{
UInt64 totalPackFull = blockSizes.totalSize + XZ_GET_PAD_SIZE(blockSizes.totalSize);
if (writeStartSizes)
{
RINOK(WriteBytes(outStream, p->outBufs[0], blockSizes.headerSize));
RINOK(WriteBytes(outStream, bufData, (size_t)totalPackFull - blockSizes.headerSize));
}
RINOK(XzEncIndex_AddIndexRecord(&p->xzIndex, blockSizes.unpackSize, blockSizes.totalSize, p->alloc));
progress2.inOffset += blockSizes.unpackSize;
progress2.outOffset += totalPackFull;
}
if (inStreamFinished)
break;
}
}
return XzEncIndex_WriteFooter(&p->xzIndex, (CXzStreamFlags)props->checkId, outStream);
}
#include "Alloc.h"
SRes Xz_Encode(ISeqOutStream *outStream, ISeqInStream *inStream,
const CXzProps *props, ICompressProgress *progress)
{
SRes res;
CXzEncHandle xz = XzEnc_Create(&g_Alloc, &g_BigAlloc);
if (!xz)
return SZ_ERROR_MEM;
res = XzEnc_SetProps(xz, props);
if (res == SZ_OK)
res = XzEnc_Encode(xz, outStream, inStream, progress);
XzEnc_Destroy(xz);
return res;
}
SRes Xz_EncodeEmpty(ISeqOutStream *outStream)
{
SRes res;
CXzEncIndex xzIndex;
XzEncIndex_Construct(&xzIndex);
res = Xz_WriteHeader((CXzStreamFlags)0, outStream);
if (res == SZ_OK)
res = XzEncIndex_WriteFooter(&xzIndex, (CXzStreamFlags)0, outStream);
XzEncIndex_Free(&xzIndex, NULL); // g_Alloc
return res;
}