Bcj2Coder.cpp revision baa3858d3f5d128a5c8466b700098109edcad5f2
1// Bcj2Coder.cpp
2
3#include "StdAfx.h"
4
5#include "../../../C/Alloc.h"
6
7#include "Bcj2Coder.h"
8
9namespace NCompress {
10namespace NBcj2 {
11
12inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); }
13inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); }
14inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); }
15
16#ifndef EXTRACT_ONLY
17
18static const int kBufferSize = 1 << 17;
19
20static bool inline Test86MSByte(Byte b)
21{
22  return (b == 0 || b == 0xFF);
23}
24
25bool 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
44CEncoder::~CEncoder()
45{
46  ::MidFree(_buffer);
47}
48
49HRESULT CEncoder::Flush()
50{
51  RINOK(_mainStream.Flush());
52  RINOK(_callStream.Flush());
53  RINOK(_jumpStream.Flush());
54  _rangeEncoder.FlushData();
55  return _rangeEncoder.FlushStream();
56}
57
58const UInt32 kDefaultLimit = (1 << 24);
59
60HRESULT 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
253STDMETHODIMP 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
268STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; }
269STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
270
271CDecoder::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
280HRESULT 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
373STDMETHODIMP 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