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 
IsJcc(Byte b0,Byte b1)12 inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); }
IsJ(Byte b0,Byte b1)13 inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); }
GetIndex(Byte b0,Byte 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 
Test86MSByte(Byte b)26 static bool inline Test86MSByte(Byte b)
27 {
28   return (((b) + SIGN_BIT) & MASK_HIGH) == 0;
29 }
30 
~CEncoder()31 CEncoder::~CEncoder()
32 {
33   ::MidFree(_buf);
34 }
35 
Flush()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 
CodeReal(ISequentialInStream ** inStreams,const UInt64 ** inSizes,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 **,UInt32 numOutStreams,ICompressProgressInfo * progress)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 
Code(ISequentialInStream ** inStreams,const UInt64 ** inSizes,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 ** outSizes,UInt32 numOutStreams,ICompressProgressInfo * progress)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 
SetInBufSize(UInt32 streamIndex,UInt32 size)255 STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; }
SetOutBufSize(UInt32,UInt32 size)256 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
257 
CDecoder()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 
CodeReal(ISequentialInStream ** inStreams,const UInt64 **,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 **,UInt32 numOutStreams,ICompressProgressInfo * progress)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 
Code(ISequentialInStream ** inStreams,const UInt64 ** inSizes,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 ** outSizes,UInt32 numOutStreams,ICompressProgressInfo * progress)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