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