Home | History | Annotate | Download | only in Compress
      1 // Bcj2Coder.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../C/Alloc.h"
      6 
      7 #include "Bcj2Coder.h"
      8 
      9 namespace NCompress {
     10 namespace NBcj2 {
     11 
     12 inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); }
     13 inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); }
     14 inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); }
     15 
     16 #ifndef EXTRACT_ONLY
     17 
     18 static const int kBufferSize = 1 << 17;
     19 
     20 static bool inline Test86MSByte(Byte b)
     21 {
     22   return (b == 0 || b == 0xFF);
     23 }
     24 
     25 bool CEncoder::Create()
     26 {
     27   if (!_mainStream.Create(1 << 18))
     28     return false;
     29   if (!_callStream.Create(1 << 18))
     30     return false;
     31   if (!_jumpStream.Create(1 << 18))
     32     return false;
     33   if (!_rangeEncoder.Create(1 << 20))
     34     return false;
     35   if (_buffer == 0)
     36   {
     37     _buffer = (Byte *)MidAlloc(kBufferSize);
     38     if (_buffer == 0)
     39       return false;
     40   }
     41   return true;
     42 }
     43 
     44 CEncoder::~CEncoder()
     45 {
     46   ::MidFree(_buffer);
     47 }
     48 
     49 HRESULT CEncoder::Flush()
     50 {
     51   RINOK(_mainStream.Flush());
     52   RINOK(_callStream.Flush());
     53   RINOK(_jumpStream.Flush());
     54   _rangeEncoder.FlushData();
     55   return _rangeEncoder.FlushStream();
     56 }
     57 
     58 const UInt32 kDefaultLimit = (1 << 24);
     59 
     60 HRESULT CEncoder::CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams,
     61     ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams,
     62     ICompressProgressInfo *progress)
     63 {
     64   if (numInStreams != 1 || numOutStreams != 4)
     65     return E_INVALIDARG;
     66 
     67   if (!Create())
     68     return E_OUTOFMEMORY;
     69 
     70   bool sizeIsDefined = false;
     71   UInt64 inSize = 0;
     72   if (inSizes != NULL)
     73     if (inSizes[0] != NULL)
     74     {
     75       inSize = *inSizes[0];
     76       if (inSize <= kDefaultLimit)
     77         sizeIsDefined = true;
     78     }
     79 
     80   CCoderReleaser releaser(this);
     81 
     82   ISequentialInStream *inStream = inStreams[0];
     83 
     84   _mainStream.SetStream(outStreams[0]);
     85   _mainStream.Init();
     86   _callStream.SetStream(outStreams[1]);
     87   _callStream.Init();
     88   _jumpStream.SetStream(outStreams[2]);
     89   _jumpStream.Init();
     90   _rangeEncoder.SetStream(outStreams[3]);
     91   _rangeEncoder.Init();
     92   for (int i = 0; i < 256 + 2; i++)
     93     _statusEncoder[i].Init();
     94 
     95   CMyComPtr<ICompressGetSubStreamSize> getSubStreamSize;
     96   {
     97     inStream->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize);
     98   }
     99 
    100   UInt32 nowPos = 0;
    101   UInt64 nowPos64 = 0;
    102   UInt32 bufferPos = 0;
    103 
    104   Byte prevByte = 0;
    105 
    106   UInt64 subStreamIndex = 0;
    107   UInt64 subStreamStartPos  = 0;
    108   UInt64 subStreamEndPos = 0;
    109 
    110   for (;;)
    111   {
    112     UInt32 processedSize = 0;
    113     for (;;)
    114     {
    115       UInt32 size = kBufferSize - (bufferPos + processedSize);
    116       UInt32 processedSizeLoc;
    117       if (size == 0)
    118         break;
    119       RINOK(inStream->Read(_buffer + bufferPos + processedSize, size, &processedSizeLoc));
    120       if (processedSizeLoc == 0)
    121         break;
    122       processedSize += processedSizeLoc;
    123     }
    124     UInt32 endPos = bufferPos + processedSize;
    125 
    126     if (endPos < 5)
    127     {
    128       // change it
    129       for (bufferPos = 0; bufferPos < endPos; bufferPos++)
    130       {
    131         Byte b = _buffer[bufferPos];
    132         _mainStream.WriteByte(b);
    133         UInt32 index;
    134         if (b == 0xE8)
    135           index = prevByte;
    136         else if (b == 0xE9)
    137           index = 256;
    138         else if (IsJcc(prevByte, b))
    139           index = 257;
    140         else
    141         {
    142           prevByte = b;
    143           continue;
    144         }
    145         _statusEncoder[index].Encode(&_rangeEncoder, 0);
    146         prevByte = b;
    147       }
    148       return Flush();
    149     }
    150 
    151     bufferPos = 0;
    152 
    153     UInt32 limit = endPos - 5;
    154     while(bufferPos <= limit)
    155     {
    156       Byte b = _buffer[bufferPos];
    157       _mainStream.WriteByte(b);
    158       if (!IsJ(prevByte, b))
    159       {
    160         bufferPos++;
    161         prevByte = b;
    162         continue;
    163       }
    164       Byte nextByte = _buffer[bufferPos + 4];
    165       UInt32 src =
    166         (UInt32(nextByte) << 24) |
    167         (UInt32(_buffer[bufferPos + 3]) << 16) |
    168         (UInt32(_buffer[bufferPos + 2]) << 8) |
    169         (_buffer[bufferPos + 1]);
    170       UInt32 dest = (nowPos + bufferPos + 5) + src;
    171       // if (Test86MSByte(nextByte))
    172       bool convert;
    173       if (getSubStreamSize != NULL)
    174       {
    175         UInt64 currentPos = (nowPos64 + bufferPos);
    176         while (subStreamEndPos < currentPos)
    177         {
    178           UInt64 subStreamSize;
    179           HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize);
    180           if (result == S_OK)
    181           {
    182             subStreamStartPos = subStreamEndPos;
    183             subStreamEndPos += subStreamSize;
    184             subStreamIndex++;
    185           }
    186           else if (result == S_FALSE || result == E_NOTIMPL)
    187           {
    188             getSubStreamSize.Release();
    189             subStreamStartPos = 0;
    190             subStreamEndPos = subStreamStartPos - 1;
    191           }
    192           else
    193             return result;
    194         }
    195         if (getSubStreamSize == NULL)
    196         {
    197           if (sizeIsDefined)
    198             convert = (dest < inSize);
    199           else
    200             convert = Test86MSByte(nextByte);
    201         }
    202         else if (subStreamEndPos - subStreamStartPos > kDefaultLimit)
    203           convert = Test86MSByte(nextByte);
    204         else
    205         {
    206           UInt64 dest64 = (currentPos + 5) + Int64(Int32(src));
    207           convert = (dest64 >= subStreamStartPos && dest64 < subStreamEndPos);
    208         }
    209       }
    210       else if (sizeIsDefined)
    211         convert = (dest < inSize);
    212       else
    213         convert = Test86MSByte(nextByte);
    214       unsigned index = GetIndex(prevByte, b);
    215       if (convert)
    216       {
    217         _statusEncoder[index].Encode(&_rangeEncoder, 1);
    218         bufferPos += 5;
    219         COutBuffer &s = (b == 0xE8) ? _callStream : _jumpStream;
    220         for (int i = 24; i >= 0; i -= 8)
    221           s.WriteByte((Byte)(dest >> i));
    222         prevByte = nextByte;
    223       }
    224       else
    225       {
    226         _statusEncoder[index].Encode(&_rangeEncoder, 0);
    227         bufferPos++;
    228         prevByte = b;
    229       }
    230     }
    231     nowPos += bufferPos;
    232     nowPos64 += bufferPos;
    233 
    234     if (progress != NULL)
    235     {
    236       /*
    237       const UInt64 compressedSize =
    238         _mainStream.GetProcessedSize() +
    239         _callStream.GetProcessedSize() +
    240         _jumpStream.GetProcessedSize() +
    241         _rangeEncoder.GetProcessedSize();
    242       */
    243       RINOK(progress->SetRatioInfo(&nowPos64, NULL));
    244     }
    245 
    246     UInt32 i = 0;
    247     while(bufferPos < endPos)
    248       _buffer[i++] = _buffer[bufferPos++];
    249     bufferPos = i;
    250   }
    251 }
    252 
    253 STDMETHODIMP CEncoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams,
    254     ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams,
    255     ICompressProgressInfo *progress)
    256 {
    257   try
    258   {
    259     return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress);
    260   }
    261   catch(const COutBufferException &e) { return e.ErrorCode; }
    262   catch(...) { return S_FALSE; }
    263 }
    264 
    265 #endif
    266 
    267 
    268 STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; }
    269 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
    270 
    271 CDecoder::CDecoder():
    272   _outBufSize(1 << 16)
    273 {
    274   _inBufSizes[0] = 1 << 20;
    275   _inBufSizes[1] = 1 << 20;
    276   _inBufSizes[2] = 1 << 20;
    277   _inBufSizes[3] = 1 << 20;
    278 }
    279 
    280 HRESULT CDecoder::CodeReal(ISequentialInStream **inStreams, const UInt64 ** /* inSizes */, UInt32 numInStreams,
    281     ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams,
    282     ICompressProgressInfo *progress)
    283 {
    284   if (numInStreams != 4 || numOutStreams != 1)
    285     return E_INVALIDARG;
    286 
    287   if (!_mainInStream.Create(_inBufSizes[0]))
    288     return E_OUTOFMEMORY;
    289   if (!_callStream.Create(_inBufSizes[1]))
    290     return E_OUTOFMEMORY;
    291   if (!_jumpStream.Create(_inBufSizes[2]))
    292     return E_OUTOFMEMORY;
    293   if (!_rangeDecoder.Create(_inBufSizes[3]))
    294     return E_OUTOFMEMORY;
    295   if (!_outStream.Create(_outBufSize))
    296     return E_OUTOFMEMORY;
    297 
    298   CCoderReleaser releaser(this);
    299 
    300   _mainInStream.SetStream(inStreams[0]);
    301   _callStream.SetStream(inStreams[1]);
    302   _jumpStream.SetStream(inStreams[2]);
    303   _rangeDecoder.SetStream(inStreams[3]);
    304   _outStream.SetStream(outStreams[0]);
    305 
    306   _mainInStream.Init();
    307   _callStream.Init();
    308   _jumpStream.Init();
    309   _rangeDecoder.Init();
    310   _outStream.Init();
    311 
    312   for (int i = 0; i < 256 + 2; i++)
    313     _statusDecoder[i].Init();
    314 
    315   Byte prevByte = 0;
    316   UInt32 processedBytes = 0;
    317   for (;;)
    318   {
    319     if (processedBytes >= (1 << 20) && progress != NULL)
    320     {
    321       /*
    322       const UInt64 compressedSize =
    323         _mainInStream.GetProcessedSize() +
    324         _callStream.GetProcessedSize() +
    325         _jumpStream.GetProcessedSize() +
    326         _rangeDecoder.GetProcessedSize();
    327       */
    328       const UInt64 nowPos64 = _outStream.GetProcessedSize();
    329       RINOK(progress->SetRatioInfo(NULL, &nowPos64));
    330       processedBytes = 0;
    331     }
    332     UInt32 i;
    333     Byte b = 0;
    334     const UInt32 kBurstSize = (1 << 18);
    335     for (i = 0; i < kBurstSize; i++)
    336     {
    337       if (!_mainInStream.ReadByte(b))
    338         return Flush();
    339       _outStream.WriteByte(b);
    340       if (IsJ(prevByte, b))
    341         break;
    342       prevByte = b;
    343     }
    344     processedBytes += i;
    345     if (i == kBurstSize)
    346       continue;
    347     unsigned index = GetIndex(prevByte, b);
    348     if (_statusDecoder[index].Decode(&_rangeDecoder) == 1)
    349     {
    350       UInt32 src = 0;
    351       CInBuffer &s = (b == 0xE8) ? _callStream : _jumpStream;
    352       for (int i = 0; i < 4; i++)
    353       {
    354         Byte b0;
    355         if(!s.ReadByte(b0))
    356           return S_FALSE;
    357         src <<= 8;
    358         src |= ((UInt32)b0);
    359       }
    360       UInt32 dest = src - (UInt32(_outStream.GetProcessedSize()) + 4) ;
    361       _outStream.WriteByte((Byte)(dest));
    362       _outStream.WriteByte((Byte)(dest >> 8));
    363       _outStream.WriteByte((Byte)(dest >> 16));
    364       _outStream.WriteByte((Byte)(dest >> 24));
    365       prevByte = (Byte)(dest >> 24);
    366       processedBytes += 4;
    367     }
    368     else
    369       prevByte = b;
    370   }
    371 }
    372 
    373 STDMETHODIMP CDecoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams,
    374     ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams,
    375     ICompressProgressInfo *progress)
    376 {
    377   try
    378   {
    379     return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress);
    380   }
    381   catch(const CInBufferException &e) { return e.ErrorCode; }
    382   catch(const COutBufferException &e) { return e.ErrorCode; }
    383   catch(...) { return S_FALSE; }
    384 }
    385 
    386 }}
    387