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