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 int kBufferSize = 1 << 17;
19 
Test86MSByte(Byte b)20 static bool inline Test86MSByte(Byte b)
21 {
22   return (b == 0 || b == 0xFF);
23 }
24 
Create()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 
~CEncoder()44 CEncoder::~CEncoder()
45 {
46   ::MidFree(_buffer);
47 }
48 
Flush()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 
CodeReal(ISequentialInStream ** inStreams,const UInt64 ** inSizes,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 **,UInt32 numOutStreams,ICompressProgressInfo * progress)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 
Code(ISequentialInStream ** inStreams,const UInt64 ** inSizes,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 ** outSizes,UInt32 numOutStreams,ICompressProgressInfo * progress)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 
SetInBufSize(UInt32 streamIndex,UInt32 size)268 STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; }
SetOutBufSize(UInt32,UInt32 size)269 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
270 
CDecoder()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 
CodeReal(ISequentialInStream ** inStreams,const UInt64 **,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 **,UInt32 numOutStreams,ICompressProgressInfo * progress)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 
Code(ISequentialInStream ** inStreams,const UInt64 ** inSizes,UInt32 numInStreams,ISequentialOutStream ** outStreams,const UInt64 ** outSizes,UInt32 numOutStreams,ICompressProgressInfo * progress)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