brotli.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #include "brotli.h"
  2. #include <contrib/libs/brotli/include/brotli/decode.h>
  3. #include <contrib/libs/brotli/include/brotli/encode.h>
  4. #include <util/generic/yexception.h>
  5. #include <util/memory/addstorage.h>
  6. namespace {
  7. struct TAllocator {
  8. static void* Allocate(void* /* opaque */, size_t size) {
  9. return ::operator new(size);
  10. }
  11. static void Deallocate(void* /* opaque */, void* ptr) noexcept {
  12. ::operator delete(ptr);
  13. }
  14. };
  15. }
  16. class TBrotliCompress::TImpl {
  17. public:
  18. TImpl(IOutputStream* slave, int quality)
  19. : Slave_(slave)
  20. , EncoderState_(BrotliEncoderCreateInstance(&TAllocator::Allocate, &TAllocator::Deallocate, nullptr))
  21. {
  22. if (!EncoderState_) {
  23. ythrow yexception() << "Brotli encoder initialization failed";
  24. }
  25. auto res = BrotliEncoderSetParameter(
  26. EncoderState_,
  27. BROTLI_PARAM_QUALITY,
  28. quality);
  29. if (!res) {
  30. BrotliEncoderDestroyInstance(EncoderState_);
  31. ythrow yexception() << "Failed to set brotli encoder quality to " << quality;
  32. }
  33. }
  34. ~TImpl() {
  35. BrotliEncoderDestroyInstance(EncoderState_);
  36. }
  37. void Write(const void* buffer, size_t size) {
  38. DoWrite(buffer, size, BROTLI_OPERATION_PROCESS);
  39. }
  40. void Flush() {
  41. DoWrite(nullptr, 0, BROTLI_OPERATION_FLUSH);
  42. }
  43. void Finish() {
  44. Flush();
  45. DoWrite(nullptr, 0, BROTLI_OPERATION_FINISH);
  46. Y_VERIFY(BrotliEncoderIsFinished(EncoderState_));
  47. }
  48. private:
  49. IOutputStream* Slave_;
  50. BrotliEncoderState* EncoderState_;
  51. void DoWrite(const void* buffer, size_t size, BrotliEncoderOperation operation) {
  52. size_t availableOut = 0;
  53. ui8* outputBuffer = nullptr;
  54. const ui8* uBuffer = static_cast<const ui8*>(buffer);
  55. do {
  56. auto result = BrotliEncoderCompressStream(
  57. EncoderState_,
  58. operation,
  59. &size,
  60. &uBuffer,
  61. &availableOut,
  62. &outputBuffer,
  63. nullptr);
  64. if (result == BROTLI_FALSE) {
  65. ythrow yexception() << "Brotli encoder failed to process buffer";
  66. }
  67. size_t outputLength = 0;
  68. const ui8* output = BrotliEncoderTakeOutput(EncoderState_, &outputLength);
  69. if (outputLength > 0) {
  70. Slave_->Write(output, outputLength);
  71. }
  72. } while (size > 0 || BrotliEncoderHasMoreOutput(EncoderState_));
  73. }
  74. };
  75. TBrotliCompress::TBrotliCompress(IOutputStream* slave, int quality) {
  76. Impl_.Reset(new TImpl(slave, quality));
  77. }
  78. TBrotliCompress::~TBrotliCompress() {
  79. try {
  80. Finish();
  81. } catch (...) {
  82. }
  83. }
  84. void TBrotliCompress::DoWrite(const void* buffer, size_t size) {
  85. Impl_->Write(buffer, size);
  86. }
  87. void TBrotliCompress::DoFlush() {
  88. if (Impl_) {
  89. Impl_->Flush();
  90. }
  91. }
  92. void TBrotliCompress::DoFinish() {
  93. THolder<TImpl> impl(Impl_.Release());
  94. if (impl) {
  95. impl->Finish();
  96. }
  97. }
  98. ////////////////////////////////////////////////////////////////////////////////
  99. class TBrotliDecompress::TImpl: public TAdditionalStorage<TImpl> {
  100. public:
  101. TImpl(IInputStream* slave)
  102. : Slave_(slave)
  103. {
  104. InitDecoder();
  105. }
  106. ~TImpl() {
  107. FreeDecoder();
  108. }
  109. size_t Read(void* buffer, size_t size) {
  110. Y_ASSERT(size > 0);
  111. ui8* outBuffer = static_cast<ui8*>(buffer);
  112. size_t availableOut = size;
  113. size_t decompressedSize = 0;
  114. BrotliDecoderResult result;
  115. do {
  116. if (InputAvailable_ == 0 && !InputExhausted_) {
  117. InputBuffer_ = TmpBuf();
  118. InputAvailable_ = Slave_->Read((void*)InputBuffer_, TmpBufLen());
  119. if (InputAvailable_ == 0) {
  120. InputExhausted_ = true;
  121. }
  122. }
  123. if (SubstreamFinished_ && !InputExhausted_) {
  124. ResetState();
  125. }
  126. result = BrotliDecoderDecompressStream(
  127. DecoderState_,
  128. &InputAvailable_,
  129. &InputBuffer_,
  130. &availableOut,
  131. &outBuffer,
  132. nullptr);
  133. decompressedSize = size - availableOut;
  134. SubstreamFinished_ = (result == BROTLI_DECODER_RESULT_SUCCESS);
  135. if (result == BROTLI_DECODER_RESULT_ERROR) {
  136. BrotliDecoderErrorCode code = BrotliDecoderGetErrorCode(DecoderState_);
  137. ythrow yexception() << "Brotli decoder failed to decompress buffer: "
  138. << BrotliDecoderErrorString(code);
  139. } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
  140. Y_VERIFY(availableOut != size,
  141. "Buffer passed to read in Brotli decoder is too small");
  142. break;
  143. }
  144. } while (decompressedSize == 0 && result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && !InputExhausted_);
  145. if (!SubstreamFinished_ && decompressedSize == 0) {
  146. ythrow yexception() << "Input stream is incomplete";
  147. }
  148. return decompressedSize;
  149. }
  150. private:
  151. IInputStream* Slave_;
  152. BrotliDecoderState* DecoderState_;
  153. bool SubstreamFinished_ = false;
  154. bool InputExhausted_ = false;
  155. const ui8* InputBuffer_ = nullptr;
  156. size_t InputAvailable_ = 0;
  157. unsigned char* TmpBuf() noexcept {
  158. return static_cast<unsigned char*>(AdditionalData());
  159. }
  160. size_t TmpBufLen() const noexcept {
  161. return AdditionalDataLength();
  162. }
  163. void InitDecoder() {
  164. DecoderState_ = BrotliDecoderCreateInstance(&TAllocator::Allocate, &TAllocator::Deallocate, nullptr);
  165. if (!DecoderState_) {
  166. ythrow yexception() << "Brotli decoder initialization failed";
  167. }
  168. }
  169. void FreeDecoder() {
  170. BrotliDecoderDestroyInstance(DecoderState_);
  171. }
  172. void ResetState() {
  173. Y_VERIFY(BrotliDecoderIsFinished(DecoderState_));
  174. FreeDecoder();
  175. InitDecoder();
  176. }
  177. };
  178. TBrotliDecompress::TBrotliDecompress(IInputStream* slave, size_t bufferSize)
  179. : Impl_(new (bufferSize) TImpl(slave))
  180. {
  181. }
  182. TBrotliDecompress::~TBrotliDecompress() = default;
  183. size_t TBrotliDecompress::DoRead(void* buffer, size_t size) {
  184. return Impl_->Read(buffer, size);
  185. }