CCompressedStream.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #include "StdInc.h"
  2. #include "CCompressedStream.h"
  3. #include <zlib.h>
  4. static const int inflateBlockSize = 10000;
  5. CBufferedStream::CBufferedStream():
  6. position(0),
  7. endOfFileReached(false)
  8. {
  9. }
  10. si64 CBufferedStream::read(ui8 * data, si64 size)
  11. {
  12. ensureSize(position + size);
  13. auto start = buffer.data() + position;
  14. si64 toRead = std::min<si64>(size, buffer.size() - position);
  15. std::copy(start, start + toRead, data);
  16. position += toRead;
  17. return size;
  18. }
  19. si64 CBufferedStream::seek(si64 position)
  20. {
  21. ensureSize(position);
  22. this->position = std::min<si64>(position, buffer.size());
  23. return this->position;
  24. }
  25. si64 CBufferedStream::tell()
  26. {
  27. return position;
  28. }
  29. si64 CBufferedStream::skip(si64 delta)
  30. {
  31. return seek(position + delta) - delta;
  32. }
  33. si64 CBufferedStream::getSize()
  34. {
  35. si64 prevPos = tell();
  36. seek(std::numeric_limits<si64>::max());
  37. si64 size = tell();
  38. seek(prevPos);
  39. return size;
  40. }
  41. void CBufferedStream::ensureSize(si64 size)
  42. {
  43. while (buffer.size() < size && !endOfFileReached)
  44. {
  45. si64 initialSize = buffer.size();
  46. si64 currentStep = std::min<si64>(size, buffer.size());
  47. vstd::amax(currentStep, 1024); // to avoid large number of calls at start
  48. buffer.resize(initialSize + currentStep);
  49. si64 readSize = readMore(buffer.data() + initialSize, currentStep);
  50. if (readSize != currentStep)
  51. {
  52. endOfFileReached = true;
  53. buffer.resize(initialSize + readSize);
  54. buffer.shrink_to_fit();
  55. return;
  56. }
  57. }
  58. }
  59. void CBufferedStream::reset()
  60. {
  61. buffer.clear();
  62. position = 0;
  63. endOfFileReached = false;
  64. }
  65. CCompressedStream::CCompressedStream(std::unique_ptr<CInputStream> stream, bool gzip, size_t decompressedSize):
  66. gzipStream(std::move(stream)),
  67. compressedBuffer(inflateBlockSize)
  68. {
  69. assert(gzipStream);
  70. // Allocate inflate state
  71. inflateState = new z_stream;
  72. inflateState->zalloc = Z_NULL;
  73. inflateState->zfree = Z_NULL;
  74. inflateState->opaque = Z_NULL;
  75. inflateState->avail_in = 0;
  76. inflateState->next_in = Z_NULL;
  77. int wbits = 15;
  78. if (gzip)
  79. wbits += 16;
  80. int ret = inflateInit2(inflateState, wbits);
  81. if (ret != Z_OK)
  82. throw std::runtime_error("Failed to initialize inflate!\n");
  83. }
  84. CCompressedStream::~CCompressedStream()
  85. {
  86. inflateEnd(inflateState);
  87. delete inflateState;
  88. }
  89. si64 CCompressedStream::readMore(ui8 *data, si64 size)
  90. {
  91. if (inflateState == nullptr)
  92. return 0; //file already decompressed
  93. bool fileEnded = false; //end of file reached
  94. bool endLoop = false;
  95. int decompressed = inflateState->total_out;
  96. inflateState->avail_out = size;
  97. inflateState->next_out = data;
  98. do
  99. {
  100. if (inflateState->avail_in == 0)
  101. {
  102. //inflate ran out of available data or was not initialized yet
  103. // get new input data and update state accordingly
  104. si64 availSize = gzipStream->read(compressedBuffer.data(), compressedBuffer.size());
  105. if (availSize != compressedBuffer.size())
  106. gzipStream.reset();
  107. inflateState->avail_in = availSize;
  108. inflateState->next_in = compressedBuffer.data();
  109. }
  110. int ret = inflate(inflateState, Z_NO_FLUSH);
  111. if (inflateState->avail_in == 0 && gzipStream == nullptr)
  112. fileEnded = true;
  113. switch (ret)
  114. {
  115. case Z_OK: //input data ended/ output buffer full
  116. endLoop = false;
  117. break;
  118. case Z_STREAM_END: // stream ended. Note that campaign files consist from multiple such streams
  119. endLoop = true;
  120. break;
  121. case Z_BUF_ERROR: // output buffer full. Can be triggered?
  122. endLoop = true;
  123. break;
  124. default:
  125. if (inflateState->msg == nullptr)
  126. throw std::runtime_error("Decompression error. Return code was " + boost::lexical_cast<std::string>(ret));
  127. else
  128. throw std::runtime_error(std::string("Decompression error: ") + inflateState->msg);
  129. }
  130. }
  131. while (endLoop == false && inflateState->avail_out != 0 );
  132. decompressed = inflateState->total_out - decompressed;
  133. // Clean up and return
  134. if (fileEnded)
  135. {
  136. inflateEnd(inflateState);
  137. inflateState = nullptr;
  138. }
  139. return decompressed;
  140. }
  141. bool CCompressedStream::getNextBlock()
  142. {
  143. if (!inflateState)
  144. return false;
  145. if (inflateReset(inflateState) < 0)
  146. return false;
  147. reset();
  148. return true;
  149. }