bzip2.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "bzip2.h"
  2. #include <util/memory/addstorage.h>
  3. #include <util/generic/scope.h>
  4. #include <contrib/libs/libbz2/bzlib.h>
  5. class TBZipDecompress::TImpl: public TAdditionalStorage<TImpl> {
  6. public:
  7. inline TImpl(IInputStream* input)
  8. : Stream_(input)
  9. {
  10. Zero(BzStream_);
  11. Init();
  12. }
  13. inline ~TImpl() {
  14. Clear();
  15. }
  16. inline void Init() {
  17. if (BZ2_bzDecompressInit(&BzStream_, 0, 0) != BZ_OK) {
  18. ythrow TBZipDecompressError() << "can not init bzip engine";
  19. }
  20. }
  21. inline void Clear() noexcept {
  22. BZ2_bzDecompressEnd(&BzStream_);
  23. }
  24. inline size_t Read(void* buf, size_t size) {
  25. BzStream_.next_out = (char*)buf;
  26. BzStream_.avail_out = size;
  27. while (true) {
  28. if (BzStream_.avail_in == 0) {
  29. if (FillInputBuffer() == 0) {
  30. return 0;
  31. }
  32. }
  33. switch (BZ2_bzDecompress(&BzStream_)) {
  34. case BZ_STREAM_END: {
  35. Clear();
  36. Init();
  37. [[fallthrough]];
  38. }
  39. case BZ_OK: {
  40. const size_t processed = size - BzStream_.avail_out;
  41. if (processed) {
  42. return processed;
  43. }
  44. break;
  45. }
  46. default:
  47. ythrow TBZipDecompressError() << "bzip error";
  48. }
  49. }
  50. }
  51. inline size_t FillInputBuffer() {
  52. BzStream_.next_in = (char*)AdditionalData();
  53. BzStream_.avail_in = Stream_->Read(BzStream_.next_in, AdditionalDataLength());
  54. return BzStream_.avail_in;
  55. }
  56. private:
  57. IInputStream* Stream_;
  58. bz_stream BzStream_;
  59. };
  60. TBZipDecompress::TBZipDecompress(IInputStream* input, size_t bufLen)
  61. : Impl_(new (bufLen) TImpl(input))
  62. {
  63. }
  64. TBZipDecompress::~TBZipDecompress() {
  65. }
  66. size_t TBZipDecompress::DoRead(void* buf, size_t size) {
  67. return Impl_->Read(buf, size);
  68. }
  69. class TBZipCompress::TImpl: public TAdditionalStorage<TImpl> {
  70. public:
  71. inline TImpl(IOutputStream* stream, size_t level)
  72. : Stream_(stream)
  73. {
  74. Zero(BzStream_);
  75. if (BZ2_bzCompressInit(&BzStream_, level, 0, 0) != BZ_OK) {
  76. ythrow TBZipCompressError() << "can not init bzip engine";
  77. }
  78. BzStream_.next_out = TmpBuf();
  79. BzStream_.avail_out = TmpBufLen();
  80. }
  81. inline ~TImpl() {
  82. BZ2_bzCompressEnd(&BzStream_);
  83. }
  84. inline void Write(const void* buf, size_t size) {
  85. BzStream_.next_in = (char*)buf;
  86. BzStream_.avail_in = size;
  87. Y_DEFER {
  88. BzStream_.next_in = 0;
  89. BzStream_.avail_in = 0;
  90. };
  91. while (BzStream_.avail_in) {
  92. const int ret = BZ2_bzCompress(&BzStream_, BZ_RUN);
  93. switch (ret) {
  94. case BZ_RUN_OK:
  95. continue;
  96. case BZ_PARAM_ERROR:
  97. case BZ_OUTBUFF_FULL:
  98. Stream_->Write(TmpBuf(), TmpBufLen() - BzStream_.avail_out);
  99. BzStream_.next_out = TmpBuf();
  100. BzStream_.avail_out = TmpBufLen();
  101. break;
  102. default:
  103. ythrow TBZipCompressError() << "bzip error(" << ret << ", " << BzStream_.avail_out << ")";
  104. }
  105. }
  106. }
  107. inline void Flush() {
  108. /*
  109. * TODO ?
  110. */
  111. }
  112. inline void Finish() {
  113. int ret = BZ2_bzCompress(&BzStream_, BZ_FINISH);
  114. while (ret != BZ_STREAM_END) {
  115. Stream_->Write(TmpBuf(), TmpBufLen() - BzStream_.avail_out);
  116. BzStream_.next_out = TmpBuf();
  117. BzStream_.avail_out = TmpBufLen();
  118. ret = BZ2_bzCompress(&BzStream_, BZ_FINISH);
  119. }
  120. Stream_->Write(TmpBuf(), TmpBufLen() - BzStream_.avail_out);
  121. }
  122. private:
  123. inline char* TmpBuf() noexcept {
  124. return (char*)AdditionalData();
  125. }
  126. inline size_t TmpBufLen() const noexcept {
  127. return AdditionalDataLength();
  128. }
  129. private:
  130. IOutputStream* Stream_;
  131. bz_stream BzStream_;
  132. };
  133. TBZipCompress::TBZipCompress(IOutputStream* out, size_t compressionLevel, size_t bufLen)
  134. : Impl_(new (bufLen) TImpl(out, compressionLevel))
  135. {
  136. }
  137. TBZipCompress::~TBZipCompress() {
  138. try {
  139. Finish();
  140. } catch (...) {
  141. }
  142. }
  143. void TBZipCompress::DoWrite(const void* buf, size_t size) {
  144. if (!Impl_) {
  145. ythrow TBZipCompressError() << "can not write to finished bzip stream";
  146. }
  147. Impl_->Write(buf, size);
  148. }
  149. void TBZipCompress::DoFlush() {
  150. if (Impl_) {
  151. Impl_->Flush();
  152. }
  153. }
  154. void TBZipCompress::DoFinish() {
  155. THolder<TImpl> impl(Impl_.Release());
  156. if (impl) {
  157. impl->Finish();
  158. }
  159. }