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