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 int kBufferSize = 1 << 17; 19 20 static bool inline Test86MSByte(Byte b) 21 { 22 return (b == 0 || b == 0xFF); 23 } 24 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 44 CEncoder::~CEncoder() 45 { 46 ::MidFree(_buffer); 47 } 48 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 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 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 268 STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; } 269 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; } 270 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 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 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