d074cc7c07
Plugin: ns7zip v2.0.0 Architectures: x86-ansi, x86-unicode, amd64-unicode License: LGPL-2.1-or-later
1117 lines
28 KiB
C++
1117 lines
28 KiB
C++
// BZip2Encoder.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../../../C/Alloc.h"
|
|
#include "../../../C/BwtSort.h"
|
|
#include "../../../C/HuffEnc.h"
|
|
|
|
#include "BZip2Encoder.h"
|
|
|
|
namespace NCompress {
|
|
namespace NBZip2 {
|
|
|
|
#define HUFFMAN_LEN 16
|
|
#if HUFFMAN_LEN > Z7_HUFFMAN_LEN_MAX
|
|
#error Stop_Compiling_Bad_HUFFMAN_LEN_BZip2Encoder
|
|
#endif
|
|
|
|
static const size_t kBufferSize = 1 << 17;
|
|
static const unsigned kNumHuffPasses = 4;
|
|
|
|
|
|
bool CThreadInfo::Alloc()
|
|
{
|
|
if (!m_BlockSorterIndex)
|
|
{
|
|
m_BlockSorterIndex = (UInt32 *)::BigAlloc(BLOCK_SORT_BUF_SIZE(kBlockSizeMax) * sizeof(UInt32));
|
|
if (!m_BlockSorterIndex)
|
|
return false;
|
|
}
|
|
|
|
if (!m_Block_Base)
|
|
{
|
|
const unsigned kPadSize = 1 << 7; // we need at least 1 byte backward padding, becuase we use (m_Block - 1) pointer;
|
|
m_Block_Base = (Byte *)::MidAlloc(kBlockSizeMax * 5
|
|
+ kBlockSizeMax / 10 + (20 << 10)
|
|
+ kPadSize);
|
|
if (!m_Block_Base)
|
|
return false;
|
|
m_Block = m_Block_Base + kPadSize;
|
|
m_MtfArray = m_Block + kBlockSizeMax;
|
|
m_TempArray = m_MtfArray + kBlockSizeMax * 2 + 2;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CThreadInfo::Free()
|
|
{
|
|
::BigFree(m_BlockSorterIndex);
|
|
m_BlockSorterIndex = NULL;
|
|
::MidFree(m_Block_Base);
|
|
m_Block_Base = NULL;
|
|
}
|
|
|
|
#ifndef Z7_ST
|
|
|
|
static THREAD_FUNC_DECL MFThread(void *threadCoderInfo)
|
|
{
|
|
return ((CThreadInfo *)threadCoderInfo)->ThreadFunc();
|
|
}
|
|
|
|
HRESULT CThreadInfo::Create()
|
|
{
|
|
WRes wres = StreamWasFinishedEvent.Create();
|
|
if (wres == 0) { wres = WaitingWasStartedEvent.Create();
|
|
if (wres == 0) { wres = CanWriteEvent.Create();
|
|
if (wres == 0)
|
|
{
|
|
wres =
|
|
#ifdef _WIN32
|
|
Encoder->_props.NumThreadGroups > 1 ?
|
|
Thread.Create_With_Group(MFThread, this, ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup), 0) : // affinity
|
|
#endif
|
|
Encoder->_props.Affinity != 0 ?
|
|
Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity) :
|
|
Thread.Create(MFThread, this);
|
|
}}}
|
|
return HRESULT_FROM_WIN32(wres);
|
|
}
|
|
|
|
void CThreadInfo::FinishStream(bool needLeave)
|
|
{
|
|
Encoder->StreamWasFinished = true;
|
|
StreamWasFinishedEvent.Set();
|
|
if (needLeave)
|
|
Encoder->CS.Leave();
|
|
Encoder->CanStartWaitingEvent.Lock();
|
|
WaitingWasStartedEvent.Set();
|
|
}
|
|
|
|
THREAD_FUNC_RET_TYPE CThreadInfo::ThreadFunc()
|
|
{
|
|
for (;;)
|
|
{
|
|
Encoder->CanProcessEvent.Lock();
|
|
Encoder->CS.Enter();
|
|
if (Encoder->CloseThreads)
|
|
{
|
|
Encoder->CS.Leave();
|
|
return 0;
|
|
}
|
|
if (Encoder->StreamWasFinished)
|
|
{
|
|
FinishStream(true);
|
|
continue;
|
|
}
|
|
HRESULT res = S_OK;
|
|
bool needLeave = true;
|
|
try
|
|
{
|
|
const UInt32 blockSize = Encoder->ReadRleBlock(m_Block);
|
|
m_UnpackSize = Encoder->m_InStream.GetProcessedSize();
|
|
m_BlockIndex = Encoder->NextBlockIndex;
|
|
if (++Encoder->NextBlockIndex == Encoder->NumThreads)
|
|
Encoder->NextBlockIndex = 0;
|
|
if (blockSize == 0)
|
|
{
|
|
FinishStream(true);
|
|
continue;
|
|
}
|
|
Encoder->CS.Leave();
|
|
needLeave = false;
|
|
res = EncodeBlock3(blockSize);
|
|
}
|
|
catch(const CInBufferException &e) { res = e.ErrorCode; }
|
|
catch(const COutBufferException &e) { res = e.ErrorCode; }
|
|
catch(...) { res = E_FAIL; }
|
|
if (res != S_OK)
|
|
{
|
|
Encoder->Result = res;
|
|
FinishStream(needLeave);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void CEncProps::Normalize(int level)
|
|
{
|
|
if (level < 0) level = 5;
|
|
if (level > 9) level = 9;
|
|
|
|
if (NumPasses == (UInt32)(Int32)-1)
|
|
NumPasses = (level >= 9 ? 7 : (level >= 7 ? 2 : 1));
|
|
if (NumPasses < 1) NumPasses = 1;
|
|
if (NumPasses > kNumPassesMax) NumPasses = kNumPassesMax;
|
|
|
|
if (BlockSizeMult == (UInt32)(Int32)-1)
|
|
BlockSizeMult = (level >= 5 ? 9 : (level >= 1 ? (unsigned)level * 2 - 1: 1));
|
|
if (BlockSizeMult < kBlockSizeMultMin) BlockSizeMult = kBlockSizeMultMin;
|
|
if (BlockSizeMult > kBlockSizeMultMax) BlockSizeMult = kBlockSizeMultMax;
|
|
}
|
|
|
|
CEncoder::CEncoder()
|
|
{
|
|
_props.Normalize(-1);
|
|
|
|
#ifndef Z7_ST
|
|
ThreadsInfo = NULL;
|
|
m_NumThreadsPrev = 0;
|
|
NumThreads = 1;
|
|
#endif
|
|
}
|
|
|
|
#ifndef Z7_ST
|
|
CEncoder::~CEncoder()
|
|
{
|
|
Free();
|
|
}
|
|
|
|
HRESULT CEncoder::Create()
|
|
{
|
|
{
|
|
WRes wres = CanProcessEvent.CreateIfNotCreated_Reset();
|
|
if (wres == 0) { wres = CanStartWaitingEvent.CreateIfNotCreated_Reset(); }
|
|
if (wres != 0)
|
|
return HRESULT_FROM_WIN32(wres);
|
|
}
|
|
|
|
if (ThreadsInfo && m_NumThreadsPrev == NumThreads)
|
|
return S_OK;
|
|
try
|
|
{
|
|
Free();
|
|
MtMode = (NumThreads > 1);
|
|
m_NumThreadsPrev = NumThreads;
|
|
ThreadsInfo = new CThreadInfo[NumThreads];
|
|
if (!ThreadsInfo)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
catch(...) { return E_OUTOFMEMORY; }
|
|
for (UInt32 t = 0; t < NumThreads; t++)
|
|
{
|
|
CThreadInfo &ti = ThreadsInfo[t];
|
|
ti.Encoder = this;
|
|
if (MtMode)
|
|
{
|
|
HRESULT res = ti.Create();
|
|
if (res != S_OK)
|
|
{
|
|
NumThreads = t;
|
|
Free();
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
void CEncoder::Free()
|
|
{
|
|
if (!ThreadsInfo)
|
|
return;
|
|
CloseThreads = true;
|
|
CanProcessEvent.Set();
|
|
for (UInt32 t = 0; t < NumThreads; t++)
|
|
{
|
|
CThreadInfo &ti = ThreadsInfo[t];
|
|
if (MtMode)
|
|
ti.Thread.Wait_Close();
|
|
ti.Free();
|
|
}
|
|
delete []ThreadsInfo;
|
|
ThreadsInfo = NULL;
|
|
}
|
|
#endif
|
|
|
|
struct CRleEncoder
|
|
{
|
|
const Byte *_src;
|
|
const Byte *_srcLim;
|
|
Byte *_dest;
|
|
const Byte *_destLim;
|
|
Byte _prevByte;
|
|
unsigned _numReps;
|
|
|
|
void Encode();
|
|
};
|
|
|
|
Z7_NO_INLINE
|
|
void CRleEncoder::Encode()
|
|
{
|
|
const Byte *src = _src;
|
|
const Byte * const srcLim = _srcLim;
|
|
Byte *dest = _dest;
|
|
const Byte * const destLim = _destLim;
|
|
Byte prev = _prevByte;
|
|
unsigned numReps = _numReps;
|
|
// (dest < destLim)
|
|
// src = srcLim; // for debug
|
|
while (dest < destLim)
|
|
{
|
|
if (src == srcLim)
|
|
break;
|
|
const Byte b = *src++;
|
|
if (b != prev)
|
|
{
|
|
if (numReps >= kRleModeRepSize)
|
|
*dest++ = (Byte)(numReps - kRleModeRepSize);
|
|
*dest++ = b;
|
|
numReps = 1;
|
|
prev = b;
|
|
/*
|
|
{ // speed optimization code:
|
|
if (dest >= destLim || src == srcLim)
|
|
break;
|
|
const Byte b2 = *src++;
|
|
*dest++ = b2;
|
|
numReps += (prev == b2);
|
|
prev = b2;
|
|
}
|
|
*/
|
|
continue;
|
|
}
|
|
numReps++;
|
|
if (numReps <= kRleModeRepSize)
|
|
*dest++ = b;
|
|
else if (numReps == kRleModeRepSize + 255)
|
|
{
|
|
*dest++ = (Byte)(numReps - kRleModeRepSize);
|
|
numReps = 0;
|
|
}
|
|
}
|
|
_src = src;
|
|
_dest = dest;
|
|
_prevByte = prev;
|
|
_numReps = numReps;
|
|
// (dest <= destLim + 1)
|
|
}
|
|
|
|
|
|
// out: return value is blockSize: size of data filled in buffer[]:
|
|
// (returned_blockSize <= _props.BlockSizeMult * kBlockSizeStep)
|
|
UInt32 CEncoder::ReadRleBlock(Byte *buffer)
|
|
{
|
|
CRleEncoder rle;
|
|
UInt32 i = 0;
|
|
if (m_InStream.ReadByte(rle._prevByte))
|
|
{
|
|
NumBlocks++;
|
|
const UInt32 blockSize = _props.BlockSizeMult * kBlockSizeStep - 1; // -1 for RLE
|
|
rle._destLim = buffer + blockSize;
|
|
rle._numReps = 1;
|
|
buffer[i++] = rle._prevByte;
|
|
while (i < blockSize)
|
|
{
|
|
rle._dest = buffer + i;
|
|
size_t rem;
|
|
const Byte * const ptr = m_InStream.Lookahead(rem);
|
|
if (rem == 0)
|
|
break;
|
|
rle._src = ptr;
|
|
rle._srcLim = ptr + rem;
|
|
rle.Encode();
|
|
m_InStream.Skip((size_t)(rle._src - ptr));
|
|
i = (UInt32)(size_t)(rle._dest - buffer);
|
|
// (i <= blockSize + 1)
|
|
}
|
|
const int n = (int)rle._numReps - (int)kRleModeRepSize;
|
|
if (n >= 0)
|
|
buffer[i++] = (Byte)n;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
Z7_NO_INLINE
|
|
void CThreadInfo::WriteBits2(UInt32 value, unsigned numBits)
|
|
{ m_OutStreamCurrent.WriteBits(value, numBits); }
|
|
/*
|
|
Z7_NO_INLINE
|
|
void CThreadInfo::WriteByte2(unsigned b)
|
|
{ m_OutStreamCurrent.WriteByte(b); }
|
|
*/
|
|
// void CEncoder::WriteBits(UInt32 value, unsigned numBits) { m_OutStream.WriteBits(value, numBits); }
|
|
Z7_NO_INLINE
|
|
void CEncoder::WriteByte(Byte b) { m_OutStream.WriteByte(b); }
|
|
|
|
|
|
#define WRITE_BITS_UPDATE(value, numBits) \
|
|
{ \
|
|
numBits -= _bitPos; \
|
|
const UInt32 hi = value >> numBits; \
|
|
*_buf++ = (Byte)(_curByte | hi); \
|
|
value -= hi << numBits; \
|
|
_bitPos = 8; \
|
|
_curByte = 0; \
|
|
}
|
|
|
|
#if HUFFMAN_LEN > 16
|
|
|
|
#define WRITE_BITS_HUFF(value2, numBits2) \
|
|
{ \
|
|
UInt32 value = value2; \
|
|
unsigned numBits = numBits2; \
|
|
while (numBits >= _bitPos) { \
|
|
WRITE_BITS_UPDATE(value, numBits) \
|
|
} \
|
|
_bitPos -= numBits; \
|
|
_curByte |= (value << _bitPos); \
|
|
}
|
|
|
|
#else // HUFFMAN_LEN <= 16
|
|
|
|
// numBits2 <= 16 is supported
|
|
#define WRITE_BITS_HUFF(value2, numBits2) \
|
|
{ \
|
|
UInt32 value = value2; \
|
|
unsigned numBits = numBits2; \
|
|
if (numBits >= _bitPos) \
|
|
{ \
|
|
WRITE_BITS_UPDATE(value, numBits) \
|
|
if (numBits >= _bitPos) \
|
|
{ \
|
|
numBits -= _bitPos; \
|
|
const UInt32 hi = value >> numBits; \
|
|
*_buf++ = (Byte)hi; \
|
|
value -= hi << numBits; \
|
|
} \
|
|
} \
|
|
_bitPos -= numBits; \
|
|
_curByte |= (value << _bitPos); \
|
|
}
|
|
|
|
#endif
|
|
|
|
#define WRITE_BITS_8(value2, numBits2) \
|
|
{ \
|
|
UInt32 value = value2; \
|
|
unsigned numBits = numBits2; \
|
|
if (numBits >= _bitPos) \
|
|
{ \
|
|
WRITE_BITS_UPDATE(value, numBits) \
|
|
} \
|
|
_bitPos -= numBits; \
|
|
_curByte |= (value << _bitPos); \
|
|
}
|
|
|
|
#define WRITE_BIT_PRE \
|
|
{ _bitPos--; }
|
|
|
|
#define WRITE_BIT_POST \
|
|
{ \
|
|
if (_bitPos == 0) \
|
|
{ \
|
|
*_buf++ = (Byte)_curByte; \
|
|
_curByte = 0; \
|
|
_bitPos = 8; \
|
|
} \
|
|
}
|
|
|
|
#define WRITE_BIT_0 \
|
|
{ \
|
|
WRITE_BIT_PRE \
|
|
WRITE_BIT_POST \
|
|
}
|
|
|
|
#define WRITE_BIT_1 \
|
|
{ \
|
|
WRITE_BIT_PRE \
|
|
_curByte |= 1u << _bitPos; \
|
|
WRITE_BIT_POST \
|
|
}
|
|
|
|
|
|
// blockSize > 0
|
|
void CThreadInfo::EncodeBlock(const Byte *block, UInt32 blockSize)
|
|
{
|
|
// WriteBit2(0); // Randomised = false
|
|
{
|
|
const UInt32 origPtr = BlockSort(m_BlockSorterIndex, block, blockSize);
|
|
// if (m_BlockSorterIndex[origPtr] != 0) throw 1;
|
|
m_BlockSorterIndex[origPtr] = blockSize;
|
|
WriteBits2(origPtr, kNumOrigBits + 1); // + 1 for additional high bit flag (Randomised = false)
|
|
}
|
|
Byte mtfBuf[256];
|
|
// memset(mtfBuf, 0, sizeof(mtfBuf)); // to disable MSVC warning
|
|
unsigned numInUse;
|
|
{
|
|
Byte inUse[256];
|
|
Byte inUse16[16];
|
|
unsigned i;
|
|
for (i = 0; i < 256; i++)
|
|
inUse[i] = 0;
|
|
for (i = 0; i < 16; i++)
|
|
inUse16[i] = 0;
|
|
{
|
|
const Byte * cur = block;
|
|
block = block + (size_t)blockSize - 1;
|
|
if (cur != block)
|
|
{
|
|
do
|
|
{
|
|
const unsigned b0 = cur[0];
|
|
const unsigned b1 = cur[1];
|
|
cur += 2;
|
|
inUse[b0] = 1;
|
|
inUse[b1] = 1;
|
|
}
|
|
while (cur < block);
|
|
}
|
|
if (cur == block)
|
|
inUse[cur[0]] = 1;
|
|
block -= blockSize; // block pointer is (original_block - 1)
|
|
}
|
|
numInUse = 0;
|
|
for (i = 0; i < 256; i++)
|
|
if (inUse[i])
|
|
{
|
|
inUse16[i >> 4] = 1;
|
|
mtfBuf[numInUse++] = (Byte)i;
|
|
}
|
|
for (i = 0; i < 16; i++)
|
|
WriteBit2(inUse16[i]);
|
|
for (i = 0; i < 256; i++)
|
|
if (inUse16[i >> 4])
|
|
WriteBit2(inUse[i]);
|
|
}
|
|
const unsigned alphaSize = numInUse + 2;
|
|
|
|
UInt32 symbolCounts[kMaxAlphaSize];
|
|
{
|
|
for (unsigned i = 0; i < kMaxAlphaSize; i++)
|
|
symbolCounts[i] = 0;
|
|
symbolCounts[(size_t)alphaSize - 1] = 1;
|
|
}
|
|
|
|
Byte *mtfs = m_MtfArray;
|
|
{
|
|
const UInt32 *bsIndex = m_BlockSorterIndex;
|
|
const UInt32 *bsIndex_rle = bsIndex;
|
|
const UInt32 * const bsIndex_end = bsIndex + blockSize;
|
|
// block--; // backward fix
|
|
// block pointer is (original_block - 1)
|
|
do
|
|
{
|
|
const Byte v = block[*bsIndex++];
|
|
Byte a = mtfBuf[0];
|
|
if (v != a)
|
|
{
|
|
mtfBuf[0] = v;
|
|
{
|
|
UInt32 rleSize = (UInt32)(size_t)(bsIndex - bsIndex_rle) - 1;
|
|
bsIndex_rle = bsIndex;
|
|
while (rleSize)
|
|
{
|
|
const unsigned sym = (unsigned)(--rleSize & 1);
|
|
*mtfs++ = (Byte)sym;
|
|
symbolCounts[sym]++;
|
|
rleSize >>= 1;
|
|
}
|
|
}
|
|
unsigned pos1 = 2; // = real_pos + 1
|
|
Byte b;
|
|
b = mtfBuf[1]; mtfBuf[1] = a; if (v != b)
|
|
{ a = mtfBuf[2]; mtfBuf[2] = b; if (v == a) pos1 = 3;
|
|
else { b = mtfBuf[3]; mtfBuf[3] = a; if (v == b) pos1 = 4;
|
|
else
|
|
{
|
|
Byte *m = mtfBuf + 7;
|
|
for (;;)
|
|
{
|
|
a = m[-3]; m[-3] = b; if (v == a) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 2)); break; }
|
|
b = m[-2]; m[-2] = a; if (v == b) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 1)); break; }
|
|
a = m[-1]; m[-1] = b; if (v == a) { pos1 = (unsigned)(size_t)(m - (mtfBuf )); break; }
|
|
b = m[ 0]; m[ 0] = a; m += 4; if (v == b) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 3)); break; }
|
|
}
|
|
}}}
|
|
symbolCounts[pos1]++;
|
|
if (pos1 >= 0xff)
|
|
{
|
|
*mtfs++ = 0xff;
|
|
// pos1 -= 0xff;
|
|
pos1++; // we need only low byte
|
|
}
|
|
*mtfs++ = (Byte)pos1;
|
|
}
|
|
}
|
|
while (bsIndex < bsIndex_end);
|
|
|
|
UInt32 rleSize = (UInt32)(size_t)(bsIndex - bsIndex_rle);
|
|
while (rleSize)
|
|
{
|
|
const unsigned sym = (unsigned)(--rleSize & 1);
|
|
*mtfs++ = (Byte)sym;
|
|
symbolCounts[sym]++;
|
|
rleSize >>= 1;
|
|
}
|
|
|
|
unsigned d = alphaSize - 1;
|
|
if (alphaSize >= 256)
|
|
{
|
|
*mtfs++ = 0xff;
|
|
d = alphaSize; // (-256)
|
|
}
|
|
*mtfs++ = (Byte)d;
|
|
}
|
|
|
|
const Byte * const mtf_lim = mtfs;
|
|
|
|
UInt32 numSymbols = 0;
|
|
{
|
|
for (unsigned i = 0; i < kMaxAlphaSize; i++)
|
|
numSymbols += symbolCounts[i];
|
|
}
|
|
|
|
unsigned bestNumTables = kNumTablesMin;
|
|
UInt32 bestPrice = 0xFFFFFFFF;
|
|
const UInt32 startPos = m_OutStreamCurrent.GetPos();
|
|
const unsigned startCurByte = m_OutStreamCurrent.GetCurByte();
|
|
for (unsigned nt = kNumTablesMin; nt <= kNumTablesMax + 1; nt++)
|
|
{
|
|
unsigned numTables;
|
|
|
|
if (m_OptimizeNumTables)
|
|
{
|
|
m_OutStreamCurrent.SetPos(startPos);
|
|
m_OutStreamCurrent.SetCurState(startPos & 7, startCurByte);
|
|
numTables = (nt <= kNumTablesMax ? nt : bestNumTables);
|
|
}
|
|
else
|
|
{
|
|
if (numSymbols < 200) numTables = 2;
|
|
else if (numSymbols < 600) numTables = 3;
|
|
else if (numSymbols < 1200) numTables = 4;
|
|
else if (numSymbols < 2400) numTables = 5;
|
|
else numTables = 6;
|
|
}
|
|
|
|
WriteBits2(numTables, kNumTablesBits);
|
|
const unsigned numSelectors = (numSymbols + kGroupSize - 1) / kGroupSize;
|
|
WriteBits2((UInt32)numSelectors, kNumSelectorsBits);
|
|
|
|
{
|
|
UInt32 remFreq = numSymbols;
|
|
unsigned gs = 0;
|
|
unsigned t = numTables;
|
|
do
|
|
{
|
|
UInt32 tFreq = remFreq / t;
|
|
unsigned ge = gs;
|
|
UInt32 aFreq = 0;
|
|
while (aFreq < tFreq) // && ge < alphaSize)
|
|
aFreq += symbolCounts[ge++];
|
|
|
|
if (ge > gs + 1 && t != numTables && t != 1 && (((numTables - t) & 1) == 1))
|
|
aFreq -= symbolCounts[--ge];
|
|
|
|
Byte *lens = Lens[(size_t)t - 1];
|
|
unsigned i = 0;
|
|
do
|
|
lens[i] = (Byte)((i >= gs && i < ge) ? 0 : 1);
|
|
while (++i < alphaSize);
|
|
gs = ge;
|
|
remFreq -= aFreq;
|
|
}
|
|
while (--t != 0);
|
|
}
|
|
|
|
|
|
for (unsigned pass = 0; pass < kNumHuffPasses; pass++)
|
|
{
|
|
memset(Freqs, 0, sizeof(Freqs[0]) * numTables);
|
|
// memset(Freqs, 0, sizeof(Freqs));
|
|
{
|
|
mtfs = m_MtfArray;
|
|
UInt32 g = 0;
|
|
do
|
|
{
|
|
unsigned symbols[kGroupSize];
|
|
unsigned i = 0;
|
|
do
|
|
{
|
|
UInt32 symbol = *mtfs++;
|
|
if (symbol >= 0xFF)
|
|
symbol += *mtfs++;
|
|
symbols[i] = symbol;
|
|
}
|
|
while (++i < kGroupSize && mtfs < mtf_lim);
|
|
|
|
UInt32 bestPrice2 = 0xFFFFFFFF;
|
|
unsigned t = 0;
|
|
do
|
|
{
|
|
const Byte *lens = Lens[t];
|
|
UInt32 price = 0;
|
|
unsigned j = 0;
|
|
do
|
|
price += lens[symbols[j]];
|
|
while (++j < i);
|
|
if (price < bestPrice2)
|
|
{
|
|
m_Selectors[g] = (Byte)t;
|
|
bestPrice2 = price;
|
|
}
|
|
}
|
|
while (++t < numTables);
|
|
UInt32 *freqs = Freqs[m_Selectors[g++]];
|
|
unsigned j = 0;
|
|
do
|
|
freqs[symbols[j]]++;
|
|
while (++j < i);
|
|
}
|
|
while (mtfs < mtf_lim);
|
|
}
|
|
|
|
unsigned t = 0;
|
|
do
|
|
{
|
|
UInt32 *freqs = Freqs[t];
|
|
unsigned i = 0;
|
|
do
|
|
if (freqs[i] == 0)
|
|
freqs[i] = 1;
|
|
while (++i < alphaSize);
|
|
Huffman_Generate(freqs, Codes[t], Lens[t], kMaxAlphaSize, HUFFMAN_LEN);
|
|
}
|
|
while (++t < numTables);
|
|
}
|
|
|
|
unsigned _bitPos; // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte
|
|
unsigned _curByte; // low (_bitPos) bits are zeros
|
|
// high (8 - _bitPos) bits are filled
|
|
Byte *_buf;
|
|
{
|
|
Byte mtfSel[kNumTablesMax];
|
|
{
|
|
unsigned t = 0;
|
|
do
|
|
mtfSel[t] = (Byte)t;
|
|
while (++t < numTables);
|
|
}
|
|
|
|
_bitPos = m_OutStreamCurrent._bitPos;
|
|
_curByte = m_OutStreamCurrent._curByte;
|
|
_buf = m_OutStreamCurrent._buf;
|
|
// stream.Init_from_Global(m_OutStreamCurrent);
|
|
|
|
const Byte *selectors = m_Selectors;
|
|
const Byte * const selectors_lim = selectors + numSelectors;
|
|
Byte prev = 0; // mtfSel[0];
|
|
do
|
|
{
|
|
const Byte sel = *selectors++;
|
|
if (prev != sel)
|
|
{
|
|
Byte *mtfSel_cur = &mtfSel[1];
|
|
for (;;)
|
|
{
|
|
WRITE_BIT_1
|
|
const Byte next = *mtfSel_cur;
|
|
*mtfSel_cur++ = prev;
|
|
prev = next;
|
|
if (next == sel)
|
|
break;
|
|
}
|
|
// mtfSel[0] = sel;
|
|
}
|
|
WRITE_BIT_0
|
|
}
|
|
while (selectors != selectors_lim);
|
|
}
|
|
{
|
|
unsigned t = 0;
|
|
do
|
|
{
|
|
const Byte *lens = Lens[t];
|
|
unsigned len = lens[0];
|
|
WRITE_BITS_8(len, kNumLevelsBits)
|
|
unsigned i = 0;
|
|
do
|
|
{
|
|
const unsigned level = lens[i];
|
|
while (len != level)
|
|
{
|
|
WRITE_BIT_1
|
|
if (len < level)
|
|
{
|
|
len++;
|
|
WRITE_BIT_0
|
|
}
|
|
else
|
|
{
|
|
len--;
|
|
WRITE_BIT_1
|
|
}
|
|
}
|
|
WRITE_BIT_0
|
|
}
|
|
while (++i < alphaSize);
|
|
}
|
|
while (++t < numTables);
|
|
}
|
|
{
|
|
UInt32 groupSize = 1;
|
|
const Byte *selectors = m_Selectors;
|
|
const Byte *lens = NULL;
|
|
const UInt32 *codes = NULL;
|
|
mtfs = m_MtfArray;
|
|
do
|
|
{
|
|
unsigned symbol = *mtfs++;
|
|
if (symbol >= 0xFF)
|
|
symbol += *mtfs++;
|
|
if (--groupSize == 0)
|
|
{
|
|
groupSize = kGroupSize;
|
|
const unsigned t = *selectors++;
|
|
lens = Lens[t];
|
|
codes = Codes[t];
|
|
}
|
|
WRITE_BITS_HUFF(codes[symbol], lens[symbol])
|
|
}
|
|
while (mtfs < mtf_lim);
|
|
}
|
|
// Restore_from_Local:
|
|
m_OutStreamCurrent._bitPos = _bitPos;
|
|
m_OutStreamCurrent._curByte = _curByte;
|
|
m_OutStreamCurrent._buf = _buf;
|
|
|
|
if (!m_OptimizeNumTables)
|
|
break;
|
|
const UInt32 price = m_OutStreamCurrent.GetPos() - startPos;
|
|
if (price <= bestPrice)
|
|
{
|
|
if (nt == kNumTablesMax)
|
|
break;
|
|
bestPrice = price;
|
|
bestNumTables = nt;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// blockSize > 0
|
|
UInt32 CThreadInfo::EncodeBlockWithHeaders(const Byte *block, UInt32 blockSize)
|
|
{
|
|
WriteByte2(kBlockSig0);
|
|
WriteByte2(kBlockSig1);
|
|
WriteByte2(kBlockSig2);
|
|
WriteByte2(kBlockSig3);
|
|
WriteByte2(kBlockSig4);
|
|
WriteByte2(kBlockSig5);
|
|
|
|
CBZip2Crc crc;
|
|
const Byte * const lim = block + blockSize;
|
|
unsigned b = *block++;
|
|
crc.UpdateByte(b);
|
|
for (;;)
|
|
{
|
|
const unsigned prev = b;
|
|
if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); if (prev != b) continue;
|
|
if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); if (prev != b) continue;
|
|
if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); if (prev != b) continue;
|
|
if (block >= lim) { break; } b = *block++; if (b) do crc.UpdateByte(prev); while (--b);
|
|
if (block >= lim) { break; } b = *block++; crc.UpdateByte(b);
|
|
}
|
|
const UInt32 crcRes = crc.GetDigest();
|
|
for (int i = 24; i >= 0; i -= 8)
|
|
WriteByte2((Byte)(crcRes >> i));
|
|
EncodeBlock(lim - blockSize, blockSize);
|
|
return crcRes;
|
|
}
|
|
|
|
|
|
void CThreadInfo::EncodeBlock2(const Byte *block, UInt32 blockSize, UInt32 numPasses)
|
|
{
|
|
const UInt32 numCrcs = m_NumCrcs;
|
|
|
|
const UInt32 startBytePos = m_OutStreamCurrent.GetBytePos();
|
|
const UInt32 startPos = m_OutStreamCurrent.GetPos();
|
|
const unsigned startCurByte = m_OutStreamCurrent.GetCurByte();
|
|
unsigned endCurByte = 0;
|
|
UInt32 endPos = 0; // 0 means no no additional passes
|
|
if (numPasses > 1 && blockSize >= (1 << 10))
|
|
{
|
|
UInt32 bs0 = blockSize / 2;
|
|
for (; bs0 < blockSize &&
|
|
(block[ bs0 ] ==
|
|
block[(size_t)bs0 - 1] ||
|
|
block[(size_t)bs0 - 1] ==
|
|
block[(size_t)bs0 - 2]);
|
|
bs0++)
|
|
{}
|
|
|
|
if (bs0 < blockSize)
|
|
{
|
|
EncodeBlock2(block, bs0, numPasses - 1);
|
|
EncodeBlock2(block + bs0, blockSize - bs0, numPasses - 1);
|
|
endPos = m_OutStreamCurrent.GetPos();
|
|
endCurByte = m_OutStreamCurrent.GetCurByte();
|
|
// we prepare next byte as identical byte to starting byte for main encoding attempt:
|
|
if (endPos & 7)
|
|
WriteBits2(0, 8 - (endPos & 7));
|
|
m_OutStreamCurrent.SetCurState((startPos & 7), startCurByte);
|
|
}
|
|
}
|
|
|
|
const UInt32 startBytePos2 = m_OutStreamCurrent.GetBytePos();
|
|
const UInt32 startPos2 = m_OutStreamCurrent.GetPos();
|
|
const UInt32 crcVal = EncodeBlockWithHeaders(block, blockSize);
|
|
|
|
if (endPos)
|
|
{
|
|
const UInt32 size2 = m_OutStreamCurrent.GetPos() - startPos2;
|
|
if (size2 >= endPos - startPos)
|
|
{
|
|
m_OutStreamCurrent.SetPos(endPos);
|
|
m_OutStreamCurrent.SetCurState((endPos & 7), endCurByte);
|
|
return;
|
|
}
|
|
const UInt32 numBytes = m_OutStreamCurrent.GetBytePos() - startBytePos2;
|
|
Byte * const buffer = m_OutStreamCurrent.GetStream();
|
|
memmove(buffer + startBytePos, buffer + startBytePos2, numBytes);
|
|
m_OutStreamCurrent.SetPos(startPos + size2);
|
|
// we don't call m_OutStreamCurrent.SetCurState() here because
|
|
// m_OutStreamCurrent._curByte is correct already
|
|
}
|
|
m_CRCs[numCrcs] = crcVal;
|
|
m_NumCrcs = numCrcs + 1;
|
|
}
|
|
|
|
|
|
HRESULT CThreadInfo::EncodeBlock3(UInt32 blockSize)
|
|
{
|
|
CMsbfEncoderTemp &outStreamTemp = m_OutStreamCurrent;
|
|
outStreamTemp.SetStream(m_TempArray);
|
|
outStreamTemp.Init();
|
|
m_NumCrcs = 0;
|
|
|
|
EncodeBlock2(m_Block, blockSize, Encoder->_props.NumPasses);
|
|
|
|
#ifndef Z7_ST
|
|
if (Encoder->MtMode)
|
|
Encoder->ThreadsInfo[m_BlockIndex].CanWriteEvent.Lock();
|
|
#endif
|
|
|
|
for (UInt32 i = 0; i < m_NumCrcs; i++)
|
|
Encoder->CombinedCrc.Update(m_CRCs[i]);
|
|
Encoder->WriteBytes(m_TempArray, outStreamTemp.GetPos(), outStreamTemp.GetNonFlushedByteBits());
|
|
HRESULT res = S_OK;
|
|
|
|
#ifndef Z7_ST
|
|
if (Encoder->MtMode)
|
|
{
|
|
UInt32 blockIndex = m_BlockIndex + 1;
|
|
if (blockIndex == Encoder->NumThreads)
|
|
blockIndex = 0;
|
|
if (Encoder->Progress)
|
|
{
|
|
const UInt64 packSize = Encoder->m_OutStream.GetProcessedSize();
|
|
res = Encoder->Progress->SetRatioInfo(&m_UnpackSize, &packSize);
|
|
}
|
|
Encoder->ThreadsInfo[blockIndex].CanWriteEvent.Set();
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByteBits)
|
|
{
|
|
m_OutStream.WriteBytes(data, sizeInBits >> 3);
|
|
sizeInBits &= 7;
|
|
if (sizeInBits)
|
|
m_OutStream.WriteBits(lastByteBits, sizeInBits);
|
|
}
|
|
|
|
|
|
HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
|
const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
|
|
{
|
|
NumBlocks = 0;
|
|
#ifndef Z7_ST
|
|
Progress = progress;
|
|
ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
|
|
RINOK(Create())
|
|
for (UInt32 t = 0; t < NumThreads; t++)
|
|
#endif
|
|
{
|
|
#ifndef Z7_ST
|
|
CThreadInfo &ti = ThreadsInfo[t];
|
|
if (MtMode)
|
|
{
|
|
WRes wres = ti.StreamWasFinishedEvent.Reset();
|
|
if (wres == 0) { wres = ti.WaitingWasStartedEvent.Reset();
|
|
if (wres == 0) { wres = ti.CanWriteEvent.Reset(); }}
|
|
if (wres != 0)
|
|
return HRESULT_FROM_WIN32(wres);
|
|
}
|
|
#else
|
|
CThreadInfo &ti = ThreadsInfo;
|
|
ti.Encoder = this;
|
|
#endif
|
|
|
|
ti.m_OptimizeNumTables = _props.DoOptimizeNumTables();
|
|
|
|
if (!ti.Alloc())
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
if (!m_InStream.Create(kBufferSize))
|
|
return E_OUTOFMEMORY;
|
|
if (!m_OutStream.Create(kBufferSize))
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
m_InStream.SetStream(inStream);
|
|
m_InStream.Init();
|
|
|
|
m_OutStream.SetStream(outStream);
|
|
m_OutStream.Init();
|
|
|
|
CombinedCrc.Init();
|
|
#ifndef Z7_ST
|
|
NextBlockIndex = 0;
|
|
StreamWasFinished = false;
|
|
CloseThreads = false;
|
|
CanStartWaitingEvent.Reset();
|
|
#endif
|
|
|
|
WriteByte(kArSig0);
|
|
WriteByte(kArSig1);
|
|
WriteByte(kArSig2);
|
|
WriteByte((Byte)(kArSig3 + _props.BlockSizeMult));
|
|
|
|
#ifndef Z7_ST
|
|
|
|
if (MtMode)
|
|
{
|
|
ThreadsInfo[0].CanWriteEvent.Set();
|
|
Result = S_OK;
|
|
CanProcessEvent.Set();
|
|
UInt32 t;
|
|
for (t = 0; t < NumThreads; t++)
|
|
ThreadsInfo[t].StreamWasFinishedEvent.Lock();
|
|
CanProcessEvent.Reset();
|
|
CanStartWaitingEvent.Set();
|
|
for (t = 0; t < NumThreads; t++)
|
|
ThreadsInfo[t].WaitingWasStartedEvent.Lock();
|
|
CanStartWaitingEvent.Reset();
|
|
RINOK(Result)
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
for (;;)
|
|
{
|
|
CThreadInfo &ti =
|
|
#ifndef Z7_ST
|
|
ThreadsInfo[0];
|
|
#else
|
|
ThreadsInfo;
|
|
#endif
|
|
const UInt32 blockSize = ReadRleBlock(ti.m_Block);
|
|
if (blockSize == 0)
|
|
break;
|
|
RINOK(ti.EncodeBlock3(blockSize))
|
|
if (progress)
|
|
{
|
|
const UInt64 unpackSize = m_InStream.GetProcessedSize();
|
|
const UInt64 packSize = m_OutStream.GetProcessedSize();
|
|
RINOK(progress->SetRatioInfo(&unpackSize, &packSize))
|
|
}
|
|
}
|
|
}
|
|
WriteByte(kFinSig0);
|
|
WriteByte(kFinSig1);
|
|
WriteByte(kFinSig2);
|
|
WriteByte(kFinSig3);
|
|
WriteByte(kFinSig4);
|
|
WriteByte(kFinSig5);
|
|
{
|
|
const UInt32 v = CombinedCrc.GetDigest();
|
|
for (int i = 24; i >= 0; i -= 8)
|
|
WriteByte((Byte)(v >> i));
|
|
}
|
|
RINOK(Flush())
|
|
if (!m_InStream.WasFinished())
|
|
return E_FAIL;
|
|
return S_OK;
|
|
}
|
|
|
|
Z7_COM7F_IMF(CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
|
const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
|
|
{
|
|
try { return CodeReal(inStream, outStream, inSize, outSize, progress); }
|
|
catch(const CInBufferException &e) { return e.ErrorCode; }
|
|
catch(const COutBufferException &e) { return e.ErrorCode; }
|
|
catch(...) { return S_FALSE; }
|
|
}
|
|
|
|
Z7_COM7F_IMF(CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps))
|
|
{
|
|
int level = -1;
|
|
CEncProps props;
|
|
for (UInt32 i = 0; i < numProps; i++)
|
|
{
|
|
const PROPVARIANT &prop = coderProps[i];
|
|
const PROPID propID = propIDs[i];
|
|
|
|
if (propID == NCoderPropID::kAffinity)
|
|
{
|
|
if (prop.vt != VT_UI8)
|
|
return E_INVALIDARG;
|
|
props.Affinity = prop.uhVal.QuadPart;
|
|
continue;
|
|
}
|
|
|
|
if (propID == NCoderPropID::kNumThreadGroups)
|
|
{
|
|
if (prop.vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
props.NumThreadGroups = (UInt32)prop.ulVal;
|
|
continue;
|
|
}
|
|
|
|
if (propID >= NCoderPropID::kReduceSize)
|
|
continue;
|
|
if (prop.vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
const UInt32 v = (UInt32)prop.ulVal;
|
|
switch (propID)
|
|
{
|
|
case NCoderPropID::kNumPasses: props.NumPasses = v; break;
|
|
case NCoderPropID::kDictionarySize: props.BlockSizeMult = v / kBlockSizeStep; break;
|
|
case NCoderPropID::kLevel: level = (int)v; break;
|
|
case NCoderPropID::kNumThreads:
|
|
{
|
|
#ifndef Z7_ST
|
|
SetNumberOfThreads(v);
|
|
#endif
|
|
break;
|
|
}
|
|
default: return E_INVALIDARG;
|
|
}
|
|
}
|
|
props.Normalize(level);
|
|
_props = props;
|
|
return S_OK;
|
|
}
|
|
|
|
#ifndef Z7_ST
|
|
Z7_COM7F_IMF(CEncoder::SetNumberOfThreads(UInt32 numThreads))
|
|
{
|
|
const UInt32 kNumThreadsMax = 64;
|
|
if (numThreads < 1) numThreads = 1;
|
|
if (numThreads > kNumThreadsMax) numThreads = kNumThreadsMax;
|
|
NumThreads = numThreads;
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
}}
|